Anthropic 格式
根据对话历史生成模型回答,支持流式、图片输入和工具调用。兼容 Anthropic Messages API:系统提示词放在 system,对话放在由内容块组成的 messages,响应的 content 也是内容块数组;Anthropic 官方 SDK 和 Claude Code 等 Anthropic 协议客户端,把 Base URL 设为网关根地址 https://api.lyrouter.com 即可接入。网关会在 Messages 与上游协议之间转换,所以这个接口除了 Claude 系列,也能调用平台上的其他对话模型。
/v1/messages鉴权
在请求头中携带 API Key,以下两种写法任选其一:
x-api-key: <API Key>
Authorization: Bearer <API Key>
两者同时存在时只读取 Authorization,Bearer 区分大小写。Anthropic SDK 的 api_key 参数发送 x-api-key,auth_token 参数发送 Authorization: Bearer;Claude Code 的 ANTHROPIC_AUTH_TOKEN 用的是后者。API Key 在工作空间的 API 密钥 页面创建,只用于 LYRouter 鉴权,不会转发给上游服务商。完整说明见接入地址与鉴权。
Anthropic SDK 和 Claude Code 会自己拼接 /v1/messages,Base URL 不要再带 /v1,否则请求路径变成 /v1/v1/messages,返回 404。
请求头
| 请求头 | 必填 | 说明 |
|---|---|---|
Content-Type |
否 | 建议携带 application/json。不带时网关按 JSON 解析请求体;写成其他值(如 application/x-www-form-urlencoded)时返回 400 |
x-api-key 或 Authorization |
是 | LYRouter API Key,写法见上文「鉴权」。只用于网关鉴权,不会转发给上游 |
anthropic-version |
否 | 网关不校验。调用 Anthropic 上游时版本由通道决定,通常为 2023-06-01 |
anthropic-beta |
否 | 原样转发给上游。请求中带有 "ttl": "1h" 的缓存标记、且发往 Anthropic 上游时,网关把它设为 extended-cache-ttl-2025-04-11,替换请求中原有的值 |
x-gateway-passthrough |
否 | LYRouter 扩展请求头,写 require 表示请求体必须原样发给 Anthropic 上游,做不到就返回 400。公共网关目前没有开启原生直通,不需要时不要发送,见网关扩展请求头与响应头 |
请求体
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | 否 | 模型 ID,也可以写 @vmodel/<名称>(虚拟模型)或 lyrouter/auto(智能路由)。省略、为 "" 或 null 时使用工作空间的默认模型,没有默认模型时返回 400。Anthropic SDK 要求必须填写 |
max_tokens |
integer | 是 | 输出上限。省略时网关不报错:发往 Anthropic 上游时补为 4096,发往 OpenAI 兼容上游时不发送,由上游默认值决定。思考是否计入这个上限由上游决定,例如阿里云百炼不计入:qwen3.7-plus 设为 16 时,output_tokens 为 100,stop_reason 为 end_turn |
messages |
array | 是 | 每项为 {"role": "user" 或 "assistant", "content": 字符串或内容块数组},见下文「内容块」 |
system |
string / array | 否 | 系统提示词。可以是字符串,也可以是 text 块数组(块上可带 cache_control);多个块会合并成一段,块之间空一行 |
stream |
boolean | 否 | true 时以 SSE 事件流返回,见下文「流式响应」 |
temperature、top_p、top_k |
number | 否 | 原样发给上游,是否生效取决于模型 |
stop_sequences |
array | 否 | 停止序列。发往 OpenAI 兼容上游时转为 stop |
tools |
array | 否 | 自定义工具 {"name", "description", "input_schema"},可带 cache_control,见下文「工具调用」 |
tool_choice |
object | 否 | {"type": "auto"}、{"type": "any"} 或 {"type": "tool", "name": "..."}。{"type": "none"} 和 disable_parallel_tool_use 不会发给上游;不希望模型调用工具时,请不要传 tools |
thinking |
object | 否 | {"type": "enabled", "budget_tokens": 2048}、{"type": "adaptive"} 或 {"type": "disabled"},转换规则见下文「网关行为」 |
metadata |
object | 否 | 只有 user_id 会发给 Anthropic 上游;OpenAI 兼容上游不发送 |
service_tier |
string | 否 | 原样发给上游 |
context_management、output_config |
object | 否 | 只发给 Anthropic 上游 |
表中未列出的其他字段不会转发给上游。
内容块
| 块类型 | 出现位置 | 说明 |
|---|---|---|
text |
system、user、assistant |
文本,可带 cache_control |
image |
user、tool_result 内 |
source 为 {"type": "base64", "media_type", "data"} 或 {"type": "url", "url"}。发往 OpenAI 兼容上游时转为 image_url,需要模型支持图片输入,见多模态 |
tool_use |
assistant |
模型发起的工具调用:id、name、input |
tool_result |
user |
工具执行结果:tool_use_id 与 content(字符串,或 text / image 块) |
thinking、redacted_thinking |
assistant 历史 |
上一轮返回的思考块,回传时保留 signature |
其他类型的块(如 document)不会转发给上游。
提示词缓存
在 system 块、消息中的 text 块或工具定义上加 "cache_control": {"type": "ephemeral"}(1 小时缓存写 {"type": "ephemeral", "ttl": "1h"}),标记可缓存前缀的结束位置:
| 标记位置 | 发往 Anthropic 协议上游 | 发往 OpenAI 兼容上游 |
|---|---|---|
system 块 |
保留;多个块合并后,以最后一个带标记的块为准 | 删除 |
消息中的 text 块 |
保留 | 删除 |
tools 中的工具定义 |
保留 | 删除 |
image 块、tool_result 块 |
不转发 | 删除 |
写入缓存的部分记为「显式缓存创建」,之后命中的部分记为「输入(缓存命中)」。OpenAI 兼容上游只有上游自动进行的缓存,不产生「显式缓存创建」。标记方法和命中率优化见降低成本与优化缓存。
请求示例
先安装 SDK:pip install anthropic 或 npm install @anthropic-ai/sdk。
curl https://api.lyrouter.com/v1/messages \
-H "x-api-key: $LYROUTER_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"max_tokens": 1024,
"system": "你是一个简洁的助手。",
"messages": [
{ "role": "user", "content": "用一句话解释什么是 API 网关。" }
]
}'
import os
import anthropic
client = anthropic.Anthropic(
api_key=os.environ["LYROUTER_API_KEY"],
base_url="https://api.lyrouter.com",
)
message = client.messages.create(
model="MODEL_ID",
max_tokens=1024,
system="你是一个简洁的助手。",
messages=[{"role": "user", "content": "用一句话解释什么是 API 网关。"}],
)
print(message.content[0].text) # content 可能为空,见下文「响应」
print(message.usage)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.LYROUTER_API_KEY,
baseURL: "https://api.lyrouter.com",
});
const message = await client.messages.create({
model: "MODEL_ID",
max_tokens: 1024,
system: "你是一个简洁的助手。",
messages: [{ role: "user", content: "用一句话解释什么是 API 网关。" }],
});
console.log(message.content[0].text);
console.log(message.usage);
将 MODEL_ID 替换为工作空间中已开通的模型 ID。改用 Authorization: Bearer 时,cURL 把 x-api-key 头换成 -H "Authorization: Bearer $LYROUTER_API_KEY";SDK 把 api_key / apiKey 换成 auth_token / authToken。
响应
非流式请求返回一个 message 对象;出错时返回 Anthropic 格式的错误对象。下面的 200 是 Anthropic 协议上游的典型响应,400 是没有写 model 且工作空间没有默认模型,401 是 API Key 无效,429 是 API Key 当天的 Token 配额已用完(见429:限流与配额)。
{
"id": "msg_01HCDu5LRGeP2o7s2xGmxyZa",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4-5-20250929",
"content": [
{
"type": "text",
"text": "API 网关是位于客户端与后端服务之间的统一入口,负责鉴权、路由、限流和监控。"
}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 294,
"output_tokens": 46,
"cache_read_input_tokens": 1536,
"cache_creation_input_tokens": 212
}
}
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "you must provide a model parameter"
}
}
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "invalid api key"
}
}
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "rate limit exceeded on apikey total_tokens/day"
}
}
| 字段 | 说明 |
|---|---|
id |
沿用上游返回的标识。Anthropic 上游以 msg_ 开头,其他上游的格式不同,不要依赖它的前缀 |
model |
上游返回的实际模型名,可能与请求中的模型 ID 不同。对应日志的「模型(实际)」 |
content |
非流式响应只包含 text 和 tool_use 块;需要拿到思考内容时,请使用流式。例外见表下说明 |
stop_reason |
end_turn、max_tokens 或 tool_use,按上游的结束原因换算。上游的其他结束原因(如 stop_sequence、refusal)都返回为 end_turn。用 {"type": "tool"} 指定工具时目前可能是 end_turn,见下文「工具调用」 |
stop_sequence |
目前响应中没有这个字段,流式的 message_start、message_delta 中也没有。用官方 SDK 读取时,Python 为 None,Node.js 为 undefined |
usage |
本次 Token 用量,见下表 |
content可能是{}:上游只输出了思考、并且在思考阶段就被max_tokens截断时,非流式响应的content目前是空对象{},而不是空数组[](glm-5.3、kimi-k2.7-code、deepseek-v4 上都出现过)。请把max_tokens留足,解析时把不是数组的content当作没有内容处理。<think>标签:部分模型(如 minimax-m3)把思考过程以<think>…</think>写在text块里,非流式和流式都是如此。网关不会剥离,需要时请自行去掉。
usage 与日志中的四项 Token
usage 与日志「Token 消耗」四项一一对应,四个字段互不重叠:
usage 字段 |
含义 | 日志中的对应项 |
|---|---|---|
input_tokens |
没有命中缓存的输入,不含缓存读取和缓存写入 | 「输入(缓存未命中)」 |
cache_read_input_tokens |
命中提示词缓存的输入 | 「输入(缓存命中)」 |
cache_creation_input_tokens |
本次写入缓存的输入,只在 Anthropic 协议上游出现 | 「显式缓存创建」 |
output_tokens |
输出,包含思考内容 | 「输出」 |
两个缓存字段为 0 时不返回。上游是 OpenAI 兼容协议时,网关从上游的 prompt_tokens 中减去缓存命中量得到 input_tokens,口径与 Anthropic 一致,本次全部输入是 input_tokens + cache_read_input_tokens + cache_creation_input_tokens。计费方法见调用费用与缓存计费。
工具调用
请求中声明工具:
{
"model": "MODEL_ID",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "查询城市的实时天气",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
],
"messages": [{ "role": "user", "content": "上海现在天气怎么样?" }]
}
模型决定调用工具时,content 中出现 tool_use 块,stop_reason 一般为 tool_use:
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "city": "上海" }
}
判断是否需要执行工具,请看 content 中有没有 tool_use 块,不要只看 stop_reason:目前 tool_choice 为 {"type": "tool", "name": "..."} 时,响应带有 tool_use 块,stop_reason 却是 end_turn;any、auto 时为 tool_use。
执行工具后,把助手这一轮原样放回 messages,再用一条 user 消息回传 tool_result,tool_use_id 与调用的 id 一致:
{
"model": "MODEL_ID",
"max_tokens": 1024,
"tools": ["……与第一次请求相同……"],
"messages": [
{ "role": "user", "content": "上海现在天气怎么样?" },
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "city": "上海" }
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "{\"temp_c\":22,\"condition\":\"多云\"}"
}
]
}
]
}
示例中 tools 的值为省略写法,实际请求要放入完整的工具定义。使用时注意:
- 只使用带
input_schema的自定义工具。Anthropic 服务端工具(如web_search)会被当作普通自定义工具发出,不会在服务端执行。 - 包含
tool_result的user消息里,与它并列的text、image块不会转发。需要补充说明时,放进tool_result的content,或放在下一条user消息中。 tool_result的is_error不会转发;执行失败时请在content中写明错误信息。
与 Chat Completions 的写法对比见工具调用。
流式响应
设置 "stream": true 后,响应是 text/event-stream,每个事件由 event: 行和 data: 行组成:
event: message_start
data: {"type":"message_start","message":{"id":"msg_01HCDu5LRGeP2o7s2xGmxyZa","type":"message","role":"assistant","model":"claude-sonnet-4-5-20250929","content":[],"usage":{"input_tokens":294,"output_tokens":0}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"API 网关是"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"input_tokens":294,"output_tokens":46,"cache_read_input_tokens":1536,"cache_creation_input_tokens":212}}
event: message_stop
data: {"type":"message_stop"}
- 事件顺序:
message_start→ 每个内容块的content_block_start、content_block_delta、content_block_stop→message_delta→message_stop。正常结束的标记是message_stop。 - 增量类型:文本为
text_delta;工具调用参数为input_json_delta(partial_json拼接后才是完整 JSON);思考内容为thinking_delta,Anthropic 上游的思考块在content_block_stop之前还有一条完整的signature_delta。OpenAI 兼容上游返回的思考内容同样以thinking块输出,但没有签名。 - 用量:完整的
usage在message_delta中,包括缓存字段。message_start的usage只有input_tokens和为 0 的output_tokens,上游是 OpenAI 兼容协议时input_tokens也为 0,请以message_delta为准。 - 中途出错:输出开始后上游中断时,网关补发
event: error,数据为{"type": "error", "error": {"type": "api_error", "message": "upstream timed out after the response had started"}},之后不再有message_stop。上游自己发出的错误事件(如overloaded_error)按同样格式转给客户端。没有收到message_stop就按失败处理,见超时与流式中断。
curl -N https://api.lyrouter.com/v1/messages \
-H "x-api-key: $LYROUTER_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID","max_tokens":1024,"stream":true,"messages":[{"role":"user","content":"写一首两行的小诗"}]}'
import os
import anthropic
client = anthropic.Anthropic(
api_key=os.environ["LYROUTER_API_KEY"],
base_url="https://api.lyrouter.com",
)
with client.messages.stream(
model="MODEL_ID",
max_tokens=1024,
messages=[{"role": "user", "content": "写一首两行的小诗"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print("\n", stream.get_final_message().usage)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.LYROUTER_API_KEY,
baseURL: "https://api.lyrouter.com",
});
const stream = client.messages.stream({
model: "MODEL_ID",
max_tokens: 1024,
messages: [{ role: "user", content: "写一首两行的小诗" }],
});
stream.on("text", (text) => process.stdout.write(text));
const final = await stream.finalMessage();
console.log("\n", final.usage);
各协议流式格式的对比见流式响应。
网关行为
网关内部统一用 OpenAI Chat Completions 格式处理对话请求。/v1/messages 的请求先转换为内部格式,再按选中通道的协议发出:Anthropic 协议的通道还原成 Messages 请求,OpenAI 兼容的通道转换为 Chat Completions 请求;响应和流式事件再转换回 Messages 格式。上游是 Anthropic 时也经过这一转换,请求和响应不会逐字节原样转发。
| 请求内容 | 发往 Anthropic 协议上游 | 发往 OpenAI 兼容上游 |
|---|---|---|
system |
合并为一段文本 | 转为 system 消息 |
text、image 块 |
保留 | 转为 content 中的 text、image_url |
tool_use、tool_result |
保留 | 转为 tool_calls 和 tool 消息 |
tools、tool_choice |
保留(none 除外) |
转为函数工具;any 转为 required,指定工具转为指定函数 |
thinking 参数 |
enabled 保留预算,adaptive 保留,disabled 不发送 |
转为 enable_thinking、thinking_budget,已知不支持的上游会删除 |
| 历史中的思考块 | 带签名的原样回传;历史里有没有签名的思考块时,这一轮删除全部思考块并关闭思考 | 删除 |
max_tokens |
保留,省略时补为 4096 | 保留,省略时不发送 |
stop_sequences |
保留 | 转为 stop |
metadata |
只保留 user_id |
删除 |
context_management、output_config |
保留 | 删除 |
cache_control |
见上文「提示词缓存」 | 删除 |
- 思考块与切换模型:同一会话中途从 OpenAI 兼容上游的模型换到 Anthropic 上游时,历史中的思考块没有签名,这一轮会关闭思考后再发送,避免上游拒绝请求。
- 模型名:
model换成所选通道的实际模型名,响应中的model是上游返回的名称。 - 虚拟模型:请求没有
system时,网关填入虚拟模型的系统提示词;已有system时不注入,见虚拟模型。 - 键顺序:发往上游的请求 JSON 中,对象的键会被重新排列(数组顺序不变),让相同前缀稳定命中上游缓存。
- 失败回退:开始输出之前上游返回错误或连接失败时,网关按失败重试与备用模型换通道,必要时改用备用模型和工作空间默认模型。流式输出开始后不再换通道。
- 响应头:上游的
Retry-After会转发给客户端。响应带anthropic-ratelimit-*头:上游返回了这组头时原样转发,否则在配置了配额时按网关配额生成;x-aigw-quota-*见网关扩展请求头与响应头。
常见错误
/v1/messages 上的错误一律转换为 Anthropic 格式。message 保持原文,不再带 error.code,error.type 按状态码确定:
{
"type": "error",
"error": {
"type": "permission_error",
"message": "Model is not available in the model plaza"
}
}
| HTTP 状态码 | error.type |
|---|---|
| 400、402、422 | invalid_request_error |
| 401 | authentication_error |
| 403 | permission_error |
| 404 | not_found_error |
| 413 | request_too_large |
| 429 | rate_limit_error |
| 503、529 | overloaded_error |
| 其他 5xx | api_error |
| 状态码 | error.message |
原因与处理 |
|---|---|---|
| 401 | missing credentials / invalid api key |
没有携带 API Key,或 Key 无效、已删除、已过期。检查 x-api-key 或 Authorization,见401 / 403:鉴权与权限 |
| 400 | you must provide a model parameter |
请求没写 model,工作空间也没有设置默认模型 |
| 400 | tool_choice 'required' is incompatible with thinking enabled |
tool_choice 为 any 或指定工具时,上游在思考模式下拒绝强制调用。改用 {"type": "auto"},或用 thinking: {"type": "disabled"} 关闭思考。kimi-k2.7-code 上关闭思考不起作用,仍返回这个错误,请改用 auto;阿里云百炼的 qwen 模型上关闭有效 |
| 403 | Model is not available in the model plaza |
工作空间没有开通该模型。Claude Code 的后台任务常用官方模型名发起请求,按 Claude Code 补齐模型配置 |
| 402 | insufficient credit balance |
账户 Credits 余额不足,见模型不可用 |
| 429 | rate limit exceeded on <对象> <指标>/<窗口> |
配额或预算已用完。Anthropic SDK 会自动重试 429,但配额用完时重试无效,见429:限流与配额 |
| 503 | No configured credential can serve this model |
模型在本工作空间没有可用的通道或凭证,见模型不可用 |
| 503 | all routed candidates exhausted |
找不到可服务这次请求的通道,例如 @vmodel/<名称> 写错、这个虚拟模型不存在。error.type 为 overloaded_error,Anthropic SDK 会自动重试,但名称写错时重试无效。核对名称,见模型不可用 |
完整列表见错误码与请求标识。需要区分同一状态码下的具体原因时,以 message 为准,或在请求日志中查看这次请求的「状态说明」。网关的响应中不包含请求 ID,排查时请在日志详情顶部复制 Request ID。