ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

破解Agent重复漂移:认知导航如何成为稳定性的关键底座

破解Agent重复漂移:认知导航如何成为稳定性的关键底座

如果要在 Agent 开发里排一个最让人崩溃的现象,我大概率会把"重复漂移"排在第一位。所谓重复漂移,就是 Agent 在长任务执行过程中逐渐偏离原始目标,同时开始反复执行同一个动作——像一台卡了带的老式录音机,明明已经输出了十次一模一样的中间结果,它依然一本正经地继续调用工具、拼接上下文、生成下一轮"看起来合理"的输出。

我这么说的底气,来自上个月一次刻骨铭心的终局交付事故。我们小组负责开发一套面向数据自动化处理的 Agent 系统,原计划在周五完成最终验收,Agent 需要在无人值守的情况下完成数据清洗、指标计算、报告生成和异常复核四个环节。前两周联调测试都很顺利,结果就在交付前夜,Agent 在其中一个关键分支上陷入循环,连续产生了上百次重复的工具调用,直到触发资源上限进程才被强制中断。更头疼的是,模块没有任何自我修复机制。我们连夜排查、反复重启、调整参数,最后还是不得不人工接管,交付整整延期了两天。

事后复盘我们发现,问题并不出在某句提示词或某个工具调用的写法上,而是整个系统里缺少了一个关键底座——认知导航。这个概念当时还在我们心里只剩模糊的影子,等真正把它想清楚并落地之后,我们才意识到它对于应对 Agent 漂移和自愈缺失的价值有多大。

1. 事故现场:一台"复读机"式 Agent 是怎么把交付拖垮的

1.1 从日志里看到的诡异循环

先把事故现场还原一下。当晚我们正在执行一组面向客户日报的批处理任务,流程设计得像一条流水线:读取数据源 -> 清洗字段 -> 计算核心指标 -> 生成报告章节 -> 交叉复核。为了压缩时长,任务被拆成若干并发子任务,每个子任务由一个 Agent 角色独立推进。

问题出现在第 37 步左右。从日志上看,Agent 突然进入了一段"搜索-总结-再搜索-再总结"的循环。我把当时压平后的关键日志抽出来,大概长这样:

step=37 action=web_search query="最新季度营收数据" step=38 action=summarize result="检索到相关页面3个,摘要..." step=39 action=web_search query="最新季度营收数据" step=40 action=summarize result="检索到相关页面3个,摘要内容未发生显著变化..." step=41 action=web_search query="最新季度营收数据" step=42 action=summarize result="同上,未发现新增信息"

单独看每一条日志,好像都说得通:它确实在搜索,确实在总结,下一轮也确实带着"更严谨"的意图继续搜索。但把 80 条日志放到一起看,就非常诡异了——搜索关键词几乎一样,总结文本几乎一样,连调用的工具参数都几乎一样。它就钉在同一个节点上,一遍一遍重复同一个动作,完全没有"这件事我已经做完、可以做下一步"的意识。

1.2 事故造成的实际影响

这次事故的代价,远比"延期两天"这四个字要大。

  • 交付延期两天,客户验收窗口整体后移,连带后续排期全部被打乱。
  • 部分中间数据文件在循环中被反复写入,产生了脏数据,回滚时又牵连了其他干净的分区。
  • 人工复核工作量翻了数倍,团队三个成员轮流值守才把报告手工补齐。
  • 最隐性的损失是信心:我们对"Agent 能不能在关键时刻稳定跑完"这件事产生了严重怀疑,后续好几周都不敢把复杂的无人值守任务交给它。

1.3 当晚的应急处理

当晚我们做了所有"常规"操作:先重启任务,发现照样卡在同样的位置;再回滚数据到半小时前,重跑一次之后依然循环;最后无奈之下人工接管,写了脚本手动完成剩余章节,才保住了次日早上的一份临时报告。

这一连串操作看起来很朴素,但背后暴露了一个事实:我们对 Agent 执行过程的监控和干预能力极其薄弱。除了"能看到日志"之外,系统对"Agent 是否在真正推进"这件事没有任何判断。那种感觉就像把车开了出去,却没有任何仪表盘——你只知道发动机在响,不知道车到底在不在往前走。

2. 为什么 Agent 会漂移:从生成的本质讲起

2.1 LLM 的每一步都是"局部最优的赌博"

要理解漂移,先得理解 Agent 的行动逻辑。Agent 的每一步动作,本质上都是 LLM 基于当前上下文做的一次概率采样。它不像传统程序那样,有一个确定性的状态转移函数——输入是什么、输出就是什么。它是从概率分布里"挑选"一个最合理的下一步。

这个机制带来一个很微妙的特性:每一步的合理性,都是局部的。前一步如果偏了,后一步就会在偏了的基础上继续推演,误差像滚雪球一样越滚越大。对 Agent 本身来说,它在第 37 步选择"继续搜索"的时候,并不真的知道第 20 步已经完成过同样的搜索,它只知道"当前上下文里,搜索看起来是一个合理动作"。

这也是为什么很多人在初步测试时觉得 Agent"挺聪明",一到长任务、复杂任务就原形毕露——短任务里偏差还没有机会累积,长任务里每步微小偏差叠加起来,足以让 Agent 彻底偏离航道。

2.2 漂移的三条成因链条

多年的项目交付经验让我意识到,Agent 漂移不是单一原因,而是几条因果链共同作用的结果。

其一,目标遗忘链。LLM 的注意力窗口有限,长任务执行到后期,最初的目标描述会被大量的中间指令、工具返回结果、历史输出淹没。模型实际上变成了"只看得到最近几轮对话"的近视眼,它确实想完成任务,但它对"任务到底是什么"的记忆已经模糊了。我们的日志里,Agent 在第 30 步之后,生成的中间结果里已经不再引用初始需求字段,这就是目标被遗忘的典型信号。

其二,上下文污染链。Agent 的每一步输出都会拼接到下一次推理的上下文里。如果前序步骤出现了错误字段、冗余信息或者误导性的中间结论,这些"污染源"会在后续每一轮被反复咀嚼,把决策越带越偏。更麻烦的是,LLM 分不清上下文里哪句是可信的、哪句是错的,它只会一视同仁地当作依据。

其三,循环自洽链。这是最阴险的一条。当 Agent 陷入循环时,它并不是无头苍蝇,而是每一轮都能给自己找到"继续做这件事"的理由——"还没找到需要的信息""结果可能不完整""再搜一次更稳妥"。在这种自洽逻辑下,它单看每一步都正当无比,但整体已经彻底失去推进能力。我们事后分析那 80 条日志,发现其中有 70 条的模型推理字段都在强调"需要进一步确认数据完整性",这就属于典型的循环自洽。

2.3 "单步正常"与"全局失控"的悖论

Agent 工程里最反直觉的地方,就是单个步骤完全正常,整体却在失控。传统程序的行为可预测性来自状态机:执行到哪一行、变量是什么、结果如何,每一步都有确定性的结果。Agent 完全不是这套逻辑,它每一步都是"生成式"的,天然没有全局一致性保障。

这个悖论也是"自愈"问题变得棘手的原因:如果出错的是工具调用参数,重试就行了;但如果出错的是"整个轨迹已经失去靶心",那重试一百次只是在同一个错误的道路上来回碾压。认知导航要解决的,恰恰是这个"全局靶心"问题。

3. "自愈"是个被严重低估的系统工程

3.1 自愈能力模型:并不只是"再试一次"

很多人一提自愈,第一反应是"出错了就重试"。这其实是把复杂问题过度简化了。真正的自愈,至少包含四件事:

  • 感知:系统能够感知当前 Agent 状态,知道"我走到哪一步了"。
  • 判断:系统能够判断这个状态是否偏离了预期轨道。
  • 决策:系统能够决定用哪种方式回到轨道。
  • 执行:系统能够生成纠偏指令并注入执行流程,让 Agent 真正回到正轨。

四件事缺一不可,而且每一件事在 Agent 场景下都不容易做到。感知难,因为 Agent 的输出是自然语言,没有一个结构化的"坐标"可以被直接读取;判断难,因为"偏没偏"很多时候是语义层面的,不是数值层面的;决策难,因为纠偏策略本身也是概率性的;执行难,因为它要把外部指令无缝嵌回 Agent 的上下文里,还不能打断整体流程。

3.2 为什么靠提示词调教实现不了自愈

在我接手这个项目之前,团队已经花了不少精力在提示词上做文章。比如在系统提示里写"请务必检查是否偏离目标""如果发现重复,请重新规划"。

这类提示词有没有用?有一点,但远远不够。原因在于 LLM 的自我评估并不可靠,它倾向于对自己生成的输出给出较高置信度。让它"自查"是否偏离目标,它大概率会回答"没有偏离,一切正常"——这不是它有意撒谎,而是它在生成的上下文里,缺少一把客观的尺子。

更深一层的问题是,循环中的每一步在 Agent 眼里都是"有正当理由的",所以用提示词检查"是否重复"时,模型会自动把重复解释为"谨慎"或"深挖",而不是"故障"。这就像让一个迷路的司机自己判断有没有迷路,他多半会说"没有,我只是在找更近的路"。

3.3 从自动驾驶说起:导航是比"更聪明的司机"更靠谱的工程解

那次事故之后,我反复在想一个问题:同样是复杂环境下的自主决策,为什么自动驾驶系统可以在高速上稳定跑几百公里?得到的答案很简单——因为自动驾驶不依赖"司机更聪明",而是依赖一套完整的外部基础设施:地图、GPS、传感器、车道线、实时路况。

这套基础设施里最核心的,是导航系统。导航系统并不负责"替司机踩油门打方向盘",它只做一件事:持续回答三个问题——车现在在哪里?要去哪里?偏航了没有?如果偏航,就重新规划路线。

我后来逐渐意识到,Agent 系统缺的也是这样一套东西。我们太关注"让 Agent 更聪明"了,却忽略了给它配一个中立、客观、独立于生成过程之外的导航层。我们缺的,就是认知导航。

4. 认知导航:Agent 稳定性的关键底座

4.1 把 GPS 的思路搬进 Agent 系统

所谓认知导航,我把它定义为:一组横跨 Agent 执行过程的元层控制机制,目的是不让 Agent 每一步都听指挥,而是保证它始终知道自己在哪里、要去哪里、有没有偏航,并在偏航时能够自动回到正轨。

它和传统控制流的本质区别在于:Agent 的执行仍然是自由的、生成式的,导航层不干预它"怎么做",只负责持续校验"做到哪了、偏没偏"。这就像 GPS 不会帮你踩油门,但它会在你走错路口的时候喊一声:"您已偏航,正在重新规划路线。"

4.2 认知导航的四个核心组件

以我的工程实践来看,一套完整的认知导航体系至少要包含四个部分。

认知地图(Cognitive Map):这是对任务空间的建模。一个任务不是含糊的"做一份报告",而是被拆成若干可验证的里程碑节点,比如"数据已清洗""指标已计算""报告初稿已完成""异常项已复核"。每个里程碑都有可执行的验证函数,能够判断是否真的达成了。认知地图解决的是"要去哪里、怎么算走到"的问题。

状态定位(State Localization):这是从 Agent 每步输出中提取"当前位置"的能力。Agent 每完成一步,导航层需要把这个动作对应的状态更新到地图上,比如"当前停留在'数据已清洗'这个节点"。定位不是简单打标签,而是需要从自然语言输出里抽取语义状态,这一步通常要借助结构化输出协议或者小模型分类器。

偏差检测(Deviation Detection):这是把"当前位置"和"预期位置"进行对比,识别三种典型的异常:重复、停滞、漂移。重复是状态的自我复制;停滞是状态长时间没有推进;漂移是状态偏离了目标相关方向。

纠偏引擎(Correction Engine):这是根据偏差类型和严重程度,生成纠偏指令的模块。它不直接修改 Agent 的内部逻辑,而是以外部指令的形式介入上下文,让 Agent 回到最近一个有效里程碑,或者重新规划子任务路径。

4.3 认知导航 vs 其他方案

有朋友问我:提示词约束、规则引擎、人工审核,是不是也能实现类似效果?我把对比整理了一下,方便大家直观理解:

方案核心思路对长任务漂移的防护自动化水平主要局限
提示词约束在系统提示里反复强调目标弱高无法客观判断"是否偏离",模型自我感觉良好
规则引擎用 if-else 拦截重复调用中高规则写多了维护成本爆炸,语义层面的漂移拦不住
人工审核每跑几步就让人看结果中低不可扩展,长任务全程盯人,成本不可接受
认知导航地图+定位+检测+纠偏强高前期需要建模和协议设计,上手门槛略高

我并不是说提示词、规则引擎这些手段没有价值,它们可以作为认知导航的补充,但单独依赖它们,很难在无人值守的长任务里保证稳定。

4.4 认知导航带来的实际影响

把认知导航接入系统之后,影响是立竿见影的。最明显的变化是,我们第一次可以在 Agent 运行的时候说清楚"它现在卡在哪了"——以前只能说"感觉不对",现在系统会直接给出偏差类型和置信度。其次是事故恢复时间大幅缩短,从"人肉排查数小时"降到"系统自动触发纠偏,几十秒内恢复正常"。这两个效果直接决定了我们敢不敢把更复杂的任务交给 Agent 全自动执行。

5. 给 Agent 装上认知导航:从设计到落地

5.1 分层架构设计

实践下来,我推荐把系统分成四层,职责清晰、边界明确:

  • 任务解析层:把用户目标解析成认知地图,生成里程碑序列。
  • 执行层:Agent 本体,负责实际调用工具、生成内容、推进步骤。
  • 导航层:包括定位、检测、纠偏三大模块,独立于执行层运行。
  • 监控层:负责指标采集、告警、可视化,面向人类运维者。

关键原则是:导航层不能和执行层共用同一个模型的同一次推理,也就是说它不能在 Agent 的"脑子"里做判断,而要在外部用独立逻辑做校验。只有这样,导航才是客观的。好比你不会让考生自己给自己改卷子。

5.2 认知地图与目标状态建模

认知地图是整个方案的起点。构建步骤如下:

  1. 把目标任务拆成不超过七个里程碑,每个里程碑对应一个可验证的状态。
  2. 给每个里程碑写一个验证函数,输入是 Agent 的当前输出,输出是布尔值。
  3. 定义里程碑之间的依赖关系,构成一张有向图。

数据结构上,我用的是类似这样的定义:

@dataclass class Milestone: id: str description: str dependencies: list[str] validator: Callable[[AgentOutput], bool] @dataclass class CognitiveMap: milestones: dict[str, Milestone] target_state: str max_steps: int def current_position(self, trajectory: list[AgentOutput]) -> str: # 遍历已完成的里程碑,返回最近一个完成的节点 id for m in self.milestones.values(): if m.validator(trajectory[-1]): return m.id return "start"

这里有个经验要分享:里程碑的验证函数不要太复杂,越简单越好。能用"输出里是否包含某个关键字段"判断的,就不要强行上语义模型。因为验证函数一旦复杂,本身就会引入误判,让导航系统变成新的故障来源。

5.3 偏差检测的三板斧

偏差检测是认知导航真正的技术核心。根据我的落地经验,做以下三类检测基本覆盖了绝大多数漂移场景。

重复检测(Repetition Detection):计算当前输出与最近 N 步输出的语义相似度。如果连续几轮相似度都超过阈值,就判定为重复。在实现上,我会用文本向量化模型算余弦相似度,比直接比对字符串要鲁棒得多。

def detect_repetition(trajectory: list[AgentOutput], window: int = 5, threshold: float = 0.92) -> bool: """如果最近连续 window 条输出彼此之间的语义相似度都超过阈值,判定为重复漂移""" recent = trajectory[-window:] if len(recent) < window: return False vectors = [embed(text) for text in recent] for i in range(len(vectors) - 1): sim = cosine_similarity(vectors[i], vectors[i + 1]) if sim < threshold: return False return True

停滞检测(Stagnation Detection):维护一个"里程碑最后推进时间"的变量,如果当前时间减去最后推进时间超过上限,说明 Agent 一直在原地打转但未必完全重复——比如它可能在换着花样做同一件事。阈值设置没有绝对标准,我们的经验是取"预期单步耗时的 5 到 8 倍"作为停滞判定线。

漂移检测(Drift Detection):计算当前输出与目标状态描述的语义相关性。低于阈值就认为 Agent 正在远离目标。这比重复检测更难调,因为相关性本身是模糊的。我的建议是不要用一个大而全的语义模型,而是分别计算"与当前里程碑的相关性"和"与最终目标的相关性",两条曲线分开监控,哪个低了就报警哪个。

def detect_drift(current_output: str, target_state: str, threshold: float = 0.55) -> bool: relevance = cosine_similarity( embed(current_output), embed(target_state) ) return relevance < threshold

5.4 分级纠偏策略

检测到偏差只是第一步,怎么纠才是关键。我把纠偏策略分成四级,每一级干预力度不同。经验是:能用轻度移动解决的,不要直接重来,因为频繁重来会浪费大量算力,还有可能引入新的问题。

一级(轻度偏离):导航层直接在下一轮上下文里注入一条提示,比如"注意:检测到连续多步重复,请基于已有信息直接推进,不要再执行相同动作"。这种提示不改变任务结构,仅仅改变 Agent 的注意力分布,通常能解决简单的循环自洽。

二级(中度偏离):回退到最近一个已验证的里程碑,把中间未经验证的输出全部丢弃,从该里程碑重新执行。这里需要认知地图提供"最近有效节点"的能力,本质上是在局部做一个受限回溯。

三级(严重偏离):放弃当前子任务路径,重新规划从当前状态到目标的路径。注意不是回到起点,而是从"当前位置"重新计算后续路径,这样能减少不必要的重复开销。

四级(危急场景):导航层判定 Agent 已经无法自行恢复,自动冻结任务,标记"需要人工介入",并向监控系统推送完整的偏差报告。千万不要让系统无限循环纠偏,见过太多二次故障,都是在"自动修复"阶段反复横跳造成的。

def dispatch_correction(deviation: Deviation, state: AgentState) -> Correction: if deviation.level == DeviationLevel.MILD: return InjectHint(deviation.reason) if deviation.level == DeviationLevel.MODERATE: return RollbackTo(milestone=state.last_valid_milestone) if deviation.level == DeviationLevel.SEVERE: return Replan(target=state.target_state) return EscalateToHuman()

5.5 配套监控指标

认知导航不能只做"事中干预",还需要给运维者提供"事中可观测、事后可复盘"的指标。我最终在监控面板上保留了五个核心指标:

  • 重复率:最近 N 步输出中,语义相似度超过阈值的比例。
  • 停滞时长:从最后里程碑推进到当前的时间间隔。
  • 里程碑达成率:已完成的里程碑数占总数的比例。
  • 纠偏触发次数:每小时各类偏差被检测到的次数,按类型区分。
  • 自愈成功率:纠偏之后,Agent 在预期步数内恢复推进的比例。

这里的"自愈成功率"是两个行为边界的重要参考。如果成功率高于 0.9,说明纠偏策略有效;如果持续低于 0.7,就要认真考虑是不是认知地图建错了,而不是继续调检测阈值。

6. 常见问题与排查实录

6.1 典型症状与根因对照

和几个同行交流之后,我发现大家遇到的问题高度相似。整理成一张排查表,可以帮你在踩坑时少走弯路:

症状可能的根因优先排查方向
Agent 反复执行同一动作循环自洽、缺少外部终止条件检查重复检测是否生效,阈值是否过高
任务跑到后面越来越偏目标遗忘、上下文污染检查里程碑验证函数是否被正确触发
纠偏后依然原地打转纠偏指令太弱、或者模型未真正理解升级到回退策略,不要只靠提示
自愈后引发二次错误回退点选择不当、数据状态不一致强化里程碑验证函数,确保回退点可信
导航层本身误报频繁语义相似度阈值过紧放宽阈值,引入人工标注样本校准

6.2 排查步骤

如果正在跑的任务突然表现异常,我建议按这样的顺序排查:

  1. 先看监控指标,确认是重复、停滞还是漂移。
  2. 再到日志里定位循环的起始步,通常从首次语义相似度超过阈值那一步开始。
  3. 检查那一步前后的上下文,判断是工具返回异常,还是目标被遗忘。
  4. 手动复跑最近一个有效里程碑到异常点之间的路径,确认可复现性。
  5. 最后决定是调检测阈值,还是升级纠偏等级。

这五步走下来,大部分问题都能定位到具体环节。很多时候,你会发现问题不在 Agent 本身,而在上游——认知地图建得不够细,或者目标任务本身描述得就不清晰。

6.3 几个我后来才懂的小技巧

第一个技巧:给里程碑设计明确的"结束信号"可以大幅减少重复漂移。比如让 Agent 在每个里程碑结束时,必须输出一行包含固定标记的 JSON。导航层看到这个标记,才知道这个节点走完了。这一步能沉淀结构化状态,检测自然就稳定了。

第二个技巧:检测重复不要只看文本相似度,还可以看工具调用参数。文本可能因为改写而变化,但工具参数往往高度结构化。在我们的案例里,重复循环中的搜索参数几乎完全一致,用参数一致性来判断,比语义相似度更早、更准。

第三个技巧:纠偏提示要放在上下文靠前的位置,并且用独立的系统消息注入,而不是拼在对话历史末尾。实测下来,放在末尾的提示容易被最近几轮的长输出淹没,放在开头或作为系统插播消息,效果会明显好很多。

7. 最后说点体会

那次终局交付事故之后,我对 Agent 工程化的理解彻底变了。以前我总觉得,让 Agent 更聪明、理解更复杂的指令,才是解决问题的关键;现在我认为,在长任务里保证稳定推进,比在单轮对话里表现得聪明重要得多。聪明是上限,稳定是底线,底线守不住,上限再高也没有交付价值。

认知导航这个概念,听起来有点像"给 Agent 装 GPS",但它真正解决的,其实是工程上一个非常朴素的问题:我们有了一套独立于 Agent 之外的手段,来回答"它现在到底在干什么、离目标还有多远、要不要帮它拉回来"。有了这套手段,我才敢把真正关键的任务交给 Agent 全自动去跑。

如果让我给正在做类似系统的人一句建议,我会说:不要等事故发生了才想起做导航层。你可以在设计阶段,先定义清楚目标状态和里程碑,哪怕只用最朴素的规则检测,也能在关键时刻救你一命。别让"让 Agent 更聪明"的愿望,盖过了"让 Agent 更可靠"的基本功课。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进