agent 在长任务中回复陷入循环、大量重复,且无轮次预算/重复检测兜底 #5072
Replies: 8 comments
|
这个复现把“模型重复输出”和“Agent turn 没有硬边界”区分得很清楚。建议修复按四层落地,并分别记账:
验收建议覆盖:有限状态轮询、相似但不同参数的工具调用、状态最终变化、永久 pending、外部进程崩溃、取消期间结果到达、断线重连和跨冷启动恢复。报告 turn/step 数、重复指纹命中、等待时间、provider usage、终止原因和清理结果;不要把“模型说我陷入循环”当作检测证据。 如果采用扩展点实现,必须证明取消在达到预算时真的阻止下一次 tool call,并且不会留下半写 Session 或悬空 approval。独立社区手册的 runaway-loop 与有界失败接续参考:https://github.com/sandbaseai/deepseek-harness-handbook/blob/main/docs/en/troubleshooting/runaway-agent-loop.md 和 https://github.com/sandbaseai/deepseek-harness-handbook/blob/main/docs/en/agent-patterns/ralph-bounded-failure-successor.md 。手册不是 DeepSeek AI 官方项目。 |
补充分析:这是「模型退化」还是「框架兜底缺失」?进一步排查后,结论是两者叠加,但根子在框架。用会话日志的硬数据说明: 一、模型确实在退化(第三方 LLM 行为)以
二、为什么
|
| 缺失 | 说明 |
|---|---|
| 无轮次/步骤预算 | 单轮可无限跑(实测 72 步),无硬性上限 |
| 重复检测只看「完全相同」参数 | 参数微变即漏检,轮询循环完全绕过 |
| 采样无 repetition penalty | GenerateOptions 只有 temperature / maxTokens / stop,无惩罚项抑制退化 |
四、客观判断
- 模型层面:
deepseek-v4-flash-0731-oc在长任务(尤其等待外部进程)中确实会退化到重复轮询,这是诱因。 - 框架层面(根子):DSH 明知模型可能循环,却只做了「完全相同调用」检测,且明确不做轮次预算(README 原文「没有内置轮次预算:限制失控轮次的策略必须从扩展点执行取消」)。这是设计取舍,但代价是这种循环无人兜底。
结论: 不能简单归咎于「第三方 LLM 的问题」。模型退化是诱因,但框架没有兜底机制才是让循环无限延续、让用户看到大量重复的根本原因。这也正是建议增加「轮次预算 + 语义相似重复检测 + 无进展检测」的原因——这些是框架该做的防御,不能指望模型永远不退化。
|
感谢 @liyangbing 的四层落地建议,非常专业且与实测吻合。补充几点我们排查中与四层方案直接对应的观察: 1. 硬预算 —— 实测确认无任何上限
2. 无进展信号 —— 指纹比文本相似度更可靠实测中 3. 外部 job 状态 —— 退避是刚需实测模型每一步都 4. 取消与恢复 —— 需要可恢复的终止原因您强调「达到上限时写入可恢复的终止原因,不能只依赖模型自我收尾」,这点很关键——实测模型自己反复说「我陷入循环了」但无法跳出,说明模型自我收尾不可靠,必须由框架写入终止原因。 关于验收您列的「不要把『模型说我陷入循环』当作检测证据」非常正确。实测中模型 turn 5~14 每轮都承认循环,但依然继续——模型的自我声明不是可靠信号,必须用指纹命中/预算耗尽等客观信号。 手册链接两个链接我都验证过(HTTP 200,内容真实):
感谢提供参考,这些手册对落地实现很有价值。 |
实现提案:为 Agent turn 增加硬边界与无进展检测(四层落地)基于 @liyangbing 的四层建议,结合对 第 1 层:硬预算(Hard Budget)目标为每个 turn 设置 现状(已核对)
扩展点
实现要点
验收用例
第 2 层:无进展信号(Progress Fingerprint)目标对轮询类工具按「资源/状态摘要」建立进展指纹,区分三种状态:
文本相似度只能作为提醒信号,不能单独决定删除历史。 现状(已核对)
扩展点
实现要点
验收用例
第 3 层:外部 Job 状态跟踪(Job State Awareness)目标让 Agent 等待时保留 现状(已核对)实测模型每步都 扩展点
实现要点
验收用例
第 4 层:取消与恢复(Cancellation & Recovery)目标预算耗尽、用户停止、provider 失败、进程退出分别写入 Session/工具生命周期;清理孤儿进程或临时目录后,提供「新 turn / 新 Session 继续」的明确入口。 现状(已核对)
扩展点
实现要点
验收用例
验收总纲覆盖场景:
报告指标:
关键原则:不要把「模型说我陷入循环」当作检测证据——必须用指纹命中、预算耗尽等客观信号。 |
|
源码核对(0.1.2-alpha.1,cd5ef81481;帖内引用的是 rc.2 编译产物 确认项:
补充 1:强制停止的机制已经存在,缺的是核算与独立原因 补充 2:max-tokens sticky 就是新终止原因的实现先例 对第 2 层的锐化:exact-args 检测键是刻意设计——gentle 提醒文案(index.ts:63-67)明写 "repeating the exact same tool call with identical arguments",检测器保证的是"完全相同调用"的精确捕获。修复方向不应是放宽键(破坏精确保证),而是为轮询类工具提供等价类提取器:指纹 = 资源 ID + 状态字段,显式丢弃易变参数( 家族映射:这是 |
|
这个 case 和我们最近在 DSH 上做的一组实验很接近。前面关于 hard budget、progress fingerprint、job backoff 的讨论我基本认同,我想补一个稍微不同的角度: 这里可能不只是「怎么检测模型 polling 太多」,还可以继续问:polling 这件事本身是否应该一直留在 Model loop。 比如: 对目前按完整 args 比较的 repeat detection 来说,这是三个不同调用。 但从 execution / progress 的角度,它们可能其实是:
我们最近做过一个受控的 no-progress Circuit 实验: 观察到: 后来在开放式任务里又自然遇到了: 所以我们后来不再把它理解成某一种特定 error 的 retry breaker,而更倾向于叫它 no-progress circuit。 不过你的 background-job case 让我觉得还能再往前走一步。 如果 job plugin / Harness 已经能够确定: 那 Model 也许根本不需要不断决定: 更自然的 execution semantics 可能是: 我们之前在 temporary-invalid lifecycle 场景里也测试过类似的 所以我现在会把这里拆成三个不同 primitive: Progress fingerprint 我也比较倾向于结构化,而不是完整 args 或文本相似度。对 job 类工具,可能更接近: 真正发生: 才算 progress,并 reset circuit。 这里还有一个边界我觉得很重要:Generic Runtime 不一定知道每种 Tool 什么叫 progress。可能更合理的是由 Tool / Plugin 提供自己的 progress semantics,而 Circuit 只消费统一的 progress signal。 我们目前把这组机制做成了一个可插拔的实验项目: https://github.com/goatliamia/dsh-runtime-capabilities 如果你有兴趣,我其实更希望你直接拿自己的 72-step polling case 去试,而不是我们再自己构造一个类似场景。 命中、没命中、或者产生 false positive 都很有价值。可以直接在仓库开一个 external-case Issue;如果你后来做了适配、留下了脱敏 trajectory / 实验结果,也很欢迎 evidence-only PR,不一定非要改 Runtime 代码。 我们自己不一定拥有每一种 provider / MCP / job environment,所以我反而希望这些 capability 能逐渐被真实遇到问题的人自己拿来验证。 对这个 case,我最想知道的是两件事:
任何一条在真实场景里不成立,对这个项目来说也同样是有价值的结果。 |
|
运行稍微复杂的任务思考时间巨长,思考链也是长篇大论,非常需要给个边界限制了 |
|
我遇到过,思考过程处理请求超时还一直等待,都无法终止。重启服务才行。 |
Uh oh!
There was an error while loading. Please reload this page.
一句话说明问题:DSH 的 agent 在长任务(尤其涉及后台 job 轮询、等待外部进程)中会陷入回复内容循环、大量重复,且没有任何机制兜底。
复现步骤
session-87f7ba51中 turn 4 单轮跑了 72 步,其中约 30+ 步是重复轮询打包状态;turn 5~14 每轮开头模型自己承认「我陷入循环了 / 我又重复了」,随后仍继续重复。实际结果
session-87f7ba51、session-389ea38a)出现该问题。预期结果
环境
@deepseek-ai/dsh0.1.1-rc.2(web profile,standard preset)deepseek-v4-flash-0731-oc(providertianyi-dp4f,reasoningEffort: max)根因分析(已排查)
repeat-tool-reminder插件(已启用,阈值[3,5,8]),但其检测键是「(tool name, 完全相同的规范化参数)」,只拦截完全相同的工具调用。实际循环是「文本回复重复 + 参数略有不同的工具调用」(轮询时检查不同目录/进程/文件),该插件完全检测不到,一次提醒都没触发。dsh-agent-loopREADME 明确「没有内置轮次预算:工具调用或 steering 会让当前轮次继续;限制失控轮次的策略必须从扩展点(如agent/turn-stopping)执行取消」。因此单轮可无限跑下去,没有硬性兜底。GenerateOptions采样只有temperature/maxTokens/stop,没有repetition_penalty/frequency_penalty/presence_penalty(README 记录为「已删除的惰性旋钮」)。模型在长任务中容易退化到重复输出,且无惩罚项抑制。建议修复方向
dsh-agent-loop增加轮次/步骤预算(单轮最大步骤数,超限强制结束轮次)。repeat-tool-reminder:检测语义相似的工具调用(如轮询类命令),或对「连续 N 步无进展」注入提醒。GenerateOptions恢复repetition_penalty支持。All reactions