🗺️
§0 项目总览
一句话定位
LLaMA-Factory = 零代码 CLI + Web GUI,统一接口覆盖 LLM 微调全流程(PT → SFT → RM → RLHF → DPO → GRPO),所有差异只通过 YAML 配置文件区分。
核心设计理念:
- 统一接口:所有训练方式通过同一个
llamafactory-cli train命令触发 - 零代码友好:LLaMA Board Web GUI 全程图形化,无需写代码
- 参数效率优先:LoRA/QLoRA 作为默认推荐,单卡 6GB 可跑 7B QLoRA
- 模型无关设计:通过 template.py 注册 Chat Template,新模型只需添加模板
- Day-0 追踪前沿:核心团队在新模型发布当天/次日提供支持
📊 项目规模
- GitHub Stars:40k+
- 论文:ACL 2024
- 支持模型:100+ 系列
- 支持训练方式:8 种
🔗 生态支持
- 模型仓库:HuggingFace / ModelScope / Modelers
- 推理后端:HF / vLLM / SGLang / KTransformers
- 实验追踪:W&B / SwanLab / MLflow / TensorBoard
- 分布式:FSDP / DeepSpeed ZeRO-1/2/3
🤖
§1 支持的模型
主流语言模型(精选)
| 模型系列 | 支持规模 | Chat Template |
|---|---|---|
| Llama 3/3.1/3.2/3.3 | 1B/3B/8B/70B | llama3 |
| Llama 4 | 109B/402B | llama4 |
| Qwen2/2.5(含 Code/Math/MoE) | 0.5B~110B | qwen |
| Qwen3(含 MoE/Thinking) | 0.6B~235B | qwen3 / qwen3_nothink |
| DeepSeek-V3 | 236B/671B | deepseek3 |
| DeepSeek-R1(含 Distill) | 1.5B~671B | deepseekr1 |
| Mistral / Mixtral | 7B/8x7B/8x22B | mistral |
| Gemma 3 / Gemma 3n | 270M~27B | gemma3 |
| GLM-4 / GLM-Z1 | 9B/32B | glm4 |
| Phi-4 / Phi-4-mini | 3.8B/14B | phi4 |
| InternLM 2/3 | 7B/8B/20B | intern2 |
| Falcon / Granite / StarCoder 2 | 多种 | 多种 |
多模态模型
| 模型系列 | 规模 | 类型 |
|---|---|---|
| LLaVA-1.5 / LLaVA-NeXT | 7B/13B/34B | 图像理解 |
| InternVL 2.5/3.5 | 1B~241B | 图像/视频 |
| Qwen2-VL / Qwen2.5-VL / Qwen3-VL | 2B~235B | 图像理解 |
| Qwen2.5-Omni / Qwen3-Omni | 3B/7B/30B | 全模态 |
| PaliGemma / PaliGemma2 | 3B/10B/28B | 图像理解 |
| MiniCPM-o / MiniCPM-V 4.5 | 8B/9B | 多模态 |
⚡ Day-0 支持:Qwen3 / Qwen2.5-VL / Gemma 3 / GLM-4.1V / InternLM 3 / MiniCPM-o-2.6
Day-1 支持:Llama 3 / GLM-4 / Mistral Small / PaliGemma2 / Llama 4
Day-1 支持:Llama 3 / GLM-4 / Mistral Small / PaliGemma2 / Llama 4
⚡
§2 训练方式 × 微调方法矩阵
| 训练方式 | Full | Freeze | LoRA | QLoRA | OFT | QOFT |
|---|---|---|---|---|---|---|
| Pre-Training (PT) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Supervised Fine-Tuning (SFT) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Reward Modeling (RM) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| PPO Training | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| DPO / ORPO / SimPO | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| KTO Training | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| GRPO Training | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
显存需求参考(7B 模型)
| 方法 | 精度 | 7B | 14B | 70B |
|---|---|---|---|---|
| Full-tuning | bf16/fp16 | 120GB | 240GB | 1200GB |
| Full-tuning (pure_bf16) | bf16 | 60GB | 120GB | 600GB |
| LoRA / Freeze | 16-bit | 16GB | 32GB | 160GB |
| QLoRA | 8-bit | 10GB | 20GB | 80GB |
| QLoRA | 4-bit | 6GB ⭐ | 12GB | 48GB |
✅ 高级算法扩展
优化器:GaLore、BAdam、APOLLO、Adam-mini、Muon
LoRA 变体:DoRA、LongLoRA、LoRA+、LoftQ、PiSSA、rsLoRA
加速:FlashAttention-2、Unsloth、Liger Kernel、KTransformers
架构:LLaMA Pro(block expansion)、Mixture-of-Depths
优化器:GaLore、BAdam、APOLLO、Adam-mini、Muon
LoRA 变体:DoRA、LongLoRA、LoRA+、LoftQ、PiSSA、rsLoRA
加速:FlashAttention-2、Unsloth、Liger Kernel、KTransformers
架构:LLaMA Pro(block expansion)、Mixture-of-Depths
🏗️
§3 代码架构解析
3.1 src/ 目录结构
src/
├── api.py # OpenAI 兼容 API 服务入口
├── train.py # 训练主入口
├── webui.py # Gradio WebUI 启动入口
└── llamafactory/ # 核心包
├── cli.py # CLI 命令分发(train/chat/export/eval/api/webui)
├── launcher.py # 分布式训练启动器(torchrun/deepspeed 封装)
│
├── hparams/ # 超参数定义(dataclass 形式)
│ ├── data_args.py # 数据相关参数
│ ├── finetuning_args.py # 微调方法参数(LoRA/QLoRA/Full 等)
│ ├── generating_args.py # 生成推理参数
│ └── model_args.py # 模型加载参数
│
├── data/ # 数据处理模块
│ ├── template.py # Chat Template 定义(所有模型的对话模板)
│ ├── dataset_info.json # 内置数据集注册表
│ ├── loader.py # 数据集加载(HF/ModelScope/本地)
│ ├── processor/ # 数据预处理(SFT/PT/RM/RLHF 格式)
│ └── collator.py # DataCollator(序列填充、打包训练)
│
├── model/ # 模型加载与适配模块
│ ├── loader.py # 模型加载(AutoModel/AutoTokenizer)
│ ├── patcher.py # 模型 Patch(注入 Flash Attention、RoPE 等)
│ ├── adapter.py # PEFT 适配器(LoRA/QLoRA/OFT 初始化)
│ └── model_utils/ # 工具函数(量化、合并、导出等)
│
├── train/ # 训练流程(按任务类型拆分)
│ ├── sft/trainer.py # CustomSeq2SeqTrainer (SFT)
│ ├── dpo/trainer.py # CustomDPOTrainer (DPO/ORPO/SimPO/BCO)
│ ├── kto/trainer.py # CustomKTOTrainer
│ ├── ppo/trainer.py # CustomPPOTrainer (PPOTrainer + Trainer 多重继承)
│ └── grpo/trainer.py # CustomGRPOTrainer
│
├── chat/ # 推理与对话模块
│ ├── hf_engine.py # HuggingFace 推理引擎
│ ├── vllm_engine.py # vLLM 推理引擎
│ └── sglang_engine.py # SGLang 推理引擎
│
├── api/ # OpenAI 兼容 REST API(FastAPI)
├── webui/ # Gradio Web UI(LLaMA Board)
└── extras/ # 常量、日志、回调(SwanLab/WandB/TensorBoard)
3.2 核心依赖
| 包名 | 推荐版本 | 作用 |
|---|---|---|
transformers | 4.50.0 | 模型定义、tokenizer、生成逻辑,HuggingFace 核心库 |
peft | 0.15.1 | LoRA/QLoRA/OFT 等参数高效微调 |
trl | 0.9.6 | PPO/DPO/KTO/GRPO/ORPO 等 RLHF 训练器 |
datasets | 3.2.0 | 数据集加载与 map 处理 |
accelerate | 1.2.1 | 分布式训练支持(FSDP/DeepSpeed 封装) |
bitsandbytes | ≥0.39 | INT4/INT8 量化(QLoRA 核心),可选 |
vllm | ≥0.4.3 | 高速推理后端,可选 |
flash-attn | ≥2.5.6 | FlashAttention-2,可选 |
🧪
§4 训练方法深度解析
训练器类继承关系:
transformers.Seq2SeqTrainer→ CustomSeq2SeqTrainer(SFT)trl.DPOTrainer→ CustomDPOTrainer(DPO/ORPO/SimPO/BCO)trl.KTOTrainer→ CustomKTOTrainer(KTO)trl.PPOTrainer + Trainer→ CustomPPOTrainer(PPO,多重继承)trl.GRPOTrainer→ CustomGRPOTrainer(GRPO)
4.1 SFT(监督微调)
用有标注的输入-输出对,最大化正确回答的对数似然。损失只计算 回答部分的 token,忽略 prompt 部分(通过 IGNORE_INDEX=-100 实现)。
# 核心损失(标准交叉熵)
loss = cross_entropy(logits, labels) # labels 中 prompt 部分设为 -100
# 可选:ASFT 损失(参考冻结模型)
ref_logits = ref_model(input_ids).logits
loss = asft_loss(logits, labels, ref_logits, alpha)
适用场景:任务适配、指令跟随能力学习、数据有限(1K+ 条)快速适配。最简单、最稳定、数据效率最高。
4.2 DPO / ORPO / SimPO
绕过 Reward Model,直接用偏好对数据优化策略,使好回答相对差回答的 log 概率差更大。
# 标准 DPO 损失(beta * log_ratio_diff → sigmoid)
logits = beta * (chosen_logps - ref_chosen_logps) \
- beta * (rejected_logps - ref_rejected_logps)
loss = -log_sigmoid(logits)
# ORPO:无需 Ref Model(log_odds 形式)
# SimPO:长度归一化 + margin,无需 Ref Model
| 变体 | pref_loss 参数 | 是否需要 Ref Model | 特点 |
|---|---|---|---|
| DPO | sigmoid | ✅ | 标准偏好学习 |
| ORPO | orpo | ❌ | 节省显存,无 ref model |
| SimPO | simpo | ❌ | 长度归一化,更好长度控制 |
| BCO | bco | ✅ | 运行均值基线 |
4.3 KTO(Kahneman-Tversky Optimization)
只需二元标签(好/坏),无需配对数据,标注成本最低。利用前景理论的损失函数。
# 数据格式:(prompt, response, kto_tag: True/False)
kl = mean(policy_logps - ref_logps) # KL 散度项
chosen_loss = 1 - sigmoid(beta * (chosen_logps - ref_chosen_logps) - beta * kl)
rejected_loss = 1 - sigmoid(beta * kl - beta * (rejected_logps - ref_rejected_logps))
loss = mean(chosen_loss) + mean(rejected_loss)
4.4 PPO(完整 RLHF)
完整在线强化学习流程:先训 Reward Model,再用 PPO 在线采样更新策略。效果最强,但流程最复杂、算力消耗最大。
# 两阶段:
# 1. 训练 RM:llamafactory-cli train stage=rm
# 2. PPO 强化学习:
for step in steps:
responses = model.generate(queries) # 生成回答
rewards = reward_model(query + response) # RM 打分
# PPO 更新:clipped surrogate + value loss + KL 惩罚
L = -min(r_t * A_t, clip(r_t, 1-ε, 1+ε) * A_t) + β * KL(π || π_ref)
注意:需要
AutoModelForCausalLMWithValueHead(额外的 value head)。TRL 版本锁定:0.8.6 ≤ trl ≤ 0.9.64.5 GRPO(DeepSeek-R1 同款)
对每个 prompt 采样 G 个回答,组内相对奖励归一化为优势值,无需 Critic / Value Head,比 PPO 更简单。
# 每个 prompt 采样 G 个回答
completions = [model.generate(prompt) for _ in range(G)]
rewards = reward_func(prompts, completions) # 规则函数 or RM
# 组内归一化(无需 Critic!)
advantages = (rewards - mean(rewards)) / (std(rewards) + 1e-8)
# PPO-clip 风格梯度更新 + KL 惩罚
ratio = exp(policy_logps - old_logps)
loss = -min(ratio * advantages, clip(ratio, 1-ε, 1+ε) * advantages) + beta * kl
| 维度 | PPO | GRPO |
|---|---|---|
| Critic / Value Head | ✅ 需要 | ❌ 不需要 |
| 优势估计 | GAE(时间差分) | 组内相对奖励归一化 |
| RM 形式 | 通常神经网络 RM | 规则函数 or RM 均可 |
| 适用场景 | 通用 RLHF | 可验证奖励的推理任务(数学/代码) |
4.6 如何选择训练方法
| 场景 | 推荐方法 | stage 参数 |
|---|---|---|
| 提升特定任务能力,有标注输入-输出对 | SFT | sft |
| 有配对偏好数据(chosen/rejected),不想维护 RM | DPO | dpo(pref_loss: sigmoid) |
| 不想要 Reference Model,节省显存 | ORPO | dpo(pref_loss: orpo) |
| 只有单条好/坏标签,无配对数据 | KTO | kto |
| 数学/代码等可验证奖励推理任务 | GRPO | grpo |
| 追求极致对齐,有充足算力 | PPO | rm → ppo |
📦
§5 数据集格式规范
两种主格式:
- Alpaca:单轮指令跟随、预训练、偏好数据,核心字段
instruction / input / output - ShareGPT:多轮对话、工具调用、多模态,核心字段
conversations列表
json、jsonl、csv、parquet、arrow
5.1 Alpaca 格式(SFT 单轮)
[{
"instruction": "将下面的句子翻译为英文。",
"input": "今天天气真好。", // 可选,与 instruction 拼接
"output": "The weather is really nice today.",
"system": "你是翻译助手。", // 可选
"history": [["Q1", "A1"], ["Q2", "A2"]] // 可选历史对话
}]
5.2 ShareGPT 格式(多轮对话)
[{
"conversations": [
{"from": "human", "value": "查询北京今天的天气"},
{"from": "function_call","value": "{\"name\": \"get_weather\", ...}"},
{"from": "observation", "value": "{\"temperature\": \"25°C\"}"},
{"from": "gpt", "value": "北京今天天气晴,25°C,适合出行。"}
],
"system": "你是天气助手。",
"tools": "[{\"name\": \"get_weather\", ...}]"
}]
角色规则:
human / observation 在奇数位置,gpt / function_call 在偶数位置,两者严格交替。
5.3 偏好数据格式(DPO/RLHF)
// Alpaca 偏好格式(ranking: true)
[{
"instruction": "写一首关于春天的诗",
"input": "",
"chosen": "春风送暖入屠苏,万紫千红总是春...", // 更好的回复
"rejected": "春天来了,花开了,很好看。" // 更差的回复
}]
// KTO 格式(只需好/坏标签)
[{
"conversations": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}],
"kto_tag": true // true = 好回复,false = 差回复
}]
5.4 dataset_info.json 配置
所有数据集通过 data/dataset_info.json 注册,是 LLaMA-Factory 的数据集注册中心。
{
// 最简 Alpaca(使用默认列名)
"my_sft": {
"file_name": "my_data.json"
},
// 自定义列名
"my_custom": {
"file_name": "data.json",
"columns": {
"prompt": "question", // 默认 instruction
"response": "answer" // 默认 output
}
},
// ShareGPT 多轮对话
"my_chat": {
"file_name": "chat.json",
"formatting": "sharegpt",
"columns": {"messages": "conversations", "tools": "tools"}
},
// DPO 偏好数据
"my_dpo": {
"file_name": "dpo.json",
"ranking": true, // 必须 true
"formatting": "sharegpt",
"columns": {"messages": "conversations", "chosen": "chosen", "rejected": "rejected"}
},
// 从 HuggingFace 加载
"openorca": {
"hf_hub_url": "Open-Orca/OpenOrca",
"columns": {"prompt": "question", "response": "response", "system": "system_prompt"}
}
}
快速准备自定义数据集(5 步)
1
确定格式:SFT → Alpaca;多轮/工具调用 → ShareGPT;偏好 → 加 ranking:true
2
准备数据文件:JSON/JSONL 放入
data/ 目录3
注册到 dataset_info.json:若列名非默认值,在 columns 中显式映射
4
快速验证:
llamafactory-cli train --dataset my_custom --max_steps 5 --output_dir /tmp/test5
正式训练:创建完整 YAML 配置文件,启动训练
⚠️ 常见注意事项:
cutoff_len:超过该长度的样本被截断(非过滤),建议 2048~4096- ShareGPT:human/gpt 必须严格交替,不能有两个连续的 human 消息
- DPO:SFT 推荐 1K+ 条,DPO 偏好对推荐 2K~50K 对,质量差距越大越好
- 多模态:
<image>占位符数量必须与 images 列表长度完全一致 - 多数据集混合:
dataset: alpaca_en,my_custom,各数据集按顺序拼接
🚀
§6 快速上手
6.1 安装
📦 方式一:源码安装(推荐)
git clone --depth 1 \
https://github.com/hiyouga/LlamaFactory.git
cd LlamaFactory
pip install -e .
🐳 方式二:Docker
docker run -it --rm \
--gpus=all --ipc=host \
hiyouga/llamafactory:latest
⚡ 方式三:uv
uv run llamafactory-cli webui
可选依赖
| 功能 | 安装命令 |
|---|---|
| QLoRA 量化(4/8-bit) | pip install bitsandbytes |
| vLLM 高速推理 | pip install vllm |
| FlashAttention-2 | pip install flash-attn |
| DeepSpeed 分布式 | pip install -r requirements/deepspeed.txt |
🇨🇳 国内加速:
export USE_MODELSCOPE_HUB=1,然后在 YAML 中使用 ModelScope 的 model ID。
6.2 第一次训练(Qwen3-4B LoRA SFT)
1
创建训练配置 YAML(
train_qwen3_lora_sft.yaml):
### model
model_name_or_path: Qwen/Qwen3-4B-Instruct
trust_remote_code: true
### method
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
### dataset
dataset: identity,alpaca_en_demo
template: qwen3_nothink # 必须与模型对应
cutoff_len: 2048
max_samples: 1000
### output
output_dir: saves/qwen3-4b/lora/sft
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
2
启动训练:
CUDA_VISIBLE_DEVICES=0 llamafactory-cli train train_qwen3_lora_sft.yaml
训练完成后输出目录包含:adapter_model.safetensors(LoRA 权重)+ training_loss.png(loss 曲线)
3
推理验证:
llamafactory-cli chat examples/inference/qwen3_lora_sft.yaml
4
合并 LoRA 权重(导出完整模型):
llamafactory-cli export examples/merge_lora/qwen3_lora_sft.yaml
# ⚠️ 合并时不要使用 quantization_bit
6.3 核心 YAML 配置字段详解
模型配置
| 字段 | 说明 | 示例 |
|---|---|---|
model_name_or_path ⭐ | HF/ModelScope ID 或本地路径 | Qwen/Qwen3-4B-Instruct |
template ⭐ | 对话模板,必须与模型匹配 | qwen3, llama3, deepseek3 |
quantization_bit | 量化位数(QLoRA) | 4 或 8 |
flash_attn | 启用 FlashAttention | fa2 / sdpa |
rope_scaling | 扩展上下文长度 | linear / dynamic |
训练方法配置
| 字段 | 说明 | 可选值 |
|---|---|---|
stage ⭐ | 训练阶段 | pt / sft / rm / ppo / dpo / kto / grpo |
finetuning_type ⭐ | 微调类型 | lora / freeze / full |
lora_rank | LoRA 秩,越大参数越多 | 8~64(默认 8) |
lora_alpha | LoRA 缩放,通常 = rank*2 | 16 |
lora_target | 应用 LoRA 的层 | all(全部) |
use_dora | DoRA(权重分解 LoRA) | true/false |
训练超参数
| 字段 | 说明 | 推荐值 |
|---|---|---|
per_device_train_batch_size | 每卡 batch size | 1~4 |
gradient_accumulation_steps | 梯度累积(有效 batch = per_device × 卡数 × 此值) | 8 |
learning_rate | 学习率 | 1e-4(LoRA)/ 5e-5(Full) |
num_train_epochs | 训练轮数 | 3.0 |
lr_scheduler_type | 学习率调度 | cosine |
bf16 | bfloat16 精度(A100/H100/4090) | true |
neftune_noise_alpha | NEFTune 噪声增强,提升泛化 | 5 |
6.4 CLI 命令速查
| 子命令 | 功能 | 示例 |
|---|---|---|
train | 训练/微调模型 | llamafactory-cli train config.yaml |
chat | CLI 对话推理 | llamafactory-cli chat infer.yaml |
webchat | Web 对话(Gradio) | llamafactory-cli webchat infer.yaml |
api | OpenAI 兼容 API Server | llamafactory-cli api infer.yaml |
export | 合并 LoRA / 导出模型 | llamafactory-cli export merge.yaml |
eval | MMLU/C-Eval 评测 | llamafactory-cli train eval.yaml |
webui | LLaMA Board 完整 GUI | llamafactory-cli webui |
# 常用训练命令
CUDA_VISIBLE_DEVICES=0 llamafactory-cli train config.yaml # 单 GPU
CUDA_VISIBLE_DEVICES=0,1 llamafactory-cli train config.yaml # 多 GPU
FORCE_TORCHRUN=1 llamafactory-cli train config.yaml # torchrun
FORCE_TORCHRUN=1 llamafactory-cli train config_ds3.yaml # DeepSpeed ZeRO-3
# 命令行覆盖 YAML 参数
llamafactory-cli train config.yaml learning_rate=5e-5 num_train_epochs=5
🌐
§7 推理与部署
LLaMA Board WebUI
llamafactory-cli webui # 本地访问 http://localhost:7860
| Tab | 功能 |
|---|---|
| Train | 选择模型、数据集、微调方法,启动/中断训练,实时查看 Loss |
| Evaluate & Predict | 加载模型,在验证集上评估指标或批量预测 |
| Chat | 交互式对话,测试训练后的模型效果 |
| Export | 合并 LoRA 权重,导出完整模型 |
vLLM 高性能推理(推荐生产)
# 启动 OpenAI 兼容 API Server(vLLM backend)
API_PORT=8000 llamafactory-cli api infer.yaml infer_backend=vllm
# 使用 OpenAI SDK 调用
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="0")
response = client.chat.completions.create(
model="your-model",
messages=[{"role": "user", "content": "你好!"}]
)
# 先合并 LoRA 再用 vLLM 直接加载(性能最优)
llamafactory-cli export merge.yaml
vllm serve /path/to/merged_model --served-model-name my-ft-model
| 推理后端 | infer_backend 参数 | 适用场景 |
|---|---|---|
| HuggingFace | huggingface | 调试、低并发 |
| vLLM | vllm | 生产部署,约 270% 速度提升 |
| SGLang | sglang | 结构化生成优化 |
| KTransformers | ktransformers | CPU 混合推理 |
实验监控集成
# W&B
report_to: wandb
# SwanLab(国产,支持中文)
use_swanlab: true
swanlab_api_key: YOUR_KEY
✅
§8 选型速查
5 分钟决策树
| 我的情况 | 推荐路线 | YAML 关键参数 |
|---|---|---|
| 🎯 单卡 24GB,快速适配特定任务 | QLoRA + SFT | finetuning_type: lora, quantization_bit: 4 |
| 🎯 单卡 16GB,效果优先 | LoRA + SFT | finetuning_type: lora, bf16: true |
| 🎯 有偏好数据,想做对齐 | LoRA + DPO | stage: dpo, pref_loss: sigmoid |
| 🎯 节省显存,无 Ref Model | LoRA + ORPO | stage: dpo, pref_loss: orpo |
| 🎯 推理/数学任务,可验证奖励 | LoRA + GRPO | stage: grpo, reward_funcs: [accuracy] |
| 🎯 多卡集群,极致对齐 | LoRA + PPO | stage: rm 先训 RM,再 stage: ppo |
| 🎯 零代码,图形界面操作 | LLaMA Board | llamafactory-cli webui |