- 人工智能
- 大模型
- 推理引擎
- 本地部署
【免费下载链接】PowerInfer
High-speed Large Language Model Serving for Local Deployment
本篇技术指南以 smallthinker/grammars/README.md 为核心文档,系统讲解 GBNF(GGML BNF)文法格式的完整语法:生产规则、终结符/非终结符、字符区间、序列与选择、重复与可选符号、注释规则与 root 规则,并结合当前仓库中smallthinker/grammars/目录下 8 个真实.gbnf示例文件、smallthinker/tools/main与smallthinker/tools/server的实际用法,以及 JSON Schema 到 GBNF 的转换流程与已知限制。读完本文,你将能够独立编写、调试 GBNF 文法,把模型输出严格约束为合法 JSON、代码或任意自定义格式,并掌握避免常见性能陷阱的写法。
GBNF 是什么
GBNF(GGML BNF)是 llama.cpp 系列项目(包括本仓库 PowerInfer 的smallthinker子目录)用于定义形式文法的格式,其核心用途是在采样阶段约束模型输出。例如,你可以用它强制模型生成合法的 JSON、只输出 emoji,或者严格按某种自定义协议输出文本。
GBNF 文法的底层原理是:在解码(采样)的每一步,用一个状态机根据当前文法推导出下一个允许出现的 token 集合,然后只在允许的 token 中做采样。本仓库的smallthinker/tools/main/main.cpp中,通过common_sampler_accept(smpl, id, /* accept_grammar= */ true)(main.cpp)在生成阶段将已采样的 token 反馈给文法状态机;而对 prompt 前缀则用accept_grammar=false(main.cpp)跳过文法约束。这一调用链印证了 GBNF 是逐 token 生效的约束机制,而不是生成后的后处理校验。
GBNF 文法在smallthinker/tools/main(llama-cli)和smallthinker/tools/server(llama-server)中以多种方式受支持,具体见下文「使用 GBNF 文法」。
背景:从 BNF 到 GBNF
巴科斯范式(Backus-Naur Form,BNF)是一种描述形式语言(如编程语言、文件格式、协议)语法的记号体系。GBNF 是 BNF 的扩展,主要增加了一些现代正则表达式风格的特性,例如:
- 字符区间
[a-z]、[^...]取反; - 重复操作符
*、+、?以及{m}、{m,}、{m,n}等计数重复; - 完整 Unicode 支持(可直接书写字符或用
\xXX、\uXXXX、\UXXXXXXXX转义)。
相比直接在 prompt 里要求"输出 JSON",GBNF 把格式约束从"概率性提示"升级为"硬性文法约束",从机制上消除格式非法的问题。
基础:生产规则
在 GBNF 中,我们定义生产规则(production rules),它规定一个**非终结符(non-terminal,即规则名)如何被替换为终结符(terminal,即实际字符,具体指 Unicode 码点)**序列和其他非终结符的组合。一条生产规则的基本格式是:
nonterminal ::= sequence...- 左侧是规则名(非终结符),右侧是替换序列;
- 每个规则用
::=分隔左右两侧。
示例:国际象棋记谱文法
在深入语法细节之前,先看 smallthinker/grammars/chess.gbnf 这个小型国际象棋记谱文法,它集中演示了后续要讲的多数特性:
# `root` 规定了整体输出的模式 root ::= ( # 必须以字符 "1. " 开头,随后是一段匹配 `move` 规则的字符序列, # 一个空格,另一个 move,最后是换行 "1. " move " " move "\n" # 其后是一个或多个后续着法,编号为一位或两位数字 ([1-9] [0-9]? ". " move " " move "\n")+ ) # `move` 是一个抽象表示,可以是兵、非兵或易位。 # 结尾的 `[+#]?` 表示着法之后可能有将军或将杀符号 move ::= (pawn | nonpawn | castle) [+#]? pawn ::= ... nonpawn ::= ... castle ::= ...这个片段展示了 GBNF 的几个关键点:
root规则用|分支组合了两个可选的顶层结构(第一手 vs 后续手);- 字面量字符串
"1. "、". "、"\n"直接作为终结符出现; ([1-9] [0-9]? ". " move " " move "\n")+用圆括号分组后整体重复;move、pawn、nonpawn、castle是子规则(非终结符)。
仓库中完整的 chess.gbnf 实际内容如下(括号内的省略号已展开为真实定义):
root ::= "1. " move " " move "\n" ([1-9] [0-9]? ". " move " " move "\n")+ move ::= (pawn | nonpawn | castle) [+#]? # 棋子类型、可选的文件/横排、可选吃子、目标文件与横排 nonpawn ::= [NBKQR] [a-h]? [1-8]? "x"? [a-h] [1-8] # 可选的兵行文件与吃子、目标文件与横排、可选升变 pawn ::= ([a-h] "x")? [a-h] [1-8] ("=" [NBKQR])? castle ::= "O-O" "-O"?可以看到nonpawn用[NBKQR]匹配棋子字母(N/B/K/Q/R),用[a-h]、[1-8]匹配目标格子,"x"表示吃子;pawn用([a-h] "x")?表达可选的前进/吃子前缀,用("=" [NBKQR])?表达可选的升变后缀。这正是 GBNF"用文法描述领域格式"的典型用法。
非终结符与终结符
非终结符(规则名)代表一个由终结符和其他非终结符构成的模式。GBNF 规定规则名必须是小写连字符单词(dashed lowercase word),例如move、castle、check-mate。这一点与许多文法系统不同——规则名不允许随意的大小写或下划线混写。
终结符是实际字符(码点),有两种写法:
- 字面量序列:
"1"、"O-O"; - 字符区间:
[1-9]、[NBKQR]。
例如 smallthinker/grammars/json.gbnf 中的字符串规则:
string ::= "\"" ( [^"\\\x7F\x00-\x1F] | "\\" (["\\bfnrt] | "u" [0-9a-fA-F]{4}) # escapes )* "\"" ws这里"\\"(即反斜杠转义后的\字符字面量)后接["\\bfnrt]表示 JSON 允许的转义字符集合,"u" [0-9a-fA-F]{4}表示 Unicode 转义序列\uXXXX。
字符与字符区间
终结符支持完整的 Unicode 范围。Unicode 字符可以直接写在文法中,例如 smallthinker/grammars/japanese.gbnf:
hiragana ::= [ぁ-ゟ] katakana ::= [ァ-ヿ] punctuation ::= [、-〾] cjk ::= [一-鿿]也可以用转义形式:
- 8 位:
\xXX(如\x7F、\x00); - 16 位:
\uXXXX(如\u2028); - 32 位:
\UXXXXXXXX。
字符区间可以用^取反,表示"匹配区间之外的任意字符"。例如:
single-line ::= [^\n]+ "\n"即"任意非换行字符,至少一个,直到换行"。这一写法在仓库中多处出现:smallthinker/grammars/list.gbnf的item ::= "- " [^\r\n\x0b\x0c\x85\u2028\u2029]+ "\n"用取反区间排除了各类换行/行分隔符;smallthinker/grammars/json.gbnf用[^"\\\x7F\x00-\x1F]排除了双引号、反斜杠和所有控制字符,从而保证 JSON 字符串的合法性。
序列与选择
序列(Sequence):符号在序列中的顺序是有意义的。例如"1. " move " " move "\n"中,"1. "必须出现在第一个move之前,两个move之间必须有空格,最后必须是换行。顺序直接决定了模型输出的字符次序。
选择(Alternatives):用|分隔的多个可选序列,表示其中任一满足即可。例如move ::= pawn | nonpawn | castle表示move可以是兵着法、非兵着法或易位三者之一。
圆括号()用于对序列分组,从而可以在更大的规则中嵌入选择分支,或将重复/可选操作符(见下节)作用于整个序列。例如 chess 文法中的(pawn | nonpawn | castle) [+#]?,先用括号把三选一包起来,再统一追加可选的将军/将杀后缀。
重复与可选符号
GBNF 支持以下重复与可选操作符(作用于其前面的单个符号或括号分组):
| 写法 | 含义 | 等价形式 |
|---|---|---|
x* | 重复 0 次或多次 | x{0,} |
x+ | 重复 1 次或多次 | x{1,} |
x? | 可选,出现 0 次或 1 次 | x{0,1} |
x{m} | 恰好重复 m 次 | — |
x{m,} | 至少重复 m 次 | — |
x{m,n} | 重复 m 到 n 次(含两端) | — |
x{0,n} | 最多重复 n 次(含) | — |
实际示例:
- smallthinker/grammars/arithmetic.gbnf:
expr ::= term ([-+*/] term)*用*表达任意长度的四则运算链; - smallthinker/grammars/list.gbnf:
root ::= item+用+要求至少一个列表项; - chess 文法的
([1-9] [0-9]? ". " move " " move "\n")+同时使用了?(一位或两位数字)和+(后续着法至少一组); - JSON 文法中
char{1,100}(由 JSON Schema 转换而来)用{m,n}精确限制字符串长度范围。
注释与换行
注释:用#指定,从#到行尾均被忽略:
# defines optional whitespace ws ::= [ \t\n]+换行规则:GBNF 允许在规则之间换行,也允许在圆括号内部、符号或序列之间换行;此外,在|选择标记之后换行会继续当前规则(即使不在括号内)。这一点让长文法可以排版成易读的多行形式,例如smallthinker/grammars/c.gbnf中statement规则的多行|分支排版:
statement ::= ( dataType identifier ws "=" ws expression ";" ) | ( identifier ws "=" ws expression ";" ) | ( identifier ws "(" argList? ")" ";" ) | ( "return" ws expression ";" ) | ...root 规则
在一个完整文法中,root规则永远定义文法的起点——它规定整个输出必须匹配的模式。缺少root规则的文法是不完整的。
例如,一个列表文法(仓库中的 list.gbnf 即此结构):
# a grammar for lists root ::= ("- " item)+ item ::= [^\n]+ "\n"root强制输出为若干个"- "开头的列表行。类似地,json.gbnf 的root ::= object强制整个输出是一个 JSON 对象,而 json_arr.gbnf 的root ::= arr强制输出为数组(且要求第一个元素前有换行、结尾无多余空白,适合批量生成 JSON 数组)。
仓库中的 GBNF 示例文件一览
smallthinker/grammars/目录提供了 8 个可直接使用、可对照学习的完整文法:
| 文件 | 用途与要点 |
|---|---|
| arithmetic.gbnf | 四则运算表达式求值格式,expr ::= term ([-+*/] term)*,演示运算符优先级、括号、ws空白规则 |
| c.gbnf | 类 C 语言的函数/语句文法,覆盖int/float/char声明、if/else、while、for、函数调用、单/多行注释等,是"让模型写代码"的完整示例 |
| chess.gbnf | PGN 风格的国际象棋代数记谱,演示字面量、字符区间、可选/重复组合 |
| english.gbnf | 英文字母/数字/标点约束(示例性质,可能不完整) |
| japanese.gbnf | 平假名/片假名/汉字/日文标点的 Unicode 区间示例 |
| json.gbnf | 手写完整 JSON 文法(对象/数组/字符串/数字/布尔/null),是最常用的基准文法 |
| json_arr.gbnf | 与 json.gbnf 相同但收紧 root 数组的空格处理,便于生成 JSON 数组 |
| list.gbnf | "- "前缀的列表项文法,排除各类换行字符 |
其中 json.gbnf 值得逐条研读:ws ::= | " " | "\n" [ \t]{0,20}是仓库约定俗成的"可选空白"写法——空串、单个空格、或换行后最多 20 个缩进;number规则用([0-9] | [1-9] [0-9]{0,15})同时保证"不允许前导零"和"不超过 16 位整数"。
使用 GBNF 文法
在 PowerInfer 的smallthinker中,GBNF 文法的入口主要有三处:
- llama-server(smallthinker/tools/server)的补全类接口:在请求体的
grammar字段传入 GBNF 文法字符串; - llama-cli(smallthinker/tools/main):通过
--grammar(内联文法)和--grammar-file(文法文件路径)两个命令行参数传入。该工具 README(tools/main/README.md)对二者的说明是:--grammar GRAMMAR、--grammar-file FILE,用于把模型输出约束为特定格式,例如强制输出 JSON 或只讲 emoji,并明确指引参见 GBNF 指南(即本文所依据的 smallthinker/grammars/README.md); - 验证工具(smallthinker/tests/test-gbnf-validator.cpp):测试一个文法是否能匹配给定的字符串,适合在接入模型前离线验证文法正确性。
尝试仓库示例文法的基本命令如下(<model>替换为你的模型路径):
./llama-cli -m <model> --grammar-file smallthinker/grammars/chess.gbnf -p 'Some prompt'JSON Schema → GBNF
手动为复杂 JSON 结构编写文法既繁琐又易错。llama.cpp 系列支持将 JSON Schema 的子集自动转换为 GBNF 文法,在 PowerInfer 的smallthinker中可通过以下方式使用:
- llama-server(smallthinker/tools/server):
- 任意补全接口:在
json_schemabody 字段传入 schema; /chat/completions接口:在response_formatbody 字段内传入,例如{"type": "json_object", "schema": {"items": {}}}或{"type": "json_schema", "json_schema": {"schema": ...}};
- 任意补全接口:在
- llama-cli(smallthinker/tools/main):通过
--json/-j标志传入(该工具 README 中也将其文档化为--json-schema SCHEMA,见 tools/main/README.md); - 提前离线转换:
- Python CLI 转换器:smallthinker/examples/json_schema_to_grammar.py;
- JavaScript 转换器:smallthinker/tools/server/public_legacy/json-schema-to-grammar.mjs(llama-server 的 Web UI 即使用它)。
端到端示例:生成"姓名 + 年龄"JSON 数组
以下命令让模型生成一个包含 10~100 个对象的数组,每个对象含name(1~100 个字符的字符串)和age(0~150 的整数),且不允许额外字段:
llama-cli \ -hfr bartowski/Phi-3-medium-128k-instruct-GGUF \ -hff Phi-3-medium-128k-instruct-Q8_0.gguf \ -j '{ "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "age": { "type": "integer", "minimum": 0, "maximum": 150 } }, "required": ["name", "age"], "additionalProperties": false }, "minItems": 10, "maxItems": 100 }' \ -p 'Generate a {name, age}[] JSON array with famous actors of all ages.'其中-hfr/-hff用于从 Hugging Face 仓库拉取 GGUF 模型文件。你也可以在任何 schema 上先行离线转换:
python3 smallthinker/examples/json_schema_to_grammar.py name-age-schema.json上述 schema 会生成如下的 GBNF 文法(节选,完整内容可在转换后查看):
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) item ::= "{" space item-name-kv "," space item-age-kv "}" space item-age ::= ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-4] [0-9] | [5] "0")) space item-age-kv ::= "\"age\"" space ":" space item-age item-name ::= "\"" char{1,100} "\"" space item-name-kv ::= "\"name\"" space ":" space item-name root ::= "[" space item ("," space item){9,99} "]" space space ::= | " " | "\n" [ \t]{0,20}注意生成结果中几个有意思的细节:
item-name ::= "\"" char{1,100} "\"" space把minLength/maxLength翻译成了{1,100}计数重复;item-age把0..150的整数范围拆解为[0-9](0~9)、[1-8][0-9] | [9][0-9](10~99)、"1"[0-4][0-9] | "1"[5]"0"(100~150)三段,演示了数值范围到字符区间文法的精确映射;root ::= "[" space item ("," space item){9,99} "]" space把minItems/maxItems翻译为{9,99}(第一个 item 之外的额外项数量)。
已知限制
JSON Schema → GBNF 转换目前支持的是 JSON Schema 的一个子集,已知限制如下(欢迎贡献修复):
additionalProperties默认视为false(这样生成的文法更快且减少幻觉);"additionalProperties": true可能产生包含未转义换行的键;- 不支持的特性会被静默跳过。目前建议使用命令行 Python 转换器(见上)来查看警告,并检查生成的文法、用验证工具测试(本仓库对应 smallthinker/tests/test-gbnf-validator.cpp);
- 同一类型中不能混用
properties与anyOf/oneOf; prefixItems存在缺陷(但items可用);minimum、exclusiveMinimum、maximum、exclusiveMaximum:目前只支持"type": "integer",不支持number;- 嵌套的
$ref存在缺陷; pattern必须以^开头、以$结尾;- C++ 版本不支持远程
$ref(Python 与 JavaScript 版本会获取 https 引用); string的format缺少uri、email;- 不支持
patternProperties。
此外还有一份非穷尽的"大概率不会实现"的特性清单(因为对无状态文法来说困难或太慢):
uniqueItems;contains/minContains;$anchor(解引用相关);not;- 条件子模式
if/then/else/dependentSchemas。
可参考 smallthinker/tests/test-json-schema-to-grammar.cpp 了解当前受支持的特性面(其中也包含大量使用示例)。
关于 additionalProperties 的说明
注意:JSON Schema 规范规定
object默认接受附加属性。由于这既慢又容易导致幻觉,本项目默认不允许附加属性。你可以通过在任意对象的 schema 中显式设置"additionalProperties": true来允许附加属性。
使用 Pydantic 生成 schema
如果你用 Pydantic 生成 schema,可以通过每个模型类的extra配置开启附加属性:
# pip install pydantic import json from typing import Annotated, List from pydantic import BaseModel, Extra, Field class QAPair(BaseModel): class Config: extra = 'allow' # 触发 JSON schema 中的 additionalProperties: true question: str concise_answer: str justification: str class Summary(BaseModel): class Config: extra = 'allow' key_facts: List[Annotated[str, Field(pattern='- .{5,}')]] question_answers: List[Annotated[List[QAPair], Field(min_items=5)]] print(json.dumps(Summary.model_json_schema(), indent=2))生成出的 JSON schema 中,QAPair与Summary的additionalProperties均为true,因此转换出的文法(节选)会额外允许任意附加键值对:
QAPair ::= "{" space QAPair-question-kv "," space QAPair-concise-answer-kv "," space QAPair-justification-kv ( "," space ( QAPair-additional-kv ( "," space QAPair-additional-kv )* ) )? "}" space QAPair-additional-k ::= ["] ( [c] ([o] ([n] ([c] ([i] ([s] ([e] ([_] ([a] ([n] ([s] ([w] ([e] ([r] char+ | [^"r] char*) | [^"e] char*) | [^"w] char*) | [^"s] char*) | [^"n] char*) | [^"a] char*) | [^"_] char*) | [^"e] char*) | [^"s] char*) | [^"i] char*) | [^"c] char*) | [^"n] char*) | [^"o] char*) | [j] ([u] ([s] ([t] ([i] ([f] ([i] ([c] ([a] ([t] ([i] ([o] ([n] char+ | [^"n] char*) | [^"o] char*) | [^"i] char*) | [^"t] char*) | [^"a] char*) | [^"c] char*) | [^"i] char*) | [^"f] char*) | [^"i] char*) | [^"t" char*) | [^"s] char*) | [^"u] char*) | [q] ([u] ([e] ([s] ([t] ([i] ([o] ([n] char+ | [^"n] char*) | [^"o] char*) | [^"i] char*) | [^"t] char*) | [^"s] char*) | [^"e] char*) | [^"u] char*) | [^"cjq] char* )? ["] space QAPair-additional-kv ::= QAPair-additional-k ":" space value QAPair-concise-answer-kv ::= "\"concise_answer\"" space ":" space string QAPair-justification-kv ::= "\"justification\"" space ":" space string QAPair-question-kv ::= "\"question\"" space ":" space string additional-k ::= ["] ( [k] ([e] ([y] ([_] ([f] ([a] ([c] ([t] ([s] char+ | [^"s] char*) | [^"t] char*) | [^"c] char*) | [^"a] char*) | [^"f" char*) | [^"_] char*) | [^"y] char*) | [^"e] char*) | [q] ([u] ([e] ([s] ([t] ([i] ([o] ([n] ([_] ([a] ([n] ([s] ([w] ([e] ([r] ([s] char+ | [^"s] char*) | [^"r] char*) | [^"e] char*) | [^"w] char*) | [^"s] char*) | [^"n] char*) | [^"a] char*) | [^"_] char*) | [^"n] char*) | [^"o] char*) | [^"i] char*) | [^"t] char*) | [^"s] char*) | [^"e] char*) | [^"u] char*) | [^"kq] char* )? ["] space additional-kv ::= additional-k ":" space value array ::= "[" space ( value ("," space value)* )? "]" space boolean ::= ("true" | "false") space char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) decimal-part ::= [0-9]{1,16} dot ::= [^\x0A\x0D] integral-part ::= [0] | [1-9] [0-9]{0,15} key-facts ::= "[" space (key-facts-item ("," space key-facts-item)*)? "]" space key-facts-item ::= "\"" "- " key-facts-item-1{5,} "\"" space key-facts-item-1 ::= dot key-facts-kv ::= "\"key_facts\"" space ":" space key-facts null ::= "null" space number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space question-answers ::= "[" space (question-answers-item ("," space question-answers-item)*)? "]" space question-answers-item ::= "[" space question-answers-item-item ("," space question-answers-item-item){4,} "]" space question-answers-item-item ::= QAPair question-answers-kv ::= "\"question_answers\"" space ":" space question-answers root ::= "{" space key-facts-kv "," space question-answers-kv ( "," space ( additional-kv ( "," space additional-kv )* ) )? "}" space space ::= | " " | "\n" [ \t]{0,20} string ::= "\"" char* "\"" space value ::= object | array | string | number | boolean | null值得注意,QAPair-additional-k这种"键名逐字符排除已知键"的写法,是转换器为了允许任意附加键的同时避免与已有键冲突而生成的特殊结构——它保证了附加键不会恰好等于question、concise_answer、justification等已定义键。
使用 Zod 生成 schema
如果你用 Zod,可以用nonstrict()/passthrough()让对象显式允许额外属性,或用z.object(...).strict()/z.strictObject(...)显式禁止。不过要注意:zod-to-json-schema 目前无论如何都会设置"additionalProperties": false:
import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; const Foo = z.object({ age: z.number().positive(), email: z.string().email(), }).strict(); console.log(zodToJsonSchema(Foo));生成的 schema 与文法如下(文法节选):
{ "type": "object", "properties": { "age": { "type": "number", "exclusiveMinimum": 0 }, "email": { "type": "string", "format": "email" } }, "required": ["age", "email"], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }age-kv ::= "\"age\"" space ":" space number char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) decimal-part ::= [0-9]{1,16} email-kv ::= "\"email\"" space ":" space string integral-part ::= [0] | [1-9] [0-9]{0,15} number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space root ::= "{" space age-kv "," space email-kv "}" space space ::= | " " | "\n" [ \t]{0,20} string ::= "\"" char* "\"" space此例也印证了上文提到的限制:exclusiveMinimum作用于number类型时,转换出的number文法并不会真正强制"正数"(仅integer的minimum/maximum被精确支持),因此在要求严格数值边界时应使用integer。
故障排查:性能陷阱与高效写法
GBNF 文法目前存在性能陷阱(参见 llama.cpp 社区 issue #4218 的讨论)。最典型的问题出在可选的重复上。
高效的可选重复写法
一个常见需求是:允许某个模式x重复最多 N 次。语义正确的写法x? x? x? ... x?(重复 N 次)可能导致采样极其缓慢——因为文法状态机要为每一处?维护分支状态,采样时的回溯开销随 N 急剧增长。
正确的高效写法是:
x{0,N}(在早期 llama.cpp 版本中,也可以写成(x (x (x ... (x)?...)?)?)?这种 N 层嵌套的可选形式,效果等价但可读性差。)
同理,优先使用{m,n}计数重复而非大量堆叠?。本仓库生成的文法(如上面item ("," space item){9,99})正是遵循这一原则的产物。
小结与下一步
GBNF 是 llama.cpp 系项目约束 LLM 输出的核心机制:它以 BNF 为基底、引入正则风格特性,用root规则 + 生产规则 + 字符区间 + 重复/可选操作符,把输出格式从"提示词的软约束"升级为"采样层面的硬约束"。围绕它,你可以:
- 直接编写
.gbnf文法(参考 smallthinker/grammars/ 下的 8 个示例); - 用 llama-cli 的
--grammar/--grammar-file、llama-server 的grammar字段接入; - 用 JSON Schema 自动转换(Python / JavaScript 转换器),并注意其子集限制与
additionalProperties语义; - 用 smallthinker/tests/test-gbnf-validator.cpp 离线验证文法,用 smallthinker/tests/test-json-schema-to-grammar.cpp 确认转换器能力边界;
- 规避
x? x? ...式低效重复,改用{0,N}。
如果你想深入验证自己写的文法,最直接的路径是:先在本仓库的smallthinker/grammars/目录中挑选一个最接近需求的示例文件作为起点,用llama-cli --grammar-file试跑,再结合test-gbnf-validator对边界字符串做离线测试。
- 人工智能
- 大模型
- 推理引擎
- 本地部署
【免费下载链接】PowerInfer
High-speed Large Language Model Serving for Local Deployment
相关推荐
llama.cpp GBNF 指南:用形式文法与 JSON Schema 约束 LLM 输出
llama.cpp GBNF 指南:用形式文法与 JSON Schema 约束 LLM 输出 本文基于 llama.cpp 仓库中的 grammars/READ
人工智能大模型模型推理服务推理引擎本地部署后端PowerInfer GBNF 语法指南:用形式文法约束 LLM 输出(含完整示例与源码解析)
PowerInfer GBNF 语法指南:用形式文法约束 LLM 输出(含完整示例与源码解析) GBNF(GGML BNF)是 PowerInfer 项目中用于
人工智能大模型推理引擎本地部署ik_llama.cpp GBNF 语法指南:用形式文法约束模型输出,实现可靠的 JSON 与结构化生成
ik_llama.cpp GBNF 语法指南:用形式文法约束模型输出,实现可靠的 JSON 与结构化生成 本文是 ik_llama.cpp(llama.cpp
人工智能大模型推理引擎本地部署模型量化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考