
1. 为什么 Demo 里的 Agent 一上生产就崩你大概率遇到过这种场景本地跑一个 ReAct Agent查天气、算汇率、读文件丝滑得像演示视频。一放到生产环境接上真实业务工具它开始编造不存在的订单号或者在同一段工具调用里来回打转日志刷了几百行还没退出。这不是模型变笨了而是 Demo 和生产之间隔着一整套约束机制。Agent 的本质不是聊天机器人它是基于 LLM 的决策执行器。传统代码里逻辑是你写的if A then B else C是确定的Agent 里逻辑是 LLM 生成的它判断当前状态、决定调哪个工具、生成什么参数。LLM 是概率模型没有确定性执行的保障。Demo 能跑通通常只是因为 Prompt 简单、上下文窗口够覆盖、工具返回格式固定、没有复杂依赖。一旦任务变成“先查库存再按价格策略算折扣最后生成订单并通知”中间任何一步的噪声都会被放大成幻觉或死循环。这篇按“先定位、再配置、后验证”的顺序写给你一份可复制的排查清单。核心思路是把 LLM 的不确定性关进工程约束的笼子里用统一的 Key 通道把调用链路收拢这样出问题时你能快速判断是模型调用链路的问题还是工具循环的问题。适合正在把 Agent 从本地推向生产、被幻觉和死循环折磨的开发者。2. 前置准备用 TaoToken 统一 Key 通道排查幻觉和死循环第一步不是改 Prompt而是让调用链路可观测、可切换。如果你的 Agent 里散落着多个模型的 Key、多个 base_url出问题时你连“这次请求到底打到哪个模型”都说不清排查无从下手。TaoToken 在这里的作用是提供一个统一的模型调用入口。你可以把它理解成一个 Key 通道Agent 里的 LLM 调用统一走同一个 base_url 和同一套 Key模型切换、额度查看、调用日志都在一个地方。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。具体操作上先去控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完在 API Keys 页面管理你的密钥页面是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。如果你用的是 Claude Code 这类编码 Agent接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite ClaudeCodeAnthropic 相关配置参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。为什么统一通道对排查这么关键因为幻觉和死循环的根因经常不在同一层。幻觉多半是模型调用链路的问题——上下文太长、模型选错、温度过高死循环多半是工具循环的问题——工具返回格式不符合预期、Agent 无法判断任务已完成、重试策略缺失。如果 Key 和 base_url 是统一的你就能通过切换模型、对比日志快速把问题归到某一层。如果链路是散的你连复现都做不到。3. 可复制配置settings.json 与 config.toml 骨架下面给两份骨架配置一份给基于 Node/Claude Code 风格的 Agent一份给 Python 侧的 config.toml。重点不是照抄而是看每个字段为什么这么设尤其是和幻觉、死循环直接相关的参数。3.1 settings.json 骨架{ llm: { provider: openai-compatible, base_url: https://taotoken.net/api, api_key: ${TAOTOKEN_API_KEY}, model: claude-sonnet-4-5, temperature: 0.2, max_tokens: 4096, timeout_ms: 60000, max_retries: 2 }, agent: { max_iterations: 12, max_tool_calls_per_turn: 3, loop_detection: { enabled: true, window: 5, similarity_threshold: 0.92 }, tool_result_max_chars: 8000, require_final_answer: true }, logging: { level: debug, trace_tool_calls: true, trace_llm_requests: true, log_dir: ./logs/agent } }几个关键点。temperature设 0.2 而不是 0是因为完全为 0 有时会让模型在工具选择上过于死板0.2 在稳定性和灵活性之间比较平衡。max_iterations是死循环的第一道闸Demo 里经常不设生产必须设12 是一个保守起点。loop_detection是循环检测window表示看最近 5 次工具调用similarity_threshold0.92 表示两次调用参数相似度超过这个值就判定为疑似循环。tool_result_max_chars限制工具返回塞进上下文的长度防止一次大结果把窗口撑爆导致后续幻觉。3.2 config.toml 骨架[llm] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model claude-sonnet-4-5 temperature 0.2 max_tokens 4096 request_timeout 60 [agent.loop] max_iterations 12 max_tool_calls_per_turn 3 repeat_window 5 repeat_similarity 0.92 [agent.tools] result_truncate_chars 8000 strict_schema true allow_write_ops false [agent.recovery] retry_on_tool_error 2 fallback_to_human true human_confirm_keywords [delete, drop, update, transfer] [logging] trace_tool_calls true trace_llm_requests true log_dir ./logs/agentstrict_schema true强制工具参数走 Schema 校验这是把“黑盒幻觉”转成“白盒代码”的关键。allow_write_ops false默认关掉写操作需要时再开避免 Agent 在生产库上自由发挥。human_confirm_keywords是人工介入的触发词命中这些关键词的写操作必须二次确认。fallback_to_human true表示重试耗尽后不直接崩而是转人工。3.3 工具层的 Schema 约束示例光有配置还不够工具本身要能拒绝脏输入。下面是一个 Python 侧的工具参数校验示例思路是让 LLM 生成结构化 JSON由代码层组装查询而不是让 LLM 自己拼 SQL。from typing import Annotated from pydantic import BaseModel, Field class DatabaseQueryParams(BaseModel): table_name: Annotated[str, Field(pattern^[a-zA-Z_]$)] columns: Annotated[list[str], Field(min_length1)] filter_condition: Annotated[dict | None, Field(defaultNone)] def execute_safe_query(params: DatabaseQueryParams) - dict: # params 已通过 Pydantic 校验这里用 ORM 或参数化查询 print(fquery table{params.table_name} cols{params.columns}) return {status: success, rows_affected: 0}LLM 调用这个工具前必须生成符合DatabaseQueryParams的 JSON。表名只允许字母和下划线字段列表不能为空过滤条件走结构化字典。这样即使模型产生幻觉它也只能在 Schema 允许的范围内“编”编不出DROP TABLE。4. 验证请求用日志区分幻觉还是死循环配置写完下一步是验证。验证的目标不是“跑通”而是“能定位”。你需要两类日志LLM 请求日志和工具调用日志。4.1 发一个最小请求验证通道先用 curl 确认 Key 通道是通的避免把通道问题误判成 Agent 问题。curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-5, messages: [{role: user, content: 只回复 ok}], temperature: 0.2 }返回里能看到choices[0].message.content就说明通道正常。如果这里就报错先解决 Key 和 base_url别往下查 Agent。4.2 用日志判断故障类型跑一个会触发工具调用的任务然后看日志。判断规则很简单现象日志特征大概率根因编造不存在的订单号LLM 请求日志里上下文很长工具返回被截断幻觉上下文或工具结果处理问题同一工具反复调用工具调用日志里连续多次参数高度相似死循环循环检测或终止条件问题工具报错后 Agent 直接退出工具日志有 error之后无 LLM 请求重试策略缺失模型答非所问LLM 请求日志里 model 字段和预期不符通道配置串了模型我试过把trace_tool_calls和trace_llm_requests同时打开一次死循环的日志里能清楚看到Agent 连续 7 次调用同一个查询工具参数只差一个分页 offset而loop_detection因为阈值设成 0.98 没触发。把阈值降到 0.92 后第 3 次就被拦下并转人工。这就是日志的价值——它告诉你问题在哪一层而不是让你猜。4.3 验证循环检测是否生效故意构造一个会循环的场景比如让 Agent 查询一个永远返回空结果的表观察它是否在max_iterations或loop_detection触发时停下。如果它停下来了并且日志里有loop_detected或max_iterations_reached的记录说明闸门生效。如果它一直跑检查loop_detection.enabled是否为 true以及window是否设得太大。5. 本篇常见错排查5.1 幻觉类问题上下文过长是最常见的幻觉来源。工具返回一个 5 万字符的 JSON直接塞进上下文模型注意力被稀释开始编。解法是tool_result_max_chars截断或者对工具结果做摘要后再入上下文。另一个来源是模型选错用了一个不擅长工具调用的模型表现为工具参数格式经常不对。这时候在 TaoToken 的模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 手动试几个模型对比同一 Prompt 下的工具调用质量比在代码里反复试快得多。温度过高也会加剧幻觉。生产环境建议 0.2 以下需要创意的场景再调高。还有一种是 Prompt 里让 LLM 自己拼 SQL 或自己算数这类确定性任务应该交给代码层别让概率模型买单。5.2 死循环类问题死循环的根因通常是 Agent 无法判断“任务已完成”。比如工具返回{status: pending}Agent 不知道 pending 要等多久就反复查。解法是给工具返回加明确的终态字段或者在 Prompt 里写清楚什么条件下必须停止。另一个根因是重试策略缺失工具报错后 Agent 不知道怎么办就重试同一个调用。配置里的retry_on_tool_error和fallback_to_human就是干这个的。max_iterations设太大也是坑。有人设 100觉得给足空间结果死循环时烧掉大量 token 才停。12 到 20 是比较合理的区间复杂任务可以到 30但要有循环检测兜底。5.3 通道类问题如果日志里 LLM 请求全部失败先查base_url是不是写成了带路径的完整地址。TaoToken 的 API 地址是 https://taotoken.net/api OpenAI 兼容接口通常拼/v1/chat/completions。Key 是否过期、额度是否用完在控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 能看到。如果多个 Agent 共用一个 Key 导致限流考虑拆 Key 或上 Coding Plan长期编码和 Agent 场景用 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 更合适。5.4 工具 Schema 类问题工具参数校验失败时Agent 有时会反复重试同一个错误参数。这时候要看日志里工具返回的错误信息是否足够明确。如果只返回invalid paramsAgent 不知道哪里错就会瞎试。返回table_name must match ^[a-zA-Z_]$这种具体信息Agent 才有机会修正。Schema 校验要严但错误信息要具体这两者不矛盾。6. 把不确定性关进笼子回到开头的问题为什么 Agent 在真实项目里容易失控因为开发者试图用概率性的 LLM 去解决确定性的工程问题却忽略了中间那层约束机制。工具调用要有边界感最小权限加结构化输出记忆要有清理策略别把整个代码库塞进上下文任务规划要有显式状态和失败恢复不能只靠 LLM 的直觉。排查幻觉和死循环核心是让链路可观测。统一 Key 通道让你能对比模型、定位层级日志让你看到每一步的思考和工具参数循环检测和迭代上限是兜底的闸门。先把这些“脏活”修好再谈智能。如果你在配 settings.json 或 config.toml 时卡在某个字段或者日志里出现了拿不准的循环模式可以去接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 对照参数说明也可以在模型对话页面手动复现一次工具调用看看模型在无框架干扰下的原始输出长什么样。很多时候问题就藏在那段原始输出里。