Qwen Code Goals 长程任务自治指南:/goal命令体系、预算窗口与证据验证机制
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
Goal(目标)是 Qwen Code 的核心自治机制:一条命令/goal <objective>让 Agent 跨多轮持续工作,直到客观条件达成、被独立验证器判定完成或受阻,或被预算窗口终止。本指南围绕 docs/users/features/goals.md 展开,结合仓库源码(goal-runtime.ts、goal-checkpoint.ts、settingsSchema.ts)深入讲解 Goals 的完整命令集、四种预算窗口、证据验证规则、目标写作方法论,以及/goal-draft技能与propose_goal审批流程。读完你不仅能熟练运行 Goal,还能写出可被验证器稳定判定、可防失控的优质目标文本。
Goal 是什么
Goal 让 Qwen Code 在达成某个明确条件之前持续工作,跨越多轮对话而不需要你逐轮催促。核心工作机制如下:
- 通过
/goal <objective>设定目标后,会话自动进入自治循环; - 每一轮(turn)的工作都被记录为证据(evidence)写入会话记录(transcript);
- 当模型认为目标已完成或受阻时,它会提交提案;
- 一个独立的验证器(verifier)仅依据记录在案的证据来评判该提案,不亲自执行任何命令或读取任何文件;
- 验证器接受提案、或 Goal 被暂停/清除/触达预算上限时,会话停止。
从源码结构看,这一整套状态机实现在 packages/core/src/goals 目录下,包括运行器 goal-runtime.ts、协议与常量 goal-protocol.ts、状态归约器 goal-reducer.ts、证据管理 goal-evidence.ts、验证器 goal-verifier.ts 与 checkpoint 压缩 goal-checkpoint.ts。
命令全集
| 命令 | 行为 |
|---|---|
/goal | 显示当前 Goal 及其状态。 |
/goal <objective> | 创建一个 Goal;若已有活跃 Goal,则替换之。 |
/goal set <objective> | 与上一条等价,显式形式。 |
/goal edit <objective> | 修订当前活跃 Goal 的措辞,无需推倒重来。 |
/goal pause//goal resume | 暂停或继续自治循环,不丢失 Goal。 |
/goal clear | 移除 Goal。 |
/goal-draft <intent> | 让内置技能先替你起草目标文本,再决定是否设置(见下文)。 |
创建、编辑或恢复(resume)一个 Goal 都要求受信任的工作区(先运行/trust)。无头(headless)场景的持久 Goal 用法参见 Headless Mode。
预算窗口:防止自治循环失控的四种护栏
1. Token 预算:model.goalTokenBudget
一旦 Goal 产生过计费轮次,底部状态胶囊(footer pill)和每张状态卡片都会显示它相对允许窗口已消耗的量,格式如1.2k/30.0m(已用/总额,单位千/百万 token)。该数字只统计 Goal 在自己轮次中发起的模型调用;子代理(subagent)与验证器自身的检查不计入。
- 默认值:30,000,000(3000 万)tokens。源码常量
GOAL_DEFAULT_TOKEN_BUDGET = 30_000_000定义于 goal-protocol.ts。注释说明这是一个"授权量子"而非成本估算——它界定一次显式用户动作(创建或之后某次 resume)愿意为多少自主延续买单,然后 Goal 停下并再次征询。 - 计量口径:按
tokensUsed指标上的totalTokenCount逐模型调用求和,即每次发送都会把完整输入上下文算一遍;而每轮的旁路查询和 checkpoint 验证调用不计量。 - 上限与防呆:合法值为
-1(无预算)、0(同样视为无预算,源码normalizeGoalTokenBudget将二者归一为Infinity)或1到300,000,000(默认值的 10 倍)。超出上限的值会被视为拼写错误拒绝并回落默认值,见 config.ts。 - resume 语义:恢复一个已耗尽窗口的 Goal,会在其已花费之上再授予一个新窗口,因此读数显示为
30.0m/60.0m而非重新归零。没有预算的 Goal 只显示已消耗量;尚未产生任何计费轮次的 Goal 则不显示任何数字。
2. 轮次上限:model.goalMaxTurns(默认关闭)
限制一个 Goal 可以完成的轮次数,包括由用户驱动的 Goal 轮次。到达上限的 Goal 会获得唯一一次"收尾轮"(wind-down turn)用于交接,然后停止,直到你 resume;resume 会在已用之上再授权一个新窗口。
- 取值为
-1(显式无上限,默认即无轮次上限)或1到 10,000(GOAL_MAX_TURNS_CAP,同样是防拼写错误护栏,见 config.ts);0被排除(excludedValues: [0])。 - 修改需要重启后生效,且只对修改之后新建的 Goal 生效——要约束已在记录上的 Goal,必须用
/goal set替换它(新 Goal 从 revision 1 开始,轮次、token、活跃时间计量全部重置,证据窗口从替换时刻重新起算,旧 Goal 的证据不再可引用),或清除后重新开始。
3. 活跃时间上限:model.goalMaxActiveMinutes(默认关闭)
限制 Goal 在运行进程中保持活跃状态的墙钟时间,包括轮次之间的等待与空闲。取值为-1(无上限)或1到 10,080(一周,GOAL_MAX_ACTIVE_MINUTES_CAP)。
精确语义(源码与 schema 描述一致,见 settingsSchema.ts):
- Goal 处于paused、blocked 或 stopped的时间不计数;跨重启的停机时间也不计数;但进程仅被挂起(suspended)仍会计费。
- 该窗口在轮次之间读取而非由定时器强制执行,所以 Goal 可能明显越过窗口才停下:已在运行中的轮次永远不会被中断;没有轮次在跑时窗口耗尽,也要等到下一轮结束才被发现。
- 活跃时间按记录到的状态转换之间计量,因此被重启打断的那一轮内的时间不收费。
- 无论哪个窗口先耗尽,Goal 都获得同一个收尾轮用于交接;resume 在已用之上再授权一个新窗口。只有耗尽的那个窗口前移,其余保持原位;
-1的退出开关同样只对已花费它的 Goal 解除上限(在随后的 resume 或 edit 上生效),仍在窗口内的 Goal 保留上限。resume 或 edit 永远不会给创建时未武装的目标添加上限。
4. Checkpoint 超时:model.goalCheckpointTimeoutSeconds
长期运行的 Goal 会周期性把已记录的证据压缩成 checkpoint 声明(claims),并伴随一次旁路模型检查(side model check),让后续轮次与验证器仍可引用。该检查的上限为model.goalCheckpointTimeoutSeconds,默认 180 秒,合法范围1到900(15 分钟)。900上限是硬编码的:流式调用的流保护(stream guard)默认生命周期上限为 15 分钟,超过后由保护器而非该设置终止检查,且提高流保护自身的上限也不会抬高900。调用是流式的,所以传输层超时(model.generationConfig.timeout,默认 120 秒)只约束建立连接和首个响应。
预算窗口背后的源码语义
从源码看,四个窗口的归一化与校验集中在 config.ts:无效值(如非整数、越界、负数中除-1外)会被记录到 debug 日志并回落默认值。goalMaxTurns与goalMaxActiveMinutes在 schema 中标记requiresRestart: true,与文档所述"修改需重启生效、只约束新建 Goal"一致。
运行器 goal-runtime.ts 通过isGoalTokenBudgetSpent、isGoalTurnBudgetSpent、isGoalActiveTimeBudgetSpent三个判定函数在每轮结束时检查窗口,并区分goalTokenBudgetReason、goalTurnBudgetReason、goalActiveTimeBudgetReason三种停止原因。这也解释了"窗口在轮次间读取"的实现:预算判定挂在轮次完成路径上,而不是独立的定时器。
每轮附带的工作指令
除了最终收尾交接轮之外,Goal 自主执行的每一轮都会在上下文中附带常驻指令:
- 报告 Goal 迄今为止的花费、已完成轮数,以及(除非无上限)允许的窗口;
- 重新检查工作区,而不是轻信前几轮的报告;
- 朝着目标文本要求的终态工作;
- 从第二轮起,若上一轮未改变任何东西,则本轮必须做点不同的事;
- 在提出"Goal 已完成"之前,用可引用的证据逐条核对每个要求。
这三条"轮次行为指令"由 goal-continuation-prompt.ts 生成,对应"无进展检测":连续三轮未记录任何可被验证器评判的内容且未提出提案时,Goal 会暂停——get_goal、update_goal这类账本读取不算进展。常量GOAL_NO_PROGRESS_TURN_LIMIT = 3定义在 goal-protocol.ts。
中断一个 Goal
- 取消当前轮次 = 暂停:在模型回答或工具仍在运行时按 Esc,当前轮停止,Goal 进入
paused状态,状态卡片和/goal都会说明停止原因。此后不会自动继续,直到你运行/goal resume。 - 输入消息 ≠ 暂停:Goal 活跃时直接打字不会暂停它,你的消息会作为下一个 Goal 轮次执行——所以用它来引导工作方向,而不是试图打断;要停止请用
/goal pause或/goal clear。
每一次暂停都会记录原因,包括:你中断了它、你运行了/goal pause、会话 token 限制阻止了下一个模型请求、轮次失败、或连续三轮没有记录到可评判内容且无提案。因预算上限停止的 Goal 会保留对应上限的原因。
验证器如何评判:证据即一切
验证器从不亲自运行命令或读取文件,它只看到 transcript 中已有的内容。评判规则:
- 可见的助手输出与工具结果算证据;目标文本、你的提示词、模型隐藏推理都不算。
- 打印出来的文本只证明文本被打印过。声称测试通过、文件已变更或远端已更新,必须在 transcript 里有对应的工具结果。
- 声称你确认、选择或批准过某事,需要一条真实来自你的消息;验证器会拒绝擅自假定用户同意的提案。
- 证据缺失时裁定为"尚未完成"(not yet)而非"完成"(done)。无人能证明的条件会让循环一直跑,直到某个上限叫停。
因此,目标文本必须迫使 Agent 产出证据:运行指定的检查并把决定性的输出行贴出来。验证器的输入结构与判定实现见 goal-verifier.ts,其输入仅由转录游标(transcript cursor)与证据目录组成。
写一个好目标:六段式模板
把以下部分按顺序写进目标文本:
| 部分 | 写什么 |
|---|---|
Outcome: | 一句话:完成时什么为真。 |
Done when: | 编号的、二值化的检查项。至少一项点名一个命令及其预期退出码或输出行,并要求粘贴该行。 |
Must not: | 不许触碰的文件、不许削弱的测试或阈值、不许采取的不可逆动作(push、delete、publish)。 |
Budget: | 建议性模型指令,何时放弃,如"20 轮后按受阻停止"。要强制约束,请在设置里配model.goalMaxTurns或model.goalMaxActiveMinutes,而不是写在这里。 |
On block: | 受阻时报告什么,以及必须由人做哪个决策。 |
Context: | 只写 Agent 在工作区里找不到的事实:分支、环境、先前的决策。 |
长度约束:一次只写一个目标。/goal set与/goal edit接受任意长度,但建议控制在约 1,200 字符以内——目标文本会在每一轮 Goal 轮次中重新发送。模型通过propose_goal提出的目标上限 1,500 字符。两个命令都会把换行折叠成空格,所以请用编号而非换行来组织条目。
Budget不是运行时约束:它只是给模型"何时停止并上报受阻"的指令,模型可能遵守也可能不遵守。要让运行时本身在轮次数或时长上停止,必须设置model.goalMaxTurns或model.goalMaxActiveMinutes;把二者写进目标文本既不会配置它们,也不会改变 Goal 的 token 预算。
弱目标 → 强目标对照
| 弱目标 | 失败原因 | 更强写法 |
|---|---|---|
| make checkout faster | 无阈值、无检查。 | Outcome: checkout p95 is below 250 ms. Done when: 1) npm run bench:checkout exits 0 and prints p95 < 250 (paste the line); 2) npm test exits 0. Must not: change the benchmark or skip tests. Budget: as model guidance, stop as blocked after 20 turns. On block: report the measured p95 and what blocks it. |
| clean up the auth module | "干净"没有证据。 | 问什么是可观察的:src/auth里零 lint 警告、某个覆盖率阈值、文件数量。 |
| ship the release | 不可逆,且需要人做决策。 | 收窄到可检查的发布前状态(tag 存在、npm run release:dry-run退出码 0),并在Must not写"不要 publish"。 |
| after I confirm the design | 验证器看不到从未发生的确认。 | 移到On block:,作为必须由人做的决策。 |
让/goal-draft替你起草
/goal-draft <what you want done>是内置技能(bundled skill),完成上述写作工作:
- 只读且克制:它只读足够确立范围与真实验证命令的工作区内容,不运行测试、不构建、不装依赖、不启动服务。
- 最多一轮提问:仅在关键选择不明确时问一轮问题,然后写出一份紧凑的目标,通常带 3–5 条完成检查(足够时更少)。显式要求会保留,不会为了凑数添加检查。
- 审计类目标的语义:完成意味着覆盖约定场景并报告证据(含已确认缺陷的复现步骤);"未发现缺陷"是合法结果。草稿不得凭空捏造最小场景数、证据文件数、探索轮数或缺陷数。
- 信息缺失的处理:若成功标准、命令、输入路径或关键决策无法确立,技能返回标记为"Needs clarification"的草稿,内含
<TODO: …>条目;它不会把该草稿提交审批,也不会打印可运行的/goal set或/goal edit命令。非必要默认项标记为[ASSUMPTION],不代替缺失的成功标准。这些约束逐条写入技能说明 SKILL.md。 - 绝不自行开工:目标就绪后,交互式终端或 Web Shell 会话可能弹出
propose_goal审批对话框;不支持该能力的客户端、无头运行、禁用该工具的会话、以及已有活跃 Goal 的会话,则改为打印一条命令供手动运行。交接说明会明确"草稿尚未被应用",不经你批准不会设置任何东西。
收紧已有目标:/goal-draft all tests pass and the lint is clean这样的调用会收紧现有目标——对活跃 Goal 的显式收紧请求产出/goal edit,替换请求产出/goal set;意图不明时,技能会在唯一一轮提问里包含这个选择。
批准模型提出的 Goal:propose_goal
在交互式终端或附带了客户端的 Web Shell 轮次中,模型拥有propose_goal工具(实现在 goal-tools.ts)。当/goal-draft完成、或你提出了跨越数轮的产出要求时,模型可以直接提议目标,而不是打印一行/goal set …供你复制。提议以审批对话框呈现完整目标文本:
- 批准:效果与
/goal set完全一致,且在当前轮结束时才生效(模型确认并停止,第一个 Goal 轮随后自动开始)。 - 拒绝:什么都不设置——模型只看到工具调用未被允许,其指令要求它不问原因、也不再提议同一目标。
- 审批绑定本轮:若该轮被取消或未走到终点,审批被丢弃,不会在后续消息或自动化轮次中被应用。
- 不可绕过:任何权限规则或审批模式(包括 YOLO)都不能跳过此对话框;另一个 Goal 活跃时、plan 模式下、未受信任目录中,工具一律拒绝;子代理永远不会被提供该工具。Web Shell 使用其既有的 Allow/Reject 权限面板。已停止的 Goal 只有在仍匹配审批所示版本时才可被替换;版本变化会使提案失效。
- 降级路径:无头运行、Web Shell 频道投递与自动轮次、以及缺少所需审批与轮次生命周期支持的 ACP 客户端,仍保留打印
/goal set的交接形式。
关闭此能力:在用户设置中设goals.modelProposed: "disabled"。由于该设置决定模型能否请求你启动自治循环,它只在 user 与 system 作用域被采纳;工作区.qwen/settings.json里的值会被忽略并给出警告。
技能的安全边界:goal-draft被指令为只读,只有其非变更类工具被自动批准(get_goal、read_file、glob、grep_search)。ask_user_question故意不自动批准,所以它的提问对话框会在技能根据你的回答起草之前显示。与其它内置技能一样,名为goal-draft的项目或个人技能会覆盖它,skills.disabled可关闭它。内置技能的发现机制见 Skills。
Checkpoint 压缩:长目标如何保住证据
长期运行的 Goal 会周期性把已记录证据压缩为 checkpoint 声明,供后续轮次与验证器引用。源码中这套机制有精确的边界(见 goal-checkpoint.ts 与 goal-checkpoint-verifier.ts):
- 声明上限:一次 checkpoint 最多 32 条声明(
GOAL_CHECKPOINT_CLAIM_LIMIT = 32,goal-protocol.ts);sourceRefs每条声明最多 32 个 id,id 必须是字符串且不重复。 - stall 上限:连续 3 次(
GOAL_CHECKPOINT_STALL_LIMIT = 3)检查停滞即停止 Goal,停止时点名最后一次检查遇到的问题。 - 纠错调用:当回复超出聚合字节预算、单条声明超字符上限、声明的数量超过一个 checkpoint 可容纳量、引用了请求中不存在的 id、或改变了所引用来源的 proof kind 时,做一次纠错模型调用并点名错误,两次调用共享同一个超时上限。回复若不是持有非空
claims数组的 JSON 对象则不纠错;任一声明畸形(任一层级多出键、未知proofKind、空声明、空的sourceRefs、含非字符串或空 id、重复 id)同样不纠错。 - 批量退化:stall 后对溢出窗口的复查不会重发同一请求,而是把证据按批次发送——
GOAL_CHECKPOINT_BATCH_RECORD_LIMIT = 24条一批(goal-checkpoint.ts),第二次 stall 后减半为 12,之后继续减半且不低于 1(checkpointBatchRecordLimit,见同文件 L92-L102),每个批次一次模型调用、各自受该超时上限约束。各批次的声明会携带到下一批,只保留最后一批的声明;失败的批次按单次调用失败处理,记录的失败信息会点名批次,如batch 2/5: …。因此 stall 后的检查最多可耗时上限的 5 倍,第二次 stall 后最多 9 倍,期间你发送的消息要等待。窗口还有余量时的检查、以及恢复会话在启动时重放的检查,仍单次发送(二者都不可能产生 stall)。 - 三类失败及对策:声明显然放不进窗口(完整声明列表仍遗留证据,或声明数/大小超预算)说明目标产生的证据超过单个窗口容量,收窄目标;回答无法折叠成声明说明 checkpoint 模型没有返回要求的结构化输出,收窄目标无济于事;从未回答则可能是 provider 不可达或限流、未在
model.goalCheckpointTimeoutSeconds内完成、或检查本身出错——记录的失败信息会说明是哪一种。这三种情形后 resume 都会开启全新的证据窗口。 - 可见性:活跃 Goal 的 stall 计数进行时,底部胶囊自行切换为
checkpoint N/3 stalled;Goal 暂停或停止后胶囊显示该状态。终端 Goal 状态卡片(如/goal或 pause/resume/verifier 卡片)显示连续停滞数(3 次中的几次)与最近一次失败;Web Shell 的 Goals 对话框与无头/goal文本输出显示同一行;Web Shell 的 Goal 事件转录卡片只显示停止原因。窗口仍有余量时的失败也会显示,但不消耗 stall——仅当 Goal 活跃时,或该失败本身就是停止原因时(如 checkpoint 请求过大无法发送)。其它原因导致的 checkpoint 停止会清除失败记录并保留 streak;已完成的 Goal 不显示 checkpoint 行。三条 check 停止的 Goal 会点名最后一条遇到的问题。
实操建议速查
- 开始:
/trust确认工作区受信任 →/goal-draft起草(或直接/goal set)→ 审批对话框确认 → Goal 自行运行。 - 引导:Goal 活跃时直接发消息作为下一轮指令;用
/goal pause、/goal clear停止。 - 防失控:长任务优先配置
model.goalMaxTurns与model.goalMaxActiveMinutes(重启后、对新 Goal 生效);日常默认 3000 万 token 预算已在 goal-protocol.ts 中就位。 - 可判定性:
Done when至少一条指名命令 + 预期输出 + 粘贴该行,让验证器有据可依。 - 证据不足时:验证器裁定 "not yet" 而非 "done",条件无法被证明时循环会持续到预算上限——这正是把不可判定的目标写进
Must not/On block的原因。
上述所有设置均通过用户级.qwen/settings.json的model段配置,完整的字段校验、默认值与取值范围以 settingsSchema.ts 为权威依据。
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考