TL;DR:在 omp 输入框里输入 ultrathink,看这个词在彩虹色中循环闪烁。按回车,agent 没收到任何可见信号,但已经跳到最高推理深度。这就是 omp 的魔幻关键词——三个特定单词在输入时显示渐变高亮,提交后自动注入隐藏的 System Notice 改变 agent 行为。检测只匹配独立 prose 关键词,跳过代码块、行内代码和 XML 标签。
这是「omp 编程工具使用探讨」专栏的一篇追加文章。第 9 篇(对比篇)本来写了”完结”,但 magic keywords 这个 feature 在开发过程中独立成型,值得单独一篇讲清楚。查看专栏目录浏览全部文章。 本文中的源码行号引用自 oh-my-pi 当前开发版本。项目在快速迭代中,行号可能随版本变化而偏移。建议以实际源码为准,文中行号仅作参考定位。
一句话讲清楚
想象这个场景:你在 omp 输入框里打出 ultrathink,这个词立刻在彩虹色中循环闪烁。按回车发送,agent 没有收到任何可见信号,但已经自动跳到最高推理深度。
这是 omp 的魔幻关键词机制——三个特定单词在输入时触发渐变高亮,提交后注入隐藏的 System Notice 改变 agent 行为。所有检测只匹配独立 prose 关键词,代码块和行内代码里的不会触发:
三个关键词各自的功能:
| 关键词 | 渐变配色 | 功能 |
|---|---|---|
ultrathink | 彩虹(红→紫,hue 0..330) | 跳过自动难度分类,直接拉到最高 thinking level |
orchestrate | 青→紫(hue 150..280) | 切换到多 agent 编排模式,10 条 strict rules |
workflowz | 琥珀→绿(hue 30..150) | 切换到确定性多子 agent 工作流模式 |
效果:两处渲染,同一套机制
关键词的着色出现在两个地方:
1. 输入框实时高亮(带 shimmer 动画)
custom-editor.ts 的 decorateText() 方法在每次渲染时调用 highlightMagicKeywords():
// custom-editor.ts:337-340const animated = this.focused && this.#shimmerEnabled() && hasMagicKeyword(editorText);const phase = animated ? (Date.now() % CustomEditor.SHIMMER_PERIOD_MS) / CustomEditor.SHIMMER_PERIOD_MS : 0;shimmer 动画参数:
- 周期
SHIMMER_PERIOD_MS = 1800(1.8 秒循环一次完整渐变) - 帧率
SHIMMER_FRAME_MS = 70(约 14 FPS) - 只有焦点在输入框且关键词存在时才运行动画计时器
- 失焦或关键词消失 → 计时器链自动停止
具体效果:关键词的每个字符独立着色,从第一个字符到最后一个字符渐变。shimmer 开启时,这个渐变会缓慢滑动——假设
ultrathink当前是”红橙黄绿”,1.8 秒后变成”橙黄绿蓝”,依此类推,像光谱在单词上流动。
2. 已发送气泡里保持静态高亮
user-message.ts 构造时同样调用 highlightMagicKeywords(),但传入 phase = 0(静态调色板):
// user-message.ts:32-35const keywordReset = theme.getFgAnsi("userMessageText") || "\x1b[39m";const baseText = (value: string) => theme.fg("userMessageText", highlightMagicKeywords(value, keywordReset));注意这里传了 resetTo 参数——气泡有自己背景色,关键词着色后必须恢复气泡的文字色,否则渐变色会”渗”到后面的文字。
源码拆解:两层架构
第一层:检测引擎(markdown-prose.ts)
关键词检测的核心难题:怎么判断一个词是在 prose 中,而不是在代码块或 XML 里?
// markdown-prose.ts:162-236export function maskNonProse(text: string): string { // 快速短路:没有 `、<、~~~ 就不需要 mask if (!text.includes("`") && !text.includes("<") && !text.includes("~~~")) { return text; } // Phase 1: 扫描隔离 fenced code block(``` 和 ~~~) // Phase 2: 扫描隔离 inline code span(`code`)和 HTML/XML 标签 // 把非 prose 区域的字符替换为空格,保持索引对齐}maskNonProse() 返回一个长度不变的字符串——非 prose 字符被替换成空格,但索引位置不变。这样上层用 .matchAll() 拿到的 index 可以直接用来切原始文本。
keywordInProse() 是检测入口:
// markdown-prose.ts:244-247export function keywordInProse(text: string, word: RegExp): boolean { if (!word.test(text)) return false; return word.test(maskNonProse(text));}先快速 String.includes 短路(hasMagicKeyword() 在 magic-keywords.ts:37-42),再用 keywordInProse 确认关键词确实在 prose 中。
第二层:渐变着色器(gradient-highlight.ts)
createGradientHighlighter() 是一个高阶函数——接收渐变参数,返回一个 (text, resetTo, phase) => string 的着色函数。
核心逻辑:
// gradient-highlight.ts:39-98(简化)function createGradientHighlighter(spec) { const { probe, highlight, stops, hue, saturation = 90, lightness = 62 } = spec;
// 调色板:编译 stops 个 HSL 色值,按主题色模式缓存(truecolor / 256 色) const palette = () => { /* 略——Bun.color 编译 hsl 值 */ };
// 核心:逐字符着色,相邻同色合并,phase 控制 shimmer 偏移 const paint = (word, resetTo, phase) => { for (let i = 0; i < word.length; i++) { const t = (i / word.length + phase) % 1; const color = palette()[Math.floor(t * stops)]; if (color !== prev) out += color; // 相邻同色不重复输出 ANSI 码 out += word[i]; } return out + resetTo; };
return (text, resetTo, phase) => { if (!probe.test(text)) return text; // 快速短路 const masked = maskNonProse(text); // 在 masked 版本上 matchAll for (const m of masked.matchAll(highlight)) { out += paint(text.slice(start, end), resetTo, phase); } };}几个设计细节:
- 快速短路:
probe是一个非全局 regex,只检查字符串中是否包含该词,不触发 markdown 解析(maskNonProse有开销) stops = 14:每个关键词最多 14 个字符,每个字符一个色值,刚好够- 相邻色合并:同样的颜色值不会重复输出 ANSI 转义码,减少输出长度
phase循环:(i/n + phase) % 1让色值随 phase 偏移,实现 shimmer 动画
第三层:三个关键词各一个文件
每个关键词一个文件,各自独立导出:
| 文件 | 导出函数 | 渐变参数 |
|---|---|---|
ultrathink.ts | containsUltrathink, highlightUltrathink, ULTRATHINK_NOTICE | hue 0..330, 14 stops |
orchestrate.ts | containsOrchestrate, highlightOrchestrate, ORCHESTRATE_NOTICE | hue 150..280, 14 stops |
workflow.ts | containsWorkflow, highlightWorkflow, WORKFLOW_NOTICE | hue 30..150, 14 stops |
magic-keywords.ts 做组合:
// magic-keywords.ts:23-28export function highlightMagicKeywords(text, resetTo?, phase?): string { return highlightWorkflow( highlightOrchestrate( highlightUltrathink(text, resetTo, phase), resetTo, phase), resetTo, phase);}链式调用顺序无关——每个 highlighter 只注入零宽度的 SGR 转义码,不会影响其他 highlighter 的匹配。
提交后的隐藏机制
检测 + 通知注入
agent-session.ts 的 #createMagicKeywordNotices() 在用户消息提交时调用:
// agent-session.ts:7582-7621#createMagicKeywordNotices(text: string): CustomMessage[] { const notices: CustomMessage[] = [];
if (this.#magicKeywordEnabled("ultrathink") && containsUltrathink(text)) { notices.push({ role: "custom", customType: "ultrathink-notice", content: ULTRATHINK_NOTICE, // 隐藏的 system-notice display: false, // 不在 UI 中显示 attribution: "user", // 算作用户输入 }); } // 同理 orchestrate 和 workflowz return notices;}这些 notice 是 display: false 的 CustomMessage——用户看不到,但 agent 的 system prompt 里会收到。
ultrathink 的特殊之处:跳过自动分类
ultrathink 还有一个额外效果——在 #applyAutoThinkingLevel() 中:
// agent-session.ts:9417-9422if (this.#magicKeywordEnabled("ultrathink") && containsUltrathink(promptText)) { // 跳过 LLM 难度分类,直接拉到最高 thinking level resolved = clampAutoThinkingEffort(model, Effort.Max);}正常情况 agent 会调 LLM 分类器判断任务难度(classifyDifficulty),然后自动设置 thinking level。ultrathink 完全绕过这个流程,直接设为最高级别。
orchestrate 的隐藏规则
orchestrate 的 notice 最复杂——10 条 rules + 7 步 workflow + 4 条 anti-patterns,核心约束:
注意:这些规则是 system prompt 的简化版摘要。完整内容见
orchestrate-notice.md,包含 10 rules + 7 steps + 4 anti-patterns,总计约 21 个约束段。
- 不完成不 yield——一个阶段做完立即启动下一阶段
- 枚举全部工作表面——读到 todo/phase list 时展开为完整列表
- 最大化并行——绝不串行 dispatch 一个子 agent
- 每个 task 自包含——子 agent 没有共享上下文
- 每个阶段后验证——红树不前进
- 提交策略——绿树才 commit
- 重试不吸收——子 agent 没做完,再 spawn 修正 agent
- 不 scope creep——不加用户没要求的,不缩用户要的
- 子 agent 不验证——父 agent 统一验证
- 右尺寸 offload——小改动自己做,不要套 task 脚手架
workflowz 的模板引擎
workflowz 的 notice 用了 prompt.render() 模板引擎:
// workflow.ts:25-26export function renderWorkflowNotice({ taskBatch }: { taskBatch: boolean }): string { return prompt.render(workflowNoticeTemplate, { taskBatch }).trim();}根据 task.batch 设置决定渲染 batched 还是 flat 的 task 调用方式。
注意:
workflowz的 notice 只在当前 session 拥有task工具时才会注入(agent-session.ts:7610)。如果 session 配置中没有task工具(例如某些简化模式),workflowz关键词不会触发任何行为变化。
设置开关
所有配置在 settings-schema.ts 中定义,默认全部开启:
"magicKeywords.enabled": { type: "boolean", default: true }"magicKeywords.ultrathink": { type: "boolean", default: true }"magicKeywords.orchestrate": { type: "boolean", default: true }"magicKeywords.workflow": { type: "boolean", default: true }可以通过 ~/.config/omp/settings.yaml 或项目级 .omp/settings.yaml 关闭:
magicKeywords: enabled: false # 一次性全部关闭 # 或单独关闭某个 workflow: false与 Claude Code 的对比
Claude Code 有一个类似的机制——ultrathink 关键词也会触发 rainbow 高亮和隐藏 notice。但 omp 做了几个扩展:
| 维度 | Claude Code | omp |
|---|---|---|
| 关键词数量 | 1 个(ultrathink) | 3 个 + 可扩展架构 |
| 渐变着色 | 单一 rainbow | 每个关键词独立配色(rainbow / teal-violet / amber-green) |
| shimmer 动画 | 无 | 有,1.8 秒循环,14 FPS |
| 气泡高亮 | 只在输入框 | 输入框 + 已发送气泡 |
| 代码块感知 | 有 | 更精确的 maskNonProse(处理 fenced block + inline code + XML) |
| 设置开关 | 无 | 全局 + 逐关键词设置 |
| 模板引擎 | 无 | workflowz 用 prompt.render() 支持条件渲染 |
适合什么场景
ultrathink:复杂推理任务(bug 分析、架构设计、数学/逻辑问题),需要 agent 花更多 token 思考。
orchestrate:多文件、多步骤的重构/迁移任务。需要 agent 拆解、并行 dispatch、逐步验证。
workflowz:需要确定性、可复现的 multi-subagent 流程。比 orchestrate 更结构化,适合”调研 → 设计 → 实现 → 验证”的流水线。
实现启示
magic keywords 这个 feature 展示了 omp 的几个设计哲学:
- 视觉反馈先于行为——关键词还没发送,渐变高亮就已经告诉用户”这个词有特殊效果”
- 两层检测——快速短路(
String.includes)避免每次按键都跑 markdown 解析,精确检测(maskNonProse)确保不出错 - 可组合的着色器——
createGradientHighlighter是函数式工厂,新增一个关键词只需要 30 行代码 - 隐藏注入——
display: false的 CustomMessage 让用户界面保持干净,agent 行为被影响但用户不被打扰
本文是「omp 编程工具使用探讨」专栏的第 10 篇(追加篇)。查看专栏目录浏览全部文章。文中的源码引用来自 oh-my-pi 当前版本,具体实现可能随版本变化。