Python 调用 Claude API 全流程:基础调用、流式输出、多轮对话,踩坑记录一次讲完

最近在做一个文档自动化的副业项目,选型的时候试了一圈,最后还是选了 Claude------代码生成质量和长文本理解都比较稳。

但中文教程真的少,官方文档是全英文的,搜到的大部分教程要么是旧版 SDK,要么只有 Hello World 就结束了。

所以自己踩完坑,写了这篇。本文覆盖:

  • 环境配置 & SDK 安装
  • 基础调用(同步)
  • 流式输出(stream)
  • System Prompt 设置
  • 多轮对话管理
  • 错误处理 & 自动重试
  • 一个可直接用的封装类

代码全部验证可运行,Python 3.10+ 均适用。


一、环境准备

bash 复制代码
# 建议用虚拟环境,避免依赖冲突
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

pip install anthropic

最低版本要求anthropic >= 0.39.0,旧版接口有 breaking change,建议直接装最新。


二、最简基础调用

先跑通一个最简单的例子,确认环境没问题:

python 复制代码
import anthropic

client = anthropic.Anthropic(
    api_key="your-api-key",           # 替换成你的 Key
    base_url="https://api.yutaikeji.cn" # 国内中转节点,省去连接问题
)

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,            # 必填,不传会报错
    messages=[
        {"role": "user", "content": "用一句话解释什么是闭包"}
    ]
)

print(message.content[0].text)

坑1max_tokens 是必传参数,忘了会直接抛 ValidationError,官方文档没有特别标注,很容易漏。


三、流式输出(Stream)

做聊天界面的话,流式输出是刚需,不然用户盯着空白页等 2 秒体验很差。

python 复制代码
import anthropic

client = anthropic.Anthropic(
    api_key="your-api-key",
    base_url="https://api.yutaikeji.cn"
)

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "写一首关于深夜写代码的诗"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)  # flush=True 很关键,否则输出会卡住

流式结束后,还能拿到完整的 usage 信息:

python 复制代码
with client.messages.stream(...) as stream:
    full_text = ""
    for text in stream.text_stream:
        print(text, end="", flush=True)
        full_text += text

    # 流结束后获取完整 response
    final_message = stream.get_final_message()
    print(f"\n\n--- Token 用量 ---")
    print(f"输入: {final_message.usage.input_tokens}")
    print(f"输出: {final_message.usage.output_tokens}")

坑2 :用 stream.text_stream 是最简单的方式,直接拿文本片段。如果你需要更底层的事件(比如 tool_use),用 stream 迭代原始事件。


四、System Prompt 配置

Claude 的 system 参数是独立的,不放在 messages 里,这点和 OpenAI 不一样。

python 复制代码
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=2048,
    system="""你是一个资深 Python 工程师助手。
回答规则:
1. 优先给可运行代码,再给解释
2. 代码中加必要注释
3. 指出潜在 bug 或性能问题""",
    messages=[
        {"role": "user", "content": "写一个带 LRU 缓存的斐波那契函数"}
    ]
)

print(response.content[0].text)

坑3 :如果你从 OpenAI 迁移过来,习惯把 system 放在 messages 第一条------Claude 也支持这种写法,但官方推荐用独立的 system 参数,两种都能用,保持一致即可。


五、多轮对话

多轮对话需要自己维护 messages 列表,每轮都要把历史带上:

python 复制代码
import anthropic

client = anthropic.Anthropic(
    api_key="your-api-key",
    base_url="https://api.yutaikeji.cn"
)

def chat():
    messages = []
    print("Claude 助手(输入 exit 退出)\n")

    while True:
        user_input = input("你: ").strip()
        if user_input.lower() == "exit":
            break
        if not user_input:
            continue

        # 把用户消息加入历史
        messages.append({"role": "user", "content": user_input})

        # 流式输出回复
        print("Claude: ", end="", flush=True)
        reply = ""

        with client.messages.stream(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            messages=messages
        ) as stream:
            for text in stream.text_stream:
                print(text, end="", flush=True)
                reply += text

        print("\n")
        # 把 Claude 的回复也加入历史,下轮对话才有上下文
        messages.append({"role": "assistant", "content": reply})

if __name__ == "__main__":
    chat()

坑4 :很多人会忘记把 assistant 的回复也加入 messages,然后发现 Claude 每轮都失忆了。要两侧都 append。

关于上下文长度:Claude 3.5 Sonnet 支持 200K token 的上下文窗口,但长对话还是要做截断处理,否则 Token 费用会快速膨胀。一个简单策略是保留最近 N 轮:

python 复制代码
MAX_HISTORY = 20  # 保留最近 10 轮对话(20条消息)

if len(messages) > MAX_HISTORY:
    messages = messages[-MAX_HISTORY:]  # 只保留最近的

六、错误处理与自动重试

生产环境里不能让程序一遇到 API 错误就崩。常见错误码:

错误码 含义 处理策略
429 Rate Limit,请求过快 指数退避重试
529 API 过载 等待后重试
500/503 服务端问题 重试最多 3 次
400 请求参数错误 不重试,修复参数
python 复制代码
import time
import anthropic
from anthropic import RateLimitError, APIStatusError, APITimeoutError

client = anthropic.Anthropic(
    api_key="your-api-key",
    base_url="https://api.yutaikeji.cn"
)

def call_with_retry(messages: list, max_retries: int = 3) -> str:
    for attempt in range(max_retries):
        try:
            response = client.messages.create(
                model="claude-sonnet-4-6",
                max_tokens=1024,
                messages=messages
            )
            return response.content[0].text

        except RateLimitError:
            wait = 2 ** attempt  # 1s, 2s, 4s 指数退避
            print(f"触发限流,{wait}s 后重试(第 {attempt + 1} 次)...")
            time.sleep(wait)

        except APITimeoutError:
            print(f"请求超时,重试中(第 {attempt + 1} 次)...")
            time.sleep(1)

        except APIStatusError as e:
            if e.status_code in (500, 503, 529):
                wait = 2 ** attempt
                print(f"服务端错误 {e.status_code},{wait}s 后重试...")
                time.sleep(wait)
            else:
                raise  # 400 之类的参数错误,直接抛出不重试

    raise RuntimeError(f"重试 {max_retries} 次后仍失败")

七、一个可直接用的封装类

把上面的逻辑整合成一个干净的 ClaudeClient

python 复制代码
import time
import anthropic
from anthropic import RateLimitError, APIStatusError, APITimeoutError
from typing import Generator

class ClaudeClient:
    def __init__(
        self,
        api_key: str,
        base_url: str = "https://api.yutaikeji.cn",
        model: str = "claude-sonnet-4-6",
        system: str = "",
        max_history: int = 20
    ):
        self.client = anthropic.Anthropic(api_key=api_key, base_url=base_url)
        self.model = model
        self.system = system
        self.max_history = max_history
        self.messages: list = []

    def _trim_history(self):
        """保留最近 N 条对话"""
        if len(self.messages) > self.max_history:
            self.messages = self.messages[-self.max_history:]

    def chat(self, user_input: str, stream: bool = False):
        """
        发送消息,返回回复文本。
        stream=True 时返回生成器,逐字输出。
        """
        self.messages.append({"role": "user", "content": user_input})
        self._trim_history()

        kwargs = dict(
            model=self.model,
            max_tokens=2048,
            messages=self.messages,
        )
        if self.system:
            kwargs["system"] = self.system

        if stream:
            return self._stream_chat(kwargs)
        else:
            return self._sync_chat(kwargs)

    def _sync_chat(self, kwargs: dict) -> str:
        for attempt in range(3):
            try:
                resp = self.client.messages.create(**kwargs)
                reply = resp.content[0].text
                self.messages.append({"role": "assistant", "content": reply})
                return reply
            except (RateLimitError, APITimeoutError):
                time.sleep(2 ** attempt)
            except APIStatusError as e:
                if e.status_code in (500, 503, 529):
                    time.sleep(2 ** attempt)
                else:
                    raise
        raise RuntimeError("请求失败,已重试 3 次")

    def _stream_chat(self, kwargs: dict) -> Generator[str, None, None]:
        reply = ""
        with self.client.messages.stream(**kwargs) as s:
            for text in s.text_stream:
                reply += text
                yield text
        self.messages.append({"role": "assistant", "content": reply})

    def reset(self):
        """清空对话历史"""
        self.messages = []


# 使用示例
if __name__ == "__main__":
    claude = ClaudeClient(
        api_key="your-api-key",
        system="你是一个 Python 专家,回答简洁,优先给代码"
    )

    # 同步调用
    print(claude.chat("用 Python 写一个单例模式"))

    # 流式调用
    for chunk in claude.chat("给上面的代码加线程安全", stream=True):
        print(chunk, end="", flush=True)
    print()

八、常见报错速查

报错信息 原因 解决方法
Missing required parameter: max_tokens 漏传 max_tokens 加上 max_tokens=1024
Invalid API Key Key 填错或已过期 检查 Key,注意别带多余空格
Connection timeout 网络问题 检查 base_url,国内建议走中转
Rate limit exceeded 请求太快 加指数退避重试
'ContentBlock' object has no attribute 'text' 旧版写法 改成 response.content[0].text

小结

场景 写法
单次问答 client.messages.create()
流式输出 client.messages.stream()
多轮对话 手动维护 messages 列表,两端都要 append
错误处理 try/except + 指数退避,区分可重试/不可重试错误
生产封装 用上面的 ClaudeClient 类,开箱即用

如果还有问题评论区说,我基本每天都在,看到会及时回复。


工具推荐

本文所有代码在以下环境测试通过:

  • Python 3.10 / anthropic 0.40+
  • API:玉兔AI(可以直连 Claude API)
相关推荐
codingWhat7 小时前
Claude Code 使用技巧:把“聪明实习生”变成你的王牌搭档
claude
itpretty9 小时前
手搓一只迷你小龙虾(Claude Code CLI + Telegram)
人工智能·ai编程·claude
arvin_xiaoting11 小时前
三角协作架构:从问题发现到验证完成
架构·系统架构·llm·claude·ai agent·openclaw·多代理协作
SkyWalking中文站12 小时前
AI Coding 如何重塑软件架构师的工作方式
claude·gemini·vibecoding
冯磊爱学习12 小时前
全程不写一行代码,我如何用 AI 做出一个复杂的小说分析系统
claude
cooldream200913 小时前
Claude Code 安装与使用指南:以MiniMax M2.5 为例的完整实践
claude·claude code·minimax m2.5
Swift社区1 天前
最强生图模型NanoBanana 2,一手深度测- 附教程
gpt·claude·gemini
一乐小哥1 天前
明明登录成功,Claude Code 为什么还报 401 无效令牌?
openai·ai编程·claude
用户277552501401 天前
给 Claude Code 加上中文超能力:Spring Boot DDD + 飞书钉钉 MCP 实战
claude