尧图精选

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

🕒 发布时间:2026/9/10 16:32:31 📁 来源:尧图网络
深入解析 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-piGemma 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注册表管理glm、hermes、kimi、xml、anthropic、deepseek、minimax、harmony、qwen3、gemini、gemma共 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—序列开始\|turnturn\|一个对话轮次role 名是正文第一行\|tool_calltool_call\|模型发出的一次工具调用\|tool_responsetool_response\|回填给模型的工具结果\|channelchannel\|推理通道\|channelthought开启思维链以channel\|闭合\|\|\|\|字符串字面量定界符两端相同eos—序列结束源码中的对应常量定义于 gemma.tsconst CALL_OPEN |tool_call; const CALL_CLOSE tool_call|; const STRING ||; const RESPONSE_OPEN |tool_response; const RESPONSE_CLOSE tool_response|; const THOUGHT_OPEN |channelthought\n; const THOUGHT_CLOSE channel|;之所以用||这样一个token而非 ASCII 引号作为字符串定界符是因为字符串值内部可以出现原始的双引号与逗号而无需任何转义——唯一不能出现在字符串内的字节序列就是||本身。这一点由gemmaValuegemma.ts的渲染逻辑保证字符串一律${STRING}${value}${STRING}包裹。三、对话轮次结构|turn与 role 映射每个轮次为|turn{role}\n{body}turn|轮次之间直接拼接、无任何分隔符只有 role 后的\n是字面量。role 取值包括system、user、modelOMP 中的developer消息在gemma方言下渲染为system。这一映射体现在 gemma.tsconst role message.role developer ? system : message.role; out gemmaTurn(role, messageContentText(message.content));gemmaTurngemma.ts把 role 与正文拼成|turn${role}\n${body}turn|。生成提示时流会在|turnmodel\n处结束由模型续写。工具调用与其结果被放在同一个model轮次内——重渲染历史时|tool_response紧跟对应的|tool_call块。renderTranscriptgemma.ts遍历消息数组时通过assistantTranscriptParts拆分出推理、正文与工具调用并用collectToolResultRun聚合紧随的toolResult消息从而把调用与结果合并进同一model轮次。四、工具定义的双轨序列化gemma方言的 prompt 会携带每个工具归一化后的 wire schema采用两种互补的呈现方式1. 行内工具目录renderToolCatalog由 catalog.ts 生成每个工具一个紧凑的 OpenAI 风格 JSON 对象、每行一个放在tools/tools之内随后通过 prompt-template.md 中的{{TOOLS}}与{{DIALECT}}占位符把工具目录与 gemma.md 格式指南拼进系统提示见renderInbandToolPromptcatalog.tstools {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]}}} /tools2. 详细工具清单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_callcall:get_current_temperature{location:||London||}tool_call|{…}内部的值文法如下值类型编码示例字符串\|\|text\|\|location:\|\|London\|\|int / float裸值count:42布尔裸值flag:truenull裸值unit:null列表[v,v,…]tags:[\|\|a\|\|,\|\|b\|\|]嵌套对象{k:v,…}config:{theme:\|\|dark\|\|}其中 key 必须匹配/^[A-Za-z_]\w*$/。OMP 渲染端renderToolCallgemma.ts按key:value拼接、gemmaValue递归编码各类值保证模型看到的格式与解析器完全一致。六、流式解析原理GemmaInbandScanner状态机OMP 对 Gemma 块的解析不是一次性正则匹配而是流式的GemmaInbandScannergemma.ts它维护outside/tool/thinking三种状态通过feed(text)增量投喂字节、flush()在流结束时冲刷缓存。对每个|tool_call块解析流程分三步找闭合符findCallClosegemma.ts从缓冲中寻找tool_call|期间用skipGemmaStringgemma.ts跳过所有||…||字符串区间——因此即使字符串值内部出现了tool_call|序列也不会提前截断块匹配头部parseGemmaCall用正则/^call:\s*([A-Za-z_]\w*)\s*\{/匹配call:NAME{随后matchDelimgemma.ts按括号深度找到匹配的}切分参数parseGemmaArgs用splitTopLevelgemma.ts在顶层逗号处切分key:value对——[]/{}括号深度与||字符串区间均被跳过——再由parseGemmaValuegemma.ts按文法递归解码||开头为字符串、[开头为列表、{开头为嵌套对象、true/false为布尔、null/none/None为 null、数值形如/^[-]?(\d|\.)/则尝试转为数字其余当作裸字符串如未加引号的枚举或类型名STRING。值得注意的行为边界事件只在完整闭合后发出只有收到完整的tool_call|闭合符后才触发toolStart/toolEnd事件id 由mintToolCallId现场铸造不存在部分参数事件未闭合块的处理若流被flush()且存在未闭合的 tool 块OMP 会丢弃该不完整块但语法上已闭合、仅缺结尾参数括号的块仍会基于可用正文完成解析推理通道当parseThinking默认开启时扫描器把|channelthought\n…channel|路由为thinkingStart/thinkingDelta/thinkingEnd事件使其不进入可见回复同时继续解析其后出现的工具调用renderThinking则把推理原样往返回同一格式。若以parseThinking: false构造通道内容会留在可见文本中见 gemma.ts。七、并行工具调用更多的块而非块内的更多条目与 JSONtool_calls[]数组一个块多条不同Gemma 4 的并行是一个块一次调用连续输出多个|tool_call…tool_call|块即表示并行调用按出现顺序返回应用侧按同样顺序为每次调用回填一个|tool_response。八、工具结果格式response:NAME{output:…}每个结果为|tool_responseresponse:NAME{output:VALUE}tool_response|。renderToolResultsgemma.ts始终把结果包在单一outputkey 下并先用JSON.parse尝试解析工具文本若工具输出是 JSON则解析为花括号语法中的嵌套对象/数组若是普通字符串则包上||…|||tool_responseresponse:get_current_weather{output:{temperature:15,weather:||sunny||}}tool_response| |tool_responseresponse:read{output:||FILE||}tool_response|Gemma 的 wire 格式没有独立的成功/失败字段OMP 对isError的结果也渲染为与成功结果相同的response:NAME{output:…}形状任何失败指示都必须体现在结果文本自身中。九、端到端示例一次天气查询的完整往返renderTranscript对一次天气查询的输出如下系统轮次还携带tools目录与格式指南此处省略模型调用与其工具响应合并进同一model轮次最终回答是下一个model轮次轮次间无分隔符仅 role 后的\n为字面量bos|turnsystem You are a helpful assistant.turn||turnuser Hey, whats the weather in Tokyo right now?turn||turnmodel |tool_callcall:get_current_weather{location:||Tokyo, JP||}tool_call||tool_responseresponse:get_current_weather{output:{temperature:15,weather:||sunny||}}tool_response|turn||turnmodel 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 是合成的格式本身不带 idOMP 在收到完整闭合块后铸造新 id 并发出相邻的toolStart/toolEnd事件渲染出的响应通过消息顺序/名称关联。NAME 必须匹配已列出函数参数为逗号分隔的key:value对多个调用必须输出为连续块正文放在块外推理只能放在调用前的|channelthought…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.formatauto会为它错误地选择 Gemma 4 语法——此时需显式设置tools.formatgemini以使用 Pythonic 约定参见 demotion.ts 中对跨模型推理降级的处理Harmony 与 Gemma 的renderThinking会输出聊天模板控制 token不允许出现在结构化原生消息内因此降级为普通think块。十一、小结Gemma 4 的 token 化工具调用方言把协议从提示词工程推进到了专用 token 层|tool_callcall:NAME{…}承载调用、|tool_responseresponse:NAME{output:…}承载结果、|channelthought承载推理配合||字符串定界符规避了传统 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),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →