ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DevAssistant Pro 接入 Claude/OpenAI 双模型,Base URL 填 TaoToken

DevAssistant Pro 接入 Claude/OpenAI 双模型,Base URL 填 TaoToken 从 DevAssistant Pro 的双模型接入说起一个 Agent 应用为什么要统一 Base URL在《第13章-从零构建-企业级Agent应用完整实战》的 Day 1-18 交付主线里13.1.4 技术选型把 LLM 接入定为 Claude API OpenAI API13.2.4 的packages/agent-core/src/llm/provider.ts与router.ts负责模型调用。真正动手时你会发现一个很现实的问题一个 Agent 应用要同时管理两家模型的 Key、两套入口、两套计费口径provider.ts里到处是分支判断router.ts的路由表越写越长。这篇就把原文“申请/填入模型 Key”这一步改写成先打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册并创建 Key回到provider.ts/router.ts时把模型供应商的 Base URL 填https://taotoken.net/api让 Claude 与 OpenAI 走同一个入口。需要先说清楚TaoToken 只提供 Key 与 Base URL不替代 DevAssistant Pro 的 Agent/Harness/RAG 逻辑你的上下文管理器、工具执行器、Token 用量表该怎么写还怎么写。一、原问题与场景双模型接入的 Key 与入口之痛原文 13.2.4 的provider.ts大致是这样的结构一个createLLMProvider(model)工厂根据模型名判断走 Anthropic SDK 还是 OpenAI SDK。问题在于Key 管理分散ANTHROPIC_API_KEY和OPENAI_API_KEY两套环境变量团队里谁用哪个模型就得配哪套 Key新人 onboarding 经常卡在这一步。入口不统一Anthropic 的 base 是https://api.anthropic.comOpenAI 的 base 是https://api.openai.com/v1router.ts里要维护两张映射表。切换成本高想在claude-sonnet-4-6和gpt-4o之间做 A/B或者按任务类型路由代码生成走 Claude、推理走 OpenAI每次都要改 provider 分支。用量统计割裂13.2.1 的token_usage表要按模型分别统计但两家返回的 usage 字段结构不同estimatedCost很难对齐。场景很具体你在 Day 1-5 搭完基础设施Day 6-10 开始接 Harness 组件agent.ts里this.llm.chat({ messages, tools, temperature })这一行要能同时喂给 Claude 和 OpenAI。如果 provider 层不收敛后面 13.3 的上下文管理器、工具执行器、安全护栏都会被拖累。二、TaoToken 前置注册、创建 Key、拿到 Base URL这一步替代原文的“申请/填入模型 Key”。流程很短打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册账号。进入控制台创建 API Key得到形如YOUR_API_KEY的凭证。记下 Base URLhttps://taotoken.net/api。注意这里不带/v1也不加 UTM 参数直接就是这一串。如果你要核对 Key 状态或看接入文档走这两个入口API Keys 管理https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite拿到 Key 和 Base URL 后回到 DevAssistant Pro 的packages/agent-core/src/llm/目录。TaoToken 在这里的角色是“统一入口”Claude 和 OpenAI 的请求都发往https://taotoken.net/api由它按模型 ID 转发。你的provider.ts不再需要维护两套 base URLrouter.ts也不再需要两套 SDK 初始化逻辑。三、可复制配置改造 provider.ts 与 router.ts先看环境变量。在项目根目录的.env或docker-compose.yml的 environment 段里加TAOTOKEN_API_KEYYOUR_API_KEY TAOTOKEN_BASE_URLhttps://taotoken.net/api然后是packages/agent-core/src/llm/provider.ts的改造。原文的createLLMProvider可以收敛成一个统一的 OpenAI 兼容客户端因为 TaoToken 的入口对 Claude 和 OpenAI 都提供 OpenAI 兼容的 chat 接口// packages/agent-core/src/llm/provider.ts import OpenAI from openai; export interface LLMProvider { chat(params: { messages: Message[]; tools?: ToolDefinition[]; temperature?: number; model?: string; }): PromiseLLMResponse; } export function createLLMProvider(defaultModel: string): LLMProvider { const client new OpenAI({ apiKey: process.env.TAOTOKEN_API_KEY!, baseURL: process.env.TAOTOKEN_BASE_URL || https://taotoken.net/api, }); return { async chat({ messages, tools, temperature 0.3, model }) { const response await client.chat.completions.create({ model: model || defaultModel, messages, tools: tools?.length ? tools : undefined, temperature, }); const choice response.choices[0]; return { content: choice.message.content || , toolCalls: choice.message.tool_calls?.map((tc) ({ id: tc.id, name: tc.function.name, parameters: JSON.parse(tc.function.arguments), })), tokensUsed: response.usage?.total_tokens || 0, }; }, }; }再看router.ts。原文的 router 负责按任务类型选模型改造后只需要维护一张“任务类型 → 模型 ID”的表不再关心供应商// packages/agent-core/src/llm/router.ts export type TaskType code_write | code_review | reasoning | doc_write; const MODEL_ROUTING: RecordTaskType, string { code_write: claude-sonnet-4-6, code_review: claude-sonnet-4-6, reasoning: gpt-4o, doc_write: gpt-4o-mini, }; export function routeModel(taskType: TaskType): string { return MODEL_ROUTING[taskType] || claude-sonnet-4-6; }这样agent.ts里的调用完全不用改this.llm.chat({ messages, tools, temperature })照旧只是底层入口从两家变成了https://taotoken.net/api一家。13.2.1 的token_usage表也不用改结构model字段照填inputTokens/outputTokens从统一的usage字段取。如果你在本地想先用 CLI 快速验证 Key 是否可用可以装一下npm i -g taotoken/taotoken taotoken cc -k YOUR_API_KEY -u https://taotoken.net/api -m claude-sonnet-4-6这条命令只是验证通道不替代你项目里的 provider 实现。四、验证请求先跑通双模型 chat再接 Harness配置改完后别急着接 13.3 的上下文管理器和工具执行器。先写一个最小验证脚本确认 Claude 和 OpenAI 两个模型都能通过同一个 Base URL 返回// scripts/verify-llm.ts import { createLLMProvider } from ../packages/agent-core/src/llm/provider; async function main() { const provider createLLMProvider(claude-sonnet-4-6); const claudeResp await provider.chat({ messages: [{ role: user, content: 用一句话说明什么是 ReAct 循环 }], model: claude-sonnet-4-6, }); console.log([Claude], claudeResp.content, tokens:, claudeResp.tokensUsed); const openaiResp await provider.chat({ messages: [{ role: user, content: 用一句话说明什么是 ReAct 循环 }], model: gpt-4o, }); console.log([OpenAI], openaiResp.content, tokens:, openaiResp.tokensUsed); } main().catch(console.error);成功的结果是两个请求都返回非空contenttokensUsed都有数值控制台没有 401/404/429。如果 Claude 和 OpenAI 都通了说明provider.ts的 Base URL 和 Key 配置正确可以继续按原文接 13.3.1 的ContextManager、13.3.2 的ToolExecutor、13.3.3 的ReasoningController。工具调用这块要特别注意tools参数传进去后返回的toolCalls结构要能被ToolExecutor.execute消费字段名对齐id/name/parameters。验证通过后13.2.1 的token_usage表可以正常写入13.4.3 的可观测性平台也能从统一的usage字段采集 Token 指标。整个链路是agent.ts→reasoning.ts→provider.ts→https://taotoken.net/api→ 模型。五、本篇常见错排查错误 1Base URL 多写了/v1。这是最常见的。https://taotoken.net/api就是完整入口不要写成https://taotoken.net/api/v1否则会 404。OpenAI SDK 的baseURL字段本身会拼/chat/completions你只需要给到/api。错误 2Key 没生效报 401。检查.env里TAOTOKEN_API_KEY是否真的被加载。Node.js 项目里process.env不会自动读.env需要dotenv或在docker-compose.yml里显式声明。另外确认 Key 没有多余空格或换行。错误 3provider.ts里还留着旧的 Anthropic SDK 分支。改造后应该只保留一个 OpenAI 兼容客户端。如果router.ts还在import Anthropic from anthropic-ai/sdk说明改造没完成会出现两套 base URL 并存。错误 4工具调用返回的parameters解析失败。有些模型返回的function.arguments是空字符串或非 JSONJSON.parse会抛异常。在provider.ts里加一层 try/catch解析失败时返回空对象让ToolExecutor去处理。错误 5模型 ID 写错。claude-sonnet-4-6、gpt-4o、gpt-4o-mini这些 ID 要和你实际可用的模型对齐。如果报“model not found”先去模型对话页确认可用模型列表https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite错误 6CORS 或网络问题。如果你在浏览器端直接调注意apps/api/src/app.ts里的 CORS 配置。服务端调用不受影响但前端直连要确认origin白名单。六、语义一致Key 与 Base URL 之外Agent 逻辑仍归你回到原文的主线。TaoToken 在这条链路里只做两件事提供 Key、提供 Base URL。DevAssistant Pro 的 Agent 运行时agent.ts、Harness 组件context.ts/tools.ts/safety.ts/reasoning.ts/memory.ts、RAG 系统13.4.1、Multi-Agent 编排13.4.2、可观测性平台13.4.3全部由你自己实现和维护。provider.ts和router.ts的改造只是把“两家模型的 Key 与入口”收敛成“一个入口 一张路由表”让 13.3 之后的组件不用关心底层供应商差异。如果你在接入过程中遇到 Key 或 Base URL 的问题走 API Keys 管理页和接入文档API Keyshttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你准备长期跑 Agent 编码任务比如让 DevAssistant Pro 持续做代码生成、审查、测试生成可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite验证模型是否可用直接去模型对话页试一条请求https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite下一步就是原文第14章的生产部署。在那之前确保你的provider.ts只认https://taotoken.net/api这一个 Base URLrouter.ts只维护一张模型路由表token_usage表能正常写入。这三件事做完Day 1-18 的交付主线就不会在 LLM 接入这一环卡住。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进