用 Python 调用 Claude API:从发消息到结构化 JSON 输出

2026-05-20 27 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:11 分钟

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:对话列表,每条带 roleuserassistant)和 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 设计的几个坑

  1. 别把 schema 写得太松additionalProperties: false 要显式加上(SDK 的 strict 模式会自动处理),否则模型可能塞进你没预期的字段。
  2. 枚举字段比自由文本好控制:像 sentiment 这种有限集合,用 enum 约束比靠自然语言描述"请只输出 positive/negative/neutral"可靠得多。
  3. 嵌套别太深:两层嵌套没问题,三层以上模型出错的概率明显上升。复杂结构拆成多次调用更稳。

多轮对话:把上下文串起来

实际业务里,单轮调用很少能满足需求。多轮对话的做法是把完整的对话历史放进 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 约束,三件套缺一不可
错误处理 APIErrorRateLimitErrorOverloadedError 要分别捕获,给用户不同反馈
成本监控 response.usage.input_tokensresponse.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 把输出变成可信赖的数据结构,这条路径比一开始就堆复杂配置要稳得多。


相关推荐