news 2026/9/10 16:32:20

深入解析 oh-my-pi 的 Gemma 4 工具调用方言:`call:NAME{key:value,…}` token 流格式与流式解析实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析 oh-my-pi 的 Gemma 4 工具调用方言:`call:NAME{key:value,…}` token 流格式与流式解析实现

深入解析 oh-my-pi 的 Gemma 4 工具调用方言:call:NAME{key:value,…}token 流格式与流式解析实现

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

Gemma 4 是 Google 开放权重模型家族中对工具调用范式的一次彻底革新:它告别了 Gemma 3 与托管版 Gemini 沿用的 Pythonictool_code风格,改用专用特殊 token 与紧凑的花括号语法。本文以 oh-my-pi(下称 OMP)中gemma方言的实现为核心,完整讲解这一 token 分隔式工具调用协议——从特殊 token、轮次结构、工具目录序列化,到GemmaInbandScanner的流式解析原理与各类解析陷阱,读者读完即可理解并复现整套工具调用与结果回填的编解码流程。

一、背景:Gemma 4 的 token 化工具调用与 OMP 方言体系

在 OMP 中,不同厂商、不同家族的模型各自对应一套"方言"(dialect),统一由 factory.ts 中的DIALECT_DEFINITIONS注册表管理(glmhermeskimixmlanthropicdeepseekminimaxharmonyqwen3geminigemma共 11 种)。每个方言通过 types.ts 中的DialectDefinition接口约束四个核心能力:

  • createScanner:创建流式扫描器(InbandScanner),负责把模型输出流中的工具调用/推理块解析为事件;
  • renderToolCall/renderAssistantToolCalls:把结构化ToolCall渲染回文本;
  • renderToolResults:把工具执行结果渲染回模型上下文;
  • renderThinking/renderTranscript:推理块渲染与整段对话历史渲染。

Gemma 4 的gemma方言定义在 gemma.ts,其格式指南即本文主文档 gemma.md,更详细的协议说明见 docs/toolconv/gemma.md。该方言的目标模型为 Google Gemma 4 指令微调系列(如google/gemma-4-E2B-it)。与 Gemma 3 / 托管版 Gemini 的tool_code/default_api形式(见 docs/toolconv/gemini.md)完全不兼容,两者不可互换。

二、特殊 token 表:不对称的管道位置

Gemma 4 用成对的特殊 token 包裹每个结构单元。注意其不对称管道规则:开启符的管道在左侧(<|x>),闭合符的管道在右侧(<x|>)。

开启符闭合符用途
<bos>序列开始
<\|turn><turn\|>一个对话轮次;role 名是正文第一行
<\|tool_call><tool_call\|>模型发出的一次工具调用
<\|tool_response><tool_response\|>回填给模型的工具结果
<\|channel><channel\|>推理通道;<\|channel>thought开启思维链(以<channel\|>闭合)
<\|"\|><\|"\|>字符串字面量定界符(两端相同)
<eos>序列结束

源码中的对应常量定义于 gemma.ts:

const CALL_OPEN = "<|tool_call>"; const CALL_CLOSE = "<tool_call|>"; const STRING = '<|"|>'; const RESPONSE_OPEN = "<|tool_response>"; const RESPONSE_CLOSE = "<tool_response|>"; const THOUGHT_OPEN = "<|channel>thought\n"; const THOUGHT_CLOSE = "<channel|>";

之所以用<|"|>这样一个token而非 ASCII 引号作为字符串定界符,是因为字符串值内部可以出现原始的双引号与逗号而无需任何转义——唯一不能出现在字符串内的字节序列就是<|"|>本身。这一点由gemmaValue(gemma.ts)的渲染逻辑保证:字符串一律"${STRING}${value}${STRING}"包裹。

三、对话轮次结构:<|turn>与 role 映射

每个轮次为<|turn>{role}\n{body}<turn|>,轮次之间直接拼接、无任何分隔符,只有 role 后的\n是字面量。role 取值包括systemusermodel;OMP 中的developer消息在gemma方言下渲染为system。这一映射体现在 gemma.ts:

const role = message.role === "developer" ? "system" : message.role; out += gemmaTurn(role, messageContentText(message.content));

gemmaTurn(gemma.ts)把 role 与正文拼成<|turn>${role}\n${body}<turn|>。生成提示时流会在<|turn>model\n处结束,由模型续写。工具调用与其结果被放在同一个model轮次内——重渲染历史时<|tool_response>紧跟对应的<|tool_call>块。renderTranscript(gemma.ts)遍历消息数组时,通过assistantTranscriptParts拆分出推理、正文与工具调用,并用collectToolResultRun聚合紧随的toolResult消息,从而把调用与结果合并进同一model轮次。

四、工具定义的双轨序列化

gemma方言的 prompt 会携带每个工具归一化后的 wire schema,采用两种互补的呈现方式:

1. 行内工具目录(renderToolCatalog:由 catalog.ts 生成,每个工具一个紧凑的 OpenAI 风格 JSON 对象、每行一个,放在<tools></tools>之内;随后通过 prompt-template.md 中的{{TOOLS}}{{DIALECT}}占位符,把工具目录与 gemma.md 格式指南拼进系统提示(见renderInbandToolPrompt,catalog.ts):

<tools> {"type":"function","function":{"name":"get_current_temperature","description":"Gets the current temperature for a given location.","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city name, e.g. San Francisco"}},"required":["location"]}}} </tools>

2. 详细工具清单(renderToolInventory:由 inventory.ts 生成,供系统提示与/dump命令共用。它以## functions开头,输出一个namespace functions { … }代码块:每个工具的描述作为//注释行放在type NAME = (_: PARAMS);类型声明上方,配置过的示例以 JSDoc 风格的// @example注释呈现。它不输出 Markdown 小节,也不输出原生 Gemma 的<|tool_call>示例。

五、工具调用格式:call:NAME{key:value,…}值文法

模型每次调用输出一个<|tool_call>…<tool_call|>块,正文为call:NAME{ARGS},其中ARGS是逗号分隔的key:value对列表:

<|tool_call>call:get_current_temperature{location:<|"|>London<|"|>}<tool_call|>

{…}内部的值文法如下:

值类型编码示例
字符串<\|"\|>text<\|"\|>location:<\|"\|>London<\|"\|>
int / float裸值count:42
布尔裸值flag:true
null裸值unit:null
列表[v,v,…]tags:[<\|"\|>a<\|"\|>,<\|"\|>b<\|"\|>]
嵌套对象{k:v,…}config:{theme:<\|"\|>dark<\|"\|>}

其中 key 必须匹配/^[A-Za-z_]\w*$/。OMP 渲染端renderToolCall(gemma.ts)按key:value拼接、gemmaValue递归编码各类值,保证模型看到的格式与解析器完全一致。

六、流式解析原理:GemmaInbandScanner状态机

OMP 对 Gemma 块的解析不是一次性正则匹配,而是流式的GemmaInbandScanner(gemma.ts),它维护outside/tool/thinking三种状态,通过feed(text)增量投喂字节、flush()在流结束时冲刷缓存。

对每个<|tool_call>块,解析流程分三步:

  1. 找闭合符findCallClose(gemma.ts)从缓冲中寻找<tool_call|>,期间用skipGemmaString(gemma.ts)跳过所有<|"|>…<|"|>字符串区间——因此即使字符串值内部出现了<tool_call|>序列,也不会提前截断块;
  2. 匹配头部parseGemmaCall用正则/^call:\s*([A-Za-z_]\w*)\s*\{/匹配call:NAME{,随后matchDelim(gemma.ts)按括号深度找到匹配的}
  3. 切分参数parseGemmaArgssplitTopLevel(gemma.ts)在顶层逗号处切分key:value对——[]/{}括号深度与<|"|>字符串区间均被跳过——再由parseGemmaValue(gemma.ts)按文法递归解码:<|"|>开头为字符串、[开头为列表、{开头为嵌套对象、true/false为布尔、null/none/None为 null、数值形如/^[+-]?(\d|\.)/则尝试转为数字,其余当作裸字符串(如未加引号的枚举或类型名STRING)。

值得注意的行为边界:

  • 事件只在完整闭合后发出:只有收到完整的<tool_call|>闭合符后才触发toolStart/toolEnd事件(id 由mintToolCallId现场铸造),不存在部分参数事件;
  • 未闭合块的处理:若流被flush()且存在未闭合的 tool 块,OMP 会丢弃该不完整块;但语法上已闭合、仅缺结尾参数括号的块,仍会基于可用正文完成解析;
  • 推理通道:当parseThinking(默认开启)时,扫描器把<|channel>thought\n…<channel|>路由为thinkingStart/thinkingDelta/thinkingEnd事件,使其不进入可见回复,同时继续解析其后出现的工具调用;renderThinking则把推理原样往返回同一格式。若以parseThinking: false构造,通道内容会留在可见文本中(见 gemma.ts)。

七、并行工具调用:更多的块,而非块内的更多条目

与 JSONtool_calls[]数组"一个块多条"不同,Gemma 4 的并行是一个块一次调用:连续输出多个<|tool_call>…<tool_call|>块即表示并行调用,按出现顺序返回;应用侧按同样顺序为每次调用回填一个<|tool_response>

八、工具结果格式:response:NAME{output:…}

每个结果为<|tool_response>response:NAME{output:VALUE}<tool_response|>renderToolResults(gemma.ts)始终把结果包在单一outputkey 下,并先用JSON.parse尝试解析工具文本:若工具输出是 JSON,则解析为花括号语法中的嵌套对象/数组;若是普通字符串,则包上<|"|>…<|"|>

<|tool_response>response:get_current_weather{output:{temperature:15,weather:<|"|>sunny<|"|>}}<tool_response|> <|tool_response>response:read{output:<|"|>FILE<|"|>}<tool_response|>

Gemma 的 wire 格式没有独立的成功/失败字段:OMP 对isError的结果也渲染为与成功结果相同的response:NAME{output:…}形状,任何失败指示都必须体现在结果文本自身中。

九、端到端示例:一次天气查询的完整往返

renderTranscript对一次天气查询的输出如下(系统轮次还携带<tools>目录与格式指南,此处省略;模型调用与其工具响应合并进同一model轮次,最终回答是下一个model轮次;轮次间无分隔符,仅 role 后的\n为字面量):

<bos><|turn>system You are a helpful assistant.<turn|><|turn>user Hey, what's the weather in Tokyo right now?<turn|><|turn>model <|tool_call>call:get_current_weather{location:<|"|>Tokyo, JP<|"|>}<tool_call|><|tool_response>response:get_current_weather{output:{temperature:15,weather:<|"|>sunny<|"|>}}<tool_response|><turn|><|turn>model The current weather in Tokyo is 15 degrees Celsius and sunny.<turn|>

十、解析陷阱与实战注意事项

结合主文档 gemma.md 的规则与 docs/toolconv/gemma.md 的提示,整理如下关键约束:

  • 字符串定界符是 token 而非引号<|"|>…<|"|>内部的",都是字面数据——例如<|"|>The city and state, e.g. "San Francisco, CA"…<|"|>同时包含两者。只能在<|"|>…<|"|>区间之外,/}切分参数;渲染端同样禁止 HTML 转义(写a & b,绝不写a &amp; b)。
  • 管道位置不对称:闭合符是<tool_call|>,不是</tool_call>也不是<|tool_call>——写错管道方向将永远无法闭合块。
  • 一次一块:并行 = 更多块,而不是一个块里更多条目。
  • 裸标量:未用<|"|>包裹的值中,true/false→ 布尔,null/none→ null,数字 → 数字,其余按裸字符串处理。
  • 调用 id 是合成的:格式本身不带 id;OMP 在收到完整闭合块后铸造新 id 并发出相邻的toolStart/toolEnd事件,渲染出的响应通过消息顺序/名称关联。
  • NAME 必须匹配已列出函数:参数为逗号分隔的key:value对;多个调用必须输出为连续块,正文放在块外;推理只能放在调用前的<|channel>thought…<channel|>块内,且绝不能在推理块里放工具调用;每个<|tool_response>必须按调用顺序读取,模型侧永远不要自己写出<|tool_response>块;每次调用必须写完整后再停止,不能先宣布再停顿。
  • 与 Gemma 3 / 托管 Gemini 的区别:它们使用gemini.md中的 Pythonictool_code/default_api形式;Gemma 4 用本 token 语法取而代之,两者不可互换。
  • Gemma 3 自动选择警告:OMP 当前的家庭亲和映射把 Gemma 3 与 Gemma 4 的模型 id 都归到gemma。若某个 Gemma 3 模型标记为supportsTools: false,则tools.format=auto会为它错误地选择 Gemma 4 语法——此时需显式设置tools.format=gemini以使用 Pythonic 约定(参见 demotion.ts 中对跨模型推理降级的处理:Harmony 与 Gemma 的renderThinking会输出聊天模板控制 token,不允许出现在结构化原生消息内,因此降级为普通<think>块)。

十一、小结

Gemma 4 的 token 化工具调用方言把"协议"从提示词工程推进到了专用 token 层:<|tool_call>call:NAME{…}承载调用、<|tool_response>response:NAME{output:…}承载结果、<|channel>thought承载推理,配合<|"|>字符串定界符规避了传统 JSON 的转义地狱。OMP 通过 gemma.ts 中的流式扫描器与一组对称的渲染器,实现了对该协议的完整双向支持;理解其状态机与括号/字符串感知的切分逻辑,是排查任何 Gemma 4 工具调用问题的钥匙。若需在 OMP 中为其他模型家族实现类似协议,factory.ts 的DialectDefinition注册表与createInbandScanner提供了可直接套用的标准骨架。

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 16:31:22

深度学习智慧监考系统:从目标检测到行为判定的工程实践

简介&#xff1a;这套智慧监考系统基于深度学习计算机视觉技术&#xff0c;面向考试作弊自动检测场景&#xff0c;适合计算机、人工智能、数据科学等专业的学生、教师及企业开发者使用&#xff0c;可支撑毕业设计、课程设计或项目演示。压缩包共二百一十八个文件&#xff0c;核…

作者头像 李华
网站建设 2026/9/10 16:29:06

2026清远化工产品成分分析检测排名 TOP5 CMA 资质提供含量检测、纯度检测、元素分析 联系方式推荐

清远本地化工产品成分分析检测机构星罗棋布&#xff0c;化工企业、新材料厂商、日化生产工厂、橡塑制造业以及食品医药企业的研发质检部门&#xff0c;在筛选服务商时极易误入无正规资质的检测陷阱。这类机构出具的成分分析报告不具备法律效力&#xff0c;无法通过市场监管部门…

作者头像 李华
网站建设 2026/9/10 16:27:46

CVAT快捷键:把鼠标放回桌上的6个时刻

CVAT快捷键&#xff1a;把鼠标放回桌上的6个时刻 【免费下载链接】cvat Computer Vision Annotation Tool (CVAT) is a leading platform for building high-quality visual datasets for vision AI. It offers open-source, cloud, and enterprise products, as well as label…

作者头像 李华