一个简单任务为什么跑了 50 轮?
一个简单任务为什么跑了 50 轮?DeepSeek V4.1 Flash × DSH 源码分析
- 作者
- DeepSeekAgent.io 编辑部
- 发布
- 更新
一个原本很简单的任务,在 DeepSeek Harness 中运行了 33–50 轮,产生 32–70 次工具调用。模型很早就找到了答案,文件修改只占 1–3 轮,剩余动作集中在修改前确认和修改后验证。任务完成后,Agent 仍继续调用工具,导致上下文和 Token 消耗增加。
本文从一组 V4.1 Flash 社区运行记录出发,对照 DSH 固定版本源码,再用论文 When Agents Do Not Stop: Uncovering Infinite Agentic Loops in LLM Agents 的分析框架解释:Agent 为什么会持续做合理的小步骤,却始终无法判断“已经够了”。
故障现场:一个简单任务跑了 33–50 轮
社区报告使用同一条提示词运行四个全新 Session,任务是给现有模型配置增加 high 和 max 两档推理强度,并把 high 设为默认值。运行覆盖 PTC 与 Standard,其中三次没有加载规则文件或 Skill。
| 运行 | 模式 | 规则文件 | Assistant 轮数 | 工具调用 |
|---|---|---|---|---|
| 1 | PTC | 有 | 39,随后由用户中止 | 未记录 |
| 2 | PTC | 无 | 50 | 59 |
| 3 | PTC | 无 | 33 | 32 |
| 4 | Standard | 无 | 43 | 70 |
三次完整记录显示,模型在第 8–12 步已经理解字段含义。修改文件用了 1–3 步,修改前的确认占 14–28 步,修改后的验证与收尾占 4–18 步。
其中一轮记录了 2,946,619 个输入 Token:探查阶段约 160 万,写入阶段约 9 万,写入后的验证约 125 万。文件修改占输入 Token 的 3%,其余消耗来自探查、确认和验证。
这是不是普通的“工具卡住”
排查时先区分四种现象:
| 现象 | 典型特征 | 处理方向 |
|---|---|---|
| 单个工具卡住 | 同一次命令长时间不返回 | 工具超时、进程终止、沙箱检查 |
| 完全重复调用 | 工具名称与参数连续相同 | 重复调用检测、参数去重 |
| 长推理无动作 | 长时间没有工具事件或文本输出 | 推理强度、输出上限、模型状态 |
| 无界确认 | 工具都成功,参数不断变化,但一直补充检查 | Turn 步数、进展判断与停止条件 |
33–50 轮案例属于第四种。每次读取、搜索和验证单独看都有理由,现有的单工具超时不会触发;参数持续变化时,精确重复调用检测也不会触发。系统缺少的是整轮累计视角。
DSH 源码里的循环怎样继续
社区后续分析基于 DSH 提交 c291e7961a515f6d7af9304e7fd1d257929aef26。在该提交的 packages/core/agent-loop/src/agent.ts 中,Turn 控制器从 while (true) 开始。每个 Step 调用模型、解析输出,并在存在 Tool Call 时执行工具。
关键路径可以简化为:
模型生成 Tool Call
→ DSH 执行工具
→ 工具结果写入 next-step
→ 下一步重新构造 messages
→ 模型继续判断和调用工具
当模型没有继续发出 Tool Call,或工具明确结束当前 Turn 时,Step 才返回完成状态。工具结果进入 next-step 后,又为下一次循环提供了新输入。因此,只要模型持续发现“还可以再确认一件事”,循环就能继续。
同一文件中的 buildRequest() 会在每一步调用 session.deriveMessages(),把当前派生出的消息历史作为下一次模型请求的 messages。越靠后的验证步骤携带的历史越长,多一次动作带来的成本也会逐步变大。案例中的修改后验证阶段消耗了约 125 万输入 Token。
Infinite Agentic Loops 研究提供了什么视角
这组 33–50 轮记录展示的是过长的 Agent Loop:三次运行最终结束,一次由用户中止。论文 When Agents Do Not Stop 研究的是更进一步的工程风险——当 Agent 的反馈路径能反复到达模型调用、工具执行、状态增长或 Agent 转交,同时整条路径缺少有效上限,就可能演化成 Infinite Agentic Loops(IAL)。这里借用论文的检查框架,分析 DSH 为什么能运行这么多轮。
它与普通代码死循环的区别在于,循环由多层行为共同形成:
- Agent 控制器允许继续执行;
- 模型每一步都能提出新的合理动作;
- 工具结果更新状态并重新进入上下文;
- 退出依赖模型决定不再调用工具;
- 局部超时或局部退出条件没有覆盖整条反馈路径。
当前退出依赖 finish_reason、工具调用和路由结果。固定提交中没有 Turn 级步数、时间或费用上限。
研究者用 IAL-Scan 扫描了 6,549 个 LLM Agent 仓库。工具报告 74 个候选问题,人工确认其中 68 个 IAL,分布在 47 个项目中,精确率为 91.9%。相关风险包括成本耗尽、上下文增长、服务不可用和重复触发外部操作。
用论文的检查项分析这次 DSH 案例
| 论文检查项 | DSH 案例中的对应位置 |
|---|---|
| 循环控制器 | turn() 内的 while (true) |
| 高成本操作 | 每一步的模型请求与工具调用 |
| 增长状态 | 工具结果、Session 事件与消息历史 |
| 反馈边 | 工具结果进入 next-step,触发下一步 |
| 模型依赖退出 | 模型停止调用工具,或工具宣告 Turn 完成 |
| 整轮上限 | 固定提交中没有面向 Turn 的总步数预算 |
模型无需重复同一句话,也无需调用完全相同的工具,反馈路径仍可运行很久。搜索文件 A、读取配置 B、检查解析器 C、再回头验证 A,每一步都不同,累计 Step、上下文和 Token 会持续增长。这个案例缺少整轮上限,并出现了循环和成本放大。
用户现在怎么止损
发现 Agent 长时间验证时,可以按下面的顺序处理:
- 查看最近 10 个 Step,确认验收目标是否已经完成。
- 直接 Steer:停止新增检查,只汇报“已完成、尚未完成、下一步”三项。
- 如果继续调用工具,取消当前 Turn,保留 Session 与 Trajectory。
- 从已经完成修改的检查点启动新 Session,只运行明确的验收命令。
- 对比工具调用数和输入 Token;后段输入快速增长时,优先减少 Step,而非只缩短单次输出。
切换 Standard、PTC 或增删 Skill 不能替代停止条件。社区记录在 PTC 和 Standard 中都出现了同类形状。模式切换仍可作为对照实验,但先给当前 Turn 建立边界。
如何从一开始节省 Token
这类任务的大量消耗发生在答案已经出现之后。可以通过明确完成条件,并缩短每一步重新携带的历史来减少 Token 消耗。
1. 把停止条件写进任务
不要只写“帮我改好并检查一下”。同时给出允许修改的范围、唯一验收命令和验证通过后的动作:
任务:完成下面这一项修改。
范围:只检查并修改指定目录,不做额外重构。
完成条件:目标行为出现,且指定验证命令通过。
验证:只运行列出的命令;通过后立即结束 Turn 并汇报结果。
如果连续 3 个 Step 没有产生新结论、文件变化或测试变化,停止并说明阻塞点。
提示词里的停止条件能够减少模型继续搜索的理由,但它仍是模型依赖的约束。长任务还需要 Harness 层面的确定性预算。
2. 缩小读取和验证范围
明确目录、文件类型和验收命令,避免从仓库根目录反复搜索。修改一个配置时,只验证配置解析和相关测试;完整构建、全仓库检查或第二轮代码审阅应由任务风险决定,不要自动叠加。
3. 把实现和扩展检查拆开
第一轮完成修改和必要验收后结束 Turn。确实需要全面回归时,再开启一个新 Session,并只提供变更摘要与验证目标。新 Session 不必在每一步重新携带前一轮的全部探索历史。
4. 盯住“修改完成后的 Step”
记录第几步产生了最终文件变化。如果之后连续 5–8 个 Step 没有新的文件变化、测试结论或交付物,可以先 Steer 要求收尾;仍未结束再取消 Turn。这个范围是排障起点,应按任务复杂度调整,不是通用硬阈值。
5. 在 Harness 层设置硬预算
提示词可以改善行为,步数、时间、Token 和费用上限可以提供确定性边界。达到软上限时先让 Agent 汇报剩余工作;超过宽限后停止当前 Turn,并保留 Session 与 Trajectory,避免为已经完成的任务继续支付上下文成本。
Harness 应该增加哪些边界
1. Turn 级步数预算
在整轮 Step 达到阈值时,先要求 Agent 汇报剩余缺口;超过宽限步数仍未结束,再停止当前 Turn。阈值要覆盖完整反馈路径,不能只限制单个工具。
2. 时间与费用双预算
Step 数相同,历史长度不同,成本也会不同。生产环境应同时记录每轮耗时、累计输入 Token、累计输出 Token 和实际费用,并设置可观察的软上限。
3. 基于进展的检测
精确重复参数只能发现最简单的循环。更有效的信号包括:验收项是否变化、修改文件集合是否变化、测试结果是否变化,以及连续多少步没有新增可交付结果。
4. 压缩后的动作台账
上下文压缩后保留“已经执行过什么、结果是什么”的短台账,避免 Agent 丢失自己的操作历史,再次读取和验证同一组事实。
5. 明确且可审计的停止原因
Turn 因步数、时间、费用或人工取消而结束时,应把原因记录进 Trajectory。后续恢复才能区分正常完成、预算耗尽和外部中止。
社区 Turn Budget Guard 做了什么
讨论参与者随后发布了 @argszero/cordis-plugin-turn-budget-guard。默认配置在 20 个 Step 后注入一次收尾请求,再给 8 个宽限 Step;仍未收束时取消当前 Turn,并保留用户在运行期间发送的消息。
- insert:
- id: turn-budget-guard
name: '@argszero/cordis-plugin-turn-budget-guard'
config:
maxSteps: 20
gracefulSteps: 8
这个插件展示了一种可挂载的止损方案。它由社区维护,安装前应检查源码,并先在独立 Profile 中验证阈值是否适合自己的长任务。未来如果 DSH Core 提供原生 Step Budget,应以原生机制统一记录停止原因和恢复行为。
一套可复现的排查记录
为了判断自己遇到的是模型偶发绕路,还是 Harness 反馈路径缺少边界,至少记录:
| 维度 | 要记录的内容 |
|---|---|
| 环境 | DSH 版本、模型 ID、模式、推理强度、Endpoint |
| 任务 | 初始提示词、工作区状态、验收标准 |
| 动作 | Step 数、工具名称、关键参数与结果 |
| 进展 | 第几步已经拿到答案、第几步完成修改 |
| 成本 | 每阶段输入/输出 Token、耗时与费用 |
| 终止 | 正常完成、用户取消、预算停止或错误 |
同一任务至少运行三次,并保留一次完整 Trajectory。关键指标包括:答案出现之后又执行了多少动作,以及这些动作是否改变最终交付物。单独记录“模型想了多久”无法描述这类故障。
相关阅读
- V4.1 Flash:DSH Minimal、Standard、PTC 怎么选
- DeepSeek V4.1 Flash 接入 Harness
- 复现 DeepSeek V4.1 Flash DeepSWE
- DeepSeek Harness 安全指南
常见问题
这是 DeepSeek V4.1 Flash 的模型 Bug 吗?
四次社区运行都使用 V4.1 Flash,说明它能触发这种行为;DSH 源码同时提供了循环持续扩张的机制。定位时应把它视为模型行为与 Harness 停止边界的组合问题,并用同题跨模型、跨版本测试继续拆分变量。
换成 PTC 就能解决吗?
不能依赖模式切换解决。案例在 PTC 与 Standard 中都出现了 33–50 轮运行。PTC 可以压缩适合批量执行的工具往返,但不会自动提供整轮 Step 上限。
为什么后面的几步特别贵?
DSH 会为下一步派生当前消息历史。Session 越长,后续请求携带的上下文通常越大,因此修改完成后的重复验证会反复付费处理已经累积的历史。