1. 从 0 开发 AI Agent为什么第一步不是写 Prompt 而是打通 LLM 调用链AI Agent 这个词在 2026 年已经被说烂了但真正动手从零写一个能跑起来的 Agent很多人卡住的地方并不是 LangChain 的 API 记不住而是模型接入层没打通。你可能会遇到这种情况本地代码写好了npm run dev一跑报 401、超时、模型名不存在或者今天用 GPT 明天换 Claude 就得改一遍环境变量和 SDK 初始化逻辑。Agent 的核心是 LLM 调用而 LLM 调用链如果一开始就是散的后面加 tool、加 memory、加 subagent 只会越来越乱。这篇内容聚焦的是从零构建 AI Agent 的工程化起点用 TypeScript Node.js LangChain 作为技术栈先把多模型调用统一到一个 Key、一个 API 通道上再谈 ReAct 循环和工具链。适合已经会写 Node.js、想往 AI 工程师方向走的前端或全栈开发者。你不需要先精通 LangChain但需要有一个能跑 Node 20 的环境和基本的 TypeScript 配置能力。我试过把 OpenAI、Claude、DeepSeek 的 Key 分别写在三个.env文件里结果调试一个 Agent 的 tool calling 时光切换模型就花了半小时。后来把接入层收敛到 TaoToken 的统一 Key 上settings.json和config.toml各维护一份Agent 代码里只认一个baseURL换模型只改一个字符串。下面按可复制的步骤来。2. TaoToken 前置统一 Key 与 API 通道在 Agent 项目里的位置TaoToken 在这里扮演的角色是模型接入层的统一入口。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 这个不加 UTM。你可以在控制台里创建 API Key然后让 LangChain 的ChatOpenAI或ChatAnthropic指向这个端点。为什么 Agent 项目特别需要这一层因为一个完整的 Agent 至少会涉及三类调用主推理模型负责 ReAct 决策、工具调用模型可能用更便宜的模型做 function calling、以及子智能体或压缩上下文时的辅助模型。如果每个模型都单独配 Key、单独处理重试和限流代码里会充斥if (model gpt)这种分支。统一 Key 之后模型切换变成配置项而不是代码逻辑。你需要先拿到一个可用的 API Key。进入控制台后创建 Key建议按项目命名比如agent-dev-local方便后面在环境变量里区分。创建完成后不要直接硬编码到代码里下一步会用.env注入。注意API Key 只显示一次创建后立即复制到密码管理器或本地.env不要提交到 Git。3. 可复制配置settings.json、config.toml 与环境变量注入这一节给出三个可直接复制的配置骨架。第一个是settings.json用于存放模型路由和 Agent 运行参数第二个是config.toml用于 LangChain 或 CLI 工具的模型声明第三个是.env注入示例。先看settings.json。这个文件放在项目根目录Agent 启动时读取决定主模型、工具模型和子智能体模型分别走哪个通道。{ llm: { provider: taotoken, baseURL: https://taotoken.net/api, apiKeyEnv: TAOTOKEN_API_KEY, defaultModel: claude-sonnet-4-20250514, toolModel: gpt-4.1-mini, subagentModel: deepseek-chat, timeoutMs: 60000, maxRetries: 3 }, agent: { maxIterations: 12, contextLimitTokens: 128000, compressThreshold: 0.8, enableSubagent: true }, tools: { allowed: [read_file, write_file, exec, web_fetch], denyPaths: [.env, .git/config, ~/.ssh] } }这里的关键是baseURL指向https://taotoken.net/apiapiKeyEnv指向环境变量名而不是 Key 本身。defaultModel、toolModel、subagentModel可以不同但都走同一个通道。再看config.toml。如果你用 LangChain 的 CLI 或某些支持 TOML 的 Agent 框架可以用这个骨架。[llm] provider openai-compatible base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model claude-sonnet-4-20250514 temperature 0.2 max_tokens 4096 [llm.fallback] model gpt-4.1-mini max_retries 2 [agent] name my-first-agent runtime nodejs langchain_version 0.3.x然后是.env注入。不要把 Key 写进settings.json用环境变量。# .env TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_BASE_URLhttps://taotoken.net/api NODE_ENVdevelopment在 TypeScript 里读取时用dotenv加载然后传给 LangChain 的模型实例。import dotenv/config; import { ChatOpenAI } from langchain/openai; import settings from ./settings.json assert { type: json }; const apiKey process.env[settings.llm.apiKeyEnv]; if (!apiKey) { throw new Error(缺少环境变量 ${settings.llm.apiKeyEnv}); } export const mainModel new ChatOpenAI({ modelName: settings.llm.defaultModel, openAIApiKey: apiKey, configuration: { baseURL: settings.llm.baseURL, }, timeout: settings.llm.timeoutMs, maxRetries: settings.llm.maxRetries, });这段代码里baseURL来自settings.jsonKey 来自.env模型名来自配置。换模型时只改settings.json里的defaultModel代码不动。4. 验证请求一次 Agent 工具链调用的完整动作与成功结果配置写完后不要急着写 ReAct 循环。先做一次最小验证让模型通过统一通道返回一个 tool call然后你手动执行这个 tool再把结果传回去。这一步跑通说明 LLM 调用链和工具链的衔接没问题。先写一个最简单的 tool 定义用 LangChain 的DynamicStructuredTool。import { DynamicStructuredTool } from langchain/core/tools; import { z } from zod; import { mainModel } from ./llm; const readFileTool new DynamicStructuredTool({ name: read_file, description: 读取指定路径的文本文件内容, schema: z.object({ path: z.string().describe(要读取的文件路径), }), func: async ({ path }) { const fs await import(fs/promises); const content await fs.readFile(path, utf-8); return content.slice(0, 2000); }, }); const modelWithTools mainModel.bindTools([readFileTool]);然后发一条请求观察返回的tool_calls。const response await modelWithTools.invoke([ { role: user, content: 请读取 package.json 文件告诉我项目名称和版本号。, }, ]); console.log(finish_reason:, response.response_metadata?.finish_reason); console.log(tool_calls:, JSON.stringify(response.tool_calls, null, 2));如果通道正常你会看到类似这样的输出finish_reason: tool_calls tool_calls: [ { name: read_file, args: { path: package.json }, id: call_abc123 } ]这说明模型已经通过 TaoToken 通道返回了工具调用意图。接下来手动执行 tool把结果作为tool消息传回去。import { ToolMessage } from langchain/core/messages; const toolCall response.tool_calls[0]; const toolResult await readFileTool.invoke(toolCall.args); const finalResponse await modelWithTools.invoke([ { role: user, content: 请读取 package.json 文件告诉我项目名称和版本号。 }, response, new ToolMessage({ content: toolResult, tool_call_id: toolCall.id, }), ]); console.log(最终回复:, finalResponse.content);成功时finalResponse.content会包含项目名称和版本号。这一步验证了两个东西一是 TaoToken 通道能正常返回 tool calling 格式二是你的 tool 执行结果能正确回传。这两点跑通后面写 ReAct 循环只是把手动步骤自动化。如果你需要更直观地验证模型对话是否正常可以打开模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 直接发一条消息确认 Key 和通道没问题。长期做编码和 Agent 开发的话Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 里有更完整的额度说明。5. 本篇常见错排查401、模型名不存在、tool_calls 为空这一节列出验证过程中最容易遇到的四类错误以及对应的排查动作。第一类401 Unauthorized。报错信息通常是Incorrect API key provided或invalid_api_key。先检查.env里的TAOTOKEN_API_KEY是否有多余空格或换行。然后确认settings.json里的apiKeyEnv和.env里的变量名完全一致大小写敏感。最后确认baseURL是https://taotoken.net/api不要多加/v1或漏掉/api。第二类模型名不存在。报错类似model_not_found或The model does not exist。这时候去控制台或模型列表确认你写的模型名是否在当前通道可用。不同通道支持的模型名可能不同claude-sonnet-4-20250514和claude-sonnet-4可能只有一个有效。把settings.json里的defaultModel换成确认可用的名称再试。第三类tool_calls为空。模型返回了文本而不是工具调用。先检查bindTools是否真的传入了 tool 数组再检查 tool 的description是否清晰。如果描述太模糊模型可能选择直接回答而不是调用工具。另外部分模型对 tool calling 的支持需要显式设置tool_choice可以在bindTools时加上{ tool_choice: auto }。第四类请求超时。Agent 场景下上下文可能很长默认超时时间不够。在settings.json里把timeoutMs调到 60000 或更高同时确认maxRetries至少为 2。如果还是超时检查网络环境是否能正常访问https://taotoken.net/api可以用curl做一次最小请求。curl -X POST https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: ping}], max_tokens: 16 }如果curl返回正常说明通道没问题问题在代码配置。如果curl也报错先解决 Key 或模型名的问题。提示排查时把NODE_ENV设为development并在代码里打印baseURL和模型名但不要打印完整 Key。6. 接入层跑通之后Agent 技能树的下一步与统一 Key 的长期价值LLM 调用链跑通之后你才算真正站在了 AI Agent 开发的起点上。接下来要补的技能包括 ReAct 循环、tool 权限分级、context 压缩、memory 分层、subagent 隔离、hook 扩展点以及 MCP server 或 skills CLI 的取舍。这些内容每一个都值得单独展开但它们的共同前提是模型接入层是稳定的、可切换的、可观测的。统一 Key 的长期价值在于当你从单模型 Agent 演进到多模型协作时不需要重写接入代码。主模型用 Claude 做推理工具模型用 GPT 做 function calling子智能体用 DeepSeek 做长上下文压缩这些切换都只改settings.json里的一个字段。API Key 的管理、额度查看、模型列表都在控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 里完成代码里只保留环境变量引用。如果你还没创建 Key先去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 生成一个然后按第 3 节的配置骨架把.env和settings.json填好。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有不同语言和框架的调用示例。Claude Code 相关的接入说明在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 如果你后面想用 Claude Code 做 Agent 调试可以参考。把第 4 节的验证脚本跑通看到tool_calls里出现read_file和正确的args你就可以开始写第一个 ReAct 循环了。接入层不拖后腿后面的技能树才长得起来。
