Files
QMDSearch/CLAUDE.md
T
kplam 51dc8dc4f6 Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈
- L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合)
- 文档三级总结与 2.5 级回退
- query 解析路由与分类
- /admin 管理页面
2026-07-29 21:24:40 +08:00

161 lines
8.1 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.
# QMDSearch - AI Agent 分层信息检索服务
## 项目概述
QMDSearch 是面向 AI Agent 的分层信息检索服务,支持多层级知识库检索、向量语义搜索和结构化数据查询。基于 Docker 部署在 NAS 上,为 AI Agent 提供高效、精准的信息检索能力。
## 技术栈
- **语言**: Python 3.12+
- **Web 框架**: FastAPI
- **向量数据库**: Qdrant (Docker)
- **缓存**: Redis (Docker)
- **关系数据库**: PostgreSQL (Docker, 可选)
- **嵌入模型**: OpenAI / 本地模型 (通过配置切换)
- **本地推理模型**: Ollama (Docker) — 用于文档三级总结,默认 qwen2.5:1.5b
- **部署**: Docker Compose on NAS
## 项目结构
```
QMDSearch/
├── app/ # 应用主目录
│ ├── main.py # FastAPI 入口(含 /admin 管理页面挂载)
│ ├── config.py # 配置管理
│ ├── api/ # API 路由层
│ │ ├── response.py # 统一响应格式 (ok/error/ApiError)
│ │ └── v1/ # API v1 版本
│ │ ├── search.py # 检索接口
│ │ ├── document.py # 文档入库/管理接口
│ │ └── knowledge.py # 知识库接口
│ ├── core/ # 核心业务逻辑
│ │ ├── retriever.py # 分层检索引擎 (L1→L2→L3→chunk)
│ │ ├── query_parser.py # query 解析与分类路由
│ │ ├── embeddings.py # 向量嵌入
│ │ ├── sparse.py # 稀疏向量编码 (BM25 近似)
│ │ ├── ranker.py # RRF 融合与结果截断
│ │ ├── summarizer.py # 文档三级总结 (Ollama)
│ │ ├── headings.py # 原生标题树解析
│ │ ├── classifier.py # 文档分类 (主类+标签+置信度)
│ │ ├── chunker.py # 标题树感知 chunk 切分
│ │ └── ingestion.py # 文档入库 (总结→分类→写入)
│ ├── models/ # 数据模型
│ │ ├── search.py # 检索请求/响应模型
│ │ ├── knowledge.py # 知识库/taxonomy 模型
│ │ └── document.py # 文档/总结模型
│ ├── services/ # 服务层 (外部交互)
│ │ ├── qdrant.py # Qdrant 客户端 (四层集合)
│ │ ├── redis.py # Redis 缓存客户端
│ │ └── ollama.py # Ollama 客户端
│ ├── static/ # 静态资源
│ │ └── admin.html # 管理页面 (单文件)
│ └── utils/ # 工具函数
├── scripts/ # 运维与评测脚本
│ ├── eval/ # 回归评测 (run_eval.py / metrics.py / judge.py / regression_set.json)
│ └── smoke_live.py # 在线冒烟脚本
├── tests/ # 测试
├── docker-compose.yml # Docker 编排
├── Dockerfile # 应用镜像
├── .env.example # 环境变量模板
├── pyproject.toml # 项目配置
└── CLAUDE.md # 本文件
```
## 分层检索架构
检索链路:query 解析路由 → L1→L2→L3 摘要树逐层剪枝 → chunk 层 hybrid 检索 (dense + sparseRRF 融合)。
0. **query 解析路由**: 用 Ollama 小模型将 query 解析为结构化结果(rewrite、关键词、命中类目+置信度);高置信且类目数不超上限时按类目过滤,低置信/解析失败/类目过多则全库兜底(不丢召回)
1. **L1 - 文档定位层**: 在文档 L1 总结集合中检索,产出候选文档集合;无命中时直接全库 chunk 兜底(fallback=True
2. **L2 - 章节大纲层**: 在候选文档内检索 L2 章节大纲,按 section 剪枝定位;未命中的文档(含 2.5 级文档)落入 L3 b 路(仅按 doc 过滤)
3. **L3 - 小节内容层**: 两路查询(aL2 命中文档按 section_path 过滤;b:其余文档仅按 doc_id 过滤)后 RRF 融合;无命中时 chunk 层回退为 L1 候选文档级检索
4. **chunk 层**: 在 L3 收窄的范围内对原文 chunk 做 hybrid 检索(dense 向量 + sparse 稀疏向量 RRF 融合),截断后返回最终 Top-K
## API 设计规范
- RESTful 风格,版本化路径: `/api/v1/`
- 请求/响应使用 Pydantic 模型校验
- 统一响应格式: `{"code": 0, "data": {...}, "message": "ok"}`
- 错误码: 0=成功, 1xxx=客户端错误, 2xxx=服务端错误
## API 清单
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/health` | 健康检查 |
| POST | `/api/v1/search` | 分层检索 |
| POST | `/api/v1/documents` | 文档入库(202 异步入库,返回 task_id |
| GET | `/api/v1/documents/tasks/{task_id}` | 入库任务状态查询(done 附 resultfailed 附 error |
| GET | `/api/v1/knowledge/categories` | 知识分类类目集 |
| GET | `/api/v1/knowledge/stats` | 统计(四层点数 + 类目分布 + uncategorized 数) |
| GET | `/api/v1/documents` | 文档列表(limit/offset 分页) |
| GET | `/api/v1/documents/{doc_id}` | 文档详情 |
| DELETE | `/api/v1/documents/{doc_id}` | 删除文档(幂等) |
| GET | `/admin` | 管理页面 |
入库任务状态持久化在 Redis(key: `ingest_task:{task_id}`):进行中与 done 保留 24hfailed 保留 7 天;Redis 不可用时降级为纯内存。
## 管理页面
浏览器访问 `/admin`,单页面含概览、文档管理(列表/详情/删除)、文档入库、检索测试台、类目列表五个区块。
## 编码规范
- 使用 uv 管理依赖
- 类型注解必须 (Python 3.12+ 语法)
- 异步优先 (async/await)
- 配置通过环境变量注入,使用 pydantic-settings
- 日志使用 structlog 结构化日志
## 文档入库与三级总结
文档入库时通过 Ollama 本地小模型对文档内容进行三级总结,然后根据总结进行分类入库。
### 总结层级
| 层级 | 名称 | 说明 | 存储用途 |
|------|------|------|----------|
| L1 | 总结 | 对整篇文档的一句话高度概括 | 快速分类、知识库路由 |
| L2 | 大纲 | 提取文档主要章节和关键主题(优先使用文档原生标题树,无结构文本回退 LLM 生成) | 检索召回、上下文概览 |
| L3 | 内容大纲 | 每个章节的详细内容摘要 | 精确匹配、深度检索 |
**2.5 级回退**: 当文档内容不足以支撑三级总结时(如短文本、简单说明),自动降级为二级总结:
- L1: 总结(同上)
- L2.5: 内容大纲(跳过大纲层,直接输出详细摘要)
### 入库流程
```
文档输入 → 文本提取 → 三级总结(Ollama) → 分类判定(L1总结) → 向量化 → 写入Qdrant
不足三级 → 2.5级回退
```
1. **文本提取**: 从文件中提取纯文本内容
2. **三级总结**: 调用 Ollama 依次生成 L1/L2/L3 总结;其中 L2 大纲优先使用文档原生标题树(不调 LLM),无结构文本(标题数 < 2)回退 LLM 生成
3. **分类判定**: 根据 L1 总结将文档分配到 taxonomy 类目,输出主类目 + 附加标签 + 置信度;LLM 输出解析失败或置信度低于阈值时归 uncategorized 兜底(低置信时候选类目名保留进 tags 供软召回)
4. **向量化**: 对原文 chunk 和各级总结分别生成 embeddingdense + sparse
5. **写入 Qdrant**: 将文档元数据、各级总结、向量写入对应四层集合(L1/L2/L3/chunks
### 本地模型 (Ollama)
- 服务: Ollama 容器,默认模型 `qwen2.5:1.5b`(约 1GB,适合 NAS 低资源环境)
- 备选模型: `qwen2.5:3b`(更好的总结质量,约 2GB
- 模型通过环境变量 `OLLAMA_MODEL` 配置
- 首次启动时自动拉取模型,需 NAS 可访问外网
## Docker 部署说明
- 目标环境: NAS (ARM64/AMD64)
- 镜像: python:3.12-slim
- 持久化: NAS 本地目录挂载
- 端口: 默认 8000 (API), 6333 (Qdrant), 6379 (Redis), 11434 (Ollama)
- 健康检查: `/api/v1/health`
## 开发流程
1. 修改代码后本地测试: `uv run pytest`
2. 构建镜像: `docker compose build`
3. 启动服务: `docker compose up -d`
4. 查看日志: `docker compose logs -f app`