oh-my-pi — 本课题摘录
读了哪几篇: 02-providers-dialects(模型接入层与方言)、06-context-and-steering(长程上下文治理与转向)。
其余四篇本轮没读。
这一家对本课题第二个决定给了最成体系的一份答案:把"怎么认出模型要调工具"做成了一个可插拔的编解码层。
它对本课题回答了什么
决定二:带内工具调用 —— 工具调用混在正文里,自己编自己解
两种做法的对比:
| 原生工具调用 | 带内工具调用 | |
|---|---|---|
| 工具目录发给谁 | 放进请求的工具字段,厂商负责 | 写进系统提示的纯文本 |
| 模型怎么喊工具 | 厂商用专门的结构化通道回传 | 模型在正文里打出一段约定标记 |
| 怎么拿到 | 直接读结构化字段 | 用扫描器从文本流里解析出来 |
| 谁能用 | 只有支持工具调用的模型 | 任何能生成文本的模型都能用 |
(依据:Agent 库 · oh-my-pi · 模型接入层与方言:in-band 工具调用(pi-ai) —— 方言层把工具调用编进/解出纯文本流——发送侧把工具目录写进 system prompt 文本、接收侧用 InbandScanner 从文本流解回结构化事件,让原生不支持 tool-call 的模型也能当 agent 用)
关键在:对上层循环来说,两种做法看起来完全一样——都是收到一个工具调用事件。
这一条是本课题第二个决定的最优结构答案: "怎么认出模型要调工具"应该是一个可替换的层,而不是循环里的一段判断。
前面 agenticseek / aider / deepanalyze 各自发明了一种格式,但都写死在自己的循环里。 oh-my-pi 把它抽成一个接口:渲染(编码)和扫描(解码)两半对称。
它要解决的第二个问题比第一个更棘手,而且理由出乎意料:
有的模型其实是被训练成"在文本里用一套特定标记"来喊工具的, 走厂商的结构化工具通道反而绕远。
这句话推翻了一个想当然:"有原生工具调用就一定用原生的"。 模型在训练时见的是什么格式,用那个格式它就最稳。 这跟 aider 的"用模型最熟悉的文本格式"、open-interpreter 的"按模型伪装成它熟悉的外壳"是同一条。
登记了 11 种方言,编码风格差异很大——有的用标签嵌套,有的用特殊符号包裹,有的用代码围栏,有的用通道标记。未知模型走一种兜底方言。
一个容易忽略的细节:即便走原生工具,给模型看的示例也得用它母语的方言,不然模型学不会。
这条很妙:"示例用什么格式"和"实际用什么通道"是两件事。 示例是给模型看的,要用它熟悉的;通道是给程序用的,可以是结构化的。
决定四:跑偏时不等生成完,边吐字边查
它要解决的问题:等模型把一整段错的东西生成完、你再事后检查,太晚了。
做法:在模型还在流式吐字时就逐块匹配规则,一旦命中——中止这次生成,回到这个回合的起点,把规则作为系统提醒注入进去,重来一遍。
它的比喻:就像时间旅行,把跑偏的那次生成当没发生过。 (依据:Agent 库 · oh-my-pi · 长程上下文治理与模型转向:compaction / TTSR / subagents / advisor —— TTSR 在模型流式吐字时逐块匹配规则,命中就中止这次生成、回到这个回合的起点、把规则作为系统提醒注入后重来一遍)
这跟 mirothinker 的回滚是同一个动作,但时机早一步:
- mirothinker:模型说完了,量一量,废了就撤;
- oh-my-pi:模型正在说,边听边量,不对就打断。
早打断省钱(不用生成完整段),但要求规则能在片段上判断。
规则可以是正则,也可以是代码结构模式(只对编辑类的流用),还能限定作用范围(正文/思考/某类文件的编辑)。
一个贴心推断:如果条件写得像个文件通配符,就自动翻译成"编辑这类文件时"的作用范围,再补一个通配条件。
"让用户写规则不必懂全套语法"是个好设计原则,值得记。
四块"电池":主循环之外的支撑
| 机制 | 解决哪类麻烦 | 一句话 |
|---|---|---|
| 压缩 | 上下文会爆 | 把旧历史总结成一段话 |
| 边跑边查 | 模型会跑偏 | 一犯规就中止、提醒、从原点重来 |
| 子代理 | 上下文会爆 + 分工 | 把子任务外包给隔离的子进程,只收回结论 |
| 顾问 | 模型会跑偏 | 第二个模型全程旁观,该拦就拦、该提醒就提醒 |
它的直觉句:主循环是发动机,这四套是仪表盘 + 刹车 + 拖车 + 副驾。没有它们车也能开,但开不远、开不稳。
这句话对本课题的立意很重要: 主循环只管"收到消息 → 想 → 调工具 → 再想"这个单回合,它不管跑不跑得远。
跟 kun 的"全部工程量不在转,而在打断和纠偏"是同一个判断的另一种表述。 两家都在说:循环骨架不难,难的是它外面那一圈。
"第二个模型旁观纠偏"这一条是本课题读到现在唯一的一家。 每回合结束喂它增量,它可以插话。
这是"怎么知道它好不好"这一圈的一个实现选项,记下来。
它没回答什么
- 循环骨架本身——在第 1 章,本轮没读。
- 一批工具怎么并发——不在这两篇。
- 结果怎么回填——方言层负责编码结果的文本形式,但回填策略不在这两篇。
坑与代价
- 十一种方言意味着十一套编解码要维护。 模型家族更新了标记,这一层就要跟着改。
- 从文本流里解析要处理"半截标记"。 流式吐字时一个标记可能被切成两块,扫描器必 须是增量的、有状态的。
判断(无锚): 这是带内方案最贵的一笔隐藏成本。最小原型如果只接一家支持原生工具调用的厂商,完全不必碰。 如果错,会错在: 如果我们从一开始就要跑本地开源模型,那这一层迟早要写,早点抽出接口反而省事。
- 边生成边查会误伤。 规则命中的是片段,而片段可能只是模型正要说"我不应该这么写"的开头。
- "从原点重来"意味着这一轮的所有生成都白费。 打断得越晚越浪费,打断得越早越容易误判。