/goal 目标系统¶
/goal 是 Codex 扩展架构里很好的示例:它把一个看似 prompt 层的需求,做成了持久化、可记账、可续跑、可被工具控制的系统。
三个工具¶
工具定义在 codex-rs/ext/goal/src/spec.rs:
get_goal:读取当前 thread goal,包括状态、预算、已用 token/时间。create_goal:显式创建目标,可选 token budget。update_goal:只能把目标标记为complete或blocked。
update_goal 的描述故意很严格:不能因为预算快用完就 complete,也不能随意 blocked;pause/resume/budget-limited/usage-limited 由系统或用户控制。
状态持久化¶
GoalToolExecutor 在 codex-rs/ext/goal/src/tool.rs 中实现。创建 goal 时,它会写入 codex_state::StateRuntime.thread_goals(),而不是只把目标放进对话历史。
状态字段包括:
- objective
- status
- token budget
- tokens used
- time used
这让目标可以跨 turn、跨 resume 存活。
Runtime 生命周期¶
GoalExtension 实现了多个 contributor:
ThreadLifecycleContributorConfigContributorTurnLifecycleContributorTokenUsageContributorToolLifecycleContributorToolContributor
它在 thread start 时初始化 GoalRuntimeHandle,在 thread resume 时恢复 active goal,在 thread idle 时尝试继续目标。
自动续跑¶
GoalRuntimeHandle::continue_if_idle 的流程:
- 检查 goal tools 是否可见。
- 加锁,避免外部 goal mutation 与续跑竞争。
- 从 state DB 读取当前 active goal。
- 构建
continuation_steering_item。 - 调用
thread.try_start_turn_if_idle(vec![item])。 - 如果线程忙、Plan mode 或有 pending trigger turn,就放弃本次自动续跑。
这说明 /goal 续跑不是强行插队,而是尊重线程调度和 Plan mode。
预算记账¶
GoalAccountingState 跟踪:
- 当前 turn id。
- turn 内 token baseline 和 delta。
- wall-clock active goal。
- 是否已经报告过 budget limit。
记账触发点包括:
- token usage event:记录当前 total token usage。
- tool finish:工具完成后结算增量,必要时注入 budget limit steering。
- turn stop/abort:结算 active goal 进度。
- turn error:把 active goal 标记为 blocked 或 usage-limited,避免错误循环继续耗 token。
Steering Item¶
codex-rs/ext/goal/src/steering.rs 把 goal 转成内部上下文 item:
- continuation
- budget limit
- objective updated
这些 item 使用 InternalModelContextFragment 注入模型,来源标记为 goal。这比普通用户消息更清楚:它是系统为了执行目标注入的 steering,不是用户新发的需求。
可学习的设计¶
/goal 展示了一个优秀扩展应该具备的边界:
- 用工具显式创建和终止目标。
- 用 state DB 持久化目标。
- 用 lifecycle contributor 记账和恢复。
- 用 idle continuation 续跑,但尊重线程调度。
- 用 steering item 和模型沟通目标状态。
- 错误、预算、usage limit 都有明确状态迁移。