llama.cpp PEG 解析器实战指南:流式解析模型输出、构建 AST 与生成 GBNF 文法
本文基于 llama.cpp 的官方开发文档 docs/development/parsing.md,系统讲解 common 库中的 PEG(Packrat/Earley 风格)解析器:如何用组合子(combinators)以 DSL 方式构建模型输出解析器,如何通过"标签节点"生成带语义的 AST,以及如何将同一套 PEG 定义转换为 GBNF 文法约束采样。读完本文,你将能够自行编写针对特定模型输出格式的解析器,并理解它与聊天模板、语法约束生成之间的协作关系。
一、定位:两套类型前缀与三大特性
llama.cpp 的 common 库内置了一个专为"解析模型输出"设计的 PEG 解析器。文档明确将其分为两类 API:
common_peg_*前缀:通用类型,面向通用解析场景,可用于解析模型输出,也可用于解析用户提供的正则模式等其它用途。核心实现位于 common/peg-parser.h 与 common/peg-parser.cpp;common_chat_peg_*前缀:面向模型输出的专用助手,封装了聊天消息的 builder 与 mapper。核心实现位于 common/chat-peg-parser.h 与 common/chat-peg-parser.cpp。
文档列出的三大核心特性,在源码中都能找到直接对应:
-
流式输入的部分解析(partial parsing)。解析结果类型
common_peg_parse_result_type定义了三种状态:FAIL、SUCCESS、NEED_MORE_INPUT(见 common/peg-parser.h 第 66-70 行)。当模型输出尚未到达终止符时,解析器不会报错,而是返回NEED_MORE_INPUT并保留已确认的 AST 节点——在 common/peg-parser.cpp 中可以看到大量COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT的返回点,分别覆盖字面量、重复、字符类等各组合子的"输入可能还没完"分支。每个 AST 节点还带有is_partial标志(common_peg_ast_node::is_partial),mapper 在提取数据时会跳过尚未闭合的部分节点(例如 common/chat-peg-parser.cpp 中if (!node.is_partial)的大量判断)。这正是逐 token 流式解析得以工作的基础。 -
内置 JSON 解析器。builder 直接提供
json()、json_object()、json_array()、json_string()、json_number()、json_bool()、json_null()、json_string_content()、json_member(key, p)等组合子;此外源码中还有文档未展开的 Python 值解析器(python_value()、python_dict()等)以及marker()、quoted_string()等工具(见 common/peg-parser.h 第 451-489 行)。 -
带语义标签的 AST 生成(tagged nodes)。通过
tag(tag, p)组合子给子解析器打上语义标签,多个节点可共享同一标签;解析完成后用 mapper 按标签从 AST 中抽取结构化数据(如reasoning_content、content、arguments)。AST 由common_peg_ast_arena管理,节点包含rule、tag、start/end、text、children与is_partial字段,并提供find_by_tag/find_by_rule辅助检索。
二、完整示例:解析带 JSON 参数的工具调用
以下是文档给出的示例,演示如何解析"先输出正文、再输出 `
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00