新闻详情

新闻详情

首页 / 资讯中心 / 详情

PowerInfer 中的 GBNF 语法完全指南:用形式文法约束 LLM 输出(从 JSON 到任意格式文本)

发布时间:2026/9/25 8:22:04来源:尧图网络
PowerInfer 中的 GBNF 语法完全指南:用形式文法约束 LLM 输出(从 JSON 到任意格式文本)
人工智能大模型推理引擎本地部署【免费下载链接】PowerInferHigh-speed Large Language Model Serving for Local Deployment项目地址https://gitcode.com/gh_mirrors/po/PowerInfer点击查看免费下载本篇技术指南以 smallthinker/grammars/README.md 为核心文档系统讲解 GBNFGGML BNF文法格式的完整语法生产规则、终结符/非终结符、字符区间、序列与选择、重复与可选符号、注释规则与 root 规则并结合当前仓库中smallthinker/grammars/目录下 8 个真实.gbnf示例文件、smallthinker/tools/main与smallthinker/tools/server的实际用法以及 JSON Schema 到 GBNF 的转换流程与已知限制。读完本文你将能够独立编写、调试 GBNF 文法把模型输出严格约束为合法 JSON、代码或任意自定义格式并掌握避免常见性能陷阱的写法。GBNF 是什么GBNFGGML 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_grammarfalsemain.cpp跳过文法约束。这一调用链印证了 GBNF 是逐 token 生效的约束机制而不是生成后的后处理校验。GBNF 文法在smallthinker/tools/mainllama-cli和smallthinker/tools/serverllama-server中以多种方式受支持具体见下文「使用 GBNF 文法」。背景从 BNF 到 GBNF巴科斯范式Backus-Naur FormBNF是一种描述形式语言如编程语言、文件格式、协议语法的记号体系。GBNF 是 BNF 的扩展主要增加了一些现代正则表达式风格的特性例如字符区间[a-z]、[^...]取反重复操作符*、、?以及{m}、{m,}、{m,n}等计数重复完整 Unicode 支持可直接书写字符或用\xXX、\uXXXX、\UXXXXXXXX转义。相比直接在 prompt 里要求输出 JSONGBNF 把格式约束从概率性提示升级为硬性文法约束从机制上消除格式非法的问题。基础生产规则在 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.gbnfhiragana :: [ぁ-ゟ] katakana :: [ァ-ヿ] punctuation :: [、-〾] cjk :: [一-鿿]也可以用转义形式8 位\xXX如\x7F、\x0016 位\uXXXX如\u202832 位\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.gbnfexpr :: term ([-*/] term)*用*表达任意长度的四则运算链smallthinker/grammars/list.gbnfroot :: 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] \nroot强制输出为若干个- 开头的列表行。类似地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.gbnfPGN 风格的国际象棋代数记谱演示字面量、字符区间、可选/重复组合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-serversmallthinker/tools/server的补全类接口在请求体的grammar字段传入 GBNF 文法字符串llama-clismallthinker/tools/main通过--grammar内联文法和--grammar-file文法文件路径两个命令行参数传入。该工具 READMEtools/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 promptJSON Schema → GBNF手动为复杂 JSON 结构编写文法既繁琐又易错。llama.cpp 系列支持将 JSON Schema 的子集自动转换为 GBNF 文法在 PowerInfer 的smallthinker中可通过以下方式使用llama-serversmallthinker/tools/server任意补全接口在json_schemabody 字段传入 schema/chat/completions接口在response_formatbody 字段内传入例如{type: json_object, schema: {items: {}}}或{type: json_schema, json_schema: {schema: ...}}llama-clismallthinker/tools/main通过--json/-j标志传入该工具 README 中也将其文档化为--json-schema SCHEMA见 tools/main/README.md提前离线转换Python CLI 转换器smallthinker/examples/json_schema_to_grammar.pyJavaScript 转换器smallthinker/tools/server/public_legacy/json-schema-to-grammar.mjsllama-server 的 Web UI 即使用它。端到端示例生成姓名 年龄JSON 数组以下命令让模型生成一个包含 10~100 个对象的数组每个对象含name1~100 个字符的字符串和age0~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]0100~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/oneOfprefixItems存在缺陷但items可用minimum、exclusiveMinimum、maximum、exclusiveMaximum目前只支持type: integer不支持number嵌套的$ref存在缺陷pattern必须以^开头、以$结尾C 版本不支持远程$refPython 与 JavaScript 版本会获取 https 引用string的format缺少uri、email不支持patternProperties。此外还有一份非穷尽的大概率不会实现的特性清单因为对无状态文法来说困难或太慢uniqueItemscontains/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_items5)]] print(json.dumps(Summary.model_json_schema(), indent2))生成出的 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: falseimport { 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对边界字符串做离线测试。赞分享人工智能大模型推理引擎本地部署【免费下载链接】PowerInferHigh-speed Large Language Model Serving for Local Deployment项目地址https://gitcode.com/gh_mirrors/po/PowerInfer点击查看免费下载相关推荐llama.cpp GBNF 指南用形式文法与 JSON Schema 约束 LLM 输出llama.cpp GBNF 指南用形式文法与 JSON Schema 约束 LLM 输出 本文基于 llama.cpp 仓库中的 grammars/READ人工智能大模型模型推理服务推理引擎本地部署后端PowerInfer GBNF 语法指南用形式文法约束 LLM 输出含完整示例与源码解析PowerInfer GBNF 语法指南用形式文法约束 LLM 输出含完整示例与源码解析 GBNFGGML BNF是 PowerInfer 项目中用于人工智能大模型推理引擎本地部署ik_llama.cpp GBNF 语法指南用形式文法约束模型输出实现可靠的 JSON 与结构化生成ik_llama.cpp GBNF 语法指南用形式文法约束模型输出实现可靠的 JSON 与结构化生成 本文是 ik_llama.cppllama.cpp人工智能大模型推理引擎本地部署模型量化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

装了 OpenClaw 却不会用?这 20 个 Skills 让你的 AI 助手聪明(TaoToken 配置篇) 2026/9/25 8:52:24

装了 OpenClaw 却不会用?这 20 个 Skills 让你的 AI 助手聪明(TaoToken 配置篇)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
JEB Pro v5.28实战:Android与WebAssembly跨平台逆向分析 2026/9/25 8:52:24

JEB Pro v5.28实战:Android与WebAssembly跨平台逆向分析

JEB Pro是我电脑里常年驻留的反编译器之一。很多做样本分析、协议还原、代码审计的朋友可能和我一样,桌面上既有jadx、Ghidra,也装着Frida和IDA,但遇到棘手样本时,最后真正能把整个分析流程串起来的,往往还是JEB Pro。…

阅读更多 →
手机镜防指纹手机镜源头厂家定制工厂,情侣款与出差便携款实力生产商 2026/9/25 8:52:18

手机镜防指纹手机镜源头厂家定制工厂,情侣款与出差便携款实力生产商

怀化市上怀品牌管理有限责任公司,简称上怀眼镜,是怀化本土经营40年的经典眼镜连锁品牌,累计服务超过10万近视用户,始终坚守科学配镜的舒适体验与视力管理的专业初心,是怀化本地集品牌镜片授权验配、视健康全周期管理、…

阅读更多 →
飞腾D2000/E2000/D3000平台U-Boot引导镜像制作与设备树配置实战 2026/9/25 8:52:18

飞腾D2000/E2000/D3000平台U-Boot引导镜像制作与设备树配置实战

很多人第一次拿到飞腾D2000的板子,都会习惯性先去翻内核、搞文件系统,结果卡在最前面的UBOOT引导阶段,串口什么输出都没有,或者内核起来一半就睡死。实际上飞腾平台的引导镜像制作,和x86那套完全不同,它既不…

阅读更多 →
外墙墙体渗水维修师傅 好工匠防水 高空作业 外墙裂缝修补专用材料 2026/9/25 8:52:11

外墙墙体渗水维修师傅 好工匠防水 高空作业 外墙裂缝修补专用材料

随着国内建筑使用年限逐步增加,以及北方特殊气候对建筑外墙的持续侵蚀,外墙防水维修市场的需求正在持续增长。京津冀区域受北方冬季冻融循环、春季持续返潮、沿海区域盐蚀、雨季强降水的多重影响,外墙渗水问题成为民居、商用建筑、工业厂房都…

阅读更多 →
GD32高级定时器互补PWM输出与死区控制实战 2026/9/25 8:51:58

GD32高级定时器互补PWM输出与死区控制实战

写GD32的高级定时器,绕不开三相电机控制、全桥逆变、UPS这类场景。做这类项目的人,百分之九十九都躲不过一个需求:要输出两路相位相反、中间还夹着一小段“空白”的PWM,而且这段空白还得精确可控。这段空白就是死区,控…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉