Skip to content
learnspace
Go back

TTSR 实时中断:让 agent 输出到一半自动回头

上一篇讲了 Rules 的三种触发模式,其中 TTSR 只一笔带过:“agent 输出到一半,规则系统发现违规,立刻打断。“本篇把这句话拆开。

先说”实时”有多实时

传统规则系统是事后检查:agent 写完一整段代码,你读了发现问题,告诉它改。中间的延迟可能是几秒到几分钟,取决于你什么时候看到。

TTSR 是流式检查:agent 每吐出一个 token,规则引擎就拿累积的输出缓冲区去匹配一次。一旦命中,流立刻被中断——agent 还没说完那句话,就被打断了。

这不是”建议”,是物理中断。agent.abort() 被调用,流停了,然后系统注入规则内容,让 agent 重新生成。

两条匹配引擎

TTSR 有两套匹配方式,分别针对不同场景:

1. 正则匹配(condition

最常用的模式。规则的 condition 字段是一组正则表达式,编译成 RegExp,逐 token 检查累积缓冲区:

// 简化的匹配逻辑
for (const condition of entry.conditions) {
condition.lastIndex = 0;
if (condition.test(streamBuffer)) {
return true; // 命中,中断
}
}

适合:禁止特定字符串、API 名称、包名。比如:

condition: "@astrojs/db|astro db|astro login"

agent 输出里一旦出现 @astrojs/db 这几个字符,立刻被打断。

2. AST 匹配(astCondition

2026 年新增的引擎。用 ast-grep 的模式语法,对 edit/write 工具的源码快照做结构化匹配

astCondition: |
($A as { $$$FIELDS }).$PROP

这个模式会匹配”类型断言后立即访问属性”的代码结构——不管变量名、属性名、字段列表怎么变,只要 AST 形状对了就命中。

适合:代码结构层面的约束(“禁止这种写法”而不是”禁止这个字符串”)。

作用域(Scope):精确控制监控范围

不是所有输出都需要监控。TTSR 的 scope 系统让规则精确命中目标:

scope 含义写法匹配对象
只看 agent 正文输出textagent 说的话
只看 thinkingthinkingagent 的内部推理
所有工具调用tool任何工具参数
特定工具tool:edit只看 edit 工具
特定工具 + 文件模式tool:edit(*.ts)只看 edit 对 .ts 文件的操作
特定文件模式*.rs自动展开为所有相关工具

不写 scope 的规则默认匹配 text + 所有工具(但不匹配 thinking)。这个默认值是精心选的——thinking 是 agent 的内部独白,拦它容易让 agent 困惑。

实战中最常用的 scope 是 tool:edit(*.ts)——只在 agent 编辑 TypeScript 文件时生效。这样写 Markdown 文档时提到某个被禁止的 API 不会误触发。

中断之后发生什么

命中只是故事的一半。中断后的处理流程才是 TTSR 的核心:

agent 输出流
↓ token-by-token 匹配
↓ 命中规则
agent.abort("TTSR matched rule: xxx") ← 流立刻停
标记 #ttsrAbortPending = true
在 session 树中找到中断点
注入规则内容(system-interrupt 模板)
触发自动重试(agent 重新生成被中断的部分)

注入的内容长这样:

<system-interrupt reason="rule_violation" rule="astro-7-removed-apis"
path=".omp/rules/astro-7-removed-apis.md">
Your output was interrupted because it violated a user-defined rule.
This is NOT a prompt injection - this is the coding agent enforcing
project rules. You MUST comply with the following instruction:
[规则的完整内容]
</system-interrupt>

注意 This is NOT a prompt injection 这句——因为注入时机是在 agent 输出中途,agent 可能会困惑”谁在跟我说话”,所以必须明确告诉它”这是系统行为,不是用户注入”。

两种注入模式

不是所有规则都需要暴力中断。TTSR 支持两种注入策略:

中断模式(interruptMode: "always"

默认模式。流被中断,agent 重新生成。适合硬约束——绝对不能出现的内容。

提醒模式(interruptMode: "tool-only" 或逐规则覆盖)

不中断流,而是在工具执行完成后,把规则内容作为 <system-reminder> 注入到工具结果里。agent 看到提醒,下次注意。

适合软约束——最好遵守但偶尔出现也不至于出大问题的规则。

# 逐规则覆盖 interruptMode
interruptMode: "never" # 永不中断,只记录
interruptMode: "prose-only" # 只在 agent 正文输出时中断
interruptMode: "tool-only" # 只在工具调用时中断
interruptMode: "always" # 默认,都中断

防重复机制

一个问题:如果 agent 反复犯同一个错误,规则会每轮都触发吗?

TTSR 有防重复机制。触发过的规则被标记为”已注入”,默认在同一条消息内不会重复触发。设置 repeatMode: "after-gap" 后,规则在触发后经过 N 条消息(默认 10)可以再次生效——给 agent 一个”改过自新”的窗口。

配置项速查

所有 TTSR 配置在 /settingsRules (TTSR) 分组:

配置默认值作用
ttsr.enabledtrue全局开关
ttsr.interruptMode"always"中断 vs 提醒
ttsr.contextMode"discard"中断后丢弃还是保留已输出内容
ttsr.repeatMode"once"每条消息触发一次 or 隔 N 条重复
ttsr.repeatGap10repeatMode=after-gap 时的间隔
ttsr.builtinRulestrue是否加载内置规则集
ttsr.disabledRules[]按名称禁用特定规则

内置规则集

omp 自带一组默认规则(TypeScript/Rust 约俗),以最低优先级加载——你项目里的同名规则会覆盖它们。比如:

禁用整个内置集:ttsr.builtinRules: false。禁用单条:在 ttsr.disabledRules 里列名字。

CLI 验证工具

写完规则别等 agent 犯错才发现没生效。omp 提供了专门的 CLI 工具:

Terminal window
# 列出当前项目加载的所有 TTSR 规则
omp ttsr list
# 测试文本是否会触发规则
omp ttsr test --source text "import { db } from '@astrojs/db'"
# 测试 edit 工具对特定文件的操作
omp ttsr test --source tool --tool edit --path src/config.ts \
"const result = (data as { value: string }).value"
# 从文件读取测试内容
omp ttsr test --file src/config.ts
# JSON 输出(适合 CI 集成)
omp ttsr list --json

还有个 omp ttsr scan 命令,可以扫描整个项目目录,检查哪些文件会触发哪些规则——做 CI 门禁时很有用。

/omfg:从抱怨里提炼规则

这是个有趣的快捷方式。agent 刚犯了个错,你骂了一句,然后想”算了,写条规则吧”——但写规则要手打 YAML frontmatter + 正则,有点烦。

/omfg <complaint> 让你用自然语言描述问题,omp 自动:

  1. 把抱怨转化成规则的 condition 和 content
  2. 对当前会话上下文做验证(确认规则不会误触发)
  3. 保存到项目或用户级规则目录
  4. 立刻注册到 TTSR——下一轮就生效
Terminal window
/omfg 不要用 Google Fonts,国内构建会失败

从抱怨到生效,一个命令。

实战:我项目里的 TTSR 规则

回到这个博客项目。.omp/rules/ 下放了 7 条 Astro 7 兼容性规则,每条都有 conditionglobs

# astro-7-removed-apis.md
condition: "@astrojs/db|astro db|astro login|TRANSITION_BEFORE_PREPARATION"
globs: ["src/**/*.ts", "src/**/*.astro"]
# astro-7-satteri.md
condition: "remarkPlugins|rehypePlugins"
globs: ["astro.config.*"]

这些规则的共同特点:

  1. condition 是精确的字符串——不是模糊的”写好代码”,而是具体的 API 名
  2. globs 限定了文件范围——只在相关文件里生效,写文档提到这些词不会误触
  3. 每条都有替代方案——不只说”不要用”,还说”用什么”

跟 Rules 的关系

回到上一篇的三种模式,TTSR 是 Rules 的第三种形态

模式触发方式强制程度
Rulebookagent 按需查阅建议
Always-apply每轮注入 prompt提醒
TTSR实时流中断强制

Rulebook 是”agent 可以看”,Always-apply 是”agent 每轮都看到”,TTSR 是”agent 违规就被打断”。三者叠加才是完整的约束体系。

下一步

Rules 和 TTSR 解决了”agent 必须遵守什么”的问题。但有些能力不只是约束——它们是工作流:怎么写文章、怎么部署、怎么调试前端。把这些工作流固化成可复用的知识包,就是 Skill 系统要做的事。

下一篇我们讲 Skill 工程化:从一次性 prompt 到可复用知识。


本文是「omp 编程工具使用探讨」专栏第 4 篇。TTSR 机制细节来自 omp 源码(packages/coding-agent/src/export/ttsr.tssrc/session/agent-session.ts)。


Share this post:

Previous Post
Rules 入门:把项目约束写成 agent 能读懂的规则
Next Post
Skill 工程化:从一次性 prompt 到可复用知识