前言
💡 痛点:想用 Claude API 但不知道如何开始?Messages API 不会用?工具调用(Tool Use)不理解?成本和限制不清楚?
🎯 解决方案 :掌握 Claude API 实战 --- 从基础调用、到高级特性、再到生产级最佳实践。
Claude API 能力全景:
#mermaid-svg-BWMB0R7kkfdEsUn2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BWMB0R7kkfdEsUn2 .error-icon{fill:#552222;}#mermaid-svg-BWMB0R7kkfdEsUn2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BWMB0R7kkfdEsUn2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .marker.cross{stroke:#333333;}#mermaid-svg-BWMB0R7kkfdEsUn2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BWMB0R7kkfdEsUn2 p{margin:0;}#mermaid-svg-BWMB0R7kkfdEsUn2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .cluster-label text{fill:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .cluster-label span{color:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .cluster-label span p{background-color:transparent;}#mermaid-svg-BWMB0R7kkfdEsUn2 .label text,#mermaid-svg-BWMB0R7kkfdEsUn2 span{fill:#333;color:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .node rect,#mermaid-svg-BWMB0R7kkfdEsUn2 .node circle,#mermaid-svg-BWMB0R7kkfdEsUn2 .node ellipse,#mermaid-svg-BWMB0R7kkfdEsUn2 .node polygon,#mermaid-svg-BWMB0R7kkfdEsUn2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .rough-node .label text,#mermaid-svg-BWMB0R7kkfdEsUn2 .node .label text,#mermaid-svg-BWMB0R7kkfdEsUn2 .image-shape .label,#mermaid-svg-BWMB0R7kkfdEsUn2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-BWMB0R7kkfdEsUn2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .rough-node .label,#mermaid-svg-BWMB0R7kkfdEsUn2 .node .label,#mermaid-svg-BWMB0R7kkfdEsUn2 .image-shape .label,#mermaid-svg-BWMB0R7kkfdEsUn2 .icon-shape .label{text-align:center;}#mermaid-svg-BWMB0R7kkfdEsUn2 .node.clickable{cursor:pointer;}#mermaid-svg-BWMB0R7kkfdEsUn2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .arrowheadPath{fill:#333333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BWMB0R7kkfdEsUn2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BWMB0R7kkfdEsUn2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BWMB0R7kkfdEsUn2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BWMB0R7kkfdEsUn2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .cluster text{fill:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 .cluster span{color:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BWMB0R7kkfdEsUn2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BWMB0R7kkfdEsUn2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-BWMB0R7kkfdEsUn2 .icon-shape,#mermaid-svg-BWMB0R7kkfdEsUn2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BWMB0R7kkfdEsUn2 .icon-shape p,#mermaid-svg-BWMB0R7kkfdEsUn2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BWMB0R7kkfdEsUn2 .icon-shape .label rect,#mermaid-svg-BWMB0R7kkfdEsUn2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BWMB0R7kkfdEsUn2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BWMB0R7kkfdEsUn2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BWMB0R7kkfdEsUn2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Claude API
文本生成
多模态 Vision
工具调用 Tool Use
批量处理
Messages API
流式响应
系统提示
图片理解
文档分析
工具定义
并行工具调用
工具结果反馈
Batch API
异步处理
Claude 模型版本:
| 模型 | 发布时间 | 上下文窗口 | 特点 |
|---|---|---|---|
| Claude 3 Haiku | 2024.3 | 200K | 最快、最便宜 |
| Claude 3 Sonnet | 2024.3 | 200K | 平衡性能与成本 |
| Claude 3 Opus | 2024.3 | 200K | 最强推理能力 |
| Claude 3.5 Sonnet | 2024.6 | 200K | Sonnet 升级版 |
| Claude 3.5 Haiku | 2024.10 | 200K | Haiku 升级版 |
一、快速开始
1.1 安装与配置
bash
# ===== 安装 Anthropic SDK =====
# Python
pip install anthropic>=0.39.0
# Node.js
npm install @anthropic-ai/sdk
# Go
go get github.com/anthropics/anthropic-go
# 环境变量配置
export ANTHROPIC_API_KEY="sk-ant-..."
export ANTHROPIC_BASE_URL="https://api.anthropic.com" # 可选
# Windows PowerShell
$env:ANTHROPIC_API_KEY = "sk-ant-..."
python
# ===== 基础配置 =====
import os
from anthropic import Anthropic
# 初始化客户端
client = Anthropic(
api_key=os.getenv("ANTHROPIC_API_KEY"),
# base_url="https://api.anthropic.com", # 默认
# timeout=60.0, # 超时时间
# max_retries=2, # 重试次数
)
# 简单调用
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": "你好!"}
]
)
print(response.content[0].text)
1.2 模型选择
python
# ===== 模型选择指南 =====
models = {
"claude-3-5-sonnet-20241022": {
"description": "最新 Sonnet,性能最强",
"context_window": 200000,
"max_output": 8192,
"price_input": 3.00, # $/1M tokens
"price_output": 15.00,
"best_for": ["复杂推理", "代码生成", "分析任务"]
},
"claude-3-5-haiku-20241022": {
"description": "最新 Haiku,速度最快",
"context_window": 200000,
"max_output": 8192,
"price_input": 1.00,
"price_output": 5.00,
"best_for": ["快速响应", "简单任务", "大规模应用"]
},
"claude-3-opus-20240229": {
"description": "最强推理能力",
"context_window": 200000,
"max_output": 4096,
"price_input": 15.00,
"price_output": 75.00,
"best_for": ["复杂任务", "深度推理", "高质量输出"]
},
"claude-3-sonnet-20240229": {
"description": "平衡性能与成本",
"context_window": 200000,
"max_output": 4096,
"price_input": 3.00,
"price_output": 15.00,
"best_for": ["通用任务", "成本平衡"]
},
"claude-3-haiku-20240307": {
"description": "最便宜,速度最快",
"context_window": 200000,
"max_output": 4096,
"price_input": 0.25,
"price_output": 1.25,
"best_for": ["简单任务", "成本敏感"]
}
}
# 选择建议
def select_model(task_complexity, need_speed, budget_sensitive):
if task_complexity == "high":
return "claude-3-opus-20240229"
if need_speed and budget_sensitive:
return "claude-3-5-haiku-20241022"
if not budget_sensitive:
return "claude-3-5-sonnet-20241022"
return "claude-3-haiku-20240307"
1.3 基础对话
python
# ===== 基础对话示例 =====
from anthropic import Anthropic
import os
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
def simple_chat():
"""简单对话"""
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system="你是一个有帮助的助手。", # 系统提示
messages=[
{"role": "user", "content": "写一个快速排序算法"}
],
temperature=0.7, # 0-1,越高越随机
)
return response.content[0].text
def multi_turn_chat():
"""多轮对话"""
messages = []
while True:
user_input = input("你: ")
if user_input.lower() in ['exit', 'quit']:
break
messages.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
temperature=0.7
)
assistant_reply = response.content[0].text
messages.append({"role": "assistant", "content": assistant_reply})
print(f"AI: {assistant_reply}")
def stream_chat():
"""流式响应"""
with client.messages.stream(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": "讲一个长故事"}
]
) as stream:
print("AI: ", end="")
for text in stream.text_stream:
print(text, end="", flush=True)
print() # 换行
二、Messages API 详解
2.1 消息结构
python
# ===== Messages API 消息结构 =====
# 单条消息
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system="你是一个专业的 Python 开发者。", # 系统提示(可选)
messages=[
{
"role": "user",
"content": "写一个快速排序算法"
}
]
)
# 多轮对话
messages = [
{"role": "user", "content": "什么是递归?"},
{"role": "assistant", "content": "递归是..."},
{"role": "user", "content": "能给个例子吗?"}
]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
# 系统提示可以是一个字符串,也可以是数组
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你是一个有帮助的助手。用中文回答。"
}
],
messages=[
{"role": "user", "content": "你好!"}
]
)
2.2 内容类型(多模态)
python
# ===== 多模态内容 =====
import base64
from anthropic import Anthropic
client = Anthropic()
def encode_image(image_path: str) -> str:
"""将图片编码为 base64"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
# 图片 + 文本
def vision_example():
base64_image = encode_image("image.jpg")
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"mime_type": "image/jpeg",
"data": base64_image
}
},
{
"type": "text",
"text": "描述这张图片"
}
]
}
]
)
return response.content[0].text
# 多个图片
def multiple_images():
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "比较这两张图片"},
{
"type": "image",
"source": {
"type": "base64",
"mime_type": "image/jpeg",
"data": encode_image("image1.jpg")
}
},
{
"type": "image",
"source": {
"type": "base64",
"mime_type": "image/jpeg",
"data": encode_image("image2.jpg")
}
}
]
}
]
)
return response.content[0].text
# PDF 文档(需要先转为图片或文本)
def pdf_example():
# Claude 不支持直接读取 PDF
# 需要先将 PDF 转为图片,或使用其他工具提取文本
pass
2.3 流式响应
python
# ===== 流式响应 =====
from anthropic import Anthropic
client = Anthropic()
# 方式 1:使用 stream() 上下文管理器(推荐)
def stream_example():
with client.messages.stream(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": "讲一个长故事"}
]
) as stream:
print("AI: ", end="")
for text in stream.text_stream:
print(text, end="", flush=True)
print()
# 方式 2:使用 stream=True(手动处理)
def stream_manual():
with client.messages.stream(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": "讲一个长故事"}
]
) 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)
print()
# 异步流式
import asyncio
from anthropic import AsyncAnthropic
async def async_stream():
client = AsyncAnthropic()
with client.messages.stream(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": "讲一个长故事"}
]
) as stream:
async for text in stream.text_stream:
print(text, end="", flush=True)
print()
三、工具调用(Tool Use)
3.1 基础工具调用
python
# ===== 工具调用实战 =====
import json
from anthropic import Anthropic
client = Anthropic()
# 定义可用工具
tools = [
{
"name": "get_weather",
"description": "获取指定城市的天气",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如:北京"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
},
{
"name": "search_database",
"description": "搜索数据库",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
}
},
"required": ["query"]
}
}
]
# 模拟函数实现
def get_weather(city: str, unit: str = "celsius"):
"""获取天气(模拟)"""
weather_data = {
"北京": {"temp": 25, "condition": "晴天"},
"上海": {"temp": 28, "condition": "多云"}
}
data = weather_data.get(city, {"temp": 20, "condition": "未知"})
return {
"city": city,
"temperature": f"{data['temp']}°{'C' if unit == 'celsius' else 'F'}",
"condition": data['condition']
}
def search_database(query: str):
"""搜索数据库(模拟)"""
return [
{"id": 1, "title": f"关于 {query} 的结果1"},
{"id": 2, "title": f"关于 {query} 的结果2"}
]
# 主流程
def chat_with_tools(user_message: str):
"""带工具调用的对话"""
messages = [
{"role": "user", "content": user_message}
]
# 第一次调用:让模型决定是否调用工具
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
tools=tools
)
# 检查是否有工具调用
if response.stop_reason == "tool_use":
# 添加助手消息(包含工具调用)
messages.append({
"role": "assistant",
"content": response.content
})
# 执行工具调用
for content_block in response.content:
if content_block.type == "tool_use":
tool_name = content_block.name
tool_input = content_block.input
tool_use_id = content_block.id
# 调用对应函数
if tool_name == "get_weather":
result = get_weather(**tool_input)
elif tool_name == "search_database":
result = search_database(**tool_input)
else:
result = {"error": "Unknown function"}
# 添加工具结果
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use_id,
"content": json.dumps(result, ensure_ascii=False)
}
]
})
# 第二次调用:让模型根据工具结果生成回复
final_response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
return final_response.content[0].text
# 没有工具调用,直接返回回复
return response.content[0].text
# 测试
if __name__ == '__main__':
result = chat_with_tools("北京今天天气怎么样?")
print(result)
3.2 并行工具调用
python
# ===== 并行工具调用 =====
# Claude 支持一次调用多个工具
tools = [
{
"name": "get_weather",
"description": "获取天气",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
},
{
"name": "get_news",
"description": "获取新闻",
"input_schema": {
"type": "object",
"properties": {
"topic": {"type": "string"}
},
"required": ["topic"]
}
}
]
def parallel_tool_calls():
messages = [
{"role": "user", "content": "北京天气怎么样?今天有什么科技新闻?"}
]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
tools=tools
)
# 检查是否包含多个工具调用
tool_uses = [c for c in response.content if c.type == "tool_use"]
if len(tool_uses) > 1:
print(f"模型并行调用了 {len(tool_uses)} 个工具:")
for tu in tool_uses:
print(f" - {tu.name}: {tu.input}")
# 并行执行(实际可以用 asyncio 并行)
results = {}
for tu in tool_uses:
func_name = tu.name
func_input = tu.input
if func_name == "get_weather":
results[tu.id] = get_weather(**func_input)
elif func_name == "get_news":
results[tu.id] = get_news(**func_input)
return results
3.3 工具调用最佳实践
python
# ===== 工具调用最佳实践 =====
# 1. 工具描述要清晰
tools_good = [
{
"name": "get_weather",
"description": "获取指定城市的当前天气信息,包括温度、天气状况、湿度等。", # 清晰描述
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,支持中英文,例如:北京、Beijing"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,celsius 为摄氏度,fahrenheit 为华氏度"
}
},
"required": ["city"]
}
}
]
# 2. 处理工具错误
def handle_tool_error():
messages = [{"role": "user", "content": "获取北京天气"}]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
tools=tools
)
if response.stop_reason == "tool_use":
messages.append({
"role": "assistant",
"content": response.content
})
for content_block in response.content:
if content_block.type == "tool_use":
try:
# 执行工具
result = execute_tool(content_block.name, content_block.input)
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content_block.id,
"content": json.dumps(result, ensure_ascii=False)
}
]
})
except Exception as e:
# 返回错误给模型
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content_block.id,
"content": f"错误: {str(e)}",
"is_error": True # 标记为错误
}
]
})
# 继续对话
final_response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
return final_response.content[0].text
# 3. 限制工具调用次数(防止无限循环)
def limit_tool_calls(user_message: str, max_iterations: int = 5):
"""限制工具调用次数"""
messages = [{"role": "user", "content": user_message}]
for i in range(max_iterations):
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
tools=tools
)
if response.stop_reason != "tool_use":
# 没有工具调用,返回最终回复
return response.content[0].text
# 处理工具调用
messages.append({
"role": "assistant",
"content": response.content
})
for content_block in response.content:
if content_block.type == "tool_use":
result = execute_tool(content_block.name, content_block.input)
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content_block.id,
"content": json.dumps(result, ensure_ascii=False)
}
]
})
# 达到最大迭代次数
return "达到最大工具调用次数限制"
四、批量处理(Batch API)
4.1 Batch API 使用
python
# ===== Batch API 批量处理 =====
from anthropic import Anthropic
import time
client = Anthropic()
# 创建批量任务
def create_batch():
# 准备批量请求
requests = [
{
"custom_id": "request-1",
"params": {
"model": "claude-3-5-haiku-20241022",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "1+1=?"}
]
}
},
{
"custom_id": "request-2",
"params": {
"model": "claude-3-5-haiku-20241022",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "2+2=?"}
]
}
}
]
# 创建批量任务
batch = client.messages.batches.create(
requests=requests
)
batch_id = batch.id
print(f"Batch ID: {batch_id}")
print(f"状态: {batch.processing_status}")
return batch_id
# 查询批量任务状态
def check_batch_status(batch_id: str):
batch = client.messages.batches.retrieve(batch_id)
print(f"状态: {batch.processing_status}")
print(f"总请求数: {batch.request_counts.processing + batch.request_counts.succeeded + batch.request_counts.errored}")
print(f"处理中: {batch.request_counts.processing}")
print(f"成功: {batch.request_counts.succeeded}")
print(f"失败: {batch.request_counts.errored}")
return batch.processing_status
# 等待批量任务完成
def wait_for_batch(batch_id: str, check_interval: int = 60):
"""等待批量任务完成"""
while True:
status = check_batch_status(batch_id)
if status == "ended":
print("批量任务已完成!")
break
elif status == "canceling":
print("批量任务正在取消...")
break
print(f"等待 {check_interval} 秒后检查...")
time.sleep(check_interval)
# 获取批量任务结果
def get_batch_results(batch_id: str):
"""获取批量任务结果"""
results = []
# 流式读取结果
for result in client.messages.batches.results(batch_id):
results.append({
"custom_id": result.custom_id,
"success": result.result.type == "succeeded",
"content": result.result.message.content[0].text if result.result.type == "succeeded" else None,
"error": result.result.error if result.result.type == "errored" else None
})
return results
# 完整流程
def batch_processing_example():
# 1. 创建批量任务
batch_id = create_batch()
# 2. 等待完成
wait_for_batch(batch_id)
# 3. 获取结果
results = get_batch_results(batch_id)
# 4. 处理结果
for r in results:
if r["success"]:
print(f"{r['custom_id']}: {r['content'][:50]}...")
else:
print(f"{r['custom_id']}: 错误 - {r['error']}")
4.2 Batch API 最佳实践
python
# ===== Batch API 最佳实践 =====
# 1. 合理分批
def optimal_batching(requests: list, batch_size: int = 1000):
"""合理分批"""
batches = []
for i in range(0, len(requests), batch_size):
batch_requests = requests[i:i+batch_size]
batch = client.messages.batches.create(
requests=batch_requests
)
batches.append(batch.id)
return batches
# 2. 错误处理
def batch_with_error_handling(requests: list):
"""带错误处理的批量处理"""
batch_id = create_batch_with_requests(requests)
wait_for_batch(batch_id)
results = get_batch_results(batch_id)
succeeded = [r for r in results if r["success"]]
failed = [r for r in results if not r["success"]]
print(f"成功: {len(succeeded)}")
print(f"失败: {len(failed)}")
# 重试失败的任务
if failed:
print("重试失败的任务...")
retry_requests = [
{
"custom_id": r["custom_id"],
"params": get_params_by_id(r["custom_id"]) # 需要根据 custom_id 获取原始请求
}
for r in failed
]
retry_batch_id = create_batch_with_requests(retry_requests)
wait_for_batch(retry_batch_id)
retry_results = get_batch_results(retry_batch_id)
return succeeded + retry_results
return succeeded
# 3. 成本优化
def estimate_batch_cost(requests: list):
"""估算批量处理成本"""
# Batch API 比实时 API 便宜 50%
# 假设平均每个请求 1000 input tokens + 500 output tokens
avg_input_tokens = 1000
avg_output_tokens = 500
num_requests = len(requests)
# Claude 3.5 Haiku 价格
input_price_per_1m = 1.00 # $1/1M tokens
output_price_per_1m = 5.00 # $5/1M tokens
total_input_cost = (avg_input_tokens * num_requests / 1_000_000) * input_price_per_1m
total_output_cost = (avg_output_tokens * num_requests / 1_000_000) * output_price_per_1m
realtime_cost = total_input_cost + total_output_cost
batch_cost = realtime_cost * 0.5 # Batch API 便宜 50%
print(f"实时 API 成本: ${realtime_cost:.2f}")
print(f"Batch API 成本: ${batch_cost:.2f}")
print(f"节省: ${realtime_cost - batch_cost:.2f}")
return batch_cost
五、错误处理与重试
5.1 错误类型
python
# ===== Claude API 错误类型 =====
from anthropic import Anthropic
from anthropic._exceptions import (
APIError,
APIConnectionError,
RateLimitError,
AuthenticationError,
BadRequestError,
InternalServerError,
NotFoundError,
PermissionDeniedError,
UnprocessableEntityError,
OverloadedError
)
client = Anthropic()
def robust_api_call(messages: list, max_retries: int = 3):
"""健壮的 API 调用(带重试)"""
for attempt in range(max_retries):
try:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
timeout=30.0 # 30 秒超时
)
return response.content[0].text
except RateLimitError as e:
# 速率限制
wait_time = 2 ** attempt # 指数退避
print(f"速率限制,{wait_time}秒后重试...")
time.sleep(wait_time)
except APIConnectionError as e:
# 网络连接错误
print(f"网络连接错误: {e}")
if attempt == max_retries - 1:
raise
time.sleep(2)
except InternalServerError as e:
# Anthropic 服务器错误(500 等)
print(f"Anthropic 服务器错误: {e}")
time.sleep(5)
except OverloadedError as e:
# 服务器过载
print(f"服务器过载,{2 ** attempt}秒后重试...")
time.sleep(2 ** attempt)
except AuthenticationError as e:
# API Key 错误
print(f"认证失败,请检查 API Key: {e}")
raise
except BadRequestError as e:
# 请求参数错误
print(f"请求参数错误: {e}")
raise
except Exception as e:
# 其他错误
print(f"未知错误: {e}")
if attempt == max_retries - 1:
raise
time.sleep(1)
raise Exception("达到最大重试次数")
5.2 重试装饰器
python
# ===== 重试装饰器 =====
import time
import functools
from anthropic._exceptions import RateLimitError, APIConnectionError, InternalServerError, OverloadedError
def retry_on_error(max_retries: int = 3, backoff_factor: float = 2.0):
"""重试装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except (RateLimitError, APIConnectionError, InternalServerError, OverloadedError) as e:
if attempt == max_retries - 1:
raise
wait_time = backoff_factor ** attempt
print(f"错误: {e}, {wait_time}秒后重试...")
time.sleep(wait_time)
raise Exception("达到最大重试次数")
return wrapper
return decorator
# 使用装饰器
@retry_on_error(max_retries=5, backoff_factor=2.0)
def call_claude_api(messages: list):
"""调用 Claude API"""
client = Anthropic()
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
return response.content[0].text
# 异步版本
import asyncio
from anthropic import AsyncAnthropic
def async_retry_on_error(max_retries: int = 3, backoff_factor: float = 2.0):
"""异步重试装饰器"""
def decorator(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return await func(*args, **kwargs)
except (RateLimitError, APIConnectionError, InternalServerError, OverloadedError) as e:
if attempt == max_retries - 1:
raise
wait_time = backoff_factor ** attempt
print(f"错误: {e}, {wait_time}秒后重试...")
await asyncio.sleep(wait_time)
raise Exception("达到最大重试次数")
return wrapper
return decorator
@async_retry_on_error(max_retries=5)
async def async_call_claude(messages: list):
"""异步调用 Claude API"""
client = AsyncAnthropic()
response = await client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
return response.content[0].text
六、成本控制
6.1 Token 计算
python
# ===== Token 计算 =====
import tiktoken
from anthropic import Anthropic
client = Anthropic()
def count_tokens(text: str):
"""计算文本的 token 数(使用 tiktoken 近似)"""
# Claude 使用自己的 tokenizer,但这里用 tiktoken 近似
# cl100k_base 是 GPT-4 的 tokenizer,可以作为近似
encoding = tiktoken.get_encoding("cl100k_base")
tokens = encoding.encode(text)
return len(tokens)
def estimate_cost(input_text: str, output_text: str, model: str = "claude-3-5-sonnet-20241022"):
"""估算成本"""
pricing = {
"claude-3-5-sonnet-20241022": {"input": 3.00, "output": 15.00},
"claude-3-5-haiku-20241022": {"input": 1.00, "output": 5.00},
"claude-3-opus-20240229": {"input": 15.00, "output": 75.00},
"claude-3-sonnet-20240229": {"input": 3.00, "output": 15.00},
"claude-3-haiku-20240307": {"input": 0.25, "output": 1.25}
}
if model not in pricing:
return None
input_tokens = count_tokens(input_text)
output_tokens = count_tokens(output_text)
input_cost = (input_tokens / 1_000_000) * pricing[model]["input"]
output_cost = (output_tokens / 1_000_000) * pricing[model]["output"]
total_cost = input_cost + output_cost
return {
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"input_cost_usd": input_cost,
"output_cost_usd": output_cost,
"total_cost_usd": total_cost
}
# 监控 token 使用
def track_token_usage(response):
"""跟踪 token 使用"""
usage = response.usage
print(f"输入 tokens: {usage.input_tokens}")
print(f"输出 tokens: {usage.output_tokens}")
# 计算成本
cost = estimate_cost("", "", model="claude-3-5-sonnet-20241022") # 需要根据实际使用计算
return usage
6.2 成本优化策略
python
# ===== 成本优化策略 =====
from anthropic import Anthropic
import tiktoken
client = Anthropic()
# 策略 1:使用更便宜的模型
def use_cheaper_model():
"""简单任务使用便宜模型"""
response = client.messages.create(
model="claude-3-5-haiku-20241022", # 比 Sonnet 便宜 3 倍
max_tokens=1024,
messages=[
{"role": "user", "content": "1+1=?"}
]
)
return response.content[0].text
# 策略 2:减少输入 token(压缩上下文)
def compress_context(messages: list, max_tokens: int = 4096):
"""压缩上下文"""
# 保留系统消息
system_msgs = [m for m in messages if m.get("role") == "system"]
# 保留最近的对话
recent_msgs = [m for m in messages if m.get("role") != "system"]
recent_msgs = recent_msgs[-10:] # 只保留最近 10 条
# 合并
compressed = system_msgs + recent_msgs
# 检查 token 数
total_tokens = sum(count_tokens(str(m)) for m in compressed)
if total_tokens > max_tokens:
# 进一步压缩
compressed = system_msgs + recent_msgs[-5:]
return compressed
# 策略 3:使用缓存(避免重复调用)
import hashlib
import json
import os
class CachedClaude:
"""带缓存的 Claude 客户端"""
def __init__(self, cache_dir: str = ".claude_cache"):
self.client = Anthropic()
self.cache_dir = cache_dir
os.makedirs(cache_dir, exist_ok=True)
def _cache_key(self, messages: list, model: str):
"""生成缓存 key"""
content = json.dumps({"messages": messages, "model": model}, ensure_ascii=False)
return hashlib.md5(content.encode()).hexdigest()
def _get_cache(self, cache_key: str):
"""获取缓存"""
cache_path = os.path.join(self.cache_dir, f"{cache_key}.json")
if os.path.exists(cache_path):
with open(cache_path, 'r', encoding='utf-8') as f:
return json.load(f)
return None
def _set_cache(self, cache_key: str, response):
"""设置缓存"""
cache_path = os.path.join(self.cache_dir, f"{cache_key}.json")
with open(cache_path, 'w', encoding='utf-8') as f:
json.dump(response, f, ensure_ascii=False)
def chat(self, messages: list, model: str = "claude-3-5-sonnet-20241022"):
"""带缓存的对话"""
cache_key = self._cache_key(messages, model)
# 检查缓存
cached = self._get_cache(cache_key)
if cached:
print("使用缓存")
return cached
# 调用 API
response = self.client.messages.create(
model=model,
max_tokens=1024,
messages=messages
)
result = response.content[0].text
# 写入缓存
self._set_cache(cache_key, result)
return result
# 策略 4:使用 Batch API(便宜 50%)
def use_batch_api(requests: list):
"""使用 Batch API"""
# 见第四章
pass
七、生产案例
7.1 案例:智能代码审查系统
python
# ===== 案例:智能代码审查系统 =====
from anthropic import Anthropic
import os
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
class CodeReviewBot:
"""智能代码审查机器人"""
def __init__(self):
self.client = client
self.review_history = []
# 定义工具
self.tools = [
{
"name": "get_file_content",
"description": "获取文件内容",
"input_schema": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "文件路径"
}
},
"required": ["file_path"]
}
},
{
"name": "run_linter",
"description": "运行代码检查工具(如 pylint、eslint)",
"input_schema": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "文件路径"
}
},
"required": ["file_path"]
}
}
]
def get_file_content(self, file_path: str):
"""获取文件内容(模拟)"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
return {"error": str(e)}
def run_linter(self, file_path: str):
"""运行代码检查(模拟)"""
# 实际应该调用 pylint、eslint 等
return {
"file": file_path,
"issues": [
{"line": 10, "message": "未使用的变量"},
{"line": 25, "message": "函数过长"}
]
}
def review_code(self, pr_description: str, file_paths: list):
"""审查代码"""
# 构建消息
messages = [
{
"role": "user",
"content": f"请审查以下 Pull Request:\n\n{pr_description}\n\n文件列表:{', '.join(file_paths)}"
}
]
# 调用 API
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
messages=messages,
tools=self.tools
)
# 处理工具调用
if response.stop_reason == "tool_use":
messages.append({
"role": "assistant",
"content": response.content
})
for content_block in response.content:
if content_block.type == "tool_use":
tool_name = content_block.name
tool_input = content_block.input
tool_use_id = content_block.id
# 执行工具
if tool_name == "get_file_content":
result = self.get_file_content(**tool_input)
elif tool_name == "run_linter":
result = self.run_linter(**tool_input)
else:
result = {"error": "Unknown tool"}
# 添加工具结果
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use_id,
"content": json.dumps(result, ensure_ascii=False)
}
]
})
# 生成最终审查意见
final_response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
messages=messages
)
review = final_response.content[0].text
self.review_history.append({
"pr": pr_description,
"review": review
})
return review
# 没有工具调用
return response.content[0].text
# 使用
if __name__ == '__main__':
bot = CodeReviewBot()
pr_desc = "修复了用户登录的 bug,添加了输入验证"
files = ["src/auth.py", "tests/test_auth.py"]
review = bot.review_code(pr_desc, files)
print(f"代码审查意见:\n{review}")
7.2 案例:文档智能问答系统
python
# ===== 案例:文档智能问答系统 =====
from anthropic import Anthropic
import os
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
class DocumentQA:
"""文档智能问答系统"""
def __init__(self, documents: list):
self.client = client
self.documents = documents
self.embeddings = self._compute_embeddings(documents)
def _compute_embeddings(self, documents: list):
"""计算文档向量(简化版,实际应使用 Embeddings API)"""
# 实际应该使用 OpenAI/Azure 的 Embeddings API
# 这里简化为使用字符数作为向量
return [len(doc) for doc in documents]
def _semantic_search(self, query: str, top_k: int = 3):
"""语义搜索(简化版)"""
# 实际应该使用向量相似度
# 这里简化为关键词匹配
results = []
for i, doc in enumerate(self.documents):
if query.lower() in doc.lower():
results.append((i, doc[:200]))
return results[:top_k]
def answer_question(self, question: str):
"""回答问题"""
# 1. 检索相关文档
relevant_docs = self._semantic_search(question, top_k=3)
if not relevant_docs:
return "抱歉,没有找到相关文档。"
# 2. 构建上下文
context = "\n\n".join([
f"文档 {i+1}:{doc}" for i, doc in relevant_docs
])
# 3. 构建提示
messages = [
{
"role": "user",
"content": f"""请根据以下文档回答问题。
文档:
{context}
问题:{question}
要求:
1. 只根据文档内容回答
2. 如果文档中没有相关信息,说"文档中没有相关信息"
3. 引用文档中的具体内容"""
}
]
# 4. 调用 API
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
return response.content[0].text
# 使用
if __name__ == '__main__':
# 初始化文档
docs = [
"OpenAI API 是 OpenAI 提供的接口,支持文本生成、图像生成等功能。",
"Claude API 是 Anthropic 提供的接口,擅长代码生成和文档分析。",
"向量数据库用于存储 Embeddings,支持语义搜索。"
]
qa = DocumentQA(docs)
# 问答
question = "OpenAI API 有什么功能?"
answer = qa.answer_question(question)
print(f"问:{question}")
print(f"答:{answer}")
八、总结
8.1 核心要点
#mermaid-svg-vjQ3GVflsF3rwxN5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vjQ3GVflsF3rwxN5 .error-icon{fill:#552222;}#mermaid-svg-vjQ3GVflsF3rwxN5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vjQ3GVflsF3rwxN5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .marker.cross{stroke:#333333;}#mermaid-svg-vjQ3GVflsF3rwxN5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vjQ3GVflsF3rwxN5 p{margin:0;}#mermaid-svg-vjQ3GVflsF3rwxN5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .cluster-label text{fill:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .cluster-label span{color:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .cluster-label span p{background-color:transparent;}#mermaid-svg-vjQ3GVflsF3rwxN5 .label text,#mermaid-svg-vjQ3GVflsF3rwxN5 span{fill:#333;color:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .node rect,#mermaid-svg-vjQ3GVflsF3rwxN5 .node circle,#mermaid-svg-vjQ3GVflsF3rwxN5 .node ellipse,#mermaid-svg-vjQ3GVflsF3rwxN5 .node polygon,#mermaid-svg-vjQ3GVflsF3rwxN5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .rough-node .label text,#mermaid-svg-vjQ3GVflsF3rwxN5 .node .label text,#mermaid-svg-vjQ3GVflsF3rwxN5 .image-shape .label,#mermaid-svg-vjQ3GVflsF3rwxN5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vjQ3GVflsF3rwxN5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .rough-node .label,#mermaid-svg-vjQ3GVflsF3rwxN5 .node .label,#mermaid-svg-vjQ3GVflsF3rwxN5 .image-shape .label,#mermaid-svg-vjQ3GVflsF3rwxN5 .icon-shape .label{text-align:center;}#mermaid-svg-vjQ3GVflsF3rwxN5 .node.clickable{cursor:pointer;}#mermaid-svg-vjQ3GVflsF3rwxN5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .arrowheadPath{fill:#333333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vjQ3GVflsF3rwxN5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vjQ3GVflsF3rwxN5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vjQ3GVflsF3rwxN5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vjQ3GVflsF3rwxN5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .cluster text{fill:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 .cluster span{color:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vjQ3GVflsF3rwxN5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vjQ3GVflsF3rwxN5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vjQ3GVflsF3rwxN5 .icon-shape,#mermaid-svg-vjQ3GVflsF3rwxN5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vjQ3GVflsF3rwxN5 .icon-shape p,#mermaid-svg-vjQ3GVflsF3rwxN5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vjQ3GVflsF3rwxN5 .icon-shape .label rect,#mermaid-svg-vjQ3GVflsF3rwxN5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vjQ3GVflsF3rwxN5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vjQ3GVflsF3rwxN5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vjQ3GVflsF3rwxN5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Claude API 实战
基础调用
高级特性
批量处理
生产实践
模型选择
Messages API
参数调优
工具调用
Vision
流式响应
Batch API
成本优化
错误处理
重试机制
监控告警
8.2 最佳实践
| 实践 | 说明 |
|---|---|
| 模型选择 | 简单任务用 Haiku,复杂任务用 Sonnet/Opus |
| 工具调用 | 让模型调用外部工具,增强能力 |
| 流式响应 | 提升用户体验,减少等待感 |
| 错误处理 | 实现重试机制,处理速率限制 |
| 成本控制 | 使用 Batch API、缓存、便宜模型 |
| Vision | 支持图片理解,但需 Base64 编码 |
8.3 成本优化
| 策略 | 节省 |
|---|---|
| 使用 Haiku | 比 Sonnet 便宜 3 倍 |
| Batch API | 比实时 API 便宜 50% |
| 缓存结果 | 避免重复调用 |
| 压缩上下文 | 减少输入 token |
| 限制 max_tokens | 控制输出长度 |
本文基于 Claude API 官方文档编写。如有问题欢迎评论区讨论!