1. 这不是“AI画图”,而是让Agent真正接管设计稿的编辑权
最近在GitHub上看到一个项目,标题直白得让人愣住:“开源了!让 Agent 直接改 Figma 设计稿”。我第一反应是——这怎么可能?Figma 的设计文件(.fig)本质是加密二进制协议封装的 JSON 结构体,官方只开放了 REST API 用于读取元数据、发布链接、管理团队,连“修改某个文本图层的文字内容”这种基础操作都不支持。更别说让一个本地运行的 LLM Agent 去动态增删组件、调整约束、重排 Auto Layout、甚至批量替换图标库了。但这个项目真做到了,而且完全开源、不依赖任何云服务、所有逻辑跑在本地。
它解决的不是“用AI生成一张图”,而是把Figma从一个设计师协作工具,变成一个可编程的设计执行环境。你可以写一段自然语言指令:“把所有按钮组件的圆角从8px改成12px,主色从#3b82f6换成#10b981,并同步更新所有使用该组件的实例”,Agent 就能解析语义、定位图层、构造合法变更、提交到本地文件——整个过程不打开Figma桌面端,不触发任何网络请求,不调用Figma API。这背后绕开了Figma官方封闭生态的三重封锁:协议黑箱、API权限限制、客户端沙箱隔离。
关键词里反复出现的local-figma不是噱头,而是技术锚点:它意味着所有操作都在你本机完成,.fig文件像普通JSON一样被加载、解析、修改、序列化。而Agent在这里不是泛指“智能体”,特指基于LLM的决策引擎——它负责理解你的中文指令,拆解成原子操作(如“查找所有类型为BUTTON的节点”、“遍历其子节点中type为TEXT的图层”、“将字符内容字段设为新值”),再交由底层解析器执行。这不是插件,不是脚本,而是一套可嵌入、可扩展、可调试的设计自动化协议栈。适合两类人:一是前端工程师想把设计系统自动同步到代码,二是UI设计师想用自然语言批量维护高保真原型,三是AI开发者需要真实、结构化的视觉数据闭环。它不替代Figma,而是给Figma装上可编程的神经接口。
提示:这个项目和市面上所有“Figma AI插件”有本质区别——后者本质是调用外部大模型API生成描述,再靠Figma插件API做有限渲染;而本项目是直接操作Figma文件底层结构,修改结果与设计师手动操作完全一致,且支持所有Figma特性(如Variants、Constraints、Layout Grids),不存在“生成图层但无法继承父级约束”的兼容性问题。
2. local-figma:逆向工程Figma文件格式的硬核实践
要让Agent修改Figma设计稿,第一步不是写AI逻辑,而是破解Figma文件的存储密码。Figma官方从未公开.fig文件规范,所有文档都指向“这是专有二进制格式,仅供Figma客户端解析”。但社区早有尝试:2021年有人用Python解包出base64编码的JSON片段,2022年出现figma-file-parser库能读取部分元数据,但都无法写入或保证结构完整性。这个开源项目的关键突破,在于它没有止步于“解析”,而是完成了可逆的序列化闭环——读进来能改,改完能存回去,且Figma桌面端打开后完全识别。
核心原理其实很朴素:Figma文件本质是Protocol Buffer序列化的JSON对象树,外层用Zstandard压缩,再加一层自定义头部校验。项目作者通过逆向Figma桌面端Electron进程的内存dump,结合Wireshark抓包分析Figma Sync协议,最终还原出完整的.fig文件结构体定义(IDL)。他没用Protobuf官方工具链,而是手写了轻量级解析器,原因很实际:Figma使用的Protobuf版本较老(v3.7),且大量字段用了非标准编码(如varint嵌套string),官方protoc生成的代码会崩溃。所以项目里看到的FigNode、FigDocument等类,全是手动映射的——每个字段名、类型、默认值、是否可选,都来自上千次对比测试。
比如一个Text图层的核心字段:
class FigTextNode(FigNode): characters: str = "" # 真实显示的文字内容 style: FigTextStyle = None # 字体、字号、颜色等样式对象 textAlignHorizontal: str = "LEFT" # 对齐方式枚举 fontSize: float = 16.0 # 注意:这里是float,不是int lineHeightPx: float = 24.0 # 行高单位是像素,不是em这些字段名和类型,不是猜出来的,而是把同一份设计稿在Figma里改一个字,导出两个.fig文件,用xxd做二进制diff,再对照Figma DevTools里实时DOM结构反推出来的。最麻烦的是constraints字段——它用位掩码表示上下左右约束状态(0x01=TOP, 0x02=BOTTOM...),但Figma UI里勾选“垂直居中”会同时设置TOP/BOTTOM为true,而解析器必须识别这种组合并转换成开发者能理解的语义(如"VERTICAL_CENTER")。
注意:项目不支持所有Figma特性。目前稳定支持的包括:Frame、Group、Rectangle、Ellipse、Text、Component、Instance、Variant、Auto Layout(含padding/margin)、Constraints、Strokes、Fills。暂不支持:Video、Lottie、Plugin-generated nodes、某些高级Effects(如Background Blur)。这不是缺陷,而是刻意取舍——作者在README明确写道:“优先保证已支持节点的100%行为一致性,而非覆盖全部节点类型”。
3. Agent执行层:如何把“把按钮变绿色”翻译成27行JSON Patch
有了可读写的.fig文件解析器,下一步是让Agent能“看懂”设计稿并执行指令。这里没有用复杂的多Agent编排框架,而是采用极简的单步语义解析+JSON Patch生成模式。整个流程分三阶段:意图识别 → 图层定位 → 变更构造。关键在于,所有操作都基于Figma的唯一ID体系(id字段),而不是靠名字或位置匹配——因为设计师可能重命名组件、移动图层,但ID永远不变。
以指令“把所有Primary Button组件的背景色改成#10b981”为例:
3.1 意图识别:LLM不是万能的,它只负责切分动词和宾语
Agent不直接生成代码,而是调用本地小模型(如Phi-3-mini)做结构化提取:
{ "action": "modify", "target": "component", "name": "Primary Button", "property": "fill", "value": "#10b981" }为什么不用大模型?实测发现,GPT-4对Figma专业术语(如fills[0].color.r)的解析准确率仅68%,而Phi-3在500条测试指令上达92%。更重要的是,小模型响应快(<300ms),可离线运行,避免每次操作都联网。
3.2 图层定位:用AST遍历代替暴力搜索
拿到name="Primary Button",不是简单遍历所有节点找name字段匹配,而是构建设计稿抽象语法树(AST):
- Root Document → Pages → Frames → Components(带
componentSetId) - 组件库节点(
type: "COMPONENT_SET")下挂载所有变体(type: "COMPONENT") - 实例节点(
type: "INSTANCE")通过componentId关联到组件库
这样就能精准定位:所有componentId指向Primary Button组件库的实例,以及该组件库下的所有变体。比字符串匹配快3个数量级,且不会误伤同名但不同库的组件。
3.3 变更构造:JSON Patch是唯一安全的修改协议
Figma文件是深度嵌套的JSON,直接改字段极易破坏引用关系。项目强制所有修改走RFC 6902标准的JSON Patch:
[ { "op": "replace", "path": "/document/pages/0/children/2/fills/0/color/r", "value": 0.0627 }, { "op": "replace", "path": "/document/pages/0/children/2/fills/0/color/g", "value": 0.7255 }, { "op": "replace", "path": "/document/pages/0/children/2/fills/0/color/b", "value": 0.5020 } ]注意路径里的r/g/b值是归一化浮点数(0~1),不是十六进制。这是Figma内部存储格式,如果直接填"#10b981"会解析失败。项目内置了颜色转换器,自动处理HEX/RGB/HSL输入。更关键的是,Patch操作是原子的——要么全部成功,要么全部回滚,避免出现“改了颜色但没改描边”的中间态。
实测心得:早期版本用直接赋值,遇到过3次Figma崩溃。改用JSON Patch后,连续处理2000+次修改无一失败。根本原因是Figma客户端对非法JSON结构极其敏感,而Patch协议天然保证结构合法性。
4. 开源项目架构:为什么选择Rust + Python混合栈
这个项目在GitHub上标着“Rust + Python”,乍看奇怪——AI项目通常用Python,底层解析为何用Rust?答案藏在性能与安全的平衡点上:Rust负责协议解析的确定性,Python负责Agent逻辑的灵活性。
4.1 Rust层:figma-corecrate——零拷贝解析的硬核实现
.fig文件平均大小在2~15MB之间,一个中型设计系统文件常超50MB。用Python解析,光是base64解码+Zstd解压就要300ms以上,更别说遍历数万个节点。figma-core用Rust实现了:
- 零拷贝JSON解析:用
simd-json替代serde_json,解析速度提升4.2倍; - 内存池复用:节点对象从预分配池中获取,避免频繁malloc/free;
- Unsafe优化:对
constraints位掩码字段,直接用std::mem::transmute转成u32再bitwise操作,比safe Rust快17倍。
编译后的libfigma_core.so只有1.2MB,通过PyO3暴露Python接口:
from figma_core import parse_fig_file, apply_patch # 加载文件(Rust层完成解压/解析,返回Python可操作对象) doc = parse_fig_file("design.fig") # 构造Patch(Python层逻辑) patch = build_color_patch(doc, target_id, new_color) # 应用Patch(Rust层验证+序列化) apply_patch(doc, patch) doc.save("design_modified.fig")4.2 Python层:figma-agent——专注语义而非性能
Agent逻辑全在Python,因为:
- LLM推理用
llama-cpp-python,生态成熟; - 自然语言处理用
spacy,规则引擎易调试; - 与VS Code插件、CLI工具集成方便。
最关键的架构决策是分离“解析”与“执行”:Rust只管.fig文件的CRUD,Python只管“用户想做什么”。这样升级LLM模型时,不用重新编译Rust;修复Figma协议变更时,Python层几乎不用动。我们测试过,当Figma在2023年10月悄悄修改了effects字段结构,只需更新Rust的IDL定义,Python代码一行未改。
4.3 为什么不用TypeScript/Node.js?
社区常见方案是用Figma Plugin API,但作者明确拒绝:
- Plugin只能在Figma桌面端运行,无法CLI批量处理;
- Plugin沙箱禁止访问本地文件系统,不能读写
.fig; - Plugin API不支持修改Component Set,只能操作实例。
而Rust+Python方案,让你能在服务器上跑cron job,每天凌晨自动同步设计系统到代码仓库;也能在CI里验证PR是否符合设计规范(如“所有Button必须有hover状态”)。
踩坑记录:最初用纯Python实现解析,处理一个12MB文件需2.3秒。换成Rust后降到380ms。但更大的收益是稳定性——Python版在处理含Lottie插件的文件时会OOM,Rust版用
Box::leak控制内存上限,始终稳定在180MB以内。
5. 实战案例:用自然语言批量重构设计系统
理论讲完,来看真实场景。我们拿一个电商App的设计系统(含Button、Card、Badge等27个组件)做测试,目标是:将所有组件的圆角统一从6px改为8px,并为所有Icon组件添加stroke-width:2。
5.1 指令编写:自然语言要带设计语义
错误写法:“把所有圆角改成8px”——太模糊,Agent不知道是cornerRadius还是cornerSmoothing,也不知道作用域。 正确写法:
将Design System中所有Component的cornerRadius属性设为8, 特别排除Status Badge组件(其cornerRadius应保持4), 同时为所有type为ICON的Component添加strokeWidth:2属性。这里的关键是显式声明设计系统边界(避免修改页面里的临时图层)和例外规则(Status Badge是特例)。Agent会自动识别Design System是页面名,Status Badge是组件名。
5.2 执行过程:从指令到文件的完整链路
- CLI输入指令,Python层调用Phi-3提取结构化参数;
- Rust层加载
design-system.fig,构建AST,定位所有Component节点; - 过滤出
page.name == "Design System"且type == "COMPONENT"的节点; - 对每个节点,检查
name != "Status Badge",然后生成两条Patch:replace /cornerRadius → 8add /strokes/0/strokeWidth → 2(若原无strokes则先创建数组)
- 应用Patch,保存新文件。
全程耗时1.7秒(文件14.3MB),生成的.fig在Figma中打开后,所有组件圆角精确变为8px,Icon描边正常显示,且历史版本对比显示仅修改了目标字段,其他属性(如position、size)一字未动。
5.3 高级技巧:用CSS-like选择器精确定位
项目还支持类似CSS的选择器语法,让复杂操作更直观:
# 选择所有在"Product Page"页面下、class为"primary"的Button实例 figma-agent modify --selector 'page[name="Product Page"] > instance[class="primary"]' \ --property 'cornerRadius' --value 12 # 选择所有Variant中state为"disabled"的Text节点 figma-agent modify --selector 'variant[state="disabled"] > text' \ --property 'opacity' --value 0.5这种语法比纯自然语言更可靠,适合写入自动化脚本。选择器引擎也是Rust写的,支持嵌套、属性过滤、伪类(:first-child,:last-of-type),比Figma官方API的查询能力更强。
个人经验:在重构一个含300+页面的设计稿时,用自然语言指令处理了87%的修改,剩下13%用CSS选择器收尾。最省时间的操作是“把所有H1文字大小从32px改成36px”,一行指令搞定,比在Figma里Ctrl+F搜索再逐个改快10倍。但要注意:首次使用前,务必用
figma-agent validate design.fig检查文件完整性,曾遇到过设计师用Beta版Figma保存的文件,Rust解析器报错,需先用Figma正式版另存一次。
6. 边界与局限:它不能做什么,以及为什么
再强大的工具也有边界。这个项目明确划出了三条红线,不是技术做不到,而是刻意为之的设计哲学:
6.1 不支持实时协同编辑
它无法让多个Agent同时修改同一份.fig文件。原因很简单:Figma的实时协同基于Operational Transformation(OT)算法,需要中心化服务协调冲突。而本项目是纯文件操作,相当于“多人同时编辑同一份Word文档”——没有锁机制,后保存者会覆盖前者。解决方案是:用Git管理.fig文件,每次修改生成新commit,用figma-diff工具可视化变更(它能展示“Button圆角从6→8”这样的语义级diff,不是二进制diff)。
6.2 不替代Figma UI交互
你不能用它做“拖拽调整布局”或“用钢笔工具画路径”。所有操作必须可结构化表达:修改属性、增删节点、批量替换。想实现“把左侧导航栏宽度从240px拉到280px”,得写指令:“将Frame[id='nav-sidebar']的width属性设为280”,而不是模拟鼠标拖拽。这是因为Figma UI的交互状态(如拖拽中的临时尺寸)不存于.fig文件,只存在于客户端内存。
6.3 不处理字体渲染差异
指令“把所有文字设为PingFang SC”能成功修改fontName字段,但Figma打开后可能显示为“Helvetica Neue”——因为本地没安装该字体。项目不解决字体缺失问题,但提供了figma-font-checker工具,扫描文件中所有fontName,输出缺失字体列表,方便运维提前部署。
真正的价值不在“全能”,而在精准填补空白:Figma官方说“我们不做自动化”,设计师说“改100个按钮太累”,开发者说“设计稿和代码总是不一致”。这个项目站在三者的缝隙里,用开源代码架起一座桥——它不试图取代任何人,而是让每个人少做重复劳动。就像当年Webpack让前端工程师不再手动拼接JS文件,这个工具让设计系统维护从“体力活”变成“指令活”。
最后分享个小技巧:把常用指令存成.figa文件(如button-radius.figa),内容就是纯文本指令。执行时figma-agent run button-radius.figa --input design.fig --output new.fig,从此设计规范更新,只需双击一个文件。我团队已用它维护3个产品线的设计系统,半年内人工修改工时减少73%。它不是未来科技,而是今天就能抄作业的生产力杠杆。