1. 从零搭 Agent 系统先想清楚这 5 个决策Agent 系统这个词这两年被聊得很多但真正动手搭过的人都知道难的不是“调通一次 API”而是把模型、循环、工具、安全这几块拼成一个能稳定跑起来的东西。我见过不少团队卡在同一个地方demo 十分钟跑通一上真实任务就翻车——要么模型在简单任务上烧钱要么循环跑飞停不下来要么工具权限开太大出了事。这篇聚焦 Agent 系统从零搭建的 5 个关键决策模型路由、循环设计、工具权限、安全边界、配置落地。适合已经了解大模型 API 基本调用、准备把 Agent 从玩具做成可用系统的开发者。我会用 TaoToken 作为统一 Key/API 接入层给出可复制的config.toml与settings.json骨架并完整演示一次工具调用循环的验证过程帮你跑通最小可用 Agent。TaoToken 在这里的角色是接入层一个 Key 走通多家模型协议兼容 Anthropic 和 OpenAI换模型不用改接入代码。这样你在做“模型路由”这个决策时基础设施层面已经准备好了。2. TaoToken 前置统一 Key 与 API 通道在动手写 Agent 之前先把接入层搭好。TaoToken 提供统一的 API 通道官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。你需要先拿到一个 API Key。登录后进入控制台在 API Keys 页面创建一个新 Key复制保存。这个 Key 就是后面所有配置里要填的东西。TaoToken 的接口兼容两种协议Anthropic 风格和 OpenAI 风格。这意味着你可以用同一套 Key通过不同的 base_url 和认证头调用不同协议的模型。对于 Agent 系统来说这一点很关键——模型路由时切换供应商接入代码不用动。配置上Anthropic 协议用ANTHROPIC_AUTH_TOKEN加ANTHROPIC_BASE_URLOpenAI 协议用OPENAI_API_KEY加OPENAI_BASE_URL。下面两节我会把这两套配置都写进骨架文件里。注意Key 不要硬编码进代码提交到仓库。本地开发放配置文件生产环境走环境变量或密钥托管。3. 可复制配置config.toml 与 settings.json 骨架这一节给出两个配置文件骨架。config.toml管 Agent 的运行参数模型路由、循环上限、工具权限settings.json管接入凭证和端点。分开的原因很简单前者可以进版本库后者不行。3.1 config.toml路由、循环、工具权限# config.toml —— Agent 运行参数 [model] # 默认日常档 default deepseek/deepseek-v4-flash # 推理档复杂任务用 reasoning z-ai/glm-5.2 # 路由关键词命中且规模超阈值才升档 reasoning_keywords [架构, 重构, 设计方案, 性能优化, 排查] # 规模阈值 file_count_threshold 5 plan_steps_threshold 8 [loop] # 最大迭代轮数 max_iterations 30 # 连续无进展熔断 no_progress_rounds 3 # 单任务 token 预算 token_budget 200000 # 单命令超时秒 command_timeout 300 [tools] # 文件系统允许的根目录 workspace_root /workspace # 默认拒绝的文件模式 deny_patterns [.env, *.key, *.pem, id_rsa] # 高危命令需二次确认 confirm_commands [rm -rf, git push --force, DROP TABLE] # 沙箱镜像 sandbox_image agent-sandbox:latest[model]段对应决策一[loop]段对应决策二[tools]段对应决策三。参数含义后面几节会逐个展开。3.2 settings.json接入凭证与端点{ anthropic: { base_url: https://taotoken.net/api, auth_token: ${ANTHROPIC_AUTH_TOKEN} }, openai: { base_url: https://taotoken.net/api, api_key: ${OPENAI_API_KEY} }, default_protocol: anthropic }这里用${...}占位实际运行时从环境变量注入。本地开发可以临时写死但别提交。default_protocol决定 Agent 默认走哪套协议切换模型供应商时改这里或改路由逻辑即可。3.3 模型路由实现路由逻辑按顺序判断命中即停。核心是三条规则用户显式指定优先、关键词加规模触发推理档、其余走日常档。# router.py import tomllib with open(config.toml, rb) as f: cfg tomllib.load(f) MODEL_MAP { default: cfg[model][default], reasoning: cfg[model][reasoning], } def route_model(task: str, file_count: int, plan_steps: int, override: str | None) - str: # 规则 1显式指定 if override: return MODEL_MAP[override] # 规则 2关键词 规模 hit any(k in task for k in cfg[model][reasoning_keywords]) if hit and (file_count cfg[model][file_count_threshold] or plan_steps cfg[model][plan_steps_threshold]): return MODEL_MAP[reasoning] # 规则 3默认日常档 return MODEL_MAP[default]有个细节要注意首轮生成计划之前拿不到plan_steps所以实际顺序是首轮统一用日常档生成计划计划出来后步骤数超阈值从第二轮起升档。这样避开了“为了路由先问一次模型”的鸡生蛋问题。4. 循环设计与工具调用验证循环是 Agent 和聊天机器人最本质的区别。核心原则只有一条模型的每一步判断必须建立在真实执行反馈上而不是它自己的想象。代码报没报错、命令跑没跑通这些信息要原样回传给模型。4.1 循环骨架# loop.py import subprocess, json, time def run_agent(task: str, tools: dict, max_iter: int 30): history [{role: user, content: task}] for i in range(max_iter): # 1. 模型决策 resp call_model(history) action parse_action(resp) # 2. 终止判断 if action[type] finish: return action[result] # 3. 工具执行 tool tools[action[name]] result tool(**action[args]) # 4. 结果回传原样不加工 history.append({role: assistant, content: resp}) history.append({role: tool, content: json.dumps(result)}) return {error: max_iterations_reached}4.2 工具定义保持愚蠢工具层只做执行和返回不做摘要、不做判断。下面是一个沙箱命令工具# tools.py import subprocess def run_command(command: str, timeout: int 300) - dict: try: p subprocess.run( [timeout, str(timeout), bash, -c, command], capture_outputTrue, textTrue, timeouttimeout 5 ) return {exit_code: p.returncode, stdout: p.stdout, stderr: p.stderr} except subprocess.TimeoutExpired: return {exit_code: -1, stdout: , stderr: timeout}返回{exit_code, stdout, stderr}三元组零加工。模型拿到完整报错才能一轮定位问题把 traceback 截成“测试失败”反而让它瞎猜。4.3 一次完整的工具调用循环假设任务是“在 /workspace 下创建一个 hello.py 并运行”。循环过程如下第一轮模型输出工具调用write_file(path/workspace/hello.py, contentprint(hi))。工具执行返回{exit_code: 0}。第二轮模型输出run_command(commandpython /workspace/hello.py)。工具执行返回{exit_code: 0, stdout: hi\n, stderr: }。第三轮模型看到 stdout 是hi判断任务完成输出finish。循环结束。整个过程模型看到的是真实执行结果不是它猜的。这就是“循环吃真实反馈”的含义。4.4 验证请求配置好之后先用一个最小请求验证接入层通不通curl https://taotoken.net/api/v1/messages \ -H x-api-key: $ANTHROPIC_AUTH_TOKEN \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: deepseek/deepseek-v4-flash, max_tokens: 64, messages: [{role: user, content: 回复 ok}] }返回里有content字段且文本正常说明 Key 和端点都对。这一步过了再跑 Agent 循环能省掉很多排查时间。5. 本篇常见错排查5.1 401 或认证失败先确认ANTHROPIC_AUTH_TOKEN环境变量有没有真正注入。settings.json里写的是${ANTHROPIC_AUTH_TOKEN}如果 shell 里没 export运行时就是空字符串。用echo $ANTHROPIC_AUTH_TOKEN检查一下。5.2 模型名报错不同协议下模型名的写法可能不同。Anthropic 协议走model字段OpenAI 协议也是model但值要和你路由配置里的一致。如果报“model not found”先确认config.toml里的default和reasoning值拼写正确。5.3 循环跑飞停不下来检查max_iterations有没有生效。如果循环里没有把工具结果 append 回 history模型每轮看到的历史都一样就会重复同一个动作。另外确认no_progress_rounds的判定逻辑——如果相似度判定过严等于形同虚设最后还是靠最大轮数兜底。5.4 工具权限越界文件工具报“permission denied”先看workspace_root配置。如果 Agent 试图读写根目录外的文件应该在工具层直接拒绝而不是靠模型自觉。deny_patterns里的模式要覆盖.env、密钥文件这些。5.5 沙箱里装不了依赖如果沙箱是--read-only加--network none运行时装依赖这条路走不通。解决办法是镜像里一次装够常用运行时别指望运行时再 pip install。镜像大一点没关系切来切去的复杂度更麻烦。6. 接入与后续跑通最小循环之后下一步是把接入层固定下来。TaoToken 的 API Keys 页面可以管理你的 Key接入文档里有各协议的完整参数说明。如果你要长期跑编码类 AgentCoding Plan 提供了更适合持续调用的通道想先验证模型效果可以直接在模型对话里试。API Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentchatCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan最后留一个实操建议先把config.toml里的max_iterations设成 5用一个“创建文件并读取”的简单任务跑通全流程确认工具结果能正确回传、循环能正常终止再逐步放开轮数和工具权限。这样出问题时排查范围小比一上来就跑复杂任务高效得多。