消息、配置与版本意识

本节目标

设计稳定的应用请求合同,把角色、内容、模型选择和供应商特性留在明确的适配边界。

供应商无关合同

应用层可以先定义自己真正需要的最小字段:

{
  "operation_id": "ticket-42-attempt-group-1",
  "prompt_version": "ticket-router-1.3.0",
  "model_profile": "fast-structured-classifier",
  "required_capabilities": ["structured_final"],
  "messages": [
    {"role": "user", "content": "……"}
  ],
  "output_schema_id": "ticket-label-1.1.0"
}

字段阅读:

  • operation_id 把同一业务意图的多次网络尝试关联起来,不能把每次请求 ID 当作业务唯一键。
  • prompt_version 让日志、评测和回归可以定位到实际使用的指令版本。
  • model_profile 是内部选择角色;部署配置再映射到具体 Provider 和模型 ID。
  • required_capabilities 列出正确性不可缺少的能力,adapter 应在调用前验证支持情况。
  • messages 保存应用层规范化输入;角色和内容块仍需要由 adapter 显式映射。
  • output_schema_id 关联下游预期的结构化合同及其版本。

model_profile 是内部逻辑配置,不是供应商模型名。部署配置再将它映射为供应商、模型标识、输出上限和受支持功能。这样可以审计和切换,但不能假装所有模型的参数语义一致。

内部合同还应区分“必需能力”和“偏好”。例如 schema 严格遵循、工具调用或图片输入若是正确性的前提,就必须在发送前做 capability check;延迟档位或缓存则可能只是优化。adapter 遇到必需能力不支持时返回 unsupported_capability,不能静默降级为纯文本再让下游误以为合同仍成立。

角色与内容映射

不同 API 对系统/开发者/用户/助手角色、内容块、图片、工具结果和对话状态有不同约束。adapter 应做显式映射并拒绝无法表达的功能,而不是静默降级。高层指令是否跨响应链继承、历史如何计费、服务端状态保留多久,都属于动态事实。

当前三家 API family 的核心差异如下;这张表只描述 2026-07-21 核验的 family,不代表同一供应商的其他 API:

API family高层指令与本轮输入多轮状态必须显式处理的边界
OpenAI Responsesinstructions 与 input可用 previous_response_id,也可手工重放 Itemsinstructions 不随 previous ID 继承;链上先前输入 token 仍计费;store=false 手工历史要重放每个 response.output Item,不能只留可见文本
Anthropic Messages通常用顶层 system + messages调用方重放完整消息历史顶层 system 是覆盖最广的做法;mid-conversation system 只对 Fable 5、Mythos 5、Opus 4.8 等已声明模型并满足位置规则时启用
Gemini Interactions v1system_instruction、input/Steps可用 previous_interaction_id 或提交统一 Stepsprevious interaction 不继承 tools、system instruction 或生成配置,续接时必须显式重发

“服务端记住历史”不等于“配置也继承”,更不等于“历史免费”。adapter 应为每个请求构造完整 caller-owned controls,并把服务端句柄是否可检索当作显式前置条件。不能在句柄失效后悄悄退化为缺少 reasoning/thought/tool blocks 的文本摘要。

有状态与无状态续接

两种方式的工程责任不同:

  • 有状态续接:保存 Provider response/interaction ID,核对前序是否已存储、保留期、数据治理和不继承字段;句柄过期或删除时显式失败或从可信快照重建。
  • 无状态续接:应用保存 canonical provider items/blocks/steps,按原顺序重放所有续接所需对象;必须有大小上限、加密、完整性和版本迁移策略。

不要只存一段 assistant 文本。工具调用身份、工具结果、reasoning/thinking 签名、拒绝/截断状态和托管工具步骤都可能影响下一轮合法性。需要摘要时,把摘要作为新输入,并明确它不是原始 Provider 对话的无损恢复。

配置要可追溯

记录:

  • 供应商 adapter 版本;
  • 逻辑模型配置 ID 与实际模型标识;
  • 提示、schema、工具定义和上下文选择器版本;
  • 关键生成参数及其“未设置/显式设置”状态;
  • 请求发起时间和应用 operation ID。

不要从某个供应商复制 temperature、top_p 或推理参数到所有模型。先查支持情况,再用任务评测证明设置的价值。模型别名可能漂移;需要强复现时使用供应商提供的稳定版本机制并保留上线评测。

配置记录要同时保留“请求的模型配置”和“响应实际报告的模型/版本”。别名、路由或后端更新可能让两者不同;只有内部 model_profile 而没有实际响应标识,无法解释回归。

对动态合同保存两种版本:人类可读的 prompt_version/schema_id/toolset_id,以及规范化内容的 digest。版本号说明发布意图,digest 检测“同名内容被改写”。不要把包含秘密或个人数据的原文直接塞进高基数 metric label;digest 也不是脱敏万能药,低熵敏感值仍可能被枚举。

输出上限和停止状态

输出达到上限、被安全策略拒绝、工具调用待处理和正常完成不是同一种成功。adapter 应把供应商停止原因映射为有限的内部状态,同时保留原始类型,未知新类型要显式暴露而不是当成完成。

练习与自测

为“结构化工单分类”设计一个最小内部请求/响应 dataclass。列出换供应商时必须适配的字段。自测:若新供应商不支持所需 schema 子集,系统会报能力不匹配,还是悄悄改用纯文本?

掌握检查

  • 应用合同只包含业务需要,供应商字段由 adapter 显式映射。
  • 必需能力与性能偏好分开;无法表达的必需能力会失败关闭。
  • 请求和响应都保留实际模型、adapter、提示、schema、工具和上下文版本。
  • 我能区分有状态句柄与无状态重放,并知道哪些指令/配置不会继承、哪些历史仍计费。
  • 对话快照保存 Provider 身份与必要 block/item/step,不把可见文本误当无损历史。
  • 未设置与显式设置参数可区分,不把某家供应商参数机械复制到另一家。
  • 完成、拒绝、截断、等待工具和未知终止原因映射为不同内部状态。

下一步

进入 结构化输出与流式响应。

参考资料