<!-- title: 回对即读错：工具返回解析六栈实测 -->

工具调用是双向契约，一半叫"调"，一半叫"读"。整个行业九成精力砸在"调对"——选对工具、传对参数、过 schema 校验、strict 模式锁死 JSON。但另一半"读对"同样致命，且更隐蔽：工具明明返回了正确数据，Agent 却在消费环节读错字段、丢量纲、混三态、漏分页、信伪装、迷嵌套，最终吐出错误答案。Strict schema 只锁住了"调"的出口（arguments 合法），锁不住"读"的入口（返回值被正确理解）。本文实测 OpenAI、Anthropic、LangChain、Pydantic AI、AutoGen、MCP 六大主流栈，在工具返回值解析这一环上的真实表现与六大沉默失效模式。

## 一、六栈对比总表

| 维度 | OpenAI Structured Outputs | Anthropic Claude | LangChain/LangGraph | Pydantic AI/Instructor | AutoGen | MCP |
|------|---------------------------|------------------|---------------------|------------------------|---------|-----|
| 返回约束机制 | strict:true + JSON Schema | 约束解码 + tool_result 内容块 | ToolMessage + OutputParser | Pydantic 校验驱动 | 自由文本/JSON | content 数组，自由形态 |
| 是否保证读对 | 否（只保调对） | 否 | 否（靠 parser 兜底） | 部分（类型对，语义仍错） | 否 | 否 |
| 空值三态处理 | 无原生 | 无原生 | 需自定义 | 可校验但需手写 | 无 | 无 |
| 分页感知 | 无 | 无 | 无 | 无 | 无 | 无 |
| 错误伪装防护 | 无 | 无 | 无 | 无 | 无 | isError 字段（弱） |
| 上手难度 | 低 | 低-中 | 中 | 中 | 中 | 中-高 |
| 变现潜力 | 高 | 高 | 中 | 中-高 | 中 | 高 |

## 二、深度分析

### 1. OpenAI Structured Outputs：锁了出口，没锁入口

OpenAI 的 `strict: true` 是 2024 年 8 月上线的杀手锏，到 2026 已成为生产默认。它在解码层做约束生成（constrained decoding），保证 `tool_calls` 里的 `arguments` 严格匹配你给的 JSON Schema——多一个字段、少一个字段、类型不对，都不可能产出。这把"调"这一半做到了近乎零误差。

```python
tools = [{
  "type": "function",
  "function": {
    "name": "get_order",
    "strict": True,
    "parameters": {
      "type": "object",
      "properties": {"order_id": {"type": "string"}},
      "required": ["order_id"],
      "additionalProperties": False
    }
  }
}]
```

问题在于：strict 只校验 Agent 传给工具的参数，不校验工具返回给 Agent 的内容。当 `get_order` 返回 `{"subtotal":9900,"tax":990,"total":10890,"grand_total":10890}`，模型完全可能取 `subtotal` 当总价报给用户——schema 没有任何手段阻止它读错字段。strict 的胜利止步于工具调用边界，返回值一进上下文，就是一片语义裸奔。变现上，这块适合做"结构化输出训练营"的进阶模块，但必须明确告诉学员：strict 不等于可靠，它只解决了三成问题。

### 2. Anthropic Claude：tool_result 是裸内容块

Anthropic 的工具用 `tool_use` 内容块发起调用，执行后以 `tool_result` 内容块回填。2026 年它也上线了 Structured Outputs（约束解码），但那是给"Agent 最终回复"用的，不是给工具返回值用的。`tool_result` 的内容本质是一个自由文本/JSON 字符串，塞进 messages 数组：

```python
messages.append({
  "role": "user",
  "content": [{
    "type": "tool_result",
    "tool_use_id": "toolu_xxx",
    "content": json.dumps({"amount": 9900, "currency": "CNY"})
  }]
})
```

这里 `content` 是字符串，模型要自己再"读"一遍 JSON。量纲丢失这一式在这里最典型：返回 `amount: 9900` 表示分，模型直接告诉用户"9900 元"或"99 元"——单位是上下文约定，不在结构里。Anthropic 没有 `isError` 之外的语义层，错误伪装（HTTP 200 + `{"ok":false}`）几乎无防护。优势是多工具并行、内容块组合灵活；劣势是返回值消费完全靠模型自觉。适合做对话型 Agent 训练营，但要配一套"返回值自校验"提示词模板才敢交付。

### 3. LangChain：ToolMessage 有壳无魂

LangChain 用 `ToolMessage` 承载工具返回，并提供 `PydanticOutputParser`、`JsonOutputParser` 等解析器。理论上 parser 能在 Agent 读取前先做一次结构化校验，把风险挡在门外：

```python
from langchain_core.messages import ToolMessage
msg = ToolMessage(
    content=json.dumps(data),
    tool_call_id=call_id,
    name="search_products"
)
parser = PydanticOutputParser(pydantic_object=SearchResult)
parsed = parser.parse(msg.content)  # 校验在这里
```

但实践中三个坑：第一，parser 只校验"形状对不对"（字段在不在、类型对不对），校验不了"读对没读对"——`total` 和 `subtotal` 都是合法数字，parser 全通过，模型照样取错。第二，parser 抛异常会中断链，生产里往往被 try/except 吞掉，退化成裸字符串。第三，分页截断这一式在 LangChain 里几乎无解——`search_products` 返回 20 条带 `has_more:true`，Agent 默认认为这就是全集，LangGraph 没有内置"分页跟随"原语。变现适合做企业 RAG + 工具链课程，但要在课程里专门加一章"返回值消费工程"。

### 4. Pydantic AI / Instructor：校验驱动，最接近"读对"

Pydantic AI 和 Instructor 把 Pydantic 校验推到极致：模型输出直接绑定类型化对象，校验失败自动重试修复。这是六栈里最接近"保证读对"的，但注意——它保证的是"输出对象合法"，不是"语义正确"。

```python
from pydantic_ai import Agent
agent = Agent('openai:gpt-4o', output_type=OrderSummary)

@agent.tool
async def get_order(ctx, order_id: str) -> OrderSummary:
    raw = await fetch(order_id)
    return OrderSummary.model_validate(raw)  # 在工具内提前校验+转换
```

聪明做法是在工具函数内部就用 Pydantic 把原始返回 `model_validate` 成强类型对象，把量纲（`amount_cents` → `amount_yuan`）、空值三态（`None`/`[]`/`error` 显式区分）在源头消解掉。这样模型拿到的已经是语义清晰的对象，读错概率大幅下降。代价是每个工具都要写转换层，开发量翻倍。变现潜力高——"类型安全 Agent 训练营"是 2026 稀缺品类，Pydantic AI 正好是抓手，但门槛在于讲师要真懂校验≠语义。

### 5. AutoGen：返回即对话，最易踩嵌套迷失

AutoGen 的工具返回值直接以消息形式进入多 Agent 对话流，没有强制的结构化层。一个 Agent 调工具、拿结果、转述给另一个 Agent，每转一次都是一次"再读"——嵌套迷失在这一栈最严重。

典型场景：工具返回 `{"order":{"customer":{"address":{"city":"上海"}}}}`，AssistantAgent 提取时把 `address` 整个对象当城市名报出去，或读到 `customer` 层就停。多 Agent 链路里，这种读错会被下游 Agent 当事实继续推理，误差级联放大。AutoGen 0.4+ 引入了结构化消息，但默认仍是自由文本。变现上 AutoGen 适合做"多智能体协作"主题训练营，但务必搭配返回值 schema 强约束演示，否则学员做出的 Demo 看着能跑、数据全是错的。

### 6. MCP：isError 是唯一的语义信号

MCP（Model Context Protocol）的 tool result 是 `content` 数组，可塞文本、图片、资源引用，并有一个 `isError` 布尔位。这是六栈里唯一原生区分"成功返回"与"工具报错"的——但仅此而已。

```json
{
  "content": [{"type":"text","text":"{\"amount\":9900}"}],
  "isError": false
}
```

`isError:false` 只表示"工具没抛异常"，挡不住错误伪装——HTTP 200 + `{"ok":false,"error":"余额不足"}` 会被标成 `isError:false`，Agent 照读为成功。分页、量纲、嵌套、字段歧义，MCP 全部不管。它的价值在协议层（统一工具接入），不在语义层（保证读对）。变现上 MCP 是 2026 最热的"工具生态"主题，训练营需求旺盛，但课程必须诚实标注：MCP 解决的是"接得进来"，不是"读得明白"。

## 三、工具返回消费流水线

```text
flowchart TD
    A[Agent 决策调用工具] --> B[strict schema 锁参数出口]
    B --> C[工具执行]
    C --> D[返回值进入上下文]
    D --> E{消费环节: 六式失效}
    E --> F1[字段歧义: total/subtotal]
    E --> F2[量纲丢失: 分/元]
    E --> F3[空值三态: null/[]/error]
    E --> F4[分页截断: has_more 被忽略]
    E --> F5[错误伪装: 200+ok:false]
    E --> F6[嵌套迷失: 读错层级]
    F1 --> G[错误答案输出]
    F2 --> G
    F3 --> G
    F4 --> G
    F5 --> G
    F6 --> G
    style B fill:#cfe8cf
    style E fill:#f5d5d5
    style G fill:#f5d5d5
```

整条流水线里，绿色只有"参数出口"一处被锁死，红色"消费环节"是六式失效的集中爆发区，而行业默认这里"模型自己会读对"。

## 四、推荐方案

**单工具、强类型场景**：首选 Pydantic AI/Instructor，在工具函数内 `model_validate` 把量纲、空值三态在源头消解，模型拿到的就是干净对象。这是当前唯一能逼近"读对"的路径。

**多工具、协议统一场景**：MCP 接入 + 每个工具配 Pydantic 转换层。MCP 管"接得进来"，Pydantic 管"读得明白"，两层分工。

**对话型、轻量场景**：Anthropic tool_use 即可，但务必在系统提示里植入返回值自校验清单（"金额需确认单位""空数组需区分无结果与查询失败""200 不等于成功"）。

**生产 RAG + 工具链**：LangGraph + 自定义分页跟随节点 + isError 显式传播。不要指望 OutputParser 解决语义，它只解决形状。

**通用兜底**：无论哪栈，都应在工具返回后插入一个"返回值契约"提示——用自然语言把字段语义、单位、空值含义、分页信号、错误判定规则写死，让模型在读取前先对齐契约。这是六栈都缺失、但人人都能补上的一层。Strict schema 解决了"调对"，返回值契约才能解决"读对"——前者是平台的胜利，后者是工程师的战场。

## 参考来源
1. [Structured outputs - Claude Platform Docs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) — Anthropic 约束解码式结构化输出的官方机制与限制说明
2. [Structured output - LangChain Docs](https://docs.langchain.com/oss/python/langchain/structured-output) — LangChain 工具返回值与 OutputParser 的解析链路
3. [Pydantic AI vs LangChain vs instructor: structured LLM outputs compared](https://dev.to/ayinedjimi-consultants/pydantic-ai-vs-langchain-vs-instructor-structured-llm-outputs-compared-13dg) — 三栈结构化输出能力与类型安全对比
4. [Handling Tool Calls in the Claude Agent SDK - The Complete Loop](https://team400.ai/blog/2026-04-claude-agent-sdk-handle-tool-calls) — tool_use/tool_result 内容块的解析与回填实践
5. [Python AI Agent Libraries: Pydantic vs Instructor vs Smolagents](https://jangwook.net/en/blog/en/python-ai-agent-library-comparison-2026/) — 2026 主流 Agent 库结构化输出与工具设计基准对比

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
