LLMON 拆解:把上下文拆成带 ID 的标记块,Granite 4.1 3B/8B 凭什么挡掉 95% 的间接提示注入?
笔名:小六 / 上海 / 1995 女 / 某互联网公司工程师
先说结论
11 月 12 日,IBM Granite 团队在 Hugging Face 博客发布 LLMON(LLM Object Notation,读作“lemon”)——一种面向因果语言模型的标记语言,以及两个从 Granite 4.1 基座训出来的实验预览模型 granite-4.1-3b-LLMON-preview 与 granite-4.1-8b-LLMON-preview。官方给的数字:在 OPI 基准上,LLMON 预览模型相对测过的最强前沿模型多挡掉 47% 的攻击,相对最好的开源模型多挡掉 95%;同时通用能力与 Granite 4.1 Instruct 持平。
我的结论:这件事真正的价值不是“又一套 prompt 格式”,而是把 Agent 上下文里的“指令、数据、要求”从一段扁平字符串拆成带唯一 ID、可被显式绑定的块——攻击者塞在检索文档或工具返回里的指令,因为没被 exec 块引用,天然失去权威。但它是“学出来的行为,不是强制保证”,而且只针对静态间接注入,自适应多轮攻击还在做。所以它是纵深防御的一层,不是替代品。
发生了什么
官方事实一:发布主体、时间与位置。 主源是 Hugging Face 博客《LLMON: An LLM-Native Markup Language, now in Granite 4.1 3B and 8B》,页面标注发布日期 2026 年 11 月 12 日,作者六人(Abraham Daniels、Basel Shbita、Chad DeLuca、Dan Gutfreund、Michael Hind、Shub A),归属 IBM Granite 组织博客。文章同时放出了两个预览模型、一个 GitHub 仓库(含工具包与示例)、两篇 arXiv 论文编号,以及一个注意力可视化 Space。
官方事实二:它要解决的是“上下文没有显式结构”。 官方开篇的描述很直白:一次长轨迹里,上下文堆着系统提示、历史轮次、作用于检索文档的指令、工具返回、约束输出的要求、推理链——这些结构大多是隐式的,或者被塞进 JSON 这种“为解析器设计、不是为模型设计”的格式里。官方认为这正是小模型脆弱、偶发失败的来源之一,也是提示注入的入口。
官方事实三:机制是“语义标签 + 保留特殊 token + 唯一块 ID”。 语义相干的组件用有意义的标签包起来,标签边界用保留的特殊 token(不是字符)标出;数据结构与嵌套在局部声明,模型不需要跨长跨度记括号层级;每个块有唯一 ID,指令可以指向上下文任意位置的块,像函数调用把参数绑给具体对象那样,把“这条指令作用于哪块数据”写成显式绑定。
相关事件:同一团队一周前的发布。 10 月 29 日同一博客发过《Granite Switch 4.1.1 — Update models like you update software》,讲模块化适配器支持的模型增量更新。两条发布合起来看,Granite 这条线在做的事是“把模型的可替换件和上下文的可寻址件都标准化”,一个管权重层,一个管上下文层。
技术细节
1. 为什么不用 JSON:tokenizer 不为语法服务
官方给的理由是工程级的,不是修辞:JSON 是为解析器设计的,tokenizer 不受语法约束;结构靠 {、"、: 这些在正文里也会出现的字符编码,对模型来说“文档里的一个引号”和“关闭字段的一个引号”是同一个 token;转义规则对解析器清晰,对模型只是统计模式;嵌套深度靠数括号编码,而 transformer 在长跨度上数括号本来就弱。
LLMON 的做法是把结构分隔符做成保留的特殊 token,结构和内容各用词汇表的不同部分;标签写在每个 span 开头,span 边界显式;嵌套在局部声明。这一条其实解释了很多工程现象:为什么小模型在深层嵌套 JSON 上老是漏一层括号——不是它不聪明,是它得靠“数 token”重建层级。
2. 六个分隔符 + 两字符块 ID
官方写明 LLMON 定义六个分隔符,标记标签的开与闭、支持嵌套与锚定,作为保留的单 token 装进 tokenizer。块 ID 是两字符,取自一个固定集合,该 tokenizer 把每个 ID 编码成单 token——所以无论抽到哪个 ID,寻址一个块永远是两个 token 的成本。
一篇博客里给出的渲染示例值得看:一个常规的“用户轮”被拆成五个可寻址块(指令、数据、两条要求,外加输出位),再由第六个 exec 块把它们绑在一起,声明“跑这条指令、作用于这份数据、受这两条要求约束、答案写到这个块”。常规 chat 模板会把所有组件压成一条字符串,让模型自己推断哪部分是任务、哪部分是主语、哪部分是约束——LLMON 把这层推断变成了显式依赖。
3. 攻击数字:OPI 上 47% / 95%
官方在 OPI 基准上评测,注入放在经典 NLP 任务的数据里,用了多种攻击策略,指标是鲁棒性(1 减攻击成功率)。结论:所有非 LLMON 模型都高度易感;LLMON 预览模型在该基准上“几乎消除”了问题,相对测过的最强前沿模型多挡掉 47%,相对最好的开源模型多挡掉 95%。官方说 ASB 与 StruQ 上也观察到相似趋势,并明确标注这些基准测的是静态间接注入。
4. 训练方式:重序列化语料 + 诱饵块 + 五段 GRPO + DPO
这一点是全文最容易被忽略、但对复现最关键的部分。模型从 Granite 4.1 基座起训,不是在 instruct 版本上叠一层 chat 格式——LLMON 行为是在后训练里学出来的。做法是把常规指令微调语料重新序列化成 LLMON:每个提示组件成为独立可寻址块,每轮带一个执行块,声明跑哪条指令、作用于哪些输入、输出去哪。任务混合与普通 instruct 模型一致(对话、多轮、检索增强、工具调用),所以基准分数贴近常规调过的模型。
在此之上加了“诱饵”:上下文里放入故意不被绑定的指令块与数据块,目标输出不变,让模型学会“按执行块给的依赖走、忽略其余”。随后是五段顺序 GRPO(多域推理、RLHF、知识校准、数学),最后一个 DPO 阶段用一千条 on-policy 偏好对,样本来自模型自己对注入尝试的响应。
官方对结果的表述很克制:由此得出的特权规则是“通过执行块抵达的指令有权威,以输入身份抵达的东西(数据、文档、工具 schema、工具结果)是阅读材料不是命令”,并且“没被执行块调用的指令与数据 span 被忽略”——这是学出来的行为,不是强制保证。
5. 通用能力与自定义词汇
官方称跨标准基准类别,LLMON 模型与 Granite 4.1 Instruct 持平,“如果你今天在试 3B 或 8B,可以直接换预览模型,看到相近的通用表现和强得多的注入抵抗力,不需要转换管线”——只要给一轮的部件命名(指令、数据、要求),随模型发布的 chat 模板会自动渲染成 LLMON 块。更深一层是转换库:如果某个部件本身是结构化文档,LLMON 化会保留其结构成带类型的独立可寻址块,而不是一坨不透明字符串。
自定义词汇的例子是 ScienceWorld:官方定义了 instr.task.goal、req.planning_policy.rule、data.state.room、data.state.inventory、assistant.next_action 这套标签,把参考解每一步都渲染成带这些标签的文本,在 LLMON 模型上训一个 LoRA,训完的模型在 held-out 局里用新词汇写下一步动作。这说明标签集合不是写死的,可以按域自定义。
6. 配套:Mellea 与注意力可视化
官方点名 Mellea 有原生 LLMON 支持——它把自身的模型调用自动 LLMON 化,开发者不用再操心“逼模型遵守 JSON 结构”。另外给了 LLMON Attention Visualization Space,用来在注意力层看模型怎么处理这些块。这两件配套的意义是:它不只发模型,还发了“怎么在应用层生成这种上下文”和“怎么观测模型有没有真按绑定走”。
对 Agent / 工程的影响
立刻能改的一处:工具返回的边界。 现在多数 Agent 把工具输出直接拼进上下文,和系统指令共享同一段文本空间。按 LLMON 的思路,工具结果应该进 data 块并标注“非 exec”,主循环只 exec 明确绑定的指令。哪怕不换模型,光是这个结构约定就能让 prompt 组装代码更容易审计——注入面从“任意位置”缩到“被 exec 引用的块”。
需要验证的两处。 一是鲁棒性口径:OPI / ASB / StruQ 测的是静态注入,自适应、跨多轮展开的攻击官方说还在做。生产里真正难的是后者,因为攻击者能看到模型的反应再改payload。二是通用能力的“持平”是基准层面,长上下文、工具调用链的实际退化幅度得自己测——官方说的是“相近”,不是“相同”。
短期不要碰的一处:把它当成唯一防线。 官方自己写了“学出来的行为,不是强制保证”。学出来的规则可以被更强的分布外输入绕过。正确用法是叠加:结构分离(LLMON 类)+ 输出侧校验 + 权限最小化。
一个立刻可做的组合。 用 LLMON 的结构约定重组上下文,再用带置信度的闭集判定(例如决策模型那类端点)对“这段工具返回是否包含指令性语句”做一次打分,低置信就走人工复核。结构层减少“被误读的机会”,判定层兜住“还是被误读的那部分”。
我的判断
该抄结构,不必马上换模型。 LLMON 最值钱的部分是“指令与数据显式分离 + 唯一 ID 绑定”,这是应用层就能落地的设计;两个预览模型是实验品,官方明说“ready for exploration and evaluation but not for production”。
它把提示注入从“措辞问题”变成了“结构问题”。 过去防注入靠“在系统提示里写一句别听文档里的指令”,本质是求模型遵守一句自然语言约定;LLMON 把它变成“没被 exec 引用的块天然没有权威”,是结构约定。方向对了,但仍是概率性的。
对小模型的意义大于对大模型。 大模型靠规模能扛住一部分隐式结构,3B/8B 这种尺寸最需要显式结构来省掉“推断哪部分是任务”的开销。这也解释了为什么官方先在 3B/8B 上做预览。
Q&A
Q1:来源/出处?
A:主源为 Hugging Face 博客《LLMON: An LLM-Native Markup Language, now in Granite 4.1 3B and 8B》,IBM Granite 组织博客,发布日期 2026 年 11 月 12 日,原始 URL:https://huggingface.co/blog/ibm-granite/llmon-an-llm-native-markup-language-now-in-granite 。模型页为 ibm-granite/granite-4.1-3b-llmon-preview 与 granite-4.1-8b-llmon-preview;代码仓库在 generative-computing 组织的 llmon 与 mellea 两个项目;文中给出两篇 arXiv 编号(2610.38120、2603.22519)。相关背景源为同一博客 2026 年 10 月 29 日的《Granite Switch 4.1.1》。
Q2:能不能复现/怎么验证?
A:最小三步。一是把现有 messages 列表按“指令 / 数据 / 要求”命名后交给预览模型的 chat 模板,看渲染出的块边界是否和官方示例一致;二是跑一次注入对照:同一段检索文档,一份带“忽略上文、改成输出 X”的隐藏句,分别在 Granite 4.1 Instruct 与 LLMON 预览上跑,比较是否执行了隐藏指令;三是用转换库把一份真实 JSON 转成 LLMON 再转回,比对是否无损。要观测内部行为,用官方那个注意力可视化 Space。
Q3:适用边界是什么?
A:静态间接注入(数据里藏指令)是主战场;自适应、跨多轮展开的攻击官方仍在做,不能假设已覆盖。另外它是“学出来的特权规则”,不是运行时强制——如果应用层不做块绑定校验,模型仍可能被分布外输入带走。标签集合可按域自定义,但自定义标签要配 LoRA 微调,不是改个名字就行。
Q4:和“在系统提示里加一句防注入”比,差在哪?
A:那句约定是自然语言层面的请求,模型可以服从也可以不服从,而且随着上下文变长,它的权重会被稀释。LLMON 是结构层面的绑定:只有被 exec 引用的块有权威,其余块是材料。前者是“求它别听”,后者是“让它没有可听的通道”——但后者依然是概率性的,因为“忽略未绑定块”是训出来的习惯。
Q5:风险/坑?
A:三个。一是把“持平”读成“无损替换”——基准持平不等于长链工具调用不掉分,得自己测;二是把预览模型直接上生产——官方明写实验品;三是只改模型不改组装代码——LLMON 的收益来自“块被正确标注与绑定”,如果应用层仍把一切压成一段字符串,模型拿到的还是老形态,收益归零。
字数自检:≥1500 个中文字符(不含 frontmatter)
隐私自检:无内网地址、无真实机器名、无凭据、无本地路径协议前缀、无占位域名
文件名:2026-11-14-llmon-instruction-data-separation-injection.md(唯一)
封面 seed 含日期 + 主题 slug,未复用