Tracing工具5分钟定位Agent串号,排查从2天缩到分钟级
上周我们团队一个客服Agent在生产环境连续搞砸了3个客户咨询,把前一个用户的手机号硬塞给了后一个用户——这就是典型的上下文污染,而排查过程整整花了我两天时间。后来用Tracing工具一追踪,问题根源5分钟就定位了。
什么是上下文污染?
简单说,就是Agent在处理多轮对话时,把不该记住的东西记住了,或者把该记住的东西搞混了。这跟人类的“失忆”症状很像——记错了、记混了、或者干脆忘了。
我见过最离谱的情况是,一个理财顾问Agent在连续服务5个客户后,把第一个客户的年收入数据带到了第五个客户的资产配置建议里。要不是合规团队review日志时发现,这锅可就大了。
等等,这里我要更正一下——准确说不是“连续服务5个客户”,而是同一个pod里跑了5个并发会话。容器没挂,但内存共享了。这个区别挺关键的,因为并发污染比顺序污染更难复现。
三个真实案例
案例1:客服Agent的“串号”事故
去年11月,我们给一个电商客户部署了基于LangChain的客服Agent。LangChain版本是0.1.17,记得很清楚,因为那天是11月14号,上线第二周就出事了:
用户A:我的订单号是20231115001,什么时候发货?
Agent:您的订单20231115001预计明天发货。
用户A:好的谢谢
[会话结束]
[新会话开始]
用户B:我想查一下物流
Agent:您好,订单20231115001的物流信息显示...看到了吗?Agent把用户A的订单号“记忆”到了用户B的会话里。原因是我们的会话管理模块有个bug——当Redis连接池耗尽时,部分会话ID没有正确更新,导致两个用户共享了同一个memory对象。
用LangSmith的trace一看,问题一目了然:
# Trace输出片段
{
"session_id": "sess_abc123", # 应该是 sess_def456
"memory_variables": {
"order_number": "20231115001", # 上一个会话的残留数据
"customer_name": "张三"
}
}这个bug修了整整一个通宵。凌晨3点我还在群里跟同事对线,争论到底是Redis客户端的问题还是我们封装层的问题。最后发现是redis-py 5.0.1的连接池参数max_connections设太小了,只有10个,高峰期直接打满。
案例2:RAG Agent的知识库幻觉
这个更隐蔽。我们构建了一个内部知识库Agent,用来回答员工关于公司政策的问题。测试时表现完美,但正式用起来就各种“胡说八道”。
通过Arize Phoenix的trace分析,我发现问题出在检索增强生成(RAG)的上下文窗口管理上:
第1轮:用户问“年假怎么算”
→ 检索到文档A(年假政策)+ 文档B(调休政策)
→ Agent正确回答
第2轮:用户问“那病假呢”
→ 检索到文档C(病假政策)
→ 但上下文窗口里还残留着文档B的片段
→ Agent把调休政策的内容混进了病假回答里具体数据是这样的:
- 上下文窗口限制:4096 tokens
- 第1轮占用:1850 tokens(用户问题+检索文档+回答)
- 第2轮时,系统应该清空检索文档,只保留对话摘要
- 实际trace显示:第2轮的context里包含了第1轮的文档片段(约600 tokens)
这就是典型的“上下文未正确裁剪”问题。后来我们在每次检索前强制清空了文档缓存,问题解决。
嗯...这个其实说起来简单,做起来坑不少。我们试了三种方案:第一种是直接truncate,结果把重要信息截掉了;第二种是用LLM做摘要压缩,但延迟增加了300ms;最后用了滑动窗口+优先级标记,勉强能用。大概调了一周吧。
案例3:Multi-Agent协作时的“记忆串扰”
这是最近踩的坑。2025年1月,我们用CrewAI 0.2.1构建了一个多Agent系统,包括:
- 数据分析Agent
- 报告生成Agent
- 审核Agent
三个Agent共享一个对话历史。用Langfuse追踪发现:
数据分析Agent输出:Q3营收增长15%,主要驱动因素是A产品线
报告生成Agent读取上下文:Q3营收增长15%,主要驱动因素是A产品线
审核Agent读取上下文:Q3营收增长15%,主要驱动因素是A产品线(正确)
但第5轮对话时:
数据分析Agent输出:Q4预测营收增长20%,基于B产品线扩张
报告生成Agent读取上下文:Q3营收增长15%...Q4预测营收增长20%...
审核Agent读取上下文:Q3营收增长15%...Q4预测营收增长20%...等等,B产品线是什么?Trace显示审核Agent在读取共享内存时,把数据分析Agent的中间推理过程(scratchpad)也读进去了。这些中间数据包含未验证的假设和计算草稿,导致审核Agent基于不完整信息做出了错误判断。
这个问题的根因,我觉得是CrewAI的memory共享机制设计得太“大方”了。它默认把所有agent的思考过程都塞进一个共享memory空间,没有做隔离。据我了解,Autogen在这块做得稍好一些,但也好不到哪去。
我是怎么用Tracing工具排查的
说实话,没有tracing工具,这些问题够我排查一周的。分享一下我的排查流程:
第一步:开启详细trace
我用的是LangSmith(LangChain生态),配置很简单:
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = "agent-debugging"
# 对所有LLM调用和工具调用打点
from langsmith import traceable
@traceable(run_type="chain")
def process_user_query(session_id, query):
# 你的Agent逻辑
pass第二步:定位异常trace
关键指标我主要看这几个:
- **上下文窗口利用率**:如果超过80%,就要警惕信息过载
- **Memory变量变化**:对比每轮对话前后的memory状态
- **Token消耗异常**:突然飙升通常意味着上下文污染
这是我写的排查脚本片段:
# 分析trace中的上下文变化
def analyze_context_pollution(trace_data):
for span in trace_data.spans:
if span.name == "memory_load":
prev_memory = span.input
curr_memory = span.output
# 检查是否有不应该存在的键
expected_keys = ["user_query", "chat_history"]
actual_keys = list(curr_memory.keys())
unexpected = set(actual_keys) - set(expected_keys)
if unexpected:
print(f"⚠️ 发现污染数据: {unexpected}")
print(f"Span ID: {span.id}")这个脚本其实写得挺糙的。后来同事pr一个更好的版本,用了pydantic做schema校验,准确率高了不少。但我这个版本胜在简单,一眼就能看懂。
第三步:重现和修复
定位到问题后,我在本地用相同的trace数据重现:
# 从trace中提取输入,本地重现
test_input = trace_data.runs[0].inputs
test_output = my_agent.invoke(test_input)
# 对比trace中的输出和本地输出
assert test_output == trace_data.runs[0].outputs预防上下文污染的实践建议
踩了这么多坑,总结几条经验:
1. 会话隔离必须彻底
不要依赖框架默认的会话管理,自己加一层校验:
def get_or_create_memory(session_id):
memory = redis.get(f"memory:{session_id}")
if memory:
# 验证memory中的session_id是否匹配
if memory.get("bound_session") != session_id:
logger.error(f"会话污染警告: {session_id}")
memory = create_new_memory(session_id)
return memory2. 上下文窗口定期“体检”
我在每次Agent响应前加了个检查:
def sanitize_context(context, current_session):
# 移除不属于当前会话的信息
cleaned = {}
for key, value in context.items():
if value.get("session_id") == current_session:
cleaned[key] = value
return cleaned3. 使用Tracing做持续监控
现在我把LangSmith的trace数据接入了Prometheus,设置了告警规则:
- 上下文token数超过3500时告警
- Memory中键的数量异常增长时告警
- 跨会话数据引用时立即告警
说到监控,上个月Grafana 11发布,dashboard做得更漂亮了。但说实话,我还是习惯看纯文本日志。老派。
写在最后
上下文污染这个问题,比模型幻觉更难排查。
幻觉至少能看出来“这句话不对”。
但上下文污染往往让Agent的回答看起来“合理但错误”——就像开头那个把订单号搞混的例子,如果用户B不主动质疑,可能就这么过去了。这才是最可怕的。
我现在养成了习惯:每次Agent上线前,先用tracing工具跑一遍多轮对话的压力测试,专门看memory的变化轨迹。这个习惯帮我至少避免了3次线上事故。最近一次是上周四,一个金融Agent差点把测试环境的利率数据带到生产环境,还好trace里看到了异常标记。
你们遇到过类似的上下文污染问题吗?是用什么工具排查的?我特别想知道在非LangChain生态(比如直接调OpenAI API)的场景下,大家怎么处理这个问题。欢迎在评论区聊聊,或者直接提issue到我维护的agent-debugging-tools仓库——虽然这个仓库最近更新不多,但我保证会看。
标签: #Agent开发 #上下文污染 #Tracing #LangChain #生产故障排查
读者评论 5