最近半年,群里聊AI做UI的频率明显降下来了,不是说不做了,而是没人再拿着一张大模型通用对话去生成整页界面了。以前大家喜欢把需求一长串丢给在线大模型,让它直接“写一个后台页面”;现在更多强调的是:单独为UI这件事训练一个小模型,几B参数,不接云,只生成界面结构和组件,能私有化,能秒回,还能绑定自己的设计系统。
这篇文章就从“UI专用小模型”这条新路子展开。适合正在做前端基建、低代码平台、智能设计工具或者内部效率工具的团队参考,也适合想搞清楚小模型能在界面生成里干什么的AI应用开发。下面我会把为什么需要它、训练数据怎么搞、如何部署到本地,以及实际跑起来遇到的各种坑,一次讲清楚。
1. 为什么UI生成要单独训一个小模型
1.1 一锅烩的通用模型,做UI时真的很别扭
先聊最直观的感受。通用大模型确实能写HTML、写Vue组件,甚至能照着截图描述生成一版视觉稿,但真正放在产品里用,问题一个比一个明显。
第一是慢。你让它生成一个完整的管理后台页面,它要先编一大段HTML,再补CSS,有时还要拆几个组件文件,生成时间几十秒不算夸张。交互式对话场景里,用户改一个按钮文案都要等十几秒,这个体验基本没法用。第二是贵。一个后台页面动辄上千token,生成十几次创造一个原型,成本立刻上来了。第三是隐私和合规。很多公司的设计规范、业务字段、内部系统页面根本不适合传到云端模型里,尤其政务、金融、医疗这类场景,数据出内网这件事就是红线。
还有一个更隐蔽的问题是“风格漂移”。同一个通用模型今天生成的是带Tailwind类名的页面,明天生成的是内联样式,后天可能给你造一堆不存在的组件名。它对“你们公司的设计系统”没有任何记忆,你必须在每一条提示词里反复描述一遍,哪怕描述得很仔细,输出也还是不可控。我在实际中见过最夸张的情况是同一个输入提示跑了三次,三个版本的类名、间距、颜色体系都不一样,拿去做验收的人都快崩溃了。
所以痛点不是“能不能生成”,而是“能不能稳定地、便宜地、可私有化地生成一套符合既有设计规范的UI”。
1.2 小模型的机会恰恰来自“任务被收窄”
很多人一听“小模型”就以为是砍参数、降能力。其实做垂直场景模型,我最看重的一点是能不能把任务边界收窄。
UI生成和通用的“聊天”“写文章”是完全不一样的活。界面这件事,本质上是一个封闭集合的问题。你项目的组件库是有限的,设计规范是有限的,页面类型也是有限的。哪怕是B端产品,翻来覆去也就是表单页、列表页、详情页、看板页、弹窗、抽屉。这些页面里的模块就那些:表格、筛选器、统计卡片、步骤条、标签页。
一旦任务边界收窄,小模型就能用有限的参数把规律背下来。比如一个3B的模型,它不需要知道怎么聊历史、怎么写菜谱,它只需要记住“按钮组件叫什么名字”“表单字段有哪些类型”“页面布局怎么排”,这完全可以做到。实际跑下来,3B量级的底座模型在生成中后台页面结构时,准确率并不比几十B的通用模型差,很多时候因为专门微调过,反而更贴近团队规范。
更重要的是,小模型可以本地部署。本地部署意味着数据不出内网、没有按token计费的后顾之忧、延迟可控,甚至可以做成离线工具。私有的设计系统可以完整塞进去,不用在提示词里偷偷摸摸地“描述一个类似Element Plus的表单样式”。模型就是你的设计系统本身。
1.3 “UI专用”不等于“从零训练”
再澄清一个误区:UI专用小模型不需要从零预训练。哪怕你手里的数据量只有几千条,也足够用LoRA方式去微调一个开源底座模型。为什么能这么轻量?因为底座模型已经具备了语言理解和代码生成的基础能力,我们只做“收口”这一步:教它在固定格式下输出固定结构。
正因为有了这个前提,整个项目投入是可以控制的,不需要买一堆显卡,不需要攒几百万条数据。我这边实践下来,一份清洗得还不错的几千条样本,配合QLoRA,在一块消费级显卡上十来分钟就能看到明显效果。如果是纯CPU训练,时间会比较难熬,但也不是不能做。总的原则就是:把“训练”这件事从玄学变成工程流水线。
2. 小模型生成UI的技术底座
2.1 选底座模型:参数不是越大越好
做UI专用模型,底座模型的选择没有想象中那么多讲究。我试过的组合里,3B到4B量级的模型最合适。再大的模型量化后体积太胖,部署麻烦,而且在这个封闭任务里未必有质的提升;再小的模型比如1B以下,文法能力会明显下降,输出容易碎。
可以考虑的底座有这么几类:
| 底座型号 | 参数量 | 量化后体积 | 实际体验 |
|---|---|---|---|
| Qwen2.5-3B-Instruct | 3B | 约2GB(Q4) | 中文理解好,结构化输出稳定 |
| Llama 3.2-3B | 3B | 约2GB(Q4) | 英文物料强,中文需要额外训练 |
| Phi-3-mini | 3.8B | 约2.5GB(Q4) | 代码能力强,中文略弱 |
| Gemma-2 2B | 2B | 约1.5GB(Q4) | 轻量,适合嵌入式端探索 |
做中文业务系统,我首选Qwen系列,因为它对中文自然语言的理解更稳,能少踩一些“语义偏掉”的坑。底座确定后,不要改它的对话模板,除非你非常清楚自己在做什么,否则后续接各种推理框架时会踩模板不匹配的坑。
2.2 输出格式比模型本身更重要
很多新手做AI生成UI,上来就让模型直接输出HTML代码。这样做不是不行,但会让小模型的难度陡增。一份完整的HTML里既有结构又有样式又有内联事件,变量多,小模型学起来费劲,还特别容易出现标签不闭合、类名乱写的问题。
我建议先定义一套简化的UI DSL,也就是让模型生成一种介于自然语言和目标代码之间的结构化描述。这里我把自己常用的一套JSON DSL简化一下展示出来:
{ "type": "Page", "name": "订单管理", "layout": "vertical", "children": [ { "type": "StatisticRow", "items": ["订单总数", "今日新增", "待发货", "退款中"] }, { "type": "Form", "columns": 3, "items": [ { "label": "订单号", "component": "Input", "placeholder": "请输入订单号" }, { "label": "状态", "component": "Select", "options": ["待付款", "已付款", "已发货"] } ] }, { "type": "Table", "columns": ["订单号", "客户", "金额", "状态", "创建时间"], "rowActions": ["查看", "编辑"] } ] }这个JSON其实就是“界面骨架”,模型只需要学会做两件事:根据用户描述,决定页面里有哪些块;给每个块配置合适的组件和属性。然后交给一个固定的渲染器,把JSON翻译成Vue、React甚至是低代码平台的DSL。
好处非常明显:组件的候选集来自你定义的白名单,模型没法凭空造出不存在的组件;样式细节从DSL里剥离掉,模型不用去操心那些它处理不好的像素级问题;结构JSON的合法性强,方便做schema校验,错了能直接反馈重新生成。表面上看多了一层转换,实际模型变好训了,下游代码也更可控了。
2.3 训练数据:真实页面就是最好的教材
训练数据是做小模型最重要的一环。我的经验是别迷信“用大模型合成几万条数据”,高级B端UI最实用的训练数据其实就在你自己的仓库里,就是你团队已经做出来的页面。
我把收集数据的思路拆成三种:
- 真实页面代码反解。从现有项目里抓Vue或React组件代码,通过AST解析还原成DSL结构。这个过程半自动,需要处理组件嵌套和路由页面,但数据质量最高。
- 低代码平台导出。如果你公司有低代码平台,那更省事,后台页面本身就是配置化的,把配置导出成我们的DSL格式就行,还天然干净。
- 大模型辅助重建。拿一些历史需求文档或截图描述,让大模型先生成一版,人工修正成标准DSL后回收。这个适合补冷门页面的样本量。
文案数据也是很重要的一块。同一个表格,用户可能说“展示订单列表”,也可能说“一个表格里面放订单信息”,训练时要把这些不同说法都映射到同一个DSL上。这就需要做数据增强:把DSL逆推回自然语言描述,换个说法生成多份,再组成训练对。
我这里实际项目里采用了约8000条“自然语言+DSL”的训练对,其中真实页面反解占了六成,低代码导出占两成,剩下是大模型辅助修正的。数据质量是质变的关键,宁可少、也要干净,脏数据会让模型输出乱得你想哭。
2.4 微调和偏好对齐
数据准备好以后,微调这一步其实没什么神秘感。直接用指令微调(SFT)做一轮,让模型学会在用户描述后输出对应的DSL。之后再根据需求决定要不要做DPO对齐。
SFT阶段要特别注意:不要把所有训练样本都写在同一个system提示里,要模拟真实调用时的上下文。例如系统提示固定一句话“你是UI生成模型,只输出JSON”,用户输入里再带上场景、屏幕尺寸、风格偏好。这样模型上线后,遇到真实请求不会不知所措。
DPO对齐这里多说一句。SFT学的是“照猫画虎”,DPO学的是“哪个输出更好哪个更差”。我从SFT生成的样本里挑一些“看着合理但其实很烂”的输出,和人工修正后的正确输出配对,做偏好对齐。这个过程能让模型少干一些“模型自嗨”的事,比如生成一堆不存在的组件名、把按钮塞进表格列头这类离谱行为。
3. 实操记录:从数据到可调用的本地UI生成服务
3.1 构造第一版训练集
先别贪多,我的做法是先搭一个最小但完整的训练集跑通全流程。你可以拿30个页面,每个页面拆出5种不同的用户说法,比如“订单管理”“订单列表”“查看订单”“订单查询页”“后台订单页面”,再配上对应的DSL,大概150条数据就够了。
对于没接触过这类训练的人,我建议把这150条数据保存成JSONL格式,每行一条样本,包含三个字段:
{ "instruction": "生成一个订单管理页面:顶部4个统计卡片,下方一个搜索表单和订单表格", "input": "", "output": "{ \"type\": \"Page\", ... }" }把instruction写成用户会说的话,output写成标准DSL字符串。这里最好用真实需求里收集过来的话术,而不是自己拍脑袋写一堆“请帮我生成一个…”的翻译腔。真实话术往往很口语化,比如“搞一个统计展示栏,下面跟表格”,模型见多了才能扛住线上用户的奇怪问法。
3.2 用QLoRA微调一个3B的UI模型
拿到第一批数据后,我通常直接选用QLoRA做参数高效微调。省显存,而且对这个封闭任务来说效果足够好。核心代码大概是下面这个套路:
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training from datasets import load_dataset import torch model_name = "Qwen/Qwen2.5-3B-Instruct" model = AutoModelForCausalLM.from_pretrained( model_name, load_in_4bit=True, torch_dtype=torch.bfloat16, device_map="auto", ) tokenizer = AutoTokenizer.from_pretrained(model_name) tokenizer.pad_token = tokenizer.eos_token lora_config = LoraConfig( r=16, lora_alpha=32, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM", ) model = prepare_model_for_kbit_training(model) model = get_peft_model(model, lora_config)训练参数方面,我比较保守地使用学习率2e-4,batch size 8,跑3到5个epoch。数据量小,第5个epoch开始就容易过拟合,表现为训练loss很低但生成时只会背诵训练集里的输出。所以我的建议是先跑到3个epoch,拿测试集看一眼,如果输出还是乱的,再往上加。
这里有个容易被忽略的点:max_seq_length不要设太低。生成一个完整的页面DSL可能超过1000token,如果训练的截断长度只有512,模型永远学不会长页面怎么收尾。我一般设成2048,少于这个会出现“开头正常、后半截乱跳”的怪毛病。
3.3 量化压缩与本地推理
微调完的模型还在HuggingFace和Peft的架子路上,直接业务调用太重。要往本地部署走,最省事的是转成GGUF格式,再用llama.cpp或者它周边生态跑。
大概的转换流程是这样的:先把LoRA权重合并回基础模型,导出成一个完整的HF格式权重目录;然后用llama.cpp提供的convert脚本把模型转成FP16版本的GGUF;再拿llama-quantize压到Q4_K_M,文件体积能缩到2GB上下。这一步做完,模型就变成单文件了,随便拷到哪台机器都能跑。
启动推理时我一般用llama-server或者自建HTTP服务,关键参数要小心:上下文长度设为2048,因为DSL输出比较长,设短了会截断;温度调到0.2甚至更低。很多人忽略温度这回事,默认0.8去生成JSON,结果就是结构经常发散。偏结构生成任务,温度尽量低,最好是不采样或只做top-k小范围采样。
3.4 作为服务接入业务
模型部署好以后,直接给业务方一个HTTP接口就行。调用链路大概是:
- 前端把用户自然语言描述和页面上下文打包成一个POST请求;
- 后端组装提示词模板,调用模型推理接口;
- 拿到模型输出后做JSON解析和schema校验;
- 校验不通过就抛回给前端,提示“正在重新生成”,或走一次简单的规则后处理;
- 通过校验的DSL交给渲染器,直接渲染出页面预览。
这个链路里最值得下功夫的是第4步的后处理。模型输出偶尔会出现组件名错误、层级嵌套不对、字段丢失之类的毛病。专门写一个校验器比反复调prompt更有效。别把希望全压在模型上,小模型加规则兜底,稳定性才能真正让人放心。
4. 不同场景下的落地形态
4.1 中后台表单页面:小模型最容易出成绩的地方
真要说哪类界面最值得先用小模型,我首选中后台表单页、列表页、数据看板。原因很简单:这类页面结构化程度极高,组件都是现成的,字段就是业务的字段,模型学起来最轻松,出活也最稳定。
你有没有发现,B端页面的规矩多,但翻来覆去就那样:顶部操作栏,中间筛选条件,下面是表格,右侧或底部是分页和按钮。这些结构如果靠人工去拼,熟练前端也要二三十分钟;交给小模型,一秒多就能拿到JSON骨架,再人工微调一下字段顺序和默认值,效率提升非常明显。
低代码平台和表单配置工具是更合适的落地对象。把AI生成的DSL转成平台的配置协议,用户在页面上还能继续拖拽改,用户感知是“AI帮我搭了初版”,但背后其实只是一个小模型加DSL转换器。这个方案不需要大模型API,自己维护一套本地服务就能稳定跑,非常划算。
4.2 营销页和大屏可视化:只能做“结构生成器”
营销首页这类视觉创意要求很高的页面,小模型就显露出短板了。它的知识库和审美上限摆在那里,让它给你配一套高级质感的视觉样式难度很高。但也不是完全不能用,把它当成“结构生成器”就很合适:让模型根据文案脑暴出页面结构,比如头图区、卖点区、案例区、客诉区怎么排,然后在空白结构上让设计师填充视觉创意。人负责审美,模型负责穷举结构。
大屏可视化也有类似逻辑。大屏页面本质是网格布局里的图表拼盘,不是每家公司的图表种类都很多。提前把图表组件和布局方式固化在DSL里,让模型根据“网络延迟指标、CPU使用率、近一小时趋势”这样的输入,自动匹配哪块放折线图、哪块放数字卡片,准确率相当可观。
4.3 移动端和嵌入式UI:模型变小后,新场景开始被解锁
小模型变小的最直接受益者是端侧场景。之前大模型没法塞进移动App或者嵌入式工具链里,现在3B量化后2GB左右,很多主机端甚至边缘设备都能考虑了。像采用ESP32-P4这类芯片的产品,如果设备端本身没有跑大模型的余量,可以把小模型放在配套的桌面工具里,输入一句产品需求,直接生成LVGL或者自定义控件需要的界面描述代码,再交叉编译到设备上。
这件事最大的意义是让“UI模板生成”不再依赖网络。产品经理在现场给客户演示时,本地笔记本上就有一个小模型,说了一句“我需要一个充电桩状态显示页”,几秒钟就出来一版结构,该有的状态、数据、告警区域都在,直接拿着去做方案沟通,比临时拖拽设计快得多。对嵌入式、单片机这类开发周期紧的场景,这种用法是实打实的加分项。
4.4 AI Agent里的UI暴露问题:另一种“小模型”形态
最近挺多人聊移动端AI Agent的UI控制能力,相关技术里有一个核心矛盾是:Agent如果看到的界面信息太完整,既浪费token又容易让决策漂移;如果UI信息暴露太少,又会变成瞎子,不知道哪个按钮在哪。于是有些团队在探索“减少UI暴露”的方案,想办法只给Agent提取关键的操作点。
这里就蹦出另一个形态的“UI小模型”:专门负责从屏幕截图或UI层级里提取结构化操作点,比如“这是一个确认弹窗”“主要按钮在右下角”。它不负责生成完整页面,负责生成页面里的“交互线索”。这正是小模型可以大展拳脚的地方,任务极其单一,输出结构固定,对上下文要求很小,本地端侧跑完全可行。未来UI生成和UI理解这两个方向会各自沉淀出不同的小模型,供不同Agent组件调用。
5. 实测过程中的踩坑笔记
5.1 模型记不住设计系统
遇到最多的问题是微调后模型还是偶尔“失忆”,把Button写成Botton、把Table写成TableList,或者突然冒出一个组件库根本不存在的组件。
后来我用了三个手段叠加解决:一是把组件名列表放进系统提示词,并且要求模型“只能从候选里选”;二是在DSL的schema校验里直接做组件名白名单过滤,一旦出现非法组件名就自动替换成最相近的合法组件;三是准备一份极少量、只有几个例子的few-shot,让模型在固定格式里照着回答。到这里,组件名乱造的问题基本被压住。
5.2 JSON输出不稳定
小模型生成带引号的JSON结构时,偶尔会在嵌套大括号或转义引号上翻车。最常见的错误是少一个花括号、字段值带着多余逗号,或者把JSON包在Markdown代码块里。
我的解决方案是配合推理框架做的语法约束采样。open-source推理里有不少支持GBNF语法约束的方案,直接把JSON Schema改成语法规则,强制模型每一步采样都按合法token来走。实测效果立竿见影,合法率能提高好几成。如果不想引入这么重的依赖,至少在后处理时做一次容错解析,比如自动补全缺失的右括号,但正确率就不如约束采样来得稳。
5.3 生成的UI布局“没有灵魂”
模型生成的页面结构经常是“所有东西从上往下堆”,看起来没什么问题但特别呆板。后来我在数据里人工加了一部分“双栏布局”“侧边栏+内容区”的例子,并显式在DSL里增加layout字段,让模型意识到页面不是一条平铺的流水账。
另一个好用的技巧是拆分生成流程:第一遍只生成页面宏观布局,第二遍再往每个区块里填充细节字段。这可以避免模型在生成长页面时“顾前不顾后”,前半段还在正轨,后半段就开始自由发挥。分步做虽然多一次请求,但质量提升非常明显。
5.4 速度、并发与量化取舍
本地小模型速度再好也比不上云端集群。我实测3B量化模型在一颗普通桌面级CPU上,生成一个1000token的页面DSL大概需要5到8秒;如果机器里有显卡,能压缩到2秒内。看起来很慢,但考虑到它是本地部署的“离线生成器”,不做高频并发,这个延迟是可接受的。
如果要做成公共后台服务,就得考虑并发瓶颈。量化和推理进程会抢CPU,我的经验是一个物理机最好只放一个模型实例,其余服务放另一个节点。如果必须高并发,就上多副本加排队,比在一个进程里堆并发要可靠得多。
5.5 评估不能只看“能不能跑通”
最后也是最重要的:一定要做自动化评估,而不是每个生成结果都拿肉眼看。我把评估拆成了四层:
- 语义匹配:模型是否识别出用户提到的关键业务字段,比如“订单号”“金额”有没有出现在DSL里。
- 结构合法性:JSON能否被正确解析,组件白名单是否通过。
- 类型正确性:Input、Table、Select这些组件是否用对了场景。
- 布局合理性:页面是否存在空的区块,或者没有内容的大块白屏。
跑完自动评估以后,再抽一二十条让人工打标。人工主要看宏观体验,自动主要拦截低级错误。这个机制上线后,我迭代训练数据的速度大幅加快,每次发现一批坏case就把它们修进训练集里,模型质量是滚着往上走的。
我自己的体会是:UI生成这件事,真正的门槛不在于模型本身,而是你愿不愿意把组件、布局、验收规则抽成一套结构化的标准。只要这个标准立住了,小模型的训练、部署、迭代都会顺很多,远程背景下的碎片时间也够维护一个不错的私有模型。最后分享一个小技巧:先让模型输出一份简化版的UI大纲,再展开成完整DSL,这一步能大幅减少结构崩塌的发生概率。如果你也在做类似的工具,我建议可以优先拿到真实页面数据,马上开始第一轮微调,效果出来了再考虑要不要继续堆数据不迟。