从零构建 DeepClassify:一个本地代码工程智能管理 Agent
从零构建 DeepClassify:一个本地代码工程智能管理 Agent
拖入代码工程 → 自动扫描 → AI 分类 → 生成文档 → RAG 语义搜索。
全程本地运行,无需联网,你的代码不离开你的机器。

github链接:https://github.com/lsy511640489/DeepClassify
演示视频链接:https://www.bilibili.com/video/BV1SVjm6oE4b/
缘起:为什么我需要这个工具
作为一个同时在维护 30+ 个代码仓库的开发者,我面临一个普遍的困境:
- 接手同事的项目,看不懂这是干什么的
- 三个月前自己从github上拉的代码忘记这是做啥的
- 硬盘里散落着各种嵌入式工程、Web 项目、AI 实验,根本分不清
现有的方案要么是云端的(代码上传到第三方),要么是纯手动的(写 README 靠自觉)。我想要的很简单:
把代码文件夹拖进去,自动告诉我是干什么的,然后能随时搜索找到。
于是有了 DeepClassify。
设计哲学:三个核心决策
1. 本地优先,不是口号
所有数据 —— SQLite 数据库、ChromaDB 向量索引、生成的 Markdown 文档 —— 全部存在你的硬盘上。LLM 通过调用本地的 Ollama 或你自己的 DeepSeek API Key 完成,代码工程一行都不会上传到第三方服务器。
这意味着:
- 内网环境也能用
- 没有 API 费用(用 Ollama 本地模型时)
- 工程代码绝对隐私
2. AI 主导分类,规则兜底
传统的代码分类工具依赖正则匹配:检测到 .ioc 文件 → STM32 工程,检测到 package.json → 前端项目。
这有两个致命问题:
- 覆盖盲区:新框架、自研框架、混合工程无法识别
- 误判:一个引用了 STM32 HAL 库的 PC 仿真器会被误判为嵌入式工程
我的做法是反过来:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Stage 1 │────▶│ Stage 2 │────▶│ Stage 3 │
│ 规则预筛选 │ │ ★ AI 分类 │ │ 规则校验 │
│ 提取硬信号 │ │ LLM 读源码 │ │ 交叉验证 │
│ 供 AI 参考 │ │ 自主判断 │ │ 矛盾降置信度 │
└──────────────┘ └──────────────┘ └──────────────┘
让 AI 读完关键源文件后自己决定 “这个工程是什么”,规则只在 AI 不可用时兜底,以及在 AI 给出结果后做一致性验证。
3. 事件驱动的管道架构
整个处理流程被建模为一条 7 步管道:
拖入工程 → 创建记录 → 扫描指纹 → AI分类 → 生成摘要 → 渲染文档 → 向量索引
│ │
└────────── 每一步通过 EventBus 发出事件 → WebSocket 实时推送 ───────┘
前端能实时看到进度:“正在扫描文件结构…” → “AI 正在分析工程类型…” → “正在生成文档…” → “完成!”
技术全景
整体架构
┌──────────────────────┐ ┌──────────────────────┐
│ Desktop UI │ │ Web UI │
│ Avalonia .NET 10 │ │ React 19 + Vite 8 │
│ MVVM + Fluent │ │ Tailwind 4 + Zustand│
└──────────┬───────────┘ └──────────┬───────────┘
│ HTTP REST │
│ 127.0.0.1:8765/api/v1 │
└──────────┬───────────────┘
│
┌──────────▼───────────┐
│ FastAPI Backend │
│ Python 3.12+ │
│ │
│ ┌────────────────┐ │
│ │ EventBus │ │
│ └───────┬────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Pipeline │ │
│ │ Scan→Classify │ │
│ │ →Doc→Index │ │
│ └───────┬────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ RAG Engine │ │
│ │ Hybrid Search │ │
│ └────────────────┘ │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Data Layer │
│ SQLite + ChromaDB │
│ + File System │
└──────────────────────┘
技术栈速览
| 层 | 技术 | 选型理由 |
|---|---|---|
| Desktop UI | Avalonia 12 + .NET 10 + CommunityToolkit.Mvvm | 跨平台桌面框架,MVVM 源码生成器消除样板代码 |
| Web UI | React 19 + TypeScript 6 + Tailwind 4 + Zustand | 轻量、类型安全、原子化 CSS |
| Backend | FastAPI + SQLAlchemy + aiosqlite + ChromaDB | 异步 Python、本地向量存储 |
| Embedding | sentence-transformers (all-MiniLM-L6-v2) | 384 维,仅 80MB,离线运行 |
| LLM | DeepSeek / Ollama / Claude 三 Provider 可切换 | 灵活适配不同场景 |
| Packaging | PyInstaller + .NET Publish | 后端打包为单一 exe,桌面端自包含发布 |
核心模块拆解
1. 工程指纹扫描器 — 不只是数文件
扫描器 (src/scanner/) 生成一个包含 15 个字段的 ProjectFingerprint:
fingerprint:
file_count: 142
total_size_mb: 3.2
languages: { C: 65%, Python: 20%, CMake: 10%, Assembly: 5% }
frameworks: [STM32 HAL, FreeRTOS, CMSIS-DSP]
build_systems: [CMake, Makefile]
dependencies: [arm-none-eabi-gcc, stm32cube-f4]
key_files: [main.c, stm32f4xx_it.c, motor_control.c]
key_dirs: [Core/, Drivers/, Middleware/]
readme_summary: "基于STM32F407的无刷直流电机FOC控制..."
source_samples: { motor_control.c: "void FOC_Clark(...){...}", ... }
关键在于不只是匹配文件名——它会解析 CMakeLists.txt 中的 target_link_libraries、package.json 的 dependencies、甚至 STM32 CubeMX 的 .ioc 文件,从中提取出有意义的框架和依赖信息。framework_detector.py 内置了 35+ 种框架的签名数据库。
2. 三级分类器 — AI 为核心,规则为安全网
Stage 1 — 预筛选 (rule_prefilter.py):
快速提取硬信号(文件模式、依赖名称、框架签名),生成结构化线索:
{
"clues": {
"embedded_signals": ["STM32 HAL", "FreeRTOS", "CMSIS-DSP"],
"likely_l1": "embedded",
"hard_evidence": [".ioc文件存在", "startup_stm32f407xx.s 存在"]
}
}
这些线索不直接决定分类,而是作为 AI 的参考上下文。
Stage 2 — AI 分类 (llm_classifier.py):
构建一个包含指纹数据、预筛选线索和关键源码样本的 Prompt,让 LLM 输出:
{
"l1": "embedded",
"l2": "stm32",
"l3": "motor-control",
"confidence": 0.92,
"reasoning": "该工程包含STM32F407的FOC算法实现,使用CMSIS-DSP库进行Clarke/Park变换,判断为STM32电机控制工程"
}
LLM 可以自由创建 taxonomy.yaml 中不存在的新子类别——分类树是开放的。
Stage 3 — 校验 (rule_validator.py):
将 AI 的分类结果与硬信号交叉比对。比如 AI 返回 l1: "web" 但工程里有 .ioc 文件和 startup_stm32.s,校验器会将置信度从 0.85 降到 0.3 —— 显然是误判。
降级路径:当 LLM 不可用时(离线 / 配额耗尽),系统自动切换到纯规则模式——25+ 条优先级规则逐条尝试,最终 UnknownRule 兜底。
3. RAG 混合检索引擎 — 语义 + 关键词
只用向量搜索的问题是:搜 “FOC 电机控制” 可能漏掉标题就是 foc_motor.c 但描述用词不同的文档。只用关键词搜索的问题是:搜 “无刷直流电机控制” 找不到只写了 “BLDC Control” 的文档。
所以用了混合检索 + RRF 融合:
用户查询: "FOC 矢量控制"
│
├──▶ ChromaDB 语义搜索 ──▶ [doc1:0.89, doc3:0.78, doc5:0.72]
│
├──▶ SQLite FTS5 全文搜索 ──▶ [doc3:0.91, doc2:0.85, doc1:0.60]
│
└──▶ RRF 融合 ──▶ [doc3, doc1, doc2, doc5] (重排序)
RRF (Reciprocal Rank Fusion) 公式:score = Σ 1/(k + rank_i),k=60。简单但有效——不依赖分数归一化,对语义和关键词搜索的分数尺度差异不敏感。
4. 文档生成器 — 按分类定制模板
不同领域的工程需要不同的文档结构。用 Jinja2 模板 + 按一级分类路由:
src/documentor/templates/
├── base.md.j2 ← 基础布局(所有文档共用)
├── embedded.md.j2 ← 嵌入 MCU 信息、外设配置、引脚分配
├── ai_ml.md.j2 ← 模型架构、训练数据、评估指标
├── web.md.j2 ← 前后端分离、API 接口、部署方式
└── default.md.j2 ← 兜底模板
每个模板注入三个信息来源:
- README 概述(从工程原始 README 提取)
- LLM 摘要(2–4 句中文功能描述)
- 规则概述(从指纹数据自动生成的技术栈清单)
5. 事件总线 — 解耦的秘密武器
整个系统通过一个不到 100 行的 EventBus 实现模块间通信:
# 发布者:管道发出进度事件
await event_bus.emit("project.scan_done", {"project_id": id, "fingerprint": fp})
# 订阅者 1:WebSocket 广播给前端
@event_bus.subscribe("project.scan_done")
async def ws_broadcast(event):
for ws in connected_clients:
await ws.send_json(event)
# 订阅者 2:任何模块都可以监听
@event_bus.subscribe("project.ready")
async def on_project_ready(event):
# 比如:发送桌面通知、触发 CI 流水线...
pass
这带来的好处是:添加新功能不需要修改现有代码。想做桌面通知?订阅 project.ready 事件即可。想做自动备份?订阅 project.ready 事件即可。Pipeline、WebSocket、未来的任何模块完全解耦。
双向 UI:Desktop + Web
DeepClassify 有两套 UI,共用同一套后端 API:
Desktop UI (Avalonia)
面向日常桌面使用的用户。特点:
- 原生 Windows 窗口,启动快,内存占用低
- 内置 AI 聊天(SSE 流式),可以直接用自然语言问 “有哪些做电机控制的 STM32 工程?”
- 知识图谱可视化 — Canvas 手绘节点,力模拟布局,拖拽旋转交互
- 粉色模式(🌸 少女粉模式)—— 一点小趣味
MVVM 架构使用 CommunityToolkit.Mvvm 的源码生成器:
public partial class SearchViewModel : ObservableObject
{
[ObservableProperty]
private string _query = "";
[RelayCommand]
private async Task SearchAsync()
{
Results = await _api.SearchAsync(Query);
}
}
// [ObservableProperty] 自动生成 Query 属性 + PropertyChanged
// [RelayCommand] 自动生成 SearchCommand
Web UI (React)
面向浏览器访问的场景(比如 NAS 上部署、局域网共享)。特点:
- SVG 力导向知识图谱 — 600 次迭代物理模拟,拖拽缩放
- 项目卡片仪表盘 + recharts 饼图
- Tailwind 暗色模式,保护深夜工作的眼睛
- 可拖拽侧边栏,宽度持久化到 localStorage
数据流全景
让我用一个完整的例子串联所有模块:
1. 用户将 "foc_motor_ctrl/" 文件夹拖入 data/inbox/
2. WatchService (watchdog)
└─ 检测到新目录 → 去抖 10s → 验证是否为有效代码工程
└─ emit("project.detected", {path: "data/inbox/foc_motor_ctrl"})
3. Pipeline.process_project()
├─ Step 1: 在 SQLite 创建 Project 记录 (status: "detected")
├─ Step 2: Scanner 扫描
│ └─ 遍历 142 个文件 → 发现 .ioc + startup_stm32f407.s + FreeRTOSConfig.h
│ └─ 解析 CMakeLists.txt → 提取 arm-none-eabi-gcc, CMSIS-DSP
│ └─ 生成 ProjectFingerprint → emit("project.scan_done")
├─ Step 3: Classifier 分类
│ └─ PreFilter → clues: {likely_l1: "embedded", signals: ["STM32", "FreeRTOS"]}
│ └─ LLM 读取 main.c + motor_control.c → "这是 STM32F407 的 FOC 电机控制"
│ └─ Validator → 交叉验证通过 → emit("project.classify_done")
├─ Step 4: LLM 摘要
│ └─ "基于STM32F407的FOC矢量控制工程,使用FreeRTOS多任务架构..."
├─ Step 5: Documentor → Jinja2 渲染 embedded.md.j2
│ └─ 输出到 data/projects/embedded/stm32/foc_motor_ctrl.md
├─ Step 6: RAG Indexer
│ └─ Chunker 按 ## 标题分割 → 每个chunk嵌入为384维向量
│ └─ 写入 ChromaDB + SQLite FTS5 → emit("project.ready")
└─ 完成! status: "ready"
4. 用户搜索 "FOC 矢量控制"
└─ Retriever 混合搜索 → ChromaDB 语义 + FTS5 关键词 → RRF 融合
└─ 返回: [foc_motor_ctrl (score: 0.94), bldc_driver (score: 0.78), ...]
5. 用户在 AI Chat 中问 "foc_motor_ctrl 用了哪些外设?"
└─ Chat API → RAG 检索相关文档块 → 拼接 Prompt → LLM 回答
一些有意思的实现细节
去抖 (Debounce) 不只是前端的事
文件监听器面临的问题:用户拖入一个 500MB 的工程文件夹,操作系统需要几秒甚至十几秒才能完成复制。如果每检测到一个新文件就触发处理,会变成灾难。
解决方案:每路径异步去抖——在该路径上 10 秒没有新事件后才触发处理。
ChromaDB 的 PyInstaller 兼容
ChromaDB 依赖 onnxruntime 和 pypika,这两个包在 PyInstaller 打包时需要显式声明 hidden-import。否则打包后的 exe 跑不起来。BUILD.md 里专门有一节 troubleshooting 记录踩过的坑。
嵌入模型的离线缓存
sentence-transformers 默认从 HuggingFace Hub 下载模型。在国内网络环境下,这可能需要数次重试。系统做了两件事:
- 模型缓存到
data/model_cache/,二次启动秒加载 - 支持
HF_ENDPOINT=https://hf-mirror.com镜像加速
知识图谱的简易物理引擎
KnowledgeGraph.tsx 没有引入 D3 或任何图布局库,而是手写了 600 次迭代的力模拟:
for i in 0..600:
cooling = exp(-i / 200) # 指数衰减
for each node pair:
dx = node2.x - node1.x
dist = max(|dx|, 1)
force = REPULSION / dist² # Coulomb 斥力
node1.x -= force * dx * cooling
node2.x += force * dx * cooling
for nodes in same category:
# 同类节点轻微吸引
attract_towards_centroid()
简单但效果不错——几百个节点也能在几百毫秒内完成布局。
项目结构一览
deepClassify/
├── desktop_ui/ # Desktop UI (Avalonia .NET)
│ └── DeepClassify.App/
│ ├── Views/ # .axaml 页面 (6个)
│ ├── ViewModels/ # MVVM ViewModel (8个)
│ ├── Services/ # ApiClient + ThemeService
│ └── Models/ # DTO 记录类型
│
├── web_ui/ # Web UI (React + Vite)
│ └── src/
│ ├── pages/ # 5个路由页面
│ ├── components/ # Layout / Sidebar / KnowledgeGraph
│ └── stores/ # Zustand 状态
│
├── src/ # Python 后端
│ ├── api/ # FastAPI (18个REST端点 + WebSocket)
│ ├── pipeline.py # 核心管道
│ ├── scanner/ # 指纹扫描 (50+语言, 35+框架)
│ ├── classifier/ # 三级分类 (AI主导 + 25条规则)
│ ├── documentor/ # Jinja2文档生成 (5套模板)
│ ├── rag_engine/ # 混合检索 (ChromaDB + FTS5 + RRF)
│ ├── storage/ # SQLAlchemy + SQLite
│ └── watch_service/ # 文件系统监听
│
├── taxonomy.yaml # 分类体系 (9大类, 50+子类)
├── design-assets/ # ★ 设计资产 & 复用指南
│ ├── README.md # 设计资产复用文档
│ └── BLOG.md # 本篇博客
└── docs/ # 架构 + RAG + 嵌入模型 文档
适用场景 & 局限
适合
- 管理大量散落的代码工程(嵌入式 / AI / Web / FPGA 混合场景尤其受益)
- 团队知识管理 —— 新人来了能快速了解每个工程是干什么的
- 个人开发者整理多年的代码积累
- 对代码隐私有严格要求的环境
不适合
- 单个巨型 monorepo 的分类(扫描器针对独立工程目录设计)
- 需要协作编辑的场景(文档生成是单向的)
- 动态语言的精确分类(Python/JS 工程的框架检测不如 C/C++ 工程精确)
写在最后
这个项目的核心思路很简单:让 AI 读你的代码,帮你理解你的代码。
更多推荐



所有评论(0)