# ai_agent
**Repository Path**: clockdotnet/ai_agent
## Basic Information
- **Project Name**: ai_agent
- **Description**: dev分支:增加uniapp移动端;基于 RAG(检索增强生成)的企业级知识库问答平台,支持多格式文档管理、混合检索、LangGraph 多节点 Agent 编排与流式问答。前端 Vue 3 + Vant,后端由 FastAPI(AI/RAG 引擎)和 Django(用户/权限服务)组成,提供完整的知识库权限体系和评估驱动的迭代优化闭环。
- **Primary Language**: Python
- **License**: MPL-2.0
- **Default Branch**: dev
- **Homepage**: http://120.27.247.229/
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2026-10-22
- **Last Updated**: 2026-11-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: 项目, 重要
## README
# 企业知识库智能问答系统
基于 RAG(检索增强生成)的企业级知识库问答平台,支持多格式文档管理、混合检索、LangGraph 多节点 Agent 编排与流式问答。前端 Vue 3 + Vant,后端由 FastAPI(AI/RAG 引擎)和 Django(用户/权限服务)组成,提供完整的知识库权限体系和评估驱动的迭代优化闭环。
## 目录
- [技术亮点](#技术亮点)
- [核心能力](#核心能力)
- [项目演示](#项目演示)
- [系统架构](#系统架构)
- [技术栈](#技术栈)
- [项目结构](#项目结构)
- [配置说明](#配置说明)
- [部署与启动](#部署与启动)
- [Docker 部署](#docker-部署)
- [页面入口](#页面入口)
- [API 速览](#api-速览)
- [RAG 与知识库机制](#rag-与知识库机制)
- [LangGraph Agent 引擎](#langgraph-agent-引擎)
- [MCP 协议集成](#mcp-协议集成)
- [评估体系](#评估体系)
- [测试与构建](#测试与构建)
- [常见问题](#常见问题)
- [相关文档](#相关文档)
## 技术亮点
| 亮点 | 说明 |
|------|------|
| 混合检索 + 父子分块 | 向量语义检索 + BM25 关键词检索双路召回;子块精准召回→父块提供完整上下文,recall@1 达 0.881 |
| LangGraph 多节点 Agent | Coordinator 任务规划 → Knowledge 检索 → Critic 质量审查 → Finalize 生成,支持自我修正闭环 |
| 评估驱动迭代 | 三层指标(检索/回答/编排)+ 消融对比,用数据而非直觉做工程决策 |
| Critic 自我修正 | 大幅减少"知识库其实有,但被误判为没有"的情况(精确率 0.50→0.87),代价仅多一次 LLM 调用 |
| 知识缺口自动发现 | 检索置信度低于阈值时,LLM 自动抽取未覆盖知识点落库,形成知识库补充闭环 |
| 双服务架构 | FastAPI(AI 推理)+ Django(用户权限)独立部署、独立伸缩、故障隔离 |
| SSE 流式输出 | Agent 每个节点的执行步骤实时推送到前端,用户可见「思考过程」 |
| 多向量后端 | 支持 ChromaDB(嵌入式)和 Milvus(分布式)无缝切换 |
| Docker 基础镜像优化 | 依赖预装基础镜像,日常构建仅复制源码,秒级完成 |
| MCP 协议双向集成 | 对外暴露知识库检索为 MCP Server(stdio/SSE),对内作为 MCP Client 动态接入外部工具 |
## 核心能力
- AI 问答:支持普通 RAG 查询和 Agent 流式问答,回答可携带来源引用。
- LangGraph Agent 引擎(可选):Coordinator → Knowledge →(证据不足时)Critic → Task / KnowledgeGap → Finalize 的多节点图,每个节点的执行步骤会作为 SSE 事件实时推送到前端。
- 任务执行:识别到「文档对比 / 报告生成 / 申请文本生成」时,调用对应 prompt 工具产出结构化结果,区别于纯问答。
- 知识缺口记录:检索 0 命中或置信极低时,LLM 抽取"未覆盖的知识点"自动落库,管理员可在「我的 → 知识缺口」页查看并改状态。
- 会话管理:FastAPI 侧将会话和消息持久化到 MySQL,前端提供会话列表、切换和删除。
- 知识库管理:支持个人、部门、公司范围的知识库,并提供成员权限模型。
- 文档管理:支持 `txt`、`pdf`、`md`、`docx`、`pptx` 文件上传、去重、记录、删除和检索。
- 混合检索:向量检索 + BM25 关键词检索,兼顾语义召回和精确词匹配。
- 父子分块:小块用于召回,大块用于给 LLM 提供完整上下文。
- 重排序:默认使用 Qwen3-Reranker-0.6B 对召回文档精排,也支持阿里云重排序配置。
- 多向量后端:默认 ChromaDB,本地需要独立向量服务时可切换 Milvus。
- 用户服务:Django 提供注册、登录、JWT、用户资料、管理员账号管理和文件上传接口。
- 移动端适配 UI:底部 Tab 覆盖 AI 问答、会话、知识库、我的四个主入口。
- MCP Server:将知识库检索、文档重排序等核心能力封装为 MCP 工具,供 Claude Desktop、Cursor 等外部 AI 客户端通过标准协议调用。
- MCP Client:Agent 支持动态连接外部 MCP Server,自动发现并注册第三方工具,扩展 Agent 能力边界。
## 项目演示
> 在线体验地址:http://120.27.247.229/
以下截图来自项目实际界面,覆盖 AI 问答、会话管理、知识库管理、文档编译和账号权限等主要场景。
### AI 问答
基于个人或共享知识库进行流式问答,回答支持 Markdown 渲染、来源引用、Agent 执行步骤和审批操作。
### 会话管理
会话页支持搜索历史会话、继续对话、归档和删除,并在右侧展示会话统计与快捷操作。
### 知识库管理
支持个人文档和共享知识库两个视图,管理员可创建个人、公开、部门、管理员专属等不同范围的知识库。
创建知识库时可填写名称、描述并选择访问范围,便于按个人、部门或管理权限隔离知识。
### 文档上传与编译
个人知识库支持上传 `PDF`、`TXT`、`MD`、`DOCX`、`PPTX` 等文件,并展示资源概览、检索配置和危险操作。
知识库详情页可查看文档列表、分块数量、更新时间,并支持继续上传或删除指定文档。
### 账号管理
管理员可在账号管理页查看用户列表、创建账号、分配部门,并设置普通用户或管理员权限。
## 系统架构
```mermaid
flowchart TD
subgraph Frontend["Vue 3 + Vite + Vant,端口 3000"]
A["AI 问答 /aichat"]
B["会话管理 /sessions"]
C["知识库 /knowledge"]
D["我的 /my"]
E["登录注册 /login /register"]
end
subgraph Gateway["Vite 开发代理"]
P1["/api -> FastAPI 8000"]
P2["/user、/file -> Django 8001"]
end
subgraph FastAPI["FastAPI RAG 服务,端口 8000"]
F1["Agent / 流式问答"]
F2["RAG 查询"]
F3["会话与消息"]
F4["知识库与文档管理"]
F5["知识缺口管理"]
F6["限流中间件"]
end
subgraph Graph["LangGraph Agent(AGENT_ENGINE=graph)"]
G1["Coordinator 任务分类"]
G2["Knowledge 检索"]
G3["Critic 证据审查"]
G4["Task:对比 / 报告 / 申请"]
G5["KnowledgeGap 缺口记录"]
G6["Finalize 输出"]
G1 --> G2
G2 -->|证据充足| G4
G2 -->|证据不足| G3
G3 -->|改写重检索| G2
G3 -->|知识库无覆盖| G5
G3 -->|阈值偏严,可作答| G6
G4 --> G6
G5 --> G6
end
subgraph RAG["RAG 核心"]
R1["SmartTextSplitter"]
R2["Parent / Child Chunks"]
R3["ChromaDB 或 Milvus"]
R4["BM25 + 向量混合检索"]
R5["Qwen3 Reranker"]
end
subgraph UserService["Django 用户服务,端口 8001"]
U1["注册 / 登录 / JWT"]
U2["用户资料"]
U3["管理员账号管理"]
U4["文件上传"]
end
subgraph Storage["存储与依赖"]
S1["MySQL:会话、消息、知识库、文档元数据"]
S2["Redis:限流、缓存、Token 黑名单"]
S3["Ollama 或阿里云百炼"]
S4["本地模型目录:Reranker"]
end
A & B & C & D & E --> Gateway
Gateway --> P1 --> FastAPI
Gateway --> P2 --> UserService
FastAPI --> Graph
FastAPI --> RAG
FastAPI --> UserService
FastAPI --> Storage
UserService --> Storage
```
### 为什么拆分为两个服务
| 服务 | 定位 | 技术选型理由 |
|------|------|------|
| DjangoUserService | 用户/权限/部门/文件管理 | Django ORM + Admin + Celery 适合标准 CRUD |
| backend (FastAPI) | AI 对话/RAG/LangGraph Agent | 原生 async + SSE 流式 + LangChain 生态集成 |
拆分收益:
- **依赖隔离**:backend 含 torch/langchain 重依赖(镜像 ~1GB+),Django 轻量(~200MB),互不拖累
- **独立伸缩**:认证高峰扩 Django,AI 推理高峰扩 backend
- **故障隔离**:AI 服务崩溃不影响登录;Django 升级时已签发 JWT 仍有效
```text
前端 ──► DjangoUserService (:8001) ← 注册/登录/JWT 签发
└─► backend FastAPI (:8000) ← AI对话/RAG(验证 JWT,管理操作 proxy 回 Django)
```
## 技术栈
| 模块 | 技术 |
| --- | --- |
| 前端 | Vue 3、Vite 7、Vant 4、Vue Router、Pinia、vue-i18n、axios、marked、highlight.js、DOMPurify |
| RAG 后端 | FastAPI、LangChain、LangGraph、LangChain Community、LangChain Chroma、LangChain Milvus、SQLAlchemy、Pydantic、Redis |
| 用户服务 | Django 5.2、Django REST framework、Simple JWT、drf-yasg、Celery、django-redis |
| 向量与检索 | ChromaDB、Milvus、BM25、jieba、sentence-transformers、Qwen3-Reranker-0.6B |
| 模型服务 | 阿里云百炼 Qwen、Ollama、本地 reranker 模型 |
| 数据存储 | MySQL、Redis、本地 ChromaDB 文件、Milvus Docker Compose |
## 项目结构
```text
.
├── backend/ # FastAPI RAG 服务
│ ├── app/
│ │ ├── agent/ # LangChain Agent、工具和中间件
│ │ │ └── graph/ # LangGraph 图:coordinator/knowledge/task/knowledge_gap/finalize 节点
│ │ ├── tools/ # 任务工具:compare/report/form 三类 prompt 构造器
│ │ ├── cache/ # Redis 缓存装饰器
│ │ ├── config/ # rag/chroma/milvus/agent/prompt 配置
│ │ ├── core/ # 响应、限流、日志、异常处理
│ │ ├── db/ # MySQL / Redis 初始化
│ │ ├── models/ # 会话、消息、知识库、文档、分块、知识缺口表
│ │ ├── mcp/ # MCP Client:连接外部 MCP Server、发现并注册工具
│ │ ├── rag/ # 检索、向量后端、重排序、切片
│ │ ├── router/ # FastAPI 路由
│ │ ├── scheduler/ # 可选的目录监听导入任务
│ │ └── services/ # 会话、知识库、文档、父子块、知识缺口服务
│ ├── mcp_server/ # MCP Server:将核心能力暴露为 MCP 工具
│ ├── tests/ # 后端测试
│ ├── main.py # FastAPI 入口
│ ├── pyproject.toml
│ └── .env.example
├── DjangoUserService/ # Django 用户服务
│ ├── apps/user/ # 注册、登录、JWT、用户资料、管理员接口
│ ├── apps/file/ # 文件上传接口
│ ├── DjangoUserService/ # Django settings / urls / celery
│ ├── manage.py
│ ├── pyproject.toml
│ └── .env.example
├── front/ # Vue 3 前端
│ ├── src/views/ # AIChat、Sessions、KnowledgeBase、KnowledgeGaps、My 等页面
│ ├── src/components/ # TabBar 等公共组件
│ ├── src/config/api.js # 前端 API 端点
│ ├── src/router/index.js # 页面路由
│ ├── src/store/ # Pinia store
│ └── vite.config.js # Vite 代理配置(端口/代理目标支持环境变量覆盖)
├── docs/ # 设计、计划、走查 runbook、测试语料、Milvus、模型、排障等文档
│ └── test-corpus/ # 企业行政制度测试语料 + 批量上传脚本 + 测试问题清单
├── docker-compose.milvus.yml # 可选 Milvus 单机栈
└── README.md
```
## 配置说明
### FastAPI 后端环境变量
在 `backend/.env` 中配置。下面是常用项,真实密钥不要提交到仓库:
```env
# LLM:ALIYUN 或 OLLAMA
LLM_TYPE=ALIYUN
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL_NAME=qwen3.5:0.8b
ALIYUN_ACCESS_KEY_SECRET=your_api_key
ALIYUN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
CHAT_MODEL_NAME=qwen3-max
# Embedding:OLLAMA 或 ALIYUN
EMBED_MODEL_TYPE=OLLAMA
TEXT_EMBEDDING_MODEL_NAME=qwen3-embedding:0.6b
ALIYUN_EMBED_MODEL_NAME=qwen3.7-text-embedding
# MySQL
MYSQL_USER=root
MYSQL_PASSWORD=your_password
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=chat_history
# Redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_DB=0
# 向量后端:chroma 或 milvus
VECTOR_STORE_BACKEND=chroma
MILVUS_HOST=127.0.0.1
MILVUS_PORT=19530
# Django 用户服务
DJANGO_API_URL=http://127.0.0.1:8001
# Reranker
RERANKER_TYPE=LOCAL
RERANKER_MODEL_PATH=/path/to/models/Qwen3-Reranker-0.6B
# Agent 引擎:loop(默认,传统单轮)或 graph(LangGraph 多节点)
AGENT_ENGINE=loop
# Critic 证据审查(仅 graph 引擎生效)
AGENT_CRITIC_ENABLE=true
AGENT_CRITIC_MAX_REVISIONS=1
# HyDE 假设文档:默认 false(评估显示开启后平均延迟约涨 3 倍且指标无提升)
RAG_HYDE_ENABLE=false
# 知识缺口阈值:最高 relevance_score 低于此值视为「知识库无明确依据」
RAG_GAP_THRESHOLD=0.75
# JWT:必须和 DjangoUserService/.env 的 JWT_SECRET_KEY 一致
SECRET_KEY=change_me
ALGORITHM=HS256
# 可选:目录监听导入
SCHEDULER_ENABLED=false
SCHEDULER_WATCH_DIR=data/watch
SCHEDULER_INTERVAL_MINUTES=10
```
### Django 用户服务环境变量
在 `DjangoUserService/.env` 中配置:
```env
JWT_SECRET_KEY=change_me
DB_NAME=user_service
DB_USER=root
DB_PASSWORD=your_password
DB_HOST=127.0.0.1
DB_PORT=3306
CELERY_BROKER_URL=redis://127.0.0.1:6379/0
CELERY_RESULT_BACKEND=redis://127.0.0.1:6379/0
REDIS_CACHE_URL=redis://127.0.0.1:6379/1
```
注意:
- `SECRET_KEY` 和 `JWT_SECRET_KEY` 必须保持一致,否则 FastAPI 无法校验 Django 签发的 JWT。
- 建议统一使用 `127.0.0.1` 替代 `localhost`,避免 IPv6 回退延迟。
### 前端环境变量
`front/vite.config.js` 支持以下进程级环境变量覆盖(多项目并存时避开端口冲突很有用):
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `FRONT_PORT` | `3000` | 前端本地开发端口 |
| `API_TARGET` | `http://127.0.0.1:8000` | `/api` 代理到的 FastAPI 地址 |
| `USER_TARGET` | `http://127.0.0.1:8001` | `/user`、`/file` 代理到的 Django 地址 |
启动示例(后端跑在 8010 时):
```bash
API_TARGET=http://127.0.0.1:8010 npm run dev
```
## 部署与启动
以下步骤面向一台全新的部署机器,以 Linux 为例(Windows 只需将路径和虚拟环境命令替换为对应写法)。部署时请把路径、数据库账号、模型和 API Key 替换为自己的环境。
### 1. 获取代码
```bash
git clone
cd
```
Windows 下命令基本一致,只需把虚拟环境路径改为 `.venv\Scripts\python.exe`。
### 2. 准备基础环境
建议提前安装:
| 依赖 | 用途 |
| --- | --- |
| Python 3.12+ | FastAPI RAG 服务 |
| Python 3.10+ | Django 用户服务 |
| uv | Python 依赖安装与虚拟环境管理 |
| Node.js 20.19+ 或 22.12+ | Vue 3 / Vite 前端 |
| MySQL 8.x | 用户、会话、知识库和文档元数据 |
| Redis 7.x | 限流、缓存、Token 黑名单 |
| Ollama 或阿里云百炼 API Key | Embedding 与聊天模型 |
| Docker / Docker Compose | 可选,用于 Redis、MySQL、Milvus |
Redis 可以直接用 Docker 启动:
```bash
docker run -d --name redis-rag -p 6379:6379 redis:alpine
```
如果使用 Milvus 作为向量库:
```bash
docker compose -f docker-compose.milvus.yml up -d
```
如果使用 Ollama embedding:
```bash
ollama serve
ollama pull qwen3-embedding:0.6b
```
### 3. 安装项目依赖
```bash
cd backend && uv sync
cd ../DjangoUserService && uv sync
cd ../front && npm install
```
### 4. 创建数据库
MySQL 中需要创建两个数据库,名称可以按 `.env` 调整:
```sql
CREATE DATABASE chat_history CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE user_service CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
### 5. 配置环境变量
复制示例配置,并按部署机器实际信息修改(各变量含义见上方「配置说明」):
```powershell
Copy-Item backend\.env.example backend\.env
Copy-Item DjangoUserService\.env.example DjangoUserService\.env
```
重点确认:`SECRET_KEY` 与 `JWT_SECRET_KEY` 一致、数据库/Redis 地址正确、`LLM_TYPE` 和模型 Key 已填写。
### 6. 初始化数据库
```bash
cd DjangoUserService
python manage.py migrate
```
FastAPI 会在启动时自动执行 SQLAlchemy `create_all`,用于创建会话、消息、知识库、文档等表。Django 的用户表需要先执行 `migrate`。
### 7. 启动服务
需要启动三个应用服务:Django 用户服务、FastAPI RAG 服务、Vue 前端。
Django 用户服务:
```bash
cd DjangoUserService
uvicorn DjangoUserService.asgi:application --host 0.0.0.0 --port 8001
```
FastAPI RAG 服务:
```bash
cd backend
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
```
Vue 前端:
```bash
cd front
npm run dev
```
生产环境建议只暴露前端和网关端口,由 Nginx 反向代理到后端服务。
### 8. 生产部署建议
开发启动可以直接使用 `npm run dev` 和 `--reload`。生产环境建议:
1. 前端执行构建并把 `front/dist` 交给 Nginx 托管:
```bash
cd front
npm run build
```
2. Nginx 将 `/api` 代理到 FastAPI,将 `/user` 和 `/file` 代理到 Django。
3. FastAPI 使用 `uvicorn` 或进程管理器常驻运行,Django 使用 `gunicorn`、`uvicorn` 或平台服务管理器常驻运行。
4. 所有密钥、数据库密码、API Key 通过服务器环境变量或 `.env` 管理,不要提交到仓库。
5. MySQL、Redis、Milvus 和模型目录应使用持久化磁盘,避免容器重建后数据丢失。
Nginx 反向代理示例:
```nginx
server {
listen 80;
server_name your-domain.com;
root /path/to/project/front/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /user/ {
proxy_pass http://127.0.0.1:8001/user/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /file/ {
proxy_pass http://127.0.0.1:8001/file/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
### 9. 验证与访问
```bash
curl http://127.0.0.1:8000/ # 期望 {"message":"Hello World"}
curl http://127.0.0.1:8001/docs/ # Django Swagger
curl http://127.0.0.1:3000/ # 前端页面
```
| 服务 | 地址 |
| --- | --- |
| 前端应用 | `http://127.0.0.1:3000` |
| FastAPI 文档 | `http://127.0.0.1:8000/docs` |
| Django Swagger | `http://127.0.0.1:8001/docs/` |
## Docker 部署
两个后端服务均已支持 Docker 容器化部署,采用**基础镜像 + 源码覆盖**策略:依赖预装到基础镜像中,日常构建仅复制源码,秒级完成。
### 前置条件
- 已安装 Docker Desktop(Windows/macOS)或 Docker Engine + Docker Compose(Linux)
- 外部 MySQL、Redis 服务可访问(容器内通过 `host.docker.internal` 或实际 IP 访问宿主机服务)
### DjangoUserService(Django 用户服务)
Docker 文件:`Dockerfile.base`(基础镜像,含全部依赖)、`Dockerfile`(仅复制源码)、`docker-compose.yml`、`.dockerignore`、`.env.docker`
**服务组成:**
| 容器 | 说明 |
|------|------|
| `django-user-service` | Django Web 服务(uvicorn ASGI),端口 8001,启动时自动执行 `migrate` |
| `django-celery-worker` | Celery 异步任务 Worker(gevent 池,监听 celery/email 队列) |
**启动命令:**
```bash
cd DjangoUserService
# 首次部署或依赖变更时,构建基础镜像(仅需执行一次)
docker build -f Dockerfile.base -t django-base:latest .
# 日常构建并启动(秒级,只复制源码)
docker compose up -d --build
# 查看日志
docker compose logs -f web
docker compose logs -f celery-worker
# 停止服务
docker compose down
```
**关键配置(`.env.docker`):**
| 变量 | 说明 |
|------|------|
| `JWT_SECRET_KEY` | 必须与 backend 的 `SECRET_KEY` 一致 |
| `DB_*` | MySQL 连接参数 |
| `CELERY_BROKER_URL` / `CELERY_RESULT_BACKEND` | Redis 连接 URL |
| `DJANGO_DEBUG` | 生产环境设为 `false` |
| `DJANGO_ALLOWED_HOSTS` | 允许的 Host 头,默认 `*` |
**数据持久化:**
- `media_data` 卷:挂载到 `/app/media`,保存用户上传的文件
---
### backend(FastAPI RAG 服务)
Docker 文件:`Dockerfile.base`(基础镜像,含全部依赖 + CPU-only torch)、`Dockerfile`(仅复制源码)、`docker-compose.yml`、`.dockerignore`、`.env.docker`
**服务组成:**
| 容器 | 说明 |
|------|------|
| `fastapi-backend` | FastAPI RAG 服务(uvicorn),端口 8000,含健康检查 |
**启动命令:**
```bash
cd backend
# 首次部署或依赖变更时,构建基础镜像(仅需执行一次)
docker build -f Dockerfile.base -t backend-base:latest .
# 日常构建并启动(秒级,只复制源码)
docker compose up -d --build
# 查看日志
docker compose logs -f backend
# 停止服务
docker compose down
```
**关键配置(`.env.docker`):**
| 变量 | 说明 |
|------|------|
| `SECRET_KEY` | 必须与 DjangoUserService 的 `JWT_SECRET_KEY` 一致 |
| `MYSQL_*` / `REDIS_*` | 数据库和缓存连接 |
| `DJANGO_API_URL` | 容器内访问 Django 服务,默认 `http://host.docker.internal:8001` |
| `RERANKER_TYPE` | Docker 中推荐 `ALIYUN`,避免挂载本地模型 |
| `VECTOR_STORE_BACKEND` | `chroma`(嵌入式,数据存卷)或 `milvus` |
| `LLM_TYPE` / `EMBED_MODEL_TYPE` | 模型服务选择 |
**数据持久化:**
| 卷 | 挂载路径 | 说明 |
|------|------|------|
| `chroma_data` | `/app/data/chromadb` | ChromaDB 向量数据,容器重建不丢失 |
| `backend_logs` | `/log/backend` | 服务日志 |
**构建特点:**
- `torch-backend = "cpu"`(pyproject.toml 中配置):自动使用 CPU 版 torch,避免下载 ~2GB CUDA 版本
- `python-magic-bin` 仅在 Windows 安装(`sys_platform == 'win32'`),Linux 容器使用系统 `libmagic`
- 基础镜像预装全部依赖,日常构建仅复制源码,秒级完成
---
### 服务间通信
```text
┌──────────────────────────────────────────────────────┐
│ Docker Host │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ django-user-service │ │ fastapi-backend │ │
│ │ :8001 │◄──│ :8000 │ │
│ │ + celery-worker │ │ DJANGO_API_URL= │ │
│ └─────────────────────┘ │ host.docker. │ │
│ │ internal:8001 │ │
│ └─────────────────────┘ │
│ │ │
└──────────────────────────────────────┼──────────────┘
▼
外部 MySQL / Redis / 阿里云 LLM
```
- backend 容器通过 `host.docker.internal` 访问宿主机上运行的 Django 服务
- 如果两个服务都在同一 Docker 网络中,可将 `DJANGO_API_URL` 改为 Django 服务名
- 前端开发时仍通过 Vite 代理访问两个服务,无需额外配置
### 常用运维命令
```bash
# 查看容器状态
docker compose ps
# 重启单个服务
docker compose restart web # DjangoUserService
docker compose restart backend # backend
# 进入容器调试
docker exec -it django-user-service sh
docker exec -it fastapi-backend sh
# 强制重建(不使用缓存)
docker compose build --no-cache
docker compose up -d
# 查看资源占用
docker stats django-user-service fastapi-backend django-celery-worker
```
## 页面入口
| 页面 | 路由 | 说明 |
| --- | --- | --- |
| AI 问答 | `/aichat`、`/aichat/:sessionId` | 新对话、历史会话追问、流式回答、Markdown 渲染 |
| 会话管理 | `/sessions` | 查看、进入和删除历史会话 |
| 知识库 | `/knowledge` | 创建知识库、查看文档、上传文档、触发知识库问答 |
| 知识缺口 | `/knowledge-gaps` | 查看 LangGraph KnowledgeGap 节点产生的待补充条目(普通用户看自己的,管理员看全部,可改状态) |
| 我的 | `/my` | 用户信息、设置、账号管理入口、知识缺口入口 |
| 登录 / 注册 | `/login`、`/register` | 用户认证 |
| 个人资料 | `/profile` | 用户资料维护 |
| 设置 | `/settings` | 偏好设置 |
| 账号管理 | `/admin/accounts` | 管理员用户管理 |
## API 速览
### FastAPI RAG 服务
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/api/agent/query/stream` | Agent 流式问答 |
| `POST` | `/api/rag/query` | 普通 RAG 查询 |
| `GET` | `/api/session/{session_id}` | 获取单个会话历史 |
| `DELETE` | `/api/session/{session_id}` | 删除会话 |
| `POST` | `/api/session/{session_id}/messages/{message_id}/feedback` | 回答审批(批准/拒绝,拒绝可联动重新生成) |
| `GET` | `/api/sessions` | 获取当前用户会话 |
| `POST` | `/api/vector/add/single` | 上传单个文档到个人知识 |
| `POST` | `/api/vector/add/multiple` | 批量上传文档 |
| `GET` | `/api/vector/list` | 查看个人文档 |
| `DELETE` | `/api/vector/document/{doc_id}` | 删除文档 |
| `DELETE` | `/api/vector/clean` | 清空个人文档 |
| `POST` | `/api/kb` | 创建知识库 |
| `GET` | `/api/kb/list` | 查看可访问知识库 |
| `PATCH` | `/api/kb/{kb_id}` | 更新知识库 |
| `DELETE` | `/api/kb/{kb_id}` | 删除知识库 |
| `POST` | `/api/kb/{kb_id}/documents` | 上传文档到知识库 |
| `GET` | `/api/kb/{kb_id}/documents` | 查看知识库文档 |
| `POST` | `/api/kb/{kb_id}/query` | 在指定知识库内问答 |
| `GET` | `/api/knowledge-gaps` | 列出知识缺口(管理员看全部,普通用户看自己的,可按 `status` 筛选) |
| `PATCH` | `/api/knowledge-gaps/{gap_id}` | 修改知识缺口状态:`pending` / `reviewed` / `resolved` / `ignored` |
### Django 用户服务
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/user/register/` | 注册 |
| `POST` | `/user/login/` | 登录并签发 JWT |
| `POST` | `/user/logout/` | 登出并加入 Token 黑名单 |
| `POST` | `/user/refresh-token/` | 刷新 Token |
| `GET` | `/user/detail/` | 获取当前用户信息 |
| `PUT/PATCH` | `/user/update/` | 更新用户资料 |
| `GET` | `/user/list/` | 管理员获取用户列表 |
| `PATCH` | `/user/{uuid}/set-admin/` | 设置管理员权限 |
| `POST` | `/file/upload/` | 文件上传 |
## RAG 与知识库机制
### 向量后端
通过 `VECTOR_STORE_BACKEND` 选择后端:
- `chroma`:默认嵌入式模式,数据保存在 `backend/data/chromadb`。
- `milvus`:独立向量服务,依赖 `docker-compose.milvus.yml` 中的 etcd、MinIO、Milvus。
### 文档处理
1. 上传文件后写入文档记录表,记录 `doc_id`、`user_id`、`kb_id`、文件名、MD5、大小和分块数。
2. 文本按语言智能分块,生成父块和子块。
3. 子块写入向量库,同时保存到 MySQL,供 BM25 构建索引。
4. 同名或相同 MD5 的文档按用户和知识库范围隔离,避免跨用户误判。
### 检索流程
1. 根据用户和知识库权限过滤可访问内容。
2. 向量检索召回语义相似子块。
3. BM25 召回关键词相关子块。
4. 合并召回结果并映射到父块上下文。
5. 使用 reranker 精排。
6. 将上下文交给 LLM 生成回答,并返回引用来源。
### 知识库权限
知识库支持 `personal`、`dept`、`company` 范围。成员权限分为:
- `viewer`:可检索。
- `editor`:可上传和删除文档。
- `admin`:可管理成员和删除知识库。
## LangGraph Agent 引擎
将后端环境变量切到 `AGENT_ENGINE=graph` 后,`/api/agent/query/stream` 不再走传统 loop,而是走 `backend/app/agent/graph/` 下的多节点图:
```text
START → coordinator → (knowledge | finalize)
knowledge → 证据充足 → (task | knowledge_gap | finalize)
→ 证据不足 → critic
critic → (knowledge 改写重检索 | knowledge_gap | task | finalize)
knowledge_gap → finalize
task → finalize → END
```
critic 只在 knowledge 判定证据不足(最高相关度低于 `RAG_GAP_THRESHOLD`)时介入,不是每次检索都跑。`AGENT_CRITIC_ENABLE=false` 可摘掉 critic 节点、退化回单向流水线(紧急回退用);改写重检索的次数上限由 `AGENT_CRITIC_MAX_REVISIONS` 控制(默认 1,硬上限 5,避免逼近 LangGraph 的 recursion_limit)。
各节点职责:
| 节点 | 作用 |
| --- | --- |
| `coordinator` | LLM 把用户问题归类到 `knowledge_qa` / `document_compare` / `report_generation` / `document_generation` / `knowledge_gap` 之一,并初始化执行计划 |
| `knowledge` | 命中知识库的混合检索 + reranker,把候选文档塞进 state |
| `critic` | 证据不足时介入,LLM 判定三类结果:`relevant`(阈值偏严,证据够用,直接作答)/ `needs_rewrite`(问题表述或指代导致召回不准,改写查询回 knowledge 重检索)/ `out_of_scope`(知识库确实无覆盖,转 knowledge_gap);自身异常时降级放行,不阻塞主路径 |
| `task` | 当任务类型属于「对比 / 报告 / 申请」时,调用 `app/tools/` 下对应的 prompt 构造器(`compare_tool` / `report_tool` / `form_tool`)让 LLM 产出结构化结果 |
| `knowledge_gap` | 检索结果空或最高 `relevance_score` 低于阈值时,LLM 抽出未覆盖的知识点,去重过滤后写入 `knowledge_gaps` 表,并把"已记录待补充条目"写回 task_messages |
| `finalize` | 汇总 task_messages / knowledge 上下文 / 引用,生成最终回答;同时发送 token 用量事件 |
每个节点执行时都会通过 GraphRunner 把"识别任务类型 / 检索知识 / 审查证据 / 生成对比 / 记录知识缺口 / 生成回答"这类步骤事件 push 到前端,前端聊天页的「步骤」区会逐条显示。
知识缺口落库后会出现在 `/knowledge-gaps` 页:管理员看全部、普通用户只看自己产生的,状态可改 `pending` → `reviewed` / `resolved` / `ignored`。
> 实测语料、走查命令和典型问题清单见 [docs/test-corpus/README.md](./docs/test-corpus/README.md) 与 [docs/walkthroughs/p5b-knowledge-gap-walkthrough.md](./docs/walkthroughs/p5b-knowledge-gap-walkthrough.md)。
### Agent 流式调用日志示例
以下是一次完整的 Agent 流式问答日志(查询"桂林旅游攻略 景点推荐 玩法"),展示了从用户提问到最终回答的端到端执行链路:
**各阶段说明:**
| 阶段 | 日志标识 | 说明 |
|------|---------|------|
| Agent 启动 | `[Agent] 启动,消息数:6` | 加载 system prompt + 历史消息 + 当前 query |
| LLM 第一轮决策 | `stage=model_turn round=1 tool_calls=1` | 模型判断需要调用 RAG 工具检索知识库 |
| 权限过滤 | `stage=build_filter` | 按用户身份构建向量库/BM25 过滤条件(部门隔离) |
| HyDE 跳过 | `RAG_HYDE_ENABLE=false` | 假设文档生成默认关闭,直接用原 query 检索 |
| 检索器初始化 | `stage=retriever_init duration=0.000s` | BM25 缓存命中,无需重建索引 |
| 子块检索 | `stage=child_retrieve child_count=6` | 向量 + BM25 混合检索,召回 6 个子块 |
| 父块扩展 | `stage=parent_fetch requested=3 found=3` | 6 个子块去重映射到 3 个父块(完整上下文) |
| Reranker 精排 | `stage=agent_context_rerank` | 阿里云 reranker 对 3 个文档精排打分 |
| 缺口判定 | `max_score=0.7941 > 0.75` | 最高分超过阈值,知识库信息充足 |
| LLM 第二轮生成 | `stage=model_turn round=2 content_chars=558` | 基于检索片段生成最终回答 |
| 流式完成 | `[Agent流式] 完成` | 汇总:2 轮 LLM、1 次工具调用、3 个引用、5134 tokens |
**性能指标解读:**
- `first_chunk=1.291s`:LLM 首 token 延迟(思考时间)
- `duration=1.928s`:RAG 工具总耗时(检索 + 重排)
- `tokens=5134`:本次请求总 token 消耗(含输入 + 输出)
- `citations=3`:返回给前端的结构化来源引用数
### Agent 多节点调用日志示例
以下是 `AGENT_ENGINE=graph` 模式下,LangGraph 多节点图的完整执行流程(查询触发 coordinator → knowledge → critic → finalize 链路):
**各节点执行说明:**
| 节点 | 日志标识 | 说明 |
|------|---------|------|
| 协调器 | `[Coordinator]` | LLM 结构化输出判定任务类型(如 `knowledge_qa`),生成执行计划 |
| 知识检索 | `[Knowledge]` | 混合检索(向量 + BM25)+ 父块扩展 + reranker 精排 |
| 质量审查 | `[Critic]` | 评估检索结果是否充分、回答是否忠实,不充分则改写 query 重检索 |
| 知识缺口 | `[KnowledgeGap]` | `max_score < 0.75` 时触发,提取未覆盖知识点写入数据库 |
| 最终生成 | `[Finalize]` | 汇总上下文 + 引用,流式生成最终回答并统计 token 用量 |
与上方单循环(`AGENT_ENGINE=loop`)的区别:graph 模式由 coordinator 显式规划、critic 闭环审查,适合需要多步推理和质量保障的复杂查询;loop 模式则更轻量,适合简单问答。
## MCP 协议集成
项目实现了 MCP(Model Context Protocol)的双向集成:既作为 **Server** 对外暴露核心能力,也作为 **Client** 动态接入外部工具。
### MCP Server(对外暴露能力)
将知识库检索等核心 RAG 能力封装为标准 MCP 工具,供 Claude Desktop、Cursor、其他 Agent 等外部 AI 客户端调用。
**暴露的工具:**
| 工具名 | 功能 |
|--------|------|
| `knowledge_base_search` | 知识库语义检索(向量 + BM25 + CrossEncoder 重排序),返回 Top-K 相关文档 |
| `document_rerank` | 对候选文档按查询相关性精排 |
| `list_knowledge_bases` | 列出系统中所有知识库及基本信息 |
| `get_current_time` | 获取当前服务器时间 |
**启动方式:**
```bash
cd backend
# stdio 模式(本地 MCP Client 以子进程方式调用)
python -m mcp_server
# SSE 模式(远程 HTTP 访问)
python -m mcp_server --transport sse --port 9000
```
**外部客户端配置示例(Claude Desktop / Cursor):**
```json
{
"mcpServers": {
"enterprise-kb": {
"command": "python",
"args": ["-m", "mcp_server"],
"cwd": "/path/to/backend"
}
}
}
```
SSE 模式下,外部 Agent 通过 `http://:9000/sse` 端点连接并调用工具。
### MCP Client(接入外部工具)
Agent 支持在启动时连接配置的外部 MCP Server,自动发现工具并注册到可用工具列表中,LLM 可通过 function calling 直接调用。
**配置方式(`backend/.env`):**
```env
# 方式一:内联 JSON
MCP_SERVERS_JSON={"servers":[{"name":"filesystem","transport":"stdio","command":"npx","args":["-y","@modelcontextprotocol/server-filesystem","/tmp"]}]}
# 方式二:配置文件路径
MCP_CONFIG_FILE=./mcp_servers.json
```
**配置文件格式:**
```json
{
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
},
{
"name": "remote-tools",
"transport": "sse",
"url": "http://localhost:9000/sse"
}
]
}
```
未配置时模块静默跳过,不影响现有功能。外部工具名自动添加 `mcp_{server}_{tool}` 前缀,避免与内置工具冲突。
## 评估体系
项目不止于把功能搭起来,还配了一套**配置驱动、可重复、支持消融对比**的评估管线(代码在 [`backend/eval/`](./backend/eval/)),用数据量化每个机制的真实收益——也就是「评估驱动迭代」而不是「凭感觉调参」。
**三层指标**,每层有独立的 ground truth:
| 层 | 指标 | 标尺 |
| --- | --- | --- |
| 检索 | recall@1 / recall@3 / MRR | 标注的 `expected_doc` |
| 回答 | 事实断言通过率 / grounding 忠实度 / rubric 覆盖率 | `answer_assertions`、引用文档 |
| 编排 | 路由准确率 / 缺口精确率·召回率 | `expected_route`、`expect_gap_triggered` |
**消融对比**(约 100 题人工标注集,子进程隔离注入开关,样本偏小、定位为指示性):
| 配置 | 缺口精确率 | rubric 覆盖率 | 忠实度 | 平均延迟 |
| --- | --- | --- | --- | --- |
| baseline | 0.50 | 0.55 | 0.52 | 6.6s |
| **+critic** | **0.87** | **0.69** | **0.65** | 7.5s |
| +hyde | 0.52 | 0.59 | 0.51 | 18.9s |
> 检索层 recall@1=0.881、MRR=0.933、事实断言通过率 0.99。
**几个用数据做的工程决策**:
- **critic 自我修正闭环有效**:把知识缺口判定精确率从 0.50 提到 0.87、rubric 覆盖率 0.55→0.69、忠实度 0.52→0.65;
- **HyDE 默认关,是算出来的**:开启后平均延迟从 6.6s 涨到 18.9s(约 3 倍),但检索和回答指标反而没涨,故代码默认关、保留 `RAG_HYDE_ENABLE` 作为可配开关;
- **LLM-judge 可信度**:judge 用千问(与被测的 DeepSeek 跨家族)规避自评偏袒,配合 temperature=0、逐点 rubric 核对、人工校准一致性。
一条命令(`python -m eval.runner`)跑完整个配置矩阵,产出三层指标对比表 + 成本表。
## 测试与构建
```bash
# 前端生产构建
cd front && npm run build
# 后端测试
cd backend && python -m pytest
# Django 检查
cd DjangoUserService && python manage.py check
```
## 常见问题
### 前端改了但浏览器没变化
优先检查 Vite 开发服务器的工作目录是否指向当前项目的 `front/`。路径错了就停掉旧进程,从正确目录重新启动。
### FastAPI 调 Django 超时
本机代理可能拦截了 `127.0.0.1` 请求。启动服务时清空代理环境变量:
```bash
# Linux / macOS
unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy
# Windows PowerShell
$env:HTTP_PROXY=''; $env:HTTPS_PROXY=''; $env:http_proxy=''; $env:https_proxy=''
```
### 知识库接口返回 401
需要先登录并让前端携带 `Authorization: Bearer `。FastAPI 会调用 Django `/user/detail/` 校验用户身份。
### Redis 连接失败
确认 Redis 服务已启动且端口可达:
```bash
redis-cli ping # 期望返回 PONG
```
### Reranker 首次启动慢
首次启动会检查或下载 Qwen3-Reranker-0.6B。确认 `RERANKER_MODEL_PATH` 指向可写且空间充足的目录。
## 相关文档
- [FastAPI API 文档](./backend/api.md)
- [Django 用户服务 API](./DjangoUserService/api.md)
- [前端 API 说明](./front/api.md)
- [测试语料与一键灌库脚本](./docs/test-corpus/README.md)
- [知识缺口走查 runbook](./docs/walkthroughs/p5b-knowledge-gap-walkthrough.md)
- [设计文档](./docs/design/)
- [实现计划](./docs/plans/)
- [ModelScope 模型说明](./docs/modelscope_model.md)
- [Milvus 迁移说明](./docs/migrate-to-milvus.md)
- [故障排除](./docs/troubleshooting.md)