Anthropic SDK vs OpenAI SDK 开发者体验对比 — 类型安全性、错误处理与流式模式实测

Anthropic SDK vs OpenAI SDK 开发者体验对比 — 类型安全性、错误处理与流式模式实测

在沙箱中同时安装anthropic 0.100.0和openai 2.36.0并深入比较。类型数量408 vs 230、错误类层级、流式实现、工具调用格式、SDK专有功能——全部从代码层面分析的实战比较指南。

pip install anthropic openai — 然后我把两个包放在一起拆解。anthropic 0.100.0,openai 2.36.0。版本号本身就说明了问题:Anthropic还停留在0.x,而OpenAI已经是2.x分支。数字本身没那么重要,重要的是它们所代表的设计哲学差异。

我在临时沙箱中运行了两个SDK,直接检查了类型层级、错误类、流式源代码和工具调用格式。以下是我的发现。

第一印象:版本号透露的信息

anthropic 0.100.0——第100个次要版本,却从未发布1.0。这要么是刻意的API稳定性谨慎,要么是营销选择。openai 2.36.0已经经历了一次major version bump。

两个SDK内部都基于httpx构建,都使用SSE(Server-Sent Events)进行流式传输。查看顶层客户端初始化参数可以发现哲学差异。

# anthropic.Anthropic() 专有参数
client = anthropic.Anthropic(
    api_key=None,
    auth_token=None,
    credentials=None,   # 企业级凭证对象
    config=None,        # 基于配置文件的设置
    profile=None,       # 命名配置文件
    webhook_key=None,
    _token_cache=NOT_GIVEN,
)

# openai.OpenAI() 专有参数
client = openai.OpenAI(
    api_key=None,
    admin_api_key=None,
    workload_identity=None,   # 基于IAM的认证
    organization=None,        # 组织ID
    project=None,             # 项目ID
    webhook_secret=None,
    websocket_base_url=None,  # 用于Realtime API
    _enforce_credentials=True,
)

Anthropic侧重企业级凭证管理——通过配置文件和命名配置文件管理多账户。OpenAI专注于团队级计费和IAM认证,通过organizationprojectworkload_identity实现。

共同参数包括max_retries=2timeoutdefault_headershttp_client——两者默认值完全相同。

类型系统比较:408 vs 230

这是沙箱中最令人惊讶的数字。

anthropic.types 导出类型数: 408
openai.types 导出类型数: 230

差距显著。Claude的API返回更丰富的响应结构——ToolUseBlockThinkingBlockCitationContentBlockLocationBashCodeExecutionOutputBlock都是有效的响应内容类型,每种都有对应的TypedDict param。OpenAI以ChatCompletion为核心,响应格式更简单统一。

Anthropic专有类型:

类型功能
ThinkingBlock / ThinkingConfigParam扩展思考(Extended Thinking)
CacheControlEphemeralParam提示词缓存(TTL: ‘5m’ / ‘1h’)
CitationContentBlockLocationAI响应引用位置追踪
BashCodeExecutionOutputBlock代码执行工具结果
MemoryTool20250818Param智能体内存工具
ServerToolCaller20260120Param服务端工具执行器
AnthropicBetaParamBeta功能头部控制

OpenAI专有:

功能说明
AssistantEventHandlerAssistants API事件流
Realtime API基于WebSocket的双向实时流
Fine-tuning模块fine_tuning模块内置
OAuthErrorAuthenticationError的子类型

类型更多不代表更好——BashCodeExecutionToolResultBlockParamBashCodeExecutionToolResultErrorParam作为独立类型提供了精确的自动补全,但也增加了学习成本。OpenAI更简单的结构让入门更快。

工具调用格式:input_schema vs function.parameters

两个SDK最显著的API设计差异在这里。

# Anthropic 工具定义
anthropic_tool = {
    "name": "get_weather",
    "description": "获取当前天气",
    "input_schema": {              # JSON Schema直接作为根
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        },
        "required": ["location"]
    }
}

# OpenAI 工具定义
openai_tool = {
    "type": "function",            # 额外的包装层
    "function": {
        "name": "get_weather",
        "description": "获取当前天气",
        "parameters": {            # 嵌套在function内部
            "type": "object",
            "properties": {
                "location": {"type": "string"}
            },
            "required": ["location"]
        }
    }
}

返回工具结果时格式也不同:

# Anthropic: 工具结果作为user消息中的content块
messages.append({
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toolu_01A...",
            "content": "15°C 晴天"
        }
    ]
})

# OpenAI: 工具结果作为独立的tool角色消息
messages.append({
    "role": "tool",
    "tool_call_id": "call_abc123",
    "content": "15°C 晴天"
})

Anthropic将所有内容统一为content块数组,OpenAI使用专用的tool角色。如果你在构建需要同时处理两者的多模型路由,这个格式差异是大部分适配器复杂度的来源。

错误处理架构:共同点与关键差异

我在沙箱中直接打印了两个SDK的错误层级。

错误层级比较图

# Anthropic错误层级 (0.100.0)
APIError
├─ APIStatusError
│   ├─ RequestTooLargeError     ← Anthropic专有
│   ├─ ServiceUnavailableError  ← Anthropic专有
│   ├─ OverloadedError          ← Anthropic专有 (HTTP 529)
│   └─ DeadlineExceededError    ← Anthropic专有
└─ APIConnectionError
    └─ APITimeoutError

# OpenAI错误层级 (2.36.0)
APIError
├─ APIStatusError
│   ├─ AuthenticationError
│   │   └─ OAuthError          ← OpenAI专有
│   └─ (其他标准错误)
└─ APIConnectionError
    └─ APITimeoutError

Anthropic专有错误在生产中很重要。OverloadedError是HTTP 529——Claude服务器流量过载。将其与RateLimitError分开捕获可以应用不同的退避策略。DeadlineExceededErrorAPITimeoutError不同——是处理时间问题,不是连接超时,修复方式不同。

from anthropic import OverloadedError, RateLimitError, DeadlineExceededError
import time

def safe_call(client, **kwargs):
    try:
        return client.messages.create(**kwargs)
    except OverloadedError:
        # 服务器过载 — 更长的退避
        time.sleep(10)
        return client.messages.create(**kwargs)
    except RateLimitError as e:
        wait = int(e.response.headers.get('retry-after', 60))
        time.sleep(wait)
        return client.messages.create(**kwargs)
    except DeadlineExceededError:
        # 处理时间超过 — 分拆为更小的请求
        raise

两个SDK默认max_retries=2,都会在429和5xx错误时自动重试。

流式模式:核心相同,表面不同

我从源文件中读取了两个流式实现。Stream.__iter__的实现几乎完全相同。

# anthropic._streaming.Stream(实际源码)
class Stream(Generic[_T], metaclass=_SyncStreamMeta):
    def __iter__(self) -> Iterator[_T]:
        for item in self._iterator:
            yield item
    def _iter_events(self) -> Iterator[ServerSentEvent]:
        yield from self._decoder.iter_bytes(self.response.iter_bytes())

# openai._streaming.Stream(几乎相同)
class Stream(Generic[_T]):  # 无元类
    def __iter__(self) -> Iterator[_T]:
        for item in self._iterator:
            yield item
    def _iter_events(self) -> Iterator[ServerSentEvent]:
        yield from self._decoder.iter_bytes(self.response.iter_bytes())

使用接口差异更明显:

# Anthropic流式
with client.messages.stream(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "你好"}]
) as stream:
    for text in stream.text_stream:   # 直接提取文本
        print(text, end="", flush=True)

# OpenAI流式
with client.chat.completions.stream(
    model="gpt-5",
    messages=[{"role": "user", "content": "你好"}]
) as stream:
    for chunk in stream:
        delta = chunk.choices[0].delta
        if delta.content:             # 需要更深层的路径访问
            print(delta.content, end="", flush=True)

Anthropic的stream.text_stream对于纯文本输出更简洁。使用Vercel AI SDK构建Claude流式代理的指南在类似模式上构建,适合进一步阅读。

Anthropic专有功能:提示词缓存、扩展思考、引用

提示词缓存

CacheControlEphemeralParamttl字段——'5m''1h'。这是我没想到的发现。以前只有ephemeral缓存一种选项,现在可以指定过期时间。

client.messages.create(
    model="claude-opus-4-7",
    system=[
        {
            "type": "text",
            "text": "非常长的系统文档...(数万token)",
            "cache_control": {"type": "ephemeral", "ttl": "1h"}
        }
    ],
    messages=[{"role": "user", "content": "请总结"}]
)

Claude API提示词缓存实战指南涵盖了在生产中应用此功能的四种模式,可降低70%成本。

扩展思考(Extended Thinking)

ThinkingConfigParamThinkingBlock让Claude将推理链以结构化输出的形式展示。OpenAI没有等效功能。

response = client.messages.create(
    model="claude-opus-4-7",
    thinking={"type": "enabled", "budget_tokens": 10000},
    messages=[{"role": "user", "content": "复杂数学问题"}]
)

for block in response.content:
    if block.type == "thinking":
        print("推理过程:", block.thinking)  # 结构化,有类型
    elif block.type == "text":
        print("最终答案:", block.text)

引用系统(Citations)

CitationContentBlockLocation追踪Claude从哪个文档片段获取信息。对于需要在回答旁展示来源归属的RAG系统非常有用。

OpenAI专有功能:Assistants API、Realtime、微调

Assistants APIAssistantEventHandler支持结合文件搜索、代码解释器和自定义函数的有状态智能体。Anthropic没有SDK层面的等效抽象。

Realtime APIwebsocket_base_url参数的存在就是为此。SDK直接支持基于WebSocket的双向流。适合语音智能体和实时交互体验。

微调(Fine-tuning) — OpenAI的fine_tuning模块允许从SDK内直接管理调优任务。Anthropic的微调是独立的企业合同路径,没有公开SDK接口。

选择哪个SDK

Anthropic SDK 0.100.0OpenAI SDK 2.36.0
导出类型数408230
错误类数量16个(含529)13个
默认最大重试22
流式核心httpx + SSEhttpx + SSE
提示词缓存✓ SDK级别
扩展思考
Realtime API
Assistants API
微调内置
引用系统
工具结果格式content块tool角色消息

选择Anthropic SDK的场景: 长上下文文档处理(提示词缓存降成本)、需要推理透明度(扩展思考)、文档QA中的引用追踪、类型安全优先的团队。

选择OpenAI SDK的场景: 语音界面或实时交互(Realtime API)、Assistants API的文件搜索+代码解释器组合、组织/项目级计费分离、管理微调后的自定义模型。

两者都用? 在前面放一个抽象层。PydanticAI在智能体层面处理多提供商路由,让你避免在整个代码库中传播工具格式差异。

这次比较真正揭示的内容

SDK不只是HTTP API的包装器。它是一份设计文档,展示了公司认为开发者会用模型做什么。

Anthropic的408个类型告诉你:我们预期你会关心缓存token、追踪引用、内省推理链。OpenAI的Realtime API和Assistants告诉你:我们预期你会构建实时面向用户的体验和有状态的对话系统。

SDK选择应该跟随模型选择,而不是领先于它。选哪个SDK,先看你选了哪个模型,再看你真正需要哪些功能。

阅读其他语言版本

这篇文章有帮助吗?

您的支持能帮助我创作更好的内容。请我喝杯咖啡吧。

关于作者

jw

Kim Jangwook

AI/LLM专业全栈开发者

凭借10年以上的Web开发经验,构建AI代理系统、LLM应用程序和自动化解决方案。分享Claude Code、MCP和RAG系统的实践经验。

返回博客列表