让 CodeBuddy 持续工作直到达成目标
用
/goal设置一个完成条件,CodeBuddy 会跨多轮持续工作,直到条件满足才把控制权交还给你。
版本要求:
/goal命令要求@tencent-ai/codebuddy-code已包含 goal 功能(agent-cli 的GoalService模块)。
/goal 命令设置一个完成条件,CodeBuddy 持续向其推进而无需你逐步催促。每轮(turn)结束时,由小模型(small-fast model)评估器判断条件是否成立——若不成立,CodeBuddy 自动开始下一轮,而不是把控制权交回给你。一旦条件满足,目标自动清除。
适合用 /goal 跟踪有可验证终态的实质性工作:
- 把一个模块迁到新 API,直到所有调用点都能编译且测试通过
- 实施一份设计文档,直到所有验收条件成立
- 把一个大文件拆成若干聚焦模块,直到每个模块都不超过尺寸预算
- 处理打了某个标签的 issue 列表,直到队列清空
本文涵盖:
与其他自治工作流对比
下面三种方式都能让会话在多次提示之间继续运行。按"下一轮由谁触发"来选择:
| 方式 | 下一轮何时开始 | 何时停止 |
|---|---|---|
/goal | 上一轮结束后立即开始 | 评估器确认条件已满足 |
/loop | 时间间隔触发 | 你主动停或模型判定工作结束 |
| Stop hook | 上一轮结束后立即开始 | 你的脚本或 prompt 自行决定 |
/goal 与 Stop hook 都在每轮之后触发。/goal 是会话级捷径——你输入条件,仅在当前会话内生效。Stop hook 写在 settings 里,对它作用域内的所有会话生效,并且既可以跑确定性脚本也可以跑模型评估的 prompt。
提示:上述方式都让"当前会话"持续运行。如果你需要的是独立于当前会话的定时工作(比如夜间跑测试、早上做 issue 分流),见计划任务。
使用 /goal
每个会话同时只能有一个 active goal。同一个命令根据参数差异承担"设置 / 查看 / 清除"三种角色。
设置目标
在 /goal 后跟你想满足的条件即可。如果当前已有 active goal,新的会替换旧的(旧 goal 的 hook 自动注销)。
text
/goal all tests in test/auth pass and the lint step is clean设置完成后,CodeBuddy 会立即开始一轮,把"条件本身"作为指令交给主 agent——不需要你再额外发 prompt。同时输入框右下方会出现一行 ⊚ /goal active (Xs) 指示器,每秒刷新已运行时长,让你随时知道当前处于 goal 模式。
每轮结束后,评估器会返回一段简短的 reason 解释"为什么条件还没/已满足"。这段 reason 会以 isMeta=true 的内部消息形式注入对话历史,让模型在下一轮看到 evaluator 的视角,从而精准补做欠缺的部分——这是模型能"知道还差哪几步"的关键。
会话级行为:goal 持续运行直到条件满足或你执行
/goal clear。运行/goal(无参数)查看 turns / tokens 等统计。
写好一个有效的 condition
评估器只会基于 CodeBuddy 在对话中已经表达出来的内容来判断条件,它不会自己跑命令、读文件。所以条件要写成"CodeBuddy 自己的输出能证明"的形式。"All tests in test/auth pass" 之所以可行,是因为 CodeBuddy 会自己跑测试,结果落在 transcript 里供评估器阅读。
一个能稳健支撑多轮工作的条件通常包含:
- 一个可度量的终态:测试结果、构建退出码、文件数、空队列……
- 一种可证明方式:例如
\`npm test\` exits 0或\`git status\` is clean - 不可破坏的约束:路上不能改的东西,比如 "no other test file is modified"
condition 长度上限 4000 字符。
如果想给 goal 设置兜底上限,可以在 condition 里加上轮数 / 时间从句,例如 or stop after 20 turns,CodeBuddy 会在每轮里把当前进度对照该从句,evaluator 也能从对话中读出。
查看状态
不带参数运行 /goal:
text
/goal在 TUI 中会打开 goal recap 面板;在 Web UI / ACP 客户端中会通过 ACP 广播打开同等面板;在 headless / SDK 等没有 UI 的环境降级为纯文本输出。
面板内容包括:
- 条件
- 已运行时长
- 已评估的 turn 数
- token 消耗(goal 期间增量)
- 评估器最近一次给出的 reason
如果当前没有 active goal、但本会话内之前曾达成过一次 goal,面板会展示那次的 condition、duration、turn 数和 token 数。
提前清除目标
text
/goal clear下面这些 token 都视为 clear 的同义词:stop、off、reset、none、cancel。仅在单 token 完全匹配时识别为清除指令——/goal stop using deprecated API 仍按"设置新 condition"处理,不会被吞掉。
执行 /clear 重启会话也会一并移除 active goal(hook 注销 + meta 清理)。
在恢复会话时携带 goal
通过 --resume / --continue 恢复会话时,未完成的 goal 会被恢复(condition 与 scope 都还原)。
当前限制:恢复时会沿用原 goal 的 createdAt / turnCount / token 起点,不重置计时器与计数器。如果你希望"重新计时",请先
/goal clear再重新/goal <condition>。已经达成或已被清除的 goal 不会被恢复(meta 已删)。
非交互模式运行
/goal 在非交互模式(headless)、Remote Control 中均可用。在 -p 模式下设置 goal 会让 evaluator 循环跑到完成:
bash
codebuddy -p "/goal CHANGELOG.md has an entry for every PR merged this week"需要在条件满足前提早终止,按 Ctrl+C。
评估机制如何工作
/goal 是对会话级 prompt-based Stop hook 的一层封装。每当 CodeBuddy 主 agent 跑完一轮,当前 condition + 当前对话 会被一并发给配置好的小模型评估器。评估器返回一个"是 / 否 / 不可达"三态结果与简短 reason:
- 是(
ok: true):清除 goal、记录"已达成"事件,UI 显示✔ Goal achieved状态条。 - 否(
ok: false):把 reason 作为isMeta=true的 user message 注入 history(让主模型看到下一步该补什么),让 CodeBuddy 继续工作。同时写入一条goal-progressUI 状态条◯ Goal not yet met… continuing。 - 不可达(
ok: false, impossible: true):用于评估器判断"在当前会话里这个目标根本不可能完成"(条件自相矛盾、依赖的能力/资源不可用、模型已经穷尽合理尝试)。立即清除 goal,UI 显示✕ Goal could not be achieved,避免把循环陷死。
评估器使用 product 配置中绑定到 lite 槽位的小模型(在不同 model provider 上分别映射到 gpt-5.1-codex-mini / gemini-2.5-flash / DeepSeek deepseek-v4-flash 等)。评估只看现有 transcript、不调用工具,所以走小模型既快又便宜。
计费:评估器消耗的 token 计在小模型账单上,相对主 turn 通常可忽略。
评估窗口约束
为了避免"刚 set 完就 achieved"——同一会话曾经达成过的目标会在 transcript 里留下成功响应——我们会把当前 goal 的 createdAt(ISO 8601)注入评估器的 user prompt,并明确指示:
Evaluate ONLY the conversation that happened AFTER this timestamp. Earlier messages MUST NOT be used as evidence.
如果设置后还没有合格活动发生,evaluator 必须返回 {"ok": false, "reason": "Goal was just set; no work has been done yet against the new condition."}。
给 evaluator 的 history 干净化
我们会在把 history 喂给 evaluator 前过滤掉以下扩展 item type(它们是项目自定义、SDK 不认识的):
goal-result/goal-progress(goal 自身的 UI 状态项)summary/topic/ai-title/custom-titlefile-history-snapshot
这些 item 既不是用户输入也不是 assistant 响应,对 evaluator 没有判断价值,且会触发 SDK 的 Unknown item type 警告。
实现说明与已知限制
下表汇总当前实现的关键行为与已知限制,便于排错时定位:
| 行为 | 状态 | 备注 |
|---|---|---|
| 设置 / 替换 / kick-off | ✅ | 设置后立即开始一轮,已有 active goal 时自动替换 |
/goal clear 别名 | ✅ | 支持 stop / off / reset / none / cancel 五种单 token 同义词 |
/clear 同步清除 active goal | ✅ | 重启会话时一并注销 goal hook 并清理 meta |
| condition 上限 | ✅ | 4000 字符 |
| reason 反馈进 history | ✅ | 注入格式:Stop hook feedback: [<condition>]: <reason> |
| 三态语义(ok / not-yet / impossible) | ✅ | 不可达时立即清除 goal,避免无效循环 |
| evaluator 走小模型 | ✅ | 使用 lite 槽位绑定的模型(按 provider 分别映射) |
/goal 无参 → 状态视图 | ✅ | TUI / Web UI 面板 + headless 文本降级 |
持续运行指示器 ⊚ /goal active (Xs) | ✅ | 输入框右下方常驻显示运行时长,1Hz 刷新 |
--resume 时 turn / timer / token 重置 | ❌ 待补 | 当前沿用原 createdAt / turnCount,需"重新计时"请先 /goal clear |
参见
/loop创建循环任务:按时间间隔重复触发,而不是直到条件满足- Hook 入门 / Hook 参考:理解 prompt-based Stop hook 的底层机制;当你需要更复杂的评估逻辑时可以自己写一个
- 非交互(headless)模式:在 CI / 脚本中通过
-p跑/goal - Remote Control:在 Web UI / 微信通道中触发 goal
- 斜杠命令一览:所有内置 slash 命令的索引