# 让 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 三者怎么选
- [设置目标](#设置目标)与[写好条件的要点](#写好一个有效的-condition)
- [查看状态](#查看状态)、[提前清除](#提前清除目标)、[非交互模式运行](#非交互模式运行)
- [评估机制](#评估机制如何工作)
- [实现说明与已知限制](#实现说明与已知限制)


## 使用 `/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）](./headless.md)、[Remote Control](./remote-control.md) 中均可用。在 `-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](./hooks.md) 的一层封装。每当 CodeBuddy 主 agent 跑完一轮，**当前 condition + 当前对话** 会被一并发给配置好的小模型评估器。评估器返回一个"是 / 否 / 不可达"三态结果与简短 reason：

- **是（`ok: true`）**：清除 goal、记录"已达成"事件，UI 显示 `✔ Goal achieved` 状态条。
- **否（`ok: false`）**：把 reason 作为 `isMeta=true` 的 user message 注入 history（让主模型看到下一步该补什么），让 CodeBuddy 继续工作。同时写入一条 `goal-progress` UI 状态条 `◯ 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-title`
- `file-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` 创建循环任务](./scheduled-tasks.md#使用-loop-创建循环任务)：按时间间隔重复触发，而不是直到条件满足
- [Hook 入门](./hooks-guide.md) / [Hook 参考](./hooks.md)：理解 prompt-based Stop hook 的底层机制；当你需要更复杂的评估逻辑时可以自己写一个
- [非交互（headless）模式](./headless.md)：在 CI / 脚本中通过 `-p` 跑 `/goal`
- [Remote Control](./remote-control.md)：在 Web UI / 微信通道中触发 goal
- [斜杠命令一览](./slash-commands.md)：所有内置 slash 命令的索引
