从零构建 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实现模块间通信:
# 发布者:管道发出进度事件awaitevent_bus.emit("project.scan_done",{"project_id":id,"fingerprint":fp})# 订阅者 1:WebSocket 广播给前端@event_bus.subscribe("project.scan_done")asyncdefws_broadcast(event):forwsinconnected_clients:awaitws.send_json(event)# 订阅者 2:任何模块都可以监听@event_bus.subscribe("project.ready")asyncdefon_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 的源码生成器:
publicpartialclassSearchViewModel:ObservableObject{[ObservableProperty]privatestring_query="";[RelayCommand]privateasyncTaskSearchAsync(){Results=await_api.SearchAsync(Query);}}// [ObservableProperty] 自动生成 Query 属性 + PropertyChanged// [RelayCommand] 自动生成 SearchCommandWeb 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 读你的代码,帮你理解你的代码。