一个支持多用户的知识图谱系统,整合 Neo4j 图数据库和 ChromaDB 向量数据库,实现文档知识管理、可视化图谱与 RAG 对话。 让散落各处的知识连成一张可探索的网 ✨
🚨 本项目的
.env(根目录与backend/)包含真实 API 密钥和 JWT 签名密钥。任何 fork、镜像或截图前必须:
- 在硅基流动 / Moonshot / 阿里云百炼控制台**轮换(撤销并重新签发)**这些 API 密钥
- 用
python -c "import secrets; print(secrets.token_urlsafe(48))"生成新JWT_SECRET并替换- 🙅 永远不要把
.env提交到 git(已在.gitignore中,但仍请小心处理历史记录)
详见 安全配置 一节。
- 📚 文档知识库:支持上传 PDF/Word/TXT/MD 格式,自动转换为 Markdown 并按层级切块
- 🔍 混合检索:多查询 + 硅基流动 Qwen3-Embedding-8B 向量检索 + BM25 关键词 + 图谱通道 + RRF 融合 + Qwen3-Reranker 重排序 + 父文档扩展再重排(统一管线
services/retriever.py,TTL+LRU 缓存) - 🕸️ 知识图谱可视化:d3 + Vue 3 实现的交互式力导向图谱,支持节点拖拽、实体编辑、合并与删除
- 💬 大模型对话:基于 Kimi / 百炼 (qwen3.7-flash) 的 RAG 问答,支持流式 / 非流式、图谱增强 RAG、对比模式、消息反馈、深度思考开关(Qwen 混合思考,推理过程以
event: thinking帧流式展示)、意图路由(闲聊 / 拒答绕过检索,分类失败回退到 RAG) - 📊 仪表盘与时间线:文档 / 实体 / 标签统计、月度增长、近期活动、实体首现时间线
- 🗺️ 文档聚类地图:2D PCA 投影可视化所有文档的语义分布
- 🔐 用户隔离:JWT 账号密码认证,SQLite 存储用户数据,Neo4j/ChromaDB 通过
user_id标签隔离 - 🛡️ 健壮性:统一 logging(请求级
X-Request-ID关联)、请求体大小全局兜底(413)、批量写入(Neo4j UNWIND)、输入校验、4xx 不重试、防 401 重定向循环、API 限流中间件、embedding 缓存自愈(损坏 blob 自动剔除)、BM25 启动预热、卡死文档启动对账(reconcile)、检索结果 TTL+LRU 缓存、向量索引零成本重建脚本、SQLite 增量迁移(schema_version追踪)、全量备份脚本
| 层级 | 技术 |
|---|---|
| 🎨 前端 | Vue 3.5 + Vite 7 + Pinia + Vue Router + d3.js · 学术雅致派双主题(Fraunces + Plus Jakarta Sans + JetBrains Mono) |
| ⚙️ 后端 API | FastAPI 0.115 + Python 3.11 + uvicorn |
| 🕸️ 图数据库 | Neo4j 5.14 (Docker, APOC 插件) |
| 🧬 向量数据库 | ChromaDB 0.4.18 (Docker) |
| 💾 用户数据 | SQLite + SQLAlchemy 2.0 + aiosqlite (单文件) |
| 🧩 嵌入模型 | 硅基流动 Qwen3-Embedding-8B (API) |
| 🎯 重排序 | 硅基流动 Qwen3-Reranker-8B (API) |
| 🤖 大模型 | Kimi API (Moonshot, kimi-k2) / 百炼 qwen3.7-flash (阿里云 DashScope) / 硅基流动 Qwen3-8B |
| 🔑 密码哈希 | bcrypt 4.1.3(原生) |
| 📝 日志 | Python logging + RotatingFileHandler(统一在 app/logger.py) |
D:/NC/
├── docker-compose.yml # Neo4j + ChromaDB 服务定义
├── .env / .env.example # 部署环境变量(example 为模板)
├── package.json # 根级 npm 脚本(concurrently 一键启动)
├── start-dev.bat / start-dev.sh # Windows / Bash 一键启动脚本
├── backend/
│ ├── .env / .env.example # 后端实际加载的 .env
│ ├── app/
│ │ ├── api/ # API 端点
│ │ │ ├── auth.py # 注册/登录/me
│ │ │ ├── documents.py # 文档上传/列表/详情/删除/切块/标签/聚类
│ │ │ ├── chat.py # RAG 对话(流式 + 非流式 + 反馈)
│ │ │ ├── graph.py # 实体/图谱查询/可视化/编辑/合并
│ │ │ ├── search.py # 语义检索
│ │ │ ├── progress.py # 文档处理进度(SSE + 历史)
│ │ │ ├── tags.py # 用户级标签聚合
│ │ │ ├── timeline.py # 时间线聚合数据
│ │ │ └── dashboard.py # 仪表盘汇总
│ │ ├── models/ # Pydantic 数据模型(含字段校验)
│ │ ├── services/ # 核心服务
│ │ │ ├── retriever.py # 统一检索管线(多查询 + 图谱 + 父文档扩展 + 再重排 + TTL 缓存)
│ │ │ ├── intent.py # 查询意图分类(fact_retrieval / chitchat / should_reject)
│ │ │ ├── embedding.py # 硅基流动嵌入(限流 + JSON 缓存)
│ │ │ ├── llm.py # 百炼 / Kimi / 硅基流动 多 LLM
│ │ │ ├── chunker.py # Markdown 层级切块(可配 overlap)
│ │ │ ├── entity_extractor.py # 实体 + 关系提取(LLM 模式)
│ │ │ ├── neo4j_client.py # Neo4j 封装(含 UNWIND 批量)
│ │ │ ├── chroma_client.py # ChromaDB 封装
│ │ │ ├── bm25.py # BM25 关键词检索(per-user 索引 + 启动预热)
│ │ │ ├── fusion.py # RRF / 加权融合
│ │ │ ├── reranker.py # 硅基流动 Rerank
│ │ │ ├── query_processor.py # 查询改写 / 变体 / 实体抽取
│ │ │ ├── reconcile.py # 卡死文档启动对账(标记 failed)
│ │ │ ├── progress_tracker.py # SSE 进度跟踪
│ │ │ └── doc_status.py # 文档处理状态机(pending/document_created/indexed/graphed/ready/failed)
│ │ ├── auth/ # JWT 鉴权 + bcrypt 密码哈希 + 限流中间件
│ │ ├── middleware.py # 纯 ASGI 中间件(请求体限流 + X-Request-ID)
│ │ ├── utils/md_parser.py # Markdown 解析(markitdown 防御性封装)
│ │ ├── config.py # 配置管理(含 CORS 白名单 + JWT 占位符拦截)
│ │ ├── database.py # SQLite 初始化 + 增量迁移(schema_version 追踪)
│ │ ├── logger.py # 统一 logging 配置(请求 ID 关联)
│ │ └── main.py # FastAPI 入口(lifespan:预热 / 对账 / 就绪探针)
│ ├── migrations/001_baseline.sql # 迁移基线(stamp version 1,未来增量迁移按序应用)
│ ├── scripts/rebuild_chroma.py # 向量索引零成本重建(SQLite chunks + embedding 缓存 → Chroma)
│ ├── clean_user_data.py # 跨 SQLite/Chroma/Neo4j/BM25 清理单个用户数据(破坏性,需确认)
│ └── Dockerfile # 从仓库根构建:docker build -f backend/Dockerfile .
├── requirements.txt # Python 依赖清单(backend 与脚本共用,CI 也从根目录安装)
├── docker-compose.yml # Neo4j + ChromaDB 服务定义
├── frontend/ # Vue 3 + d3 前端(学术雅致派双主题)
│ ├── src/
│ │ ├── components/
│ │ │ ├── GraphPanel.vue # 图谱核心面板(d3 力导向)
│ │ │ ├── layout/ # Layout / Sidebar
│ │ │ ├── decor/ # 装饰层(Decor / shapes)
│ │ │ └── ui/ # 通用 UI(Button/Card/Tag/Stat/Switch/...)
│ │ ├── views/
│ │ │ ├── Home.vue # 登录 / 注册
│ │ │ ├── DocumentsPage.vue # 文档列表 + 上传
│ │ │ ├── DocumentDetailPage.vue # 文档详情(标签 / 实体 / 关联)
│ │ │ ├── ClusterMapPage.vue # 2D PCA 聚类地图
│ │ │ ├── GraphPage.vue # 图谱主页(搜索 / 可视化)
│ │ │ ├── EntityDetailPage.vue # 实体详情页
│ │ │ ├── EntityTimelineAnimationPage.vue # 实体时间线动画
│ │ │ ├── DashboardPage.vue # 仪表盘
│ │ │ ├── TimelinePage.vue # 时间线
│ │ │ ├── ChatPage.vue # RAG 对话
│ │ │ └── SearchPage.vue # 语义检索
│ │ ├── composables/
│ │ │ └── useTheme.js # 亮/暗主题切换 + localStorage 持久化
│ │ ├── api/ # axios 客户端封装(auth/documents/chat/graph/...)
│ │ ├── router/index.js # 路由 + 鉴权守卫
│ │ ├── store/auth.js # Pinia auth store
│ │ ├── utils/ # categorize / timelineAnim / SSE 流式解析 工具
│ │ ├── styles/variables.css # 学术雅致派设计令牌(墨蓝+琥珀双主题)
│ │ ├── App.vue
│ │ └── main.js
│ ├── index.html
│ ├── package.json
│ └── vite.config.js
├── scripts/ # 根级脚本
│ ├── run-backend.mjs # npm run backend 调用(跨平台启动 uvicorn)
│ └── backup.sh # 全量备份(SQLite 在线 .backup + Neo4j/Chroma/uploads)
└── data/ # 数据目录 (gitignore)
├── sqlite/ # SQLite 数据库
├── uploads/ # 上传的原文件
├── logs/ # 应用日志(RotatingFileHandler)
├── neo4j/ # Neo4j 数据卷
└── chromadb/ # ChromaDB 数据卷
docker-compose up -d这将启动:
- 🕸️ Neo4j: http://localhost:7474 (浏览器界面), bolt://localhost:7687
- 🧬 ChromaDB: http://localhost:8000
# 根目录(用于 IDE / 工具)
cp .env.example .env
# 后端实际加载的 .env
cd backend
cp .env.example .env必填项:
SILICON_FLOW_API_KEY— 硅基流动 API 密钥(用于 Embedding + Rerank + 备用 LLM)BAILIAN_API_KEY— 阿里云百炼 API 密钥(用于 LLM)KIMI_API_KEY— Moonshot Kimi API 密钥(备用 LLM)JWT_SECRET— 生产环境必须用python -c "import secrets; print(secrets.token_urlsafe(48))"生成
可选调整:
CORS_ALLOWED_ORIGINS— 逗号分隔的允许来源(默认http://localhost:5173)APP_ENV—development(默认)或production(生产环境会拒绝默认 JWT_SECRET 启动)UPLOAD_DIR/SQLITE_PATH/LOG_DIR— 数据与日志目录ENABLE_LLM_EXTRACTION/USE_RULE_EXTRACTION— 实体提取策略(默认纯 LLM 模式)ENTITY_BATCH_SIZE— 实体提取批大小(默认 200)
# 推荐:使用根目录 .venv(依赖清单在仓库根 requirements.txt)
cd ..
.venv/Scripts/python.exe -m pip install -r requirements.txt
.venv/Scripts/python.exe -m spacy download zh_core_web_smcd backend
../.venv/Scripts/python.exe -m uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload或使用一键启动脚本(Windows / Bash 双版本 / 根级 npm 脚本):
# Windows
start-dev.bat
# Bash (Git Bash / WSL)
./start-dev.sh
# 或:根目录 npm 脚本(需要先 npm install)
npm run dev # concurrently 同时启动前后端
npm run backend # 仅后端
npm run frontend # 仅前端API 文档地址:http://localhost:8001/docs 健康检查: http://localhost:8001/health
cd frontend
npm install
npm run dev前端访问地址:http://localhost:5173
Vite 已配置 /api 代理到 http://localhost:8001。
所有需要鉴权的接口都要求 Authorization: Bearer <jwt> header。SSE 进度接口优先使用 header 鉴权;但由于原生 EventSource 客户端无法设置自定义 header,作为兼容回退也接受 ?token= 查询参数。EventSource 即使用此回退方式)。
POST /api/auth/register— 用户注册(用户名 3-50 字符[A-Za-z0-9_.-]、密码 ≥ 8 字符且必须含字母+数字)POST /api/auth/login— 用户登录(OAuth2 form-data,返回 JWT)GET /api/auth/me— 获取当前用户信息
POST /api/documents/upload— 上传文档(multipart/form-data,支持 .pdf/.docx/.doc/.txt/.md/.markdown,≤ 10MB)GET /api/documents— 列出用户文档(分页:?skip=0&limit=100;可选?tag=xxx过滤)GET /api/documents/{id}/detail— 文档详情(metadata + 标签 + 切块统计 + 关键实体 + 关联文档)GET /api/documents/{id}/chunks— 文档切块列表GET /api/documents/cluster-map— 2D PCA 聚类地图(所有文档的语义投影)DELETE /api/documents/{id}— 删除文档(联动清理 SQLite + ChromaDB + Neo4j)GET /api/documents/{id}/tags— 文档标签列表POST /api/documents/{id}/tags— 添加文档标签(幂等,返回最新标签列表)DELETE /api/documents/{id}/tags/{tag:path}— 移除文档标签(返回最新标签列表)
POST /api/search— 语义检索(向量 + BM25 + Rerank + 图谱关联)
GET /api/graph/entities?query=xxx— 实体名称模糊搜索POST /api/graph/query— 语义图谱查询(按query在向量空间找相关 chunk,再展开图谱)GET /api/graph/visualization— 获取当前用户的全量图谱可视化数据GET /api/graph/entities/{name:path}/detail— 实体详情(实体 + 统计 + 文档 + 关联实体 + 示例 chunk)PATCH /api/graph/entities/{name:path}— 更新实体的类型 / 描述DELETE /api/graph/entities/{name:path}— 删除实体(清理 MENTIONS / RELATES_TO)POST /api/graph/entities/merge— 合并实体({source, target},source 被删除并重新指向 target)
POST /api/chat— 发送消息(非流式 RAG 问答,支持use_graph_rag/compare_mode/enable_thinking;意图路由自动判定是否检索)POST /api/chat/stream— 发送消息(Server-Sent Events 流式)
流式帧顺序:
event: sources(参考来源)→event: thinking(仅当enable_thinking=true,模型推理过程,可折叠)→ 默认data:帧(回答正文 chunk)→event: done(终止)。 深度思考模式:enable_thinking透传 Qwen 混合思考参数,模型先流式输出reasoning_content再输出正文;对不支持该参数的模型层,HTTP 400 时自动去参重试一次。首字计时锚定到正文首 token,非推理首 token。
GET /api/chat/conversations— 获取对话列表GET /api/chat/conversations/{id}/messages— 获取对话历史DELETE /api/chat/conversations/{id}— 删除对话POST /api/chat/messages/{id}/feedback— 提交消息反馈({rating, note?})GET /api/chat/messages/{id}/feedback— 获取消息反馈DELETE /api/chat/messages/{id}/feedback— 删除消息反馈
GET /api/tags?q=xxx— 用户级标签聚合(按使用频次倒排,可选模糊搜索)
GET /api/timeline— 文档月度分布 + 近期文档 + 实体首现时间线
GET /api/dashboard/summary— 仪表盘汇总(统计 + 近期活动 + 热门实体 + 热门标签 + 月度增长)
GET /api/progress/{doc_id}— SSE 流式进度事件(30 秒 keepalive,完成/错误自动关闭)GET /api/progress/{doc_id}/history— 历史进度事件列表
GET /health— 存活探针(liveness),返回{"status": "healthy"}GET /health/ready— 就绪探针(readiness),逐个 ping SQLite / ChromaDB / Neo4j,任一核心存储不可用返回 503GET /— 返回 API 元信息
PDF/Word/TXT/MD → markitdown → Markdown → 层级解析 → 语义切块
→ 硅基流动 Embedding (Qwen3-Embedding-8B) → ChromaDB 存储
→ Neo4j 实体关系提取 (BM25 索引同步) → SSE 进度推送
用户 Query
├─ 意图分类 (LLM, ENABLE_INTENT_ROUTING)
│ ├─ chitchat / should_reject → 跳过检索,模板直接应答
│ └─ fact_retrieval (默认 / 失败回退) → 进入统一检索管线 retrieve()
│
└─ retrieve()(services/retriever.py,结果按 TTL+LRU 缓存)
1. 并行 LLM 预处理:会话改写 + 多查询变体(+ 图谱实体抽取)
2. 并行 Embedding(逐文本缓存)
3. 逐查询 向量 + BM25 召回(multi-query)
4. 图谱通道作为独立 RRF 列表(GRAPH_RAG_MODE: auto / on / off)
5. 多列表 RRF 融合(图谱权重 GRAPH_RRF_WEIGHT 可配)
6. Qwen3-Reranker → seed chunks
7. 扩展:前后邻居 (Chroma) + 父文档同节兄弟 (SQLite),去重
8. 扩展集再重排(按 relevance 排序,不驱逐邻居)
9. 实体 / 关系富化 (Neo4j)
→ 构建 Prompt → Kimi / 百炼 LLM → 流式返回结果
├─ enable_thinking=true: 先流式 reasoning_content (event: thinking)
└─ 默认: 直接流式正文 (data: chunk)
{
"chunk_id": "uuid",
"document_id": "doc_uuid",
"user_id": "user_uuid",
"content": "文本内容",
"hierarchy": {
"level": 2, # 标题层级
"path": ["标题1", "标题1.1"], # 层级路径
"parent_id": "parent_chunk_id"
},
"position": {
"start_line": 10,
"end_line": 25,
"prev_chunk_id": "uuid", # 前一块(用于上下文召回)
"next_chunk_id": "uuid" # 后一块
}
}// 节点
(:User {user_id, username, password_hash, created_at})
(:Document {doc_id, title, user_id, file_path, created_at})
(:Chunk {chunk_id, content, embedding_id, user_id, position, hierarchy_path})
(:Entity {name, type, description, user_id}) // 从文本提取
// 关系
(:User)-[:OWNS]->(:Document)
(:Document)-[:CONTAINS]->(:Chunk)
(:Chunk)-[:NEXT]->(:Chunk) // 文档顺序
(:Chunk)-[:MENTIONS]->(:Entity) // 提及实体
(:Entity)-[:RELATES_TO {relation_type}]->(:Entity) // 实体关系| 项 | 状态 | 备注 |
|---|---|---|
| 🔑 JWT 签名 | 必须替换 | python -c "import secrets; print(secrets.token_urlsafe(48))" |
| 🌐 CORS 来源 | 白名单 | 通过 CORS_ALLOWED_ORIGINS 配置,禁用通配符 |
| 🔒 密码哈希 | bcrypt | 72 字节硬截断;不 mutate 调用方入参 |
| 🗝️ API 密钥 | 环境变量 | 勿硬编码到代码;.env 已 gitignore |
| 🚦 JWT 占位符 | 任意环境拦截 | get_settings() 在任何环境(含 development)检测到公开占位符即 RuntimeError;APP_ENV 仅为环境标记 |
| 🚪 401 处理 | 拦截器去重 | 防重入 + 派发 auth:logout 事件 |
| 📡 进度 SSE | Authorization header 优先 | EventSource 回退接受 ?token=(会进代理日志,视为敏感 URL) |
| 💾 嵌入缓存 | JSON 序列化 | 取代 pickle(防反序列化漏洞);损坏 blob 自愈剔除 |
| 🚦 API 限流 | 中间件 | app/auth/rate_limit.py 对认证接口限流,防暴力枚举 |
| 📦 请求体大小 | 全局兜底 | RequestBodyLimitMiddleware 按 MAX_REQUEST_BODY 在消费 body 前拒绝超大请求(413);上传端点另按 MAX_FILE_SIZE 流式校验 |
| 🆔 请求追踪 | correlation id | RequestIDMiddleware 为每个请求打 X-Request-ID,回写响应头并注入日志 |
| ✅ 注册校验 | 强校验 | 用户名 [A-Za-z0-9_.-]、密码 ≥ 8 字符含字母+数字 |
| 👥 Neo4j 删除 | 跨用户隔离 | delete_document step 4 强制 user_id 过滤 |
| ⚡ 批量写入 | UNWIND | 实体/关系/MENTIONS 由 N 次往返降为 1 次 |
| 变量名 | 说明 | 默认值 | 必填 |
|---|---|---|---|
APP_ENV |
运行环境 | development |
否 |
NEO4J_URI |
Neo4j 连接地址 | bolt://localhost:7687 |
否 |
NEO4J_USER |
Neo4j 用户名 | neo4j |
否 |
NEO4J_PASSWORD |
Neo4j 密码 | 12345678 |
否 |
CHROMA_HOST / CHROMA_PORT |
ChromaDB 主机端口 | localhost / 8000 |
否 |
SQLITE_PATH |
SQLite 数据库路径 | ./data/sqlite/app.db |
否 |
SILICON_FLOW_API_KEY |
硅基流动 API 密钥(Embedding + Rerank + 备用 LLM) | - | 是 |
SILICON_FLOW_BASE_URL |
硅基流动 base URL | https://api.siliconflow.cn/v1 |
否 |
KIMI_API_KEY |
Moonshot Kimi 密钥(备用 LLM) | - | 否 |
KIMI_BASE_URL |
Moonshot base URL | https://api.moonshot.cn/v1 |
否 |
BAILIAN_API_KEY |
阿里云百炼 LLM 密钥 | - | 是 |
BAILIAN_BASE_URL |
百炼 base URL | https://dashscope.aliyuncs.com/compatible-mode/v1 |
否 |
BAILIAN_MODEL |
百炼模型 | qwen3.7-flash |
否 |
LLM_MODEL_KIMI |
Kimi 模型 | kimi-k2-0905-preview |
否 |
LLM_MODEL_SILICON |
硅基流动模型 | Qwen/Qwen3-8B-Instruct |
否 |
JWT_SECRET |
JWT 签名密钥 | 占位符(生产必须替换) | 是 |
JWT_ALGORITHM |
JWT 算法 | HS256 |
否 |
ACCESS_TOKEN_EXPIRE_MINUTES |
Token 有效期(分钟) | 60 |
否 |
UPLOAD_DIR |
上传文件目录 | ./data/uploads |
否 |
MAX_FILE_SIZE |
最大文件大小(字节) | 10485760(10MB) |
否 |
EMBEDDING_MODEL |
嵌入模型名 | Qwen/Qwen3-Embedding-8B |
否 |
EMBEDDING_DIM |
嵌入维度 | 1024 |
否 |
RERANK_MODEL |
Rerank 模型 | Qwen/Qwen3-Reranker-8B |
否 |
CORS_ALLOWED_ORIGINS |
允许的 CORS 来源(逗号分隔) | localhost 开发地址 | 否 |
MAX_REQUEST_BODY |
全局请求体大小上限(字节) | 15728640(15MB) |
否 |
ENABLE_INTENT_ROUTING |
启用查询意图路由(闲聊/拒答绕过检索) | True |
否 |
INTENT_CLASSIFY_TIMEOUT |
意图分类超时(秒) | 3.0 |
否 |
GRAPH_RAG_MODE |
图谱 RAG 模式(auto/on/off) | auto |
否 |
MULTI_QUERY_NUM_VARIANTS |
多查询变体数 | 3 |
否 |
QUERY_REWRITE_MIN_LEN |
触发查询改写的最短长度(字符) | 20 |
否 |
RERANK_RECALL_K |
每路召回送入 RRF/重排的候选数 | 25 |
否 |
GRAPH_RRF_WEIGHT |
图谱通道 RRF 权重 | 1.0 |
否 |
ENABLE_EXPANSION_RERERANK |
扩展集再重排 | True |
否 |
RETRIEVAL_CACHE_TTL |
检索结果缓存 TTL(秒) | 300 |
否 |
BM25_PREWARM |
启动预热 per-user BM25 索引 | True |
否 |
PARENT_SECTION_MAX_CHARS |
父文档扩展最大字符数 | 2000 |
否 |
PARENT_SECTION_SIBLING_LIMIT |
父文档扩展兄弟块上限 | 4 |
否 |
CONVERSATIONAL_REWRITE_HISTORY_TURNS |
会话改写参考的历史轮数 | 4 |
否 |
CHUNK_OVERLAP |
切块重叠字符数(仅影响新上传) | 50 |
否 |
ENABLE_LLM_EXTRACTION |
启用 LLM 实体提取 | True |
否 |
USE_RULE_EXTRACTION |
同时使用规则提取 | False |
否 |
ENTITY_BATCH_SIZE |
实体提取批大小 | 200 |
否 |
ENTITY_EXTRACTION_DELAY |
实体提取批间延迟(秒) | 0 |
否 |
LLM_EXTRACTION_CONCURRENCY |
实体提取并发上限 | 20 |
否 |
LLM_EXTRACT_MAX_TOKENS |
实体提取 max_tokens | 1024 |
否 |
LOG_DIR / LOG_LEVEL |
日志目录与级别 | ./data/logs / INFO |
否 |
| 路径 | 页面 | 说明 |
|---|---|---|
/login |
Home.vue |
登录 / 注册 |
/documents |
DocumentsPage.vue |
文档列表与上传 |
/documents/:id |
DocumentDetailPage.vue |
文档详情 |
/documents/map |
ClusterMapPage.vue |
2D PCA 聚类地图 |
/graph |
GraphPage.vue |
图谱主页(搜索 + 可视化) |
/graph/timeline-animation |
EntityTimelineAnimationPage.vue |
实体时间线动画 |
/entities/:name |
EntityDetailPage.vue |
实体详情 |
/dashboard |
DashboardPage.vue |
仪表盘 |
/timeline |
TimelinePage.vue |
时间线 |
/chat |
ChatPage.vue |
RAG 对话 |
/search |
SearchPage.vue |
语义检索 |
除
/login外所有路由均需要登录,由router/index.js的beforeEach守卫统一拦截。
- Python 3.11+
- Node.js 18+
- Docker & Docker Compose
# 冒烟测试(无需 Neo4j/ChromaDB)
cd backend
../.venv/Scripts/python.exe -c "
from app.main import app
from fastapi.testclient import TestClient
c = TestClient(app)
assert c.get('/health').status_code == 200
print('OK')
"
# 集成测试(需启动 Docker 服务)
docker-compose up -d
../.venv/Scripts/python.exe -m pytest backend/tests单元测试无需真实密钥:
conftest.py会setdefault一个临时JWT_SECRET(CI 中也显式注入),JWT 占位符拦截不会阻断测试。
.github/workflows/ci.yml 在 push 到 main/master 及 PR 时自动运行 backend 的 pytest tests/ -q(Python 3.11)。无需真实密钥——JWT_SECRET 与 APP_ENV=test 由 workflow 注入。
表结构由 database.init_db 的 CREATE TABLE IF NOT EXISTS 创建;其后 _run_migrations 按 backend/migrations/NNN_*.sql 文件名序号顺序应用,进度记录在 schema_version 表。001_baseline.sql 仅 stamp 版本 1(无结构变更),新增增量迁移直接加 002_*.sql 即可。
# 全量备份(SQLite 在线 .backup + Neo4j/Chroma/uploads 打包)
# 默认数据根是 backend/data(应用的所有启动方式 CWD=backend,相对路径
# ./data/... 实际落在 backend/data/),备份产物在 backend/data/backups/
./scripts/backup.sh
# 自定义路径:DATA_DIR=/var/lib/kg OUT_DIR=/tmp ./scripts/backup.sh
# 注意:脚本会拒绝备份缺失或 0 字节的 SQLite 文件(历史上曾静默备份空库)# 从仓库根或 backend/ 运行均可(SQLite 路径自动锚定到 backend/data)
cd backend
../.venv/Scripts/python.exe clean_user_data.py 3 --dry-run # 预览
../.venv/Scripts/python.exe clean_user_data.py 3 --yes # 真删
# 若应用正在运行,清理后需重启以清空内存中的检索/聚类缓存当 Chroma 向量丢失(容器重建 / 误删 / 迁移)但 SQLite 的 chunks 表与 embedding_cache 仍在时,可零 API 成本回灌:
cd backend
../.venv/Scripts/python.exe scripts/rebuild_chroma.py
# 输出示例:
# [info] chunks with cached embedding: 54
# [info] upserted batch 1: 54/54
# [done] upserted 54 chunks; collection now holds 54 vectors# 构建并运行基础设施(Neo4j + ChromaDB;后端本身不在此 compose 内)
docker-compose up -d
# 单独构建后端镜像 —— 必须从仓库根构建(requirements.txt 在根、app 在 backend/)
docker build -f backend/Dockerfile -t kg-backend .
docker run -p 8001:8001 --env-file backend/.env kg-backend# backend
fastapi==0.115.0
uvicorn==0.32.0
python-jose[cryptography]==3.3.0
bcrypt==4.1.3
python-multipart==0.0.17
neo4j==6.1.0
chromadb==0.4.18
numpy==1.26.4
httpx==0.27.0
aiofiles==24.1.0
markitdown==0.0.1a3
markdown-it-py==3.0.0
jieba==0.42.1
spacy==3.7.5
rank-bm25==0.2.2
python-dotenv==1.0.0
pydantic==2.9.2
pydantic-settings==2.6.0
sqlalchemy==2.0.36
aiosqlite==0.20.0
# dev / test only
pytest==8.3.3
pytest-asyncio==0.24.0
# frontend
vue ^3.5.24
vue-router ^4.6.3
pinia ^2.3.1
d3 ^7.9.0
axios ^1.7.9
@tanstack/vue-virtual ^3.13.28
vite ^7.2.4
注意:原
passlib[bcrypt]==1.7.4已移除,改为原生bcrypt==4.1.3(passlib 与新版 bcrypt 存在兼容问题;且 chromadb 0.4.18 依赖bcrypt>=4.0.1,钉 3.x 会让全新环境的 pip 解析直接失败——CI 曾因此一直红)。app/auth/security.py在哈希前主动截断 72 字节、验证时捕获 ValueError,兼容 4.x 行为;存量$2b$哈希可直接验证。
- 🗝️ API 密钥保护:
.env中的密钥若已泄露,立即在控制台轮换 - 🔑 JWT_SECRET:任意环境检测到公开占位符即拒绝启动(不限于 production);用
python -c "import secrets; print(secrets.token_urlsafe(48))"生成 - ⚡ 硅基流动限速:嵌入服务实现了批量处理、异步队列和并发控制(Semaphore=5)
- 🧮 NumPy 版本:必须使用 NumPy 1.x(<2.0)以保证 ChromaDB 兼容性
- 🧬 ChromaDB 版本:客户端和服务端必须都使用 0.4.18 版本
- 🐳 Docker 内存:Neo4j 需要充足内存,建议 4GB+
- 🛡️ markitdown 防御:
utils/md_parser.py兼容新旧 API(text_content/markdown/title),失败时回退到txt解析 - 🧩 Neo4j APOC:docker-compose 启用了 APOC 插件,UNWIND 批量写入依赖其函数
⚠️ 实体合并:POST /api/graph/entities/merge会硬删 source 并将所有引用指向 target,操作不可逆- 🔧 Chroma entrypoint 绕过:
docker-compose.yml覆盖了 chromadb 0.4.18 镜像 entrypoint——原 entrypoint 每次启动pip install --force-reinstall chroma-hnswlib,新版会拉入 numpy 2.x 导致np.float_崩溃。改为直接跑 uvicorn,沿用镜像内可用的 hnswlib - ♻️ 向量索引重建:若 Chroma 向量丢失(容器重建/误删/迁移),运行
backend/scripts/rebuild_chroma.py可从 SQLitechunks+embedding_cache(md5-keyed)零 API 成本回灌,复用get_chroma_client保证集合名/cosine/upsert 与摄入路径一致
MIT