先说下背景。Codex 和 Claude Code 现在都内置了 /goal:你给一句"怎样算做完"的条件,agent 就自己一轮接一轮地干,直到满足为止,中间不用你每轮敲"继续"。我平时在 Kimi Code 里跑那种目标很明确的长任务——比如把 test/auth 跑到全绿、把一个重构收尾、把 lint 清干净——最累的就是每轮结束都得手动催一下。所以想提一个同类的 /goal。
按 CONTRIBUTING 的规矩,新功能先开 issue 聊,所以这里先把想法和实现思路摆出来。方向要是 OK,我来实现(会挂在实验 flag 后面)。
/goal 大概是个什么东西(参考 Codex / Claude Code)
/goal <条件>:设定完成条件,马上开一轮。
/goal(不带参数):看当前状态——条件、跑了多久、几轮、花了多少 token、裁判上一次给的理由,以及本会话里已经达成过的目标。
/goal clear(stop / off / cancel 这些别名):中途取消。
- 关键机制:每轮结束后,用一个轻量模型当"裁判"读一遍,判断条件达没达成。没达成,就把裁判给的一句理由喂回去当下一轮的指引,继续;达成了,就自动清掉目标、记一笔。
- 作用域:一个会话同时只能有一个目标;
--resume 时条件会恢复(计时和计数清零)。本质上就是个会话级的 Stop hook。
两家有个值得注意的差别,对我们其实是个设计选择:
- Claude 的裁判只看对话里 agent 已经产出来的东西,自己不调工具(官方文档:"Set a completion condition with /goal and Claude keeps working across turns until the condition is met",默认用 small fast model 即 Haiku 评估)。
- Codex 的裁判会去对照真实证据(文件、命令输出、测试结果)来判定,而且多了
/goal pause / /goal resume(OpenAI 文档,0.128.0 引入)。
为什么我觉得 Kimi Code 接这个很顺
翻了下引擎,发现这套机制的底子其实已经有了。turn 循环里 packages/agent-core/src/agent/turn/index.ts:391 的 shouldContinueAfterStop,在 Stop hook "拦下"的时候,会把 hook 给的 reason 当成 system_trigger 的用户消息塞进上下文,然后继续跑:
const stopBlock = await this.agent.hooks?.triggerBlock('Stop', { … });
if (stopBlock !== undefined) {
stopHookContinuationUsed = true;
this.agent.context.appendUserMessage([{ type: 'text', text: stopBlock.reason }],
{ kind: 'system_trigger', name: 'stop_hook' });
return { continue: true };
}
这不就正好是"裁判理由 → 下一轮指引"嘛。唯一挡路的是上面那行故意加的一次性限制:
// Stop hooks get one continuation; otherwise a hook that always blocks would loop forever.
if (stopHookContinuationUsed) return { continue: false };
所以 /goal 说白了就是:一个会话级、由裁判把关的 Stop 续命——只要目标还在、裁判说"还没好",就允许它接着续;再配一个轮数 / token 的硬上限兜底,免得真死循环。
实现大概长这样(挂 flag)
- flag:
packages/agent-core/src/flags/registry.ts 里加 { id: 'goal', env: 'KIMI_CODE_EXPERIMENTAL_GOAL', default: false, surface: 'both' },全程用 flags.enabled('goal') 守着(按 AGENTS.md,发布前默认关)。
- 命令:
apps/kimi-code/src/tui/commands/registry.ts 里加个 goal 条目(处理 clear / stop 参数),新建 commands/goal.ts 写 handleGoalCommand,在 commands/dispatch.ts 里接上。
- 目标状态:会话级的一个对象
{ condition, turns, startedAt, tokensBaseline, lastReason },同时只允许一个。
- 循环:目标激活时,
shouldContinueAfterStop 里走裁判分支,而不是被那个一次性限制卡住——跑一个轻量、不调工具的 LLM 裁判看最近的对话;达成就清掉目标、continue: false;没达成就用现成的 appendUserMessage(..., { kind: 'system_trigger' }) 把理由塞回去、continue: true;同时卡住 max-turns / max-tokens 上限,也尊重 abort 信号能随时停。
- TUI:footer / 状态栏给个
◎ /goal active 的指示,/goal 无参看详情;裁判每轮的理由在 transcript 里露出来。
- resume:把条件存进 session metadata,resume 时恢复(计数清零),跟上面参考的行为对齐。
几个想先跟你们对一下的点
- 那个一次性续命限制:是给 goal 单开一条分支,还是把 Stop hook 的续命预算做成可配?不太想削弱原来"hook 老是拦 → 死循环"的保护。
- 裁判用哪个模型:同一个会话模型,还是单配一个便宜快的?现在有没有现成的"副模型"概念能复用,还是这个 flag 顺带引一个(类似
KIMI_..._GOAL_EVAL_MODEL)?
- 跟别的模式怎么共处:plan mode / auto / 后台任务 /
-p 无头模式,这些场景下允不允许用 /goal?
- 安全默认值:轮数 / token / 墙钟时间的默认上限定多少合适,到顶时给个清楚的"已停止:预算用尽"提示。
- 命名:跟 Codex / Claude 保持一致(
/goal、/goal clear),还是按 Kimi Code 自己的命名习惯调一下?
最后
方向要是认可,我来实现:挂在 KIMI_CODE_EXPERIMENTAL_GOAL 后面,补 loop / 裁判这条路径的测试和 changeset,再用 gen-docs 出个文档页。先把设计聊清楚——尤其上面续命限制和裁判模型这两点——再动代码。
参考 / 出处
prior art(两家都已内置 /goal):
kimi-code 这边我引用到的代码位置(实现思路就是基于这些):
- Stop 续命点:
packages/agent-core/src/agent/turn/index.ts:391(shouldContinueAfterStop,把 hook 的 reason 当 system_trigger 用户消息塞回去并续轮)
- 一次性续命限制:
packages/agent-core/src/agent/turn/index.ts:395-396
- 实验 flag 注册表(目前是空的,带格式说明):
packages/agent-core/src/flags/registry.ts
- 内置 slash 命令注册表(目前 28 个,无 goal):
apps/kimi-code/src/tui/commands/registry.ts;分发在 apps/kimi-code/src/tui/commands/dispatch.ts
先说下背景。Codex 和 Claude Code 现在都内置了
/goal:你给一句"怎样算做完"的条件,agent 就自己一轮接一轮地干,直到满足为止,中间不用你每轮敲"继续"。我平时在 Kimi Code 里跑那种目标很明确的长任务——比如把test/auth跑到全绿、把一个重构收尾、把 lint 清干净——最累的就是每轮结束都得手动催一下。所以想提一个同类的/goal。按 CONTRIBUTING 的规矩,新功能先开 issue 聊,所以这里先把想法和实现思路摆出来。方向要是 OK,我来实现(会挂在实验 flag 后面)。
/goal大概是个什么东西(参考 Codex / Claude Code)/goal <条件>:设定完成条件,马上开一轮。/goal(不带参数):看当前状态——条件、跑了多久、几轮、花了多少 token、裁判上一次给的理由,以及本会话里已经达成过的目标。/goal clear(stop / off / cancel 这些别名):中途取消。--resume时条件会恢复(计时和计数清零)。本质上就是个会话级的 Stop hook。两家有个值得注意的差别,对我们其实是个设计选择:
/goal pause//goal resume(OpenAI 文档,0.128.0 引入)。为什么我觉得 Kimi Code 接这个很顺
翻了下引擎,发现这套机制的底子其实已经有了。turn 循环里
packages/agent-core/src/agent/turn/index.ts:391的shouldContinueAfterStop,在 Stop hook "拦下"的时候,会把 hook 给的reason当成system_trigger的用户消息塞进上下文,然后继续跑:这不就正好是"裁判理由 → 下一轮指引"嘛。唯一挡路的是上面那行故意加的一次性限制:
所以
/goal说白了就是:一个会话级、由裁判把关的 Stop 续命——只要目标还在、裁判说"还没好",就允许它接着续;再配一个轮数 / token 的硬上限兜底,免得真死循环。实现大概长这样(挂 flag)
packages/agent-core/src/flags/registry.ts里加{ id: 'goal', env: 'KIMI_CODE_EXPERIMENTAL_GOAL', default: false, surface: 'both' },全程用flags.enabled('goal')守着(按 AGENTS.md,发布前默认关)。apps/kimi-code/src/tui/commands/registry.ts里加个goal条目(处理clear/stop参数),新建commands/goal.ts写handleGoalCommand,在commands/dispatch.ts里接上。{ condition, turns, startedAt, tokensBaseline, lastReason },同时只允许一个。shouldContinueAfterStop里走裁判分支,而不是被那个一次性限制卡住——跑一个轻量、不调工具的 LLM 裁判看最近的对话;达成就清掉目标、continue: false;没达成就用现成的appendUserMessage(..., { kind: 'system_trigger' })把理由塞回去、continue: true;同时卡住 max-turns / max-tokens 上限,也尊重 abort 信号能随时停。◎ /goal active的指示,/goal无参看详情;裁判每轮的理由在 transcript 里露出来。几个想先跟你们对一下的点
KIMI_..._GOAL_EVAL_MODEL)?-p无头模式,这些场景下允不允许用/goal?/goal、/goal clear),还是按 Kimi Code 自己的命名习惯调一下?最后
方向要是认可,我来实现:挂在
KIMI_CODE_EXPERIMENTAL_GOAL后面,补 loop / 裁判这条路径的测试和 changeset,再用gen-docs出个文档页。先把设计聊清楚——尤其上面续命限制和裁判模型这两点——再动代码。参考 / 出处
prior art(两家都已内置
/goal):kimi-code 这边我引用到的代码位置(实现思路就是基于这些):
packages/agent-core/src/agent/turn/index.ts:391(shouldContinueAfterStop,把 hook 的reason当system_trigger用户消息塞回去并续轮)packages/agent-core/src/agent/turn/index.ts:395-396packages/agent-core/src/flags/registry.tsapps/kimi-code/src/tui/commands/registry.ts;分发在apps/kimi-code/src/tui/commands/dispatch.ts