Claude API多轮对话与System Prompt实战:上下文管理与角色设定指南
2026/9/1 4:37:35 网站建设 项目流程

最近在准备 Claude 相关体系化学习时,很多同学都卡在同一个地方:第一次调用 Claude API 成功后,不知道如何让模型在多轮对话里保持上下文,也不知道 System Prompt 到底应该放在哪个位置、和用户消息有什么区别。这两个问题恰好是 Claude API 知识体系中非常关键的组成,也是很多人从“能调通接口”走向“能设计完整对话应用”的分水岭。

这篇文章围绕 Claude API 学习路线的第二部分展开,聚焦 Conversations(多轮会话)与 System(系统提示)两个核心概念。文章会从最基础的概念讲起,配合完整的 Python 和 HTTP 调用示例,逐步演示如何实现带角色设定的多轮对话,并整理高频报错的排查思路和工程落地建议。无论你是准备 Claude 相关认证,还是在做实际项目集成,这篇文章都能帮你把基础打牢。

1. 背景与核心概念

1.1 什么是 Conversations

在 Claude API 的语境里,Conversations 表示的并不是一个像数据库会话那样由服务端长期维持的连接,而是模型在一次连续交互中看到的消息序列。

Claude 的 Messages API 设计上是无状态的。也就是说,每一次请求,模型并不会自动记住你上一次问了什么。为了让模型在多轮对话中表现正常,客户端必须把之前的对话历史拼接好,在每次请求时一起发给 API。这个由历史消息组成的序列,就是 Conversations。

从研究者的角度看,这有点像语言模型的工作记忆机制:模型每处理一个新请求,都要重新读取全部相关上下文,才能生成合理回复。因此 Conversations 的管理本质上是一个“上下文重建”的过程。

1.2 什么是 System

与 Conversations 不同,System 是独立于用户消息和助手回复之外的一条指令层,用来设定模型在整个对话期间需要遵守的全局规则。

System Prompt 可以做的事情非常多,比如:

  • 指定角色身份,例如“你是一名资深后端工程师”;
  • 规定输出格式,例如“请以 JSON 返回”;
  • 划定回答边界,例如“只回答与数据库相关的问题”;
  • 注入业务约束,例如“如果信息不足,请明确要求用户补充”。

在 Claude API 的请求参数中,System 是通过顶层参数传入的,而不是放在 messages 数组里作为一条普通消息。这一点非常重要,很多新手会把 system 消息写成 messages 数组里的role: "system",导致行为不符合预期。

1.3 为什么掌握这两部分是认证和实战的必修课

如果你在准备 Claude Certified Architect 或类似的进阶认证,Claude API 知识体系里 Conversations 和 System 几乎是必考的基础支柱。理由也很直接:

认证考察的不是“会不会调用一个接口”,而是你能不能设计出一个稳定、可控、可扩展的对话应用。而“稳定”依赖对多轮会话的正确处理,“可控”则依赖 System Prompt 的设计。

从工程角度看,这两个能力直接决定产品体验:

  • 没有正确的多轮会话管理,用户一多问两句,模型的回答就开始“失忆”;
  • 没有好的 System Prompt,模型输出就会飘忽不定,难以满足业务约束。

因此,这篇文章会花较多篇幅在原理和代码示例上,而不是只给结论。

2. 环境准备与 API 基础

2.1 准备 API Key 与网络环境

调用 Claude API 的第一步是准备 API Key。

访问 Anthropic 控制台,登录后可以在 API Keys 页面创建密钥。创建后要立即复制保存,因为密钥只会完整显示一次。

这里强调两个安全习惯:

  1. API Key 是敏感凭据,不要提交到 Git 仓库,不要把 Key 硬编码到前端代码中;
  2. 服务端调用时,建议通过环境变量注入。

在本地开发和测试时,可以把 Key 写入环境变量。以 Windows 和 macOS/Linux 为例,常见做法如下:

# macOS / Linux export ANTHROPIC_API_KEY="sk-ant-xxxx" # Windows PowerShell $env:ANTHROPIC_API_KEY="sk-ant-xxxx"

2.2 安装 Python SDK

官方提供了anthropicPython SDK,安装命令如下:

pip install anthropic

安装完成后,可以验证版本:

python -c "import anthropic; print(anthropic.__version__)"

如果显示正常版本号,说明 SDK 已安装成功。

这里需要提醒一句:SDK 版本更新比较频繁,不同版本之间的 API 参数可能有细微差异,示例代码中的用法以最常见的 SDK 写法为准,如果遇到参数报错,优先查看当前版本的官方文档。

2.3 第一个 Messages 请求

先写一个最简单的请求,验证 API Key 和网络环境是否正常。

# 文件路径:quickstart.py import anthropic client = anthropic.Anthropic() response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[ {"role": "user", "content": "你好,请用一句话介绍你自己。"} ] ) print(response.content[0].text)

运行:

python quickstart.py

这里有三点需要说明:

  • model参数指定要使用的模型名称,实际可用模型以你的账号权限和官方文档为准;
  • max_tokens是必填参数,表示模型最多生成多少个 token;
  • messages参数接收一个消息数组,单轮对话时只需要包含一条 user 消息。

如果代码运行顺利,你会看到模型返回一段自我介绍。但请注意,这个请求里没有任何 System Prompt,也没有历史消息,当用户继续追问“我刚才问了你什么”时,模型是回答不上来的。

3. Conversations 多轮会话机制拆解

3.1 API 的无状态设计

理解 Claude API 的多轮会话,最关键的一点就是接受“API 无状态”这个事实。

什么是无状态?

简单来说,Claude 不会在服务端保存你每一次调用的上下文。每一次messages.create请求,模型都只根据当前请求内的参数生成回复。

这样的设计带来了一个好处:API 服务器不需要维护每个用户的对话状态,请求易于横向扩展,也更容易做负载均衡。但对开发者来说,这意味着你必须自己管理对话历史。

来看一个直观对比。

错误示例:两次请求互相独立,模型无法得知第一次请求的内容。

import anthropic client = anthropic.Anthropic() # 第一次请求 response1 = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[ {"role": "user", "content": "我的名字是张三。"} ] ) print("第一次回复:", response1.content[0].text) # 第二次请求,没有携带历史 response2 = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[ {"role": "user", "content": "我叫什么名字?"} ] ) print("第二次回复:", response2.content[0].text)

运行后你会发现,第二次请求模型并不知道你叫张三,因为它没有收到第一条消息。

正确示例:把历史消息按顺序传给模型。

import anthropic client = anthropic.Anthropic() messages = [ {"role": "user", "content": "我的名字是张三。"}, {"role": "assistant", "content": "好的,张三,很高兴认识你!有什么我可以帮你的吗?"}, {"role": "user", "content": "我叫什么名字?"} ] response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=messages ) print("回复:", response.content[0].text)

这次模型能正确回答出“张三”。

3.2 messages 数组的结构与 role 规则

Claude API 的 messages 数组遵循一个非常清晰的规则:

  • 数组内按时间顺序排列消息;
  • 每条消息包含rolecontent两个核心字段;
  • 支持的 role 有userassistant
  • 消息角色应该严格交替,不能出现连续两条 user 或连续两条 assistant。

为什么角色要交替?因为模型的训练数据中,对话通常是 user/assistant 轮流出现的。如果请求中出现连续的两条 user 消息,模型在理解上会产生歧义,无法判断哪一条是用户的当前意图。

开发者常见的错误是每次请求时只把新增的用户消息追加到数组中,忽略了上一轮模型的回复。正确的做法是:

messages.append({"role": "user", "content": "当前用户输入"}) response = client.messages.create(..., messages=messages) messages.append({"role": "assistant", "content": response.content[0].text})

也就是说,每次请求后,要把模型的回复也追加到历史数组中,下一次请求再整体携带。

3.3 多轮对话的完整实现

下面用一个完整示例演示如何构建多轮对话循环。

# 文件路径:conversation_demo.py import anthropic client = anthropic.Anthropic() messages = [] print("开始对话,输入 exit 退出。") while True: user_input = input("你:") if user_input.lower() == "exit": break messages.append({"role": "user", "content": user_input}) response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=messages ) assistant_reply = response.content[0].text print("Claude:", assistant_reply) messages.append({"role": "assistant", "content": assistant_reply})

这个脚本的关键点在于:

  • 维护一个messages列表作为对话历史;
  • 每次用户输入后,先追加 user 消息;
  • 请求完成后,再把 assistant 回复追加到列表;
  • 下一轮循环时,messages 记录了完整上下文。

3.4 上下文长度与 Token 管理

多轮对话看似简单,但实践中最容易踩坑的就是 Token 超出限制。

Claude 模型的上下文长度是有限的。不同模型的上下文窗口大小不同,以常见的 Sonnet 模型为例,通常有 200K 级别的上下文窗口,但实际使用时还会受到max_tokens参数和请求头字节数的影响。

当历史消息过多时,你会遇到类似下面的错误:

Request too large for the model

或者:

This conversation has too many messages

解决思路通常有几种:

  1. 滑动窗口截断:只保留最近 N 轮消息,丢弃最早的历史;
  2. 摘要压缩:当历史超过阈值时,让模型把之前的对话生成一段摘要,之后用摘要替代原始历史;
  3. 关键信息抽取:只保留用户资料、偏好、待办事项等结构化信息,丢弃闲聊内容。

滑动窗口是最简单的方案,适合 MVP 阶段。下面是一个示例:

MAX_HISTORY_ROUNDS = 5 # 最多保留 5 轮 def trim_messages(messages: list, max_rounds: int = MAX_HISTORY_ROUNDS) -> list: # messages 长度为 round * 2(每轮包含 user 和 assistant) max_len = max_rounds * 2 if len(messages) <= max_len: return messages # 保留最后 max_len 条,同时始终保留第一条 user 消息作为上下文开场 return [messages[0]] + messages[-max_len:]

这个函数的核心思路是保留一条固定的开场消息,同时只保留最近 N 轮对话。这种方式能有效控制 Token 消耗,缺点是比较粗暴,可能丢失早期关键信息。生产环境中更推荐结合摘要压缩方案。

4. System Prompt 详解与设计方法

4.1 System 参数的位置与作用

System Prompt 在 Claude API 中有独立的参数位置。以 Python SDK 为例,它位于顶层:

response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, system="你是一名严谨的数据库管理员,回答时只提供经过验证的 SQL 建议。", messages=[ {"role": "user", "content": "如何优化这条查询?"} ] )

很多同学会把 System Prompt 写成 messages 数组里的一条:

messages = [ {"role": "system", "content": "你是助手"}, {"role": "user", "content": "你好"} ]

这种做法在 Claude Messages API 中是不正确的。Messages API 的 messages 数组只接受 user 和 assistant 两种 role,system 必须放在顶层参数中。如果你使用了不支持的消息角色,SDK 会抛出参数校验错误。

4.2 System Prompt 与 User Prompt 的优先级

System Prompt 的指令优先级高于普通用户消息中的提示。这意味着,即使在用户输入中出现了“忽略你之前的设定”这类内容,模型通常会优先遵循 System Prompt 中设定的规则。

为什么?

因为 System Prompt 从机制上位于消息序列的更高层级,它在模型内部被视作对话的全局配置。模型在生成回复时,会先参考系统指令,再理解用户请求。

这一特性对工程应用非常有用。你可以在 System Prompt 中写死安全边界和输出规范,而不必担心用户通过输入内容绕过限制。但要注意,任何提示词都有被“越狱”的可能性,System Prompt 不是安全边界,而是一种行为引导。对于敏感场景,仍然需要服务端的权限校验和内容过滤。

4.3 System Prompt 的设计技巧

设计一个好的 System Prompt,可以从以下四个维度入手。

第一,角色明确。

不要只说“你是一个助手”,更有效的写法是:

你是一名拥有 8 年经验的 Python 后端工程师,擅长 FastAPI 和 PostgreSQL,回答问题时优先考虑性能与可维护性。

角色越具体,模型的语言风格和专业知识倾向就越明确。

第二,行为约束。

给模型设定执行规则,例如:

1. 如果问题信息不足,先列出你缺少的关键信息,再请求用户补充; 2. 回答必须包含方案理由,不能只给结论; 3. 代码示例必须使用 Python 3.10+ 语法。

第三,输出格式规范。

对于需要程序进一步处理的场景,直接规定格式:

请严格按照以下 JSON 结构返回结果,不要包含多余文字: {"answer": "你的回答", "confidence": 0.0-1.0}

第四,边界兜底。

告诉模型遇到什么情况该拒绝或明确说明:

如果用户询问的内容与当前业务无关,请礼貌拒绝并引导回到主题。 如果你不确定答案,请直接说“我不确定”,不要编造信息。

4.4 带 System Prompt 的多轮对话示例

将 System Prompt 与多轮对话结合,才是实际项目中最常见的形态。

# 文件路径:customer_service_demo.py import anthropic client = anthropic.Anthropic() system_prompt = """ 你是一家电商平台的智能客服助手。 请遵循以下规则: 1. 回答简洁,不超过 100 字; 2. 涉及订单问题时,先确认用户订单号; 3. 遇到无法解决的问题,引导用户联系人工客服; 4. 不要编造促销活动信息。 """ messages = [] print("客服助手已上线,输入 exit 退出。") while True: user_input = input("用户:") if user_input.lower() == "exit": break messages.append({"role": "user", "content": user_input}) response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, system=system_prompt, messages=messages ) assistant_reply = response.content[0].text print("客服:", assistant_reply) messages.append({"role": "assistant", "content": assistant_reply})

这个示例体现了 System Prompt 的两个价值:

  1. 所有轮次共享同一套规则,用户如何追问,模型的回答边界都不会漂移;
  2. 历史对话通过 messages 数组传递,配合 system 参数,模型能同时理解“全局规则”和“实时上下文”。

5. 完整实战:构建一个带 System Prompt 的多轮对话助手

这一节我们做一个综合实战,把前文的概念串起来。

5.1 需求分析

目标:实现一个“数据库优化助手”,应具备以下能力:

  • 设定用户为数据库架构师角色;
  • 输出格式统一为“问题分析 + 优化建议”;
  • 当用户没有提供表结构或 SQL 时,主动要求补充;
  • 支持多轮追问;
  • 保留最近的对话上下文。

5.2 项目结构

db_assistant/ ├── config.py # 配置项 ├── assistant.py # 对话助手核心逻辑 └── main.py # 命令行入口

5.3 核心代码

先写配置模块。

# 文件路径:config.py MODEL_NAME = "claude-3-5-sonnet-20241022" MAX_TOKENS = 1024 MAX_HISTORY_ROUNDS = 6 SYSTEM_PROMPT = """ 你是一名资深数据库架构师,专注于 MySQL 和 PostgreSQL 的性能优化。 你的工作方式: 1. 如果用户没有提供完整的 SQL 和表结构,请先请求补充,不要直接猜测; 2. 每次回答分为两部分:问题分析、优化建议; 3. 优化建议必须先写结论,再写理由; 4. 涉及索引优化时,需要说明索引生效的条件; 5. 回答要专业,但避免堆砌术语。 """

接下来是对话助手的核心逻辑。

# 文件路径:assistant.py import anthropic from config import MODEL_NAME, MAX_TOKENS, SYSTEM_PROMPT class DBAssistant: def __init__(self): self.client = anthropic.Anthropic() self.messages = [] self.max_history_rounds = 6 def _trim_history(self): """裁剪历史消息,保留最开始的 user 消息和最近 N 轮对话。""" max_len = self.max_history_rounds * 2 if len(self.messages) <= max_len: return self.messages = [self.messages[0]] + self.messages[-max_len:] def ask(self, user_input: str) -> str: self.messages.append({"role": "user", "content": user_input}) response = self.client.messages.create( model=MODEL_NAME, max_tokens=MAX_TOKENS, system=SYSTEM_PROMPT, messages=self.messages ) reply = response.content[0].text self.messages.append({"role": "assistant", "content": reply}) self._trim_history() return reply

最后是命令行入口。

# 文件路径:main.py from assistant import DBAssistant def main(): assistant = DBAssistant() print("数据库优化助手已启动,输入 exit 退出。\n") while True: question = input("你:") if question.lower() == "exit": break answer = assistant.ask(question) print("\n助手:") print(answer) print() if __name__ == "__main__": main()

5.4 运行与验证

执行:

python main.py

推荐按以下顺序测试:

  1. 直接问一句“我的查询很慢怎么办”,模型应该会要求你补充 SQL 和表结构;
  2. 提供完整的 SQL 和表结构,模型应该按“问题分析 + 优化建议”的结构回答;
  3. 接着追问“如果去掉这个索引呢”,模型应该能结合上一轮的对话上下文继续分析。

这里要重点说明_trim_history的作用。当对话轮次超过 6 轮后,旧消息被裁剪,但始终保留第一条 user 消息,这样模型能维持基本的任务背景,又不会让请求体无限膨胀。

5.5 结果说明

运行效果大致如下:

你:我的查询很慢怎么办 助手: 问题分析:你还没有提供具体的 SQL 语句和表结构,我无法判断慢查询的根因。 优化建议:请先补充以下信息—— 1. 完整的 SQL 语句; 2. 涉及的建表语句或表结构; 3. 表的数据量和索引情况。

如果你接着补充表结构,它会基于上下文给出更有针对性的优化建议。

6. 常见问题与排查思路

6.1 API Error: 529 Overloaded

这是 Claude API 使用中最常见的高频错误之一,错误信息类似:

api error: 529 overloaded. this is a server-side issue, usually temporary.

含义是 Anthropic 服务端当前负载过高,暂时无法处理请求。这不是你的代码问题,而是服务端暂时性过载。

处理建议:

  1. 不要立即高频重试,避免加重服务端压力;
  2. 使用指数退避策略,例如第一次等待 1 秒、第二次等待 2 秒、第三次等待 4 秒;
  3. 在服务端代码中加入重试逻辑,但设置最大重试次数。

Python 示例:

import time import anthropic client = anthropic.Anthropic() def create_with_retry(max_retries=5, **kwargs): for attempt in range(max_retries): try: return client.messages.create(**kwargs) except anthropic.APIStatusError as e: if e.status_code == 529 and attempt < max_retries - 1: time.sleep(2 ** attempt) continue raise

6.2 请求体过大或上下文超限

当对话历史过长时,会收到类似“Request too large”的错误。

处理思路:

  • 对历史消息做滑动窗口裁剪;
  • 将早期上下文压缩成摘要;
  • 将大段历史写入外部存储,只保留结构化摘要;
  • 使用更长的上下文窗口模型(如果业务允许)。

6.3 多轮对话模型“失忆”

这是最常见的开发误区。根因不是模型问题,而是你没有在请求中传递历史消息。

排查顺序:

  1. 检查 messages 数组是否包含之前的 user 和 assistant 消息;
  2. 检查 messages 顺序是否是从早到晚;
  3. 检查代码中是否有误把历史列表重置为空。

6.4 消息角色错误

如果你在 messages 中使用了role: "system",SDK 会报参数错误。

正确做法是把 System Prompt 放到system顶层参数中,messages 数组只保留 user 和 assistant。

6.5 认证与权限错误

如果返回 401 或 403,通常是 API Key 无效或账号权限不足。

排查建议:

  • 确认 API Key 复制完整,没有多余空格;
  • 确认环境变量已正确加载;
  • 确认账号有权限访问指定的模型。

6.6 常见问题速查表

问题现象常见原因解决思路
529 Overloaded服务端过载指数退避重试,错峰请求
上下文超限历史消息无限累积滑动窗口裁剪或摘要压缩
模型“失忆”请求未携带完整历史每次请求拼接 messages 历史
system 参数报错把 system 写入 messages 数组使用顶层 system 参数
401 UnauthorizedAPI Key 错误检查 Key 与环境变量
403 Forbidden账号无模型权限检查账号权限并申请对应模型

7. 最佳实践与工程建议

7.1 会话管理:宁可多传,不可漏传

在 Token 预算允许的情况下,多传历史消息通常比少传更安全。模型看到完整上下文,才能做出一致的回答。但需要注意,历史消息越长,响应延迟和成本越高。

工程上建议:

  • 设置每轮对话的最大历史轮数;
  • 对关键业务信息(用户身份、偏好、订单号)做结构化抽取;
  • 在 API 层对历史列表做序列化缓存,方便后续审计和追踪。

7.2 System Prompt:用版本化思维管理

System Prompt 是产品的“灵魂”,它会直接影响输出质量。建议把它当作代码一样管理:

  • 不要直接在请求中拼接字符串,而是放在配置中心或单独文件中;
  • 每次修改保留历史版本,方便 AB 测试;
  • 使用变量占位符,根据不同场景注入不同配置。

示例:

system_prompt_template = """ 你是{role}。 规则: 1. {rule_1} 2. {rule_2} """ system_prompt = system_prompt_template.format( role="电商客服", rule_1="回答不超过 100 字", rule_2="不要编造订单信息" )

7.3 异常处理与重试策略

任何外部 API 调用都必须假设可能失败。建议在项目中统一封装请求逻辑:

  • 捕获网络异常、超时、限流、服务端错误;
  • 对 429 和 529 做指数退避重试;
  • 对 4xx 错误直接上报,不要无意义重试;
  • 为所有外部调用添加超时时间,避免线程长时间阻塞。

7.4 安全边界与权限控制

即使 System Prompt 设定了禁止行为,也不能把它当作安全防线。对于敏感业务:

  • 在应用层校验用户输入,过滤危险指令;
  • 对模型输出做内容审核;
  • API Key 只保存在服务端环境变量中,不传递到前端;
  • 对用户身份做鉴权后再调用模型接口。

7.5 成本控制

多轮对话的成本随历史消息增长而上升。建议在请求前估算 token 数,超过阈值时自动压缩历史。可以通过response.usage字段查看每次请求消耗的 token 数,并据此调整策略。

response = client.messages.create(...) print(response.usage) # 输出类似:Usage(input_tokens=123, output_tokens=45)

8. 总结与学习路线

通过这篇文章,我们从原理到实战完整拆解了 Claude API 中的两个核心知识点。

关于 Conversations,最关键的是理解 API 的无状态设计,并把多轮对话历史的拼接、裁剪和管理作为工程问题来处理。所有“模型失忆”的根因,几乎都能追溯到请求中缺少历史消息或历史消息顺序错乱。

关于 System,最重要的转变是意识到它和普通用户消息不是一回事。System Prompt 是全局规则层,适合放置角色、格式、边界等稳定配置,而 messages 数组则负责承载实时对话流。

如果继续深入学习,建议按下面的路线延伸:

  1. 掌握 Claude API 完整的请求参数,包括 temperature、top_p、stop_sequences 等采样参数;
  2. 学习 Function Calling / Tool Use,让模型具备调用外部工具的能力;
  3. 研究流式输出(Streaming),优化对话应用的交互体验;
  4. 了解 Embedding 与 RAG,把知识库能力接入对话系统;
  5. 最后回到认证体系本身,把 API 能力映射到架构设计题目中。

实际项目中,优先关注的是会话历史的成本控制、System Prompt 的版本管理,以及错误重试的稳定性设计。这三件事做好了,对话应用的可用性会明显提升。

希望这篇文章能帮你在 Claude API 的学习中少走一些弯路。如果本文对你有帮助,可以收藏备用,遇到具体问题时也欢迎在评论区留言交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询