Coding Agent Goal 机制详解
前两篇整理了 Claude Code 与 Codex 的 Memory 和上下文压缩:
Claude Code 与 Codex Memory 机制详解
Claude Code 与 Codex 压缩机制详解
本次聚焦 Goal 机制。
对比 Kimi Code、Codex、DeepSeek Harness (dsh) 和 Claude Code 的实现与文档,它们核心解决同一问题:如何让 Agent 持续执行任务直至目标达成?
解析 Goal 需关注三点:
Kimi Code、Codex、DeepSeek Harness 和 Claude Code 在 Goal 机制上大同小异。本文将其流程串联为主线,并在关键处剖析异同,供读者把握核心思路。
Goal 的整体流程
首先定义"turn"(回合):Agent 围绕一次输入持续工作的过程,期间可能多次调用模型和工具:
用户输入
→ 模型决定调用工具
→ 程序执行工具,返回结果
→ 模型继续思考和调用工具
→ 模型输出最终回复,本轮结束
例如让 Agent 进行数据库迁移。它修改完数据访问层并运行部分测试后,回复:“基础改造已完成,接下来需适配接口层。”此时单轮对话结束,但整体任务未完。
Goal 机制在此之上增加了一层控制:
设置并保存目标
→ Agent 执行一轮
→ 检查目标状态、完成情况和执行限制
├─ 可以继续 → 提交后续输入,继续当前会话
└─ 应当停止 → 完成、暂停或记录阻塞
上述四种 Coding Agent 均遵循此主线,差异主要体现在:状态存储方式、下一轮触发源头、任务完成判定者。
Kimi Code、Codex 和 dsh 提供了模型可调用的 Goal 工具;Claude Code 则将完成检查交由独立的评估模型处理(后文详述)。
目标如何创建和保存
#目标 Goal 状态
用户命令入口与模型工具入口可分离。
以 Kimi Code 为例,执行 /goal 完成数据库迁移 后,CLI 直接调用 session.createGoal() 保存目标,并将目标文本作为输入交给 Agent,无需模型先调用 CreateGoal。
模型侧仍保留对应工具,供其他场景使用:
| 项目 | 模型侧的 Goal 工具 | 值得注意的区别 |
|---|---|---|
| Kimi Code | CreateGoal、GetGoal、SetGoalBudget、UpdateGoal |
预算有独立设置工具 |
| Codex | create_goal、get_goal、update_goal |
创建时可传 token 预算;模型更新仅允许 complete 或 blocked |
| dsh | create_goal、get_goal、update_goal |
创建时可传自动续轮上限;更新需带目标 ID 与 revision |
这些工具操作的是程序状态,可概括为:
目标:完成数据库迁移
状态:active
限制:允许消耗 tokens 数、执行轮数或运行时长
消耗:当前已用资源
“任务进度”与“资源消耗”需区分:进度由模型判断,消耗由程序计量。
dsh 的 revision 机制颇为实用:模型读取 Goal 后携带版本号更新;若期间目标被修改,旧版本更新将无法覆盖新状态,避免冲突。
#状态放在哪里
上述状态追踪了目标进度与消耗,需同时存在于内存与持久化存储中,以便从旧 session 恢复。
Kimi Code 将 Goal 变更写入主 Agent 的持久化事件日志。
主要包含三个事件:
goal.create:创建目标
goal.update:更新状态、预算或累计消耗
goal.clear:清除目标
这些事件存入该 Session 下主 Agent 的 wire.jsonl。
goal.update 可仅携带部分字段(如仅更新 tokensUsed 或 status)。恢复时需按顺序重放事件,不可仅取最后一条 update 作为完整 Goal。
dsh 也采用 Session 事件,但组织形式不同。
goal/change 保存目标生命周期状态;自动续轮消息正式进入 Session 后,对应的 user/message 会推进轮数。
若 Coding Agent 中途退出,可通过 session 文件中的事件推导 goal 的真实状态。
Codex 直接将当前 Goal 保存在 SQLite。
Codex 独树一帜,将 Goal 状态追踪存储于本地 Sqlite 数据库(goals_1.sqlite)。thread_goals 表通过 thread_id 关联会话,保存目标、状态、token 预算与累计消耗等字段。读取时直接查询当前记录,不依赖重放聊天历史中的工具调用。
Claude Code 的 Goal 存储字段与写入路径因无对应新源码,暂不作推断。
Agent 停下来后,如何继续
假设数据库迁移完成第一部分,Agent 正常结束本轮 turn,但 Goal 仍未完成。
#轮次结束与空闲回调
Kimi Code 调用链直接,当前 turn 结束后:
handleTurnEnded()
→ 检查结束原因、当前目标与预算
→ launchContinuationTurn()
→ IAgentLoopService.submit({ message })
提交的消息简化如下:
{
role: 'user',
content: [{ type: 'text', text: '继续推进当前目标……' }],
origin: { kind: 'system_trigger', name: 'goal_continuation' }
}
消息角色为 user,但来源标记为程序触发。即以 user 口吻自动发送:"**当前任务进度 xxx,请继续**"。
Codex 将续轮置于线程空闲阶段:
on_turn_stop:结算本轮消耗
→ on_thread_idle
→ continue_if_idle()
→ start_turn_if_idle(...)
它从数据库读取 active Goal,检查是否允许自动继续,将 Goal 提示词包装成内部上下文输入并启动下一轮,触发来源标记为 goal。
dsh 由独立的 goal-round-driver 插件负责:
agent/status 变为 idle
→ requestDrive()
→ drive()
→ agent.followup(message)
其要求目标为 active、自动执行已激活、无竞争待处理输入且未达轮数上限。续轮消息 source 中包含 goalId、revision 和 round。
这些检查至关重要:自动续轮排队后,用户可能已修改目标或发送新问题。dsh 会在消息执行前再次核对,避免旧的续轮请求生效。
#Claude Code 的 Stop hook
Claude Code 官方将 /goal 描述为 Session 级的 prompt-based Stop hook:在模型准备结束时进行评估,决定是否允许停止。
通用 Stop hook 可返回阻止停止的理由,由程序反馈给模型以继续执行。
泄露源码显示基础机制:stopHooks.ts 将阻塞理由包装成消息;query.ts 在允许继续时将其追加到上下文,并 continue 查询循环。
下一轮模型会看到什么
程序保存 Goal 并不意味模型自动知晓状态。每次调用模型,仍需将必要信息放入请求。
#目标与预算重新进入上下文
Kimi 的续轮消息负责推动执行,内容概括如下(中文示意):
当前有一个 active Goal。
目标:完成数据库迁移。
已执行轮数、消耗 tokens、经过时间。
预算与剩余额度:……
本轮完成部分有效工作。
若有剩余工作则保持 active,正常结束并由程序继续下一轮。
全部要求完成并验证后,再调用 UpdateGoal。
它还要求模型检查用户是否明确提出预算。若用户说明“最多 10 轮”而状态未记录,需先调用 SetGoalBudget。
关键点:用户在自然语言中提出的预算限制,会通过工具调用真正写入系统内存/session。
Codex 直接在续轮提示词中放入 objective、tokens_used、token_budget 和 remaining_tokens。dsh 的 renderGoalRoundPrompt() 则将目标和 Round: 当前轮数/上限 一起写入续轮消息。
Claude Code 的反馈还包含评估器给出的未完成理由,作为后续工作指引。
#继续工作,也要防止目标缩水
Kimi Code 和 Codex 的提示词均强调:不可因预算快用完或完成小阶段,就将整个目标标为 complete。
Codex 还要求区分实际进展、经验证的等待和无进展。例如等待后台任务时需确认任务仍在运行,不能仅凭聊天记录反复声称“还在等待”。
dsh 的提示词较短,同样要求根据当前工作区、工具结果和持久化状态判断,完成前需读取当前 Goal。
这些 Prompt 价值在于防止持续执行中的常见问题:模型为结束当前工作,将“完成全部迁移”悄悄缩小为“完成基础改造”。
Goal 的原始目标与完成要求需不断保持在上下文中。
谁来判断目标完成了
#执行模型通过工具声明完成
Kimi Code、Codex 和 dsh 路径中,主 Agent 模型根据执行结果调用 Goal 更新工具。
以 Codex 为例:
{
"status": "complete"
}
程序验证参数、当前状态和预算后保存变更。
这主要靠提示词要求模型自查:原始目标有哪些条件,哪些已验证,还缺哪些证据。Kimi 和 Codex 均明确禁止将计划、总结或部分结果视为完成。
#Claude Code 额外评估一次
Claude Code 使用独立的小模型评估目标,返回未满足、已满足或不可能满足的判断;后两种结果会清除目标并记录。评估器仅查看对话证据,不读取文件或运行测试。
此设计将执行与判断分离,初现 multi-agent 雏形,契合其博客中宣传的 executor 与 evaluator 分离理念。
预算
除执行流程外,Goal 还需约束,即预算。为防止无休止执行,需人为设定 token、轮次或时间预算。各 Coding Agent 可设置的预算如下:
| 项目 | 本文检查到的 Goal 限制 |
|---|---|
| Kimi Code | tokens、turns、运行时间 |
| Codex | token 预算,另有耗时统计 |
| dsh | 自动 Goal 续轮次数,默认上限 256 |
同为 token 预算,Kimi 与 Codex 计算方式不同。
Kimi 的 Goal usage 读取主 Agent 的输出 token 数。Codex 公式为:
Goal token 消耗 = 输入 tokens − 缓存输入 tokens + 输出 tokens
Codex 还会将相关 sub-Agent 的 usage 计入 Agent 计账状态,不同 Agent 计算口径不一。
Claude Code 文档建议将时间或轮数要求写进完成条件,由评估器判断。
写在最后
Goal 是 Coding Agent 向长程任务的试探。虽在开发中个人更倾向于手动输入“继续”,但对比四套机制后可见,Goal 的核心逻辑清晰:保存目标,在合适节点检查状态,自动提交后续输入直至完成。这比单纯输入“继续”更具目标一致性。
Goal 实现大同小异,真正影响效果的是细节:预算计量方式、用户插话后旧请求的处理、会话恢复是否自动运行,以及模型声明完成的证据标准。

