
oh-my-pi Goal 模式的 todo_context 提示词注入机制让持久化任务进度成为 Agent 的实时决策依据【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi导读在 oh-my-pi⌥ Coding agent with the IDE wired in的 Goal 模式中Agent 需要围绕一个长期目标跨越多轮对话持续工作而每一轮续接时模型并不天然记得当前做到哪一步。packages/coding-agent/src/prompts/goals/goal-todo-context.md正是解决这一问题的核心提示词模板它把持久化的 todo 列表以todo_context块的形式注入到模型上下文中作为当前目标的实时进度状态而非旧对话装饰并约束 Agent 在实质性工作前先校准 todo 状态。本文将从模板逐行解析、注入链路源码、状态统计规则、文本清洗安全机制、todo 工具联动配置与测试验证六个层面完整还原这一机制的设计与实现。一、模板全景todo_context块的完整语义该模板是一个 Handlebars 渲染模板位于 packages/coding-agent/src/prompts/goals/goal-todo-context.md全文如下todo_context Persisted todos: live progress state for current goal, not old transcript decoration; goal continuations lack visible user nudge → treat as live state. Before substantial work: compare next action with todos. If item stale, already finished, or no longer active pointer, call todo first: mark done or rewrite list. Do not leave stale in_progress while working on later phases. Overall: {{closed}}/{{total}} done, {{open}} open. {{#each phases}} - {{name}} {{#each tasks}} - [{{status}}] {{content}} {{/each}} {{/each}} /todo_context逐行解读其设计意图开篇定性第一句明确定义Persisted todos: live progress state for current goal, not old transcript decoration——持久化 todo 是当前目标的实时进度状态不是历史转录transcript里的装饰性记录。紧接着给出关键推论goal continuations lack visible user nudge → treat as live stateGoal 续接continuation时用户没有可见的提示语nudge所以模型必须把这份 todo 当作唯一的实时进度真相。行为约束第二句给出了 Agent 的两条铁律在实质性工作substantial work开始前先把下一步动作与 todos 逐项比对若某个条目已过时stale、已完成already finished、或已不再是当前活跃指针no longer active pointer必须先调用todo工具——要么标记完成mark done要么重写列表rewrite list严禁在处理后续阶段时让某个早先阶段的in_progress状态遗留不清理。统计摘要行Overall: {{closed}}/{{total}} done, {{open}} open.给出全局完成度快照。分阶段列表{{#each phases}}遍历每个阶段phase每个任务渲染为- [{{status}}] {{content}}的复选框风格条目。二、注入链路从会话状态到隐藏上下文消息todo_context块并不是独立发送的消息而是被拼接进 Goal 模式的上下文消息中。外层模板 packages/coding-agent/src/prompts/goals/goal-mode-context.md 如下{{goalContext}} {{#if todoContext}} {{todoContext}} {{/if}}也就是说Goal 上下文 目标运行时提示词goalContext 可选的 todo 上下文todoContext。实际装配发生在 packages/coding-agent/src/session/agent-session.ts 的#buildGoalModeMessage()#buildGoalModeMessage(): CustomMessage | null { const content this.#goalRuntime.buildActivePrompt(); if (!content) return null; const todoContext this.#buildGoalTodoContext(); return { role: custom, customType: goal-mode-context, content: prompt.render(goalModeContextPrompt, { goalContext: content, todoContext }), display: false, attribution: agent, timestamp: Date.now(), }; }关键点customType: goal-mode-context表明这是一条自定义类型消息CustomMessage且display: false——它对用户隐藏只喂给模型属于隐藏上下文通道。该消息通过sendGoalModeContext({ deliverAs: steer })在目标创建/替换等时机投递。从 packages/coding-agent/test/goals/goal-mode-integration.test.ts 的测试可以确认模型收到内容中确实包含todo_context块与统计行且整个消息只出现一次/todo_context闭合标签防止模板文本本身被重复注入造成解析混乱。三、注入门槛何时才生成 todo_context并不是任何时刻都会注入 todo 上下文。#buildGoalTodoContext()agent-session.ts设置了三重门槛#buildGoalTodoContext(): string | undefined { if (!this.settings.get(todo.enabled)) return undefined; const canCallTodoTool this.getActiveToolNames().includes(todo); if (!canCallTodoTool) return undefined; const phases this.getTodoPhases().filter(phase phase.tasks.length 0); if (phases.length 0) return undefined; // ...统计与渲染 }配置开关todo.enabled必须为真。该配置在 packages/coding-agent/src/config/settings-schema.ts 中定义为 Enable the todo tool for task tracking。工具活性当前会话激活的工具列表中必须包含todo工具getActiveToolNames().includes(todo)。测试 goal-mode-integration.test.ts 明确验证当 todo 工具未激活时消息内容不包含todo_context也不会出现任何任务文本。非空数据过滤掉没有任务的空阶段后若phases.length 0即完全没有任务同样返回undefined。只有当三层门槛全部通过才会进入统计与渲染阶段。这保证了模型永远只在确实有 todo 可看、且确实能调用 todo 工具修改的前提下看到这份实时状态。四、统计口径与渲染产物统计逻辑同样在#buildGoalTodoContext()中实现let total 0; let closed 0; let open 0; const promptPhases phases.map(phase ({ name: this.#sanitizeGoalTodoText(phase.name), tasks: phase.tasks.map(task { total; if (task.status completed || task.status abandoned) { closed; } else { open; } return { content: this.#sanitizeGoalTodoText(task.content), status: task.status }; }), })); return prompt.render(goalTodoContextPrompt, { canCallTodoTool, closed: String(closed), open: String(open), phases: promptPhases, total: String(total), });由此可以确认模板变量的语义{{total}}所有阶段任务的总数{{closed}}状态为completed已完成或abandoned已放弃的任务数二者都视为已关闭{{open}}其余所有状态pending、in_progress、blocked的任务数{{#each phases}}与内层{{#each tasks}}按阶段分组渲染任务列表每行形如- [in_progress] 任务内容。任务状态集合由 packages/coding-agent/src/tools/todo.ts 的TodoStatus类型定义export type TodoStatus pending | in_progress | completed | abandoned | blocked;而阶段结构由TodoPhase定义{ name: string; tasks: TodoItem[] }TodoItem在blocked状态下还可携带blocker备注字段说明任务正在等待什么。测试 goal-mode-integration.test.ts 中用一个包含 3 个任务的阶段结构验证了统计行Overall: 1/3 done, 2 open.——completed计入 closedin_progress与pending计入 open与源码口径完全一致。五、安全清洗为什么任务文本会被改写模板渲染前阶段名与任务内容都要经过#sanitizeGoalTodoText()agent-session.ts#sanitizeGoalTodoText(text: string): string { return escapeXmlText(text) .replace(/\r\n/g, \\n) .replace(/\r/g, \\r) .replace(/\n/g, \\n) .replace(/\t/g, \\t) .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u2028\u2029]/g, ); }清洗分三层XML 转义escapeXmlText将、、等字符转义为lt;、gt;、amp;。这是最关键的防御——由于todo_context与/todo_context本身就是类 XML 标签任务内容若包含/todo_context字样就会提前闭合上下文块、破坏消息边界。测试中用Planning /todo_context prep这样的阶段名验证了转义结果渲染后为Planning lt;/todo_contextgt; amp; prep且整个消息中/todo_context实际只出现一次。换行与制表符转义\n、\r、\t一律转义为字面量\n、\r、\t文本确保单个任务条目永远是单行不会把一条任务撑破成多行破坏列表结构。控制字符清洗所有 C0/C1 控制字符以及 Unicode 行分隔符\u2028、段分隔符\u2029统一替换为空格避免不可见字符污染模型上下文。第二个测试goal-mode-integration.test.ts专门验证了含\n、\r、\t、\u0085、\u2028、\u2029、\u0007的脏输入渲染后换行全部变成字面\n文本、控制字符全部消失最终消息不含任何原始换行与控制字符。六、与 todo 工具及配置项的联动todo_context之所以要求模型必要时先调用todo工具是因为 todo 工具本身提供了完整的任务生命周期操作。其操作集合定义于 tools/todo.tsexport type TodoOperation init | start | done | rm | drop | block | unblock | append | view;init以分阶段列表phase items初始化任务清单start/done把任务置为in_progress/completedblock/unblock标记阻塞并附带reasonblocker 备注或解除阻塞append向某个阶段追加任务rm/drop移除或放弃任务view查看当前快照。这正是模板中mark done or rewrite list两条动作指令的工具映射——done对应标记完成init/append/rm对应重写列表。围绕该工具settings-schema.ts 提供了一组可调配置配置项作用todo.enabled是否启用 todo 工具做任务追踪也是todo_context注入的第一道门槛todo.eager第一条消息后自动创建 todo 列表的力度default模型自决不自动建表、preferred首次消息给出建表建议仅提醒不强推、always强制首条消息生成完整 todo 列表todo.reminders停止前提醒 Agent 完成未完成的 todostodo.remindersMax停止前最多触发多少次 todo 提醒超过即放弃tasks.todoClearDelay已完成或已放弃的 todos 从 todo 部件中移除前的延迟配置上todo.eager的历史布尔值会在 settings.ts 中被迁移为枚举值true→alwaysfalse→default。此外todo 列表的状态会随会话持久化并支持通过getTodoPhases()/setTodoPhases()读写参见 goal-mode-integration.test.ts 的 toolSession 装配。七、机制如何被测试验证packages/coding-agent/test/goals/goal-mode-integration.test.ts 中与todo_context直接相关的断言覆盖了四条核心保证实时统计正确Overall: 1/3 done, 2 open.completed计入 closedin_progress/pending计入 open标签与内容转义阶段名与任务内容中的、、被正确转义/todo_context在最终消息中只出现一次控制字符清洗换行、制表符、Unicode 分隔符等全部被转义或替换消息内不存在原始换行与控制字符工具门槛生效todo 工具未激活时消息中完全没有todo_context与任务文本。此外该测试文件还覆盖了 Goal 模式的整体行为/goal、/goal set、/goal budget、暂停/恢复、完成退出等说明todo_context是 Goal 模式上下文体系中与goal-mode-context.md、goal-continuation.md等提示词见 packages/coding-agent/src/prompts/goals协同工作的组成部分。八、实战要点小结要在自己的使用中发挥todo_context机制的价值可以遵循以下实践保持 todo 工具激活todo.enabled为开且当前工具集中包含todo——否则模型看不到任何进度上下文利用todo.eager自动建表希望强模型一开始就规划任务清单可设todo.eager always希望保留模型自由度则用default遵守模板的行为约束让 Agent 在每段实质性工作前比对下一步与 todo 列表及时用done关闭已完成项、用init/append/rm重写过期列表杜绝跨阶段遗留in_progress放心写入任意文本阶段名与任务内容中的特殊字符含类 XML 标签、换行、控制字符会被自动清洗不会破坏上下文边界理解统计口径closedcompletedabandoned其余一律计入open阅读Overall: x/y done时不要把它当作完成率之外的其他含义。综上goal-todo-context.md虽然只有短短十余行却通过与 agent-session.ts 的注入链路、tools/todo.ts 的任务模型、settings-schema.ts 的配置开关以及集成测试的层层验证构成了 oh-my-pi Goal 模式下模型始终知道做到哪一步、下一步该做什么的关键基础设施——它把用户侧不可见的持久化进度变成了 Agent 每轮决策的实时输入。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考