03.05概念更新于 2026年9月17日

输出契约

让下游系统可以消费的格式、字段与异常状态约定。

03.05 · 课时讲解

输出契约

Agent 与下游消费者之间的正式约定:结构、含义、状态与版本。

1 / 11

一句话理解:输出契约是 Agent 与下游消费者之间的正式约定:交付什么结构、每个字段是什么含义、成功与各种“不成功”分别怎么表示、版本如何演进;它让 Agent 的结果可以被界面、系统和其他 Agent 可靠地使用。

Demo 阶段,Agent 的输出只给开发者自己看,写成一段文字完全没问题。进入生产后,同一份输出可能同时被四方消费:客服工作台要展示结论和话术,工单系统要读取处置动作,监控看板要统计退款原因分布,另一个“退款审批 Agent”要接着处理。任何一方理解错一个字段,都会出问题。

更隐蔽的风险来自“不成功”的情况:查不到数据、工具超时、需要人工介入。如果这些情况没有被约定,Agent 可能把“系统暂时无法获取支付状态”写成一句话塞进 reason,而下游程序会把它当作一个正常结论记录下来。

结构化输出 解决的是“格式能不能被解析”,输出契约解决的是“格式背后的含义是否被双方一致理解”。

一份输出同时被多方消费,契约就站在 Agent 与它们之间:

输出契约位于 Agent 与四个下游消费者之间:客服工作台、工单系统、监控看板和退款审批 Agent 退款 Agent 产出 JSON 结果 输出契约 v1.2 结构与字段 状态语义 人读 / 机读分离 版本与校验 客服工作台 结论、证据与话术 工单系统 actions、need_human_review 监控看板 退款原因分布、状态统计 退款审批 Agent 接着处理审批
图 1 · 一份输出、多方消费:契约是 Agent 与下游之间的正式约定

契约包含什么

要素 说明 常见遗漏
消费者 谁在使用这份输出,各自需要哪些字段 只考虑界面,忘了统计和审计
结构与字段 字段名、类型、是否必填、取值范围 字段含义只存在于开发者脑子里
枚举值 每个枚举值的精确定义与判定标准 新增枚举值时没有通知下游
状态语义 成功、部分成功、需要补充信息、无法完成、需人工介入分别如何表示 只定义了成功时的结构
人读与机读的分离 给人看的文本与给程序用的字段分开 程序去解析一段自然语言
版本 契约版本号、兼容策略、变更流程 悄悄改字段导致下游报错
校验与处理 不符合契约时谁来处理、如何处理 默认“模型不会出错”

客服退款 Agent 的输出契约

下面是一份可以直接放进需求文档的契约说明。它在 一份好指令的构成 中输出格式的基础上,补充了版本号、缺失信息和执行故障状态,这也是真实项目里契约通常的演进方式;采用这份契约时,指令中的输出格式要同步更新。

字段定义:

字段 类型 必填 含义与规则 消费者
schema_version string 是 契约版本,当前为 "1.2" 所有
status enum 是 本次任务的完成状态,见下方状态语义 所有
order_no string 是 必须与输入订单号一致 工单系统、审计
refund_decision enum 是 符合退款政策、部分符合、不符合、需要补充证据、无法判断 看板、工单系统
reason string 是 一句话判断依据;状态非 resolved 时说明缺少什么 客服工作台
refund_amount number 或 null 是 建议退款金额(元),不得超过可退余额;无法确定时为 null 工单系统、财务
evidence array 是 每项含 source(工具名)、time、detail;无证据时为空数组 客服工作台、审计
actions array 是 每项含 owner 枚举、action、system;无动作时为空数组 工单系统
customer_message string 或 null 是 客户话术,不含到账时间与审批结果承诺;不适合联系客户时为 null 客服工作台
missing_info array 是 缺失或获取失败的数据项,例如 payment_status 客服工作台、监控
need_human_review boolean 是 是否需要人工复核 工单系统
escalation_reason string 或 null 是 需要复核的原因 主管工作台

状态语义与下游处理:

status 含义 客服工作台 工单系统
resolved 已作出退款判定,给出可执行方案 展示结论、证据与话术 按 actions 生成待确认任务
need_more_info 需要用户补充信息才能继续(如缺订单号、质量问题照片) 展示需要补充的内容 不创建任务
unable_to_confirm 已尽力调查,但数据不足或矛盾,无法得出结论 展示已知部分与 missing_info 创建“人工排查”任务
tool_failure 关键数据源不可用,调查无法进行 提示稍后重试 记录故障,触发监控告警

注意 业务结果 与 执行故障 被区分开了:unable_to_confirm 是 Agent 正常工作后得出的“查不清”,而 tool_failure 是系统问题。混在一起的话,监控看板上“无法判断率上升”到底是业务变复杂了还是某个接口挂了,就无从分辨。

示例:数据不足时的输出

{
  "schema_version": "1.2",
  "status": "unable_to_confirm",
  "order_no": "A-10231",
  "refund_decision": "无法判断",
  "reason": "订单签收 3 天,仍在无理由退款时效内,但支付记录与已退金额暂时无法获取,无法计算可退金额",
  "refund_amount": null,
  "evidence": [
    {"source": "get_order", "time": "2026-09-14 08:14", "detail": "订单状态为已签收,实付 268 元(示例数字)"}
  ],
  "actions": [
    {"owner": "客服", "action": "人工核实该订单的历史退款记录", "system": "支付管理后台"}
  ],
  "customer_message": "您好,我们正在核实这笔订单的支付与退款记录,已提交相关团队加急排查,有结果后会尽快联系您。",
  "missing_info": ["payment_status", "refunded_amount"],
  "need_human_review": true,
  "escalation_reason": "关键数据获取失败,无法完成判定"
}

契约如何落地:四层保障

契约不是写在文档里就生效的,需要在四个位置同时落实:

输出契约的四层保障:指令说明含义,结构化输出强制结构,程序校验 Schema 表达不了的规则,契约测试回归 ① 指令 说明字段含义、状态判定标准、人读字段的写作要求 ② 结构化输出 JSON Schema 强制结构、类型、必填与枚举 ③ 程序校验 Schema 表达不了的规则 · order_no 与输入一致 · resolved 时 evidence 不能为空 · 需复核时 escalation_reason 不能为空 · refund_amount ≤ 实付 − 已退 · 话术中不出现到账时间承诺 不通过 重试,或降级为 unable_to_confirm ④ 契约测试 每个状态都有用例;改指令、模型或契约后回归
图 2 · 契约落地的四层保障:任何一层都不能单独承担

JSON Schema 部分的写法示意(节选,完整结构按上表补齐):

{
  "type": "object",
  "properties": {
    "schema_version": {"type": "string", "enum": ["1.2"]},
    "status": {"type": "string", "enum": ["resolved", "need_more_info", "unable_to_confirm", "tool_failure"]},
    "customer_message": {"anyOf": [{"type": "string"}, {"type": "null"}]},
    "missing_info": {"type": "array", "items": {"type": "string"}},
    "need_human_review": {"type": "boolean"}
  },
  "required": ["schema_version", "status", "customer_message", "missing_info", "need_human_review"],
  "additionalProperties": false
}

Schema 支持的特性各家不同,例如 Claude 的结构化输出不支持字符串长度和数值范围约束,这些规则要放到第 3 层由程序保证。可空字段、anyOf 等写法也请以所用平台的文档为准并实际测试。

契约设计的几条经验

  • 每个状态都要让消费者知道“下一步做什么”。 如果一个状态出现后下游不知道怎么处理,说明这个状态定义得不对。
  • 所有字段都必填,用空数组和 null 表达“没有”。 下游不必区分“字段不存在”和“字段为空”,逻辑更简单。
  • 人读字段和机读字段分开。 customer_message 给人看,actions 与 refund_amount 给系统用。不要让程序去解析一段话术,也不要让客服去读 JSON。
  • 枚举值是契约中最敏感的部分。 新增、删除或改名任何一个枚举值,都要按版本变更处理,并通知所有消费者。
  • 版本号从第一天就加上。 兼容的变更(例如新增一个可选的展示字段)升小版本;不兼容的变更(改字段含义、删枚举值)升大版本,并给下游留出迁移期。

常见误区

  • “有了 JSON Schema 就有了契约。” Schema 只描述结构,不描述含义、状态语义和处理方式。契约需要文档、校验和测试共同构成。
  • “出错时让 Agent 在文字里说明就行。” 自然语言的错误说明无法被程序识别和统计,必须用状态和结构化字段表达。
  • “字段越多,信息越全,下游越方便。” 每个字段都增加模型出错的机会和维护成本。只保留有明确消费者的字段。

设计要点

  • 列出所有消费者,以及他们各自依赖的字段。
  • 为成功、需补充信息、无法完成、执行故障分别定义状态,并写明下游处理方式。
  • 区分业务结果与系统故障,区分人读字段与机读字段。
  • 在指令、Schema、程序校验、契约测试四层落实契约。
  • 为契约加版本号,并建立变更评审流程。

练习

为「销售分析 Agent」设计一份输出契约(约 45 分钟):

  1. 场景:每周一自动分析上周销售数据,结果同时发给区域经理(阅读)、补货系统(自动创建补货建议)和经营看板(统计)。
  2. 列出三个消费者分别需要的字段,合并成一份字段定义表(类型、必填、含义、消费者)。
  3. 至少定义 4 个状态,包括“数据延迟未就绪”和“发现异常但无法归因”,并写出每个状态下三个消费者的处理方式。
  4. 写出 3 条 Schema 无法表达、需要程序校验的规则。

延伸阅读

下一步

指令、示例、规则和契约都有了,但它们一定会不断修改。最后一课「Prompt 的迭代与版本管理」讲如何用测试集驱动修改,避免“改好一个、坏掉一片”。

试试:上下文、MCP、沙箱、Prompt 注入、外链