1
0
Files
nex/docs/api_reference/test_mock_anthropic.md
lanyuanxiaoyao bc1ee612d9 refactor: 实现 ConversionEngine 协议转换引擎,替代旧 protocol 包
- 新增 ConversionEngine 核心引擎,支持 OpenAI 和 Anthropic 协议转换
- 添加 stream decoder/encoder 实现
- 更新 provider client 支持新引擎
- 补充单元测试和集成测试
- 更新 specs 文档
2026-04-20 13:02:28 +08:00

1716 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 enabledextended thinking | P1 |
| 扩展思考 | 17. thinking adaptive | P2 |
| 输出格式 | 18. output_config 为 json_object | P1 |
| 输出格式 | 19. output_config 为 json_schemastructured output | P1 |
| 流式响应 | 20. 流式文本响应SSE | P0 |
| 流式响应 | 21. 流式工具调用 | P1 |
| 流式响应 | 22. 流式 + thinking 内容 | P1 |
| 缓存控制 | 23. system prompt cache_controlephemeral | 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单轮纯文本对话
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好,请介绍一下你自己"
}
]
}
```
**期望 Mock 响应200 OK**
```json
{
"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多轮对话
**请求:**
```json
{
"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**
```json
{
"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
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"system": "你是一个专业的编程助手,请用简洁的语言回答问题。",
"messages": [
{
"role": "user",
"content": "什么是递归?"
}
]
}
```
**期望 Mock 响应200 OK**
```json
{
"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多文本块
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "你是一个专业的编程助手。"
},
{
"type": "text",
"text": "请用简洁的语言回答问题,并尽量提供代码示例。"
}
],
"messages": [
{
"role": "user",
"content": "什么是闭包?"
}
]
}
```
**期望 Mock 响应200 OK**
```json
{
"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
**请求:**
```json
{
"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**
```json
{
"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
**请求:**
```json
{
"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**
```json
{
"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 内容块
**请求:**
```json
{
"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**
```json
{
"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单工具调用
**请求:**
```json
{
"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**
```json
{
"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并行多工具调用
**请求:**
```json
{
"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**
```json
{
"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
---
### 用例 10tool_choice 为 "none"
**请求:**
```json
{
"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**
```json
{
"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 块
- 纯文本回复
---
### 用例 11tool_choice 指定具体工具名
**请求:**
```json
{
"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**
```json
{
"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`
---
### 用例 12tool_choice 为 "any"(强制调用)
**请求:**
```json
{
"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**
```json
{
"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
}
}
```
**验证点:**
- 模型必须调用至少一个工具
---
### 用例 13temperature + top_p + top_k
**请求:**
```json
{
"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**
```json
{
"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
}
}
```
---
### 用例 14max_tokens 截断
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 20,
"messages": [
{
"role": "user",
"content": "请详细介绍一下人工智能的发展历史"
}
]
}
```
**期望 Mock 响应200 OK**
```json
{
"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
- 内容被截断
---
### 用例 15stop_sequences 截断
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "从1数到10每行一个数字"
}
],
"stop_sequences": ["5"]
}
```
**期望 Mock 响应200 OK**
```json
{
"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` 字段包含触发截断的序列
---
### 用例 16thinking enabledextended thinking
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "一个房间里有3个灯泡房间外有3个开关每个开关控制一个灯泡。你只能进房间一次如何确定哪个开关控制哪个灯泡"
}
],
"thinking": {
"type": "enabled",
"budget_tokens": 2048
}
}
```
**期望 Mock 响应200 OK**
```json
{
"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
---
### 用例 17thinking adaptive
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "请帮我分析一下这个算法问题"
}
],
"thinking": {
"type": "adaptive",
"budget_tokens": 1024
}
}
```
**期望 Mock 响应200 OK**
```json
{
"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
}
}
```
---
### 用例 18output_config 为 json_object
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "提取以下信息的姓名和年龄张三今年25岁是一名工程师"
}
],
"output_config": {
"format": {
"type": "json_object"
}
}
}
```
**期望 Mock 响应200 OK**
```json
{
"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 字符串
---
### 用例 19output_config 为 json_schemastructured output
**请求:**
```json
{
"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**
```json
{
"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
**请求:**
```json
{
"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流式工具调用
**请求:**
```json
{
"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 内容
**请求:**
```json
{
"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
---
### 用例 23system prompt cache_controlephemeral
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "你是一个专业的编程助手。",
"cache_control": {
"type": "ephemeral"
}
}
],
"messages": [
{
"role": "user",
"content": "你好"
}
]
}
```
**期望 Mock 响应200 OK**
```json
{
"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 数
---
### 用例 24metadata.user_id
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好"
}
],
"metadata": {
"user_id": "user_12345"
}
}
```
**期望 Mock 响应200 OK**
```json
{
"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
**请求:**
```json
{
"model": "nonexistent-model-xyz",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好"
}
]
}
```
**期望 Mock 响应400 Bad Request**
```json
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "The model 'nonexistent-model-xyz' does not exist or is not available."
}
}
```
---
### 用例 26缺少 max_tokens 返回 400
**请求:**
```json
{
"model": "claude-opus-4-7",
"messages": [
{
"role": "user",
"content": "你好"
}
]
}
```
**期望 Mock 响应400 Bad Request**
```json
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "'max_tokens' is a required parameter for the Messages API."
}
}
```
---
### 用例 27缺少 messages 返回 400
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024
}
```
**期望 Mock 响应400 Bad Request**
```json
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "'messages' is a required parameter and must be a non-empty array."
}
}
```
---
### 用例 28内容安全策略拒绝refusal
**请求:**
```json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "生成违法内容..."
}
]
}
```
**期望 Mock 响应200 OK**
```json
{
"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 详情
---
### 用例 29disable_parallel_tool_use
**请求:**
```json
{
"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**
```json
{
"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
}
}
```
**验证点:**
- 只调用了一个工具(而非并行调用两个)
---
### 用例 30container 复用
**请求:**
```json
{
"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**
```json
{
"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 响应通用结构规范
### 非流式响应通用结构
```json
{
"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"` |
### 错误响应通用结构
```json
{
"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` | 安全策略拒绝 |