StartLux-Decision 源码深度分析
分析日期:2026-10-04
项目地址:https://github.com/StartLuxLabs/StartLux-Decision
Stars:178 | Language:Python | Size:~85MB
模型权重:HuggingFace / ModelScope
一、项目概览
| 属性 | 信息 |
|---|---|
| 定位 | 类型化决策模型(Typed Decision Model) |
| 模型规格 | 5 个 Dense 尺寸(0.8B/2B/4B/9B/27B)+ 1 个 MoE(35B-A3B) |
| 核心能力 | 给定状态+问题集合 → 每个选项返回概率,不生成任何文本 |
| 上下文窗口 | 256K tokens(262,144) |
| 多模态 | 支持图像输入(视觉塔) |
| 延迟 | 0.8B: 12ms / 4B: 26ms / 27B: 102ms / 35B-A3B: 52ms(单 H200) |
| 协议 | TypeSafe /v1/systemone 格式,兼容 Jev API |
| 许可证 | 自定义(非标准开源许可) |
应用场景
- 客服工单分类/路由
- 国际象棋落子决策
- 游戏 AI(超级马里奥、星际争霸、Doom)
- 浏览器自动化(Computer Use)
- 体育比赛策略(JevBall 足球)
二、项目结构
StartLux-Decision/
├── startlux_decision/ # 核心推理代码
│ ├── __init__.py
│ ├── model.py # ⭐ 核心模型推理引擎(38KB)
│ ├── server.py # HTTP 服务
│ ├── jevfmt.py # 问题格式渲染
│ ├── check.py # 快速内核检查
│ ├── mlx_model.py # Apple Silicon MLX 后端
│ ├── mlx_int8.py # MLX INT8 量化
│ └── gguf_server.py # GGUF 格式服务
├── finetune/ # 微调代码
│ ├── finetune_lora.py # ⭐ LoRA 微调脚本
│ └── calibrate.py # 温度校准
├── eval/ # 评估代码
│ ├── typed_decisions.py # 评估套件
│ ├── suites.py # 基准测试集
│ ├── latency.py # 延迟测试
│ └── di/ # Decision Index 评估
├── demos/ # 演示
│ ├── chess/ # 国际象棋对弈
│ └── computer_use/ # 浏览器自动化
├── docs/ # 文档
│ ├── inference.md # 推理指南
│ ├── finetuning.md # 微调指南
│ ├── evaluation.md # 评估指南
│ └── results.md # 结果数据
├── results/ # 原始评估结果
└── requirements.txt
三、是否包含模型网络结构?
结论:包含推理代码,但网络结构依赖 HuggingFace Transformers,不是从头定义的。
模型加载方式
python
# startlux_decision/model.py
def load_model(path, device):
config = transformers.AutoConfig.from_pretrained(path)
extra = {"experts_implementation": "grouped_mm"} if getattr(config.get_text_config(), "num_experts", 0) else {}
model = getattr(transformers, config.architectures[0]).from_pretrained(path, ...)
return model, getattr(model.model, "language_model", model.model)
这意味着:
- 网络结构定义在 HuggingFace 的
transformers库中(通过config.architectures[0]动态加载) - 模型使用了线性注意力层 (Linear Attention),需要
flash-linear-attention和causal-conv1d加速库 - MoE 变体使用
grouped_mm(分组矩阵乘法)实现专家层 - 包含视觉塔(Vision Tower),用于图像理解
model.py的核心是推理引擎(CUDA Graphs、共享 KV 缓存、分块预填充),而不是网络层定义
简言之: 你看不到 class TransformerBlock(nn.Module) 这样的定义------结构由 HuggingFace 的 AutoConfig + 检查点中的 config.json 决定。
四、能否自己训练?
可以做 LoRA 微调,但不能从头预训练。
✅ 提供的:LoRA 微调
finetune/finetune_lora.py 提供了完整的 LoRA 微调流程:
bash
# 1. LoRA 微调
python finetune/finetune_lora.py --model StartLux-Decision-4B \
--train train.jsonl --dev dev.jsonl --out runs/mine
# 2. 温度校准(拟合每种问题类型的最优温度)
python finetune/calibrate.py --model runs/mine/merged --dev dev.jsonl
# 3. 部署
python -m startlux_decision.server --model runs/mine/merged --port 8090
训练数据格式(JSONL):
json
{
"state": {"ticket": "Refund still missing after two weeks", "customer_tier": "pro"},
"questions": {
"team": {"type": "choice", "instructions": "Which team?",
"criteria": {"billing": "Payments", "shipping": "Delivery", "technical": "Bugs"}},
"urgent": {"type": "noul", "instructions": "Solved today?"},
"severity": {"type": "score", "criteria": ["cosmetic", "annoying", "blocks customer"]}
},
"targets": {"team": {"billing": 1.0}, "urgent": {"false": 1.0}, "severity": {"1": 0.7, "2": 0.3}}
}
微调参数:
- rank=16, alpha=32, dropout=0.05
- lr=1e-4, warmup=5%, cosine decay
- 2 epochs, 4 micro-batches per optimizer step
- 损失函数:选项字母 logits 上的交叉熵
性能参考: 单 H200 上,4B 模型 6000 条样本约 3 分钟完成。
❌ 不提供的
| 缺失项 | 说明 |
|---|---|
| 预训练代码 | 没有从头训练模型的代码 |
| 预训练数据集 | 只知道用了 14 个公开 benchmark 的训练集(已过滤测试集) |
| 网络结构定义 | 依赖 transformers 库中的架构类 |
| 训练超参细节 | 预训练的 lr schedule、batch size、数据配比等未公开 |
五、决策推理的核心原理 ⭐
核心机制:Logit Readout(Logits 直读)
这是最关键的创新------完全不做文本生成,一次前向传播直接读出答案。
用户输入: state + questions
│
▼
┌─ 将每个问题渲染为 prompt ──────────────┐
│ "工单: 退款两周未到账... │
│ Question: 哪个团队处理这个工单? │
│ A. billing (Payments, refunds) │
│ B. shipping (Delivery, tracking) │
│ C. technical (App bugs, outages) │
│ Answer:" │
└─────────────────────────────────────────┘
│
▼
一次前向传播 (single forward pass)
│
▼
读取 "Answer:" 位置的 hidden state (h)
│
▼
logits = h × letter_rows^T ← 只乘 A-Z 这 26 个字母的 output embedding
│
▼
probabilities = softmax(logits / temperature)
│
▼
返回: {"billing": 0.877, "shipping": 0.005, "technical": 0.118}
源码关键片段
python
# 初始化时:只提取选项字母(A-Z)对应的 output embedding 行
self.letter_rows = head.index_select(0, torch.tensor(self.letters)).float()
# 推理时:一次前向传播,取最后一个有效 token 的 hidden state
hidden = self.body(input_ids=ids, use_cache=False).last_hidden_state
h = hidden[torch.arange(ids.shape[0]), last].float()
# 矩阵乘法得到 26 个字母的 logits → softmax 得到概率
logits = h @ self.letter_rows.T
与传统 LLM 的对比
| 维度 | 传统 LLM(如 GPT/Claude) | StartLux-Decision |
|---|---|---|
| 输出方式 | 自回归生成文本(token by token) | 一次前向传播,直读 logits |
| 计算量 | 生成 N 个 token → N 次前向传播 | 1 次前向传播 |
| 确定性 | 采样有随机性 | 确定性(softmax 固定) |
| 输出格式 | 自由文本,需要解析 | 结构化概率分布 |
| 延迟 | 数百毫秒到数秒 | 12-102ms |
| 置信度 | 无内置置信度 | 每个选项都有概率值 |
| 幻觉风险 | 可能生成不存在的选项 | 不可能(只能选给定选项) |
三种问题类型
| 类型 | 说明 | 输出 |
|---|---|---|
choice |
从 N 个选项中选(最多 26 个) | 选中项 + 所有选项的概率分布 |
noul |
Yes/No 二选一 | yes 的概率(0-1) |
score |
评分等级(最多 10 级) | 概率加权平均分 + 每级概率 |
超过 26 个选项时自动分组:先分组淘汰,top-3 进入决赛轮,落选选项保留残余概率。
六、为何能做到极快?------ 三层加速
1️⃣ 无生成(最大贡献)------ 省掉 90%+ 计算量
传统 LLM: "我认为应该分配给 billing 团队..." → 30+ tokens → 30+ 次前向传播
Decision: [billing=0.877, shipping=0.005, technical=0.118] → 1 次前向传播
2️⃣ CUDA Graphs ------ 消除 kernel 启动开销
python
# 启动时预录制 CUDA 图:每种输入长度一张图
GRAPH_LENGTHS = (128, 192, 256, 320, 384, 512, 640, 768, 1024, 1536, 2048, 3072, 4096)
GRAPH_ROWS = (1, 2, 3, 4) # 每个请求的问题数 → 共 40 张图
# 推理时直接重放图,跳过逐层 kernel 调度
graph = torch.cuda.CUDAGraph()
with torch.cuda.graph(graph, pool=pool):
out = self._slot_logits(ids, last)
效果对比(4B 模型):
- CUDA Graphs ON: 26.0 ms
- CUDA Graphs OFF: 90.3 ms
- 差距:3.5 倍
3️⃣ Flash Linear Attention + 共享 KV 缓存
- 线性注意力层 :模型混合使用标准注意力和线性注意力,后者需要
flash-linear-attention+causal-conv1d加速(没有这两个库会慢 10 倍以上) - 共享 KV 缓存:同一请求的多个问题共享相同的 state/evidence,共享部分只计算一次:
python
SHARE_MIN = 4096 # 共享前缀超过此长度才触发
PREFILL_CHUNK = 32768 # 分块处理长输入,控制激活内存
# 同一请求中多个问题:"哪个团队?""是否紧急?""严重程度?"
# 三个 prompt 都包含完整的 state → 共享前缀只计算一次
# 后面的问题部分作为一个 batch 在共享 KV 上运行
延迟实测(单 H200 GPU,bf16)
| 模型 | 3 个问题(一次前向) | 单个 Yes/No |
|---|---|---|
| 0.8B | 12.2 ms | 8.3 ms |
| 2B | 15.5 ms | 9.6 ms |
| 4B | 26.0 ms | 14.7 ms |
| 9B | 35.7 ms | 17.6 ms |
| 27B | 102.3 ms | 50.7 ms |
| 35B-A3B (MoE) | 52.5 ms | 36.4 ms |
35B-A3B 比 27B 快一半,因为 MoE 每次只激活约 3B 参数。
与竞品延迟对比
| 模型 | 3 fields 延迟 | 硬件 |
|---|---|---|
| StartLux-Decision-0.8B | 12.2 ms | H200 |
| StartLux-Decision-4B | 26.0 ms | H200 |
| Intern-Decision-0.8B | 34.0 ms | RTX 4090 |
| Intern-Decision-4B | 44.2 ms | RTX 4090 |
| Jev 1.13 | 64.0 ms | TypeSafe API (server time) |
七、长输入处理(256K tokens)
python
# 长 prompt 分块处理,控制内存
PREFILL_CHUNK = 32768 # 每次处理 32K tokens
# 每个 chunk:
# 1. 对之前的 keys 做非因果注意力(无 mask)
# 2. 对自己的 keys 做因果注意力
# 3. 两部分的输出通过 log-sum-exp 合并
- 256K tokens 的 27B/35B 需要约 80GB GPU 显存
- 内存主要由 KV 缓存占用,激活内存通过分块控制
八、多模态(图像输入)
python
# 图像作为证据的一部分
answers, usage = m.decide(
"Photo at delivery: <image>",
{"damaged": {"type": "noul", "instructions": "Is the parcel damaged?"}},
images=["parcel.jpg"]
)
- 每张图像 resize 到最多 1,048,576 像素(
max_pixels) - 每 32×32 像素 → 1 个 token(1024×1024 图 = 1024 tokens)
- 视觉塔每个请求只运行一次,所有问题共享图像 token
- 视觉塔额外占用 0.2-0.9 GB 显存
- 可通过
images=False关闭以节省显存
九、Decision Index 评估结果
StartLux-Decision-27B 在 Decision Index 0.2.1 上得分 63.88,超过 Jev 1.13 的 57.91。
| 领域 | StartLux-27B | Jev 1.13 | 差距 |
|---|---|---|---|
| Decision Index 总分 | 63.88 | 57.91 | +5.97 |
| 语言理解 | 74.5 | 62.0 | +12.5 |
| 检索与分类 | 66.8 | 55.4 | +11.4 |
| 工具与自动化 | 82.2 | 75.1 | +7.1 |
| 艺术与人类品味 | 47.9 | 37.7 | +10.2 |
| 知识与推理 | 44.3 | 51.4 | -7.1 |
弱项: 在 GPQA Diamond(研究生级科学问答)、MMLU-Pro、BBH 等纯知识推理基准上不如 Jev 1.13。
十、技术总结
| 问题 | 答案 |
|---|---|
| 包含网络结构吗? | 推理代码完整,结构定义在 HuggingFace transformers 中,不是自定义网络 |
| 能自己训练吗? | ✅ LoRA 微调(完整代码),❌ 从头预训练(无代码和数据) |
| 决策原理? | 一次前向传播 → 读取选项字母位置的 logits → softmax 得到概率分布。不生成任何文本 |
| 为何快? | ① 无生成(1 次 vs N 次前向传播)② CUDA Graphs 消除启动开销 ③ Flash Linear Attention + 共享 KV |
| 创新点? | 把"分类/决策"问题重新定义为"读 logits"问题,用 transformer 的 output embedding 空间直接做分类 |
本质上: 这不是一个新的模型架构,而是一个巧妙的推理范式------把 transformer 当成"概率分配器"而非"文本生成器"。创新在于"怎么读答案",而非"模型长什么样"。