缓存时间:
2026/10/05 17:31
# 教一个 1.5B 模型生成 bash 命令,用 40 万条合成样本达到 GPT-4o 水平——Dirac
来源:https://dirac.run/posts/easycommand
## 起因
虽然大部分编码工作都交给 LLM 完成,但有一件事我总要靠 Google 搜索:Bash 命令。工作被打断、打开 Google、输入完整查询、跳到 Stack Overflow 之类的网站、再查找我想要的语法——这个过程总是很破坏心流。
直觉上,我一直觉得小模型很适合做这件事,因为命令集合是有限的、语法是明确定义的、训练数据也容易生成。于是有一天我决定亲自验证一下。在整个实验过程中,我尝试了六个不同的模型:SmolLM 135M、SmolLM 360M、Qwen3-0.6B、Qwen2.5-Coder-1.5B、Qwen3.5-0.8B 和 Qwen3.5-2B。0.6B 和 1.5B 的模型表现最好,所以这些检查点被保留了下来。
## 你可能想要的东西
我在 Hacker News 上提到了这个实验(https://news.ycombinator.com/item?id=49869735),大家要求我提供模型、训练数据和完整说明。
- 微调数据集(https://huggingface.co/datasets/dirac-run/ec-training-data):401,975 条不重复的英文请求/Bash 命令配对。
- 模型:1.5B GGUF(https://huggingface.co/dirac-run/ec-1.5b-gguf)和 0.6B GGUF(https://huggingface.co/dirac-run/ec-0.6b-gguf)。
- 命令行工具(https://github.com/dirac-run/ec)。
原本只是想做一个语法速查工具,最后变成了一场有趣的实验:教小模型做事——并验证它们是否真的变强了。
## Astra 运行了整个训练循环
当我说我"训练"了这些模型,其实指的是 Astra 是训练控制器:不同的 LLM 负责生成合成数据集,另一组 agent 负责审核并将数据行纳入其中。Astra 负责训练和评估流程;我则提供目标、约束条件和方向修正。
起点是在一个独立的对话中想出一类命令类别,然后搭建一个生成和验证这些命令的流程。最初大约有 3 万条样本。之后的循环很简单:训练、评估(我让 Astra 创建了一些基准)、分析、生成更多数据、思考不同的训练方法、然后进行下一轮训练。整个过程大约花了 10 天,大部分时间都是模型各自在做自己的任务:生成、审核、评估。
Prompt 大致是这样(以及其他内容):
> 我们只关心英文到 Bash 命令的转换,其他都不关心。只要在这个领域有提升,我们愿意牺牲其他任何领域的能力。我们想要人类历史上最好的英文转 Bash 模型,不管需要做什么都要做到。
我提供的唯一真正方向是:在脑中跟踪多个训练检查点、提示模型在特定类别下比较它们、要求模型调查为什么某个检查点在一个领域的提升换来了另一个领域的退步,等等。
发布的模型使用 LoRA 进行有监督微调,从 Qwen3-0.6B 和 Qwen2.5-Coder-1.5B-Instruct 出发。令人惊讶的是,4-bit 版本保留了 16-bit 版本的大部分提升,而且在 CPU 系统上运行很方便。
## 更多数据并不总是答案
发布的数据集包含 **401,975 条不重复的请求/回答配对**,涵盖文件操作、文本处理、Git、压缩包、网络、引号转义以及组合命令。一条数据看起来是这样的:
```json
{"request":"list files in this directory","response":{"kind":"COMMAND","value":"ls"}}
```
这些样本是合成生成的,经过审核,并在可行的情况下通过执行进行校验。并非每一行都被单独执行过。同一命令的不同描述被有意保留。
有一个实验非常清楚地展示了"学习样例"和"学习行为"之间的区别。一个 0.6B 模型在与时间相关的训练请求上从 **8/72 提升到了 64/72**。但在独立的时间迁移请求上,它仍然只有 **1/18**。与此同时,常规命令的表现从 **70/102 下降到了 60/102**。
模型学会的是提供的答案本身。这和可靠地在其他地方运用它们并不是一回事。
有针对性的修复又带来了另一个问题:保持率。一个后续的 1.5B 检查点在 ALFA 更新版本上比我发布的模型多通过了 5 个用例,但在内部测试套件上丢失了 63 个通过项。我保留了更均衡的检查点。导出过程也会改变检查点的排名,因此评估实际的量化文件就成了选择流程的一部分,而不是事后补充。
详细结果(https://github.com/dirac-run/ec/blob/main/docs/RESULTS.md)保留了这些对比,包括那些没有帮助的实验。
## 我最终也调试了基准测试
我使用了 ALFA(一个包含 300 个任务的 shell 基准)以及内部执行测试。调查其中的分歧既发现了模型的错误,也发现了评估器的问题:参数传递、测试夹具的重置、参考命令,以及那些可能接受错误输出或拒绝正确替代方案的检查。
这项工作催生了 ALFA-updated(https://github.com/dirac-run/ALFA-updated)。它保留了原有的任务集,但记录了修复后的环境和评分规则。这是一个独立的基准定义,而不是对上游 ALFA 的静默替换。
### 基准对比
下表结合了我们的本地结果(https://github.com/dirac-run/ec/blob/main/docs/RESULTS.md)、whatisit 项目报告的分数(https://github.com/ThorOdinson246/whatisit-nl2sh)以及 barbarabhb 模型卡上的分数(https://huggingface.co/barbarabhb/nl2sh-qwen25-coder-1.5b-GGUF)。**ALFA-updated?** 一列标明使用的基准版本:Yes 表示我们修复后的版本,No 表示原始 ALFA。whatisit 出现了两次:它公布的原始 ALFA 分数是 **62.0%**,而我们在 ALFA 更新版本上测得 **63.7%**——报告分数提高了 1.7 个百分点。
| 模型 / 配置 | 大小 | 通过率 | ALFA-updated? | 报告来源 |
|---|---|---|---|---|
| GPT-4o(已发布) | Cloud API | 73.0% | No | ALFA 作者 |
| **EasyCommand 1.5B, Q4_K_M** | 940.4 MiB | 212/300 — 70.7% | Yes | 我们的本地运行 |
| nl2sh-3b | 1.9 GB | 65.7% | No | whatisit |
| barbarabhb / nl2sh-qwen25-coder-1.5b, Q4_K_M | 941 MB | 65.67% | No | barbarabhb |
| barbarabhb / nl2sh-qwen25-coder-1.5b, Q4_K_M + imatrix | 941 MB | 65.00% | No | barbarabhb |
| barbarabhb / nl2sh-qwen25-coder-1.5b, Q6_K | 1.2 GB | 64.33% | No | barbarabhb |
| barbarabhb / nl2sh-qwen25-coder-1.5b, Q8_0 | 1.6 GB | 63.67% | No | barbarabhb |
| whatisit / nl2sh-1.5b (https://huggingface.co/ThorOdinson246/nl2sh-1.5b-Q4_K_M), Q4_K_M | 941 MB(报告值) | 191/300 — 63.7% | Yes | 我们的本地运行 |
| whatisit / nl2sh-1.5b, Q4_K_M | 941 MB | 62.0% | No | whatisit |
| Qwen2.5-Coder-7B, untuned | 4.4 GB | 61.3% | No | whatisit |
| EasyCommand 0.6B, Q8_0 | 767.5 MiB | 174/300 — 58.0% | Yes | 我们的本地运行 |
| EasyCommand 0.6B, Q4_K_M | 461.8 MiB | 165/300 — 55.0% | Yes | 我们的本地运行 |
| Qwen2.5-Coder-1.5B, untuned | 941 MB | 54.0% | No | whatisit |
在 ALFA-updated 上**比 whatisit 多通过了 21 个(+7 个百分点)**,各自使用自己的设置(EC 限制 256 token,whatisit 限制 64 token)。
一个专精模型达到了 217/300(72.3%)的峰值,但在内部测试中丢失了 63 个通过项。我发布了更均衡的检查点。
这些测试用于指导开发,而不是作为独立的留出测试。GPT-4o 公布的 73–74% 分数(https://aclanthology.org/2025.naacl-long.555.pdf)使用的是原始 ALFA;类似的分数并不能证明两者持平。完整的方法论和运行细节在评估笔记中(https://github.com/dirac-run/ec/blob/main/docs/RESULTS.md)。
## 试用它,或者继续训练
如果你想在本地运行,ec(https://github.com/dirac-run/ec)内嵌了 llama.cpp,提供命令预览和交互式执行。建议先使用 `--preview`;一条看起来合理的命令仍然可能是错的。目标平台是 GNU/Linux Bash,不是所有 shell 和平台都支持。
如果你想动手实验,可复用的部分有:
- **训练数据(https://huggingface.co/datasets/dirac-run/ec-training-data):**完整的已发布去重语料,不只是样本。
- **GGUF:**0.6B(https://huggingface.co/dirac-run/ec-0.6b-gguf)和 1.5B(https://huggingface.co/dirac-run/ec-1.5b-gguf)。
- **可训练权重:**0.6B(https://huggingface.co/dirac-run/ec-0.6b)和 1.5B(https://huggingface.co/dirac-run/ec-1.5b),包括合并后的 BF16 检查点和原始 LoRA adapter。
- **训练笔记(https://github.com/dirac-run/ec/blob/main/docs/TRAINING.md)**和**基准代码(https://github.com/dirac-run/ALFA-updated)**。
模型和数据采用 Apache-2.0 许可;应用和基准代码采用 MIT 许可。扁平化的数据集无法复现历史加权方式,也没有官方测试集划分。如果你使用它进行训练,请按任务族划分留出集,而不是随机划分改写句子。
我很想看看其他人能改进什么:更好的覆盖率、更可靠的迁移能力,或者能暴露我遗漏之处的新测试。这一切始于一个想要命令速查工具的想法。分享这些材料似乎是继续学习的好方式。