第 13 章

Claude API 完全实战——从第一个请求到生产级集成

第13章:Claude API 完全实战——从第一个请求到生产级集成

本章学习目标:5分钟内跑通第一个 API 请求;掌握 Messages API 每个参数的实际影响;用 Python 和 TypeScript 实现流式输出;理解 Tool Use 工具调用的完整循环;用 Prompt Caching 把重复请求成本降低 90%;写出带指数退避的生产级错误处理。

快速入门:5分钟跑通第一个请求

pip install anthropic
export ANTHROPIC_API_KEY="sk-ant-api03-..."
# 或者放在项目 .env 文件,确保 .env 已加入 .gitignore
import anthropic

client = anthropic.Anthropic()  # 自动读取 ANTHROPIC_API_KEY 环境变量

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system="你是一个代码审查专家,只用中文回复,回复简洁专业。",
    messages=[
        {"role": "user", "content": "帮我审查这段代码:\n```python\ndef divide(a, b):\n    return a / b\n```"}
    ]
)

print(message.content[0].text)
print(f"消耗 tokens: 输入 {message.usage.input_tokens}, 输出 {message.usage.output_tokens}")

运行后你会看到 Claude 指出 b=0 时会抛出 ZeroDivisionError,并给出修复建议。这就是一次完整的 API 调用。

Messages API 参数完全解析

每个参数都有实际影响,不设置不等于"无所谓":

参数 常见值 效果 不设置会怎样
model claude-sonnet-4-6 决定智能水平和成本 必须设置,否则报错
max_tokens 1024–4096 限制输出长度,超出截断 必须设置,否则报错
temperature 0(代码)/ 0.7(创意) 输出随机性,0 最确定 默认 1.0,代码任务会乱
system 角色定义和规则 全程约束模型行为 模型回复更随意,无角色意识
top_p 通常不改 另一种随机性控制 默认 1.0,与 temperature 二选一调

temperature 的实际影响(重要):

流式输出(Streaming)

对于需要生成长文本的场景,流式输出让用户看到实时生成过程,感知速度从"等待5秒"变成"立刻看到内容":

import anthropic

client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=2048,
    messages=[{"role": "user", "content": "写一个完整的 FastAPI CRUD 接口"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)  # 实时打印,不换行

# 流结束后获取完整用量信息
final = stream.get_final_message()
print(f"\n输入 {final.usage.input_tokens} tokens,输出 {final.usage.output_tokens} tokens")

TypeScript / Next.js Route Handler 的流式实现:

// src/app/api/generate/route.ts
import Anthropic from "@anthropic-ai/sdk";
import { NextRequest } from "next/server";

const client = new Anthropic();

export async function POST(req: NextRequest) {
  const { message } = await req.json();

  const stream = await client.messages.stream({
    model: "claude-sonnet-4-6",
    max_tokens: 2048,
    messages: [{ role: "user", content: message }],
  });

  const readableStream = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        if (
          chunk.type === "content_block_delta" &&
          chunk.delta.type === "text_delta"
        ) {
          controller.enqueue(new TextEncoder().encode(chunk.delta.text));
        }
      }
      controller.close();
    },
  });

  return new Response(readableStream, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

Tool Use(函数调用)完整示例

Tool Use 让 Claude 能调用你定义的函数,是构建 Agent 的基础。工作流程:你定义工具描述 → Claude 决定调用哪个工具 → 你执行并返回结果 → Claude 基于结果给出最终回复。

import anthropic
import json

client = anthropic.Anthropic()

# 定义工具——本质是 JSON Schema 描述函数签名
tools = [
    {
        "name": "get_weather",
        "description": "获取指定城市的当前天气",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名称"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    },
    {
        "name": "query_database",
        "description": "查询数据库中的订单信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string"},
                "status": {"type": "string", "enum": ["pending", "completed", "cancelled"]}
            }
        }
    }
]

def handle_tool_call(tool_name: str, tool_input: dict) -> dict:
    """实际执行工具的函数——这里调用真实的 API 或数据库"""
    if tool_name == "get_weather":
        return {"temperature": 25, "condition": "晴天", "city": tool_input["city"]}
    elif tool_name == "query_database":
        return {"order_id": tool_input["order_id"], "status": "completed", "amount": 199.00}
    return {}

def chat_with_tools(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            tools=tools,
            messages=messages
        )

        if response.stop_reason == "tool_use":
            # Claude 要调用工具,处理所有 tool_use 块
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = handle_tool_call(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result, ensure_ascii=False)
                    })

            # 把工具结果发回给 Claude,继续对话
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # stop_reason == "end_turn",Claude 给出最终回复
            return response.content[0].text

result = chat_with_tools("北京今天天气怎么样?另外帮我查一下订单 ORD-12345 的状态")
print(result)

注意 Claude 可能同时调用多个工具: 一次响应中可能有多个 tool_use 块。上面的代码用循环处理所有块,这是正确做法。如果只处理第一个,在多工具场景下会出错。

Prompt Caching(成本优化关键)

使用场景: 每次请求都包含相同的大段内容——系统提示、参考文档、代码库上下文。不加 cache_control 的情况下,这些内容每次都按正常价格计费。加了之后,缓存命中时价格降低约 90%。

import anthropic

client = anthropic.Anthropic()

# 假设有一个 5000 token 的大型代码库上下文
long_codebase_context = "...(项目所有文件的内容,几千行)..."

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "你是代码审查专家,审查时只指出真实的 bug,不要泛泛而谈。",
            "cache_control": {"type": "ephemeral"}  # 标记为可缓存
        },
        {
            "type": "text",
            "text": long_codebase_context,
            "cache_control": {"type": "ephemeral"}  # 这段也缓存
        }
    ],
    messages=[{"role": "user", "content": "审查最新提交的 auth.py 文件"}]
)

# 查看缓存命中情况
usage = response.usage
print(f"缓存创建 tokens: {usage.cache_creation_input_tokens}")  # 首次:正常价格
print(f"缓存读取 tokens: {usage.cache_read_input_tokens}")      # 命中:约降 90%

实际节省计算: 如果 system prompt 有 5000 tokens,每次请求节省约 $0.075(Sonnet 价格 $3/MTok × 0.9 折扣)。一天 100 次请求节省 $7.5,一个月节省 $225。缓存有效期约 5 分钟,高频调用场景下收益显著。

生产级错误处理与重试

直接调用 API 而不做重试,在生产环境会遇到不必要的失败。Rate Limit 错误是最常见的——用指数退避重试几乎总能解决。

import anthropic
import time
from anthropic import APIStatusError, APIConnectionError, RateLimitError

client = anthropic.Anthropic()

def call_claude_with_retry(messages: list, max_retries: int = 3) -> str:
    """带指数退避重试的生产级 API 调用"""
    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:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt  # 指数退避:1s, 2s, 4s
                print(f"Rate limit,等待 {wait_time}s 后重试(第 {attempt + 1} 次)")
                time.sleep(wait_time)
            else:
                raise  # 重试耗尽,向上抛出

        except APIConnectionError:
            # 网络问题,短暂重试
            if attempt < max_retries - 1:
                time.sleep(1)
            else:
                raise

        except APIStatusError as e:
            if e.status_code >= 500:
                # 5xx 服务端错误,可重试
                if attempt < max_retries - 1:
                    time.sleep(2 ** attempt)
                    continue
            # 4xx 客户端错误(prompt 太长、参数错误等),不重试,直接报错
            raise

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

模型选择与成本参考(2025年)

模型 输入价格 输出价格 适合场景
claude-haiku-4-5 $0.80/MTok $4/MTok 简单分类、快速问答、大批量处理、意图识别
claude-sonnet-4-6 $3/MTok $15/MTok 复杂代码生成、深度分析、日常主力模型
claude-opus-4-6 $15/MTok $75/MTok 最复杂推理、架构设计、高精度要求任务

实际选型原则: 绝大多数场景用 Sonnet 就够了。只有当 Sonnet 明确给出错误答案时才升级到 Opus。Haiku 用于高频调用(每秒数十次)或对成本极敏感的批量任务——同等质量的简单任务,Haiku 比 Sonnet 便宜约 15 倍。

本章要点

  1. max_tokens 和 model 是必填参数,temperature 默认 1.0 对代码任务不友好,代码生成设为 0。
  2. 流式输出的关键是监听 content_block_delta 事件中的 text_delta,Python 用 .stream() 上下文管理器,TypeScript 用 for await 循环。
  3. Tool Use 必须处理循环:Claude 可能多轮调用工具,直到 stop_reason 为 end_turn 才是最终回复;一次响应中可能有多个 tool_use 块。
  4. Prompt Caching 只对重复的大段内容有效:cache_control 加在每次请求都相同的部分,变化的用户消息不加;缓存约 5 分钟有效期,低频场景收益有限。
  5. 重试逻辑区分错误类型:RateLimitError 和 5xx 用指数退避重试,4xx 客户端错误不重试(重试也不会成功)。
本章评分
4.7  / 5  (21 评分)

💬 留言讨论