任务没做完 Agent 为什么停了?拆解 Pi /goal 的持续执行机制
Agent 提前停下来的本质,是把「一轮回答结束」错认成「整个任务完成」。Pi 的 /goal 扩展把这层混淆拆开。
- 核心问题:一轮回答结束 ≠ 任务完成,普通 Agent 缺少独立完成判断机制。
- 目标弱化:长上下文里,初始目标被中间产物稀释,模型围绕当前成果回复。
- Goal 四步循环:保存完整目标 → 每轮重读提示词 → 必须调用
goal_complete→ 未完成自动续跑。 - 两种判断方式:Codex 工作模型自查 + 完成工具;Claude Code 独立评估模型判断。
- 适用任务四特征:结果明确、可验证、逐步收敛、不需要频繁人工决策。
- 任务描述四要素:结果、范围、验收方式、禁止操作。
- 能力边界:Goal 是持续执行保障层,正确性最终仍取决于模型和验证方法。
一轮回答结束 ≠ 整个任务完成
Agent 用久了都会遇到这种场景:让 Agent 完成 6 项工作,它做完第 4 项就给出最终回复,要求你确认后再继续;你只要再敲一句「继续」,它马上就能发现遗漏、补完剩下的部分。
这里有一个 容易混淆的区别:
- 一轮回答结束:模型决定不再调用工具,生成最终回复。
- 整个任务完成:用户交代的所有要求都通过验收。
普通 Agent 的「回合结束」完全取决于 当前模型自己判断——只要它觉得「产物够完整」,就会围 绕当前结果组织一段像样的回复,把剩余工作交还给用户。
长上下文里的两个隐形杀手
为什么模型会在任务没完成时就给出最终回复?两个原因叠加。
杀手一:目标在长上下文里被弱化。 任务越长,中间产生的文件内容、命令输出、阶段性结果越多。最初的完整目标在这些信息里逐渐失去注意力,模型开始围绕 当前窗口里最显眼的产物 组织答案。
杀手二:缺少独立的完成判断。 普通 Agent 没有独立的「完成检查」环节,完全靠模型根据上下文自行判断。任务一旦变长,模型看到主要产物已经生成(或遇到一次执行失败),就倾向于汇报当前状态、让你接管下一步。
举个例子:让 Agent 整理 20 份资料生成「摘要、大纲、事实核验表」三件套。它做完摘要和大纲后,看到两个文件已经存在,于是回复「资料整理和内容结构已完成,建议后续补充事实核验」——从一轮回答看这段回复 完整合理,但事实核验表依然缺失。
Pi /goal 的四步持续执行机制
/goal 扩展的设计思路很直接:把「完成判断」从模型的一次性决策变成跨回合的循环。Pi 的设计哲学可以参考 Pi 的设计哲学:核心极简、行为外置的 4 包架构——「极简默认 + Extension 改 Agent 自身」这套思路在 /goal 里体现得最完整。
1. 完整目标独立保存
/goal 启动后,扩展把用户最初的完整目标 单独存储 并标记为活动状态。即便后续对话变长、上下文被压缩、或者稍后重新打开同一个会话,这个未完成的目标依然能恢复。
2. 每轮重新注入提示词
每开一个新回合,扩展都会向上下文注入一段核心提示词,大意是:
保留用户最初的完整目标;根据当前文件、命令、产物逐项检查;不要停在分析、计划、部分成果;只有所有要求都证明完成,才能提交最终完成。
新回合等于把任务范围和验收规则 又强调一遍,降低原始目标在长上下文里被遗忘的概率。
3. goal_complete 显式完成
模型想结束整个任务,必须 显式调用 goal_complete 完成工具,并提交一段完成摘要(说明做完 了什么、用哪些证据验证)。扩展会拦截「摘要为空」「还有测试失败」这类明显矛盾,通过后才把目标从活动状态变成完成状态。
普通最终回复只能结束当前回合,不能结束整个 Goal。只有调用 goal_complete 才算数。
4. 自动续跑
如果模型像之前的例子一样,只生成了普通最终回复而没调用完成工具,当前回合虽然结束,但 Goal 依然保持活动。扩展会自动开启下一个 Agent 回合——新回合 再次拿到完整目标和同一套核心规则,重新从未完成的部分开始工作。
这个循环会一直持续,直到:
- Agent 成功调用
goal_complete - 用户主动暂停或清除目标
- 达到 token 预算
- 遇到必须外部介入的阻塞