OpenClaw Goal 实战指南:会话级目标的 /goal 命令、模型目标工具与 Token 预算机制
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
本文以 OpenClaw 的 Goal(会话目标)功能为主体,完整覆盖 docs/tools/goal.md 中定义的所有实操内容——/goal命令族、六种状态机、token_budget预算机制、模型侧三个目标工具、Control UI 的 Gateway 协议与重试契约、TUI 展示与排障方法——并结合 OpenClaw 源码(目标存储、状态迁移、协议 Schema)逐项验证其底层实现,帮助你在长会话中为目标驱动的 Agent 工作建立可见、可控、可审计的目标管理方案。
Goal 是什么:会话级持久目标
在 OpenClaw 中,goal(目标)是附加在当前会话上的一条持久性目标。它为 agent 和操作者提供了一个长期工作的共同指向,但刻意不把它变成后台任务、提醒、cron 作业或常设指令。
从源码结构看,Goal 是典型的会话状态:它挂在会话条目(SessionEntry.goal字段)上,随 session key 迁移,能跨进程重启存活,并出现在三个地方——/goal命令输出、面向模型的目标工具(goal tools)、TUI 底部状态栏。
一个值得注意的设计细节:脱离式(detached)命令执行完成后,其结果会回到发起它的用户可见线程,因此即使命令执行本身使用了独立的沙箱策略会话,下一轮对话依然能看到同一个 goal。
快速上手
最简使用方式:
/goal start get CI green for PR 87469 and push the fix /goal /goal edit get CI green for PR 87469, push the fix, and update docs /goal pause waiting for CI /goal resume /goal complete pushed and verified /goal clearstart其实是可选的:/goal get CI green for PR 87469同样会创建目标。OpenClaw 会把/goal后面所有不是已知动作词的文本都当作新目标文本。
两个行为细节:
- 显式动作(
start、edit)会保留目标文本中的换行、缩进和连续空格; - 目标文本的首尾空白会被裁剪(trim),这与 src/config/sessions/goals.ts 中
createSessionGoal对options.objective.trim()的处理一致,且空目标会直接抛出objective required错误。
Goal 的适用场景:什么时候该用,什么时候不该用
当会话存在一个需要在很多轮对话中保持可见的具体成果时,适合使用 goal:
- PR 收尾:修复、验证、自动评审、推送、创建或更新 PR;
- 调试任务:复现 bug、定位负责的模块、打补丁、证明修复有效;
- 文档补写:读相关文档、写新页面、加交叉链接、验证 docs 构建通过;
- 维护任务:检查现状、做有边界的修改、跑对检查、汇报变更。
反过来,goal 不是任务队列。如果工作应该脱离会话运行、按计划重复、扇出成受管理的子任务、或作为持久策略存在,应当使用 Task Flow、tasks、cron jobs 或 standing orders(相关文档位于 docs/automation/ 目录)。
命令参考
/goal不带参数时,打印当前目标摘要。实际输出格式由 src/config/sessions/goals.ts 的formatSessionGoalStatus生成:
Goal Status: active Objective: get CI green for PR 87469 and push the fix Tokens used: 12k Token budget: 12k/50k Commands: /goal edit <objective>, /goal pause, /goal complete, /goal clear完整命令表如下:
| 命令 | 效果 |
|---|---|
/goal或/goal status | 查看当前目标 |
/goal start <objective> | 为当前会话创建新目标 |
/goal set <objective>、/goal create <objective> | start的别名 |
/goal <objective> | 同样创建新目标(任何非动作词文本) |
/goal edit <objective> | 改写当前目标文本;状态和 token 记账保持不变 |
/goal pause [note] | 暂停一个 active 目标 |
/goal resume [note] | 恢复 paused、blocked、usage_limited 或 budget_limited 目标 |
/goal complete [note] | 标记目标达成 |
/goal done [note] | complete的别名 |
/goal block [note] | 标记目标受阻 |
/goal blocked [note] | block的别名 |
/goal clear | 从会话中移除目标 |
关键约束与限制:
- 一个会话同一时刻只能有一个 goal。再启动第二个会失败并报
Goal error: goal already exists,直到当前目标被清除。这一点在 src/config/sessions/goals-transitions.ts 的buildCreatedSessionGoal中得到印证:只要entry.goal已存在,就抛出SessionGoalTransitionError("goal already exists")。 /goal start不接受 token-budget 参数。只有模型侧的create_goal工具可以设置预算(见下文)。/new和/reset会清除当前会话的目标,因为它们的语义就是开启全新的会话上下文。
命令的解析与执行入口在 src/auto-reply/reply/commands-goal.ts,其状态输出逻辑与上文formatSessionGoalStatus的逐行结构一一对应。
六种状态:状态机与恢复规则
| 状态 | 含义与恢复方式 |
|---|---|
active | 会话正在 pursuit 该目标 |
paused | 操作者暂停了目标;/goal resume恢复为 active |
blocked | agent 或操作者报告了真实阻塞;有新信息或状态变化后/goal resume可恢复 |
budget_limited | 达到配置的 token 预算;/goal resume以全新的预算窗口从同一目标继续 |
usage_limited | 为未来 usage-limit 停止状态保留;恢复方式同上 |
complete | 目标达成,终态;必须/goal clear后才能开始新目标;重复 complete 会保留首次完成时间(即使再次附加状态备注) |
源码中的状态集合与协议 Schema 完全一致。SessionGoalSchema在 packages/gateway-protocol/src/schema/sessions-goal.ts 中定义,status字段是一个六值联合(active / paused / blocked / usage_limited / budget_limited / complete),整个 schema 还携带tokenStart、tokenStartFresh、tokensUsed、tokenBudget(可选)、continuationTurns以及各状态的时间戳(pausedAt、blockedAt、completedAt、usageLimitedAt、budgetLimitedAt)——这些字段解释了/goal输出中 "Tokens used" 与 "Token budget" 两行的数据来源。
complete的终态约束在 src/config/sessions/goals-transitions.ts 中实现:已 complete 的目标拒绝任何非 complete 的状态变更;且completedAt取首次完成时间(accounted.completedAt ?? now),这解释了"重复完成保留原始完成时间"的文档承诺。
Token 预算:记账原理与 budget_limited 迁移
Goal 可以带一个可选的正整数 token 预算,只能通过create_goal工具的token_budget参数设置。预算的记账规则在 src/config/sessions/goals-transitions.ts 的accountSessionGoalUsage中实现,要点有三:
- 基线选择:预算从目标创建时刻会话的"fresh"(新鲜)token 计数起算(
tokenStart)。如果建目标时会话只有过期或未知的 token 快照(tokenStartFresh为 false),OpenClaw 会等待下一个 fresh 快照再采用为基线——目标存在之前消耗的 token 不会记到目标头上。 - 用量计算:
tokensUsed = max(已记录用量, freshTotal - tokenStart),即"已记账用量"与"fresh 总量减去基线"取较大者,避免快照回退导致用量倒退。 - 自动迁移:只要目标是
active、配置了预算且tokensUsed >= tokenBudget,状态立即迁移到budget_limited并记录budgetLimitedAt。
达到预算不会删除目标或擦除目标文本,它只是告诉 agent 和操作者:在恢复或清除之前,目标不再被主动 pursuit。/goal resume会从当前 fresh token 计数开启新的预算窗口——源码中(src/config/sessions/goals-transitions.ts)明确在 resume 时重置tokenStart为当前 fresh 总量、tokensUsed归零,并清除budgetLimitedAt/usageLimitedAt标记。
两个实践要点:
- 模型应当省略
token_budget,除非你明确要求设置预算;对于要求所有工具参数必须显式给出的传输层,可传null表示无预算(工具 Schema 中token_budget的可选类型正是integer(minimum: 1) | null,见 src/agents/tools/goal-tools.ts)。 - Token 预算是会话目标的护栏,不是计费上限。Provider 配额、成本报告、上下文窗口行为仍走 OpenClaw 常规 usage 与模型控制。
模型目标工具:get_goal / create_goal / update_goal
OpenClaw 向 agent harness 暴露三个目标工具,实现集中在 src/agents/tools/goal-tools.ts:
| 工具 | 用途 |
|---|---|
get_goal | 读取当前会话目标:完整目标文本、状态、token 用量、可选预算 |
create_goal | 仅当用户或系统指令明确要求时创建目标;会话已有目标则失败 |
update_goal | 将目标标记为complete或blocked |
权限边界:模型不能静默地 pause、resume、clear 或替换目标——这些保留给操作者(通过/goal与 reset 命令)。因此 agent 可以报告"达成"或"真实阻塞",却无法悄悄移动目标本身。这个边界在源码中是硬编码的:MODEL_UPDATABLE_SESSION_GOAL_STATUSES只含["complete", "blocked"](src/config/sessions/goals.ts),update_goal的工具参数 Schema 直接由该常量生成枚举,任何其它状态值都会抛出status must be one of complete, blocked的输入错误。
update_goal的使用准则(写进了工具 description,src/agents/tools/goal-tools.ts):
- 只有当完整目标被逐项验证、且无遗留必做工作时才标记
complete; - 只有当同一阻塞条件连续至少三个 goal turn 重复出现、且不靠用户输入或外部变化无法取得进展时,才标记
blocked;普通难度或"还差一点打磨"不算; - blocked 目标被 resume 后,连续计数从 3 重新起算,此前的 blocked turn 不计入;
- 预算几乎耗尽不构成把未完成工作标记 complete 的理由;
- 更新目标状态不会向用户发送任何回复——agent 仍须提供用户要求的最终可见回复。
两个实现细节值得注意:
update_goal成功后返回体里带nextAction提示,明确要求模型"继续本轮并给出可见最终回复"(src/agents/tools/goal-tools.ts);- 当没有需要变更的活动目标时,工具捕获
SessionGoalTransitionError并返回status: "error"加"不要重试 update_goal"的指引,而不是抛出异常——这让模型能优雅地结束本轮(src/agents/tools/goal-tools.ts)。
测试覆盖可参考 src/agents/tools/goal-tools.test.ts。
每轮注入的 Goal 上下文
每个带 active goal 的用户/聊天轮次都会注入一行 user 角色上下文:
Active goal: <objective> — advance; keep active until fully achieved; block only after the same blocker on 3 consecutive turns; after update_goal, provide the requested visible final.行为规则:
- 为保持紧凑,长目标文本会被截断;
paused、blocked、budget_limited、usage_limited、complete状态的目标不注入——操作者的"停止"意图会一直生效,直到目标被 resume。
Control UI:Goal Composer 与 Gateway 协议契约
Web Control UI 中的 Goal 交互由 Gateway 的结构化能力支撑,其请求/响应契约的权威定义在 packages/gateway-protocol/src/schema/sessions-goal.ts。
Goal Composer:在命令选择器中选Goal、输入目标文本并发送;composer 会显示 Goal 标签,让你看到 Send 将执行的动作。目标文本是字面量:诸如clear这样的词、/stop这样的文本在 Goal 模式下不会变成命令。取消会把文本保留为普通聊天草稿。
发送时序保证:Start Goal 会把 Goal、其 user turn 和运行准入(admission)一起保存后才确认 Send;准入失败则草稿原样保留、不创建 Goal。Start 与 Resume 要求本地会话空闲且历史可恢复,不做排队或转向其他 run;UI 对不支持或忙碌的会话直接报错,而不是创建一个不活跃的 Goal。
目标 Pill:聊天 composer 上方显示紧凑 pill——状态图标、状态标签(如Pursuing goal)、截断目标、实时耗时计时器。内联控件:
- 铅笔:打开 Edit Goal composer;保存只改目标文本,取消恢复之前的聊天草稿;
- 暂停/恢复:更新当前 Goal;Resume 会经由正常聊天准入启动续跑,其内部输入留在模型历史中、不呈现为人工聊天消息,但助手回复可见;
- 垃圾桶:清除当前 Goal;
- 展开箭头:展开显示完整目标、最新状态备注、token 用量与耗时。
Edit、Pause、Clear不发送斜杠命令、不添加聊天轮次;控件指向展示时的 Goal ID,因此陈旧按钮无法误改被替换的新 Goal。请求中断时原样重试;成功重放会刷新当前状态而非恢复旧 Goal 快照。无连接时操作按钮不可用,但展开箭头仍可用;并发 Goal 操作在操作 pending 期间会被拒绝。这些控件要求 Gateway 声明了结构化 Goal 能力;文本/goal命令在 CLI 等其它命令可用表面上始终可用。
Gateway 请求与重试契约(与协议 Schema 逐项对应):
- 启动:Goal start 走
chat.send,普通message即目标文本,另带intent: { kind: "session-goal-start", version: 1, issuedAtMs };保留常规idempotencyKey、附件与回复字段;拒绝按请求的运行时或投递路由覆盖——Goal 工作统一使用会话设置与本地投递,使恢复保持同一契约。目标文本必须含非空白内容,且上限 16,000 字符(对应 Schema 中objective的maxLength: 16_000)。 - 更新/清除:
sessions.goal.update接受edit(带objective)或pause/resume/block/complete(带可选note,上限 2,000 字符);sessions.goal.clear删除 Goal。两类方法都要求sessionKey、goalId、operationId、issuedAtMs;agentId与sessionId可选用于精确锁定目标。两者要求正常的会话参与权限和operator.writescope(服务端实现见 src/gateway/server-methods/sessions-goal.ts,测试见 src/gateway/server-methods/sessions-goal.test.ts)。 - 重试与幂等收据:重试时保持原 operation ID、时间戳、目标与载荷不变。收据自
issuedAtMs起24 小时内有效;早于 Gateway 时钟 5 分钟以上的时间戳被拒绝;同一 ID 复用于不同请求被拒绝;过期请求不能重建已清除的 Goal。每会话上限4,096 条未过期收据,达到上限时拒绝新操作直至收据过期,而不是驱逐有效重试状态。 - 结果字段:结果含
operationId、action、sessionId、goalId、status(started/updated/cleared,与 协议 Schema 的SessionsGoalMutationResultSchema一致),以及存在时的最终goal和 start/resume 时的runId。重放会附加replayed: true——注意它是原始操作的结果而非当前 Goal 状态,重放后应刷新会话。收据防止重复 Goal 变更与输入轮次,但不承诺外部工具或 Provider 副作用的 exactly-once。
TUI 底部状态栏
TUI footer 在 agent、session、model 字段之后、token/mode 指示器之前,保持当前会话目标的可见性:
Pursuing goal (12k/50k)——active 且带预算的目标;Goal paused (/goal resume)——paused;Goal blocked (/goal resume)——blocked;Goal hit usage limits (/goal resume)——usage_limited;Goal unmet (50k/50k)——budget_limited;Goal achieved (42k)——complete。
footer 刻意保持紧凑,完整目标文本、备注、token 预算与可用命令请用/goal查看(展示逻辑见 src/shared/session-goal-display.ts)。
多通道行为与排障
通道行为:/goal在所有支持命令的 OpenClaw 会话中可用(包括 TUI 与允许文本命令的聊天表面)。Goal 状态挂在 session key 上而非传输层上,因此共享同一 session key 的两个表面会看到同一个 goal。Goal 状态不是投递指令:它不会强制回复走某个通道、不改变队列行为、不批准工具、也不调度工作。
排障速查表:
| 消息 | 含义 |
|---|---|
Goal error: goal already exists | 会话已有目标。用/goal查看;完成了就/goal complete;想换目标先/goal clear |
Goal error: goal not found | 会话还没有目标。用/goal start <objective>创建 |
Goal error: goal is already complete | 目标是终态。清除后才能开始或恢复新目标 |
如果 token 用量显示0或看起来过期,说明当前活跃会话还没有 fresh token 快照;用量会在 OpenClaw 记录会话 usage 与从转录(transcript)推导的总量时刷新。对应源码逻辑即前文tokenStartFresh的基线采纳机制:只有 fresh 快照到达后,记账才会切换为实时总量差值。
延伸阅读
- 斜杠命令总览:docs/tools/slash-commands.md
- 自动化体系(Task Flow、cron、standing orders):docs/automation/
- Goal 状态迁移的单元测试:src/config/sessions/goals.test.ts、src/config/sessions/goals-operations.test.ts
- Gateway Goal 能力声明:packages/gateway-protocol/src/server-capabilities.ts
适用前提:本文所述命令、协议字段与限额(16,000 字符目标、2,000 字符备注、24 小时收据窗口、4,096 收据上限)均以当前仓库中 docs/tools/goal.md 与 packages/gateway-protocol/src/schema/sessions-goal.ts 的定义为准;Control UI 的结构化 Goal 控件依赖 Gateway 声明对应能力,纯 CLI 环境请优先使用文本/goal命令。
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考