协议兼容性与限制
LYRouter 提供 Chat Completions、Anthropic Messages、Responses 三种对话接口,而每个服务商通道只使用一种上游协议:OpenAI 兼容或 Anthropic。客户端接口与通道协议不同时,网关自动转换请求、响应、流式事件和错误格式;转换只保留双方都有对应含义的字段,其余字段会被去掉或改写,且不会写进响应头。Responses 请求不做协议转换,只能由提供 Responses 接口的通道服务。依赖特定参数、缓存标记或 usage 字段的应用,请按本页确认实际组合下的行为。
客户端接口 × 通道协议
| 客户端调用的接口 | OpenAI 兼容通道 | Anthropic 协议通道 |
|---|---|---|
Chat Completions(/v1/chat/completions) |
按原格式发送,网关只调整推理参数,删除缓存标记和历史消息中的 reasoning_content(DeepSeek 系列保留当前轮 assistant 消息上的 reasoning_content) |
转换为 Messages 请求;响应和流式事件转换回 Chat 格式 |
Anthropic Messages(/v1/messages) |
转换为 Chat Completions 请求;响应和流式事件转换回 Anthropic 格式 | 先转换为内部的 Chat 结构,再转换回 Messages 发送;平台开启原生直通时可以原样发送,见下文 |
Responses(/v1/responses) |
原样发送到通道的 Responses 接口;通道没有该接口时由上游返回错误 | 不可用。模型声明了接口范围且不含 Responses 时,网关直接返回 400 This model does not support the responses API |
- 通道由网关选择:同一个模型可能同时有两种协议的通道,失败重试时也可能换到另一种协议的通道,所以同一个请求在不同时间可能经过不同的转换。要确认某次请求实际发出的格式,在 日志分析 的日志详情中查看「服务商」,以及「原始报文」中的「上游请求」。原始报文的正文只在工作空间开启全文日志后保存,失败的请求除外。
- 不需要转换的组合最保真:Chat Completions 客户端走 OpenAI 兼容通道、Responses 客户端走提供 Responses 接口的通道时,请求基本按原样发送。对字段有严格要求的应用,可以请当前账号管理员在模型详情中只保留同协议的来源,见应用接入多个服务商。
- 网关拒绝在先:模型目录声明了接口或输入范围时,不符合的请求(如对话模型调用图像接口、给只收文本的模型发图片)在转换之前就返回 400,见模型能力与兼容性。
Chat Completions → Anthropic 协议通道
| 请求字段 | 处理 |
|---|---|
role 为 system、developer 的消息 |
合并为 Anthropic 的 system,多条之间以空行连接。只接受字符串形式的 content,写成内容数组的 system / developer 消息会被丢弃 |
user、assistant 消息的 content |
字符串原样转换;内容数组中只转换 text 和 image_url(data URL 转为 base64 图片,http(s) 地址转为 URL 图片),input_audio、file 等其他类型丢弃 |
tool 消息、assistant.tool_calls |
分别转为 tool_result 块和 tool_use 块;arguments 解析为 JSON 对象,解析失败时为空对象 |
assistant.reasoning_content |
不转发 |
tools |
转为 name、description、input_schema(取自 parameters) |
tool_choice |
auto → auto,required → any,指定函数 → tool;none 不转发,请求带 tools 时模型仍可能调用工具 |
max_tokens、max_completion_tokens |
转为 max_tokens;两者都没有时补 4096 |
stop |
转为 stop_sequences,字符串会变成单元素数组 |
metadata |
只保留 user_id,其他键丢弃 |
enable_thinking、thinking_budget |
转为 thinking:有预算时为 {"type": "enabled", "budget_tokens": N},只开启没有预算时为 {"type": "adaptive"},关闭时不发送 |
n、logprobs、top_logprobs、response_format、frequency_penalty、presence_penalty、seed、parallel_tool_calls、logit_bias、functions、function_call、reasoning_effort、stream_options |
丢弃 |
其他字段(temperature、top_p、top_k、stream、service_tier 等) |
原样发送;Anthropic 不认识的字段由上游决定是否接受 |
响应转换回 Chat 格式:文本块合并为 message.content,tool_use 块转为 tool_calls,思考内容放在 reasoning_content;stop_reason 映射为 finish_reason(end_turn、stop_sequence、pause_turn、refusal → stop,max_tokens → length,tool_use → tool_calls)。因为 n 被丢弃,choices 始终只有一项。
强制调用工具时关闭思考:tool_choice 为 required 或指定函数、且请求没有写推理参数时,网关替你关闭思考,避免上游以「思考模式不支持强制工具调用」拒绝请求。这条规则对任何接口发往 Anthropic 协议通道或阿里云百炼等使用 enable_thinking 的通道都生效。
Anthropic Messages → 通道
/v1/messages 请求先转换为内部的 Chat 结构,再按通道协议发出。经过这一步后:
| 请求内容 | 处理 |
|---|---|
system(字符串或文本块数组) |
合并为一段文本,多个块以空行连接;多个块上的 cache_control 只保留最后一个 |
| 消息中的块 | 转换 text、image、tool_use、tool_result、thinking、redacted_thinking;document 等其他类型的块丢弃 |
同一条 user 消息中既有 tool_result 又有文本或图片 |
只保留 tool_result。需要附带的文本请放在下一条 user 消息中 |
tools |
按 name、description、input_schema 转换。只有 type 和 name 的服务端工具(如 web_search)会变成同名的普通函数工具,由客户端自己执行 |
tool_choice |
auto、any、tool 转换;none 和 disable_parallel_tool_use 不转发 |
max_tokens、stop_sequences、stream、temperature、top_p、top_k、service_tier |
保留 |
thinking |
{"type": "disabled"} 视为关闭,其他类型视为开启,budget_tokens 保留为思考预算;发往 Anthropic 协议通道时还原为带预算的 enabled 或不带预算的 adaptive |
metadata、context_management、output_config |
发往 Anthropic 协议通道时还原(metadata 只保留 user_id);发往 OpenAI 兼容通道时删除 |
| 其他顶层字段 | 不转发 |
发往 OpenAI 兼容通道 时,思考开关按服务商的写法传递:阿里云百炼等通道收到 enable_thinking / thinking_budget;OpenAI、DeepSeek 官方通道不支持这两个字段,会被删除;网关没有适配的服务商原样收到这两个字段,是否生效由上游决定。output_config 中的 effort 设置不会换算成 reasoning_effort。历史消息中的思考块和全部 cache_control 标记也会删除。
响应转换回 Anthropic 格式:
stop_reason只会是end_turn、max_tokens、tool_use三种,上游的stop_sequence、pause_turn、refusal都变为end_turn,响应中不出现stop_sequence字段(流式与非流式相同)。这一点对 Anthropic 协议通道同样适用。- OpenAI 兼容通道返回的思考内容(
reasoning_content)在流式响应中转为thinking事件,不带签名;非流式响应中不包含思考内容。 - 回答只有思考内容(例如在思考阶段就被
max_tokens截断)时,非流式响应的content目前是空对象{},而不是空数组[]。按数组遍历content前请先判断类型。 - 发往 Anthropic 协议通道时,思考块的签名原样往返,可以在下一轮回传。历史消息中有不带签名的思考块(例如上一轮由 OpenAI 兼容通道回答)时,这一轮去掉全部历史思考块,并且不开启思考,避免上游因签名校验失败而拒绝。
推理参数
三个推理参数分属不同服务商,网关不在它们之间换算,只按选中的通道删掉对方不认识的字段。除 Anthropic 协议一行外,下表各行指 OpenAI 兼容通道:
| 通道 | reasoning_effort |
enable_thinking |
thinking_budget |
|---|---|---|---|
| OpenAI | 保留 | 删除 | 删除 |
| Anthropic 协议 | 删除 | 转为 thinking |
转为 thinking |
| 阿里云百炼(含通义千问) | 删除 | 保留 | 保留 |
| DeepSeek 官方 | 删除 | 删除 | 删除(思考由模型决定) |
| 其他服务商 | 原样发送 | 原样发送 | 原样发送 |
同一模型的不同通道可能属于不同行。需要稳定的推理行为时,请为每个通道验证,或用虚拟模型固定推理参数。
缓存标记 cache_control
| 组合 | 处理 |
|---|---|
/v1/messages → Anthropic 协议通道 |
保留 system、文本块和工具定义上的标记;多个 system 块的标记合并为一个;打在图片块或 tool_result 块上的标记不转发 |
/v1/chat/completions → Anthropic 协议通道 |
保留内容数组中文本、图片部分和 tools 中各工具上的标记;system 消息的标记写在消息对象上(与 role、content 同级),content 用字符串 |
| 任意接口 → OpenAI 兼容通道 | 全部删除,包括阿里云百炼等支持显式缓存的服务商。这些通道依靠上游的自动前缀缓存 |
- 1 小时缓存:标记中写
"ttl": "1h"时,网关把anthropic-beta请求头设为extended-cache-ttl-2025-04-11,替换客户端在该请求头中的原有值。 - 虚拟模型的缓存设置:虚拟模型开启了缓存且请求本身没有任何标记时,网关只在发往 Anthropic 协议通道时给系统提示词加标记;请求自带标记时以请求为准。
- 标记是否生效、命中了多少,在日志的「显式缓存创建」和「输入(缓存命中)」中查看,优化方法见降低成本与优化缓存。
usage 字段
响应中的 usage 在不同组合下统计方式不同:
| 客户端接口 ← 通道 | 响应中的 usage |
|---|---|
| Chat Completions ← OpenAI 兼容 | 上游原样返回:prompt_tokens 包含缓存命中部分,命中数在 prompt_tokens_details.cached_tokens(DeepSeek 为 prompt_cache_hit_tokens) |
| Chat Completions ← Anthropic 协议 | prompt_tokens 等于上游的 input_tokens,不含缓存命中和缓存写入;非流式响应另带 cache_read_input_tokens、cache_creation_input_tokens,流式最后一个 chunk 只有 prompt_tokens、completion_tokens、total_tokens |
| Anthropic Messages ← Anthropic 协议 | input_tokens 不含缓存,cache_read_input_tokens、cache_creation_input_tokens 只在大于 0 时出现 |
| Anthropic Messages ← OpenAI 兼容 | input_tokens = 上游 prompt_tokens − 缓存命中数,cache_read_input_tokens = 缓存命中数,output_tokens = 上游 completion_tokens |
| Responses | 上游原样返回:input_tokens 包含缓存命中部分,命中数在 input_tokens_details.cached_tokens |
- 流式 Chat Completions 中,网关总会请求
usage,最后一个 chunk 带用量;Responses 和 Anthropic Messages 按各自协议在结束事件中给出用量。 - 请求日志已统一:日志中的「输入(缓存未命中)」「输入(缓存命中)」「输出」「显式缓存创建」四项按同一方式换算,互不重叠,不受协议组合影响。核对费用、统计命中率时以日志和用量分析为准,不要直接累加不同组合下的
prompt_tokens。
错误格式与流式结束标记
| 客户端接口 | 错误响应体 | 流式正常结束 | 流式输出开始后上游中断 |
|---|---|---|---|
| Chat Completions | {"error": {"message", "type", "code"}},code 只在部分错误上出现 |
data: [DONE] |
补一个 data: {"error": {..., "code": "stream_interrupted"}},没有 data: [DONE] |
| Anthropic Messages | {"type": "error", "error": {"type", "message"}},没有 code |
event: message_stop |
补一个 event: error,没有 message_stop |
| Responses | 与 Chat Completions 相同;上游的错误原样转发 | response.completed 事件,没有 [DONE] |
补一个 response.failed 事件(error.code 为 server_error),没有 response.completed |
/v1/messages上的网关错误和上游错误都转换为 Anthropic 格式,message原文不变,error.type按状态码确定(如 401 →authentication_error,429 →rate_limit_error,503 →overloaded_error)。- 连接上游失败或等待超时的 500 / 504 返回 HTML 错误页,不是 JSON,任何接口都一样,见超时与流式中断。
- 对话接口在转换中丢弃的参数不写进
X-Gateway-Unsupported-Parameters响应头;这个响应头和X-Gateway-Strict-Parameters请求头目前只用于图像、视频接口,见网关扩展请求头与响应头。
完整错误码见错误码与请求标识。
Responses 的服务端状态
Responses 请求原样转发,store、background、previous_response_id 都由上游处理,响应对象保存在创建它的上游:
| 操作 | 网关的处理 | 限制 |
|---|---|---|
带 previous_response_id 的新请求 |
用它把请求归入同一会话(日志的「会话」列),然后照常选择通道 | 不保证落到同一个上游。换了通道或凭证时,新的上游找不到上一轮响应,返回上游的错误 |
- 多轮对话:「最低价」「负载均衡」策略会在 30 分钟内把同一 API Key 对同一模型的请求保持在同一通道,多数接续请求会落回原上游,但失败重试、策略切换或保持期过后仍可能换通道,见通道选择与路由策略。需要可靠的多轮对话时,在客户端保存历史,每轮把完整上下文放进
input发送。 - 虚拟模型的参数:通过 Responses 调用虚拟模型时,「推理参数」按 Chat Completions 的字段名合并进请求,例如
max_tokens不会换成max_output_tokens。上游不接受这些字段时会返回 400,供 Responses 客户端使用的虚拟模型请只设置两种接口同名的参数(如temperature、top_p)。
原生直通
原生直通让 /v1/messages 请求在发往 Anthropic 协议通道时跳过上文的转换:请求体除 model 换成上游模型名外按客户端发来的内容发送,响应按上游返回的字节原样返回,计费和日志不受影响。平台可以只开启响应原样返回,也可以请求、响应都开启;默认都关闭,所有 /v1/messages 请求都经过转换。
需要确保请求未被改写的客户端,可以加请求头要求直通,做不到时网关拒绝请求,而不是悄悄转换:
curl https://api.lyrouter.com/v1/messages \
-H "x-api-key: $LYROUTER_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "x-gateway-passthrough: require" \
-H "Content-Type: application/json" \
-d '{"model": "MODEL_ID", "max_tokens": 1024, "messages": [{"role": "user", "content": "你好"}]}'
x-gateway-passthrough |
行为 |
|---|---|
不带,或 prefer |
能直通就直通,不能时自动走转换,请求照常完成 |
require(不区分大小写) |
必须直通;不能直通时返回 400 native passthrough was required by `x-gateway-passthrough: require` but is not available for this request (<原因>) |
括号中的原因说明是哪一项条件不满足:
| 原因 | 含义 | 处理 |
|---|---|---|
route_disabled |
平台没有为该接口开启请求原样发送(默认状态) | 去掉这个请求头,接受转换 |
client_protocol |
请求不是发往 /v1/messages 的 |
这个请求头只对 Anthropic Messages 有效 |
wire_protocol |
选中的通道不是 Anthropic 协议 | 换用由 Anthropic 协议通道服务的模型 |
endpoint_type |
不是对话请求 | 去掉这个请求头 |
channel_disabled |
平台对这个通道关闭了直通 | 换用其他模型或去掉请求头 |
preset_injection |
虚拟模型设置了系统提示词、推理参数、锁定参数或缓存,需要改写请求 | 直接调用模型 ID,或去掉虚拟模型中的这些设置 |
shape_oai |
请求体含 OpenAI 专有字段(如 response_format、n),或消息角色不只是 user / assistant,或工具是 OpenAI 格式 |
改成纯 Anthropic 格式的请求 |
decision_error |
网关判断时出错 | 稍后重试;持续出现时联系支持 |
直通只作用于请求实际发往的那个通道;失败重试换到 OpenAI 兼容通道时,带 require 的请求同样返回 400。
常见问题
设置了 response_format,为什么返回的不是 JSON?
答: 请求被路由到 Anthropic 协议通道时,response_format 在转换中被丢弃。在日志中确认这次请求的「服务商」;需要稳定的 JSON 输出时,在提示词中写明格式,或只保留 OpenAI 兼容的来源,见模型能力与兼容性。
用 Claude Code 调用 Claude 模型,请求会被改写吗?
答: 会经过一次转换,但思考块签名、缓存标记、metadata、context_management、output_config 都会还原。需要注意的是 document 块、与 tool_result 同在一条消息中的文本、服务端工具和 stop_reason 的细分值,见上文「Anthropic Messages → 通道」。缓存命中情况在日志的「输入(缓存命中)」中确认。
同一个应用同时用 OpenAI SDK 和 Anthropic SDK,用量怎么统一统计?
答: 不要直接汇总响应中的 usage,两种格式对缓存的计数方式不同。请以请求日志和 用量分析 为准,它们已把四项 Token 换算成同一方式,见通过日志定位调用问题和用量归因与分析。