跳到主要内容
Router One
返回博客

OpenAI Agents SDK、Claude Agent SDK、Pydantic AI、CrewAI 横评:一把网关 Key 全接入

发布作者Router One 团队方法说明

五个 Agent 框架,同一个问题:把模型调用改走 Router One 之后,到底变了什么?框架本身没有变——OpenAI Agents SDK 自带处理 handoff、guardrail 和 session 的循环;Claude Agent SDK 把 Claude Code 的引擎和权限机制嵌进你的应用;Pydantic AI 提供类型化输出;CrewAI 运行角色化的 crew;LangChain 在 LangGraph 运行时之上搭 Agent。变的是底下那次请求:一把 sk- Key,/models 里的精确模型 id,以及每次模型请求在 Dashboard → Logs 里的一条 Trace——模型、tokens、花费、延迟、状态和 request_id。网关负责请求,循环仍归框架。

一句话结论。OpenAI Agents SDK:在 Router One client 上显式构造 Chat Completions 模型,关掉 tracing。Claude Agent SDK:Claude Code 的两个环境变量、一个精确的 Claude 系列 id,每一轮就是一条 /v1/messages Trace。Pydantic AIOpenAIChatModelOpenAIProvider,五者中接入面最小。CrewAIcustom_openai=Trueopenai/ 前缀规则。LangChain 与 LangGraphChatOpenAIuse_responses_api=False,LangGraph 不改变请求本身。

对照表

SDK语言怎样指向网关实际协议能到达的目录系列留在 SDK 侧的部分
OpenAI Agents SDKPython;另有 TypeScript 版AsyncOpenAI(base_url=…, api_key=…) 放进 OpenAIChatCompletionsModelset_tracing_disabled(True)Chat Completions(直接传模型名则默认走 Responses)全部聊天模型;Responses 原生服务 GPT 系列与 DeepSeek id函数工具、handoff、guardrail、session、tracing
Claude Agent SDKPython、TypeScriptClaudeAgentOptions(env={...}) 传入 ANTHROPIC_BASE_URLANTHROPIC_AUTH_TOKEN,或在 shell 里导出Anthropic Messages;引擎自己拼 /v1/messages当前上架的 Claude 系列 id工具执行、权限、hooks、session、subagent
Pydantic AIPythonOpenAIChatModel(id, provider=OpenAIProvider(base_url=…, api_key=…))Chat Completions(openai: 简写意味着 Responses)全部聊天模型Agent 循环、校验、工具、消息历史
CrewAIPythonLLM(model="openai/" + id, custom_openai=True, base_url=…, api_key=…),或 MODELOPENAI_API_BASEOPENAI_API_KEY 环境变量Chat Completions(api="responses" 可切换)全部聊天模型Agent 循环、任务顺序、委派、工具、记忆
LangChain / LangGraphPython;JS/TS 用 useResponsesApi: falseChatOpenAI(base_url=…, api_key=…, model=…, use_responses_api=False)Chat Completions(用到 Responses 专属功能时切到 Responses)全部聊天模型图、Agent 循环、工具、状态与持久化

五者中有四个说的是 Chat Completions,而 /v1/chat/completions 服务目录里的所有聊天模型,所以同一个 anthropic/claude-sonnet-5openai/gpt-5.5 id 在下面每份配置里都能用;Claude Agent SDK 的引擎说 Anthropic Messages,只能到达 Claude 系列 id。最后一列是边界——里面没有一项跑在网关上。

OpenAI Agents SDK:显式的 Chat Completions 模型,关掉 tracing

SDK 通过默认 provider 在 Responses API 上解析字符串模型名,并且用与模型调用相同的那把 Key 把 trace 上传到 OpenAI 服务器。指南绕开了这两个默认值:带 Router One base URL 和 Key 的 AsyncOpenAI client,包进 OpenAIChatCompletionsModel,于是每次请求都是 POST /v1/chat/completions,任何聊天模型 id 都能用;再加上 set_tracing_disabled(True),因为 Router One 的 Key 顶替不了 platform.openai.com 的 Key。

import os
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, Runner, set_tracing_disabled

set_tracing_disabled(True)
client = AsyncOpenAI(
    base_url="https://api.router.one/v1",
    api_key=os.environ["ROUTER_ONE_API_KEY"],
)
model = OpenAIChatCompletionsModel(
    model=os.environ["ROUTER_ONE_MODEL_ID"],
    openai_client=client,
)

文件剩下的部分是 Agent(..., model=model)Runner.run_sync(agent, prompt, max_turns=3)。只有当你的 Agent 用到的每个 id 在模型页都列出 /v1/responses,并且需要托管工具或 previous_response_id 时,才保留 Responses 默认;把 Claude、Gemini 或 Grok 的 id 发到那里,会在调用任何模型之前收到 HTTP 400 model '<id>' must be called via …——这个端点接受什么见 Responses API 页

坑在这里。 OPENAI_AGENTS_DISABLE_TRACING=1 也能关掉 tracing;仍想用 OpenAI 的 Traces 面板,就用 set_tracing_export_api_key(...) 单独给导出器一把 Key,并在全局注册 client 时传 use_for_tracing=False。指南:OpenAI Agents SDK 接入 Router One

Claude Agent SDK:两个变量、一个精确 Claude id、每轮一条 Trace

SDK 拉起随包附带的 Claude Code 引擎,工具在你的机器上执行,每一轮模型调用都是一次 Anthropic Messages 请求。把它指向 Router One 和把 Claude Code 指向 Router One 是同一件事:ANTHROPIC_BASE_URL 填不带 /v1 的主机根地址 https://api.router.one,因为引擎会自己拼上 /v1/messagesANTHROPIC_AUTH_TOKEN 以 Bearer 头携带你的 Key;ANTHROPIC_API_KEY 不需要设置。两个变量通过 env 传入或在 shell 里导出都行,因为 Python SDK 会把 env 合并到继承的环境之上。

options = ClaudeAgentOptions(
    model=os.environ["ROUTER_ONE_MODEL_ID"],
    env={
        "ANTHROPIC_BASE_URL": "https://api.router.one",
        "ANTHROPIC_AUTH_TOKEN": os.environ["ROUTER_ONE_API_KEY"],
    },
    max_turns=3,
)

之后 query(prompt=..., options=options) 逐条产出引擎的消息。model 必须是 /models 里详情页列出 POST /v1/messages 的精确 id,不能是 sonnet 这类别名——别名在客户端解析成内置默认 id。这个端点也列出了 DeepSeek id,但 Claude Code 的网关文档写明 Anthropic 不支持把它路由到非 Claude 模型,所以 SDK 请保持使用 Claude 系列 id。

坑在这里。 TypeScript 里 options.env 会整个替换环境,要把 process.env 展开进去。Claude Code 设置文件里的 env 块会同时覆盖 shell 和 options.env——删掉那里旧的 ANTHROPIC_BASE_URL,或传 setting_sources=[]。残留的 ANTHROPIC_API_KEY 会作为第二个请求头一起发出,请 unset 掉。max_budget_usd 比较的是 SDK 内置价格表算出的客户端估算,官方文档明确说不能据此做财务决策;真正止损的是 Key 上的 maxSpend。指南:Claude Agent SDK 接入 Router One;终端里同样两个变量的用法见 Claude Code 国内接入

Pydantic AI:先 OpenAIChatModel,再选类型化输出模式

按当前 Pydantic AI 文档,Agent('openai:...') 里不带类名的 openai: 前缀对应的是 OpenAIResponsesModel,所以指南显式构造模型:OpenAIChatModel 选定 /v1/chat/completionsOpenAIProvider 接收带 /v1 的 base URL 和 Key,目录 id 原样传入,自带的前缀也保留。

from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.openai import OpenAIProvider

model = OpenAIChatModel(
    os.environ["ROUTER_ONE_MODEL_ID"],
    provider=OpenAIProvider(
        base_url="https://api.router.one/v1",
        api_key=os.environ["ROUTER_ONE_API_KEY"],
    ),
)
agent = Agent(model, output_type=str)

output_type=str 让第一次请求不依赖工具和 JSON schema 输出;agent.run_sync(prompt, usage_limits=UsageLimits(request_limit=3)) 限制 Pydantic AI 为本次运行记录的模型请求次数。

坑在这里。 类型化输出是模型能力开始起作用的地方:把 BaseModel 传给 output_type(或用 ToolOutput)走的是输出工具,模型必须支持工具调用;NativeOutput 用模型原生的 JSON schema 响应格式;PromptedOutput 把 schema 写进提示、事后校验。校验重试会产生额外的模型请求,每次各有一条 Trace,所以类型化输出失败时先退回 output_type=str。指南:Pydantic AI 接入 Router One;另见结构化输出页

CrewAI:custom_openai=True 与 openai/ 前缀规则

CrewAI 从每个模型字符串里读 provider 前缀并据此选客户端:anthropic/google/ 开头的 id 会走 CrewAI 自带的对应厂商路径,要求那家的 API 和 Key。custom_openai=True 覆盖这个选择,让任何 id 都固定走 OpenAI SDK 的 Chat Completions 路径;此模式下 CrewAI 只去掉最前面的一个 openai/ 段,其余原样发送。

from crewai import Agent, Crew, LLM, Task

llm = LLM(
    model="openai/" + os.environ["ROUTER_ONE_MODEL_ID"],
    custom_openai=True,
    base_url="https://api.router.one/v1",
    api_key=os.environ["ROUTER_ONE_API_KEY"],
)

llm=llm 传给每个 Agent,加上 TaskCrewcrew.kickoff() 返回的结果里 .raw 就是文本。因为会被吃掉一层前缀,GPT 系列 id 要写两遍——openai/openai/gpt-5.5——否则发出去的是不带前缀的 gpt-5.5;Logs 里 model 一列应显示完整的目录 id。脚手架生成的项目也可以改用 MODELOPENAI_API_BASEOPENAI_API_KEYMODEL 同样写成 openai/ 加目录 id。

坑在这里。 agents.yaml 里的纯字符串或 Agent(llm="...") 在构建时拿不到 base URL,会被交给 CrewAI 的 LiteLLM 兜底路径,而它默认没有安装——把模型放进 MODELLLM 对象。api="responses" 会让同一个对象改走 /v1/responses,所以 api 保持默认值。crew.usage_metrics 是 token 统计,不是账单;CREWAI_TRACING_ENABLED 是 CrewAI 的 tracing,不是网关的。指南:CrewAI 接入 Router One

LangChain 与 LangGraph:ChatOpenAI 配 use_responses_api=False

ChatOpenAI 直接接收 base URL 和 Key,指南用 use_responses_api=False 把这条固定在 Chat Completions 上;JS/TS 的对应选项是 useResponsesApi: false。官方集成页写明:用到 Responses 专属功能或设置 use_responses_api=True 时,ChatOpenAI 会改走 Responses API;显式传入的 base_url 优先于 OPENAI_API_BASEOPENAI_BASE_URL 两个环境变量——所以两者都写进代码。

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://api.router.one/v1",
    api_key="sk-your-router-one-key",
    model="<model-id-from-/models>",
    use_responses_api=False,
)

print(llm.invoke("Hello!").content)

LangGraph 不改变这一点。LangChain 文档把 LangChain 定义为 Agent 框架,把 LangGraph 定义为底下的运行时——持久执行、流式、人在回路、持久化——而 create_agent 接受已初始化的模型实例,所以上面这个 llm 就是 Agent 或手写图节点发往 Router One 的东西。checkpoint 和状态留在你的进程里。

坑在这里。 原生 Anthropic 功能或非标准的供应商字段需要匹配的集成和端点;启用内置工具或会话状态功能可能让 LangChain 切到 Responses——启用前重新核对模型页。指南:LangChain 接入 Router One;端点细节见流式输出工具调用两页。

网关做什么、不做什么

Router One 负责模型请求。每一次请求它都在 Dashboard → Logs 记一条 Trace——模型、输入输出 tokens、花费、延迟、HTTP 状态、request_id——并执行 Key 上的上限:maxSpend,以及对付失控循环的 rateLimittokenLimitTpm。id 发到不服务它的端点,会在调用任何模型之前被 HTTP 400 拒绝;各端点接受和拒绝的清单见 API 兼容性事实页

它不执行工具、不保存 Agent 状态,也不替框架跑循环。函数工具、handoff、权限检查、session、subagent、checkpoint 和校验重试都跑在你的进程里;/v1/responses 上的托管工具字段会被接受并计量,但 Router One 自己不执行工具。网关也没有 embeddings 端点——这五者的 RAG pipeline 近亲 Haystack 指南展示了边界落在哪里。账本记什么,见成本追踪页

给一次 Agent 运行定预算

上面每个框架都有循环上限,但没有一个是花费上限:OpenAI Agents SDK 的 max_turns 数模型调用次数(不传时为 10),Claude Agent SDK 的 max_turns 数工具调用往返轮数,Pydantic AI 的 UsageLimits(request_limit=...) 数它记录的请求次数。重试在这些上限之外,而每一次到达网关的尝试都是独立请求,各有自己的 Trace 和费用;total_cost_usdcrew.usage_metrics 这类客户端数字是估算或统计,不是你实际被扣的钱。

真正止损的上限在 Key 上:一个框架一把 Key,各设 maxSpend,失控的运行到顶即停、返回 402,钱包和其他 Key 不受影响。按 request_id 核账——按 Key、时间窗和精确模型过滤 Logs,把条数对上框架报告的轮数或步数,保留失败 Trace 的 request_id。被取消的流式请求既不会被悄悄扣费,也不会被悄悄记零:在 POST /v1/chat/completionsPOST /v1/responses 上,客户端中途取消的请求记为 HTTP 499 client_cancelled,只按上游实际报告的用量计费;网关最多等 5 秒拿最终 usage,释放预留余额,不重试也不故障转移(见定价事实页)。

router.one 为每个框架建一把 Key,各设上限,让 Trace 告诉你哪个循环值这笔账。

常见问题

Claude 优先的团队该选哪个 SDK? 想把 Claude Code 的引擎——文件和 shell 工具、权限模式、subagent——嵌进应用,就选 Claude Agent SDK:它说 Anthropic Messages,只用 Claude 系列 id。不需要那个引擎的话,另外四个都能在 /v1/chat/completions 上调用 Claude id,之后想试别的系列只是换 id,不是换框架。

GPT 优先的团队该选哪个 SDK? OpenAI Agents SDK,真正要决定的是端点:它的 Responses 默认路径原生服务当前上架的 GPT 系列和 DeepSeek id,带托管工具和 previous_response_id;显式的 OpenAIChatCompletionsModel 则对所有聊天模型可用。SDK 自己也建议一个工作流只用一种模型形态。

一把 Key 能同时接五个框架吗? 能。本文每份配置接受的都是同一把 sk- Key,每次调用都落进同一个钱包。更好的做法是一个框架一把 Key:每把各带自己的 maxSpend,Dashboard → Logs 按 Key 过滤,哪个框架花了多少是事实而不是估算。

Responses 和 Chat Completions 的区别重要吗? 它决定哪些 id 能用、哪些功能存在。/v1/chat/completions 服务所有聊天模型;/v1/responses 原生服务 GPT 系列和 DeepSeek id,并承载 Responses 专属功能;/v1/messages 以 Anthropic 格式服务 Claude 系列和 DeepSeek id。配错了会在调用任何模型之前收到 HTTP 400 must be called via …——这时换 id 或换模型类,不要改 base URL。

相关权威页面

这篇文章归入「LLM API 网关与路由」主题,以下页面作为商业页、配置文档、证据页和信任事实源。

商业主页面Router One API 网关承接统一模型调用、路由、fallback、预算和观测的产品首页。API 文档Router One API 文档OpenAI 兼容端点、CLI 配置和模型调用示例。证据页智能路由方法论路由信号、最终模型与 provider,以及客户侧 trace 的字段边界。对比页OpenRouter 替代方案专业对比全球模型目录与中国友好路由、支付能力的差异。信任页可引用事实表面向搜索爬虫、AI 答案引擎和客户的稳定事实源。数据留存数据留存政策prompt/completion 留存边界和请求元数据政策。网关页面统一 LLM API 网关一个 OpenAI 兼容端点接入整个模型目录,含路由、fallback 与预算控制。路由页面智能模型路由候选排序如何使用延迟、公示成本与可靠性信号。故障转移页面LLM 供应商故障转移什么样的请求才符合在另一条健康供应商路由上重试的条件。可观测页面逐请求 Trace 日志每个请求的最终模型与供应商、Token、延迟、状态与报错。兼容性页面OpenAI 兼容端点沿用 OpenAI SDK,只改 base URL 即可触达各个模型系列。成本追踪页面LLM 成本追踪按 Key、按模型、按请求的花费归因,配合硬性消费上限。转售方页面在 Router One 上搭你自己的 API 服务带消费上限的客户 Key、按 Key 的用量归因,以及明确的「不提供」清单。客户端接入SDK 与客户端配置指南把任意编程 agent、SDK、聊天客户端或 LLM 应用平台指向同一个端点,每个都有专属指南。模型对比模型价格与上下文两两对比每百万 token 单价、上下文窗口与能力,渲染自实时模型目录。

相关阅读