五个 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 AI:OpenAIChatModel 加 OpenAIProvider,五者中接入面最小。CrewAI:custom_openai=True 加 openai/ 前缀规则。LangChain 与 LangGraph:ChatOpenAI 配 use_responses_api=False,LangGraph 不改变请求本身。
对照表
| SDK | 语言 | 怎样指向网关 | 实际协议 | 能到达的目录系列 | 留在 SDK 侧的部分 |
|---|---|---|---|---|---|
| OpenAI Agents SDK | Python;另有 TypeScript 版 | AsyncOpenAI(base_url=…, api_key=…) 放进 OpenAIChatCompletionsModel;set_tracing_disabled(True) | Chat Completions(直接传模型名则默认走 Responses) | 全部聊天模型;Responses 原生服务 GPT 系列与 DeepSeek id | 函数工具、handoff、guardrail、session、tracing |
| Claude Agent SDK | Python、TypeScript | ClaudeAgentOptions(env={...}) 传入 ANTHROPIC_BASE_URL 与 ANTHROPIC_AUTH_TOKEN,或在 shell 里导出 | Anthropic Messages;引擎自己拼 /v1/messages | 当前上架的 Claude 系列 id | 工具执行、权限、hooks、session、subagent |
| Pydantic AI | Python | OpenAIChatModel(id, provider=OpenAIProvider(base_url=…, api_key=…)) | Chat Completions(openai: 简写意味着 Responses) | 全部聊天模型 | Agent 循环、校验、工具、消息历史 |
| CrewAI | Python | LLM(model="openai/" + id, custom_openai=True, base_url=…, api_key=…),或 MODEL、OPENAI_API_BASE、OPENAI_API_KEY 环境变量 | Chat Completions(api="responses" 可切换) | 全部聊天模型 | Agent 循环、任务顺序、委派、工具、记忆 |
| LangChain / LangGraph | Python;JS/TS 用 useResponsesApi: false | ChatOpenAI(base_url=…, api_key=…, model=…, use_responses_api=False) | Chat Completions(用到 Responses 专属功能时切到 Responses) | 全部聊天模型 | 图、Agent 循环、工具、状态与持久化 |
五者中有四个说的是 Chat Completions,而 /v1/chat/completions 服务目录里的所有聊天模型,所以同一个 anthropic/claude-sonnet-5 或 openai/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/messages;ANTHROPIC_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/completions,OpenAIProvider 接收带 /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,加上 Task 和 Crew,crew.kickoff() 返回的结果里 .raw 就是文本。因为会被吃掉一层前缀,GPT 系列 id 要写两遍——openai/openai/gpt-5.5——否则发出去的是不带前缀的 gpt-5.5;Logs 里 model 一列应显示完整的目录 id。脚手架生成的项目也可以改用 MODEL、OPENAI_API_BASE 和 OPENAI_API_KEY,MODEL 同样写成 openai/ 加目录 id。
坑在这里。 agents.yaml 里的纯字符串或 Agent(llm="...") 在构建时拿不到 base URL,会被交给 CrewAI 的 LiteLLM 兜底路径,而它默认没有安装——把模型放进 MODEL 或 LLM 对象。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_BASE 和 OPENAI_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,以及对付失控循环的 rateLimit 和 tokenLimitTpm。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_usd、crew.usage_metrics 这类客户端数字是估算或统计,不是你实际被扣的钱。
真正止损的上限在 Key 上:一个框架一把 Key,各设 maxSpend,失控的运行到顶即停、返回 402,钱包和其他 Key 不受影响。按 request_id 核账——按 Key、时间窗和精确模型过滤 Logs,把条数对上框架报告的轮数或步数,保留失败 Trace 的 request_id。被取消的流式请求既不会被悄悄扣费,也不会被悄悄记零:在 POST /v1/chat/completions 和 POST /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。