API多轮对话
Messages API 深入:多轮对话
·约 10 分钟阅读
上一篇你学会了单次 API 调用。但实际应用中,用户和 Claude 之间通常需要多轮对话——就像聊天一样,你一句我一句。Messages API 通过 messages 数组来实现多轮对话,理解它的工作原理是构建任何 AI 应用的基础。
你将学到什么
- messages 数组的 role 机制(user / assistant / system)
- 如何构建多轮对话
- 上下文管理:何时保留、何时清理
- stop_reason 的实际应用
Messages 数组结构
Claude API 是无状态的——每次请求都是独立的。要实现多轮对话,你需要在每次请求中带上所有历史消息。
messages = [
{"role": "user", "content": "我叫张三"},
{"role": "assistant", "content": "你好张三!有什么我可以帮你的?"},
{"role": "user", "content": "你还记得我叫什么吗?"},
]
# Claude 能看到完整历史,所以知道你叫张三
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=messages,
)
三种角色:
- user:用户的消息
- assistant:Claude 的回复
- system:系统指令(通过单独的 system 参数传入,不在 messages 中)
Note: messages 数组必须以 user 消息开头,且 user/assistant 交替出现。不能连续出现两条相同角色的消息。
构建多轮对话应用
Python 版完整示例
import anthropic
client = anthropic.Anthropic()
def chat():
messages = []
system = "你是一位友好的中文助手。回答简洁有用。"
print("开始对话(输入 'quit' 退出)\n")
while True:
user_input = input("你: ")
if user_input.lower() == "quit":
break
messages.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048,
system=system,
messages=messages,
)
assistant_text = response.content[0].text
messages.append({"role": "assistant", "content": assistant_text})
print(f"Claude: {assistant_text}\n")
chat()
TypeScript 版
import Anthropic from "@anthropic-ai/sdk";
import * as readline from "readline";
const client = new Anthropic();
async function chat() {
const messages: Anthropic.MessageParam[] = [];
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const ask = (q: string) =>
new Promise<string>((resolve) => rl.question(q, resolve));
console.log("开始对话(输入 quit 退出)\n");
while (true) {
const input = await ask("你: ");
if (input === "quit") break;
messages.push({ role: "user", content: input });
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
system: "你是一位友好的中文助手。回答简洁有用。",
messages,
});
const text =
response.content[0].type === "text" ? response.content[0].text : "";
messages.push({ role: "assistant", content: text });
console.log("Claude:", text, "\n");
}
rl.close();
}
chat();
System Prompt 的作用
system 参数在每次请求中都会发送,但不计入 messages 数组。它设定了 Claude 在整个对话中的行为规则。
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="你是一位资深的 Python 导师。用简洁的中文回答问题,代码示例使用 Python 3.12+。",
messages=messages,
)
Tip: system prompt 对 Claude 的影响很大。在多轮对话中,它始终有效,不会被后续消息覆盖。
上下文管理策略
每条消息都消耗 token。随着对话变长,token 消耗会增加,成本也会上升。你需要管理上下文。
策略一:滑动窗口
只保留最近 N 轮对话。
MAX_TURNS = 20 # 保留最近 20 条消息
if len(messages) > MAX_TURNS:
messages = messages[-MAX_TURNS:]
策略二:总结压缩
当对话过长时,用 Claude 自己总结历史对话,替换详细记录。
if len(messages) > 30:
summary_response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=500,
messages=[{
"role": "user",
"content": "请用 200 字总结以下对话的关键信息:\n" +
"\n".join(m["content"] for m in messages)
}]
)
summary = summary_response.content[0].text
messages = [
{"role": "user", "content": f"以下是之前对话的总结:{summary}"},
{"role": "assistant", "content": "好的,我已了解之前的对话内容。请继续。"},
]
策略三:新建对话
某些场景下,话题切换时直接清空历史,开始新对话。
多内容块消息
一条消息可以包含多个内容块(文字 + 图片):
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "描述这张图片"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": base64_image_data,
}
}
]
}]
)
实战练习
Tip: 构建你的第一个多轮对话应用。
- 用上面的代码模板,创建一个终端聊天程序
- 加入上下文管理:当消息超过 20 条时自动截断
- 在对话中测试 Claude 是否记得之前提到的信息
关键要点
Note: 本文核心总结
- Claude API 无状态,多轮对话需要在每次请求中带上完整历史
- messages 数组以 user 开头,user/assistant 交替
- system prompt 独立于 messages,始终有效
- 注意管理上下文长度以控制成本