掌柜问数 —— 项目全景
学习理念:本文档是整个 21 篇课件集合的总览和导航。先读这篇,知道整个项目长什么样、每篇文档该花多少精力,再按优先级逐篇深入。
海外对标:掌柜智库对标 Google Vertex AI Search + Neo4J GraphRAG 的企业级知识库方案。
本节 AI 替代率:~80% | 人工干预率:~20%
| 角色 | 能力范围 |
|---|---|
| 🤖 AI 擅长 | 解释架构图、对比技术选型、生成目录结构 |
| 👤 人类需理解 | 哪些是新知识(P0)、哪些是已学技术(P1)、哪些是一次性配置(P2) |
21 篇文档优先级总表
| 优先级 | 编号 | 文档 | 行数 | 代码段 | 阅读建议 |
|---|---|---|---|---|---|
| 🔥 P0 必须要学 | 01 | 项目全景(本文) | 749 | 2 | 先读,建立全局认知 |
| 03 | 导入骨架代码 | 1175 | 11 | BaseNode/State/Graph 基石 | |
| 04 | 入口+PDF转MD | 858 | 6 | 第一个业务节点 | |
| 06 | 文档切分节点 | 1177 | 22 | RAG 核心策略 | |
| 07 | 商品名识别 | 1196 | 13 | LLM + Milvus 结合 | |
| 10 | 知识图谱构建 | 1384 | 25 | 🔴 最大新知识(Neo4J/GraphRAG) | |
| 11 | 查询骨架代码 | 1250 | 10 | 查询流程基石 | |
| 12 | 商品名确认 | 1148 | 16 | 查询侧核心节点 | |
| 14 | HyDE检索 | 783 | 16 | 🔴 新知识 | |
| 15 | 知识图谱查询 | 1139 | 22 | 🔴 新知识 | |
| 17 | RRF融合 | 744 | 16 | 🔴 新知识 | |
| 18 | Rerank重排序 | 1024 | 20 | 🔴 新知识 | |
| 🟡 P1 看注释就行 | 05 | 图片处理+MinIO | 1151 | 9 | VLM 调用套路固定 |
| 08 | 切片向量化 | 629 | 12 | bge-m3 在 Ch16 已学透 | |
| 09 | 向量入库 | 813 | 11 | Milvus 在 Ch16/17 已学透 | |
| 13 | 向量检索 | 943 | 23 | Milvus 检索,同上 | |
| 19 | 答案生成 | 968 | 21 | LLM 调用套路固定 | |
| 🟢 P2 后面可以查 | 20 | Web层与前端 | 1115 | 31 | 前端代码量大,需要时再查 |
| 02 | 环境配置部署 | 1100 | 4 | 一次性配置,跑通后不用再看 | |
| 🔵 P3 一目而过 | 16 | 网络搜索MCP | 648 | 16 | Ch16 已学 MCP |
| 21 | 项目总结 | 321 | 1 | 略读即可 |
1. 项目概述
1.1 项目定位与目标
项目定位:
掌柜问数是一个企业级智能知识库系统,结合了 RAG(检索增强生成) 和 知识图谱 技术,旨在为垂直领域(如电子产品手册、维修指南、技术文档等)提供精准、智能的知识检索与问答服务。
核心目标:
- 将非结构化文档(PDF、Markdown)转化为可检索的结构化知识
- 通过多路召回策略提升检索准确率
- 利用知识图谱实现实体关系推理
- 提供流畅的流式问答交互体验
1.2 核心功能特性
| 功能模块 | 描述 |
|---|---|
| 文档智能导入 | 支持 PDF/Markdown 文件上传,自动解析、切分、向量化 |
| 知识图谱构建 | LLM 自动抽取实体和关系,构建领域知识图谱 |
| 混合向量检索 | 稠密向量 + 稀疏向量(BM25)混合检索 |
| 多路召回融合 | 向量检索 + 知识图谱查询 + HyDE + Web 搜索 |
| 智能重排序 | Reranker 模型重排序,断崖检测动态截断 |
| 流式问答 | SSE 实时推送,逐字输出答案 |
| 会话历史管理 | MongoDB 存储对话历史,支持上下文连续对话 |
1.3 适用场景
- 产品手册问答:电子产品使用说明、维修手册等
- 技术文档检索:API 文档、开发指南、FAQ 等
- 企业知识库:内部制度、操作规范、培训资料等
- 售后客服支持:产品故障排查、使用指导等
2. 系统架构
2.1 整体架构图

2.2 核心模块说明
| 模块 | 职责 | 技术实现 |
|---|---|---|
| API 层 | HTTP 接口暴露、请求路由 | FastAPI + Uvicorn |
| Processor 层 | 业务流程编排、节点调度 | LangGraph |
| Tools 层 | 工具函数封装、外部服务调用 | Python 模块 |
| 数据层 | 数据持久化、检索 | Milvus / Neo4j / MongoDB / MinIO |
2.3 数据流向图
2.3.1 导入流程数据流

2.3.2 查询流程数据流

2.4 技术栈选型
| 类别 | 技术选型 | 版本/说明 |
|---|---|---|
| 后端框架 | FastAPI + Uvicorn | 异步高性能 HTTP 服务 |
| 工作流引擎 | LangGraph | 有状态图编排框架 |
| 大语言模型 | 阿里云 DashScope (Qwen) | qwen-flash / qwen3-vl-flash |
| 向量嵌入 | OpenAI API (text-embedding-v4) + BGE-M3 | 1536维 / 1024维+稀疏 |
| 重排序模型 | BGE-Reranker-Large | 本地部署 |
| 向量数据库 | Milvus | 混合检索(稠密+稀疏) |
| 图数据库 | Neo4j Community | 知识图谱存储与查询 |
| 文档数据库 | MongoDB | 对话历史存储 |
| 对象存储 | MinIO | 文件与图片存储 |
| PDF解析 | MineRU | PDF 转 Markdown |
| 前端 | HTML5 + JS | 无框架,轻量实现 |
3. 核心技术原理
3.1 知识图谱构建原理
3.1.1 抽取流程

3.1.2 实体与关系类型
实体类型:
| 类型 | 描述 | 示例 |
|---|---|---|
| Device | 设备/产品 | 万用表、示波器 |
| Part | 部件/组件 | 探针、表盘、电池仓 |
| Operation | 操作/功能 | 测量电压、校准 |
| Step | 操作步骤 | 步骤1-连接探针 |
| Warning | 警告/注意 | 警告-请勿超量程 |
| Condition | 条件/状态 | 低电量、超载 |
| Tool | 工具/配件 | 探针、电池 |
关系类型:
| 关系 | 描述 | 示例 |
|---|---|---|
| HAS_OPERATION | 拥有操作 | 万用表 → HAS_OPERATION → 测量电压 |
| HAS_PART | 拥有部件 | 万用表 → HAS_PART → 探针 |
| HAS_STEP | 包含步骤 | 测量电压 → HAS_STEP → 步骤1 |
| USES_TOOL | 使用工具 | 测量电压 → USES_TOOL → 红黑探针 |
| HAS_WARNING | 相关警告 | 测量电压 → HAS_WARNING → 警告-超量程 |
| NEXT_STEP | 下一步骤 | 步骤1 → NEXT_STEP → 步骤2 |
| AFFECTS | 影响 | 低电量 → AFFECTS → 测量精度 |
| REQUIRES | 需要条件 | 测量电压 → REQUIRES → 正确档位 |
3.2 RAG 工作机制
RAG (Retrieval-Augmented Generation) 是一种结合检索与生成的技术范式:

3.3 向量检索
混合向量 (Dense + Sparse)
本项目采用 BGE-M3 模型实现混合向量检索:
| 向量类型 | 维度 | 检索方式 | 优势 |
|---|---|---|---|
| 稠密向量 (Dense) | 1024维 | HNSW 近似最近邻 | 语义相似性强 |
| 稀疏向量 (Sparse) | 动态维度 | BM25 倒排索引 | 关键词精确匹配 |
检索流程:
python
# 混合检索示例
reqs = build_hybrid_search_requests(
dense_vector=query_dense, # 稠密向量
sparse_vector=query_sparse, # 稀疏向量
dense_search_params={"metric_type": "COSINE"},
sparse_search_params={"metric_type": "IP"},
top_k=10
)
# 融合排序(权重可调)
results = execute_hybrid_search(
search_requests=reqs,
ranker_weights=(0.5, 0.5) # 50% 稠密 + 50% 稀疏
)3.4 多路召回策略
本项目实现了 四路并行召回:

RRF (Reciprocal Rank Fusion) 公式:
$$ RRF_score(d) = \sum_{r \in R} \frac{1}{k + rank_r(d)} $$ 其中:
- R 是所有召回来源
- rank_r(d) 是文档 d 在来源 r 中的排名
- k 是平滑参数(通常取 60)
断崖检测算法:
python
for i in range(min_topk - 1, max_topk - 1):
gap = score[i] - score[i + 1]
relative_gap = gap / (abs(score[i]) + 1e-6)
# 满足任一条件则截断
if gap >= gap_abs_threshold or relative_gap >= gap_ratio_threshold:
return documents[:i + 1]4. 项目目录结构说明
4.1 各模块职责
knowledge/
├── api/ # API 路由层
│ ├── query_router.py # 查询服务路由 (port 8001)
│ │ ├── POST /query # 发起查询
│ │ ├── GET /stream/{session_id} # SSE 流式获取
│ │ ├── GET /history/{session_id}# 获取历史
│ │ └── DELETE /history/... # 清除历史
│ └── import_router.py # 导入服务路由 (port 8000)
│ ├── POST /upload # 上传文件
│ └── GET /status/{task_id} # 查询任务状态
│
├── core/ # 核心配置
│ ├── deps.py # 依赖注入(单例管理)
│ └── paths.py # 路径常量配置
│
├── processor/ # 业务处理流程(LangGraph)
│ ├── import_process/ # 导入流程
│ │ ├── main_graph.py # 导入流程图定义
│ │ ├── state.py # 状态类型定义
│ │ └── nodes/ # 处理节点
│ │ ├── entry.py # 入口节点
│ │ ├── pdf_to_md.py # PDF 转 MD
│ │ ├── md_img.py # 图片处理
│ │ ├── document_split.py # 文档切分
│ │ ├── item_name_recognition.py # 商品识别
│ │ ├── bge_embedding.py # 向量嵌入
│ │ ├── import_milvus.py # Milvus 存储
│ │ └── knowledge_graph.py # 知识图谱构建
│ │
│ └── query_process/ # 查询流程
│ ├── main_graph.py # 查询流程图定义
│ ├── state.py # 状态类型定义
│ ├── prompt.py # 提示词模板
│ └── nodes/ # 处理节点
│ ├── item_name_confirm.py # 商品确认
│ ├── search_embedding.py # 向量检索
│ ├── search_embedding_hyde.py# HyDE 检索
│ ├── query_kg.py # 知识图谱查询
│ ├── web_search_mcp.py # Web 搜索
│ ├── rrf.py # RRF 融合
│ ├── rerank.py # 重排序
│ └── answer_output.py # 答案生成
│
├── schemas/ # 数据模型定义
│ ├── query_schema.py # 查询请求/响应模型
│ ├── upload_schema.py # 上传响应模型
│ └── task_schema.py # 任务状态模型
│
├── services/ # 业务服务层
│ ├── file_import_service.py # 文件导入服务
│ └── task_service.py # 任务管理服务
│
├── tools/ # 工具函数库
│ ├── milvus_utils.py # Milvus 向量库操作
│ ├── neo4j_utils.py # Neo4j 图数据库操作
│ ├── embedding_utils.py # 向量嵌入工具
│ ├── llm_utils.py # LLM 客户端封装
│ ├── reranker_utils.py # Reranker 模型
│ ├── mongo_history_utils.py # MongoDB 历史记录
│ ├── sse_utils.py # SSE 流式推送
│ ├── task_utils.py # 任务状态管理
│ ├── minio_utils.py # MinIO 对象存储
│ └── normalize_sparse_vector.py # 稀疏向量规范化
│
├── front/ # 前端页面
│ ├── chat.html # 聊天界面
│ └── import.html # 导入界面
│
├── test/ # 测试代码
├── docs/ # 文档目录
├── temp_data/ # 临时数据目录
├── .env # 环境配置文件
└── requirements.txt # Python 依赖声明4.2 配置文件说明
.env 环境配置
ini
# ====== 模型缓存配置 ======
MINERU_MODEL_SOURCE=modelscope # MineRU 模型来源
MODELSCOPE_OFFLINE=1 # 离线模式
MODELSCOPE_CACHE=/path/to/cache # ModelScope 缓存路径
HF_HOME=/path/to/huggingface # HuggingFace 缓存路径
# ====== LLM API 配置 ======
OPENAI_API_KEY=sk-xxx # API 密钥
OPENAI_API_BASE=https://dashscope... # API 基础地址
LLM_DEFAULT_MODEL=qwen-flash # 默认 LLM 模型
LLM_DEFAULT_TEMPERATURE=0.1 # 温度参数
VL_MODEL=qwen3-vl-flash # 视觉语言模型
ITEM_MODEL=qwen-flash # 商品识别模型
KG_MODEL=qwen-flash # 知识图谱模型
# ====== BGE 模型配置 ======
BGE_M3_PATH=/path/to/bge-m3 # BGE-M3 本地路径
BGE_DEVICE=cuda:0 # GPU 设备
BGE_FP16=True # 半精度推理
BGE_RERANKER_LARGE=/path/to/reranker # Reranker 路径
BGE_RERANKER_DEVICE=cuda:0 # Reranker 设备
# ====== 向量配置 ======
EMBEDDING_DIM=1536 # 嵌入维度
EMBEDDING_MODEL=text-embedding-v4 # 嵌入模型
# ====== Milvus 配置 ======
MILVUS_URL=http://localhost:19530 # Milvus 服务地址
CHUNKS_COLLECTION=kb_chunks # 切片集合名
ENTITY_NAME_COLLECTION=kb_entity_names # 实体名集合
ITEM_NAME_COLLECTION=kb_item_names # 商品名集合
MILVUS_METRIC_TYPE=COSINE # 距离度量
MILVUS_MIN_COSINE_SCORE=0.75 # 最小相似度
# ====== Neo4j 配置 ======
NEO4J_URI=bolt://localhost:7687 # Neo4j URI
NEO4J_DATABASE=neo4j # 数据库名
NEO4J_USERNAME=neo4j # 用户名
NEO4J_PASSWORD=password # 密码
# ====== MongoDB 配置 ======
MONGO_URL=mongodb://localhost:27017 # MongoDB 连接
MONGO_DB_NAME=kb001 # 数据库名
# ====== MinIO 配置 ======
MINIO_ENDPOINT=localhost:9000 # MinIO 端点
MINIO_ACCESS_KEY=minioadmin # 访问密钥
MINIO_SECRET_KEY=minioadmin # 私有密钥
MINIO_BUCKET_NAME=knowledge-base # 存储桶名5. 数据处理流程
5.1 文档导入流程

5.2 知识抽取过程
LLM 提示词设计要点:
你是一个知识图谱构建专家,请从以下文本中抽取实体和关系。
实体类型:
- Device(设备/产品)
- Part(部件/组件)
- Operation(操作/功能)
- Step(操作步骤,格式:步骤N-动作短语)
- Warning(警告,格式:警告-内容)
- Condition(条件/状态)
- Tool(工具/配件)
关系类型:
- HAS_OPERATION(拥有操作)
- HAS_PART(拥有部件)
- HAS_STEP(包含步骤)
- USES_TOOL(使用工具)
- HAS_WARNING(相关警告)
- NEXT_STEP(下一步骤)
- AFFECTS(影响)
- REQUIRES(需要条件)
输出要求:
- 实体名称简短(≤15字)
- 输出纯 JSON(无 Markdown 围栏)
- 格式:{"entities": [...], "relations": [...]}5.3 图谱构建步骤

5.4 检索增强流程

6. 前后端数据交互
6.1 主要 API 接口概览
6.1.1 导入服务 (Port 8000)
| 方法 | 路径 | 描述 |
|---|---|---|
| POST | /upload | 上传文件(支持多文件) |
| GET | /status/{task_id} | 查询任务处理状态 |
| GET | /health | 健康检查 |
6.1.2 查询服务 (Port 8001)
| 方法 | 路径 | 描述 |
|---|---|---|
| POST | /query | 发起查询(流式/非流式) |
| GET | /stream/{session_id} | SSE 流式获取答案 |
| GET | /history/{session_id} | 获取会话历史 |
| DELETE | /history/{session_id} | 清除会话历史 |
| GET | /health | 健康检查 |
6.2 请求/响应格式
6.2.1 文件上传
请求:
http
POST /upload
Content-Type: multipart/form-data
files: [file1.pdf, file2.md, ...]响应:
json
{
"message": "Files uploaded successfully",
"task_ids": ["task_001", "task_002"]
}6.2.2 任务状态查询
请求:
http
GET /status/task_001响应:
json
{
"task_id": "task_001",
"status": "processing", // pending | processing | completed | failed
"progress": 45, // 0-100
"message": "正在构建知识图谱...",
"file_name": "万用表使用手册.pdf",
"created_at": "2026-02-23T10:00:00"
}6.2.3 知识查询(非流式)
请求:
http
POST /query
Content-Type: application/json
{
"query": "万用表如何测量直流电压?",
"session_id": "sess_001", // 可选,留空自动生成
"is_stream": false
}响应:
json
{
"message": "处理完成",
"session_id": "sess_001",
"answer": "测量直流电压的步骤如下:\n1. 将旋钮调至 V= 档位...",
"done_list": []
}6.2.4 知识查询(流式)
请求:
http
POST /query
Content-Type: application/json
{
"query": "万用表如何测量直流电压?",
"session_id": "sess_001",
"is_stream": true
}响应(建立 SSE 连接):
http
GET /stream/sess_001
Accept: text/event-streamSSE 事件流:
data: {"type": "delta", "data": {"delta": "测"}}
data: {"type": "delta", "data": {"delta": "量"}}
data: {"type": "delta", "data": {"delta": "直"}}
...
data: {"type": "final", "data": {"answer": "测量直流电压...", "status": "completed"}}6.2.5 历史查询
请求:
http
GET /history/sess_001?limit=10响应:
json
{
"session_id": "sess_001",
"items": [
{
"_id": "65d1a2b3...",
"session_id": "sess_001",
"role": "user",
"text": "万用表如何测量电压?",
"rewritten_query": "万用表测量直流电压的步骤",
"item_names": ["万用表"],
"ts": 1708756800
},
{
"_id": "65d1a2c4...",
"session_id": "sess_001",
"role": "assistant",
"text": "测量直流电压的步骤如下...",
"ts": 1708756805
}
]
}6.3 前端交互流程
6.3.1 导入界面交互
javascript
// 1. 发送查询
async function sendQuery(query) {
const response = await fetch('/query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: query,
session_id: currentSessionId,
is_stream: true
})
});
const data = await response.json();
currentSessionId = data.session_id;
// 2. 建立 SSE 连接
const eventSource = new EventSource(`/stream/${currentSessionId}`);
eventSource.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'delta') {
// 3. 逐字渲染
appendToAnswer(msg.data.delta);
} else if (msg.type === 'final') {
// 4. 完成处理
eventSource.close();
finalizeAnswer(msg.data.answer);
}
};
}6.3.2 聊天界面交互
javascript
// 1. 上传文件
async function uploadFiles(files) {
const formData = new FormData();
files.forEach(file => formData.append('files', file));
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
// 2. 轮询任务状态
data.task_ids.forEach(taskId => {
pollTaskStatus(taskId);
});
}
// 3. 状态轮询
async function pollTaskStatus(taskId) {
const interval = setInterval(async () => {
const response = await fetch(`/status/${taskId}`);
const status = await response.json();
updateProgressBar(taskId, status.progress);
if (status.status === 'completed' || status.status === 'failed') {
clearInterval(interval);
showResult(status);
}
}, 2000); // 每2秒轮询
}7. 快速开始
7.1 环境要求
| 项目 | 要求 |
|---|---|
| Python | >= 3.10 |
| CUDA | >= 自己机器适配的版本(GPU 推理需要) |
| 内存 | >= 16GB(建议 32GB) |
| 显存 | >= 8GB(BGE-M3 + Reranker) |
| 存储 | >= 50GB(模型缓存) |
依赖服务:
- Milvus(向量数据库)
- Neo4j(图数据库)
- MongoDB(文档数据库)
- MinIO(对象存储)
7.2 安装步骤
bash
# 1. 创建虚拟环境
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux/Mac
# 2. 安装依赖(使用国内镜像)
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 3. 配置环境变量
copy .env.example .env
# 编辑 .env 文件,填入正确的配置7.3 基础配置
- 启动依赖服务
bash
# Docker Compose 方式(推荐)
docker-compose up -d milvus neo4j mongodb minio- 下载模型
bash
# BGE-M3 模型(通过 ModelScope)
python -c "from modelscope import snapshot_download; snapshot_download('BAAI/bge-m3')"
# BGE-Reranker-Large
python -c "from modelscope import snapshot_download; snapshot_download('BAAI/bge-reranker-large')"- 初始化数据库
bash
# 运行初始化脚本(如有)
python scripts/init_db.py7.4 运行示例
bash
# 启动导入服务 (端口 8000)
uvicorn api.import_router:app --host 0.0.0.0 --port 8000 --reload
# 启动查询服务 (端口 8001)
uvicorn api.query_router:app --host 0.0.0.0 --port 8001 --reload访问前端页面:
8. 附录
A. Milvus 集合 Schema
文档切片
| 字段 | 类型 | 描述 |
|---|---|---|
| pk | INT64 | 主键,自增 |
| chunk_id | INT64 | 切片 ID |
| content | VARCHAR(65535) | 切片内容 |
| title | VARCHAR | 章节标题 |
| dense_vector | FLOAT_VECTOR(1024) | 稠密向量 |
| sparse_vector | SPARSE_FLOAT_VECTOR | 稀疏向量 |
| item_name | VARCHAR | 商品名称 |
| created_at | VARCHAR | 创建时间 |
图谱实体
| 字段 | 类型 | 描述 |
|---|---|---|
| pk | INT64 | 主键,自增 |
| entity_name | VARCHAR | 实体名称 |
| dense_vector | FLOAT_VECTOR(1024) | 稠密向量 |
| sparse_vector | SPARSE_FLOAT_VECTOR | 稀疏向量 |
| source_chunk_id | VARCHAR | 来源切片 ID |
| context | VARCHAR | 上下文描述 |
| item_name | VARCHAR | 所属商品 |
B. Neo4j 图模型
(Entity {name, item_name, types[], description})
│
├── [:HAS_OPERATION] ──→ (Entity:Operation)
├── [:HAS_PART] ──→ (Entity:Part)
├── [:HAS_STEP] ──→ (Entity:Step)
├── [:HAS_WARNING] ──→ (Entity:Warning)
├── [:USES_TOOL] ──→ (Entity:Tool)
├── [:NEXT_STEP] ──→ (Entity:Step)
├── [:AFFECTS] ──→ (Entity)
├── [:REQUIRES] ──→ (Entity:Condition)
└── [:MENTIONED_IN] ──→ (Chunk {id, item_name})C. 关键文件速查表
| 功能 | 文件路径 |
|---|---|
| 导入 API | api/import_router.py |
| 查询 API | api/query_router.py |
| 导入流程图 | processor/import_process/main_graph.py |
| 查询流程图 | processor/query_process/main_graph.py |
| 知识图谱构建 | processor/import_process/nodes/knowledge_graph.py |
| 知识图谱查询 | processor/query_process/nodes/query_kg.py |
| 向量检索 | processor/query_process/nodes/search_embedding.py |
| 重排序 | processor/query_process/nodes/rerank.py |
| 答案生成 | processor/query_process/nodes/answer_output.py |
| 提示词模板 | processor/query_process/prompt.py |
| Milvus 工具 | tools/milvus_utils.py |
| Neo4j 工具 | tools/neo4j_utils.py |
| LLM 客户端 | tools/llm_utils.py |
| SSE 推送 | tools/sse_utils.py |