智能体日志怎么埋才好排查问题
智能体上线后最折磨人的不是它不工作,是它"有时候不工作"。你拿同一句话去问,十次有一次它抽风,你还复现不出来。这种问题,没日志就是抓瞎。
我自己摸出来一套埋点的粒度,分享下,主要面向那种拖拽配出来的智能体(就是不写后端、靠平台编排节点那种)。
先想清楚:一次请求要还原哪些东西
排查线上问题,本质是回放"用户这一次到底经历了什么"。所以一条完整的 trace 至少得能还原这几段:
用户原始输入(别清洗,原样存,脏数据本身就是线索)
命中了哪个意图 / 走了哪条分支
检索了知识库没有、召回了哪几条、相似度多少
喂给模型的最终 prompt(拼接后的完整体)
模型原始返回(不是处理后的,是裸返回)
每一步的耗时和 token 消耗
少一段,排查就断一节。我吃过亏的就是第4条——只存了"用户问题"和"模型回答",中间 prompt 怎么拼的没存。结果有次模型胡说八道,我盯着输入输出看半天看不出问题,最后才发现是 RAG 召回的一段过期文档被拼进 prompt 了,模型照着错文档答的。模型没错,喂它的料错了,但没存 prompt 我根本看不见这一层。
用一个 trace_id 串起来
每次请求入口生成一个 trace_id,往后所有节点的日志都带上它。这样不管日志落在哪,按 trace_id 一搜就是完整链路。
[trace_id=a3f9] input: "退款多久到账" [trace_id=a3f9] intent: refund_query (conf=0.82) [trace_id=a3f9] rag: hit=3, top_sim=0.71, doc_ids=[12,45,88] [trace_id=a3f9] prompt_len=1840 tokens [trace_id=a3f9] llm: latency=2.3s, out_tokens=156 [trace_id=a3f9] output: "退款一般3-7个工作日..."这种格式人眼能扫,grep 也好搜。别一上来就上花哨的结构化 JSON 把自己绕进去,先能 grep 出来再说。
平台编排的智能体怎么埋
如果是手写代码的 Agent,埋点就是加日志语句的事。但很多人是在那种零代码拖节点的平台上搭的智能体,这时候日志埋点的思路不太一样——你看不到底层代码,得靠平台自带的运行记录。
我用的那个低代码搭智能体的平台,每次运行会把每个节点的输入输出、耗时自动记下来,相当于 trace 框架白送了。但有两个地方它默认记得不够细,得自己补:
节点间传的变量:默认只记最终输出,中间节点改了什么变量得自己在节点里显式输出一下,不然断链。
外部工具(MCP)调用的原始返回:这个一定要记。我接了个查物流的 MCP,有次它接口超时返回了个空对象,智能体傻乎乎拿空对象往下走,最后回了句莫名其妙的话。日志里看到 MCP 返回是空,五秒钟定位。
几个实用的脏经验
采样别一刀切。正常请求按比例采样(比如10%),但报错的、置信度低的、耗时超阈值的,100%全采。出问题的请求才是你最想看的。
prompt 太长别全存,存个 hash + 前后各200字,需要完整的再按 trace_id 去捞。不然日志量爆炸,存储费比模型费还贵。我有个项目就是日志没节制,一个月日志存储花了小三百,比调模型的钱还多,肉疼。
敏感字段脱敏再落库。手机号、地址这些日志里该打码打码,别图省事原文存,合规会找你。
小结
日志埋点的核心就一句:让任意一次请求都能被完整回放。trace_id 串链路,重点记 prompt 拼接和工具原始返回这两个最容易出锅的地方,报错请求全采样。
(模型这层我直接调的讯飞星辰 MaaS,现成大模型 API,省了自己部署推理服务那套,日志里也就少了一整层 GPU 那边的坑。)
