问题描述
在长时间运行的任务中,Agent 第一次可以正常执行大量 turns;但达到单次任务最大轮次后,再次要求继续时,新任务虽然已获得新的 turn 配额,却往往只执行 1~2 turns 就自行结束。
该问题在 Goal Mode / Reflect 持续唤醒场景下尤其明显。它并非简单的 max_turns=180 限制:后续任务的 turn 计数已重新从 1 开始,但仍会远未达到 180 turns 就提前结束。
复现步骤
- 启动一个需要长期持续执行的 Goal Mode 任务。
- 让 Agent 进行多轮分析、验证和优化,使 LLM session history 变长。
- 第一次任务结束或达到最大轮次后,通过 Goal Mode / Reflect 再次唤醒 Agent。
- 在续跑提示中明确要求 Agent 不得直接宣告完成,应继续检查和改进。
- 观察续跑任务的实际执行轮数。
实际表现
一次实际运行中:
- 第一次唤醒:约 122 turns
- 第二次唤醒:约 6 turns
- 第三次唤醒:约 2 turns
每次唤醒的内部 turn 均已重新从 1 开始。续跑时,模型通常很快生成总结性文本,不再调用工具,随后 Agent 将任务判定为完成并退出。
预期表现
- 新一次唤醒应正常获得完整的 turn 配额;
- 前序长对话不应导致模型在 1~2 turns 内错误判断任务已完成;
- Goal Mode 应基于当前状态继续进行实质性操作;
- 引擎应能区分真正完成、异常提前收尾、达到最大轮次、用户中止和后端错误。
初步根因分析
初步定位到以下链路:
- 不同
put_task 之间复用了同一个 LLM session history;
- 第一次长任务的历史中积累了大量“全部完成”“最终交付”“收口”等完成态内容;
- 再次唤醒时,虽然注入了
CONTINUATION_PROMPT,但模型仍会看到此前完整或裁剪后的长历史;
- 模型受完成态历史影响,很快输出总结而不再调用工具;
- 无工具调用进入
no_tool 路径;
do_no_tool 返回的 StepOutcome 没有 next_prompt;
agent_loop.py 将其判定为 CURRENT_TASK_DONE 并退出。
调用链可概括为:
long session history
→ completion-state semantic pollution
→ model outputs final text without tool calls
→ do_no_tool(...)
→ StepOutcome(..., next_prompt=None)
→ CURRENT_TASK_DONE
→ exits after 1–2 turns
因此,这更像是长上下文中的完成态语义污染,而不只是最大轮次配置问题。
相关代码
agent_loop.py:next_prompt is None 时将任务视为 CURRENT_TASK_DONE,无法区分真正完成和异常提前收尾。
ga.py:do_no_tool 返回 StepOutcome(response, next_prompt=None)。
llmcore.py:现有 trim_messages_history 主要按容量/字符量裁剪,缺少语义感知;裁剪后仍可能保留大量完成态内容。
reflect/goal_mode.py:CONTINUATION_PROMPT 无法稳定覆盖长历史中积累的完成态语义。
建议方案
1. Goal Mode 续跑时重建精简上下文
新唤醒时不直接继承完整原始 history,而是构造:
System Prompt
+ 结构化任务状态摘要
+ 当前目标
+ 已完成事项
+ 待验证事项
+ 交付物路径
+ 本轮继续执行要求
状态可从 working.key_info、history_info、checkpoint、Goal Mode state file 和工作目录恢复。
2. 增加有上限的 early-exit guard
Goal Mode 新唤醒后,若模型在前 N turns 内直接进入 no_tool,先注入一次恢复提示,要求检查当前产物并执行至少一项实质性验证或改进;设置重试上限,避免真正完成时无限循环。
3. 细化退出原因
建议区分:
CURRENT_TASK_DONE
MAX_TURNS_EXCEEDED
EARLY_NO_TOOL
USER_ABORTED
BACKEND_ERROR
GOAL_BUDGET_EXHAUSTED
不建议仅调大 max_turns,因为续跑任务本身并未耗尽新的 turn 配额。
相关 Issues
暂未发现完全相同的问题。可能相关:
其中 #417 和本问题可能同属长上下文下的行为退化,但一个表现为重复/死循环,本问题表现为过早总结退出。
环境信息
Repository: lsdefine/GenericAgent
Mode: Goal Mode / Reflect
Configured max turns per task: 180
First long run: approximately 122 turns
Second continuation: approximately 6 turns
Third continuation: approximately 2 turns
问题描述
在长时间运行的任务中,Agent 第一次可以正常执行大量 turns;但达到单次任务最大轮次后,再次要求继续时,新任务虽然已获得新的 turn 配额,却往往只执行 1~2 turns 就自行结束。
该问题在 Goal Mode / Reflect 持续唤醒场景下尤其明显。它并非简单的
max_turns=180限制:后续任务的 turn 计数已重新从 1 开始,但仍会远未达到 180 turns 就提前结束。复现步骤
实际表现
一次实际运行中:
每次唤醒的内部 turn 均已重新从 1 开始。续跑时,模型通常很快生成总结性文本,不再调用工具,随后 Agent 将任务判定为完成并退出。
预期表现
初步根因分析
初步定位到以下链路:
put_task之间复用了同一个 LLM session history;CONTINUATION_PROMPT,但模型仍会看到此前完整或裁剪后的长历史;no_tool路径;do_no_tool返回的StepOutcome没有next_prompt;agent_loop.py将其判定为CURRENT_TASK_DONE并退出。调用链可概括为:
因此,这更像是长上下文中的完成态语义污染,而不只是最大轮次配置问题。
相关代码
agent_loop.py:next_prompt is None时将任务视为CURRENT_TASK_DONE,无法区分真正完成和异常提前收尾。ga.py:do_no_tool返回StepOutcome(response, next_prompt=None)。llmcore.py:现有trim_messages_history主要按容量/字符量裁剪,缺少语义感知;裁剪后仍可能保留大量完成态内容。reflect/goal_mode.py:CONTINUATION_PROMPT无法稳定覆盖长历史中积累的完成态语义。建议方案
1. Goal Mode 续跑时重建精简上下文
新唤醒时不直接继承完整原始 history,而是构造:
状态可从
working.key_info、history_info、checkpoint、Goal Mode state file 和工作目录恢复。2. 增加有上限的 early-exit guard
Goal Mode 新唤醒后,若模型在前 N turns 内直接进入
no_tool,先注入一次恢复提示,要求检查当前产物并执行至少一项实质性验证或改进;设置重试上限,避免真正完成时无限循环。3. 细化退出原因
建议区分:
CURRENT_TASK_DONEMAX_TURNS_EXCEEDEDEARLY_NO_TOOLUSER_ABORTEDBACKEND_ERRORGOAL_BUDGET_EXHAUSTED不建议仅调大
max_turns,因为续跑任务本身并未耗尽新的 turn 配额。相关 Issues
暂未发现完全相同的问题。可能相关:
其中 #417 和本问题可能同属长上下文下的行为退化,但一个表现为重复/死循环,本问题表现为过早总结退出。
环境信息