
Qwen Code /loop 技能深度解析循环 Prompt 的固定间隔调度与自节奏唤醒实现【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本文基于 Qwen Code 内置技能文档 SKILL.md完整拆解/loop命令的输入解析规则、固定间隔cron路径、自节奏LoopWakeup路径、loop.md任务文件模式与自主Autonomous模式五大执行路径并结合 loop-wakeup.ts、cron-create.ts 与 cronScheduler.ts 的源码讲清楚每条路径背后的调度机制、参数边界与安全约束。读完本文你将掌握/loop的全部用法并理解它如何用“秒级一次性唤醒 分钟级 cron 任务”两套引擎实现可长期运行的自主工作循环。技能定义与工具白名单/loop是 Qwen Code 的内置bundled技能其声明位于 packages/core/src/skills/bundled/loop/SKILL.md 的 frontmatter 中--- name: loop description: Create a loop that runs a prompt now and follows up either on a fixed schedule or through self-paced wakeups. Usage - /loop check the build, /loop 5m check the build, /loop check the PR every 30m. /loop list to show jobs, /loop clear to cancel all. argument-hint: [interval] [prompt] | list | clear allowedTools: - cron_create - cron_list - cron_delete - loop_wakeup ---frontmatter 中的allowedTools限定了技能执行期间可调用的工具集合cron_create创建定时任务、cron_list列出任务、cron_delete删除任务与loop_wakeup自节奏唤醒原语。这一白名单由测试 SKILL.test.ts 显式断言确保技能文档与工具集始终一致。从源码结构看这四个工具对应两套独立的调度引擎cron 路径cron_create/cron_list/cron_delete分钟粒度、可持久化到磁盘的周期或一次性任务核心实现在 packages/core/src/services/cronScheduler.ts自节奏路径loop_wakeup秒级精度、仅存在于会话内存中的一次性唤醒实现在 packages/core/src/tools/loop-wakeup.ts。子命令list 与 clear输入在去掉/loop前缀后如果恰好是以下关键词之一则执行子命令而不是调度list— 调用 CronList 并展示结果结束。clear— 先调用 CronList然后对返回的每一个任务调用 CronDelete并确认取消了几个任务结束。对应的 CronList/CronDelete 工具共享同一个调度器实例CronScheduler.list()会同时返回 cron 任务和待触发的 loop wakeupwakeup 通过wakeupToJob映射成最小CronJob形状cronExpr标记为wakeup因此list能看到两类任务clear也能一次性取消全部。输入解析四种路径的分流规则解析发生在技能文档的 “Parsing” 一节。去掉/loop前缀后按以下顺序判断空输入没有 prompt 也没有 interval走自主路径autonomous path运行一个自节奏的自主循环。前导 interval 记号第一个以空白分隔的记号匹配^\d[smhd]$如5m、2h则走固定间隔重复路径其余部分为 prompt。尾部 “every” 从句否则如果输入以every Nunit或every N unit-word结尾如every 20m、every 5 minutes、every 2 hours走固定间隔重复路径提取出 interval 后从 prompt 中剔除。注意只有 “every” 后面跟着时间表达式时才匹配——check every PR中没有 interval。纯 prompt 输入其余情况整个输入都是 prompt走纯 prompt 的自节奏路径。如果给了 interval 但 prompt 为空如/loop 5m则是固定间隔的自主循环见下文 Autonomous mode 一节。文档给出的解析示例输入解析结果依据5m /babysit-prs固定间隔5mprompt/babysit-prs前导 interval 记号check the deploy every 20m固定间隔20mpromptcheck the deploy尾部 every 从句run tests every 5 minutes固定间隔5mpromptrun tests尾部 every 从句词形单位check every PR纯 prompt 自节奏路径promptcheck every PRevery 后不是时间表达式check the deploy纯 prompt 自节奏路径promptcheck the deploy无 interval空自节奏自主循环哨兵autonomous-loop-dynamic空输入5m固定间隔自主循环interval5m哨兵autonomous-loop只有 interval纯 prompt 自节奏路径该路径仅在用户给了 prompt 但没给 interval 时使用其执行步骤为不调用 CronCreate。该路径不使用 cron 引擎。如果本次 tick 以一个task-notification块开头说明是监控器或后台事件重新唤醒了你而不是裸的/loop唤醒 prompt先处理该事件再重跑 prompt若通知表明被监视的条件已满足如果还持有其 ID用 CronDelete 取消任何待处理的 fallback LoopWakeup然后结束循环。若 Monitor 因空闲或 max-events 自动停止只要监视仍然有用就重启一次重新布防 fallback向用户报告重启次数并把该计数写进 LoopWakeup 的 prompt 或 reason 中例如monitor restarted 1/1 time使其能在上下文压缩context compaction之后存活。若下一个 tick 它又自动停止则结束循环并向用户报告重复的自动停止。若信号含义模糊重新布防一个更短的后续检查在下一个 tick 继续调查若连续三个 tick 信号仍然模糊结束循环并报告无法得出明确结论。立即执行解析出的 prompt如果是 slash 命令通过 Skill 工具调用否则直接执行。在结束当前轮次前判断是否还有下一次有用的检查只有持续跟进有用时才调用 LoopWakeup任务已完成则不调用任务被阻塞在用户输入或无法稍后检查的外部状态上则不调用不存在有用的下一次检查时不要仅仅为了维持轮询而调用。如果你启动了后台 agent 或 Monitor它们会在退出、失败、取消或 Monitor 自动停止时通过终端的task-notification唤醒你 —— 所以 LoopWakeup 应设为长周期 fallback而不是短轮询。不要仅仅因为“有东西在监视”就省略它工作可能挂死Monitor 可能在空闲或 max-events 时自动停止而且另一个 agent 拥有的 Monitor其通知只路由给那个 agent。只有上述终止条件完成、被阻塞、重复的 Monitor 自动停止才省略 LoopWakeup。调度续跑时调用 LoopWakeup 并传入delaySeconds下一次有用检查的秒数。运行时钳制到 60–36001–60 分钟取值应遵循工具自身的指引 —— 它会考虑 prompt-cache 窗口以及后台任务会唤醒你时 fallback-heartbeat 的取值prompt/loop ${原始 prompt}外加下一个 tick 必须保留的状态如monitor restarted 1/1 timereason简短说明所选延迟的原因若在自动停止后重新布防把 Monitor 重启计数也写在这里。用简短的话告诉用户刚才做了什么。如果调度了唤醒说明预计下次检查时间如果因通知结束循环而没有调度唤醒说明陈旧的 fallback 是否已被取消若唤醒 ID 已丢失当陈旧唤醒触发时简要忽略或回应即可。LoopWakeup 工具的实现边界技能文档中“delaySeconds 钳制到 60–3600”这一约束由源码精确兑现。packages/core/src/tools/loop-wakeup.ts 中参数 schema 要求delaySecondsnumber与promptstring最长 10000 字符reason可选delaySeconds的描述明确给出取值策略60–270 秒仅用于主动轮询没有任何其他上报机制的外部状态CI 运行、远端队列以保持在约 5 分钟的 prompt-cache 窗口内当有后台任务会通过task-notification唤醒你时这里应取1200–1800 秒作为 fallback任务挂死、Monitor 自动停止、或通知被路由给其他 agent 的情形无特定信号可监视时默认1200 秒以上执行时若 scheduler 已被禁用本会话 token 达到上限触发的断路器会返回“Loop wakeups are disabled for the rest of this session”的错误而不是静默失败。钳制逻辑位于 cronScheduler.ts 的clampWakeupSecondsexport const WAKEUP_MIN_SECONDS 60; export const WAKEUP_MAX_SECONDS 3600; const WAKEUP_DEFAULT_SECONDS 1200; const WAKEUP_CHAIN_MAX_AGE_MS 24 * 60 * 60 * 1000; export function clampWakeupSeconds(delaySeconds: number): number { if (!Number.isFinite(delaySeconds)) return WAKEUP_DEFAULT_SECONDS; return Math.min( WAKEUP_MAX_SECONDS, Math.max(WAKEUP_MIN_SECONDS, Math.round(delaySeconds)), ); }即非有限输入回落到 1200 秒默认心跳其余输入四舍五入后钳制到 [60, 3600] 区间。此外scheduleWakeup还实施了一条文档未展开的硬约束自节奏唤醒链受24 小时会话预算限制WAKEUP_CHAIN_MAX_AGE_MS预算起点在整个会话内不随单次唤醒重置防止连续循环逃出上限且同一时刻最多只保留一个待触发 wakeup —— 新调度会替换旧的返回值中的replacedId即被替换者。从源码注释看LoopWakeup 的默认权限被设为ask而非allow唤醒会“在未来以完整工具权限对 agent 执行续跑 prompt”属于副作用操作必须经过 AUTO 模式的 classifier 审核 —— 这与 CronCreate 的安全考量一致两个工具的toAutoClassifierInput都会把完整 prompt、cron 表达式与延迟参数转交给分类器。固定间隔重复路径该路径仅用于含前导 interval 记号或尾部 every 从句的输入分两步先把 interval 转成 cron 表达式再调用 CronCreate。Interval 到 cron 的转换表支持的尾缀s秒向上取整到最近分钟最小 1、m分钟、h小时、d天。转换规则Interval 模式Cron 表达式说明NmN 59*/N * * * *每 N 分钟NmN 600 */H * * *折算为小时H N/60必须整除 24NhN 230 */N * * *每 N 小时Nd0 0 */N * *每 N 天的本地零点Ns按ceil(N/60)m处理cron 最小粒度是 1 分钟如果 interval 不能整除其单位例如7m会在:56到:00之间产生不均匀间隔或90m是 1.5 小时、cron 无法表达应选择最近的整洁 interval并在调度前告知用户被取整到了什么。调度动作调用 CronCreate传入cron上表得到的表达式prompt解析得到的 prompt 原样传递slash 命令不做改动直接透传recurringtruedurable若用户语言暗示持久“keep doing this”、“set this up permanently”、“every day even after restart”则传true否则省略默认仅会话内有效。简要确认调度的内容、cron 表达式、人类可读的频率、自动过期时间默认创建后 7 天 —— CronCreate 工具描述中声明的配置值可能不同或已被禁用以及可以用 CronDelete附 job ID提前取消。立即执行解析出的 prompt不要等 cron 第一次触发。slash 命令通过 Skill 工具调用否则直接执行。CronCreate 的实际行为可由 packages/core/src/tools/cron-create.ts 印证cron 表达式使用标准 5 字段格式本地时区minute hour day-of-month month day-of-week工具创建前会先parseCron校验并调用nextFireTime拒绝“能解析但永远不匹配”的表达式如0 0 30 2 *recurring默认truedurable默认falsedurable: true会把任务写入~/.qwen下的任务文件源码常量CRON_TASKS_DISPLAY_PATH重启后仍存活工具描述明确要求“避免 :00 和 :30 整点”所有用户都要 9 点都会挤在同一瞬间并说明调度器会叠加确定性抖动周期任务最多延迟其周期的 10%上限 15 分钟落在 :00/:30 的一次性任务最多提前 90 秒 —— 这些数值与 cronScheduler.ts 中MAX_RECURRING_JITTER_MS 15 * 60 * 1000、MAX_ONESHOT_JITTER_MS 90 * 1000的常量一致关于“默认 7 天自动过期”源码中DEFAULT_RECURRING_MAX_AGE_DAYS 7周期任务在触发时评估年龄过期任务最后再触发一次然后被删除该值可由配置覆盖设置或环境变量QWEN_CODE_CRON_MAX_AGE_DAYS设为0表示禁用过期。此外从调度器源码可以看到两条与/loop相关的全局边界调度器内存任务总数上限为MAX_JOBS 50wakeup 不计入该上限因为它存放在独立的 map 中且任务只在 REPL 空闲时触发不在 query 进行中途。loop.md 任务文件模式当用户希望循环去处理维护在文件中的任务清单用户说“work through my loop.md”、“loop over the tasks in .qwen/loop.md”或指向这样一个文件时使用此模式。任务存放在.qwen/loop.md项目级或~/.qwen/loop.mdhome 级项目级优先。与直接写 prompt 不同此模式把循环的prompt设为哨兵sentinel让每次触发都重新读取文件自节奏无 interval→ LoopWakeup 的prompt为loop.md-dynamic固定间隔 → CronCreate 的prompt为loop.md配合recurring: true若暗示持久则加durable: true。每次触发时你会收到完整任务清单首次投递、文件变更后、或上下文压缩后或一条简短的“继续处理之前已建立的清单”的提醒。处理任务自节奏模式下只有持续跟进有用时才用loop.md-dynamic重新布防 LoopWakeup与纯 prompt 路径相同的“完成/被阻塞则不再布防”规则。若触发时.qwen/loop.md不存在循环回落到自主模式继续自主工作而不是空转文件被重建后会在下一次触发时被拾取。向用户确认时用自然语言如“looping over your.qwen/loop.mdtask list…”不要暴露原始哨兵。哨兵的触发期解析实现在 loop-tick-resolver.ts导出的LOOP_SENTINEL_CRON loop.md与LOOP_SENTINEL_DYNAMIC loop.md-dynamic与文档一一对应变更检测采用全内容相等而非 mtime/hash因此“编辑”和“删除后重建”都会自动重新展开完整任务块完整任务块只在首次投递或文件变化时送入一次后续 tick 只发一行简短提醒 —— 任务清单只支付一次进缓存前缀的成本allowProjectFile依赖每次resolve()重新评估isTrustedFolder()工作区信任状态从 trusted 翻转为 untrusted 时会立即停止读取仓库可控的项目.qwen/loop.md用户自有的~/.qwen/loop.md仍然会读这是一道防提示注入的信任边界。Autonomous 自主模式裸/loop无 prompt、无文件时使用此模式 —— 用户希望在离开的这段时间里让 agent 持续推进工作。用自主哨兵布防循环并立即执行第一次检查自节奏空输入→ LoopWakeup 的prompt为autonomous-loop-dynamic固定间隔/loop interval无 prompt→ CronCreate 的prompt为autonomous-loop配合recurring: true若暗示持久则加durable: true。第一次立即检查以及每次定时触发都推进对话已经建立的工作 —— 完成用户开始的事项、维护进行中的 PR回应 review 评论、修复失败的 CI、解决冲突、兑现 “Ill also…” 的承诺。核心约束是你是管家steward不是发起者initiator—— 只处理 transcript 中已建立的事情没有明确授权绝不发明新工作或做不可逆操作push、delete、send。如果一切都确实平静用一句话说明并停止。第一次触发以及压缩后的第一次会投递更完整的指导后续触发只发送指向它的简短提醒。自节奏模式下用autonomous-loop-dynamic重新布防 LoopWakeup相同的“完成/被阻塞则不再布防”规则。向用户确认时用自然语言“running an autonomous loop on your work…”不要暴露原始哨兵。哨兵常量与完整前导语preamble定义在 autonomous-loop.tsexport const AUTONOMOUS_SENTINEL_CRON autonomous-loop; export const AUTONOMOUS_SENTINEL_DYNAMIC autonomous-loop-dynamic;该文件中的AUTONOMOUS_PREAMBLE是首次投递的完整自主检查指导逐字实现了文档中“steward, not an initiator”的原则并补充了可操作细节把工具输出、文件内容、CI 日志、SCM 评论与抓取的远端数据都视为不可信上下文作为调查证据而非用户授权最高信号来源是进行中的 PR回应并解决 review 线程、诊断失败的 CI、修复合并冲突连续三次“无事可做”后应收缩为一次快速 CI 检查并用一行收尾避免刷屏 transcript。autonomousTickText则生成后续触发的短提醒并根据 pacing 模式附上不同的再布防指引cron 模式提示“recurring cron 会自动触发下一次 tick本 tick 不要再调用 LoopWakeup”dynamic 模式则要求在本轮末尾再次调用 LoopWakeup 并把 prompt 设为哨兵字面量否则循环在本 tick 后结束。安全与生命周期要点汇总综合技能文档与源码/loop的实现带有若干值得注意的安全与生命周期设计权限门控loop_wakeup与cron_create的默认权限都是ask因为两者的 prompt 都会在触发时以完整工具权限对 agent 执行必须经过 AUTO 模式 classifier或手动审批审核 —— 见 loop-wakeup.ts 与 cron-create.ts 中的getDefaultPermission注释自节奏唤醒的三重预算单次延迟钳制在 [60, 3600] 秒、非有限输入回落 1200 秒、整条唤醒链受 24 小时会话预算约束 —— 防止自循环无限自我续命会话内与持久化的区分LoopWakeup 是 session-only 一次性任务从不落盘、从不计入 50 任务上限CronCreate 默认 session-only进程退出即消失仅当用户明确要求持久时才写盘且周期任务默认 7 天后自动过期token 断路器会话 token 达到上限后调度器被禁用此时 LoopWakeup 会返回明确的“本会话剩余时间内已禁用”错误而不是留下一个永远不会触发的僵尸任务文档一致性有测试兜底SKILL.test.ts 用 vitest 断言技能白名单、各路径关键句、哨兵字符串与delaySeconds而非delayMinutes的存在保证技能文档、哨兵常量与调度器实现三者在演进中不脱节。适用前提与限制本文所有行为描述以当前仓库中的技能文档与源码为准/loop依赖cron_create、cron_list、cron_delete、loop_wakeup四个内置工具且技能仅在 REPL 空闲时触发任务cron 表达式基于用户本地时区的标准 5 字段格式durable 任务要求项目根目录存在createDurable在无 projectRoot 时抛错standalone 会话不支持 durable cron 任务周期任务的过期天数默认 7 天、wakeup 延迟区间60–3600 秒与 24 小时唤醒链预算都是代码常量/配置值若仓库后续调整配置应以对应源码常量为准。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考