Files
zend-token/docs/cross-provider-mapping.md
2026-08-23 02:12:08 +08:00

11 KiB
Raw Permalink Blame History

跨 Provider 映射表

本文件把三家的字段与语义放在一起对照,标注哪些能无损映射、哪些必然有损。数据来源见 openai.mdanthropic.mddeepseek.md 各自的来源列表。


1. 端点与鉴权

OpenAI Chat OpenAI Responses Anthropic Messages DeepSeek
路径 /v1/chat/completions /v1/responses /v1/messages /chat/completions
鉴权 Authorization: Bearer 同左 x-api-key Authorization: Bearer
版本 header anthropic-version: 2023-06-01 必需

2. 请求参数

语义 OpenAI Chat OpenAI Responses Anthropic DeepSeek
消息列表 messages input messages messages
系统提示 messages[role=system|developer](可多条、可在任意位置) instructions(顶层) system(顶层,只有一条) messages[role=system]
输出上限 max_completion_tokensmax_tokens 已废弃) max_output_tokens max_tokens(必填) max_tokens
温度 temperature 0~2 同左 temperature 0~1 temperature 0~2
top-p top_p top_p top_p top_p
top-k top_k
停止序列 stop(≤4 stop_sequences stop(≤16
频率惩罚 frequency_penalty 已废弃
存在惩罚 presence_penalty 已废弃
多候选 n1~128
JSON 模式 response_format text.format output_config.format response_format(仅 json_object
工具 tools[].function.parameters tools[].parameters tools[].input_schema 同 OpenAI Chat
工具选择 tool_choice: auto/required/none/具体 同左 tool_choice: auto/any/none/tool 同 OpenAI Chat
禁并行工具 parallel_tool_calls: false 同左 tool_choice.disable_parallel_tool_use: true parallel_tool_calls
推理开关 reasoning_effort reasoning.{effort,summary} thinking + output_config.effort thinking.{type,reasoning_effort}
缓存控制 prompt_cache_key / prompt_cache_options.ttl 同左 cache_control 断点 无(全自动)
用户标识 safety_identifieruser 已废弃) 同左 metadata.user_id user_id(≤512,限字符集)

必然有损的转换

  • 多条 system 消息 → Anthropic:只能合并。Anthropic 官方兼容层的做法是把所有 system/developer 消息用单个换行 \n 连接成一条,放在最前。
  • n > 1 → 任何非 OpenAI 上游:无法实现,只能拒绝或退化为 n=1
  • frequency_penalty / presence_penalty / logit_bias / seed → Anthropic 或 DeepSeek:无对应,只能忽略。
  • temperature OpenAI(02) → Anthropic(01):需 clamp。官方兼容层的选择是 >1 一律截为 1(而不是线性缩放)。
  • top_k → OpenAI:无对应,只能丢弃。

3. 消息与内容块

语义 OpenAI Chat Anthropic
纯文本 content: "..."[{type:"text", text}] content: "..."[{type:"text", text}]
图片 {type:"image_url", image_url:{url, detail}} {type:"image", source:{type:"base64"|"url", media_type, data|url}}
图片 detail detail: low/high/auto 无对应,丢弃
PDF/文档 {type:"file"} {type:"document", source:{...}, citations}
音频输入 {type:"input_audio"} 不支持
助手工具调用 assistant.tool_calls[] content 里的 tool_use block
工具结果 独立 {role:"tool", tool_call_id, content} 消息 user 消息 content 里的 tool_result block
工具错误 靠 content 文本表达 tool_result.is_error: true
推理内容 reasoning itemResponses thinking / redacted_thinking block
引用 annotations citations5 种 location 类型)

工具 ID 三套命名

调用侧 结果侧
OpenAI Chat tool_calls[].id tool.tool_call_id
OpenAI Responses function_call.call_id function_call_output.call_id
Anthropic tool_use.idtoolu_ 前缀) tool_result.tool_use_id

中转站需要维护 ID 映射表,不能假设上游会接受下游生成的 ID 格式。


4. 停止原因

语义 OpenAI finish_reason Anthropic stop_reason DeepSeek finish_reason
自然结束 stop end_turn stop
达到上限 length max_tokens length
命中停止序列 stop stop_sequence stop
调用工具 tool_calls tool_use tool_calls
内容过滤 content_filter refusal+ stop_details.category content_filter
上下文溢出 无(表现为 400 model_context_window_exceeded
服务端工具暂停 pause_turn
资源不足 insufficient_system_resource

注意stopstop_sequence 在 OpenAI 侧合并成一个值,Anthropic → OpenAI 方向可无损降级;反向则丢失信息。pause_turn 在 OpenAI 格式下无法表达,中转站必须在网关内部自行完成续跑循环,不能透传给客户端。


5. Usage 统计

语义 OpenAI Anthropic DeepSeek
输入 token usage.prompt_tokens usage.input_tokens (仅未缓存部分) usage.prompt_tokens(总数)
输出 token usage.completion_tokens usage.output_tokens usage.completion_tokens
总计 usage.total_tokens 需自行相加 usage.total_tokens
缓存命中 prompt_tokens_details.cached_tokens cache_read_input_tokens prompt_cache_hit_tokens
缓存写入 无单独计数 cache_creation_input_tokens 无(prompt_cache_miss_tokens 是未命中数)
推理 token completion_tokens_details.reasoning_tokens output_tokens_details.thinking_tokens completion_tokens_details.reasoning_tokens

关键陷阱Anthropic → OpenAI 时,

prompt_tokens = input_tokens + cache_read_input_tokens + cache_creation_input_tokens

直接用 input_tokens 会严重低报(缓存命中时可能低报 99%)。


6. 流式协议映射

OpenAI Anthropic
结构 无状态 chunk 序列 有状态的 block 事件机
事件名 无(都是匿名 data: 有(event: message_start 等)
文本增量 choices[0].delta.content content_block_delta + text_delta
工具增量 delta.tool_calls[].function.arguments(按 index 累加) content_block_delta + input_json_delta.partial_json
推理增量 Responses 有专门事件 thinking_delta + signature_delta
usage 最后一个 chunk(需 include_usage),choices 为空数组 message_delta.usage累计值
结束标记 data: [DONE] message_stop
保活 无标准(实践中发注释行) ping 事件
中途错误 无标准 error 事件(200 之后仍可能发)

Anthropic → OpenAI 流式转换要点

  1. message_start → 发一个只含 role: "assistant" 的首 chunk。
  2. content_block_start(text) → 无对应,吞掉。
  3. text_deltadelta.content
  4. content_block_start(tool_use) → 发一个带 id/namearguments: ""tool_calls[index] chunk。
  5. input_json_deltatool_calls[index].function.arguments 追加。
  6. thinking_delta → 无标准落点;常见做法是映射到 delta.reasoning_contentDeepSeek 风格,事实标准)。signature_delta 无处可放,会丢失——这意味着经过 OpenAI 格式往返后的对话无法再发回 Anthropic 上游
  7. message_delta.stop_reason → 最后一个 chunk 的 finish_reason
  8. message_stopdata: [DONE]
  9. ping → 丢弃或转成 SSE 注释行保活。

OpenAI → Anthropic 流式转换要点

必须自行维护 block index 状态机:文本与工具调用要分配不同的 index,并在切换时补发 content_block_stop / content_block_start。OpenAI 的 chunk 里没有"块结束"信号,只能靠 tool_calls[].index 变化和 finish_reason 推断。


7. 推理内容三方对照(最不可互通的部分)

OpenAI Responses Anthropic DeepSeek
载体 reasoning item thinking / redacted_thinking block reasoning_content 字符串
可见文本是什么 summary(需显式开启) 摘要,永远不是原始 CoT 思考内容字符串
真实推理载体 encrypted_content signature(完整思考的密文) 无,就是明文字段
密文是否必需 ZDR / store:false 时必需 始终存在,服务端解密它重建思考 不适用
可否修改 加密内容需原样回传 不可修改,改了直接 400 可自由处理
多轮是否必回传 有函数调用时必须 工具轮次内必须;其他情况可省略但不可改 无工具调用时不必;有工具调用时必须
关闭方式 reasoning.effort: "none" / "minimal" 部分模型根本无法关闭 thinking.type: "disabled"
跨模型可移植 ,绑定生成模型,换模型必须剥离
跨平台可移植 API/Bedrock/Vertex 三方通用

为什么 Anthropic 侧无法靠字段翻译打通

Anthropic 的 thinking 文本字段是摘要,真正的推理内容加密在 signature 里,服务端回传时解密 signature 来重建思考,并不读你传的文本(详见 anthropic.md §5)。

所以这条常见的转换路径是死路

Anthropic thinking block → reasoning_content 字符串 → 存下来 → 转回 thinking block

转回去的东西只有摘要文本、没有 signatureAnthropic 侧要么 400,要么丢失全部推理连续性。

可行做法

把 signature / encrypted_content / redacted_thinking.data 当成 opaque blob 原样搬运。 三家的密文字段都明确声明为不可解析,中转站不需要理解它们:

  1. 网关按会话 ID 存储上游返回的原始 content block 数组(含密文字段)。
  2. 下游客户端只看到脱敏后的摘要文本(映射到 reasoning_content 或 Responses 的 reasoning item)。
  3. 下一轮请求时,用存储的原始块重建上游请求,而不是从下游回传的文本反向构造。

需要额外注意的两点:

  • 模型路由会破坏它Anthropic thinking block 绑定生成它的模型,换模型时必须剥离。其他模型静默忽略而不报错,但照样计输入 token——做故障转移时不清理,用户会为无用内容付费且毫无提示。
  • signature 很长(Claude 4+ 显著变长)。存储层的列宽、日志截断、header 大小限制都可能悄悄截断它,而截断后的 signature 会在下一轮触发 400。