Agent死循环6小时:从“相信LLM判断”到“焊死硬件条件止

7 阅读4分钟

一次多 Agent 调度引擎的“失忆”事故,让我彻底放弃了把“停止”决策权交给 LLM。

背景

我正在做一个多 Agent 调度引擎(MAS-Engine),目标很野——让 Agent 能够自主潜入 Python / Node.js / Rust 项目,从需求提取一路干到构建交付,全程无人工干预。所谓“独立模式”,就是扔给它一个项目路径,它自己推理、执行、验证,像一位不知疲倦的实习生,只不过这位实习生背后是 LLM。

问题现象

输入一个 Rust 项目后,Agent 开始反复执行 cargo build,控制台一轮一轮滚动,直到第 15 轮被熔断终止,输出一行冷冰冰的:“达到最大迭代次数 (15) 终止,构建仍未成功。”

但诡异的是——构建本身在第 8 轮已经成功了。编译产物安静地躺在 target/debug/ 下,只是 Agent 浑然不知。它像一只困在迷宫的仓鼠,继续“验证式重复”,对眼前的出口视而不见。

我犯过的错误

我花了大量时间做软约束层面的尝试:

  • 在 Prompt 里加“请确认是否完成”→ 无效。
  • 把温度从 0.7 降到 0.1 → 无效。
  • 强制约束 JSON 输出格式,要求包含 "status": "done" → 无效,它确实输出了 JSON,但 status 永远是 "in_progress"

前前后后改了十几个版本,全部失败

核心教训来得痛且迟:所有这些全是“软约束”,Agent 仍然拥有最终判断权。  当它认为“多验证一次更安全”时,它可以——也一定会——无视指令。Prompt 不是代码,LLM 不是 CPU。

根因分析

Agent“失忆”了。

每次执行 cargo build 后,标准输出、错误信息、返回码确实被捕获了,但没有被写入下一轮 Agent 的决策上下文

Agent 在下一轮看到的信息与上一轮完全一致:同样的代码、同样的项目路径、同样的参数。它没有“刚刚执行过”的感知,只能根据当前上下文推断出“应该构建”,于是再次发起同样的命令。它不知道自己是第几次构建,不知道上一次返回了什么,更不知道产物已经生成了。

这不是“推理错误”,是“感知缺失”。  Agent 不是笨,是瞎。它像一个失忆的工人,每次醒来都以为自己是第一次上班,于是重复干同一件事。

这个根因让我后背发凉——我一直在调教“智力”,却忘了给“眼睛”。

修复方案

1. 执行结果强制感知

每一步执行后,不再单纯将结果存入上下文变量(那只是一段静默的内存),而是强制提取返回码、stdout / stderr 摘要,并渲染成下一轮 System Prompt 中的“上一轮执行报告”。Agent 必须在行动前“看见”自己刚做了什么。

python

# 伪代码示意
def build_execution_report(result):
    return f"""
[上一轮执行报告]
命令: {result.command}
返回码: {result.returncode}
标准输出摘要: {result.stdout[:200]}
是否包含构建成功信号: {"Finished" in result.stdout}
"""
# 将报告注入下一轮的 system prompt

Agent 现在每轮开局都会读到一份“历史档案”,无法假装没发生过。

2. 硬条件止(工程护栏)

这是最狠的一刀。在循环体首位增加状态机硬拦截。

python

def should_force_complete(last_result):
    if last_result is None:
        return False
    # 直接判断系统底层返回的成功标志,而不是让 LLM 去解读
    return last_result.success is True

# 循环入口
while round < MAX_ROUNDS:
    if should_force_complete(last_exec_result):
        transition_to(DELIVERY_COMPLETED)
        break
    # 否则才让 Agent 继续决策
    action = agent.decide()
    last_exec_result = executor.run(action)

彻底剥夺 Agent 对“要不要停”的决策权。只要系统层面检测到“上一轮执行成功”,强制跳转至完成状态并 break,Agent 连说“不”的机会都没有。

3. 15 轮熔断兜底

原有的 15 轮熔断保留,作为最后一道系统级保险。即使未来出现新的“失忆”变种,任何极端情况下都不会无限卡死。

验证结果

构建完成。控制台在构建成功后的下一轮立即退出,不再有任何“验证式重复”。整个循环在硬条件拦截下干净利落地终止,而非等到 15 轮熔断。日志里终于出现了久违的 DELIVERY_COMPLETED,而不是 MAX_ITERATIONS_REACHED

原则

Agent 负责“怎么做”,但“什么时候停”必须是工程护栏说了算。

我们花了 6 小时才明白:把判断权交给 LLM,就像把刹车交给乘客。顺风时相安无事,一旦需要紧急制动,乘客可能还在犹豫“要不要踩”,而车已经冲下悬崖。

这不是不信任 AI,而是承认当前的 LLM 在“元认知”层面仍不可靠。它不知道自己知道什么,也不知道自己不知道什么。工程系统的稳定性必须建立在确定性逻辑之上,而非概率判断之上。下一次,我会从第一行代码就把“感知”和“控制”焊死在循环的骨架上,而不是事后打补丁。


如果你也在做 Agent 编排,不妨问问自己:你的 Agent,真的“看见”自己的每一步了吗?