1. 上线第三天Agent 开始“不听话”了AI Agent 上线后最让人抓狂的不是它不会做而是它昨天还乖乖调用搜索工具今天突然开始乱删文件、乱调接口、简单问题烧掉几十倍 Token。你翻遍业务日志只看到一句result: success完全不知道中间发生了什么。这个场景几乎每个做 Agent 的团队都会撞上因为 Agent 本质是一个动态决策系统不是固定代码流程。传统应用出问题看日志就行Agent 的链路是用户问题 → LLM 理解 → 制定计划 → 选择工具 → 调用 API → 读取结果 → 再次推理 → 生成答案中间任何一步偏了最终结果就错而你根本看不到它中间想了什么、调了什么、花了多少。我试过最笨的办法在每个节点print(response)结果日志刷屏还是定位不到是哪一步把/project/config当成临时文件删了。问题不在模型能力而在可观测性缺失。这篇就聚焦一个具体排查场景Agent 上线后行为漂移怎么用 TaoToken 统一 Key 配合 Langfuse 追踪快速判断“不听话”到底是模型、提示词还是通道配置导致的。适合已经跑通 Demo、准备上生产或已经上线踩坑的开发者跟着做能拿到一套可复制的配置骨架和验证步骤。2. 为什么排查前要先统一 Key 通道在接入 Langfuse 之前有个前置动作经常被忽略把 Agent 里散落各处的模型调用收敛到一个统一入口。很多项目的 Key 是这么来的Planner 用一个 KeyTool 调用用一个 KeyRAG 总结又换一个 Key甚至有人把不同厂商的 Key 硬编码在不同文件里。一旦行为漂移你连“这次请求到底走了哪个通道、哪个模型”都说不清Langfuse 里看到的 trace 也是碎的。TaoToken 在这里的作用是提供一个统一的 API 入口把模型调用集中管理。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。统一 Key 之后Agent 的每一次 LLM 调用都经过同一个通道Langfuse 抓到的 trace 才能完整串起来。这一步不是为了“换个供应商”而是为了让可观测性有落脚点——通道不统一追踪就是断的。具体操作上先去控制台创建 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 。生成后不要写死在代码里用环境变量注入。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言 SDK 的 base_url 配置方式照着改就行。3. 可复制的统一 Key 配置骨架下面这套骨架我实测下来比较稳核心思路是所有模型调用走一个 client 工厂base_url 指向 TaoTokenKey 从环境变量读同时预留 Langfuse 的 trace 注入点。先装依赖pip install openai langfuse然后写配置模块agent_llm.pyimport os from openai import OpenAI from langfuse.decorators import observe, langfuse_context # 统一 Key 通道所有模型调用都从这里走 client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api, ) # 模型名按你实际使用的填这里用占位 DEFAULT_MODEL os.environ.get(AGENT_MODEL, your-model-name) observe() def call_llm(prompt: str, model: str DEFAULT_MODEL): # 把关键元数据挂到当前 trace 上方便 Langfuse 过滤 langfuse_context.update_current_observation( metadata{channel: taotoken, model: model} ) resp client.chat.completions.create( modelmodel, messages[{role: user, content: prompt}], ) usage resp.usage langfuse_context.update_current_observation( usage{ input: usage.prompt_tokens, output: usage.completion_tokens, } ) return resp.choices[0].message.contentLangfuse 的环境变量也要配好否则 trace 发不出去export LANGFUSE_PUBLIC_KEYpk-lf-xxxx export LANGFUSE_SECRET_KEYsk-lf-xxxx export LANGFUSE_HOSThttps://cloud.langfuse.com export TAOTOKEN_API_KEY你的TaoToken Key export AGENT_MODEL你的模型名这里有个关键点observe()装饰器会把函数调用自动变成 Langfuse 的一个 spanupdate_current_observation把通道信息和 Token 用量挂上去。这样在 Dashboard 里你既能按channeltaotoken过滤也能直接看到每次调用的 input/output token 数。Agent 的 Planner、Tool 调用、RAG 总结如果都走call_llm整条链路就自动串成一个 trace。4. 把 Agent 执行链路接进 Langfuse光有 LLM 调用记录还不够Agent 的“不听话”往往出在工具调用和规划步骤上。下面把工具调用也纳入追踪用一个文件管理 Agent 举例复现“为什么删错目录”的排查过程。from langfuse.decorators import observe from agent_llm import call_llm observe() def plan_task(question: str): prompt f你是任务规划器。用户请求{question}\n输出要调用的工具和参数。 return call_llm(prompt) observe() def call_tool(tool_name: str, params: dict): # 真实项目里这里接你的工具实现 if tool_name delete_files: return {deleted: params.get(paths, []), status: success} return {status: unknown_tool} observe() def agent_run(question: str): plan plan_task(question) # 简化解析实际项目按你的规划格式处理 tool_result call_tool(delete_files, {paths: [/project/cache]}) final call_llm(f根据工具结果生成回答{tool_result}) return final跑一次agent_run(帮我清理项目中的临时文件)执行完去 Langfuse Dashboard你会看到一棵 trace 树Trace: 帮我清理项目中的临时文件 ├── plan_task │ └── call_llm (channeltaotoken, input_tokens..., output_tokens...) ├── call_tool (delete_files) └── call_llm (生成回答)这时候如果发现删错了目录直接点开plan_task那个 span看它输出的工具参数里paths到底是什么。如果paths里出现了/project/config问题就在规划阶段——要么提示词没约束清楚要么模型理解偏了。如果paths是对的但call_tool执行错了问题在工具实现。如果call_llm的 input_tokens 异常大说明提示词里塞了太多无关上下文。这就是可观测性的价值把“猜”变成“看”。5. 验证调用链是否正常的操作步骤配置完别急着上生产先做一轮验证确认 trace 能正常上报、Token 能对上、链路是完整的。第一步跑一个最小请求确认通道通curl https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:your-model-name,messages:[{role:user,content:ping}]}返回正常说明 Key 和通道没问题。如果这里就报 401先查 Key 和 base_url别往下走。第二步跑agent_run然后去 Langfuse 看三件事trace 是否出现、channeltaotoken的 metadata 是否挂上、usage 里的 token 数是否和响应里的 usage 一致。如果 trace 没出现检查LANGFUSE_HOST和网络如果 token 数为空检查update_current_observation的 usage 字段名。第三步做一次 Token 消耗对比。同一个问题分别用统一 Key 通道和之前的散装配置各跑一次在 Langfuse 里按 trace 对比 input/output token。如果统一通道后 Token 明显下降说明之前有重复调用或上下文冗余如果反而上升检查是不是把不该带的上下文塞进了 prompt。第四步模拟一次“行为漂移”。故意把规划提示词改模糊比如把“只删除临时文件”改成“清理项目”再跑一次看 Langfuse 里plan_task的输出是否开始包含危险路径。这一步能帮你确认当问题真的发生时你的追踪链路能不能定位到根因。验证通过的标准很简单任意一次 Agent 请求你能在 Langfuse 里看到完整的 trace 树每个 LLM 调用都有 token 数每个工具调用都有参数和结果并且能按通道过滤。达到这个状态再谈优化。6. 本篇常见错排查Langfuse 里看不到 trace最常见的是环境变量没生效尤其是LANGFUSE_HOST写错或没设。另一个原因是observe()装饰的函数没有被真正调用或者调用发生在装饰器作用域外。检查一下agent_run是不是被observe()包住了。Token 数和实际对不上如果你在call_llm里手动传了 usage但模型返回的 usage 字段名不同有的用prompt_tokens有的用input_tokens就会对不上。统一用响应里的resp.usage取值别自己算。通道 metadata 丢失update_current_observation必须在observe()装饰的函数内部调用且要在 LLM 请求前后都行但别在函数外调。如果 metadata 没挂上检查是不是在异步环境里用了同步装饰器。Agent 行为漂移但 trace 正常如果 trace 显示规划、工具、总结都“正常”但结果还是错问题可能在提示词或模型本身。这时候用模型对话功能单独测一下同一个 prompt地址是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 把 trace 里记录的原始 prompt 贴进去看模型单独跑会不会也偏。如果单独跑正常、Agent 里偏问题在上下文拼接如果单独跑也偏问题在提示词或模型选择。长期编码和 Agent 调试想省成本如果你在反复调 Agent 的规划逻辑每次改一点就跑一次全链路Token 消耗会很快。这种情况可以看看 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 适合需要长期迭代编码和 Agent 逻辑的场景。Claude Code 相关的接入配置在 https://taotoken.net/claudecode?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 有需要可以对照。排查的核心逻辑就一句话先确认通道统一再确认 trace 完整最后按 span 逐层看。模型、提示词、通道配置这三个变量只要 trace 够细一定能定位到是哪个。
