首页
/ Open Interpreter 的 write-goal 技能:把模糊意图写成可验证的 /goal 完成契约

Open Interpreter 的 write-goal 技能:把模糊意图写成可验证的 /goal 完成契约

2026-09-06 21:37:10作者:范垣楠Rhoda

本文基于 Open Interpreter(一个面向 Kimi K3 等开放模型的编码 Agent)仓库中的内置技能文档 write-goal.md,完整解析「如何把一个粗糙的想法打磨成 goal 模式可以无人值守推进的目标文本」。你会掌握 goal 的五大契约要素(终态、证明、边界、循环、停止规则)、预算的 opt-in 原则、与用户协作的五步工作流,以及 CreateGoal/SetGoalBudget/UpdateGoal 等底层工具在 kimi_code_aliases.rs 中的真实实现约束。

write-goal 技能是什么:Kimi Code 内置技能的"目标撰写器"

write-goal 是 Open Interpreter 中 Kimi Code 兼容层内置的三个技能之一(另两个是 check-kimi-code-docsupdate-config)。技能清单在构建系统提示词时以硬编码方式注入,见 kimi_code.rs 中的 KIMI_CODE_BUILTIN_SKILLS 常量:

### Built-in
- check-kimi-code-docs: Answer questions about the Kimi Code product ...
- update-config: Inspect or edit kimi-code's own config ...
- write-goal: Help the user craft a well-specified `/goal` objective for goal mode
  — turn a rough intention into a completion contract with a clear finish line,
  proof, boundaries, and stop rule. Use when the user asks for help writing,
  refining, or improving a goal.
  Path: builtin://write-goal

当模型调用 Skill 工具请求 write-goal 时,kimi_code_skill.rs 会通过 include_str! 直接加载本文所讨论的这份 Markdown 文件,剥离 frontmatter 后注入到会话中:

let builtin_contents = match skill_name {
    "check-kimi-code-docs" => Some(include_str!("kimi_code_skills/check-kimi-code-docs.md")),
    "update-config" => Some(include_str!("kimi_code_skills/update-config.md")),
    "write-goal" => Some(include_str!("kimi_code_skills/write-goal.md")),
    _ => None,
};

加载的内容会被包装成 <kimi-skill-loaded name="write-goal" source="builtin" ...> 块,以 user 角色消息形式记录进会话历史,之后模型按技能正文中的规则执行。仓库中的测试 kimi_code_request_renders_kimi_code_builtin_skills(位于 kimi_code.rstests 模块)专门断言系统提示词包含 - write-goal: 且占位符 {{ KIMI_SKILLS }} 已被替换,保证这份文档确实随每次请求下发。

技能文档开头就给出了它的定位:目标不是一段任务描述,而是一份完成契约(completion contract)——它必须说清四件事:什么必须变成"真的"(what must become true)、这个"真"如何被证明(proven)、工作可以及不可触及的范围(where the work may and may not reach)、以及何时停下来汇报而不是继续空转(when to stop and report)。同时文档强调:起草和启动是两个独立步骤——先与用户敲定措辞,用户批准后模型才调用 CreateGoal 启动,且启动时还会再次弹出最终确认,用户始终保留"是否运行"的最后一票。

Ask, don't narrate:把选择题交给 AskUserQuestion

这是文档中自称"本技能最重要的一条规则":每一个交给用户做的决定都必须通过 AskUserQuestion 工具发出,没有例外

goal 撰写本质上是一连串选择:范围圈多大、措辞用哪种、要不要加预算以及多大、以什么权限模式启动。对其中每一个:停下来,调用 AskUserQuestion。相关的选择尽量合并进同一次 AskUserQuestion 调用里(工具本身支持一次问 1–4 个问题,见 kimi_code_tools.jsonAskUserQuestion 的参数 schema:minItems: 1, maxItems: 4,每个问题 2–4 个选项,系统自动追加 "Other" 选项)。文档明确禁止三种写法:

  • 写一段列出选项的散文,让用户用文字回复;
  • 说"如果你更倾向 A 或 B 请告诉我";
  • 把三个问题堆成一堵文字墙。

文档的理由很实际:文字菜单是"缺陷而非风格"——它更慢、容易被扫过去、且通常换来一个模糊回答,迫使再来一轮。唯一允许的纯文本提问场景是 AskUserQuestion 确实不可用(例如 auto 权限模式,或宿主不支持该工具),此时退化为一条带清晰标注选项的短消息并等待。注意边界:对开放式输入(例如"什么能证明这件事做完了?")用普通文字提问是允许的——这条规则只针对在选项之间做选择的场景。

AskUserQuestion 在 Kimi Code 工具集中是真实存在的第一方工具(见 kimi_code_tools.json),其描述还约定:若推荐某个选项,把它放在第一个并在标签后加 "(Recommended)";background=true 可后台提问、自动在后续回合收到答案。这些工程细节让 write-goal 的"提问纪律"有真实的 UI 载体。

Rules of engagement:五条协作守则

文档给出五条"交战规则",约束 agent 何时、以什么姿态介入 goal 撰写:

  1. 只在用户要求时提供帮助。 绝不主动把普通请求包装成 goal,也绝不自行启动 goal。"修一下这个测试"是普通请求,只有当用户明说要 goal 时才按 goal 处理。如果某个任务看起来适合 goal 模式,可以提一次——但等用户自己选。
  2. 用用户的语言写。 用户用什么语言交流,目标文本就草稿为什么语言;若项目配置或保存的记忆中指定了偏好语言,则优先遵循。周边讨论也保持同语言。
  3. 启动前先展示。 总是把完整的目标草稿原样呈现给用户并取得同意。用户读到的是将成为 objective 的精确文本,而不是对它的转述。
  4. 与用户共同起草,而非替用户起草。 goal 撰写是一场对话:给出草稿、解释你做的选择、邀请修改、吸收反馈,预期会超过一轮。
  5. 尊重用户的最终决定。 如果你已经指出模糊或风险之处,用户仍然想要更松更薄的 goal,就照他说的写。把取舍说明一次即可,不要反复重议,更不要背着用户"偷偷优化"措辞。

什么让一个 goal 变好:契约的五要素

文档的核心观点是:最强的 goal 定义的是"证明",而不是"努力"。 "持续提升代码质量"描述的是努力,永远不会结束;"npm test 退出码为 0 且 src/auth 之外没有任何文件被改动"描述的是证明,因此可被检查。一个合格契约应包含五个部分:

  1. 终态(End state)——必须变为真的那个条件。把终点具体命名:一个通过的测试套件、一个被清空的队列、一次零命中的搜索、一个已部署的产物。
  2. 证明(Proof)——终态成立的可观察证据。优先选 agent 能运行、你事后能核查的东西:命令的退出码、测试数量、grep/rg 零命中、一个现在存在的文件、一个越过阈值的指标。
  3. 边界(Boundaries)——工作可碰与不可碰的部分。点名范围(哪个模块、哪个目录),也点名禁区(不要编辑规范文档、不要改动无关文件、不要做破坏性数据变更)。
  4. 循环(The loop)——如果工作是迭代式的,说明如何迭代:每次改动后重跑检查、逐条处理队列项目、回放失败用例直到通过。
  5. 停止规则(The stop rule)——当"完成"不可达时如何诚实地收场。一句"扩大范围前先停下来问"的条款,加一条明确的受阻路径("如果外部服务宕机,记录下来然后继续下一项"),能让 agent 选择汇报而不是伪造通过或无限循环。文档特别强调:这是关于诚实性的,不是关于花钱上限的——它必须与预算分开表述。

两个几乎能改善任何 goal 的习惯:

  • 把目标做成队列形(queue-shaped)。 能收缩一个列表的目标效果最好:失败的测试、未关闭的 issue、错误堆栈、待迁移的文件、待处理的行。队列既给 agent 一份工作清单,也给你一个可计数的完成定义。
  • 依托已有的验证设施。 测试、CI、类型检查、lint、eval 套件、浏览器审计、零命中搜索都是杠杆——正是它们让 goal 可以无人值守运行且结果仍可被信任。如果一项任务没有任何方式证明完成,帮用户补一个,或者重新考虑 goal 模式是否适合这个任务。

还有一句值得记住的总结:跑得久不等于跑得好。 一份几轮就结束的紧凑契约,胜过一份每改一行代码就把整套测试重跑、烧上数小时的开放式契约。

Budgets are opt-in:预算必须用户主动要

goal 模式支持按回合(turn)或 token 计数运行,但文档的立场非常明确:不要默认设置预算,也绝不要把回合上限写进目标文本里。 一个规格良好的 goal 本来就会自己停下——证明通过、或撞上阻塞时——所以任意上限通常除了"有概率把活砍在半截"之外什么都做不了。

预算真正有用的场景是那些可能长时间无人值守运行的开放式/探索性目标。此时可以建议一个预算,但要围绕用户真正有感知的量来表述:token 成本。数值让用户自己选,你要做的只是对照工作量做一次合理性检查——一个远超任务所需的上限(例如一个几轮就能完成的目标配一千回合)不是安全网,只是招引浪费。如果用户要了一个看起来过大的数值,指出来并提供更小的替代,但仍尊重其最终决定。

五步工作流:从意图到 CreateGoal

文档给出的标准协作流程是:

  1. 理解意图。 问清用户真正想要的结果、以及什么能证明它完成了。如果缺终点线或缺检查项,这个缺口就是你们首先要一起解决的事。一旦开放问题收敛为具体选项,立刻用 AskUserQuestion 把它们摆给用户——不要用文字罗列。
  2. 起草目标。 用用户的语言写出具体目标,按任务规模覆盖上面契约中尽可能多的部分。保持可读:简单工作一两句即可,较大的工作用短的结构块(终态、检查、边界、停止规则)。
  3. 展示并解释。 完整呈现草稿并逐条过选择:终点线选了什么、什么能证明它、你圈出了什么禁区、何时停。指出仍然含糊的地方。
  4. 共同修订。 收下用户的修改,产出新草稿。当你在权衡不同措辞或范围时,把候选项作为 AskUserQuestion 的选项给出,而不是用文字描述。重复直到用户满意;如果他们想要的比你建议的更松,说明一次,然后写他们的版本。
  5. 启动它。 用户批准措辞后,调用 CreateGoal 并带上商定好的 objective(如果谈定了 completionCriterion 也一并传入)。不要只把文本打印出来让用户自己粘贴,也不要未经批准就启动。启动时仍会弹出最终确认,用户保留是否运行的最后一票。

可复用的目标模板

文档为中等复杂度以上的目标给出一个填空式结构:

<What must become true.>
Done when <command/search/state that proves it>.
Scope: only <files/area>; do not <off-limits action>.
Loop: <how to iterate — rerun the check after each change, etc.>.
If <blocking condition>, stop and report instead of forcing a pass.

说明同样重要:并非每个 goal 都需要每一行,而且其中任何一行都不是回合上限——goal 在证明通过或撞上阻塞时停止。小而范围清晰的任务可以只是一句清楚的话;随工作量增长、或一次跑错的自主运行的代价升高时,再逐步加上结构。

Weak to strong:三组对照示例

文档用三组"弱 → 强"示例演示契约化改写:

  • Find all bugs in this codebase.(找出这个代码库里的所有 bug)——没有终点线、没有证明、没有停止条件,agent 可能立刻卡住,也可能远远跑过你想要的范围。 Fix every test in test/auth that currently fails, rerun npm test until it exits 0, change no file outside test/ or src/auth, and report anything you cannot fix with its location and why.(修好 test/auth 中当前失败的每一个测试,重跑 npm test 直到退出码为 0,不改动 test/ 或 src/auth 之外的任何文件,并把无法修复的项连同位置和原因汇报出来)
  • Optimize the project.(优化这个项目)——没有范围、没有度量。 Migrate the payment module to the new API, make npm test -- payment exit 0, keep the diff limited to payment-related files, and stop and ask before touching shared infrastructure.(把支付模块迁移到新 API,使 npm test -- payment 退出码为 0,diff 限定在支付相关文件内,触碰共享基础设施前停下来先问)
  • Make it faster.(让它更快) Make renderFrame at least 3x faster measured by the bench/render benchmark; if you cannot reach 3x after several attempts, report the best result and why.(按 bench/render 基准测量,让 renderFrame 至少快 3 倍;若多次尝试后达不到 3 倍,汇报最好结果及原因)

Common mistakes:九条常见错误速查表

错误 更优做法
启动或建议一个用户没要求的 goal 只有用户开口后才起草 goal;其他时候最多提一次选项
用户用别的语言交流时却用英语起草 跟随用户语言(或项目/记忆中的偏好语言)
用户还没看过精确文本就运行 goal 先完整展示草稿并取得同意
违背用户明说意愿地"悄悄打磨"goal 取舍说明一次,然后写用户要的版本
把离散选择埋进散文里 用 AskUserQuestion 给出选项(不可用时用带标注的纯文本选项)
规格写的是努力("持续提升 X") 规格写的是证明("当检查 X 通过时完成")
把回合上限写进目标,或未经理由就设置预算 让 goal 靠自身证明停下;仅在有实用处建议预算,且围绕 token 成本表述
没有受阻路径 为阻塞条件加一条明确的"停下来并汇报"规则
goal 没有任何可验证完成的方式 把它锚定到测试、搜索、指标或其他可检查的依据

源码级佐证:write-goal 指导的工具如何真实执行

技能文档教的是"怎么写",而 Open Interpreter 仓库中的实现则定义了"运行时怎么管"。对照读一遍源码,可以看到文档里每条规则背后都有对应的机器约束。

CreateGoal 的参数与失败语义。 kimi_code_aliases.rs 中的 handle_create_goal 对应文档第 5 步"启动它":

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateGoalArgs {
    objective: String,
    completion_criterion: Option<String>,
    #[serde(default)]
    replace: bool,
}
  • objective 必须非空,否则返回模型错误 "Goal not created: objective must not be empty."——这正是技能反复强调"目标必须有可验证终态"的机器兜底;
  • completion_criterion 为可选,落库前会被截断到 4000 字符(criterion.trim().chars().take(4_000));
  • 若已存在当前 goal 且未传 replace=true,创建直接失败并提示 "Pass replace=true to replace it"——与 kimi_code_tools.jsonCreateGoal 描述一致:"Creating a goal fails if one already exists",也呼应技能"不要未经用户明确要求就放弃当前 goal"的纪律;
  • 创建成功后,goal 以 ThreadGoalStatus::Active 持久化到 state_db.thread_goals(),即跨进程重启可恢复——这就是"多回合、无人值守推进"的存储基础。

GetGoal 返回的快照与预算记账。 goal_snapshot 返回的结构恰好是技能所说的"可核查证据"的数据形态:

struct GoalSnapshot {
    objective: String,
    completion_criterion: Option<String>,
    status: &'static str,
    turns_used: u64,
    tokens_used: i64,
    wall_clock_ms: u64,
    budget: GoalBudgetSnapshot,   // 含 remaining_*、*_reached、over_budget
}

其中 tokens_used 是相对 goal 启动时 output token 基线的增量(current_tokens.saturating_sub(goal.output_tokens_at_start)),wall_clock_msstarted_at 计时。无 goal 时返回 { "goal": null },与工具描述一致。

SetGoalBudget 的合法性窗口印证"预算是 opt-in"。 handle_set_goal_budget 实现了技能文档"预算建议"部分的硬边界:

  • 数值必须为正且有限(value must be positive);
  • turns/tokens 单元向上取整且最小为 1;
  • 时间类单元(milliseconds/seconds/minutes/hours)统一换算成毫秒后必须落在 1 秒到 24 小时1_000..=86_400_000 毫秒)之间,否则返回 "is not a reasonable goal budget"。

这些边界与 kimi_code_tools.jsonSetGoalBudget 的描述逐条对应("A time budget must be between 1 second and 24 hours")。技能文档建议"围绕 token 成本表述预算",而工具枚举里 tokens 正是六个合法单元之一。

UpdateGoal 的三种状态与"诚实汇报"机制。 handle_update_goal 接受 active/complete/blocked 三态:

  • completeblocked 时,工具会取走当前 goal,输出一行统计("Worked N turns over X, using Y tokens")并指令模型写一条面向用户的收尾消息——complete 要总结做了什么、跑了哪些验证;blocked 要说明具体阻塞、以及需要什么输入才能继续,且"不要再调用任何 goal 工具"。这与技能里"停止规则是关于诚实汇报,而非伪造通过"的要求在运行时层面闭环;
  • 持久化侧,complete/blocked 会删除 state_db 中的 thread goal 记录,active 则把状态更新回 Active。

此外,kimi_code_tools.jsonUpdateGoal 的描述进一步定义了防滥用纪律:同一非终局阻塞条件必须连续至少 3 个 goal 回合重复才能调用 blocked("blocked audit"),而 complete 不允许仅仅因为"预算快烧完了"或"想停"就使用。这些正是技能文档"stop rule"从自然语言约定落到协议级约束的部分。

goal 扩展与 TUI 侧的 /goal 独立于 Kimi Code 兼容层,仓库还有一个专门的 goal 扩展 crate codex-rs/ext/goal,导出 GoalExtensionGoalServiceGoalRuntimeHandlePreviousGoalSnapshot 以及 CREATE_GOAL_TOOL_NAME 等常量,并带 accounting(记账)、steering(续跑引导)、metrics、events 等模块,配套 goals_migrations SQL 完成持久化。TUI 侧则有 goal_menu.rsgoal_files.rs(支持把 goal 写成文件、从文件写入 objective)与 ESC 中断后 goal 暂停的快照测试,说明 /goal 从起草、启动到暂停、续跑的完整交互链路在产品里是一等公民。

实践指引:如何在这个项目里用到 write-goal

  • 触发方式:在会话中直接向 agent 提出"帮我写一个 goal / 改进这个目标措辞",技能清单里 write-goal 的触发条件("Use when the user asks for help writing, refining, or improving a goal")会引导模型加载该技能;也可以理解 Skill 工具按 builtin://write-goal 路径读取的正是本文档所在的 write-goal.md
  • 阅读顺序建议:先掌握"五要素契约"与三组 weak→strong 示例(这是可迁移的通用写作方法);再看五步工作流与"Ask, don't narrate"规则(这是 agent 侧的交互协议);最后对照 kimi_code_aliases.rskimi_code_tools.json,把文本约定映射到 CreateGoalobjective/completionCriterion/replaceSetGoalBudget 的六个单位与 1 秒–24 小时窗口、UpdateGoal 的三态与 3 回合受阻审计上。
  • 适用前提与限制:上述行为属于 Kimi Code 兼容层与 goal 扩展的实际实现,参数名(camelCase 的 completionCriterion)、4000 字符截断、24 小时时间预算上限等均以当前仓库源码为准;若未来工具 schema 变更,以 kimi_code_tools.json 的最新内容为准。

小结

write-goal 技能的本质,是把"让 agent 自己去努力"这种无法验收的委托,改写成一份双方都能核查的完成契约:用可运行的检查替代形容词(证明替代努力),用队列和现有验证设施支撑无人值守,用显式停止规则保证撞墙时汇报而非硬撑,并让预算始终由用户按需开启。技能文档负责教模型"怎么写",而 CreateGoal/GetGoal/SetGoalBudget/UpdateGoal 的实现与持久化层负责保证"写出来的东西被如何执行与记账"——两者对照阅读,是理解该项目 goal 模式最完整的路径。

登录后查看全文
热门项目推荐
相关项目推荐