Appearance
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_callfunction_call_outputprevious_response_idconversation- built-in tools
- remote MCP
构成完整回路。
Anthropic 这边强调的是:
content blockstool_usetool_resultstop_reasoninput_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
输出层至少要回答三件事:
- 输出给人看,还是给系统消费
- 输出是自然语言、JSON,还是严格 schema
- 失败时返回什么格式
如果这层不提前收紧,后面就会出现:
- 文本看着通顺,但下游没法解析
- 字段名一会儿叫
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 模型输出后先校验,再执行动作
尤其是工具调用,顺序应当是:
- 模型给出工具名和参数
- 应用侧校验 schema、权限、审批条件、幂等性
- 再决定是否执行
而不是:
- 模型一出参数就真的去改库、发消息、扣款
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_id或conversation完成 - 让结构化输出和工具约束直接进入协议层
- 让 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 blocks、tool_use、tool_result、stop_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 形态不同,但底层共同模式已经越来越清楚:
- 指令和用户输入必须分层
- 内容最好 typed 化,而不是一段巨型文本
- 工具调用必须是显式动作对象,不要从自然语言里猜
- 工具结果也是协议内对象,不能只是字符串拼接
- 停止原因与状态信号必须进入应用控制流
- 多轮续接是对象续接,不只是文本回放
- 结构化输出和工具约束必须由 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
所以更合理的分层是:
- 你的业务先定义内部 tool contract
- 再决定哪些能力通过 MCP 暴露或接入
- 最后再做审批、观测、评测与回放接线
17. 多轮状态续接绝不是“把历史全塞回去”
这是很多 Agent 系统最容易失控的地方。
17.1 至少分清 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 件事
- 哪些动作进入审批
- 审批时给人看什么证据
- 审批结果如何回到运行链路
- 审批拒绝后系统怎么收口
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. 一份更适合生产的最小协议清单
如果系统已经准备走生产,最少建议补齐这些内容:
- 输入分层:规则、用户输入、检索证据、工具结果、状态摘要分开
- 输出分层:用户可读输出与系统可消费输出分开
- schema 校验:结构化输出和工具入参都做严格校验
- 工具闭环:调用、审批、执行、回填、补偿路径明确
- 状态续接:明确历史、摘要、持久状态的边界
- 终止条件:完成、等待、失败、人工接管分类明确
- 幂等与追踪:请求 ID、tool call ID、审批 ID、trace ID 明确
- 留痕审计:模型版本、上下文版本、工具版本、审批动作可回放
- 版本管理:instruction、schema、tool、context template 分开版本化
- 评测接线:协议对象而不是只有最终文本进入 eval
24. 一个推荐的“协议对象”心智模型
你可以把一次复杂任务拆成下面几类对象:
instruction_setuser_requestcontext_bundletool_definitionsmodel_output_itemstool_execution_recordsapproval_recordsstate_snapshottrace_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 专题更偏“执行循环、状态、记忆和工具编排”
- 工作流专题更偏“状态机、补偿、审批、人机协同”
四者是分层关系,而不是互斥关系:
- 先把应用内部的消息、状态、工具和审批 contract 设计清楚
- 再决定哪些能力通过 MCP 暴露或接入
- 再把这些 contract 接入 Agent 执行循环
- 最后用工作流状态机把高风险动作和长任务治理起来
如果第一层没做好,后面接再多 Agent 或 MCP 能力,也只是把混乱放大。
27. 建议和哪些专题一起看
- 模型调用与消息结构入门
- 07-LLM参数与采样控制
- 04A-Tool能力接口与执行边界
- 04B-MCP协议与连接器接入
- 05-状态、记忆与上下文工程
- 工作流状态机专题
- Prompt版本治理专题
- 可观测性与tracing专题
- 安全审批策略案例专题
28. 重点官方资料
- OpenAI Migrate to Responses
- OpenAI Conversation state
- OpenAI Function calling
- OpenAI Tools
- OpenAI Structured outputs
- OpenAI Built-in tools guide
- OpenAI MCP and Connectors
- OpenAI Reasoning
- Anthropic Tool use overview
- Anthropic Handling stop reasons
- Anthropic Messages API
- Google Gemini Interactions overview
- Google Gemini Structured output
- Google Gemini Function calling
- Google Gemini Stateful guide
- Model Context Protocol specification
29. 一句话总结
今天做 LLM 系统时,真正稳定的不是“某一段 prompt”,而是:
一套把指令、消息、状态、结构化输出、工具调用、审批和回放串起来的协议 contract