Skip to content

08. LLM 协议、消息与工具交互

版本:v1.2

最后更新:2026-07-09

适用对象:需要把 LLM 从“会聊天的模型”升级成“可接入产品、工作流、Agent、工具网关与企业平台的稳定接口”的产品、研发、架构、平台与安全同学

很多团队最开始做大模型时,关注点往往是:

  • prompt 怎么写
  • 模型怎么选
  • 温度怎么调

但系统一旦进入真实生产,最先暴露问题的常常不是这些,而是协议层。

典型症状包括:

  • 同一个任务换一次模板后,输出字段名就漂了
  • 工具 schema 改了,模型还按旧参数生成
  • 多轮续接时把旧摘要、旧工具结果和新用户意图混成一锅
  • 用户输入覆盖了系统规则
  • 工具失败后没有统一闭环,只能靠模型“自己兜”
  • 一部分链路靠自然语言,一部分链路靠 JSON,一部分链路靠人工猜语义

这些问题的共同点不是“模型不聪明”,而是:

  • 系统缺少稳定的消息与动作契约

这页里的“协议”不是狭义网络传输协议,而是:

  • 指令如何表达
  • 消息如何分层
  • 上下文如何拼装
  • 输出如何约束
  • 工具如何调用与回填
  • 状态如何续接
  • 审批、回放、审计如何闭环

1. 为什么协议层会决定系统上限

如果你把 LLM 只当成单轮文本生成器,那么一个 prompt 往往就够了。

但一旦系统里出现下面任何一项,prompt 就不再是唯一接口:

  • 多轮会话
  • 结构化输出
  • 工具调用
  • 检索增强
  • 审批节点
  • 人工接管
  • 长任务恢复
  • 流式事件
  • 跨系统交接

因为此时你维护的已经不是“一次回答”,而是一条状态链路。

一句话理解:

  • Prompt 解决的是模型能不能理解你,协议解决的是系统能不能稳定承接模型。

2. 先分清三类“协议”

2.1 模型调用协议

这一层关心一次模型调用怎么稳:

  • 指令放在哪
  • 用户输入如何表达
  • 检索结果如何注入
  • 输出如何约束
  • 工具结果如何回填
  • 下一轮如何续接

这是本页主线。

2.2 工具与 Agent 生态协议

这一层关心系统之间怎么接:

  • MCP
  • A2A
  • 远程工具 / 连接器
  • tool discovery
  • prompt / resource 暴露方式

它解决的是:

  • 外部能力如何标准化暴露给模型系统

2.3 组织与治理协议

这一层关心的是运行规则:

  • 哪些动作必须审批
  • 谁能执行写操作
  • 哪些失败允许自动重试
  • 哪些失败必须人工接管
  • 哪些操作必须留痕

很多团队没有把这层写进 API 文档,但线上恰恰最容易在这里出事故。


3. 2026 年看 LLM 协议,为什么不能只盯着“messages 数组”

过去很多工程实践会把“协议”近似理解成:

  • 一个 messages 数组
  • 里面放若干 system / user / assistant
  • 最后拿到一段文本

这个理解在简单聊天任务里够用,但在现在的模型系统里已经不够了。

以 OpenAI 当前官方文档为例,协议不再只是一段历史文本,而是围绕:

  • instructions
  • 输入项 input
  • 输出项 output
  • function_call
  • function_call_output
  • previous_response_id
  • conversation
  • built-in tools
  • remote MCP

构成完整回路。

Anthropic 这边强调的是:

  • content blocks
  • tool_use
  • tool_result
  • stop_reason
  • input_schema

Gemini 当前则把协议重点放在:

  • Interactions API
  • previous_interaction_id
  • stateful mode
  • stateless mode
  • thought signatures
  • structured output 与 function calling 组合

所以今天更准确的说法应该是:

  • LLM 协议已经从“消息数组格式”演进成“状态化的 typed items + tools + control flow 契约”。

4. 一个成熟的 LLM 协议通常包含哪 7 层 contract

4.1 Instruction contract

这一层负责长期稳定规则:

  • 系统目标
  • 安全边界
  • 风格与语气
  • 拒答条件
  • 工具使用原则
  • 输出约束

关键原则:

  • 长期稳定规则不要和用户输入混写
  • 规则要能单独版本化
  • 规则要能灰度替换

如果你把全部内容都揉成一大段 prompt,后面很难回答:

  • 是规则改坏了
  • 还是用户输入本身就有问题

4.2 Message contract

消息层要回答:

  • 哪些内容是规则
  • 哪些内容是用户输入
  • 哪些内容是检索证据
  • 哪些内容是工具结果
  • 哪些内容是中间状态摘要

如果这些边界不清,模型就会:

  • 把不可信输入当命令
  • 把旧工具结果当新事实
  • 把错误摘要继续传下去

4.3 Context contract

上下文层不是“能塞多少塞多少”,而是明确:

  • 哪些信息必须带
  • 哪些只在某类任务出现
  • 哪些过期后必须丢弃
  • 哪些要摘要而不是原样回放

上下文 contract 的核心目标是:

  • 让模型看到当前任务真正需要的上下文,而不是历史垃圾全集

4.4 Output contract

输出层至少要回答三件事:

  1. 输出给人看,还是给系统消费
  2. 输出是自然语言、JSON,还是严格 schema
  3. 失败时返回什么格式

如果这层不提前收紧,后面就会出现:

  • 文本看着通顺,但下游没法解析
  • 字段名一会儿叫 category,一会儿叫 type
  • 应该拒绝时却给了半截建议

4.5 Tool contract

工具层 contract 关心的不是“模型会不会调”,而是:

  • 什么时候该调
  • 什么时候不要调
  • 哪些参数必填
  • 参数约束是什么
  • 是否允许并行
  • 是否需要审批
  • 失败后如何回传

工具定义越松,模型越容易“看起来像在做事,实际上在乱猜”。

4.6 State contract

状态层是最容易被忽略、也是最容易把系统做乱的一层。

你必须定义:

  • 哪些中间产物只在本轮有效
  • 哪些对象会写入长期状态
  • 哪些内容可以被摘要
  • 下一轮续接依赖什么对象
  • 工具失败后状态如何回滚

如果没有这层,很多多轮 Agent 最终都会变成:

  • 历史越跑越脏
  • 工具结果越堆越重
  • 摘要越来越错

4.7 Approval and audit contract

到了生产环境,很多动作不能只看模型输出本身。

还要明确:

  • 哪些动作需要人工确认
  • 审批面板展示哪些证据
  • 审批后如何恢复执行
  • 谁执行了什么动作
  • 哪个模型版本给出的建议
  • 用了哪些上下文与工具

这已经超出“调用模型”本身,但绝对属于完整协议的一部分。


5. 把一次完整模型调用看成“受控回路”

一条更接近生产的最小调用回路通常是:

text
User Input
 -> Input Classification
 -> Instruction Contract
 -> Context Assembly
 -> Tool Definitions
 -> Model Response
 -> Validate Output / Tool Args
 -> Execute Tool or Request Approval
 -> Append Tool Result / Update State
 -> Continue or Terminate

关键不是流程图本身,而是:

  • 每一步都必须有明确 contract

5.1 用户输入进入前先做边界分离

不要让用户输入直接和系统规则拼成一个字符串。

至少要分清:

  • 用户意图
  • 系统规则
  • 检索证据
  • 工具结果
  • 状态摘要

5.2 模型输出后先校验,再执行动作

尤其是工具调用,顺序应当是:

  1. 模型给出工具名和参数
  2. 应用侧校验 schema、权限、审批条件、幂等性
  3. 再决定是否执行

而不是:

  • 模型一出参数就真的去改库、发消息、扣款

5.3 工具结果回填要带语义标签

不要把工具返回的原始文本直接扔回历史。

至少要标明:

  • 来自哪个工具
  • 哪次调用
  • 成功还是失败
  • 是否可信
  • 是否已经写入业务系统

5.4 终止条件要提前定义

完整链路必须定义:

  • 什么叫任务完成
  • 什么叫需要继续调工具
  • 什么叫需要人工接管
  • 什么叫中止并回滚

否则系统很容易:

  • 工具失败后无限重试
  • 证据不足时继续编
  • 明明该停却一直多说

6. 为什么“写好一个 prompt”在生产里一定不够

很多团队初期做法是:

  • 全部规则写进一段 prompt
  • 检索内容粘进去
  • 工具结果再拼进去
  • 让模型“自己理解”

这套做法不是不能跑,而是后期不可维护。

6.1 不利于版本管理

你很难回答:

  • 这次退化是规则变了,还是检索变了
  • 是 schema 变了,还是工具说明变了

6.2 不利于安全边界

用户输入、系统规则、工具结果混写后,模型更难区分:

  • 哪些是命令
  • 哪些是数据
  • 哪些是不可信内容

6.3 不利于重放与回归

没有协议分层时,复现一次问题通常非常痛苦,因为你很难重建:

  • 当时的规则版本
  • 当时的上下文拼装
  • 当时的工具定义
  • 当时的响应类型

7. OpenAI 2026 的协议心智模型:Responses 不是 Chat Completions 的简单换壳

根据当前 OpenAI 官方文档,Responses API 的设计重点已经不只是“给模型一组消息”,而是:

  • 让输入项与输出项 typed 化
  • 让工具调用成为协议内第一类对象
  • 让跨轮续接可以通过 previous_response_idconversation 完成
  • 让结构化输出和工具约束直接进入协议层
  • 让 built-in tools 与 remote MCP 被统一纳入模型调用回路

这带来一个重要工程变化:

  • 应用侧不应该再把“文本拼接”当成唯一协议模型

7.1 instructions 是长期规则入口

OpenAI 当前文档把稳定指令放在 instructions,而不是让你把所有规则都塞进用户输入。

工程上更好的分工是:

  • instructions 放稳定规则
  • input 放任务输入和上下文项
  • tools 放可调用能力
  • text.format 或 schema 放输出约束

7.2 input 更适合承载 typed content,而不是一段“巨型提示词”

OpenAI 现在支持把输入组织成更结构化的 content items。

它带来的最大价值不是“写法变多”,而是:

  • 你可以更清楚地区分规则、用户数据、工具结果和多模态输入

7.3 output 不再只有一段字符串

一次响应里可能出现:

  • 最终文本
  • reasoning 相关项
  • function_call
  • built-in tool 项
  • 结构化输出结果

所以应用侧解析响应时,不应该只盯着:

  • output_text

而应该先问:

  • 这次输出里有没有动作项
  • 有没有必须处理的 tool call
  • 有没有需要回填的状态对象

8. OpenAI 的多轮续接:previous_response_id、Conversations 和显式状态保留

8.1 previous_response_id 适合轻量续接

OpenAI 当前官方文档里,previous_response_id 是很关键的轻量续接能力。

它更像是:

  • “沿着上一次响应继续往下走”

适合:

  • 继续处理同一任务
  • 回填工具结果后续跑
  • 简化短链路多轮续接

8.2 Conversations API 适合需要服务端管理状态的场景

如果你的系统需要更持久的多轮状态管理,Conversations API 会更合适。

它解决的是:

  • 历史不想每轮都由客户端手工回放
  • 希望服务端帮助维护对话对象
  • 需要更稳的状态边界

8.3 Stateless 模式下必须考虑 reasoning item 如何保留

OpenAI 文档明确提到:

  • 如果你在 stateless 方式下自行管理历史,需要显式处理 reasoning 相关项

尤其在需要跨轮保留推理上下文时,官方当前文档要求:

  • reasoning.encrypted_content 放回后续请求

这意味着:

  • “续接”并不只是把上一轮文本贴回去
  • 还包括协议对象级别的显式续接

8.4 不要把“多轮历史”误解成“全量文本回放”

真正有工程价值的续接,一定要区分:

  • 对话历史
  • 工具轨迹
  • 持久业务状态
  • 可丢弃临时上下文
  • 可回放但不必每轮携带的原始证据

9. Anthropic 的协议重点:content blockstool_usetool_resultstop_reason

Anthropic 的公开文档里,有几个协议点尤其值得借鉴。

9.1 它把消息内容明确拆成 content blocks

这有一个很重要的工程启发:

  • 不同类型内容不应该混成一大段自然语言

如果一个协议天然支持 typed content,应用侧就更容易:

  • 识别哪部分是文本
  • 哪部分是工具动作
  • 哪部分是图像或文档输入

9.2 tool_use 明确告诉你模型想执行动作

Anthropic 并不是让你从自然语言里猜“模型是不是想调工具”,而是通过 typed block 显式表达:

  • 这一步模型请求了哪个工具
  • 参数是什么

这对应用侧来说非常关键,因为可以:

  • 先校验
  • 再审批
  • 再执行

9.3 tool_result 强调工具往返也是消息协议的一部分

很多团队会把工具结果当“旁路数据”塞回 prompt。

Anthropic 的做法提醒我们:

  • 工具结果本身也应当是协议内的一等输入

9.4 stop_reason 不是日志字段,而是控制流字段

Anthropic 官方文档强调要显式处理 stop_reason

对工程系统来说,stop_reason 不是“看看就好”,而是决定:

  • 这轮是否结束
  • 是否应该继续执行工具
  • 是否需要继续补材料
  • 是否应该提示人工接管

如果应用层不显式处理停止原因,实际效果通常是:

  • 模型想调工具,但应用当成最终回答了
  • 模型只是暂停等待输入,应用误以为任务结束

10. Gemini 当前的协议重点:Interactions、stateful/stateless、thought signatures

Gemini 当前文档里,协议设计重点也已经超出传统 messages 模式。

10.1 Interactions API 强调“交互对象”而不是单次文本补全

这体现出的工程思路是:

  • 对话不是若干文本块,而是有状态交互对象

10.2 previous_interaction_id 让续接更加显式

这和 OpenAI 的 previous_response_id 在思路上相近:

  • 下一轮不是重新造上下文,而是显式续接上一个交互对象

10.3 Stateful 与 stateless 必须分开理解

Gemini 当前文档明确区分:

  • stateful mode:服务端管理历史和 thought signatures
  • stateless mode:客户端管理历史,并在后续请求中携带 thought signatures

这个区分特别重要,因为它直接影响:

  • 客户端状态管理复杂度
  • 回放能力
  • 漏传上下文时的退化方式

10.4 Structured outputs 与 function calling 可以组合使用

这意味着你不必在“给系统消费的结构结果”和“给系统执行的动作结果”之间二选一。

更成熟的设计往往是:

  • 最终对用户的解释是自然语言
  • 对系统的路由 / 分类 / 审批建议是结构化输出
  • 对外部系统动作则通过 function calling 触发

11. 今天设计 LLM 协议,最该学到的不是厂商语法,而是共同模式

虽然各家 API 形态不同,但底层共同模式已经越来越清楚:

  1. 指令和用户输入必须分层
  2. 内容最好 typed 化,而不是一段巨型文本
  3. 工具调用必须是显式动作对象,不要从自然语言里猜
  4. 工具结果也是协议内对象,不能只是字符串拼接
  5. 停止原因与状态信号必须进入应用控制流
  6. 多轮续接是对象续接,不只是文本回放
  7. 结构化输出和工具约束必须由 schema 驱动

所以最稳妥的工程心智模型不是:

  • “我适配哪家厂商的字段名”

而是:

  • “我自己的系统 contract 是否已经稳定,再映射到不同厂商协议”

12. 输出协议要尽早从“自然语言”升级到“结构化 contract”

12.1 什么时候继续用自然语言

以下场景仍然适合自然语言:

  • 面向用户展示的最终回答
  • 解释性总结
  • 长文改写
  • 创意表达

12.2 什么时候必须上结构化输出

以下场景建议尽早使用 schema:

  • 分类结果
  • 标签预测
  • 字段抽取
  • 审批建议
  • 风险分级
  • 工作流下一步路由
  • 工具参数生成

原因不是“JSON 更高级”,而是:

  • 系统才能稳定消费

12.3 JSON 不是协议,schema 和语义版本才是协议

很多人把“能输出 JSON”误当成“协议稳定”。

其实还差很远:

  • 字段含义是否固定
  • 枚举值是否固定
  • required 字段是否固定
  • 失败格式是否固定
  • 版本是否固定

更准确地说:

  • JSON 只是载体
  • schema 才是格式 contract
  • 语义版本才是长期兼容 contract

13. OpenAI 的 Structured Outputs 为什么改变了输出协议的设计方式

根据 OpenAI 当前官方文档,Structured Outputs 的核心价值不只是“让模型尽量输出 JSON”,而是:

  • 让输出约束进入协议层
  • 让 schema 与工具定义共享更严格的 contract 思路

13.1 strict: true 是“让模型服从 schema”,不是“让解析器更努力”

在 OpenAI 当前文档里,strict: true 的含义是:

  • 让模型生成结果严格遵守你声明的 schema

工程上这意味着:

  • 你应该把边界写进 schema,而不是继续堆 prompt 描述

13.2 additionalProperties: false 很重要

如果你允许任意额外字段,表面上更灵活,实际上会导致:

  • 下游字段兼容越来越脆
  • 模型偷偷发明新字段
  • 日志和评测越来越难对齐

13.3 required 字段约束能把“半成品输出”拦在协议层

很多业务问题并不是模型“完全不会”,而是:

  • 它给了一个 80 分的半成品

但系统动作通常不能消费 80 分 JSON。

所以:

  • 对动作型输出,宁可严格拒绝,也不要模糊放行

14. 工具调用协议的核心不是“能调”,而是“动作闭环”

一次可靠的工具调用至少要解决这些问题:

  • 该不该调
  • 调哪个
  • 参数怎么填
  • 是否需要追问
  • 是否允许并行
  • 是否需要审批
  • 失败后怎么办
  • 结果如何回填
  • 下一步是继续、终止还是转人工

这说明工具调用协议本质上是:

  • 一个小状态机

而不只是:

  • 一个函数签名

14.1 工具 schema 不是越宽松越好

过宽的 schema 会导致:

  • 模型自由发挥过头
  • 无意义字段变多
  • 隐含必填项经常漏

好 schema 的目标不是“万能”,而是:

  • 最小可用
  • 边界清晰
  • 错误容易暴露

14.2 工具描述必须写“什么时候用、什么时候不要用”

仅写“这是一个查订单的工具”远远不够。

更有效的说明通常包括:

  • 什么时候必须调用
  • 什么时候不要调用
  • 缺少哪些信息时先追问
  • 工具失败时不要瞎编

14.3 失败结果本身也属于协议

工具失败后,不能只回一个:

  • error

更可用的失败 contract 至少要区分:

  • 参数错误
  • 权限错误
  • 暂时性系统错误
  • 资源不存在
  • 需要人工审批

这样应用层才能决定:

  • 是重试
  • 是降级
  • 是追问
  • 还是转人工

15. OpenAI 当前工具回路里最该掌握的几个字段

15.1 function_call

这是模型明确请求执行某个函数工具的动作对象。

应用侧看到后不应该立刻执行,而应当先做:

  • schema 校验
  • 权限校验
  • 审批判断
  • 幂等检查

15.2 call_id

call_id 的重要性在于:

  • 它把“模型发起的这次工具请求”和“你回填的工具结果”稳定关联起来

没有这个关联字段,工具结果一多,系统就很容易回填错对象。

15.3 function_call_output

OpenAI 当前协议要求你把工具执行结果以 function_call_output 形式回传。

这说明:

  • 工具结果不是旁路文本
  • 而是模型协议里的正式输入项

15.4 tool_choice

tool_choice 的价值在于显式控制:

  • 这轮是否允许模型自由选工具
  • 是否强制某个工具
  • 是否根本不允许调用工具

很多线上事故都和这个字段配置过宽有关。

15.5 parallel_tool_calls

OpenAI 当前文档强调:

  • 是否允许并行工具调用需要显式控制

对高风险动作或有顺序依赖的动作,通常应更保守:

  • 宁可串行,也不要让模型自由并发写操作

15.6 allowed_tools

当系统存在大量工具时,限制本轮允许使用的工具集合,往往比写很长的 prompt 更有效。

这相当于把“能做什么”前移到协议层,而不是交给模型自己猜。


16. 为什么 built-in tools、function tools、remote MCP 应该分开设计

OpenAI 当前工具体系已经不只是一种函数调用。

更实用的划分通常是:

  • function tools:你自己定义 schema 和执行器
  • built-in tools:平台原生提供的能力
  • remote MCP / connectors:通过标准接入层暴露的外部能力

这三类工具虽然都叫“tool”,但工程边界不一样。

16.1 function tools 更像“应用内 contract”

你负责:

  • 参数 schema
  • 描述
  • 权限校验
  • 执行逻辑
  • 失败语义
  • 幂等控制

16.2 built-in tools 更像“平台托管能力”

它们更适合:

  • 平台已经提供了明确语义
  • 你希望快速接入
  • 不想自己维护底层实现

但要注意:

  • 平台工具也不意味着可以跳过你的业务审批与审计

16.3 remote MCP 更像“外部能力接入总线”

MCP 的价值不是替代你的应用协议,而是:

  • 用统一接口暴露 tools、resources、prompts

所以更合理的分层是:

  1. 你的业务先定义内部 tool contract
  2. 再决定哪些能力通过 MCP 暴露或接入
  3. 最后再做审批、观测、评测与回放接线

17. 多轮状态续接绝不是“把历史全塞回去”

这是很多 Agent 系统最容易失控的地方。

17.1 至少分清 5 类状态对象

你最好把状态分成:

  1. 原始对话历史
  2. 工具执行轨迹
  3. 持久业务状态
  4. 中间推理 / 思考对象
  5. 摘要与压缩状态

它们不是一回事,也不应该用同一种保留策略。

17.2 持久状态和可丢弃状态必须分开

例如:

  • 用户已确认的地址属于持久业务状态
  • 上一轮工具失败的临时堆栈信息可能只适合短期保留

17.3 摘要不是天然可信的

只要系统开始做上下文压缩,就必须回答:

  • 这是第几轮摘要
  • 从哪些原始项压缩而来
  • 哪些细节被丢掉了
  • 摘要错了时怎样追溯

17.4 恢复链路必须显式

长任务恢复不能依赖:

  • “重新把所有 prompt 再拼一次”

而应依赖:

  • 明确的状态对象
  • 明确的回放边界
  • 明确的幂等与补偿策略

18. 停止原因、终止条件和继续条件都必须进入协议

只要系统有工具、多轮或审批,最终都要回答:

  • 这轮为什么停
  • 是真正结束,还是等待下一步输入
  • 是需要继续调工具,还是要人工接管

18.1 “模型不再输出文本”不等于任务结束

常见错误理解是:

  • 模型输出停止了,所以任务结束了

但实际可能是:

  • 模型在等待工具结果
  • 模型在等待更多输入
  • 模型要求人工审批

18.2 终止条件要可执行,而不是口头约定

更稳妥的终止条件通常要明确:

  • 是否已经满足业务完成定义
  • 是否还存在待处理动作对象
  • 是否存在未决审批
  • 是否存在可重试错误

18.3 “需要继续”也要分类

继续不只一种:

  • 继续推理
  • 继续调工具
  • 继续补证据
  • 继续等待人工

不同继续类型,对应的协议对象也不同。


19. 流式协议不只是“边吐字边显示”,而是事件流

很多团队把 streaming 理解成:

  • 文本一个字一个字出来

但在复杂系统里,流式协议更应该被理解成:

  • 一组按时间到达的 typed events

事件可能包括:

  • 文本增量
  • reasoning 相关项
  • tool call 产生
  • tool result 回填
  • 状态切换
  • 审批请求

这对前后端协作非常重要,因为前端不能只假设:

  • “收到流就一定是在补正文案”

20. 审批协议必须前置,不要等高风险动作快上线才补

很多系统一开始只做“模型出结论”,后来再加:

  • 发邮件
  • 改配置
  • 扣款
  • 写库
  • 对外发消息

这时如果审批协议没有前置设计,常见后果是:

  • 只能在工具执行前临时弹窗
  • 审批后不知道怎么恢复执行
  • 审批记录和模型建议脱节

20.1 审批协议至少要回答 4 件事

  1. 哪些动作进入审批
  2. 审批时给人看什么证据
  3. 审批结果如何回到运行链路
  4. 审批拒绝后系统怎么收口

20.2 审批结果本身也应该是协议对象

不要把审批结果只当作 UI 状态。

它更应该像:

  • 一个可以被回放、审计、评测的状态项

21. 观测、回放和评测为什么属于协议的后半段

协议不是“模型能跑起来”就结束了。

到了生产环境,你必须能解释:

  • 这轮用了什么规则版本
  • 看到了什么上下文
  • 调了哪些工具
  • 参数是什么
  • 哪一步失败了
  • 结果是哪个模型给出的
  • 为什么最终走了某条分支

21.1 没有 protocol-level trace,就很难做真正的回放

如果日志里只留:

  • 最终答案

那么你几乎不可能完成:

  • 事故复盘
  • 评测回放
  • 灰度对比
  • 协议变更验证

21.2 评测不应只看最终文本

更成熟的评测会把这些对象一起纳入:

  • 结构化输出是否合规
  • 工具参数是否正确
  • 停止点是否合理
  • 状态续接是否稳定
  • 审批路由是否正确

也就是说:

  • 协议对象本身就是评测对象

22. 企业里更实用的协议拆法

22.1 Instruction contract 单独版本化

单独维护:

  • 系统规则
  • 安全政策
  • 风格规范
  • 拒答与升级规则

22.2 Context contract 单独版本化

单独维护:

  • 来源
  • 裁剪策略
  • 摘要策略
  • 过期策略
  • 引用策略

22.3 Output contract 单独版本化

单独维护:

  • 自然语言格式
  • JSON Schema
  • 失败格式
  • 版本号

22.4 Tool contract 单独版本化

单独维护:

  • 工具名
  • 参数 schema
  • 描述
  • 并行策略
  • 审批要求
  • 幂等要求
  • 失败类型

22.5 State contract 单独版本化

单独维护:

  • 持久状态对象
  • 临时状态对象
  • 续接方式
  • 回放方式
  • 回滚方式

22.6 Approval contract 单独版本化

单独维护:

  • 审批触发条件
  • 证据面板字段
  • 审批结果类型
  • 审批后恢复策略

23. 一份更适合生产的最小协议清单

如果系统已经准备走生产,最少建议补齐这些内容:

  1. 输入分层:规则、用户输入、检索证据、工具结果、状态摘要分开
  2. 输出分层:用户可读输出与系统可消费输出分开
  3. schema 校验:结构化输出和工具入参都做严格校验
  4. 工具闭环:调用、审批、执行、回填、补偿路径明确
  5. 状态续接:明确历史、摘要、持久状态的边界
  6. 终止条件:完成、等待、失败、人工接管分类明确
  7. 幂等与追踪:请求 ID、tool call ID、审批 ID、trace ID 明确
  8. 留痕审计:模型版本、上下文版本、工具版本、审批动作可回放
  9. 版本管理:instruction、schema、tool、context template 分开版本化
  10. 评测接线:协议对象而不是只有最终文本进入 eval

24. 一个推荐的“协议对象”心智模型

你可以把一次复杂任务拆成下面几类对象:

  • instruction_set
  • user_request
  • context_bundle
  • tool_definitions
  • model_output_items
  • tool_execution_records
  • approval_records
  • state_snapshot
  • trace_events

这个做法的好处是:

  • 你先有内部稳定对象,再映射到 OpenAI / Anthropic / Gemini / MCP

而不是一开始就把业务逻辑绑死在某家 SDK 的字段命名上。


25. 常见反模式

25.1 把规则和数据混写

后果包括:

  • 用户输入更容易污染系统规则
  • 安全边界不清
  • 回归与复现困难

25.2 只有格式,没有语义版本

即使都是:

  • {"category":"refund"}

也不代表语义没变。

你还需要版本信息来回答:

  • 字段是不是新增含义
  • 枚举集合是不是变化了
  • 下游是否能兼容

25.3 只校验最终文本,不校验工具入参

很多高风险事故根源并不在最终回答,而在:

  • 工具参数本身就错了

25.4 没有失败后的状态机

工具失败后最常见的错误是:

  • 再试一次
  • 还不行再试一次
  • 最后模型编一个答案收尾

正确做法是明确:

  • 哪些错误允许重试
  • 重试几次
  • 谁决定降级
  • 谁决定人工接管

25.5 没有幂等与追踪标识

如果工具是写操作,你还需要协议级定义:

  • 请求 ID
  • call_id
  • 幂等键
  • 审批 ID

否则重放和重试都可能变成重复执行。

25.6 把 MCP、工具调用和业务协议混成一层

MCP 解决的是:

  • 能力如何标准化暴露

它不直接替代:

  • 你的业务状态机
  • 你的审批策略
  • 你的领域输出 contract

25.7 把“多轮”误解成“把历史全粘回去”

这样做短期省事,长期几乎必然带来:

  • 成本越来越高
  • 摘要越来越脏
  • 状态越来越难解释

26. 这页和 MCP / Agent / 工作流专题是什么关系

可以这样理解:

  • 本页更偏“应用内部协议设计”
  • MCP 更偏“外部能力标准化接入”
  • Agent 专题更偏“执行循环、状态、记忆和工具编排”
  • 工作流专题更偏“状态机、补偿、审批、人机协同”

四者是分层关系,而不是互斥关系:

  1. 先把应用内部的消息、状态、工具和审批 contract 设计清楚
  2. 再决定哪些能力通过 MCP 暴露或接入
  3. 再把这些 contract 接入 Agent 执行循环
  4. 最后用工作流状态机把高风险动作和长任务治理起来

如果第一层没做好,后面接再多 Agent 或 MCP 能力,也只是把混乱放大。


27. 建议和哪些专题一起看


28. 重点官方资料


29. 一句话总结

今天做 LLM 系统时,真正稳定的不是“某一段 prompt”,而是:

  • 一套把指令、消息、状态、结构化输出、工具调用、审批和回放串起来的协议 contract