oh-my-pi v15.13 发布 —— 以前只有 GLM 语法,现在给每个模型写了方言
oh-my-pi(简称 Pi)今天一口气发了两版(v15.13.2 + v15.13.3),两版都在做同一件事:把 tool-calling 从「一套语法走天下」变成「每个模型用母语说话」。以前只有 GLM 一个 in-band 语法选项(用来在不能发 native tools 的场景下兜底),现在覆盖了 8 种;同时 Azure OpenAI 以一等公民正式入驻,还多了 interruptible 机制——agent 可以在工具执行中途听指挥改方向了。

以前:不是不会 multi-tool,是只说一种方言
Pi 对比其他编程助手的核心差异之一,是有一套 in-band tool-calling 系统——当模型不支持原生 tool calls 或者你需要更精细的控制时,模型通过文本来「声明」它要调什么工具,而不是发 native tool calls。
但 v15.13 之前,这套系统只支持 GLM(智谱)一种语法。PI_OWNED_TOOLS=1 就是 GLM 模式,别无选择。问题在于:每个模型家族的 in-band 格式差异巨大。同一个 tool-calling 语义,Claude 用 <tool_use> XML 块表达,DeepSeek 用 DSML,GPT-5 是 Harmony 的 to=functions... headers,Hermes 用 <|tool_call|> token——它们写的是同一件事,但语法天差地别。
现在:8 种语法,每个模型说自己的语言
v15.13.2 最核心的变化,就是把 PI_OWNED_TOOLS 从布尔开关变成了语法选择器。PI_OWNED_TOOLS=<syntax> 直接指定语法,并且后台自动为每个模型匹配它最舒服的格式——preferredToolSyntax(modelId) 函数从模型家族 token 推断:Claude → anthropic、GLM → glm、Kimi → kimi、Qwen → qwen3、DeepSeek → deepseek、OpenAI/gpt-oss → harmony,其余 fallback 到通用 xml。
我特别喜欢这个设计的一个细节:每种语法都配了独立的 scanner、prompt、history renderer、stream adapter。不是「把不同语法转成统一格式再处理」,而是在各自的范式内部直接工作。代价是开发成本高——每新加一个语法都要从头写一套解析/渲染管线——但收益是 tool-calling 更准确,不会因为格式转换丢失边缘 case。
配套的 ToolExample 类型系统也同步重构了。13 个内置工具(eval、browser、todo、patch 等)的 examples 从手写 markdown <examples> 块变成了类型化的 ToolExample<z.input<typeof schema>> 数据,AI 层自动用模型的母语语法渲染。你在配置中看到的文档示例,现在是「到哪个山头唱哪首歌」。
v15.13.3:打断正在执行的工具、Azure OpenAI 正式入驻
v15.13.3 同一天跟进,集中在三个方向:
打断机制(interruptible):新增 interruptible 工具字段。当 agent 处于 immediate 中断模式时,如果用户在工具执行中途发了一条 steering 消息,agent 可以提前终止当前工具、立即处理这条指令。以前想发一条「别查了先看看这个文件」只能等工具跑完。现在不等了,说到一半就能打断。
Azure OpenAI 入驻:Azure 用户可以直接认领 GPT-4/4.1/4o、GPT-5 家族、o-series、Codex 等模型,通过 AZURE_OPENAI_API_KEY 激活,deployment URL 由 AZURE_OPENAI_BASE_URL 或 AZURE_OPENAI_RESOURCE_NAME 决定。Azure 的 Responses API 在 reasoning 模型上有个小差异——xhigh 等 effort 控制项需要经过 Azure proxy 层额外处理——Pi 已经完成了兼容适配,推理模型的 effort 控制可以正常工作。
Gemini/Gemma 语法:增加了 Gemini 的 ```tool_code Python 风格语法和 Gemma 的 <|tool_call|> token 分隔语法。更重要的是修复了解析器在 Python 注释、字符串字面量等边界情况下的 bug——如果解析器在注释里看到 ```tool_code 就以为要调工具,session export 会多一堆虚假调用记录。
两个藏得很深的流式 bug 修复
Cursor MCP 参数的重复拼接(#2615):用 task 工具做多子 agent 调度时,Cursor provider 的 stream handler 在拼接 args_text_delta 时把累积的快照追加到缓冲区上,导致 JSON 被重复拼接——tasks 数组收到的是 ["task1"]["task1task2"] 而非 ["task1","task2"]。修复后 handler 会去掉已缓冲的前缀,并且把 McpArgs map 合并进流式参数而不是直接覆盖。
Codex 并发 tool-call 的 item_id 污染(#2619):同时并发两个 tool-call 时,args_text_delta 只跟踪一个 currentItem,第二个 tool-call 的 delta 被错误地追加到第一个的参数里。现在按 item_id 做 key 来路由 delta 事件,output_index 兜底,同一工具的多 call 不再互相污染。
这两个 bug 藏得都不浅——你不做多子 agent 并发调度、不用 Cursor/Codex provider,永远不会遇到。但对使用这些场景的人来说,每次 task 报 Invalid input: expected array, received undefined 的根源就在这里。
hashline DSL 大改名
hashline(Pi 的行内编辑 DSL)把所有操作符换成了缩写关键词。影响面不小——如果你在写自动化脚本或用 hashline 做批处理:
replace → SWAP delete → DEL
insert before → INS.PRE insert after → INS.POST
insert head → INS.HEAD insert tail → INS.TAIL
replace_block → SWAP.BLK delete_block → DEL.BLK
insert_after_block → INS.BLK.POST
Pi 对新旧格式同时解析(.= 也替代了 .. 作为推荐的范围分隔符),现有脚本不会立刻崩,但新写法应该开始适应了。我的建议是:写新脚本直接上缩写,旧脚本等跑出问题时顺手改。
Unexpected Stop 检测 + 模型默认更新
v15.13.3 加入了一个轻量检测器 unexpectedStopDetection:当模型 assistant 回复中说「我要调工具了」但最终没有发出任何 tool call 时,一个 tiny/smol 分类器识别出这个模式并继续这一轮,不让对话干等。默认最多重试 3 次。
Pi 还为大量 provider 更新了默认模型指向:OpenAI 家族 → GPT-5.5,Gemini → gemini-3.1-pro-preview,GLM → glm-5.2,Kimi → kimi-k2.7-code,Qwen → qwen3.7-plus,Claude 代理中转类 → Claude Opus 4.x。如果你在用默认模型,现在打开 Pi 就是新版本。Fireworks 还新增了 flux-1-schnell-fp8、gpt-oss-20b、qwen3-embedding-8b、qwen3-reranker-8b,Google 新增了 Gemma 4 E2B IT 和 Gemma 4 E4B IT。
升级
# macOS / Linux(重新运行即升级)
curl -fsSL https://omp.sh/install | sh
# Homebrew
brew upgrade can1357/tap/omp
# Bun(推荐,与 CI 同速)
bun update -g @oh-my-pi/pi-coding-agent
# mise 管理版本
mise use -g github:can1357/oh-my-pi
📢 关注我,获取最新开源更新日志和开发者工具动态。
#ohMyPi #InBandToolCalling #AzureOpenAI #CodingAgent #开源工具