第 3 章

Token 经济学:Input/Output/Thinking/Cache Token 精确计算与成本预估

第三章:API 快速上手:鉴权、限流、SDK 安装与第一个请求

3.1 获取 API Key

在调用 Claude API 之前,你需要一个 API Key。获取流程如下:

  1. 访问 console.anthropic.com
  2. 注册账户(需要邮箱验证)
  3. 进入 API Keys 页面
  4. 点击 Create Key,命名你的 key(建议按用途命名,如 production-chatbotdev-testing
  5. 复制并立即保存 API Key——它只会显示一次

API Key 的安全管理

API Key 拥有访问你账户的完整权限,包括消耗你的信用额度。永远不要

正确的做法是通过环境变量传递:

# Linux/macOS
export ANTHROPIC_API_KEY="sk-ant-api03-..."

# Windows PowerShell
$env:ANTHROPIC_API_KEY = "sk-ant-api03-..."

# .env 文件(配合 python-dotenv 使用)
ANTHROPIC_API_KEY=sk-ant-api03-...

对于生产环境,推荐使用密钥管理服务(AWS Secrets Manager、GCP Secret Manager、HashiCorp Vault 等),避免在任何配置文件中明文存储 key。

Key 的格式

Anthropic API Key 以 sk-ant-api03- 开头,后接约 90 个字符的随机字符串。如果你看到其他格式,可能使用了旧版本的 key 或者来自其他服务。

3.2 理解限流机制

Anthropic 的限流(Rate Limiting)有两个独立维度,需要分别理解:

速率限制(RPM / TPM)

每个账户有两种速率限制:

不同账户等级的限制不同。以 Sonnet 为例(2025 年数据,以官方文档为准):

账户等级        RPM     Input TPM    Output TPM
────────────    ────    ─────────    ──────────
Free / Tier 1   50      40,000       8,000
Tier 2          1,000   80,000       16,000
Tier 3          2,000   160,000      32,000
Tier 4          4,000   400,000      80,000
Tier 5          4,000   400,000      80,000(+TPD 限制)

升级账户等级需要在控制台中完成充值或绑定账单,并等待 Anthropic 审核(通常在 24-48 小时内)。

限流响应的处理

当触发限流时,API 返回 HTTP 429 状态码:

{
  "type": "error",
  "error": {
    "type": "rate_limit_error",
    "message": "Rate limit exceeded. Please wait 30 seconds before retrying."
  }
}

响应头中包含限流相关信息:

anthropic-ratelimit-requests-limit: 1000
anthropic-ratelimit-requests-remaining: 0
anthropic-ratelimit-requests-reset: 2024-01-15T10:30:00Z
anthropic-ratelimit-tokens-limit: 80000
anthropic-ratelimit-tokens-remaining: 0
anthropic-ratelimit-tokens-reset: 2024-01-15T10:30:30Z
retry-after: 30

正确的重试策略是指数退避(Exponential Backoff)

import time
import anthropic
from anthropic import RateLimitError

def call_with_retry(client, max_retries=5, **kwargs):
    """
    带指数退避的 API 调用
    """
    for attempt in range(max_retries):
        try:
            return client.messages.create(**kwargs)
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise  # 最后一次重试也失败,则抛出异常
            
            # 从响应头读取等待时间,或使用指数退避
            wait_time = (2 ** attempt) + (0.1 * attempt)  # 1, 2.1, 4.2, 8.3, 16.4 秒
            print(f"限流触发,{wait_time:.1f}秒后重试(第{attempt+1}次)")
            time.sleep(wait_time)

并发请求的 Token 预算管理

在高并发场景下,TPM 限制可能比 RPM 更先触发。以下是一个简单的 token 预算管理器:

import threading
import time
from collections import deque

class TokenBudgetManager:
    """
    滑动窗口 token 预算管理器
    确保每分钟 token 消耗不超过限制
    """
    
    def __init__(self, tokens_per_minute: int):
        self.tpm_limit = tokens_per_minute
        self.window = deque()  # (timestamp, token_count) 队列
        self.lock = threading.Lock()
    
    def can_request(self, estimated_tokens: int) -> bool:
        with self.lock:
            now = time.time()
            # 移除 60 秒之前的记录
            while self.window and now - self.window[0][0] > 60:
                self.window.popleft()
            
            current_usage = sum(t for _, t in self.window)
            return current_usage + estimated_tokens <= self.tpm_limit
    
    def record_usage(self, token_count: int):
        with self.lock:
            self.window.append((time.time(), token_count))
    
    def wait_for_budget(self, estimated_tokens: int, timeout: float = 120):
        """等待直到有足够的 token 预算"""
        start = time.time()
        while not self.can_request(estimated_tokens):
            if time.time() - start > timeout:
                raise TimeoutError("等待 token 预算超时")
            time.sleep(1)

3.3 安装 SDK

Python SDK

Anthropic 官方 Python SDK 是最完整的实现,支持所有 API 特性。

pip install anthropic
# 或者用 poetry
poetry add anthropic
# 或者用 uv
uv add anthropic

推荐版本固定策略

# pyproject.toml
[tool.poetry.dependencies]
python = "^3.9"
anthropic = "^0.34.0"  # 允许小版本更新,锁定大版本

SDK 依赖项很轻量:主要是 httpx(HTTP 客户端)、pydantic(数据验证)和 typing-extensions

TypeScript/Node.js SDK

npm install @anthropic-ai/sdk
# 或
yarn add @anthropic-ai/sdk
# 或
pnpm add @anthropic-ai/sdk

TypeScript 版本提供完整的类型定义,在 TypeScript 项目中享有完整的 IDE 提示。

// package.json 版本参考
{
  "dependencies": {
    "@anthropic-ai/sdk": "^0.26.0"
  }
}

直接使用 HTTP(无 SDK)

对于不使用 Python 或 Node.js 的环境,可以直接调用 REST API:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Hello, Claude!"}
    ]
  }'

注意两个必须的请求头:

3.4 第一个请求:从零到运行

Python 版本

import anthropic

# 客户端自动从 ANTHROPIC_API_KEY 环境变量读取 key
client = anthropic.Anthropic()

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "用一句话解释量子纠缠。"}
    ]
)

print(message.content[0].text)
# 输出类似:量子纠缠是指两个或多个粒子之间存在关联,
# 当测量一个粒子的状态时,无论相距多远,另一个粒子的状态会即刻确定。

理解响应对象结构

# message 对象的完整结构
print(message)
# Message(
#   id='msg_01XFDUDYJgAACzvnptvVoYEL',
#   type='message',
#   role='assistant',
#   content=[
#     TextBlock(
#       text='量子纠缠是指...',
#       type='text'
#     )
#   ],
#   model='claude-sonnet-4-6',
#   stop_reason='end_turn',
#   stop_sequence=None,
#   usage=Usage(
#     input_tokens=21,
#     output_tokens=58
#   )
# )

# 常用字段访问
text = message.content[0].text           # 响应文本
input_tokens = message.usage.input_tokens  # 输入 token 数
output_tokens = message.usage.output_tokens # 输出 token 数
stop_reason = message.stop_reason         # 停止原因
model_used = message.model               # 实际使用的模型版本

TypeScript 版本

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();
// API key 自动从 process.env.ANTHROPIC_API_KEY 读取

async function main() {
  const message = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [
      { role: "user", content: "用一句话解释量子纠缠。" }
    ],
  });

  const textBlock = message.content[0];
  if (textBlock.type === "text") {
    console.log(textBlock.text);
  }
  
  console.log(`Token 使用:输入 ${message.usage.input_tokens},输出 ${message.usage.output_tokens}`);
}

main();

3.5 流式响应(Streaming)

对于需要实时显示响应的应用(如聊天界面),使用流式响应可以显著改善用户体验。

Python 流式示例

import anthropic

client = anthropic.Anthropic()

# 方法一:使用上下文管理器(推荐)
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)

print()  # 换行

# 获取最终消息(包含完整的 usage 信息)
final_message = stream.get_final_message()
print(f"\n总计:{final_message.usage.input_tokens} 输入 + "
      f"{final_message.usage.output_tokens} 输出 tokens")
# 方法二:直接使用 stream 对象,处理所有事件类型
with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=2048,
    messages=[{"role": "user", "content": "详细解释 TCP 三次握手"}]
) as stream:
    for event in stream:
        if event.type == "content_block_delta":
            if event.delta.type == "text_delta":
                print(event.delta.text, end="", flush=True)
        elif event.type == "message_stop":
            print("\n[流结束]")

TypeScript 流式示例

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

async function streamResponse() {
  const stream = await client.messages.stream({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [{ role: "user", content: "写一首关于春天的五言绝句。" }],
  });

  for await (const chunk of stream) {
    if (
      chunk.type === "content_block_delta" &&
      chunk.delta.type === "text_delta"
    ) {
      process.stdout.write(chunk.delta.text);
    }
  }

  const finalMessage = await stream.finalMessage();
  console.log(`\nTokens: ${finalMessage.usage.input_tokens} in, ${finalMessage.usage.output_tokens} out`);
}

streamResponse();

3.6 带 System Prompt 的完整示例

System prompt 是定义 Claude 行为和角色的核心工具。以下是一个实用的完整示例:

import anthropic
import os

client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

def create_technical_assistant():
    """创建一个技术文档助手"""
    
    system_prompt = """你是一位专业的技术文档工程师,专注于为开发者提供清晰、准确的技术解释。

你的工作原则:
1. 始终提供可运行的代码示例
2. 解释每个代码块的作用
3. 指出常见的陷阱和注意事项
4. 在不确定时,明确说明你的不确定性

输出格式:
- 使用 Markdown 格式
- 代码块需要指定语言
- 重要概念用粗体标注
"""
    
    return system_prompt

def ask(question: str, conversation_history: list = None) -> tuple[str, list]:
    """
    向技术助手提问
    返回:(响应文本, 更新后的对话历史)
    """
    if conversation_history is None:
        conversation_history = []
    
    # 添加用户消息
    conversation_history.append({
        "role": "user",
        "content": question
    })
    
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        system=create_technical_assistant(),
        messages=conversation_history
    )
    
    assistant_reply = response.content[0].text
    
    # 将助手响应加入历史
    conversation_history.append({
        "role": "assistant",
        "content": assistant_reply
    })
    
    return assistant_reply, conversation_history


# 使用示例
history = []
reply, history = ask("Python 中的 GIL 是什么?", history)
print(reply)

reply, history = ask("那异步编程如何绕过 GIL 的限制?", history)
print(reply)

3.7 错误处理完全指南

生产代码必须处理各类 API 错误。Anthropic SDK 提供了结构化的异常类:

import anthropic
from anthropic import (
    APIError,
    APIConnectionError,
    APITimeoutError,
    AuthenticationError,
    BadRequestError,
    NotFoundError,
    PermissionDeniedError,
    RateLimitError,
    InternalServerError,
    UnprocessableEntityError,
)

def robust_api_call(client, **kwargs):
    """
    带完整错误处理的 API 调用
    """
    try:
        return client.messages.create(**kwargs)
    
    except AuthenticationError as e:
        # API Key 无效或已过期
        print(f"认证失败:{e.message}")
        print("请检查 ANTHROPIC_API_KEY 环境变量")
        raise
    
    except BadRequestError as e:
        # 请求参数错误(如 max_tokens 超出限制)
        print(f"请求参数错误:{e.message}")
        print(f"错误代码:{e.status_code}")
        raise
    
    except RateLimitError as e:
        # 触发限流
        print(f"限流触发:{e.message}")
        # 此处应实现重试逻辑(见 3.2 节)
        raise
    
    except APIConnectionError as e:
        # 网络连接问题
        print(f"连接失败:{e}")
        # 可安全重试
        raise
    
    except APITimeoutError as e:
        # 请求超时
        print(f"请求超时:{e}")
        # 可安全重试
        raise
    
    except InternalServerError as e:
        # Anthropic 服务端错误(5xx)
        print(f"服务端错误:{e.message}")
        # 短暂等待后可重试
        raise
    
    except UnprocessableEntityError as e:
        # 422 错误:通常是 prompt 内容违反使用政策
        print(f"内容策略错误:{e.message}")
        # 不应重试,需要修改请求内容
        raise
    
    except APIError as e:
        # 其他 API 错误
        print(f"API 错误({e.status_code}):{e.message}")
        raise

可重试 vs 不可重试错误

可安全重试(等待后重试):
  429 RateLimitError      - 等待 retry-after 头指定的时间
  500 InternalServerError - 指数退避,最多 3 次
  APIConnectionError      - 检查网络后重试
  APITimeoutError         - 增加超时时间后重试

不可重试(需要修复代码或请求):
  401 AuthenticationError - 检查 API Key
  400 BadRequestError     - 修复请求参数
  422 UnprocessableEntityError - 修改请求内容
  404 NotFoundError       - 检查模型 ID 是否正确

3.8 配置 HTTP 客户端

自定义超时

import anthropic
import httpx

# 设置自定义超时(默认是 600 秒)
client = anthropic.Anthropic(
    timeout=httpx.Timeout(
        connect=5.0,    # 连接超时:5 秒
        read=120.0,     # 读取超时:2 分钟
        write=10.0,     # 写入超时:10 秒
        pool=5.0        # 连接池超时:5 秒
    )
)

使用代理

import anthropic
import httpx

client = anthropic.Anthropic(
    http_client=httpx.Client(
        proxy="http://your-proxy.example.com:8080"
    )
)

连接池配置(高并发场景)

import anthropic
import httpx

# 为高并发场景配置更大的连接池
client = anthropic.Anthropic(
    http_client=httpx.Client(
        limits=httpx.Limits(
            max_connections=100,
            max_keepalive_connections=20
        )
    )
)

3.9 异步客户端

对于基于 asyncio 的应用(FastAPI、aiohttp 等),使用异步客户端可以避免阻塞事件循环:

import asyncio
import anthropic

async def main():
    # 使用 AsyncAnthropic 而不是 Anthropic
    client = anthropic.AsyncAnthropic()
    
    message = await client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[{"role": "user", "content": "解释异步编程的核心概念"}]
    )
    
    print(message.content[0].text)

# FastAPI 中的使用示例
from fastapi import FastAPI

app = FastAPI()
async_client = anthropic.AsyncAnthropic()

@app.post("/chat")
async def chat_endpoint(user_message: str):
    response = await async_client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[{"role": "user", "content": user_message}]
    )
    return {"reply": response.content[0].text}

小结

本章覆盖了从零开始使用 Claude API 的所有基础知识:

  1. API Key 获取与安全管理:始终通过环境变量传递,永不硬编码
  2. 限流机制:理解 RPM 和 TPM 两个维度,用指数退避处理 429 错误
  3. SDK 安装:Python(pip install anthropic)和 TypeScript(npm install @anthropic-ai/sdk
  4. 第一个请求:5 行代码完成基本调用,理解响应对象结构
  5. 流式响应:使用 stream() 上下文管理器实现实时输出
  6. 错误处理:区分可重试和不可重试错误,实现生产级的健壮性

下一章将深入 Prompt 工程,学习如何通过系统性的角色设定、指令结构和上下文管理,最大化 Claude 的输出质量。

本章评分
4.8  / 5  (123 评分)

💬 留言讨论