Anthropic 的 Claude API 已经成为不少开发者在文本生成、信息抽取、代码辅助场景下的首选。但很多人拿到 API Key 后,只停留在"发一条消息、拿一段回复"的阶段,忽略了系统指令和结构化输出这两个真正让 API 融入生产流程的能力。这篇文章把三个核心操作——发送 prompt、设置 system prompt、用 schema 控制 JSON 输出——串成一条完整链路,每个环节都给出可直接运行的代码。
安装与认证:先把路打通
安装官方 SDK 只需一行:
pip install anthropic
认证方式有两种:环境变量或构造参数传入。推荐环境变量,避免密钥硬编码进代码仓库:
export ANTHROPIC_API_KEY="sk-ant-xxxxx"
SDK 会自动读取这个变量。如果你需要在代码里显式传入(比如多 Key 轮换),也可以:
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-xxxxx")
下面所有示例都基于这个 client 对象。
基础对话:发送 prompt 并拿到回复
最简单的调用——给 Claude 一条用户消息,拿到纯文本回复:
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=256,
messages=[
{"role": "user", "content": "用一句话解释什么是向量数据库。"}
],
)
print(response.content[0].text)
输出类似:
向量数据库是一种专门存储和检索高维向量数据的系统,常用于语义搜索和推荐场景,通过近似最近邻算法实现毫秒级相似度匹配。
几个关键参数说明:
model:模型名,claude-sonnet-4-20250514是当前性价比最高的选择;重精度用claude-opus-4-20250514,追速度用claude-haiku-4-20250514。max_tokens:控制输出长度上限,不是"期望长度"。设太小会截断回复,设太大不会浪费——API 按实际输出计费。messages:对话列表,每条带role(user或assistant)和content。多轮对话时把历史消息一起传入即可。
系统指令:给 Claude 定规矩
system 参数和 messages 平级,用来设定 Claude 的行为边界、角色身份、输出风格。它不在对话历史里,但每轮调用都会生效。
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=512,
system="你是一名资深后端工程师,只回答与 Python 和服务器架构相关的问题。回答风格:简洁、用代码说话、避免废话。如果问题超出范围,回复'超出我的专业范围'。",
messages=[
{"role": "user", "content": "怎么在 FastAPI 里做请求限流?"}
],
)
print(response.content[0].text)
你会拿到一段偏代码风格的回答,而不是泛泛的科普。系统指令的实用技巧:
- 角色锚定:写清楚"你是谁",比模糊的"请专业地回答"效果好得多。
- 格式约束:在 system 里要求"只输出 JSON,不要解释",比在 user prompt 里追加同样要求更稳定。
- 安全边界:限定回答范围,防止用户用巧妙 prompt 把对话拐到无关领域。
结构化输出:用 schema 让 Claude 返回可解析的 JSON
纯文本回复在聊天场景够用,但如果你要把 Claude 的输出喂给下游系统(数据库写入、API 返回、自动化流水线),就需要稳定的结构。Claude API 提供了 response_format 参数,配合 JSON Schema 强制模型输出符合预期的 JSON。
下面是一个完整示例:让 Claude 从一段产品评论里抽取结构化信息。
import anthropic
import json
client = anthropic.Anthropic() # 依赖环境变量 ANTHROPIC_API_KEY
# 定义你期望的 JSON 结构
json_schema = {
"type": "object",
"properties": {
"product_name": {
"type": "string",
"description": "评论中提到的产品名称"
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "neutral"],
"description": "评论的整体情感倾向"
},
"issues": {
"type": "array",
"items": {"type": "string"},
"description": "评论中提到的具体问题列表,没有则为空数组"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "推断的评分,1-5"
}
},
"required": ["product_name", "sentiment", "issues", "rating"]
}
review_text = """
买了这台 MX450 打印机用了一个月,打印速度还行,但经常卡纸,
驱动安装也折腾了好久。整体体验一般,给3分吧。
"""
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system="你是一个产品评论分析助手。只输出 JSON,不要任何额外文字。",
messages=[
{"role": "user", "content": f"请从以下评论中抽取信息:\n{review_text}"}
],
# 关键参数:强制 JSON 输出
extra_body={
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "review_extraction",
"schema": json_schema,
"strict": True # 启用严格模式,模型必须遵守 schema
}
}
},
)
# 解析结果
result = json.loads(response.content[0].text)
print(json.dumps(result, indent=2, ensure_ascii=False))
预期输出:
{
"product_name": "MX450",
"sentiment": "neutral",
"issues": [
"经常卡纸",
"驱动安装折腾"
],
"rating": 3
}
strict: True 是这里的关键开关——它要求模型必须填充 schema 中所有 required 字段,且字段类型、枚举值都不能偏离。如果模型判断某个字段实在无法从输入推断,它会填该类型的"零值"(字符串填空串,数组填空数组),而不是偷偷省略字段。这让下游解析不再需要兜容错逻辑。
schema 设计的几个坑
- 别把 schema 写得太松:
additionalProperties: false要显式加上(SDK 的 strict 模式会自动处理),否则模型可能塞进你没预期的字段。 - 枚举字段比自由文本好控制:像
sentiment这种有限集合,用enum约束比靠自然语言描述"请只输出 positive/negative/neutral"可靠得多。 - 嵌套别太深:两层嵌套没问题,三层以上模型出错的概率明显上升。复杂结构拆成多次调用更稳。
多轮对话:把上下文串起来
实际业务里,单轮调用很少能满足需求。多轮对话的做法是把完整的对话历史放进 messages:
conversation = [
{"role": "user", "content": "帮我设计一个 Redis 缓存策略,场景是电商商品详情页。"},
{"role": "assistant", "content": "建议用 Cache-Aside 模式……(上一轮的回复)"},
{"role": "user", "content": "如果缓存失效时并发请求很大怎么办?"},
]
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=512,
system="你是后端架构顾问,回答简洁,必要时给代码片段。",
messages=conversation,
)
print(response.content[0].text)
注意:messages 里不包含 system,系统指令始终是独立参数。对话历史越长,token 消耗越大,长对话场景要考虑截断或摘要历史。
实战清单:接入生产前要确认的事
| 项目 | 说明 |
|---|---|
| API Key 管理 | 用环境变量或密钥管理服务,绝不硬编码;多 Key 时做好轮换和失效切换 |
| token 预算 | max_tokens 设为业务所需上限,不是越大越好;输入+输出总 token 不能超过模型上下文窗口 |
| 重试与限流 | Anthropic 有速率限制(RPM/TPM),生产代码要加 tenacity 或类似库做指数退避重试 |
| schema 严格模式 | 需要结构化输出时,strict: True + required 全覆盖 + enum 约束,三件套缺一不可 |
| 错误处理 | APIError、RateLimitError、OverloadedError 要分别捕获,给用户不同反馈 |
| 成本监控 | response.usage.input_tokens 和 response.usage.output_tokens 每次调用都会返回,接入日志系统做日维度统计 |
一个带重试和错误处理的调用骨架:
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import anthropic
client = anthropic.Anthropic()
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, max=10),
retry=retry_if_exception_type(anthropic.RateLimitError),
)
def call_claude(prompt: str) -> str:
try:
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=256,
messages=[{"role": "user", "content": prompt}],
)
return response.content[0].text
except anthropic.APIError as e:
print(f"API 错误: {e.status_code} - {e.message}")
raise
# 使用
answer = call_claude("解释一下 Python 的 GIL。")
print(answer)
Claude API 的三个层次——发消息、定规矩、控结构——逐层叠加就能从"玩具级调用"演进到"生产级集成"。先把基础对话跑通,再加 system prompt 锁定行为,最后用 JSON Schema 把输出变成可信赖的数据结构,这条路径比一开始就堆复杂配置要稳得多。