Skip to content

[Feature] 加个 /goal:设好"完成条件"后让 agent 自己一轮轮干到达成 #226

@zbl1998-sdjn

Description

@zbl1998-sdjn

先说下背景。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:391shouldContinueAfterStop,在 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)

  1. flag:packages/agent-core/src/flags/registry.ts 里加 { id: 'goal', env: 'KIMI_CODE_EXPERIMENTAL_GOAL', default: false, surface: 'both' },全程用 flags.enabled('goal') 守着(按 AGENTS.md,发布前默认关)。
  2. 命令:apps/kimi-code/src/tui/commands/registry.ts 里加个 goal 条目(处理 clear / stop 参数),新建 commands/goal.tshandleGoalCommand,在 commands/dispatch.ts 里接上。
  3. 目标状态:会话级的一个对象 { condition, turns, startedAt, tokensBaseline, lastReason },同时只允许一个。
  4. 循环:目标激活时,shouldContinueAfterStop 里走裁判分支,而不是被那个一次性限制卡住——跑一个轻量、不调工具的 LLM 裁判看最近的对话;达成就清掉目标、continue: false;没达成就用现成的 appendUserMessage(..., { kind: 'system_trigger' }) 把理由塞回去、continue: true;同时卡住 max-turns / max-tokens 上限,也尊重 abort 信号能随时停。
  5. TUI:footer / 状态栏给个 ◎ /goal active 的指示,/goal 无参看详情;裁判每轮的理由在 transcript 里露出来。
  6. resume:把条件存进 session metadata,resume 时恢复(计数清零),跟上面参考的行为对齐。

几个想先跟你们对一下的点

  1. 那个一次性续命限制:是给 goal 单开一条分支,还是把 Stop hook 的续命预算做成可配?不太想削弱原来"hook 老是拦 → 死循环"的保护。
  2. 裁判用哪个模型:同一个会话模型,还是单配一个便宜快的?现在有没有现成的"副模型"概念能复用,还是这个 flag 顺带引一个(类似 KIMI_..._GOAL_EVAL_MODEL)?
  3. 跟别的模式怎么共处:plan mode / auto / 后台任务 / -p 无头模式,这些场景下允不允许用 /goal?
  4. 安全默认值:轮数 / token / 墙钟时间的默认上限定多少合适,到顶时给个清楚的"已停止:预算用尽"提示。
  5. 命名:跟 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 的 reasonsystem_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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions