跳转到内容

Anthropic Messages 协议

Protocol value
anthropic_messages
Request path
/v1/messages
Main code areas
src/protocol/anthropic/messages/wiresrc/provider/anthropic_messagessrc/ingress/anthropic_messagessrc/translation/anthropic_messages
Conversion targets
pass-through selfopenai_responses

当前运行时已支持 anthropic_messages -> anthropic_messages 透传。完整 wire model 已在协议层建模,协议转换和 provider 兼容处理仍应保持显式、可测试。

Anthropic Messages 请求主体是 MessageCreateParamsBase

struct MessageCreateParamsBase {
max_tokens: u32,
messages: Vec<MessageParam>,
model: String,
stream: Option<bool>,
system: Option<SystemPrompt>,
tools: Option<Vec<ToolUnion>>,
tool_choice: Option<ToolChoice>,
thinking: Option<ThinkingConfigParam>,
temperature: Option<Number>,
top_k: Option<u32>,
top_p: Option<Number>,
stop_sequences: Option<Vec<String>>,
...
}

运行时 ingress 目前只要求 body 是 JSON 且包含非空 model,然后保留原始 body:

struct PreparedAnthropicMessagesRequest {
body: Vec<u8>,
model: String,
}

当前 wire request body 直接由 MessageCreateParamsBase 表示;不再额外保留薄封装 projection 类型。

Anthropic 的 system 不在 messages 数组里,而是单独字段:

enum SystemPrompt {
Text(String),
Blocks(Vec<TypedTextBlockParam>),
}

消息由 MessageParam 表示:

struct MessageParam {
content: MessageParamContent,
role: Role,
}
enum MessageParamContent {
Text(String),
Blocks(Vec<ContentBlockParam>),
}

ContentBlockParam 是请求侧 block union,包含文本、图片、文档、搜索结果、思考块、工具调用、工具结果、server tool 相关结果等。

Anthropic 非流式响应由 Message 表示:

struct Message {
id: String,
content: Vec<ContentBlock>,
model: String,
role: Role,
type: MessageType,
stop_reason: Option<StopReason>,
stop_sequence: Option<String>,
usage: Usage,
...
}

响应内容是 ContentBlock

enum ContentBlock {
Text(TextBlock),
Thinking(ThinkingBlock),
RedactedThinking(RedactedThinkingBlock),
ToolUse(ToolUseBlock),
ServerToolUse(ServerToolUseBlock),
WebSearchToolResult(WebSearchToolResultBlock),
WebFetchToolResult(WebFetchToolResultBlock),
CodeExecutionToolResult(CodeExecutionToolResultBlock),
BashCodeExecutionToolResult(BashCodeExecutionToolResultBlock),
TextEditorCodeExecutionToolResult(TextEditorCodeExecutionToolResultBlock),
ToolSearchToolResult(ToolSearchToolResultBlock),
ContainerUpload(ContainerUploadBlock),
}

这里的重点是 Anthropic 把模型输出组织为 content blocks,而不是 OpenAI Responses 的 output items 或 Chat Completions 的单个 assistant message。

Anthropic 的用户端工具定义是 custom Tool

struct Tool {
name: String,
description: Option<String>,
input_schema: InputSchema,
strict: Option<bool>,
cache_control: Option<CacheControlEphemeral>,
...
}

模型请求客户端执行工具时,响应内容里出现 ContentBlock::ToolUse

struct ToolUseBlock {
id: String,
caller: ToolCaller,
input: Value,
name: String,
}

客户端执行后,在下一轮请求里用 ContentBlockParam::ToolResult 回填:

struct ToolResultBlockParam {
tool_use_id: String,
content: Option<ToolResultContentParam>,
is_error: Option<bool>,
cache_control: Option<CacheControlEphemeral>,
}

关联字段是 tool_use_id,它对应上一轮 ToolUseBlock.idis_error 用于表达客户端工具执行失败。

tool_choice 控制模型工具选择:

enum ToolChoice {
Auto(ToolChoiceAuto),
Any(ToolChoiceAny),
Tool(ToolChoiceTool),
None(ToolChoiceNone),
}

AutoAnyTool 支持 disable_parallel_tool_use,用于限制并行工具调用。

Anthropic 的协议模型显式区分 server tools。请求侧 ToolUnion 可以包含自定义工具和多种服务端工具,例如:

  • bash_20250124
  • code_execution_20250522
  • code_execution_20250825
  • code_execution_20260120
  • memory_20250818
  • text_editor_*
  • web_search_*
  • web_fetch_*
  • tool_search_*

服务端工具调用在响应内容中表现为 ServerToolUseBlock

struct ServerToolUseBlock {
id: String,
caller: ToolCaller,
input: Value,
name: ServerToolName,
}

服务端工具结果通过专门的 result block 返回:

struct ServerToolResultBlock {
id: String,
caller: ToolCaller,
input: Value,
name: ServerToolName,
content: ServerToolResultContent,
error: Option<ToolErrorBlock>,
type: String,
}

ServerToolName 覆盖 web、code execution、bash、text editor、tool search 等服务端能力。

ToolCaller 表示是谁触发了这个工具:

enum ToolCaller {
Direct(DirectCaller),
CodeExecution20250825(ServerToolCaller),
CodeExecution20260120(ServerToolCaller20260120),
}

这允许协议表达“直接由模型调用”和“由某个 code execution 工具继续调用其他 server tool”的区别。

Anthropic Messages 流事件由 MessageStreamEvent 表示:

enum MessageStreamEvent {
Ping(PingEvent),
MessageStart(MessageStartEvent),
MessageDelta(MessageDeltaEvent),
MessageStop(MessageStopEvent),
ContentBlockStart(ContentBlockStartEvent),
ContentBlockDelta(ContentBlockDeltaEvent),
ContentBlockStop(ContentBlockStopEvent),
}

典型事件顺序:

message_start
content_block_start
content_block_delta*
content_block_stop
message_delta
message_stop

ping 是保活事件,不改变消息内容。

内容增量由 ContentBlockDelta 表示:

enum ContentBlockDelta {
TextDelta(TextDelta),
InputJsonDelta(InputJsonDelta),
CitationsDelta(CitationsDelta),
ThinkingDelta(ThinkingDelta),
SignatureDelta(SignatureDelta),
}

文本流使用 TextDelta.text。工具输入流通常使用 InputJsonDelta.partial_json,客户端需要按 content block index 聚合 partial JSON。思考内容使用 ThinkingDelta,签名使用 SignatureDelta

block 通过 index 关联:

  • ContentBlockStartEvent.index:开始一个 content block。
  • ContentBlockDeltaEvent.index:给对应 block 增量追加内容。
  • ContentBlockStopEvent.index:该 block 结束。

消息级结束由 MessageStopEvent 表示;MessageDeltaEvent 携带 stop_reasonstop_sequence 和增量 usage。

Anthropic Messages 的输出单元是 assistant 消息中的 content[] 数组。与 Responses 的顶层 output[] 不同,Anthropic 把所有输出内容(文本、思考、工具调用、服务端工具调用)都嵌套在一个 Message.content[] 里。

同一个“并行读取两个文件”的工具调用,在 Anthropic 中表现为一个 assistant 消息里的多个 tool_use block:

{
"role": "assistant",
"content": [
{"type": "tool_use", "id": "tu_1", "name": "read_file", "input": {"path": "src/main.rs"}},
{"type": "tool_use", "id": "tu_2", "name": "read_file", "input": {"path": "Cargo.toml"}}
]
}

对应的工具结果在下一个 user 消息中以 tool_result block 回填,同样可以包含多个 result block:

{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "tu_1", "content": "fn main() {...}"},
{"type": "tool_result", "tool_use_id": "tu_2", "content": "[package]\nname = ..."}
]
}

Anthropic 通过 tool_choice 中的 disable_parallel_tool_use 字段控制是否允许并行工具调用:

{"type": "auto", "disable_parallel_tool_use": true}

当设为 true 时,模型每次只会产出一个 tool_use block。省略或设为 false 时,模型可以在单个 assistant 消息中产出多个 tool_use block。

OpenAI 协议的对应字段是请求级 parallel_tool_calls: false。proxai 在 openai_responses -> anthropic_messages 转换时,将 parallel_tool_calls: false 映射为 tool_choice.disable_parallel_tool_use: true

Anthropic 要求工具调用和工具结果严格相邻:

  1. assistant 消息包含一个或多个 tool_use block。
  2. 紧接着的 user 消息必须包含对应的 tool_result block(每个 tool_use_id 恰好匹配一个 result)。
  3. 中间不能插入其他 role 的消息。

部分 provider(如 MiniMax M3)严格执行此约束,对拆分或交错的工具轮次会返回 tool call result does not follow tool call (2013) 错误。proxai 在翻译时保持相邻 tool_use / tool_result 的聚合,避免拆散并行工具调用。

SSE 流式时,多个并行 tool_useinput_json_delta 事件可以交错到达,通过 ContentBlockStartEvent.indexContentBlockDeltaEvent.index 区分属于哪个 block:

index=0 input_json_delta {"path":
index=1 input_json_delta {"path":
index=0 input_json_delta "src/main.rs"}
index=1 input_json_delta "Cargo.toml"}

这与 Responses 通过 item_id + output_index 区分并行 item 的方式类似,但 Anthropic 使用的是消息内 content block 的数组 index。

关于 Anthropic 与其他协议在流式标识模型、事件粒度、以及跨协议翻译时如何生成缺失的 item_id / index,详见 协议转换## Streaming identifier model and parallel assembly 章节。

完整的多轮 SSE 示例已经拆到独立页面,避免协议概览页过长。

anthropic_messages -> anthropic_messages 当前是透传:

  1. ingress 校验 JSON 和非空 model
  2. translation 构造 ProviderRequest::AnthropicMessages,保留原始 body。
  3. provider 直接转发到上游。
  4. 非流式响应原样返回并可 capture。
  5. SSE 响应保留原始 bytes 和 headers;AnthropicSseObserver 使用 SseEventScanner 扫描事件,将 MessageStreamEvent 归纳到 AnthropicResponseState,并记录 completed/closed/error 日志。AnthropicCompatible provider 会先经过 provider-local SSE normalization。

当前 Anthropic observer 不重组语义流,也不注入工具参数超时诊断。协议层的 MessageStreamEvent 主要用于 provider response 观察、摘要和跨协议翻译。