Flint 是微软开源的可视化中间语言,让 agent 用一份精简的规格描述图表,由编译器推导出 scale、坐标轴、间距、标签、布局这些低层配置,再编译到 Vega-Lite、ECharts、Chart.js、Plotly 和 Excel 五个后端。MIT,配套论文 arXiv:2607.20775。HN 上 255 分 / 67 评,仓库 ★2,872。
本文不复述 README。我把 npm 包装下来,拿我们自己文章里真实用过的一张图跑了一遍,量了三件事:一份规格能省掉多少字符、编译器具体补出了什么、以及 agent 写错时会发生什么。
先给结论:agent 需要写的规格是 211 个字符,Flint 编译出的 Vega-Lite 是 2,788 个字符,放大 13.2 倍。 这个倍数是本文实测的,论文摘要里没有任何量化结果。
agent 直接写图表配置,问题出在哪
让模型直接产出 Vega-Lite 或 ECharts 配置是常见做法,问题是这类配置里绝大部分内容与「你想表达什么」无关,而与「渲染引擎需要什么」有关:轴的类型、刻度最小步长、标签格式化表达式、色板、图例字号、条形间距、画布尺寸。
这些字段模型每次都要重新生成一遍。生成得多就容易出错,出错了又难以定位,因为报错来自渲染引擎而不是来自语义。
Flint 的切法是在中间插一层:agent 只声明数据字段的语义和图表的编码方式,低层配置由编译器从语义推导。
实测一:一份规格编译到五个后端
我用的是我们此前一篇文章里的真实图表数据(三本书 × 三种策略的 token 消耗对比,九行数据)。喂给 Flint 的完整输入是 790 个字符,其中纯数据 579 个,真正需要 agent 写的规格部分只有 211 个字符:
semantic_types: { book:'Category', strategy:'Category', tokens:'Count' },
chart_spec: {
chartType: 'Bar Chart',
encodings: { y:{field:'book'}, x:{field:'tokens'}, color:{field:'strategy'} },
}
同一份输入,五个后端全部编译成功:
| 后端 | 输出字符数 | 相对规格放大 |
|---|---|---|
| Vega-Lite | 2,788 | 13.2× |
| ECharts | 2,286 | 10.8× |
| Chart.js | 1,263 | 6.0× |
| Plotly | 1,716 | 8.1× |
| Excel | 606 | 2.9× |
这就是它省掉的部分。 如果让 agent 直接写 Vega-Lite,它要生成的是那 2,788 个字符;走 Flint,它只写 211 个。
版本信息:flint-chart v0.4.1,零运行时依赖,安装体积 32 MB。
实测二:编译器具体补了什么
只声明了图表类型和三个编码通道,编译器推导出来的东西比我预期的细。以 Vega-Lite 输出为例:
数据类型推断:book 和 strategy 判为 nominal,tokens 判为 quantitative。
标尺:给 quantitative 轴加上 zero: true,也就是强制从零起。条形图不从零起是最常见的误导性画法之一,这里是默认拦掉的。
轴格式化:给出 tickMinStep: 1,以及一段标签表达式:整数用千分位格式化,非整数直接留空。这一句是手写配置时几乎不会想到、但缺了就会出现「119264.5」这类刻度的地方。
色板:自动选了 tableau10。
版式:画布 329 × 320,条形步长 27,分面间距 32,轴标签 10 号、标题 11 号、图例 10/11 号。字号在各处保持一致。
还有两样超出静态配置范畴的东西。输出里带一个 _options,列出这张图可调的旋钮并标注每个旋钮是否适用(圆角对条形图适用,独立 Y 轴对这张图不适用)。以及一个 _transform,预先算好了同一份数据的合法替代画法:图表类型可切成棒棒糖图,布局有五种排列(X⇄Y、Y⇄Color、Color+行、X⇄Y·Color+列)。
也就是说它交付的不只是一份配置,而是一份配置加上这份数据还能怎么画的可能性集合。
实测三:写错了会怎样,校验分两层
「agent 能可靠产出」是这个项目的核心卖点,所以我构造了五种典型错误去试。结果分成两层,这个分层很重要,别搞混。
直接调用底层库(assembleVegaLite 这类函数)时:
| 构造的错误 | 底层库的反应 |
|---|---|
| 图表类型编造一个不存在的 | 抛错:Unknown chart type: Spider Web Chart |
| 字段名拼错,引用不存在的列 | 静默通过,产出 1,667 字符 |
| 语义类型编造一个不存在的 | 静默通过 |
完全不给 semantic_types | 静默通过,从数据自行推断 |
| 数据是空数组 | 静默通过,且 y 轴类型从 quantitative 悄悄变成 nominal |
只有第一种会报错。对 agent 场景最危险的那种,也就是模型幻觉出一个不存在的列名,在底层库这里编译干净通过,你拿到一张空图。
但这不是全部。我接着去看了它面向 agent 的那一层,flint-chart-mcp,校验就在这里:
const dataFields = new Set(rows.flatMap(row => Object.keys(row)));
if (!dataFields.has(field)) throw new Error(
`chart_spec.encodings.${channel}.field "${field}" does not exist in data.values`)
MCP 层还会拦:通道与图表类型不匹配、必填通道缺失、数据为空、行数超限、文件体积超限、画布尺寸超限、CSV 表头重复或为空。
所以这是有意的分层,不是疏漏:底层库保持宽松,什么都能编;严格校验放在 agent 实际会走的那条路上。
但这条分层必须知道:如果你图省事,在 agent 循环里直接 import { assembleVegaLite } 调库,你拿不到 README 承诺的那份可靠性,得自己补字段校验。走 MCP server 才有。
README 说 70+ 个语义类型,实装是 44 个
仓库 README 写「使用 70+ 个语义类型,例如 Rank、Temperature、Price、Country」。
我从装好的包里直接数:
Object.keys(require('flint-chart').SemanticTypes).length // → 44
npm 上 v0.4.1 是最新版(2026-07-27 发布),实际导出 44 个,不是 70 多个。npm 包自带的 README 里也没有这句话,「70+」只出现在 GitHub 仓库的 README。
44 个覆盖得其实不窄:时间类 15 个(DateTime 到 Decade)、数量类 10 个(Quantity、Price、Percentage、Temperature、Profit 等)、地理类 7 个(Latitude 到 ZipCode)、类别与其他 12 个。
差额从哪来我没有查到证据,可能是计入了子变体或规划中的类型。这里只报我量到的数:44。
另需说明的是,论文摘要里没有任何量化结果:没有错误率、没有规格体积对比、没有胜率,主张是「简化创作过程且不牺牲视觉质量」,属定性表述。本文开头那个 13.2 倍是我自己测的,不是论文的数据。论文标题也与 HN 上的标题不同,是 A Semantics-Driven Data Visualization Intermediate Language(2026-07-22 提交),作者 Yunhai Wang、Kecheng Lu、Junhao Chen、Alper Sarikaya、Chenglong Wang。
该不该用
适合的情形很具体:你有一条让 agent 产出图表的链路,且不想被某一个渲染库绑死。一份规格编译到五个后端这件事,自己维护成本不低。另一种是需要输出 Excel 原生图表的场景,这条路径少有替代品。
不太适合的情形同样具体:如果你的图是高度定制的版式(特定的手绘风格、非常规的标注、精确控制到像素的排版),那么编译器自动推导的那部分正是你要覆盖掉的,中间层反而变成阻碍。我们自己的文章配图就属于这一类,用的是手写 HTML 加截图,Flint 替代不了。
它真正的价值区间是「结构化数据 → 标准图表」的批量场景,不是「一张精心设计的插图」。
三点需要掂量:底层库的宽松校验(要走 MCP 层)、README 与实装的语义类型数对不上、论文没有量化结果。仓库建于 2026-05-13,fork 比 5.1%、watch 比 0.31%,都在正常区间。
思考总结
这个项目值得记住的不是那 13.2 倍,而是它把问题定位在了哪里。
让模型直接生成渲染配置,本质是让它同时负责两件事:决定图表要表达什么,以及满足渲染引擎的全部格式要求。前者需要判断,后者只需要规则。把后者交给编译器,模型的输出面就从两千多字符缩到两百多,出错的机会随之减少。
这个思路可以脱离图表这个具体场景来看:凡是模型的输出里存在「可由已知信息推导出来」的部分,就应该考虑加一层编译,而不是让模型每次重新生成。 判断留给模型,推导留给编译器。
至于要不要装,取决于一个很朴素的问题:你的图表是数据的表达,还是设计的产物。 前者 Flint 帮你省事,后者它帮不上忙。
参考
- 仓库:github.com/microsoft/f… (MIT)
- 论文:A Semantics-Driven Data Visualization Intermediate Language,arXiv:2607.20775(2026-07-22)
- npm:
flint-chartv0.4.1 /flint-chart-mcp - 本文所有实测数据于 2026-08-02 在本机取得,测试用的图表数据来自作者此前文章的真实配图需求