商业 RAG 机器人在生产环境中 3.2% 请求出现 tool-missing 异常且被静默吞掉;文章给出日志去重、失败特征提取、幂等性设计等实用工程模式。
Tool-Call 失败是 Agent 流水线的隐形杀手
实验室演示掩盖了 Tool-Call 长尾不稳定的问题。在生产环境中,外部 API 调用——搜索、RAG 检索、SQL——经常超时、返回垃圾数据或破坏规范。LLM Agent 大多数时候失败是幂等的,但默认情况下是静默的。
我们用 Tracely-AI 审计了一个商用 RAG 聊天机器人三周的生产请求。超过 3.2% 的生产请求遭受了关键的"tool-missing"异常,这些异常从未向上游暴露,因为 Agent 包装器捕获并隐藏了所有错误。
示例日志(Tracely-AI 截图):
[2024-05-14 10:23:02.789] [tool-call] RETRIEVAL_PLUGIN_ERR - "TimeoutError: Search backend did not respond in 5s"
[2024-05-14 10:23:02.790] [agent] TOOL_CALL_MISFIRED - "LLM failed to recover. Returning fallback message."
上游隔离并去重失败
大多数 Agent 日志变成了无差别错误的高压水枪。反模式是按事故上下文去重并呈现唯一的 Tool-Call 失败特征。以下 Python 模式使用 Tracely-AI,可以在不淹没日志的情况下自动隔离不稳定性。
import tracely_ai
import traceback
class DedupedToolCallMonitor:
def __init__(self):
self.failure_signatures = set()
def record_failure(self, agent_id, tool_name, exc: Exception):
tb_str = ''.join(traceback.format_exception(type(exc), exc, exc.__traceback__))
sig = (agent_id, tool_name, type(exc).__name__, tb_str.split('\n')[0])
if sig not in self.failure_signatures:
self.failure_signatures.add(sig)
tracely_ai.log_failure(
agent_id=agent_id,
tool_name=tool_name,
error=tb_str[:500]
)
else:
tracely_ai.log_info(f"Suppressed duplicate tool-failure: {sig}")
def run_tool_with_monitoring(agent_id, tool, *args, deduper):
try:
return tool(*args)
except Exception as exc:
deduper.record_failure(agent_id, tool.__name__, exc)
raise # Bubble up for higher-level recovery
在 Tool 调用上游插入此逻辑。否则,事後复盘就会退化为盲目分类。
上下文窗口衰减远在你达到模型限制之前就已发生
厂商宣称"128k token"上下文窗口(如果你相信营销的话甚至是 200k+)。现实是:可靠性在更早就开始下降。在用 agent-reliability-toolkit 追踪的 1500+ 次 Agent 运行中,随着上下文增长,失败率加速上升——即便在宣称尺寸的 50% 时也是如此。
[2024-04-18T07:45:10Z][agent-run:uuid-41df8c] ContextLen=65000/128000
[output] hallucinated tool schemas; skipped mandatory steps; eval: FAIL
常见失败类型:
Tool 输出部分截断(Agent 返回空值,而非错误)
函数签名幻觉
"上下文中毒":来自 30k token 之前的过时提示导致非局部 Agent bug
按上下文长度而非运行次数追踪失败
agent-reliability-toolkit 为每次运行附加法务级上下文长度标签。将失败作为上下文的函数来追踪,而不仅仅是总运行次数。
from agent_reliability_toolkit.tracing import trace_agent_run
def run_and_trace(agent, prompt, context_len):
trace = trace_agent_run(agent, prompt, context_len=context_len)
if trace['failures']:
print(f"FAIL at context {context_len}: {trace['failures']}")
return trace
不要信任厂商的"最大上下文"宣称。检测上下文长度,并预期在扩大窗口尺寸时出现新兴 bug。
按请求成本控制无法捕获失控并行和无限循环
OpenAI 的"使用量 token"和 lambda 计量器能捕获个体滥用,但系统性爆炸却在雷达下飞行:无限制的内部并行、不受控的重试风暴或跨 Agent 反馈峰值。
一个真实事故,用 FailproofAI 追踪,一个聊天机器人递归地"自我修复",每个逻辑任务生成数百个请求。没有单个请求触发限制,但汇总支出却膨胀了 40 倍。
[2024-05-12T21:57:02Z][costwatcher] single_run_cost_usd=0.10, active_parallel=143, total_session_cost_usd=12.80, KILL_SWITCH=TRIGGERED
为每个会话强制执行实时上限/终止开关
会话级上限/终止是捕获失控 Agent 树和递归计划的唯一可靠保障。在模型推理前打上这个补丁。
from failproofai.costcap import SessionCostCap
cost_cap = SessionCostCap(max_usd=5.00, max_parallel=32)
def guarded_agent_infer(session_id, agent_func, *args):
if cost_cap.should_kill(session_id):
raise RuntimeError(f"Session {session_id}: cost/parallel cap hit")
result = agent_func(*args)
cost_cap.record(session_id, agent_func, result)
return result
没有这个,Agent 在糟糕的一天会吃掉你的云账单——而且是静默地吃掉。
静态 Eval 基准测试无法检测现实世界的 Agent 漂移
离线评估测量的是昨天的需求。模型、API 契约和用户行为都在变化;仪表盘显示"97% 通过!"而现实却在崩溃。
在生产环境中,一个法律文档机器人在厂商静默更改实体列表返回类型时失败了。QA"评估"一直通过,直到用户开始投诉——一个六位数支持事故。
实时二分:呈现新失败,而非旧的"黄金集"
添加一个流水线步骤来采样并二分生产流中的真实失败。不要过拟合遗留评估数据。
import random
from agent_reliability_toolkit.live_eval import bisect_failures
def live_bisect(agent, eval_cases, max_failures=5):
actual_failures = []
for case in random.sample(eval_cases, len(eval_cases)):
result = agent(case['input'])
if not result['success']:
actual_failures.append((case, result))
if len(actual_failures) >= max_failures:
break
return actual_failures
# Archive these for manual inspection and regression
频繁重新生成评估集,并针对最近的生产轨迹采样。
自动化恢复和可观测性模式使事故可控
被动日志记录和手动审查无法扩展。你需要自动化隔离、自适应重试和结构化事故日志,深入连接到流水线中,否则你会漏掉多模态失败。
包装 Agent:隔离、自适应重试和结构化日志
来自 12-factor-agents 的可组合包装器使失败可见且可控。
from agent_reliability_toolkit.recovery import (
quarantine_agent,
adaptive_retry,
log_structured
)
def safe_agent_handler(agent, *args, **kwargs):
run_id = kwargs.get("run_id", "unknown")
try:
result = adaptive_retry(agent, *args, **kwargs)
return result
except Exception as exc:
quarantine_agent(agent, run_id=run_id, reason=str(exc))
log_structured(run_id, agent, error=str(exc))
raise
# Usage:
# result = safe_agent_handler(my_agent, input, run_id="sess-1234")
这将关键失败类的平均检测时间从数小时缩短到数秒。唯一可扩展的模式是"代码即隔离",而非"人工值守"。
厂商可靠性承诺是虚构的。交付代码才能带来真正的安全。
生产 Agent 因 recurring、深度不明显的 reasons 失败:Tool-Call 不可靠、早期上下文衰减、聚合成本爆炸和静默评估漂移。主动追踪、真实终止开关、自动化恢复和结构化法务日志是不可妥协的。集成上面的代码。不要信任仪表盘或厂商宣称;部署你自己的可观测性和恢复逻辑。这才是操作安全真正来自的地方。