Files
aha/dev_rule.md
T

548 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AHA 开发规范
本文档不是通用 Rust 模板,而是基于当前 `aha` 仓库真实代码结构整理出的项目级规范。目标有两个:
1. 统一现有模型目录的职责分层。
2. 为未来新增原生模型、GGUF 模型、ONNX 模型提供可执行的接入标准。
---
## 1. 当前项目真实结构
### 1.1 顶层目录职责
```text
aha/
├── src/
│ ├── main.rs # CLI 与服务启动入口
│ ├── lib.rs # 对外库导出
│ ├── api/ # HTTP/OpenAI 兼容接口
│ ├── exec/ # `aha run` 的模型直调入口
│ ├── models/ # 模型实现与模型工厂
│ ├── tokenizer/ # tokenizer 封装
│ ├── chat_template/ # chat template 封装
│ ├── position_embed/ # 位置编码
│ ├── process.rs # 服务进程管理
│ └── utils/ # 下载、设备、dtype、文件查找等通用工具
├── tests/ # 集成测试与格式测试
├── docs/ # 面向用户的文档
└── dev_rule.md # 本规范
```
### 1.2 模型接入链路
新增一个模型,必须理解下面这条链路,而不是只加 `src/models/<name>/`
1. `src/models/<name>/`
实现模型本体、配置、推理入口。
2. `src/models/mod.rs`
注册模块、枚举 `WhichModel`、模型元信息、模型工厂 `load_model``ModelInstance` 能力分派。
3. `src/exec/<name>.rs`
接入 `aha run -m <model>` 的直调能力。
4. `src/exec/mod.rs`
导出 exec 模块。
5. `src/main.rs`
`WhichModel` 路由到具体 exec 实现;必要时处理 GGUF / mmproj / ONNX 的路径参数。
6. `src/api/mod.rs`
若模型属于 `chat` / `embedding` / `rerank` / `asr` / `ocr` 等服务能力,需要保证 `ModelInstance` 对应方法可用。
7. `tests/`
至少补齐加载测试和最小推理测试。
8. `docs/``README*``changelog`
更新用户可见说明。
---
## 2. 模型目录统一规范
### 2.1 最低标准
从现在开始,`src/models/<model_name>/` 的最低结构如下:
```text
src/models/<model_name>/
├── mod.rs
├── config.rs
├── model.rs
└── generate.rs
```
说明:
- `mod.rs` 必须只做模块导出,不写业务逻辑。
- `config.rs` 负责配置结构体、默认值、配置文件加载辅助。
- `model.rs` 负责底层网络结构、后端封装、权重加载、核心推理逻辑。
- `generate.rs` 负责对外可调用入口。
### 2.2 可选文件
满足以下条件时再新增文件,不要随意拆目录:
- `processor.rs`
仅用于多模态输入预处理或复杂后处理,例如图像、音频、视频、OCR patch 处理。
- `tokenizer.rs`
仅当模型目录需要自定义 tokenizer 适配,且通用 `TokenizerModel` 不够用。
- `audio_vae.rs``minicpm4.rs` 等附属文件
仅当模型实现包含稳定、独立的子模块时允许拆出。
### 2.3 各文件职责边界
#### `mod.rs`
只允许:
- `pub mod ...`
- 必要时 `pub use ...`
不允许:
- 业务实现
- 大段常量
- 初始化逻辑
#### `config.rs`
负责:
- `config.json`
- `generation_config.json`
- `preprocessor_config.json`
- 与格式相关但不属于运行态的配置映射
不负责:
- 真正加载权重
- 调用 tokenizer
- 调用 Candle forward
#### `model.rs`
负责:
- Candle 模型结构体
- 权重加载
- 格式后端封装
- 核心 `forward` / `embed` / `rerank` / `transcribe` / `process_image` 等内部能力
不负责:
- CLI 参数解析
- HTTP 请求结构
- `aha_openai_dive` 响应组装
#### `generate.rs`
负责:
- 作为当前模型目录的对外入口类型
- 将 tokenizer、processor、model backend 串起来
- 实现 `GenerateModel` trait,或提供该模型暴露给 `ModelInstance` 的统一入口
约束:
- 对外入口类型名必须稳定,供 `src/models/mod.rs``src/exec/*``tests/*` 使用。
- 目录重构时优先保持公开方法不变,例如 `init``generate``generate_stream``embed``rerank`
---
## 3. 按能力划分的目录模板
### 3.1 文本生成模型
适用:
- Qwen3
- Qwen3.5
- MiniCPM4
模板:
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
└── generate.rs
```
规则:
- `generate.rs` 负责 `GenerateModel` 实现。
- `model.rs` 负责 `forward`、KV cache、权重加载。
### 3.2 多模态模型
适用:
- Qwen3VL
- Qwen2.5VL
- Qwen3ASR
- DeepSeek-OCR
- Hunyuan-OCR
- PaddleOCR-VL
模板:
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
├── generate.rs
└── processor.rs
```
规则:
- `processor.rs` 负责把请求输入变成模型可消费的 tensor 或替换文本。
- `generate.rs` 只做流程编排,不堆复杂前处理。
### 3.3 Embedding / Reranker 模型
适用:
- Qwen3-Embedding
- Qwen3-Reranker
- 后续任意检索相关模型
模板:
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
└── generate.rs
```
规则:
- `model.rs` 负责 embedding backend 或 rerank backend。
- `generate.rs` 提供稳定对外入口,供 `ModelInstance::embedding` / `ModelInstance::rerank` 调用。
- 公共检索算法优先放在 `src/models/common/retrieval.rs`,不要在各模型目录重复实现。
### 3.4 纯图像或工具型模型
适用:
- RMBG2.0
模板:
```text
src/models/<model>/
├── mod.rs
├── model.rs
└── generate.rs
```
补充要求:
- 如果模型已经存在稳定配置文件,仍然建议补 `config.rs`
- 新增此类模型时,不要因为现有个别旧目录较薄,就继续复制旧写法。
---
## 4. 多格式模型规范
### 4.1 格式定义
当前项目已经在 `src/models/mod.rs` 中抽象出:
- `Safetensors`
- `Gguf`
- `Onnx`
新增模型时,必须先明确自己属于哪一种格式,不能等实现完成后再补枚举。
### 4.2 格式接入原则
#### 原生 / Safetensors 模型
规则:
- 默认走项目当前主路径。
- 权重查找统一使用 `find_type_files(path, "safetensors")`
- 设备与 dtype 统一通过 `get_device``get_dtype` 获取。
- 能被自动下载的模型,必须在 `WhichModel::is_download_managed()` 语义下成立。
#### GGUF 模型
规则:
- 必须在 `WhichModel::artifact_format()` 返回 `ModelArtifactFormat::Gguf`
- `main.rs` 中必须要求 `--gguf-path`,多模态 GGUF 额外支持 `--mmproj-path`
- tokenizer 若从 GGUF metadata 构建,必须封装在 `model.rs` 或专门 backend 中,不允许散落在 exec 或 main。
- GGUF 的 mmproj 仅属于后端实现细节,不应污染上层 API 类型。
#### ONNX 模型
规则:
- 必须在 `WhichModel::artifact_format()` 返回 `ModelArtifactFormat::Onnx`
- 若运行时尚未集成 ONNX 推理,仍然要保证:
- 模型枚举和格式分类准确;
- 测试能够验证 ONNX 文件可发现、可读取、可创建 session;
- 错误提示明确说明“格式已识别,但 runtime 未集成”。
- ONNX Runtime 的 session 创建、输入输出张量映射必须收敛到模型目录内部,不允许堆到测试文件里作为“事实实现”。
### 4.3 多格式目录组织建议
对未来一个同时支持 `safetensors + gguf + onnx` 的模型,推荐结构:
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
├── generate.rs
├── backend_safetensors.rs # 当实现明显变大时再拆
├── backend_gguf.rs
└── backend_onnx.rs
```
约束:
- 如果格式实现还很轻,先放在 `model.rs` 的私有 backend 结构体里。
- 只有当 `model.rs` 已明显过大,才允许拆 `backend_*` 文件。
- 不允许一开始就为了“看起来完整”拆很多空文件。
### 4.4 多格式统一入口要求
对于同一模型家族,不管底层格式是什么,对外入口必须统一:
- 同一个 `WhichModel` 语义只表达“模型变体”,不表达内部临时实现。
- `generate.rs` 的公开类型应稳定。
- `src/models/mod.rs::load_model()` 负责根据 `WhichModel` 选择正确初始化路径。
---
## 5. 命名与导出规范
### 5.1 目录命名
- 目录名统一使用小写蛇形命名。
- 与上游模型 ID 不一致时,以项目内部统一命名为准,例如 `qwen3_asr``qwen3_embedding`
### 5.2 类型命名
- 配置类型:`<ModelName>Config`
- 生成配置类型:`<ModelName>GenerationConfig`
- 底层模型:`<ModelName>Model``<ModelName>Backend`
- 对外入口:`<ModelName>GenerateModel``<ModelName>EmbeddingModel``<ModelName>RerankerModel`
- 处理器:`<ModelName>Processor`
### 5.3 `mod.rs` 命名要求
必须导出到以下位置:
- `src/models/mod.rs`
- `src/exec/mod.rs`
禁止:
- 同一个模型目录里同时存在多个对外主入口但没有清晰职责说明。
---
## 6. 新增模型的强制清单
### 6.1 模型目录
必须完成:
1. 创建 `src/models/<model>/`
2. 按模板补齐最少文件
3. 保持职责边界清晰
### 6.2 模型注册
必须同时修改 `src/models/mod.rs`
1. `pub mod <model>;`
2. `WhichModel` 新增枚举项
3. `LISTED_MODELS` 增加公开模型
4. `artifact_format()`
5. `openai_model_id()`
6. `owner()`
7. `model_id()`
8. `model_type()`
9. `ModelInstance` 增加实例变体
10. `load_model()` 增加加载分支
### 6.3 CLI 接入
必须同时修改:
1. `src/exec/<model>.rs`
2. `src/exec/mod.rs`
3. `src/main.rs`
要求:
- `aha run` 必须能走到该模型。
- GGUF / ONNX 如有特殊路径参数,必须在 `main.rs` 路由时处理。
### 6.4 API 接入
当模型支持服务能力时,必须确保:
- `chat` 类型实现 `GenerateModel`
- `embedding` 类型实现 `ModelInstance::embedding`
- `rerank` 类型实现 `ModelInstance::rerank`
- `asr` / `ocr` / `image` 类型能复用现有 API 行为
### 6.5 文档接入
至少更新:
- `README.md`
- `README.zh-CN.md`
- `docs/supported-models.md`
- `docs/supported-models.zh-CN.md`
- `docs/changelog.md`
- `docs/changelog.zh-CN.md`
如果 CLI 或 API 行为变更,还要更新:
- `docs/cli*.md`
- `docs/api*.md`
---
## 7. 测试规范
### 7.1 每个新模型至少要有的测试
1. 加载测试
验证模型文件能被发现并初始化。
2. 最小推理测试
验证 `generate` / `embed` / `rerank` / `transcribe` / `remove_background` 至少能跑通一次。
3. 错误输入测试
验证空输入、非法输入、缺失文件路径能返回明确错误。
### 7.2 多格式模型测试矩阵
对于 `safetensors + gguf + onnx` 多格式模型,建议至少覆盖:
1. safetensors 加载测试
2. gguf 文件发现与 tokenizer/session/backend 加载测试
3. onnx 文件发现测试
4. onnxruntime session 创建测试
5. 至少一种真实输入的结果合理性测试
### 7.3 测试文件命名
- 单模型:`tests/test_<model>.rs`
- 多格式专项:`tests/test_<model>_multi_format.rs`
### 7.4 校验命令
最少校验:
```bash
cargo fmt --check
cargo check
```
建议校验:
```bash
cargo test --test <target_test>
```
注意:
- 大模型测试通常依赖本地权重,不要求在所有环境全量跑通。
- 但编译检查必须通过。
---
## 8. 代码风格约束
### 8.1 不允许的做法
-`mod.rs` 堆业务逻辑
-`exec` 中写模型核心推理代码
-`main.rs` 中写模型权重加载细节
- 在测试文件里藏正式实现逻辑
- 为了“结构完整”创建一堆空模块
- 复制已有模型代码后只改字符串,不抽象公共逻辑
### 8.2 推荐做法
- 公共检索逻辑放 `src/models/common/retrieval.rs`
- 公共 GGUF 工具放 `src/models/common/gguf.rs`
- 设备、dtype、权重文件发现统一走 `src/utils/mod.rs`
- 公开方法保持小而稳定,内部再分层
### 8.3 兼容性原则
当重构模型目录时:
- 优先保持对外类型名不变
- 优先保持 `init(...)` 签名不变
- 优先保持 `src/models/mod.rs``src/exec/*`、测试里的调用方式不变
如果必须改公开接口,必须同步修改:
- `src/models/mod.rs`
- `src/exec/*`
- `src/main.rs`
- `src/api/mod.rs`
- `tests/*`
- 相关文档
---
## 9. 当前仓库的落地结论
根据当前仓库实际情况,后续新增模型必须遵循以下结论:
1. `qwen3_embedding``qwen3_reranker` 现在也必须按 `config.rs + model.rs + generate.rs + mod.rs` 维护。
2. 未来新增 Embedding/Reranker 模型,不允许再只保留 `mod.rs + generate.rs`
3. `docs/development.md` 当前更接近通用模板,不应作为新增模型时的唯一依据;以本文件和实际代码结构为准。
4. ONNX 目前在仓库里已经有测试与格式识别语义,但运行时集成尚未完整落地;新增 ONNX 模型时,必须同时补 runtime 设计,而不是只补测试。
5. GGUF 模型接入必须从一开始就考虑 `main.rs` 路径参数、`WhichModel::artifact_format()``load_model()` 的完整链路。
---
## 10. 新增模型模板清单
### 10.1 原生文本模型
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
└── generate.rs
```
### 10.2 原生多模态模型
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
├── generate.rs
└── processor.rs
```
### 10.3 多格式检索模型
```text
src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
├── generate.rs
├── backend_safetensors.rs # 可选
├── backend_gguf.rs # 可选
└── backend_onnx.rs # 可选
```
最终原则只有一句:
新增模型不是“把代码放进一个目录”,而是“把模型完整接入到 models factory、CLI、API、tests、docs 五条链路中”,并且目录职责必须与现有标准结构保持一致。