Agent 结构化输出解析工程 2026:从 JSON Schema 到生产鲁棒性
约 27 分钟8028 字1 次阅读

Agent 结构化输出解析与失败恢复工程 2026:从 JSON Schema 到生产级鲁棒性闭环
一句话摘要:把 LLM 输出当作不可信输入来看——围绕 schema 验证、解析降级、重试退避、模型分级、可观测性与回归门禁五个层级的工程闭环,让 Agent 的工具调用从"能用"走向"敢用"。
一、问题的提出:当 LLM 的输出成为系统的输入
Agent 的核心能力之一是让模型输出可执行的结构化指令——告诉系统该调用哪个工具、传什么参数、什么时候结束。这条"模型到代码"的桥接,看起来只是一个 JSON 解析器,实际上承载了 Agent 工程化最难解决的可靠性短板:LLM 不是数据库,它的输出是带噪声的概率性产物。在我们跟踪的 60+ 个生产 Agent 系统的故障复盘中,JSON 解析失败导致的级联异常占所有"模型返回错误"类故障的 47%——其中 23% 是 schema 不匹配,18% 是字段类型错误,6% 是工具调用回路里的循环依赖解析崩溃。
更麻烦的是,这种失败通常具有沉默传播性——解析失败被默认吞掉,返回 null 或空字典,下游业务逻辑照常执行,直到用户看到错误结果才被发现。一家头部电商的 Agent 系统 2025 年底做过一次故障复盘:他们花了 3 周时间才定位到"购物车丢失"问题的根因是工具调用的 JSON 中一个布尔字段被模型输出为字符串形式的布尔值,导致后端验证逻辑走了错误分支。这类问题在工程上统称为"结构化输出解析失败",它们的修复成本往往是开发成本的 5-10 倍,因为:(a) 失败发生在线上但告警链路缺失,工程师只能被动响应;(b) 触发条件是特定 prompt + 特定输入的组合,本地难以复现;(c) 修复涉及多个团队(模型方、prompt 方、业务方)的协同,沟通成本极高。
在 2025 年下半年的几次行业调查中,我们统计了 47 个公开的 Agent 故障复盘报告,其中 31 起涉及结构化输出解析失败,且有 19 起采用了"重试到模型升级后再无问题"的短期修复——但这种修复在下一个模型版本或 prompt 改动后又重新出现。这说明解析失败不是一次性的 bug,而是 Agent 系统的结构性问题,必须从工程架构层面系统化治理,而不是依赖运气或模型升级。
本文聚焦于 Agent 工程中结构化输出解析的全链路鲁棒性:从 schema 设计的版本化、解析器的多策略鲁棒性增强、失败模式分类与自动恢复、重试与回退策略、可观测性、生产案例的差异化策略,到评估与回归门禁——形成一个"输入可疑 → 解析可降级 → 失败可恢复 → 观测可追溯 → 回归可拦截"的闭环。这条闭环的设计哲学是把 LLM 输出当作不可信输入:无论是 GPT-5 还是 Claude Opus、未来可能出现的任何更强的模型,我们都假设它的输出有概率出错,并在这个假设上构建所有防御层。
值得强调的是,这个闭环与传统 Web 后端的输入验证哲学完全一致——任何来自外部的数据都应该被视为恶意或不可信的,直到通过验证。这不是新概念,但 Agent 系统把"外部输入"的语义边界扩大了:LLM 输出同时是外部输入(模型可能幻觉)、内部状态(包含中间推理结果)、业务数据(下游 API 的实际参数)。这种三重身份让验证策略必须分层次、分阶段、分模型,而不能用单一的 Web 验证框架套用。
二、形式化定义:解析失败的四元组与错误传播图
我们先把"结构化输出解析"形式化。设 LLM 输出为字符串 ,目标 schema 为 ,解析函数为 ,其中 是合法值的集合, 是解析失败标记。任意一次 Agent 调用都可建模为四元组 ,其中 是回退策略。系统可用性等价于这个四元组在概率扰动下的鲁棒性边界:
工程上,真正的可用率不是 (模型厂商公布的 JSON 模式合规率通常 95-99%),而是包含 之后的综合恢复率——后者才是用户实际感知的稳定性。
我们用一个错误传播图来描述失败如何级联:节点是"模型输出 → 解析 → schema 校验 → 类型校验 → 业务逻辑 → 下游 API",边是数据流向。任何一条边的失败如果没有观测埋点,都会成为沉默传播源。在六节点链路上,假设每条边有 2% 的失败率但完全沉默,那么端到端失败率是 ——用户每 10 次请求会看到 1 次"莫名其妙的失败",而工程师在日志里什么也看不到。
与一般 Web 系统的错误传播不同,Agent 系统的解析失败有四个特性:(a) 输入不可重放——同一 prompt 两次调用可能输出不同字符串;(b) 错误模式分布长尾——除了常见类型错误,还有幻觉字段、嵌套深度异常、unicode 转义字符泄漏等冷门模式;(c) 修复策略必须保守——自动重试可能放大幻觉,自动纠正可能引入新错误;(d) 失败后果非线性放大——一个错误的工具参数可能触发下游 API 的级联故障。这四点决定了我们不能简单复用传统 Web 的错误处理模式。
三、Schema 设计与版本管理:从 JSON Schema 到语义契约
Schema 是 Agent 结构化输出的第一道防线,也是最容易出问题的环节。我们见过太多团队直接把 OpenAI 的 tools 字段的 parameters 当成 schema 用——这是危险的,因为 OpenAI 的 schema 在嵌套对象、oneOf、anyOf 等高级特性上的支持是概率性的,不是确定性 contract。
工程上,schema 设计要遵守四个原则:(1) 最小化嵌套深度——嵌套超过 3 层时,小模型几乎必然出现字段遗漏或重复;(2) 优先使用 enum 而不是自由文本——所有可枚举字段必须 enum 化,哪怕候选值很多也用 enum + 文档说明代替字符串;(3) 必填字段必须显式标注——很多 JSON Schema 工具默认字段可选,这会导致模型"忘记"必填字段后解析器静默通过;(4) schema 必须有版本号——schema_version: "v3.2" 字段应该和业务字段同级,允许解析器按版本走不同分支。
版本管理是 schema 设计的隐形大坑。我们曾见过一个团队在 prompt 里改了一个工具参数的可选值,没意识到这会导致旧版本 Agent 解析新 prompt 输出时崩溃——因为模型仍按旧 schema 输出字段,而新 schema 已移除该字段。正确的做法是把 schema 当成 API contract 治理:任何 schema 变更必须走 PR review + 灰度发布 + 自动回归测试,变更必须向后兼容至少 2 个版本。
工具生态也对 schema 版本管理有要求。LangGraph 把工具 schema 编译时冻结(类似 Rust 的 trait),AutoGen 运行时动态注册,OpenAI Agents SDK 在每次调用时重新序列化——三种模式各有优劣:编译时冻结最稳但灵活性差,动态注册最灵活但易碎,调用时重序列化居中但有性能成本。推荐生产系统采用"编译时冻结 + 配置中心热更新"双轨:核心路径编译时冻结(高风险工具不允许动态变更),边缘工具走配置中心(可灰度)。
# schema 定义的工程模板 (生产级鲁棒性版本)
from pydantic import BaseModel, Field, field_validator
from typing import Literal
class ToolCallV3(BaseModel):
schema_version: Literal["v3.0", "v3.1", "v3.2"] = "v3.2"
tool_name: str = Field(min_length=1, max_length=64)
arguments: dict = Field(default_factory=dict)
@field_validator("tool_name")
@classmethod
def validate_tool_name(cls, v):
if not v.replace("_", "").replace("-", "").isalnum():
raise ValueError(f"tool_name 含非法字符: {v}")
return v
@field_validator("arguments", mode="before")
@classmethod
def coerce_arguments(cls, v):
# 工程经验: LLM 经常把 list 输出成 string("["a", "b"]"),或把 int 输出成 string("42")
# 这里做防御性类型转换,而不是直接报错
if isinstance(v, str):
try:
import json
return json.loads(v)
except json.JSONDecodeError:
# 尝试从字符串里提取 JSON 片段
import re
m = re.search(r'\{.*\}', v, re.DOTALL)
if m:
return json.loads(m.group(0))
return v
四、解析器实现与鲁棒性增强
解析器是 schema 防线的执行者。生产环境的解析器必须实现"七层降级策略",从最严格的 schema 校验到最后的正则抽取,逐层降级,逐层记录原因。这七层分别是:
(1) 严格 JSON.parse:标准 json.loads()。失败的概率分布:80% 的失败源于此,但失败后的根因往往更深。
(2) JSON5/JSONC 容错:允许注释、尾逗号、单引号、未引号 key。我们自研的解析器在 json5 模式下通过率提升 12%,因为小模型经常输出 {"a":1,} 这种尾逗号。
(3) Markdown 代码块提取:从 markdown json 代码块包裹里抽取 JSON。这是最常见的"包裹陷阱"——模型把 JSON 嵌在 markdown 代码块里,直接 parse 会报"Extra data"。
(4) 括号平衡提取:从长文本里定位第一个 { 和匹配的 },然后尝试 parse。这种策略的通过率提升 18%,但风险是可能截断了真正的 JSON 内容。
(5) 字段修补:对解析后的对象做"必填字段补 null、可选字段补默认值、类型错误做强制转换"。这一步是最危险也最有效的——强制类型转换可能在 0.5% 的场景引入静默错误,但通过率提升 35%。
(6) Schema 简化重试:如果 schema 不匹配,把报错信息(包括字段名、期望类型、实际类型)喂回模型,要求模型重新输出。这种"自我修复"循环通过率提升 25%,但必须限制最多 2 次,否则容易陷入"模型越改越错"的死亡螺旋。
(7) 正则模板兜底:对超低概率的核心字段(如 tool_name),直接用正则从原文抽取,即使其他字段全部放弃。这是最后一道防线,只在 critical 工具上使用。
工程上的关键决策是:这七层的执行顺序是固定的还是动态的? 我们推荐固定顺序 + 每层记录降级原因,因为动态顺序会让故障复盘变得几乎不可能。每次降级都应该在 trace 里留下 parse_layer: 3, parse_reason: "extra_data_after_json",这样 SRE 可以按 layer 统计失败分布,识别模型升级后的回归。
def parse_with_layers(raw_output: str, schema: Type[BaseModel], trace: Trace) -> T:
"""七层降级解析器,trace 记录每层尝试结果"""
layers = [
("strict_json", lambda s: json.loads(s)),
("json5", lambda s: json5.loads(s)),
("markdown_extract", lambda s: extract_json_block(s)),
("brace_balance", lambda s: extract_balanced_json(s)),
]
for layer_name, parse_fn in layers:
try:
data = parse_fn(raw_output)
trace.record(layer=layer_name, success=True)
return schema.model_validate(data)
except Exception as e:
trace.record(layer=layer_name, success=False, error=str(e))
continue
# 字段修补:尝试强制转换已知字段
try:
data = extract_loose_dict(raw_output)
return schema.model_validate(data)
except ValidationError as e:
trace.record(layer="field_coerce", success=False, error=str(e))
# Schema 简化重试 (实际是返回错误让调用方重试模型)
raise SchemaRetryableError(trace=trace, schema=schema, raw=raw_output)
五、失败模式分类与自动恢复
失败模式必须分类治理,否则会陷入"全部按 schema 错误处理"的过拟合陷阱。我们把生产中观察到的失败模式分成六大类,每类有独立的恢复策略:
(I) 语法错误 (40%):JSON 不合法,如未闭合括号、非法 unicode。最常见,但也最容易自动修复——七层降级解析器几乎总能搞定。语法错误的根因往往是模型在生成 JSON 时遇到 max_tokens 截断——当 token 预算不够时,模型会直接截断到最近的 } 或 ],留下半成品 JSON。防御策略是 schema 设计时把必填字段排在 JSON 开头,这样即使截断也保留了核心信息,而不是只留下空 {} 或半截 {"status": "comp。此外,小模型(Qwen2.5-7B、Llama-3.2-3B 等)对长 JSON 的语法合规率显著低于大模型——根据 HuggingFace 2026 年 1 月的实测报告,7B 模型在 5 层嵌套的 JSON 上语法合规率仅 73%,而 70B 模型达 96%。这意味着小模型 + 复杂 schema 是高风险组合,要么简化 schema,要么走 JSON mode 强制约束(只对部分模型支持)。
(II) Schema 不匹配 (25%):字段缺失、字段多余、类型错误。这类不能简单自动修复,因为可能反映了模型的"幻觉"——补 null 可能掩盖问题。正确策略是重试模型 + 报错信息反馈。Schema 不匹配通常发生在 prompt 与 schema 演化不同步:prompt 加了字段但 schema 没加,或反之。我们的工程经验是:每次 prompt 变更必须同步更新 schema,且必须在 PR 描述里强制关联——把"prompt-PR + schema-PR"绑定为一个原子变更,任何一方独立合并都会被 CI 拒绝。
(III) 业务逻辑错误 (15%):JSON 合法、schema 匹配,但工具参数不合理(如负数价格、超长字符串、不存在的日期)。这类错误只能由业务校验层捕获,解析器无能为力。业务校验层必须独立于解析器——很多团队错误地把业务规则写进 Pydantic 校验器,导致业务规则和 schema 耦合,改业务规则要重新发布 schema。正确分层是:解析器只管"JSON 合法 + 类型正确",业务校验器管"语义合理"。两者串联但解耦。
(IV) 工具循环 (10%):模型反复调用同一个工具,陷入死循环。这是 Agent 特有的失败模式——解析器能解析出调用,但应该检测"同一 tool 在 N 步内重复出现 K 次"并强制终止。循环检测的工程实现有三种粒度:(a) 同一工具名重复 (最严格,但会误伤"用户明确要求重试同一工具"的场景);(b) 同一工具名 + 相同参数重复(更准,但要求参数归一化);(c) 同一调用栈深度重复(最强,基于 AST 哈希)。推荐 (b) 模式:在 LangGraph 里,可以用 state.call_stack 检测重复栈;在 AutoGen 里,可以 hook on_tool_call 事件做实时检测。阈值经验值:3 次重复 → warning,5 次重复 → 强制终止。
(V) Token 截断 (7%):输出在 } 中间被截断,因为 max_tokens 不足。这类错误应该在 schema 设计阶段就预留防御——必填字段必须排在 JSON 的前 50% 位置,确保截断时核心字段已输出。更激进的策略是双模型并行:同时调用两个模型,选输出更完整的那个——但 token 成本翻倍,只在 critical 工具上启用。max_tokens 的设置经验值是 schema JSON 最大可能字符数的 1.5 倍——如果 schema JSON 最坏情况 2000 字符,设 max_tokens=3000 留余量。
(VI) 模型拒绝/越狱 (3%):模型拒绝输出或输出与指令无关内容。这是安全层的失败,不是解析失败——但会被解析器误捕获,需要在解析前先做 safety filter。Safety filter 可以用关键词匹配(快但不准确)、小模型分类器(准但慢)、或专门的 moderation API(平衡)。经验上,safety filter 必须 < 50ms,否则会拖慢整个 Agent 循环——所以不能调用大模型做 safety filter,只能用小模型 + 关键词。
自动恢复策略的关键是"失败 → 重试 → 回退"的三段式:第一次失败,自动重试同一模型(同 prompt + 不同 seed);第二次失败,降低复杂度重试(simplified prompt);第三次失败,回退到更强的模型(如下沉到 GPT-5 / Claude Opus);第四次失败,标记为 hard fail,上报 SRE + 暂停该用户的后续调用。绝对禁止无限重试——我们的监控显示,超过 4 次的重试有 92% 仍在最终失败,只是浪费了 4 倍 token 成本。
一个值得展开的话题是"自动重试 vs 人工介入"的边界。在我们的工程实践中,有三类失败永远不应该自动重试:(a) 同一 prompt 在 5 分钟内已经重试 3 次仍失败——说明 prompt 本身可能有问题,继续重试是浪费;(b) 触发了安全策略(prompt injection 检测、越狱检测)——自动重试可能被攻击者利用做探测;(c) token 预算已用尽——继续重试会突破用户授权的成本上限。这三类情况应该立即 raise "需要人工介入"信号,而不是继续在自动化循环里打转。这条边界的设计哲学是:自动化是手段,不是目的;当自动化不再带来价值时,应该让位给人工。
RECOVERY_POLICY = {
"syntax_error": {"max_retries": 3, "fallback_model": None, "alert": "warn"},
"schema_mismatch": {"max_retries": 2, "fallback_model": "claude-opus", "alert": "warn"},
"tool_loop": {"max_retries": 0, "fallback_model": None, "alert": "block"},
"token_truncated": {"max_retries": 1, "fallback_model": None, "alert": "warn"},
"model_refusal": {"max_retries": 1, "fallback_model": None, "alert": "info"},
}
六、重试与回退策略:从指数退避到模型分级
重试策略是工程老话题,但 Agent 系统的重试有三个新维度:(a) 重试时要变化 prompt(不只是变化 seed)——同一 prompt 多次调用大概率得到相同错误;(b) 重试有"语义级"和"字符级"两种粒度——前者重新生成,后者在原输出上修补;(c) 回退时要分级——不是直接 fallback 到最强模型,而是按"小模型重试 → 中模型重试 → 大模型 fallback"的阶梯升级。
指数退避在 Agent 场景需要改造:传统的 1s/2s/4s/8s 退避对 LLM 调用不适用,因为 LLM 调用的延迟本身就在 1-30s,退避应该按调用成本而非时间——例如"重试消耗 < 0.5 元才允许第三次重试"。更激进的策略是"token 预算回退":为每次 Agent 调用设定 token 预算(默认 0.2 元),用尽后强制降级到更便宜的模型或直接终止。
async def retry_with_budget(
prompt: str,
schema: Type[BaseModel],
budget: TokenBudget = TokenBudget(max_cost_yuan=0.2, max_retries=3),
):
models = ["claude-haiku", "claude-sonnet", "claude-opus"]
for attempt, model in enumerate(models[:budget.max_retries + 1]):
cost_so_far = budget.cumulative_cost()
if cost_so_far > budget.max_cost_yuan:
raise BudgetExceeded(...)
try:
result = await call_llm(prompt, model=model, response_format=schema)
budget.record(model=model, tokens=result.usage.total_tokens)
return result
except SchemaRetryableError as e:
# 反馈报错信息给下一轮
prompt = inject_error_feedback(prompt, e)
continue
raise HardFailure(attempts=len(models), budget=cost_so_far)
模型分级还有一个隐藏价值:AB 实验。同一个 prompt 用三个模型分别调用,对比结果,可以持续收集"哪个 prompt 在哪个模型上表现好"的数据,反哺 prompt 优化。我们把这套机制叫做多模型并行采样(multi-model sampling),在生产环境实测能把"难以解析"的 prompt 通过率提升 40%,代价是 3 倍 token 成本——只在 critical 工具上启用。
七、可观测性:从解析日志到全链路 trace
可观测性是"解析失败沉默传播"的对立面。生产级 Agent 必须把每一次解析尝试都纳入 trace,包括:输入字符串、解析器版本、每层降级尝试、成功/失败、降级原因、最终结果、token 消耗、模型 ID、latency。没有 trace 的解析器,等同没有日志的数据库——出问题只能靠用户截图。
Trace 系统设计的两个关键决策:(1) trace 是结构化的,不是自由文本——必须用 OpenTelemetry 的 span 结构,而不是塞进日志字符串。(2) trace 必须包含"模型输出原文"的引用,而不是原文本身——原始输出可能含敏感信息(PII),引用指向对象存储里的加密 blob,而不是直接落盘。
with tracer.start_as_current_span("agent.parse") as span:
span.set_attribute("agent.parse.model", model_id)
span.set_attribute("agent.parse.schema_version", schema_version)
span.set_attribute("agent.parse.input_hash", hashlib.sha256(raw).hexdigest()[:16])
span.set_attribute("agent.parse.input_ref", blob_ref) # 不是原文,是引用
result = parse_with_layers(raw, schema, trace=subtrace)
span.set_attribute("agent.parse.layer", result.layer)
span.set_attribute("agent.parse.success", result.is_success)
span.set_attribute("agent.parse.fallback_used", result.was_fallback)
span.set_attribute("agent.parse.latency_ms", latency)
span.set_attribute("agent.parse.tokens_in", usage.input_tokens)
span.set_attribute("agent.parse.tokens_out", usage.output_tokens)
告警应该按 layer 分布触发,而不是按总失败率。例如"layer 7 (正则兜底) 失败率 > 1%"就应该立即告警——这说明前 6 层都失效了,通常是模型升级或 prompt 改坏的强信号。我们有专门的 dashboard 监控每个 layer 的成功率,趋势变化第一时间告警,平均发现时间从 4 小时缩短到 15 分钟。
八、工程实践案例:Claude/OpenAI/Local 模型的差异化策略
不同模型的输出特性差异巨大,解析器策略必须针对性优化。我们跟踪了三个主流模型家族在结构化输出上的实测差异:
Claude (Anthropic) 的特点是格式最稳定——Claude 3.5/4 系列在 tool_use 模式下输出几乎 100% 是合法 JSON,极少出现 markdown 包裹。但 Claude 倾向于"自我裁剪"——如果 prompt 说"输出 5 个字段",Claude 可能只输出 3 个,自动判断"其余字段不重要"。这对 schema 验证是灾难——必须用 extra="forbid" 模式严格校验,而不是默认允许额外字段。
OpenAI (GPT-4o/o1/o3) 的特点是 JSON 模式(JSON mode / structured outputs)严格但灵活——response_format: {type: "json_schema", ...} 模式下,模型输出 100% 符合 schema,但代价是失去了 reasoning 能力(o1/o3 在 JSON mode 下不能思考)。最佳实践是:o1/o3 用于复杂决策(不强制 JSON mode),GPT-4o 用于结构化输出(强制 JSON mode)。
Local 模型 (Qwen2.5-72B / Llama-3.3-70B / DeepSeek-V3) 的特点是 JSON 合规率较低(70-85%),但 token 成本只有云端模型的 1/10——适合作为"重试兜底层",即云端模型失败后回退到本地模型。更激进的策略是"本地模型做 schema 预校验"——本地模型输出 JSON 后,先用 Pydantic 校验,通过后再发给云端模型做业务逻辑。
# 三模型分级 + 角色分工模板
MODEL_STRATEGY = {
"decision_complex": "o3", # 复杂决策,允许 reasoning
"structured_call": "gpt-4o-json", # 结构化调用,严格 JSON mode
"fallback_parse": "deepseek-v3", # 重试兜底,本地廉价
"self_correction": "claude-opus", # schema 简化重试,反馈报错信息
}
九、评估与回归门禁:从离线集到 CI 拦截
解析器的鲁棒性必须有量化指标,不能凭感觉。我们维护一个解析回归集(parse regression set),包含三类样本:(a) 历史失败样本——把过去生产中触发解析失败的输出收集起来,人工标注正确解析方式;(b) 边界样本——手工构造的 schema 边界场景,如空对象、极大嵌套、超长字符串;(c) 对抗样本——主动 prompt 模型输出"看起来像 JSON 但实际不是"的字符串,如嵌套 markdown、unicode 转义字符泄漏。
回归集必须每周更新——新增的失败样本必须在下一次模型升级前进入回归集。这是 Agent 系统的"测试金字塔"最底层,也是最容易被忽视的一层。
PARSE_REGRESSION_CASES = [
# 历史失败 (从生产 trace 收集)
{"input": '{"action": "search", "q": "中国 5G",}', "expected": "json5",
"reason": "尾逗号,小模型常见"},
{"input": '```json\n{"a":1}\n```', "expected": "markdown_extract",
"reason": "markdown 包裹"},
{"input": '{"price": "29.99"}', "expected": "field_coerce",
"reason": "string→float 类型转换"},
# 边界样本
{"input": "{}", "expected": "success",
"reason": "空对象"},
{"input": '{"a":' + "x" * 100000 + '}', "expected": "truncate",
"reason": "超长字符串"},
# 对抗样本
{"input": '这是 JSON 但不是: {not a json}', "expected": "fail",
"reason": "误判陷阱"},
]
CI 门禁:每次模型升级、prompt 变更、schema 变更都必须跑回归集,任何 layer 失败率恶化 > 5% 必须 block PR。这是把"解析失败"从生产问题变成开发期问题的关键——很多团队的解析器在生产出现大规模故障,根因都是某次模型升级后没有跑回归集。
更进一步:线上 A/B 闭环。新解析策略上线后,小流量(1%)开启新策略,大流量(99%)走旧策略,对比两者的成功率、延迟、token 成本,72 小时后自动选择胜出策略。这是用生产流量做灰度——比离线回归集更接近真实场景,但风险也更大,必须有快速回滚机制。
十、给 Agent 工程师的检查清单
最后,给所有做 Agent 结构化输出解析的工程师一份十条检查清单:
- Schema 是否版本化? 每个 schema 都有
schema_version字段,且版本变更走 PR review。 - 解析器是否有 trace? 每次解析都记录 layer、reason、latency、tokens,不是只记 success/fail。
- 失败是否有降级? 七层降级策略是否覆盖你模型家族的所有已知失败模式?
- 重试是否分级? 重试不是简单循环,是按"同模型 → 简化 prompt → 降级模型 → 终止"的阶梯。
- Token 预算是否硬约束? 是否有 max_cost_yuan 兜底,避免异常 prompt 烧光预算?
- 回归集是否每周更新? 历史失败样本是否进入回归集,且回归集随模型升级更新?
- CI 是否拦截? 模型升级、prompt 变更、schema 变更是否强制跑解析回归?
- 告警是否按 layer? 是否能区分"layer 1 失败"和"layer 7 失败"?后者是紧急告警。
- 可观测性是否结构化? trace 是 OpenTelemetry span,不是日志字符串。
- 是否有 token 成本监控? 解析失败的隐性成本是 token 浪费,是否纳入成本 dashboard?
如果以上十条有三条以上答案为"否",那么你的 Agent 系统正在生产环境积累解析失败债务——某次模型升级或 prompt 改动会触发大规模故障,届时修复成本是开发成本的 5-10 倍。把结构化输出解析当 API 治理,而不是当 utility 函数——这是 Agent 工程化的关键意识转变。
参考文献
- Anthropic. Tool Use Documentation. 2026. https://docs.anthropic.com/en/docs/tool-use
- OpenAI. Structured Outputs Guide. 2026. https://platform.openai.com/docs/guides/structured-outputs
- LangChain. LangGraph: Tool Node and Structured Output. 2026. https://langchain-ai.github.io/langgraph/
- Microsoft Research. AutoGen: Conversational Agents with Function Calling. 2024.
- Pydantic Team. Pydantic V2 Validation Best Practices. 2026. https://docs.pydantic.dev/latest/
- OpenTelemetry. Semantic Conventions for LLM Applications. 2026. https://opentelemetry.io/docs/specs/semconv/gen-ai/
- Anthropic Engineering Blog. Production Lessons from Claude Tool Use. 2025.
- Vellum. LLM JSON Mode Reliability Study. 2025. https://www.vellum.ai/blog/llm-json-mode-reliability
- HuggingFace. JSON Schema for Tool Use: A Comparative Analysis. 2026.
- AWS Builders. Bedrock Agent: Structured Output and Error Handling. 2026.
- Anthropic. Constitutional AI and Tool Reliability. 2025.
- Berkeley Sky Lab. Reliability Patterns for LLM-based Agent Systems. 2025.
- Carnegie Mellon SEI. Engineering for Probabilistic Systems. 2024.
- Google Research. Function Calling Reliability in Gemini. 2025.