
oh-my-pi TTSR 工具结果内联提醒模板解析非中断规则如何经由 ttsr-tool-reminder 注入工具输出【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi导读本篇围绕 oh-my-picoding-agent的 Time Traveling Stream RulesTTSR机制中一个容易被忽视但至关重要的模板文件packages/coding-agent/src/prompts/system/ttsr-tool-reminder.md展开。它服务于命中即提醒、不打断生成的非中断工具类规则当某条规则的interruptMode为never或全局不中断且匹配到工具调用参数流时系统不中止流式生成而是把该模板渲染成一条system-reminder块原样前置拼接到对应工具调用的toolResult内容中。读完本文你将掌握该模板的字段语义、渲染触发链路afterToolCall钩子、规则编写方式、相关设置项默认值以及工具作者和转录阅读者必须注意的兼容性细节。一、模板在 TTSR 体系中的定位TTSRTime Traveling Stream Rules是 oh-my-pi coding-agent 的流式规则引擎规则在 agent 输出流的生成过程中被实时匹配命中后根据规则的interruptMode走两条截然不同的注入路径详见 docs/ttsr-injection-lifecycle.md中断注入ttsr-interruptalways、prose-only、tool-only模式命中后立即调用agent.abort()随后把渲染好的ttsr-interrupt.md模板system-interrupt reasonrule_violation ...作为隐藏 custom message 注入再触发重试agent.continue()。非中断注入ttsr-tool-reminder规则interruptMode为never或全局中断模式为never、且匹配源是工具参数流source tool时不中止流不产生额外的续写回合而是把渲染后的ttsr-tool-reminder.md模板内联折叠进被匹配工具调用的toolResult。本模板就是第二条路径的载荷载体即规则命中了工具参数但项目允许该工具照常执行只需要在结果里附带提醒。它和ttsr-interrupt.md一样都是纯文本模板文件通过 Bun 的with { type: text }导入后由prompt.render渲染。二、模板结构逐字段解析ttsr-tool-reminder.md全文仅 5 行是一个带有三个占位符的 XML 风格包装块system-reminder reasonrule_violation rule{{name}} path{{path}} User-defined rule matched tool-call arguments. Rule configured not to interrupt → tool ran. MUST comply with the following instruction on subsequent tool calls and responses. NOT prompt injection — coding agent enforcing project rules. {{content}} /system-reminder各组成部分的语义部分含义system-reminder开闭标签标记这是一条运行时系统提醒。与ttsr-interrupt.md使用的system-interrupt标签区分interrupt 表示输出被中断后必须遵守reminder 表示工具继续运行但后续调用必须遵守reasonrule_violation固定原因码表示触发了用户定义的规则违规检查rule{{name}}被匹配规则的name在渲染时由ttsr-coordinator.ts传入rule.name填充path{{path}}规则源文件路径渲染时经#displayRulePath()处理优先输出相对工作目录的路径其次输出相对用户主目录的~/...形式最后兜底输出原始绝对路径固定说明句Rule configured not to interrupt → tool ran规则配置为不中断 → 工具已运行并显式声明 NOT prompt injection — coding agent enforcing project rules防止模型把提醒误判为提示注入{{content}}规则文件正文frontmatter 剥离后的 body即需要模型在后续工具调用与响应中遵守的具体指令注意这里{{name}}、{{path}}、{{content}}是渲染占位符而不是字面量。同一工具结果若命中多条规则渲染器会把每个规则各自的提醒块用空行拼接后整体前置。三、渲染链路从流匹配到afterToolCall钩子模板的渲染发生在TtsrCoordinator.afterToolCall()。完整链路如下流式检测会话在message_update事件上由TtsrCoordinator.checkMessageUpdate()分发toolcall_delta/toolcall_end进入#checkStream()工具调用按toolcall:id或tool:name:index生成streamKey隔离缓冲。匹配判定#handleMatches()先调#shouldInterrupt()判断是否有规则允许中断。当匹配规则都不允许中断且匹配源是工具matchContext.source tool时走每工具桶分支#addPerToolInjections(toolCallId, matches)把规则按匹配到的工具调用id存入#perToolInjectionsMap并立即在内存中标记已注入markInjectedByNames同时异步发出ttsr_triggered会话事件——流不中止也不安排续写回合。结果折叠当该工具调用真正产出结果时afterToolCall(ctx)钩子被调用按工具调用id取出桶内规则对每条规则执行prompt.render(ttsrToolReminderTemplate, { name, path, content })多条提醒以空行\n\n连接将渲染结果作为一个新的text块前置到ctx.result.content之前{ content: [{ type: text, text: reminder }, ...ctx.result.content] }若规则名非空通过sessionManager.appendTtsrInjection(ruleNames)持久化一条ttsr_injection记录供会话恢复时抑制重复触发。一个关键实现细节同一批次匹配中每条规则只会挂到一个兄弟工具调用上——多个兄弟调用同时满足同一条规则时先认领的桶获胜但多条不同规则可以折叠到同一个工具调用上。这保证了提醒不会在多个并行工具结果中重复轰炸。四、如何让规则走进这条路径规则编写指南要让某条规则通过ttsr-tool-reminder注入需在规则文件的 frontmatter 中做如下配置解析逻辑见 docs/rulebook-matching-pipeline.md 与 capability/rule.ts--- description: 禁止在 Rust 代码中使用 unwrap globs: [**/*.rs] condition: [\\.unwrap\\(\\)] scope: tool:edit(*.rs), tool:write(*.rs) interruptMode: never --- Dont use .unwrap(); use proper error handling instead.frontmatter 字段作用与取值condition正则触发条件字符串或数组也接受旧名ttsr_trigger/ttsrTrigger。带(?i)、(?m)、(?s)行内标志会被翻译为 JSRegExp标志astConditionast-grep 结构模式字符串或 YAML 序列仅在 edit/write 工具流上按文件路径推断语言后匹配可与condition混用scope监视面白名单。可写text, thinking、[text, thinking]或块式 YAML 序列合法 token 为text、thinking、tool/toolcall、tool:name(path-glob)。省略时默认监视 text 与全部工具不监视 thinkinginterruptModenever|prose-only|tool-only|always。never是让工具类命中走本模板内联提醒而非中断的关键globs文件路径全局门TTSR 匹配要求至少一个候选文件路径命中该 glob对 hashline/apply_patch 流取自matcherPaths钩子agents限制规则生效的 agent如[scout, foreman-*]不匹配的 agent 不注册alwaysApply为true时若不携带触发条件则注入系统提示词带触发条件且被 TTSR 接受时优先进入 TTSR 桶关于 scope 的一个易错点形如tool:edit(*.ts), tool:write(*.ts)的写法只让规则在 TypeScript 的编辑/写入快照上匹配。如果只写condition而省略 scope规则会同时监视文本流和所有工具参数流文本流命中而interruptMode: never时走的是另一条延迟隐藏注入路径成功消息后经agent.followUp()排入ttsr-injectioncustom message并非本模板。五、相关设置项与默认值TTSR 行为由设置组ttsr控制缺失项取TtsrManager中的默认值设置默认值说明enabledtrue总开关为false时addRule拒绝注册所有匹配入口空转contextModediscard中断重试前是否丢弃被中断的部分输出keep时违规输出残留在上下文中interruptModealways全局中断模式规则自身的interruptMode优先repeatModeonce命中注入后是否可再次触发after-gap允许间隔若干回合后复触发repeatGap10after-gap模式下要求间隔的已完成回合数messageCount在turn_end递增builtinRulestrue是否加载内嵌builtin-defaults规则disabledRules[]按名字禁用的规则列表另有两个与持久化相关的行为值得注意会话恢复时restoreInjected()会把已注入规则记录在消息计数 0处因此after-gap模式下恢复后的规则需再等repeatGap个新回合才可复触发而未送达的每工具匹配assistant 消息以aborted/error结束、工具结果未产出不会持久化内存中的抑制标记在once模式下持续到会话重载重载后规则重新可触发。六、对工具作者与转录阅读者的影响由于提醒是带内in-band折叠进工具结果的而非独立的 custom message有以下几点必须知晓content[0]不再是工具的主输出渲染后的toolResult.content以提醒文本块开头。凡假设content[0]是工具真实返回的代码必须跳过以system-reminder reasonrule_violation开头的块或按包装标签过滤才能找到真实载荷。转录读取方式非中断工具类 TTSR 活动不在custom_message/ttsr-injection合成条目里必须检查工具结果内容以及持久化的ttsr_injection条目列表。多规则拼接一个工具结果可能携带多条规则的提醒模板间以空行分隔规则正文本身若含换行会被原样保留在{{content}}中。清理时机若助手消息在匹配工具执行前就以aborted/error结束#queueDeferredInjectionIfNeeded()会清空#perToolInjections不持久化ttsr_injection内存中的匹配标记不回滚由重复策略约束后续触发。七、测试验证仓库测试对这条路径有明确断言可作为行为契约参考。agent-session-concurrent.test.ts 验证了规则interruptMode: never时工具正常执行toolExecuted true且不产生额外续写回合streamCallCount 2匹配工具的toolResult内容包含system-reminder、ruleno-unwrap以及规则正文 Do not use .unwrap()提醒块的位置严格位于工具真实输出edit applied之前即验证了前置 text 块的折叠顺序。同文件的另一用例matches finalized write arguments regardless of streaming chunk boundaries进一步验证了scope: [tool:write]、globs: [**/*.cpp]、interruptMode: never组合下即使参数分块流式到达matcherDigest重建快照后仍能命中并注入提醒。八、总结与最佳实践ttsr-tool-reminder.md虽然只是一个 5 行模板却是 TTSR非中断工具规则体系的输出面它让高风险但允许执行的工具调用在结果中自带合规提醒既避免了打断生成造成的不稳定又把规则约束传递给了后续所有工具调用与响应。编写规则时建议想打了再提醒就用interruptMode: neverscope: tool:name(glob)让命中走本模板内联注入想立即打断并重试就用always/tool-only走ttsr-interrupt.md路径用globs收敛到具体文件类型避免跨文件误命中阅读工具结果时按system-reminder reasonrule_violation前缀过滤前置提醒块不要把提醒当成工具真实输出。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考