Files
aha/dev_rule.md
T

14 KiB
Raw Blame History

AHA 开发规范

本文档不是通用 Rust 模板,而是基于当前 aha 仓库真实代码结构整理出的项目级规范。目标有两个:

  1. 统一现有模型目录的职责分层。
  2. 为未来新增原生模型、GGUF 模型、ONNX 模型提供可执行的接入标准。

1. 当前项目真实结构

1.1 顶层目录职责

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_modelModelInstance 能力分派。
  3. src/exec/<name>.rs 接入 aha run -m <model> 的直调能力。
  4. src/exec/mod.rs 导出 exec 模块。
  5. src/main.rsWhichModel 路由到具体 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>/ 的最低结构如下:

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.rsminicpm4.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.rssrc/exec/*tests/* 使用。
  • 目录重构时优先保持公开方法不变,例如 initgenerategenerate_streamembedrerank

3. 按能力划分的目录模板

3.1 文本生成模型

适用:

  • Qwen3
  • Qwen3.5
  • MiniCPM4

模板:

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

模板:

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
  • 后续任意检索相关模型

模板:

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

模板:

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_deviceget_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 的模型,推荐结构:

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_asrqwen3_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 校验命令

最少校验:

cargo fmt --check
cargo check

建议校验:

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.rssrc/exec/*、测试里的调用方式不变

如果必须改公开接口,必须同步修改:

  • src/models/mod.rs
  • src/exec/*
  • src/main.rs
  • src/api/mod.rs
  • tests/*
  • 相关文档

9. 当前仓库的落地结论

根据当前仓库实际情况,后续新增模型必须遵循以下结论:

  1. qwen3_embeddingqwen3_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 原生文本模型

src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
└── generate.rs

10.2 原生多模态模型

src/models/<model>/
├── mod.rs
├── config.rs
├── model.rs
├── generate.rs
└── processor.rs

10.3 多格式检索模型

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 五条链路中”,并且目录职责必须与现有标准结构保持一致。