- 新增 ConversionEngine 核心引擎,支持 OpenAI 和 Anthropic 协议转换 - 添加 stream decoder/encoder 实现 - 更新 provider client 支持新引擎 - 补充单元测试和集成测试 - 更新 specs 文档
38 KiB
Anthropic Messages API 端到端测试用例与 Mock 设计
本文档针对 POST /v1/messages 接口,设计端到端测试用例及对应的 Mock 返回值结构。
一、测试场景总览
| 分类 | 测试用例 | 优先级 |
|---|---|---|
| 基础对话 | 1. 单轮纯文本对话 | P0 |
| 基础对话 | 2. 多轮对话(user + assistant + user) | P0 |
| 系统消息 | 3. 字符串 system prompt | P0 |
| 系统消息 | 4. 数组 system prompt(多文本块) | P1 |
| 消息内容 | 5. 用户消息含图片(base64 source) | P1 |
| 消息内容 | 6. 用户消息含图片(URL source) | P1 |
| 消息内容 | 7. 用户消息含 tool_result 内容块 | P0 |
| 工具调用 | 8. 单工具调用 | P0 |
| 工具调用 | 9. 并行多工具调用 | P1 |
| 工具调用 | 10. tool_choice 为 "none" | P2 |
| 工具调用 | 11. tool_choice 指定具体工具名 | P2 |
| 工具调用 | 12. tool_choice 为 "any"(强制调用) | P2 |
| 参数控制 | 13. temperature + top_p + top_k | P1 |
| 参数控制 | 14. max_tokens 截断 | P1 |
| 参数控制 | 15. stop_sequences 截断 | P2 |
| 扩展思考 | 16. thinking enabled(extended thinking) | P1 |
| 扩展思考 | 17. thinking adaptive | P2 |
| 输出格式 | 18. output_config 为 json_object | P1 |
| 输出格式 | 19. output_config 为 json_schema(structured output) | P1 |
| 流式响应 | 20. 流式文本响应(SSE) | P0 |
| 流式响应 | 21. 流式工具调用 | P1 |
| 流式响应 | 22. 流式 + thinking 内容 | P1 |
| 缓存控制 | 23. system prompt cache_control(ephemeral) | P2 |
| 元数据 | 24. metadata.user_id | P2 |
| 错误处理 | 25. 无效 model 返回 400 | P1 |
| 错误处理 | 26. 缺少 max_tokens 返回 400 | P1 |
| 错误处理 | 27. 缺少 messages 返回 400 | P1 |
| 错误处理 | 28. 内容安全策略拒绝(refusal) | P2 |
| 其他 | 29. disable_parallel_tool_use | P2 |
| 其他 | 30. container 复用 | P2 |
二、测试用例详情
用例 1:单轮纯文本对话
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好,请介绍一下你自己"
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test001",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "你好!我是 Claude,由 Anthropic 开发的 AI 助手。我可以帮你回答问题、写作、编程、数学推理、数据分析等多种任务。我的知识截止到 2025 年 1 月。请问有什么我可以帮你的?"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 15,
"output_tokens": 68
}
}
验证点:
content[0].type=="text"stop_reason=="end_turn"usage字段完整
用例 2:多轮对话
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "什么是 Go 语言的 interface?"
},
{
"role": "assistant",
"content": "Go 语言的 interface 是一种类型,它定义了一组方法签名。任何实现了这些方法的类型都自动实现了该 interface,无需显式声明。这是 Go 语言"隐式接口"设计的核心特点。"
},
{
"role": "user",
"content": "能举个例子吗?"
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test002",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "当然。例如定义一个 `Reader` interface:\n\n```go\ntype Reader interface {\n Read(p []byte) (n int, err error)\n}\n```\n\n`os.File` 和 `bytes.Buffer` 都实现了 `Read` 方法,所以它们都自动实现了 `Reader` interface,无需显式声明 `implements Reader`。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 120,
"output_tokens": 85
}
}
用例 3:字符串 system prompt
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"system": "你是一个专业的编程助手,请用简洁的语言回答问题。",
"messages": [
{
"role": "user",
"content": "什么是递归?"
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test003",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "递归是一种函数调用自身来解决问题的方法。它包含两个关键部分:\n\n1. **基准情况**:直接返回结果的终止条件\n2. **递归情况**:将问题分解为更小的子问题,调用自身解决\n\n例如计算阶乘:`n! = n × (n-1)!`,基准情况是 `0! = 1`。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 45,
"output_tokens": 72
}
}
用例 4:数组 system prompt(多文本块)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "你是一个专业的编程助手。"
},
{
"type": "text",
"text": "请用简洁的语言回答问题,并尽量提供代码示例。"
}
],
"messages": [
{
"role": "user",
"content": "什么是闭包?"
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test004",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "闭包是指函数可以访问并"记住"其定义时所在作用域中的变量,即使在该作用域已经执行完毕后仍然可以访问。\n\n```javascript\nfunction makeCounter() {\n let count = 0;\n return function() {\n count++;\n return count;\n };\n}\nconst counter = makeCounter();\nconsole.log(counter()); // 1\nconsole.log(counter()); // 2\n```\n\n`count` 变量被内部函数"捕获",形成了闭包。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 85,
"output_tokens": 95
}
}
用例 5:用户消息含图片(base64 source)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "这张图片里有什么?"
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "/9j/4AAQSkZJRgABAQEAYABgAAD..."
}
}
]
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test005",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "图片中有一只橘色的猫,它正躺在棕色的沙发上晒太阳。猫的毛色是橘白相间的,眼睛是绿色的,看起来非常放松和满足。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 1280,
"output_tokens": 45
}
}
用例 6:用户消息含图片(URL source)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "请描述这张图片"
},
{
"type": "image",
"source": {
"type": "url",
"url": "https://example.com/images/landscape.jpg"
}
}
]
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test006",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "这是一幅美丽的自然风景图。前景是一片翠绿的草地,点缀着野花。中景有一条蜿蜒的小河,河水清澈见底。远处是连绵起伏的青山,山顶覆盖着白雪。天空湛蓝,飘着几朵白云。整体画面给人一种宁静祥和的感觉。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 1520,
"output_tokens": 82
}
}
用例 7:用户消息含 tool_result 内容块
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "北京天气怎么样?"
},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01test001",
"name": "get_weather",
"input": {"city": "北京"}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01test001",
"content": "北京今天晴,气温 25°C,东南风 2 级。"
}
]
}
],
"tools": [
{
"name": "get_weather",
"description": "获取指定城市的天气信息",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
}
},
"required": ["city"]
}
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test007",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "北京今天天气很好,晴天,气温 25°C,东南风 2 级,非常适合外出活动。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 250,
"output_tokens": 35
}
}
验证点:
- 能正确理解 tool_result 内容
- 基于工具返回结果生成自然语言回复
用例 8:单工具调用
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "北京天气怎么样?"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取指定城市的天气信息",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
}
},
"required": ["city"]
}
}
],
"tool_choice": {
"type": "auto"
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test008",
"type": "message",
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01test008",
"name": "get_weather",
"input": {
"city": "北京"
}
}
],
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 180,
"output_tokens": 42
}
}
验证点:
stop_reason=="tool_use"content[0].type=="tool_use"input包含正确的参数
用例 9:并行多工具调用
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "帮我查一下北京、上海、广州三个城市的天气"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取指定城市的天气信息",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test009",
"type": "message",
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01test009a",
"name": "get_weather",
"input": {"city": "北京"}
},
{
"type": "tool_use",
"id": "toolu_01test009b",
"name": "get_weather",
"input": {"city": "上海"}
},
{
"type": "tool_use",
"id": "toolu_01test009c",
"name": "get_weather",
"input": {"city": "广州"}
}
],
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 200,
"output_tokens": 120
}
}
验证点:
content数组包含 3 个 tool_use 块- 每个 tool_use 有不同的 ID
用例 10:tool_choice 为 "none"
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "随便聊聊"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取天气",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"}
}
}
}
],
"tool_choice": {
"type": "none"
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test010",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "好的!今天天气不错,很适合聊天。你想聊些什么话题呢?可以是技术、生活、娱乐等任何你感兴趣的内容。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 150,
"output_tokens": 42
}
}
验证点:
- 尽管定义了 tools,响应中不包含 tool_use 块
- 纯文本回复
用例 11:tool_choice 指定具体工具名
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "查天气"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取天气",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}}
},
{
"name": "get_news",
"description": "获取新闻",
"input_schema": {"type": "object", "properties": {"topic": {"type": "string"}}}
}
],
"tool_choice": {
"type": "tool",
"name": "get_weather"
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test011",
"type": "message",
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01test011",
"name": "get_weather",
"input": {}
}
],
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 200,
"output_tokens": 35
}
}
验证点:
- 强制使用了
get_weather而非get_news
用例 12:tool_choice 为 "any"(强制调用)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "帮我做点什么"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取天气",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}}
}
],
"tool_choice": {
"type": "any"
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test012",
"type": "message",
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01test012",
"name": "get_weather",
"input": {}
}
],
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 150,
"output_tokens": 30
}
}
验证点:
- 模型必须调用至少一个工具
用例 13:temperature + top_p + top_k
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "写一首关于春天的短诗"
}
],
"temperature": 0.9,
"top_p": 0.95,
"top_k": 50
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test013",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "春风拂面花自开,\n柳絮飞舞满园香。\n燕归巢中呢喃语,\n万物复苏迎朝阳。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 25,
"output_tokens": 32
}
}
用例 14:max_tokens 截断
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 20,
"messages": [
{
"role": "user",
"content": "请详细介绍一下人工智能的发展历史"
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test014",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "人工智能起源于1950年代,图灵提出了机器能否思考的问题。1956年达特茅斯会议正式确立了AI领域。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "max_tokens",
"stop_sequence": null,
"usage": {
"input_tokens": 22,
"output_tokens": 20
}
}
验证点:
stop_reason=="max_tokens"output_tokens== 20(等于 max_tokens)- 内容被截断
用例 15:stop_sequences 截断
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "从1数到10,每行一个数字"
}
],
"stop_sequences": ["5"]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test015",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "1\n2\n3\n4\n"
}
],
"model": "claude-opus-4-7",
"stop_reason": "stop_sequence",
"stop_sequence": "5",
"usage": {
"input_tokens": 22,
"output_tokens": 10
}
}
验证点:
stop_reason=="stop_sequence"stop_sequence字段包含触发截断的序列
用例 16:thinking enabled(extended thinking)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "一个房间里有3个灯泡,房间外有3个开关,每个开关控制一个灯泡。你只能进房间一次,如何确定哪个开关控制哪个灯泡?"
}
],
"thinking": {
"type": "enabled",
"budget_tokens": 2048
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test016",
"type": "message",
"role": "assistant",
"content": [
{
"type": "thinking",
"thinking": "这是一个经典的逻辑推理问题。我需要找到一种方法,通过一次观察来区分三个开关。关键是灯泡有两种可观察的状态:亮或灭。但如果只用亮灭,我只能区分一个开关(开的那个)。我需要找到另一种可观察的属性...温度!灯泡开着会发热。所以我可以:先开一个开关等一会儿,然后关掉,再开另一个开关,然后进房间。亮着的是第二个开关,热的是第一个开关,冷且灭的是第三个开关。"
},
{
"type": "text",
"text": "解决方案如下:\n\n1. **打开第一个开关**,等待几分钟\n2. **关闭第一个开关**,**打开第二个开关**\n3. **立即进入房间**\n\n此时观察:\n- **亮着的灯** → 由第二个开关控制\n- **摸起来发热的灯** → 由第一个开关控制(因为开过几分钟)\n- **既不亮也不热的灯** → 由第三个开关控制"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 95,
"output_tokens": 280
}
}
验证点:
content包含thinking块和text块output_tokens包含 thinking tokens
用例 17:thinking adaptive
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "请帮我分析一下这个算法问题"
}
],
"thinking": {
"type": "adaptive",
"budget_tokens": 1024
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test017",
"type": "message",
"role": "assistant",
"content": [
{
"type": "thinking",
"thinking": "用户提到了一个算法问题,但没有具体说明是什么问题。我需要请用户提供更多细节。"
},
{
"type": "text",
"text": "我很乐意帮你分析算法问题!不过你没有具体说明是什么问题。请详细描述一下:\n\n1. 问题的具体要求是什么?\n2. 输入输出的格式是怎样的?\n3. 有没有什么约束条件?\n\n有了这些信息后,我可以帮你分析解题思路和最优解法。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 35,
"output_tokens": 95
}
}
用例 18:output_config 为 json_object
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "提取以下信息的姓名和年龄:张三,今年25岁,是一名工程师"
}
],
"output_config": {
"format": {
"type": "json_object"
}
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test018",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "{\"name\": \"张三\", \"age\": 25, \"occupation\": \"工程师\"}"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 45,
"output_tokens": 28
}
}
验证点:
content[0].text是合法的 JSON 字符串
用例 19:output_config 为 json_schema(structured output)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "创建一个用户信息记录,姓名李四,年龄30岁"
}
],
"output_config": {
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"email": {"type": "string"}
},
"required": ["name", "age"]
}
}
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test019",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "{\"name\": \"李四\", \"age\": 30}"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 120,
"output_tokens": 22
}
}
验证点:
- 输出严格符合 JSON schema 定义
- 不包含 schema 未定义的字段
用例 20:流式文本响应(SSE)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好"
}
],
"stream": true
}
期望 Mock 响应(200 OK, Content-Type: text/event-stream):
event: message_start
data: {"type":"message","id":"msg_01stream001","role":"assistant","content":[],"model":"claude-opus-4-7","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"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":"你"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"好"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"!"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"我"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"是"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Claude"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"很高兴"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"为你"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"服务"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"。"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8}}
event: message_stop
data: {"type":"message_stop"}
验证点:
- 事件顺序:message_start → content_block_start → content_block_delta* → content_block_stop → message_delta → message_stop
message_start包含完整的 message 对象- 每个
content_block_delta包含增量文本 message_delta包含 stop_reason 和最终 usage- 以
message_stop结束
用例 21:流式工具调用
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "北京天气怎么样?"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取天气",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
}
],
"stream": true
}
期望 Mock 响应(200 OK, Content-Type: text/event-stream):
event: message_start
data: {"type":"message","id":"msg_01stream003","role":"assistant","content":[],"model":"claude-opus-4-7","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":180,"output_tokens":0}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_01stream003","name":"get_weather"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"city"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"\":\""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"北京"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"}\""}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":42}}
event: message_stop
data: {"type":"message_stop"}
验证点:
content_block_start包含 tool_use 类型和工具名input_json_delta逐步拼接完整的 JSON 输入stop_reason=="tool_use"
用例 22:流式 + thinking 内容
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "1+1=?"
}
],
"thinking": {
"type": "enabled",
"budget_tokens": 1024
},
"stream": true
}
期望 Mock 响应(200 OK, Content-Type: text/event-stream):
event: message_start
data: {"type":"message","id":"msg_01stream004","role":"assistant","content":[],"model":"claude-opus-4-7","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":30,"output_tokens":0}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"这是一个简单的"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"数学问题。"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"1+1=2"}}
event: content_block_stop
data: {"type":"content_block_stop","index":1}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":25}}
event: message_stop
data: {"type":"message_stop"}
验证点:
- 先有 thinking 块,再有 text 块
thinking_delta类型正确- 两个内容块有不同的 index
用例 23:system prompt cache_control(ephemeral)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "你是一个专业的编程助手。",
"cache_control": {
"type": "ephemeral"
}
}
],
"messages": [
{
"role": "user",
"content": "你好"
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test023",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "你好!我是 Claude,很高兴为你服务。我是由 Anthropic 开发的 AI 助手,可以帮你解答问题、写作、编程等各种任务。请问有什么我可以帮你的?"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 25,
"output_tokens": 52,
"cache_creation_input_tokens": 15,
"cache_read_input_tokens": 0
}
}
验证点:
cache_creation_input_tokens反映缓存创建的 token 数
用例 24:metadata.user_id
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好"
}
],
"metadata": {
"user_id": "user_12345"
}
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test024",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "你好!我是 Claude,很高兴为你服务。请问有什么我可以帮你的?"
}
],
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 18
}
}
用例 25:无效 model 返回 400
请求:
{
"model": "nonexistent-model-xyz",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好"
}
]
}
期望 Mock 响应(400 Bad Request):
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "The model 'nonexistent-model-xyz' does not exist or is not available."
}
}
用例 26:缺少 max_tokens 返回 400
请求:
{
"model": "claude-opus-4-7",
"messages": [
{
"role": "user",
"content": "你好"
}
]
}
期望 Mock 响应(400 Bad Request):
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "'max_tokens' is a required parameter for the Messages API."
}
}
用例 27:缺少 messages 返回 400
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024
}
期望 Mock 响应(400 Bad Request):
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "'messages' is a required parameter and must be a non-empty array."
}
}
用例 28:内容安全策略拒绝(refusal)
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "生成违法内容..."
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test028",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "抱歉,我无法协助生成此类内容。我的设计原则是提供有益、安全和负责任的帮助。如果你有其他问题或需要帮助的地方,我很乐意为你服务。"
}
],
"model": "claude-opus-4-7",
"stop_reason": "refusal",
"stop_sequence": null,
"stop_details": {
"type": "refusal",
"category": "cyber",
"explanation": "请求内容违反了使用政策。"
},
"usage": {
"input_tokens": 18,
"output_tokens": 42
}
}
验证点:
stop_reason=="refusal"stop_details包含 refusal 详情
用例 29:disable_parallel_tool_use
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "帮我查北京和上海的天气"
}
],
"tools": [
{
"name": "get_weather",
"description": "获取天气",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}}
}
],
"disable_parallel_tool_use": true
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test029",
"type": "message",
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01test029",
"name": "get_weather",
"input": {"city": "北京"}
}
],
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 120,
"output_tokens": 35
}
}
验证点:
- 只调用了一个工具(而非并行调用两个)
用例 30:container 复用
请求:
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "运行一段 Python 代码"
}
],
"tools": [
{
"type": "code_execution_20250825",
"name": "code_execution",
"container": {
"type": "python",
"version": "3.11"
}
}
]
}
期望 Mock 响应(200 OK):
{
"id": "msg_01test030",
"type": "message",
"role": "assistant",
"content": [
{
"type": "server_tool_use",
"id": "toolu_01test030",
"name": "code_execution",
"input": {
"code": "print('Hello, World!')",
"language": "python"
},
"caller": {
"type": "direct"
}
}
],
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 150,
"output_tokens": 55
}
}
三、Mock 响应通用结构规范
非流式响应通用结构
{
"id": "msg_<unique_id>",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text" | "tool_use" | "thinking" | "redacted_thinking" | "server_tool_use" | "web_search_tool_result" | "web_fetch_tool_result" | "code_execution_tool_result" | "bash_code_execution_tool_result" | "text_editor_code_execution_tool_result" | "tool_search_tool_result",
"text": "<text_content>" | null,
"id": "toolu_<id>" | null,
"name": "<tool_name>" | null,
"input": { /* tool input */ } | null,
"thinking": "<thinking_content>" | null,
"data": "<redacted_data>" | null
}
],
"model": "<model_name>",
"stop_reason": "end_turn" | "max_tokens" | "stop_sequence" | "tool_use" | "pause_turn" | "refusal",
"stop_sequence": "<sequence>" | null,
"stop_details": {
"type": "refusal",
"category": "cyber" | "bio",
"explanation": "<explanation>"
} | null,
"usage": {
"input_tokens": <int>,
"output_tokens": <int>,
"cache_read_input_tokens": <int> | null,
"cache_creation_input_tokens": <int> | null
}
}
流式 Event 通用结构
| Event Type | Key Fields |
|---|---|
message_start |
type: "message", id, role, content: [], model, stop_reason: null, usage |
content_block_start |
type: "content_block_start", index, content_block: {type, ...} |
content_block_delta |
type: "content_block_delta", index, delta: {type, text/partial_json/thinking} |
content_block_stop |
type: "content_block_stop", index |
message_delta |
type: "message_delta", delta: {stop_reason, stop_sequence}, usage: {output_tokens} |
message_stop |
type: "message_stop" |
错误响应通用结构
{
"type": "error",
"error": {
"type": "invalid_request_error" | "authentication_error" | "permission_error" | "not_found_error" | "rate_limit_error" | "api_error",
"message": "<human_readable_message>"
}
}
请求必需参数
| 参数 | 类型 | 说明 |
|---|---|---|
model |
string | 模型标识符(如 claude-opus-4-7) |
max_tokens |
integer | 最大生成 token 数(必需) |
messages |
array | 消息数组,至少包含一条(必需) |
请求可选参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
system |
string/array | - | 系统 prompt |
temperature |
number | 1.0 | 采样温度(0.0-1.0) |
top_p |
number | - | 核采样阈值 |
top_k |
number | - | 从 top K 采样 |
stop_sequences |
array | - | 停止序列 |
tools |
array | - | 工具定义 |
tool_choice |
object | auto | 工具选择策略 |
thinking |
object | - | 扩展思考配置 |
output_config |
object | - | 输出格式配置 |
stream |
boolean | false | 是否流式 |
metadata |
object | - | 请求元数据 |
disable_parallel_tool_use |
boolean | false | 禁用并行工具调用 |
container |
any | - | 容器标识符 |
Stop Reason 枚举
| 值 | 含义 |
|---|---|
end_turn |
自然结束 |
max_tokens |
达到 max_tokens 限制 |
stop_sequence |
遇到自定义停止序列 |
tool_use |
模型调用了工具 |
pause_turn |
长运行 turn 暂停 |
refusal |
安全策略拒绝 |