为什么 Structured Output 会变成 Agent 的基础设施?

专栏:AI Agent 开发|12

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 前面我们已经分别学过 Structured Output、Tool Calling 和 Tool 选择。现在把视角拉高:当模型输出不再只给人看,而是要交给下一段代码继续执行时,为什么"结构化契约"会越来越重要?这一篇只建立这层工程认识,不提前把 Planning、Memory、Multi-Agent 的具体算法讲完。 |

一、先看一个 Agent 为什么会被"自由文本"卡住

假设模型给出计划:

|--------------------------------------------|
| 第一步,查询北京天气。 第二步,查看路况。 第三步,如果下雨且拥堵,就建议改乘地铁。 |

人一眼就懂。

但如果 Runtime 真要执行,它马上会问:

• 第一步调用哪个 Tool?

• 参数字段叫什么?

• 第二步依赖第一步吗?

• "下雨且拥堵"这个条件要怎样被代码判断?

如果只能靠正则、关键字和字符串切分把这些信息"抠"出来,模型稍微换一种说法,程序就可能失效。

图 1 自由文本适合表达,结构化对象适合成为程序之间稳定的接口

二、Structured Output 真正改变的,是 LLM 和代码之间的边界

上一篇 08 已经讲过:Structured Output 可以让模型返回符合约定结构的数据。

到了 Agent 里,这件事的重要性会放大,因为模型输出经常不是终点,而是下一步程序的输入。

例如:

复制代码
{
  "action": "get_weather",
  "arguments": {
    "city": "北京"
  }
}

Runtime 不需要猜"模型想调用什么",直接读取字段、校验,然后决定是否执行。

所以"基础设施"这几个字,不是说 Agent 的每一块都必须输出 JSON,而是:

凡是 LLM 输出要跨过边界进入程序逻辑,最好先有一份明确、可验证的数据契约。

三、哪些地方最明显地需要结构化契约

图 2 Tool、Plan、State、Agent Message、Memory Metadata 都可能需要结构化,但它们的来源和实现并不完全相同

1. Tool Call:模型输出动作和参数

这是最直观的一类。`name + arguments` 如果没有固定结构,Runtime 就无法可靠分发。Tool Calling 本身已经把这一层做成了结构化接口。

2. Planning:当计划需要被程序执行

如果计划只是展示给用户,自由文本完全可以。

但如果程序要按步骤执行、记录依赖和进度,那么这种结构更有用:

复制代码
{
  "steps": [
    {
      "id": 1,
      "action": "get_weather",
      "depends_on": []
    },
    {
      "id": 2,
      "action": "check_traffic",
      "depends_on": []
    }
  ]
}

重点不是"Plan 必须是 JSON",而是只要 Plan 要进入程序编排,它就需要机器可读的结构。

3. State:状态本身应该有数据模型

这里要和 Structured Output 区分一下。State 不一定由模型生成,所以它不是天然等于"模型结构化输出"。

但任务状态要保存、恢复、检查点续跑时,程序通常仍需要稳定的数据模型,例如:

复制代码
{
  "run_id": "run_123",
  "status": "waiting_approval",
  "current_step": 3
}

4. Multi-Agent Message:跨组件传递任务时要减少歧义

如果 Agent A 给 Agent B 发送的是一段散文,B 还要重新理解"到底让我干什么"。

对于要自动路由的消息,`type / payload / status` 这类字段会更清晰。

但多 Agent 协议具体怎么设计留到后面的专题,这一篇只建立"跨边界需要契约"的直觉。

5. Memory:正文不一定结构化,元数据常常需要

Memory 也不能简单写成"长期记忆必须 JSON"。

一段用户经历、网页摘要、会议记录完全可以保存成文本;但为了筛选和检索,往往还会配结构化元数据:

复制代码
{
  "user_id": "u_42",
  "created_at": "2026-09-05T15:30:00+08:00",
  "tags": ["travel", "preference"],
  "text": "用户更喜欢早班航班"
}

四、为什么"结构正确"仍然不等于"系统可以执行"

图 3 结构化输出只是第一道门;后面仍然有业务规则、权限和真实动作

假设模型严格返回:

|----------------------------------------------------------------------|
| { "action": "transfer_money", "amount": 1000000, "currency": "CNY" } |

字段齐全、类型正确,并不意味着这笔转账应该执行。

Runtime 仍然要检查:

• amount 是否超过业务上限。

• 用户是否有权限。

• 账户是否存在、余额是否足够。

• 是否需要人工确认。

所以 Structured Output 提高的是**接口可靠性和可验证性**,不是把模型输出自动变成可信事实。

五、一个最小例子:让"计划"真正变成程序对象

Python 可以先用 Pydantic 定义一份简单契约:

复制代码
from typing import Literal
from pydantic import BaseModel

class Step(BaseModel):
    id: int
    action: Literal["get_weather", "check_traffic"]
    city: str

class Plan(BaseModel):
    steps: list[Step]

模型输出拿回来以后:

复制代码
raw = {
    "steps": [
        {"id": 1, "action": "get_weather", "city": "北京"},
        {"id": 2, "action": "check_traffic", "city": "北京"},
    ]
}

plan = Plan.model_validate(raw)

for step in plan.steps:
    print(step.id, step.action, step.city)

这段代码最重要的不是 Pydantic,而是:下游代码终于不再处理"某段话",而是在处理有类型、有字段的对象。

六、Structured Output、JSON Mode、Function Calling 不要再次混在一起

|----------------------------|---------------------------|--------------------|
| 能力 | 主要解决什么 | 在 Agent 里的典型位置 |
| 普通 JSON 提示 | 让模型尽量按 JSON 写 | Demo / 低要求场景 |
| JSON Mode | 保证有效 JSON(具体能力看 Provider) | 需要 JSON 语法正确 |
| Schema 级 Structured Output | 约束模型输出结构 | Plan、分类结果、结构化业务结果 |
| Function / Tool Calling | 生成 Tool Call 与 arguments | 动作选择与工具参数 |

它们都可能产生结构化数据,但用途不同。

不要因为这一篇说"结构化契约是基础设施",就把所有问题都强行塞进同一个 `response_format`。

七、契约设计应该"够用",而不是越复杂越专业

原稿把 enum、required、版本、重试、线程安全全部一次塞进来,会让初学者误以为契约越严越好。

真正实用的原则是:

• 只定义下游真的要用的字段。

• 字段含义要清楚,不要同一个 `status` 在不同模块里代表不同东西。

• 枚举适合有限集合,但不要为了枚举而枚举。

• 可选字段就明确可选,不要全设 required。

• 契约变化以后,调用方和消费方要一起考虑兼容性。

如果一份 Schema 已经复杂到没人敢改,它就从"基础设施"变成新的负担。

八、失败以后是不是一定"回喂模型重试"

不一定。

如果目标 Provider 已经提供严格 Schema 约束,很多结构错误本来就能在生成阶段减少。

即便出现失败,也要分情况:

• 结构错误:可以重新生成或换降级策略。

• 业务值错误:应该由业务校验处理,不一定让模型重试。

• 权限拒绝:应停止执行,而不是鼓励模型"换个参数再试"。

• 安全拒答:不能当普通解析失败无限重试。

错误处理属于 Runtime 策略,后面会单独深入。

九、为什么这会影响 Eval、Tracing 和版本演进

一旦边界有结构,就更容易测试。

例如 Tool Call 可以检查:

|-------------------------------------------------------------------------|
| expected_action = "get_weather" assert result.action == expected_action |

Tracing 也可以记录:

• 本轮 action 是什么。

• 参数是多少。

• 哪一步校验失败。

• 哪个版本的 Schema 产生了结果。

自由文本当然也能记录,但结构化字段让统计和回归更直接。

十、这一篇最重要的边界

|----------------------------------------------------------------------------------------------------|
| Structured Output 是 LLM → 程序边界上的重要基础设施;"结构化数据契约"则是更大的软件工程概念。State、数据库记录、消息协议等可以本来就由代码定义,不需要都让模型生成。 |

把这两个层次分开,后面的 Agent 架构就不会变成"所有东西都套一个 JSON Schema"的机械设计。

十一、几个常见误区

• 误区 1:程序完全不能处理自由文本。程序当然可以处理文本,只是自由文本不适合作为需要稳定字段语义的自动化接口。

• 误区 2:所有 Agent 能力都必须是 Structured Output。只有 LLM 生成的结构化边界才属于 Structured Output;其他模块也可以使用普通数据模型。

• 误区 3:Schema 越严格越安全。结构约束不能代替权限、安全和业务校验。

• 误区 4:Memory 必须全部存成 JSON。文本正文 + 结构化元数据是很常见的组合。

• 误区 5:结构化输出成功就代表计划一定合理。它只说明数据形状符合契约。

十二、下一篇

下一篇 13《从零写第一个 Agent Loop》会把前面 06~12 的知识真正串起来:Context、模型调用、Structured Output、Tool Call、Tool Result 和停止条件,最终放进一个可以跑起来的最小循环。

相关推荐
南京兴帝文化传媒有限公司3 小时前
本地生活服务商户GEO优化技术实践:AI大模型收录机制与地图POI权重算法拆解
大数据·人工智能·算法·生活·geo优化实操·csdn运营技巧·ai内容收录
2601_962293533 小时前
人工智能 & 神经网络完整入门路线(零基础可走,分阶段)
人工智能·python·深度学习·神经网络·机器学习
平原20183 小时前
AI 鞋履设计升级:一张主图与多角度详情图的生成流程
人工智能
人工智能时代 准备好了吗3 小时前
品牌更名后,旧名称与新名称的AI表现如何连续观察?
人工智能
yume_sibai3 小时前
02-Flutter进阶开发
前端·flutter
慧一居士3 小时前
QoderWork 和 QoderWake 区别和使用场景对比
人工智能
余槐i3 小时前
使用Ollama本地部署和测试Kimi、GLM等多款大语言模型实战
人工智能·语言模型·自然语言处理·大模型·api
拓海SEO外贸4 小时前
关键词聚类(Keyword Clustering)怎么做
人工智能·机器学习·聚类
a1117764 小时前
莱茵生命终端 网页 html
前端·开源
“AI国潮设计-小江”4 小时前
【Python/SDXL实战】潮汕国潮IP视觉落地:普宁英歌舞猫IP & 创意甜品设计(附ComfyUI工作流与商业授权说明)
开发语言·人工智能·python·prompt·aigc