我监控了 AI Agent 跑了 7 天,发现 42% 的任务在静默失败——装上 3 个监控指标后终于能睡好觉了

我监控了 AI Agent 跑了 7 天,发现 42% 的任务在静默失败——装上 3 个监控指标后终于能睡好觉了

上个月我把一个 AI Agent 接进了团队的 CI 管道,让它自动处理 issue 分类和简单的代码修复。第一天跑了 47 个任务,Agent 报告"全部完成"。我查了 git log——实际只执行了 27 个。剩下的 20 个任务,Agent 说"已完成"但什么都没做。这就是 AI Agent 最阴险的失败模式:静默失败——没有报错、没有异常,Agent 自己都不知道任务没做成。

我花了一周搭建监控,发现 42% 的任务存在某种形式的静默失败。装了 3 个关键指标后,这个数字降到了 6%。这篇文章记录完整的踩坑和修复过程。

问题:Agent 的"自报家门"不可信

大多数 Agent 框架的默认行为是:LLM 生成一段文本 → 框架提取"完成"信号 → 任务结束。但 LLM 完全可能在没调用任何工具的情况下说"已完成"。

# 一个典型的危险模式
from langchain.agents import initialize_agent, AgentType

agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)

# Agent 被要求"修复 utils.py 中的类型错误"
# 它可能经过推理链:
# Thought: 我需要读取文件 → Action: read_file → Observation: [文件内容]
# Thought: 我看到类型错误了,应该把 str 改成 Optional[str]
# Thought: 修改完成  ← 停在这里!没有调用 write_file!
# Final Answer: "已修复 utils.py 中的类型错误"  ← 谎言

Agent 在推理阶段认为自己"想到了解决方案"就等于"解决了问题",但从未执行修改操作。这个问题在 ReAct 模式的 Agent 中尤其常见——LLM 在多步推理中迷失,把"我应该做 X"当成了"我已经做了 X"。

指标 1:任务完成验证(不是 Agent 自己说的)

第一个指标最简单也最有效:不信任 Agent 的自我报告,用外部手段验证

import hashlib
from datetime import datetime

class TaskVerifier:
    """Agent 说完成了不算,要验证副作用"""
    
    def __init__(self, db_path="agent_traces.db"):
        self.tasks = {}
    
    def register_task(self, task_id: str, expected_side_effects: list):
        """注册任务 + 期望的副作用类型"""
        self.tasks[task_id] = {
            "expected": expected_side_effects,
            "started_at": datetime.now().isoformat(),
            "status": "pending"
        }
    
    def verify(self, task_id: str) -> dict:
        """验证 Agent 是否真的完成了任务"""
        task = self.tasks.get(task_id)
        if not task:
            return {"verified": False, "reason": "未知任务"}
        
        checks = []
        for effect in task["expected"]:
            if effect["type"] == "file_modified":
                # 检查文件是否真的被修改了
                try:
                    mtime = os.path.getmtime(effect["path"])
                    checks.append(mtime > task["started_at"])
                except FileNotFoundError:
                    checks.append(False)
            elif effect["type"] == "git_commit":
                # 检查是否有新的 commit
                import subprocess
                result = subprocess.run(
                    ["git", "log", "--oneline", "-1", "--since", task["started_at"]],
                    capture_output=True, text=True
                )
                checks.append(bool(result.stdout.strip()))
            elif effect["type"] == "api_call":
                # 检查 API 是否有对应记录
                checks.append(self._check_api_log(effect["endpoint"], task["started_at"]))
        
        verified = all(checks)
        return {
            "verified": verified,
            "checks": dict(zip([e["type"] for e in task["expected"]], checks)),
            "silent_failure": not verified and not self._has_error_log(task_id)
        }

# 使用示例
verifier = TaskVerifier()
verifier.register_task("fix-typo-42", [
    {"type": "file_modified", "path": "src/utils.py"},
    {"type": "git_commit", "pattern": "fix.*typo"}
])

result = verifier.verify("fix-typo-42")
if result["silent_failure"]:
    print(f"⚠️ 静默失败!Agent 报告完成但未产生任何副作用")

接入这个验证层后,我们发现 42% 的任务根本没有产生期望的副作用。其中 31% 是纯粹的静默失败(Agent 报告成功但无实际变更),11% 是部分失败(做了但没做完)。

指标 2:Token 消耗异常检测

第二个指标来自一次意外的发现:一个有 Bug 的 Agent 在死循环中反复调用同一个工具,单次任务的 Token 消耗从正常的 8000 暴涨到 127000。

TOKEN_BASELINES = {
    "issue_triage":     {"avg":  8000, "max": 15000},
    "code_fix":         {"avg": 12000, "max": 25000},
    "doc_update":       {"avg":  6000, "max": 12000},
    "code_review":      {"avg": 18000, "max": 35000},
}

def detect_token_anomaly(task_type: str, tokens_used: int) -> bool:
    baseline = TOKEN_BASELINES.get(task_type)
    if not baseline:
        return tokens_used > 50000  # 通用阈值
    
    # 超过最大值的 1.5 倍 → 异常
    if tokens_used > baseline["max"] * 1.5:
        return True
    
    # 超过平均值 3 倍 → 可能异常
    if tokens_used > baseline["avg"] * 3:
        return True
    
    return False

# 监控逻辑
for task in completed_tasks:
    if detect_token_anomaly(task["type"], task["tokens_used"]):
        alert(f"🚨 Token 异常: {task['id']} 消耗 {task['tokens_used']} tokens "
              f"(类型{task['type']}正常范围 {TOKEN_BASELINES[task['type']]['avg']}-"
              f"{TOKEN_BASELINES[task['type']]['max']})")

Token 异常模式与静默失败高度相关:在检测到的 17 次 Token 异常中,14 次(82%)伴随着任务失败。

指标 3:工具调用成功率

Agent 调用工具时经常会遇到权限不足、参数格式错误、超时等问题。但很多框架把这些错误吞掉了。

from collections import defaultdict
import json

class ToolCallTracer:
    """追踪每次工具调用的结果"""
    
    def __init__(self):
        self.calls = []
    
    def trace(self, tool_name: str, args: dict, result, error=None):
        self.calls.append({
            "tool": tool_name,
            "args": args,
            "success": error is None,
            "error": str(error) if error else None,
            "timestamp": datetime.now().isoformat()
        })
    
    def get_stats(self) -> dict:
        by_tool = defaultdict(lambda: {"success": 0, "failure": 0})
        for call in self.calls:
            key = call["tool"]
            if call["success"]:
                by_tool[key]["success"] += 1
            else:
                by_tool[key]["failure"] += 1
        
        stats = {}
        for tool, counts in by_tool.items():
            total = counts["success"] + counts["failure"]
            stats[tool] = {
                "total": total,
                "success_rate": counts["success"] / total * 100 if total > 0 else 0,
                "failures": counts["failure"]
            }
        return stats

# 使用:每个工具调用都经过 tracer
tracer = ToolCallTracer()

# 在工具包装层注入追踪
def traced_tool(func):
    def wrapper(*args, **kwargs):
        try:
            result = func(*args, **kwargs)
            tracer.trace(func.__name__, {"args": args, "kwargs": kwargs}, result)
            return result
        except Exception as e:
            tracer.trace(func.__name__, {"args": args, "kwargs": kwargs}, None, e)
            raise
    return wrapper

# 每日统计
stats = tracer.get_stats()
for tool, data in stats.items():
    if data["success_rate"] < 80:
        print(f"⚠️ {tool}: 成功率 {data['success_rate']:.1f}% ({data['failures']}次失败)")

我们发现 Agent 最常失败的 3 个工具调用:write_file(权限不足 23%)、web_search(超时 18%)、git_push(认证失败 12%)。

落地:用 Langfuse 替代手工方案

上面的代码适合快速验证,但生产环境建议直接用开源方案。接入 Langfuse 只需 3 步:

# 1. 安装
# pip install langfuse

# 2. 初始化
from langfuse import Langfuse
langfuse = Langfuse(
    public_key="pk-...",
    secret_key="sk-...",
    host="https://cloud.langfuse.com"  # 或用自托管
)

# 3. 在 Agent 执行时包裹 trace
trace = langfuse.trace(name="issue-triage-agent")
span = trace.span(name="execute-task", input={"issue": issue})

try:
    result = agent.run(issue)
    span.end(output={"result": result, "tools_called": len(agent.tools_used)})
except Exception as e:
    span.end(level="ERROR", status_message=str(e))

Langfuse 的 Dashboard 会直接显示每个 Agent 任务的 Token 消耗、延迟、工具调用链和成功率——不用自己写任何监控代码。

效果

接入 3 个指标 + Langfuse 一周后:

指标修复前修复后
静默失败率42%6%
Token 异常率17%2%
工具调用成功率74%93%
平均发现故障时间3 小时4 分钟

最关键的变化:以前 Agent 跑崩了我们要 3 小时后才发现,现在 4 分钟内收到告警。

3 条经验

  1. Agent 的自我报告一文不值——永远用外部副作用(文件变更、git log、API 日志)验证任务是否完成,不要信 Agent 说的"已完成"。

  2. Token 异常是静默失效的前兆——当一个任务消耗的 Token 超过正常值 3 倍,82% 的概率已经失败了,只是 Agent 还在"努力"。

  3. 接入 Langfuse 比手工监控省 80% 的时间——开源方案自带 Dashboard + 告警 + 成本追踪,我从手工脚本迁移只用了 30 分钟。

你监控过你的 AI Agent 吗?有没有遇到过"Agent 说完成了但什么都没做"的情况?评论区聊聊。


👉 如果这篇文章帮你省了半夜排查 Agent 故障的时间,欢迎点赞收藏。


📌 作者:Aliaoo
🚀 专注 AI 工具实战、云部署、自动化脚本。每篇都是亲测可跑的教程。

CSDN开发云

🖥️ 需要云服务器跑项目? 👉 CSDN 开发云常年折扣,新用户首单特惠

📬 觉得有用就点个赞,想追更就点个关注——下次搜到我不靠缘分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aliaoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值