8,773 元 从 0 训练一个 MC 知识助手
—— 一份完整复盘
从 0 训练 200M–1B 的 Minecraft 中文百科小模型,21 天 21 个版本,最终 qa_120 = 1.91。 同期同条件下,开源 Qwen2.5-1.5B 不做任何微调 = 2.83。本文如实记录方法、数字、转折点。
1. 背景与目标
目标很具体:一个端侧(手机)能跑的中文 Minecraft 百科助手—— 问"末影龙血量多少"、"红石中继器最大延迟几 tick"、"如何刷铁机制",能给出准确、自然的中文回答。
不做的事同样明确:通用 AI、多步推理、数值计算、创作类、实时信息、商业化。
2. 技术架构与核心认知
整个项目最重要的一条认知——模型负责风格,RAG 负责事实。早期考虑过把所有 MC 知识"灌"进 200M 参数里, 花了一段时间才意识到这条路走不通:
- 200M 参数装不下所有事实——参数化记忆是有损压缩,模型容易把 1561 错记成 1500。
- 模组扩展要求"加索引而不是重训"——如果新模组必须重训模型,整个产品形态就崩了。
- 这是当代主流——ChatGPT browsing、Perplexity 都是 RAG 路线,不是退化。
模型负责
- 理解用户意图
- 组织语言成连贯回答
- 决定何时调 RAG / 工具
- 保持 MC 领域语感
- 识别越界并拒答
RAG / 工具 KB 负责
- 所有精确数字(血量 / 距离 / tick)
- 合成配方与材料
- 掉落表与概率
- 属性列表与机制细节
- 模组扩展(加 RAG 桶即可)
这条认知反过来重写了训练数据策略:训练数据不是为了灌输事实,而是让模型熟悉 MC 领域的中文表达, 并学会"基于给定资料回答"的范式。
3. 数据:四条支流 + 合成闸门
数据按用途分四类,原始素材是同一批,但流向不同管道:
| 支流 | 来源 | 规模 | 用途 |
|---|---|---|---|
| 预训练语料 | wiki 派生 / 开源中文 / 代码综合派生 | ~2B tokens(规划) | 风格 + 领域语感 |
| SFT 数据 | wiki QA + 拒答 + code QA | ~150K records | 对齐 + RAG 范式 |
| RAG 索引 | wiki / code / structured chunks | ~76K chunks | 推理时事实召回 |
| 工具 KB | jar/data/ 直接结构化 | 314 个数字常量 + 配方/掉落 | 精确事实查询 |
预训练语料构成(~2B tokens 规划)
教师模型:实际跑的是 DeepSeek V4 Pro,不是 Claude
原计划用 Claude Sonnet 4.6 做数据生成 + Haiku 做 verifier(写在 CLAUDE.md 里)。 实际跑下来是 DeepSeek V4 Pro + V4 Flash 两个 DeepSeek 模型——5 月 DeepSeek 2.5 折活动, 原价 ~¥10,776 的 token 消耗实付 ¥2,694,性价比压过 Claude。
| 角色 | 模型 | 具体工作 | 规模 |
|---|---|---|---|
| 数据生成 | DeepSeek V4 Pro(reasoning) | 给定 chunk 生成 (question, answer) 对,max_tokens 2500(小了 reasoning 会截断 JSON) | ~46K SFT QA 主力 + 815 PoC |
| Verifier | DeepSeek V4 Flash | 独立判别 chunk ⊨ answer,过滤生成结果 |
跟随每批生成走 |
| 评分裁判 | deepseek-chat | qa_120 每题 0-5 分 5 维评分(grounding / naturalness / etc.),concurrency 20 | v19 / v20.5 / 5b / 5c / G2 / 20.6 / 20.7 / Qwen 各模型 ≈ 1,000+ judge 调用 |
项目内部和 TRAINING_LOG 经常写成 "V4 Pro distill",严格说这不是蒸馏——
蒸馏特指 softmax / logit 层面的 soft label 传递(学生学老师的概率分布),
这里 V4 Pro 给的是 (q, a) 文本 hard label,更准确的说法是 "合成数据 SFT" / "教师生成数据 SFT"。
后者训练成本和效果上限都和蒸馏不一样。
用 DeepSeek 自己做 judge 的 self-bias 问题
既然数据生成器是 DeepSeek,评分裁判也是 DeepSeek,自然有 self-preference bias 的风险。PoC 阶段做过一次对比:
| 维度 | V4 Pro 自评 | Claude 人工抽审 20-30 题 | 差 |
|---|---|---|---|
| grounding(事实贴合度) | 4.90 / 5 | ~4.25 / 5(85%) | +13% inflated |
几个具体 case 是 V4 Pro 给自己 grounding=5 但实际事实错误——它对自己的幻觉无感知。 解决办法:最终 qa_120 eval 的 judge 不靠 V4 Pro 自评,改用 deepseek-chat(同 family 不同模型)+ 强制人工抽审校准。 不能解决 self-bias 但能压一截。这个 +13% bias 是后续看 qa_120 数字时要记得在脑子里打折的—— 报道里的 v20.7 = 1.91 / Qwen2.5-1.5B = 2.83 都带这个噪声,绝对值有偏但相对排名是稳的(4 个模型 judge 用同一套 prompt)。
合成数据的四道闸门
上述 ~150K SFT 样本几乎全部由 V4 Pro 严格 source-grounding 生成。这个量级若请人写完全不可能, 但 LLM 容易瞎编,所以每条数据流过四道闸门:
| 闸门 | 机制 | 过滤的问题类型 |
|---|---|---|
| 1 · Prompt 锁死 | 必须只基于提供的 chunk;强制 SKIP 出口 | 原文没说就不许补 |
| 2 · Citation 字符串匹配 | 引用必须能匹配回原文 | 引用编造 |
| 3 · 独立 Verifier | Haiku 二次判别 chunk ⊨ answer | 跨模型校验降低系统性偏差 |
| 4 · 数字白名单 | 所有数字必须在原文白名单里 | 1561 → 1500 这类典型幻觉 |
代码派生还有一道额外约束:派生 chunk 绝不嵌入原始 Java 代码片段——变量名、方法签名、运算符都不行 (合规边界)。LLM 抽取时 prompt 锁死,verifier 92.3% 通过率验证有效。
4. 训练:6×A800 上的 21 天
硬件实际配置(与规划的落差)
规划(写在 CLAUDE.md)
- 8 × A800-80GB 物理机
- NVLink Switch 互联
- 双 EPYC 9354 / 256 GB DDR5
- 1 TB + 4 TB NVMe
实际(云租 BlockElite)
- 6 × A800-40GB 虚拟机
- PHB(无 NVLink),all-reduce 走 PCIe
- 48 vCPU / 94 GB RAM 单 NUMA
- vdb 200G + 2T virtio-blk(非物理 NVMe)
这个落差影响了分布式策略选择——原本预期用 ZeRO-2 / ZeRO-3,实际 PHB 拓扑下 ZeRO 通信开销大于预期, DDP 反而更稳(deepspeed 在该机环境装配失败,最终全程 DDP + BF16)。具体分析放在下面 §分布式策略。
模型架构:自实现 Llama 风格 decoder-only
整个 transformer 栈自己撸了一遍——不依赖 HF transformers 的 LlamaForCausalLM,
目的是把每个组件的行为摸透。组件清单:
- Attention:MHA(无 GQA),
F.scaled_dot_product_attention自动派发到 Flash Attention 2 kernel - 位置编码:RoPE,θ=10000,max_position_embeddings=2048
- FFN:SwiGLU(
gate_proj×up_proj经 SiLU 后 →down_proj) - Normalization:RMSNorm(eps=1e-5),pre-norm
- Embedding:
tie_word_embeddings=True(输入 embedding 与 lm_head 共享权重) - Tokenizer:SentencePiece BPE 32K,自训中文+英文+代码混合语料
- 所有 Linear 无 bias(Llama 标准做法)
三档参数量配置(src/model/config.py):
| 档位 | vocab | hidden | layers | heads | head_dim | FFN | params |
|---|---|---|---|---|---|---|---|
| 200M(v1 起步) | 32,000 | 1,024 | 14 | 16 | 64 | 2,752 | ~200M |
| 500M(v20.4 主线) | 32,000 | 1,536 | 16 | 24 | 64 | 4,096 | ~500M |
| 1B(v19 路线) | 32,000 | 2,048 | 20 | 16 | 128 | 5,504 | ~1.08B |
全部 MHA(KV heads = Q heads,无 GQA),block size 2048,tied embeddings。v1 取 hidden 1024 / 14 层是端侧 150 MB Q4 体积的反推下限——再小语言流畅度肉眼变差。
训练超参(v20.4 pretrain 实测)
v20.4 是 from-0 路线的历史最强基线,21 天里跑的最重要一次 pretrain。完整配置:
| 启动 | 2026-05-21 08:27 UTC,torchrun --nproc_per_node=6,6×A800-40GB |
| 模型 | LlamaConfig.llama_500m() |
| 语料 | T1 mc_knowledge 6.31M × 40 + T2 派生 22.45M × 20 + T3 中文 1.46B × 3 = 5.09B tokens(MC 占比 13.77%) |
| Block size | 2048 |
| Batch | per-device micro 8 × grad_accum 4 × 6 GPU = 192 sequences/step |
| Tokens/step | 192 × 2048 = 393K tokens/step |
| Max steps | 13,400(13,400 × 393K ≈ 5.26B 名义,实际跑到 step 13,399) |
| Optimizer | AdamW (β₁=0.9, β₂=0.95),weight_decay 0.1 只加在 2D+ 参数上(bias / norm 不衰减),fused kernel,grad clip 1.0 |
| LR schedule | 3e-4 cosine 衰减到 min_lr 3e-5,warmup 2,000 steps |
| 精度 | BF16(A800 原生支持,无需 loss scaling) |
| Seed | 42 |
| Eval / Ckpt 间隔 | 500 / 1,000 steps |
35K tok/s/GPU 在 A800-40GB + 500M 模型 + bf16 + block 2048 上属于正常区间—— Flash Attention 2 已经压满 attention,瓶颈在 PCIe all-reduce(下节)。
分布式策略:6 卡 PHB 为什么选 DDP 而不是 ZeRO
规划阶段假设是 8×A800-80GB + NVLink Switch,可以舒服跑 ZeRO-2 / ZeRO-3 节省显存。实际拿到的机器
nvidia-smi topo -m 一查全是 PHB(PCIe Host Bridge)——
GPU 跨 CPU socket 通信,all-reduce 走 PCIe 4.0 + QPI/UPI,理论带宽 32 GB/s,实际有效 10-15 GB/s。
对比 NVLink Switch(450 GB/s 量级),整整差一个数量级。
| 方案 | 每 step 通信量 | 显存收益 | PHB 下的结论 |
|---|---|---|---|
| DDP(实际选) | 1 次 all-reduce ≈ 2× 模型 bf16 = ~1 GB(500M) | 无(每卡都全量 optimizer state) | 通信少 单次集合操作,ring-allreduce 易并行 |
| ZeRO-2 | reduce-scatter + all-gather,总量 ~2× DDP | optimizer state / gradient 分片到 6 卡 | 瓶颈放大 PCIe 下两阶段集合都吃带宽 |
| ZeRO-3 | 参数也分片,每 forward/backward 都要 gather | 极致省显存 | 不适用 500M 模型 40GB 显存够,省显存换通信亏 |
500M 模型 + Adam(fp32 状态 ≈ 8× params bytes = 4 GB)+ bf16 weight 1 GB + 梯度 1 GB ≈ ~6-8 GB/卡, A800-40GB 完全装得下,本来就不需要 ZeRO 省显存。再把通信开销 2× 就纯亏。
DeepSpeed 装配在该机器上失败过一次(依赖编译问题),加上上面分析,干脆全程 PyTorch 原生 DDP + NCCL backend。结果 v20.4 pretrain 6h57min / 211K tok/s 跑完,没出过一次 OOM 或 NCCL 死锁—— 工程上简单稳定 > 理论上最优。如果未来上 8 卡 NVLink 物理机,ZeRO-2 才有意义。
SFT 超参(v20.5 实测)
Pretrain 之后接 SFT,47K records(refusal 占 25.3%)。配置和 pretrain 差几个量级:
| 基座 | v20.4 pretrain final.pt(step 13,399) |
| 数据 | train 45,226 / val 2,364 / drop 4 records |
| Batch | per-device 8 × grad_accum 2 × 6 GPU = global 96 sequences |
| LR | 3e-5 cosine(比 pretrain 低 10×),warmup 5%,min_lr 3e-6 |
| Max steps | 1,200,patience=2 早停 |
| Eval / Ckpt | 100 / 200 steps |
实测早停在 step 900 触发,best.pt 落在 step 700(val loss 1.72):
| step | train | val | 备注 |
|---|---|---|---|
| 100 | 2.66 | 2.53 | 启动初期 |
| 500 | 1.70 | 1.82 | — |
| 700 | 1.75 | 1.72 | ← best.pt(val 最低) |
| 800 | 1.59 | 1.79 | val 反弹 +0.07 |
| 900 | 1.61 | 1.81 | val 二连升 → 触发早停 |
Wall clock 17 min,throughput 173K tok/s(比 pretrain 的 211K 略低因 SFT 多了 loss mask 计算)。 train-val gap 仅 −0.03,SFT 数据分布与 val 集高度一致,无过拟合—— 这是 v18-v19 时代未见过的好状态(v18h4b SFT 时代 train-val gap 通常 +0.4)。
Val loss 漂亮 ≠ qa_120 漂亮。v20.5 val 1.72 看着很好,实测 qa_120 跑出来 1.43(FAIL 4/5 gates,比 v19 退步 −0.39)。 后续 v20.5b 改 refusal、v20.5c 改 RAG source_priority、v20.6/v20.7 改数据组合, 一路拉到 v20.7 = 1.91。loss 和最终任务分数的关系比想象中弱——一定要看真实 eval 数字, val loss 只是一个"训练有没有炸"的工程指标,不是质量指标。
其他训练配置汇总
| 架构 | Llama 风格 decoder-only(自实现,详上) |
| 参数量 | 500M(v20.7 final SFT 基础) |
| Tokenizer | SentencePiece BPE 32K(自训中文) |
| 注意力 | Flash Attention 2(PyTorch 2.x SDPA 自动派发) |
| 精度 | BF16(无需 loss scaling) |
| 分布式 | 6 卡 DDP + 梯度累积(详上) |
| Pretrain tokens | 5.09B(v20.4) |
| SFT 数据 | v20_7_sft_pool 148K records |
| 量化产物 | GGUF Q4_K_M(344 MB)+ fp16 GGUF(1.1 GB) |
21 天的版本迭代节奏
5. 实测结果与四方对比
评估集 qa_120(12 类 × 10 题),DeepSeek deepseek-chat 0-5 分 judge。统一 RAG 配置(mc_kb 1 桶 / bge-small-zh / top-5)。
对比四个模型——两个项目自训 + 两个 Qwen 开源底座(zero-shot,无任何 fine-tune)。
满分 5.0 · 同条件 RAG · 数据来源:archive/v1-v21/TRAINING_LOG.md "3 方对比"
分类目详细对比(mc_kb 1 桶)
| 类目 | v19 (1B) | v20.7 (500M) | Qwen2.5-1.5B | Qwen vs v19 |
|---|---|---|---|---|
| advancement | 1.00 | 1.00 | 0.29 | -0.71 |
| biome | 2.40 | 2.20 | 3.60 | +1.20 |
| command | 2.10 | 1.90 | 2.90 | +0.80 |
| dimension | 1.00 | 2.25 | 4.50 | +3.50 |
| drops | 1.47 | 2.00 | 3.13 | +1.67 |
| enchant | 3.40 | 2.80 | 3.60 | +0.20 |
| entity | 2.13 | 3.20 | 3.93 | +1.80 |
| item | 1.20 | 2.40 | 3.80 | +2.60 |
| recipe | 0.80 | 0.95 | 2.30 | +1.50 |
| redstone | 1.80 | 1.50 | 3.60 | +1.80 |
| refusal | 3.78 | 1.33 | 0.67 | -3.11 |
| tutorial | 1.20 | 1.90 | 2.30 | +1.10 |
| OVERALL | 1.82 | 1.91 | 2.83 | +1.01 |
Qwen2.5-1.5B 没经过任何 MC 领域 fine-tune,仅靠通用中文能力 + mc_kb RAG。 10/12 类目超 v19,最大单类目差距 +3.50(dimension)。 项目自训模型唯一无可争议的优势是 refusal(v19 = 3.78,因为 SFT 训过拒答样本,Qwen 没训)。
从 0 训练的 ceiling 估计
v20.7 (1.91) 是从 v20.4 (1.78) 加 refusal SFT 拉上来的,但 refusal +1.67 的同时 in-scope 部分类目退步 -0.18 全局。 数学分析后估计——500M from-0 + 多源 SFT 的真实 ceiling 在 1.85-1.90, 接近 v19 (1.82) 但很难显著超越。
6. 两个值得记录的 bug
Bug 1 · Qwen3 fallback 路由错误(让一个数字白做了)
本来第四方对比模型是 Qwen3-0.6B(更新一代),最早实测 2.81。但同时观察到一个反常现象: Qwen3-0.6B 和 Qwen2.5-1.5B 推理速度几乎一样——参数差 2.5×,速度不该一样。
# serve/app.py line 367
backend = req.backend if req.backend in ("v19", "qwen") else "qwen"
# qwen3 不在合法列表 → fallback 到 "qwen"
# 实际跑的是 Qwen2.5-1.5B,输出 100% identical
原 mc-serve 加 qwen3 routing 时漏改 fallback 验证。后果:2.81 这个数字是 Qwen2.5-1.5B 重测, Qwen3-0.6B 的真实分数当时还不知道,需要在 mc-serve 切到全 HF fp16 后重测。
用户提出"反常观察"时,第一动作应该是 diff 实际输出(最低代价 sanity), 不要直接归因到"框架性能瓶颈"这种抽象解释。这次浪费了 1-2 轮诊断时间,"4 方对比"里的 Qwen3 数字也误入了训练日志。
Bug 2 · GGUF vs HF fp16 在自训模型上的输出差异
mc-serve 一开始 v20.7 用 GGUF + llama.cpp 跑,Qwen 系列用 HF + transformers 跑。同题"钻石剑的攻击伤害是多少?"实测:
| 推理路径 | 输出 | 判定 |
|---|---|---|
| v20.7 / GGUF fp16 / llama.cpp | "钻石剑的攻击伤害为 7 点..." | 正确 |
| v20.7 / HF fp16 / transformers | "20 点 / 基础 60 点 / 锋利 v 72 点" | 数字混乱 |
精度完全相同(都是 fp16),但 sampling/kernel 实现差异显著——GGUF 路径在自训模型上表现更稳。 这意味着部署框架选择不只是工程问题,会直接影响 eval 结果。后续为了和 Qwen 公平横向对比,统一切到 HF fp16, 但记得在结论里说明部署框架对自训模型的影响。
7. v22:路线切换到 Qwen 底座
实测出 1.91 vs 2.83 之后,用户判定:"模型架构 + 参数量本身是主瓶颈"—— from-0 路线见底,再投入也补不齐与开源大模型的差距。v22 起的主线锁定:
v1 ~ v21(已废弃)
- 6×A800 from-0 训练 200M-1B
- 自训 SentencePiece BPE 32K
- ~2B tokens 预训练(规划)
- 自写 Llama 模型代码
- 21 天 ¥8,773 → 1.91
v22 起(主线)
- Qwen 开源底座(0.5B ~ 1.7B 候选)
- Qwen BBPE 151K 词表
- LoRA SFT(按需,1-2 张 4090 即可)
- transformers / vLLM 生态
- 2.83 zero-shot 起步 → 加 LoRA 拼 3.0+
v22 候选评估范围(截至本文撰写时)
| 候选 | 参数 | fp16 体积 | 端侧 Q4 | Pretrain | 许可 |
|---|---|---|---|---|---|
| v19(baseline) | 1.08B | 2.2 GB | 344 MB | ~5B 自训 | CC BY-NC-SA |
| v20.7(baseline) | 0.5B | 1.1 GB | 344 MB | 5.09B 自训 | CC BY-NC-SA |
| Qwen2.5-0.5B | 0.5B | ~1.0 GB | ~350 MB | 18T | Apache 2.0 |
| Qwen2.5-1.5B | 1.54B | 3.0 GB | ~1.0 GB | 18T | Apache 2.0 |
| Qwen3-0.6B | 0.6B | 1.5 GB | ~400 MB | 36T | Apache 2.0 |
| Qwen3-1.7B | 1.7B | ~3.4 GB | ~1.1 GB | 36T | Apache 2.0 |
当前阶段在做 6 backend × qa_120 的完全公平横向对比(统一 HF fp16 + mc_kb 1 桶 RAG + 同生成参数 + 同评分方式)。 评估完后按 质量 45% / 成本 30% / 体验 25% 的决策矩阵拍最终选型, 再决定是否进 LoRA SFT、以及部署形态(纯端侧 / 自建云 / 混合)。
"加模组 = 加 RAG 桶(几小时上线)+ 可选 LoRA",不再"加模组 = 重训模型"。 模型负责风格、RAG 负责事实——这条核心认知没变,只是把"模型"换成了更强的开源底座。
8. 可复用到别处的经验
先把"模型负责什么、外部系统负责什么"想清楚。小模型时代尤其关键—— 把不该让模型干的事(精确事实、实时数据、多步计算)交给外部,模型只做它擅长的。
用脚本代替直觉做关键判断。早期直觉觉得"派生 122K 样本应该差不多了吧",
跑 audit_pretrain_tokens.py 一看:实测 15M tokens vs Chinchilla 下限 2B,差 130 倍。
数据量、机器环境、压缩前后召回——任何能量化的地方都不要靠感觉。
合成数据要有多道闸门。单纯 prompt 不够,需要 SKIP 出口 + 字符串校验 + 独立 verifier + 数字白名单。每多一道都能去掉一类系统性问题。
用户提"反常观察"时先做最小 sanity,不要直接归因到抽象解释。 Qwen3 fallback bug 那次,"速度异常"本应 30 秒 diff output 验证,结果先去解释"框架瓶颈"。教训。
给产物加 meta,不给等于裸奔。每个数据集 / ckpt / 索引 / eval 报告都附 git commit + seed + data sha256 + tokenizer hash。跨 ckpt 比较前先验 hash 是否一致基线,不一致就标"非同基线对比"。
把"为什么这么决定"写下来,跟代码同等重要。否决方案、踩过的坑、改方向的转折点—— 这些信息几个月后没人记得,但下次踩坑前最值得读。代码讲 What,commit 讲 When, 只有专门的决议日志能讲 Why。
该承认主瓶颈时承认。投入 ¥8,773 + 21 天 + 21 个版本后,最理性的动作是承认 "模型 + 参数量本身是瓶颈",切到 Qwen 底座,而不是继续 fine-tune 自训模型。 沉没成本不是论据,决策矩阵才是。
9. 附录:成本与产出汇总
v1-v21 总账
| 项 | 金额 | 占比 | 说明 |
|---|---|---|---|
| 训练机器(6×A800-40GB 云租) | ¥6,085 | 69.4% | 约 ¥290/天 × 21 天 |
| DeepSeek API(V4 Pro + V4 Flash) | ¥2,694 | 30.7% | 5 月 2.5 折后实付 |
| 总计 | ¥8,773 | 100% | 2026-05-01 ~ 2026-05-21 |
产出清单
| 类目 | 产出 |
|---|---|
| 训练完成的模型版本 | 4 个(v20.4 / v20.5 系列 / G2 1B / v20.6 / v20.7) |
| Pretrain ckpt | v20.4 500M, 5.09B tokens, 6.95h |
| SFT ckpt | 6+ 个(v20.5 / 5b / 5c / G2 / 20.6 / 20.7) |
| 数据池 | 145K-148K records(v20.6/v20.7 SFT pool) |
| GGUF 量化 | fp16 1.1 GB + Q4_K_M 344 MB |
| 部署 | mc-serve 4 backend(v20.7 / v19 / Qwen2.5-1.5B / Qwen3-0.6B) |
| 方法论沉淀 | 数据 pipeline / SFT 数据组合 / 训前 gate / 可重现性元规则 |
事后 ROI 评估
v20 路径 ¥8,773 投入 → eval 1.91(接近 v19 1.82)。
同步发现 Qwen2.5-1.5B zero-shot + RAG = 2.83(¥0 成本,仅 3 GB 下载)。
同条件 RAG 下,自训 -0.92 分。
但产出价值不仅在 eval 分数:
- 完整 from-0 pretrain + SFT + 量化 + 部署闭环(学习目的达成)
- 数据 pipeline + SFT 数据组合方法论沉淀(v22 起 ~150K SFT 可直接复用)
- RAG 索引 + mc-serve 多 backend 测试床(v22 评估直接利用)
- 项目级 lesson 沉淀(pretrain × SFT 必须同源 / refusal 比例 10-15% / 训前 gate 制度等)
这个项目还在进行中。v22 6 backend 完全公平对比 + LoRA SFT 选型决策矩阵即将完成,
完整决议日志、各 spec 实测数据都在仓库里(CLAUDE.md / DECISIONS.md /
V22.md / TRAINING_LOG.md 四份分层文档)。
如果你也在做类似的小模型 + RAG 项目,希望本文里的方法论部分对你有用: 明确分工、流程 gate、合成数据多闸门、可重现性元规则、该 pivot 时果断 pivot—— 这些不绑定具体技术栈,换个领域照样能用。