基于 LangGraph v1 和 PGVector 的智能文档问答系统,实现异步、可持久化的检索增强生成(RAG)Agent。
核心特性:
- 🧠 Agentic RAG:LLM 自主决策是否需要检索文档
- 🖼️ 多模态支持:通过 MinerU 解析 PDF,支持文本 + 图片显示
- 🔍 语义检索:PGVector + OpenAI embeddings
- 💬 推理模型:DashScope Qwen(OpenAI 兼容接口)
- 💾 对话记忆:基于
thread_id的线程隔离 - 🎨 现代化前端:React + Markdown 渲染,支持图片、表格、代码块
用户问题
↓
query_or_respond (LLM 决策,是否调用检索工具)
├─→ 无 tool_calls → 直接回答 → END
└─→ 有 tool_calls → tools (retrieve_context)
↓
generate (基于检索结果生成答案) → END
后续可以按 官方 Agentic RAG 教程 扩展 grade_documents / rewrite_question 等节点。
gravaity/
├── backend/
│ ├── src/
│ │ ├── agent/ # LangGraph 工作流
│ │ │ ├── graph.py # 主工作流定义
│ │ │ ├── state.py # 状态管理
│ │ │ ├── prompts.py # LLM 提示词
│ │ │ └── vectorstore.py # 向量存储操作
│ │ ├── api/ # FastAPI 接口
│ │ │ ├── app.py # 应用入口
│ │ │ └── routes/
│ │ │ ├── chat.py # 聊天接口
│ │ │ └── documents.py # 文档处理接口
│ │ ├── tools/ # LLM 工具
│ │ │ └── retrieval.py # 检索工具
│ │ ├── utils/ # 工具函数
│ │ │ ├── llm.py # 模型加载
│ │ │ └── mineru_processor.py # MinerU 文档处理
│ │ ├── config/ # 配置管理
│ │ │ └── settings.py # 环境变量加载
│ │ └── db/ # 数据库
│ │ ├── database.py # 连接池
│ │ └── checkpointer.py # 对话持久化
│ ├── .env.example # 环境变量示例
│ ├── pyproject.toml # Python 依赖
│ └── start_backend.py # 启动脚本
│
├── frontend/ # React 前端
│ ├── src/
│ │ ├── components/ # React 组件
│ │ ├── hooks/ # 自定义 hooks
│ │ ├── styles.css # 样式表
│ │ └── main.tsx # 入口
│ ├── package.json # Node 依赖
│ └── vite.config.ts # Vite 配置
│
├── data/ # MinerU 解析结果存放
├── docs/ # 文档
│ └── DOCUMENT_PROCESSING.md # 文档处理指南
└── openspec/ # 变更提案
- Python 3.10+
- Node.js 18+
- PostgreSQL 14+ (with pgvector extension)
- API Keys: OpenAI (embeddings), DashScope (Qwen)
# 进入项目根目录
cd gravaity
# 安装后端依赖
pip install -e .cp .env.example .env编辑 .env 填写必要配置:
# OpenAI Embeddings (用于向量化文档)
OPENAI_EMBEDDINGS_API_KEY=sk-...
# DashScope Qwen (推理模型)
DASHSCOPE_API_KEY=sk-...
CHAT_MODEL=qwen-plus-latest
# PostgreSQL (需启用 pgvector)
POSTGRES_CONNECTION_STRING=postgresql://user:password@localhost:5432/gravaity
# 文档处理
FRONTEND_IMAGES_DIR=./frontend/public/documents/images
FRONTEND_IMAGE_PREFIX=/documents/images
VECTOR_COLLECTION=pdf_documents
CHUNK_SIZE=1000
CHUNK_OVERLAP=200# 确保 pgvector 已启用
psql -U postgres -d gravaity -c "CREATE EXTENSION IF NOT EXISTS vector;"python ./start_backend.py后端运行在 http://0.0.0.0:8000
cd frontend
npm installnpm run dev前端运行在 http://localhost:5173
使用 MinerU 解析 PDF 文件(生成 Markdown + 图片):
# MinerU 安装与使用详见: https://github.com/opendatalab/MinerU
mineru --pdf /path/to/document.pdf --output-dir ./data/ocr/输出结构:
data/ocr/document_name/
├── auto/
│ ├── document.md # 解析后的 Markdown
│ └── images/ # 提取的图片
│ ├── xxx.jpg
│ └── yyy.jpg
curl -X POST http://localhost:8000/documents/process-mineru \
-H "Content-Type: application/json" \
-d '{
"source_path": "./data/ocr/document_name",
"embed": true,
"collection_name": "pdf_documents"
}'参数说明:
source_path: MinerU 输出目录的父目录(包含auto/子目录)embed: 是否嵌入向量库(true/false)collection_name: 向量集合名称
响应示例:
{
"status": "success",
"message": "Document processed successfully",
"images_copied": 15,
"chunks_created": 23,
"collection_name": "pdf_documents"
}在聊天界面提问,系统会自动检索相关文档并显示(包括图片)。
打开 http://localhost:5173,输入问题。系统会:
- 自动判断是否需要检索文档
- 从向量库检索相关内容
- 基于检索结果生成答案
- 支持 Markdown 渲染(包括图片、表格、代码块)
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{
"thread_id": "user-123",
"message": "文档中提到了什么关键技术?"
}'curl http://localhost:8000/chat/threads/user-123/history- Agentic RAG 完整流程:增加
grade_documents/rewrite_question等节点(参考官方教程) - 长期记忆(跨线程 Store):基于
src/db/memory_store.py注入AsyncPostgresStore,在节点中通过store: BaseStore+config: RunnableConfig做用户记忆的读写(参考官方 Add Memory 文档) - API 层:在
src/api/中使用 FastAPI 封装对graph的ainvoke/astream调用
在 curl 中使用正斜杠或双反斜杠:
# ✅ 推荐:使用正斜杠
curl -X POST http://localhost:8000/documents/process-mineru \
-H "Content-Type: application/json" \
-d '{"source_path": "./data/ocr/document_name", "embed": true}'
# ✅ 也可以:Windows 双反斜杠
curl -X POST http://localhost:8000/documents/process-mineru \
-H "Content-Type: application/json" \
-d "{\"source_path\": \"D:\\\\code\\\\gravaity\\\\data\\\\ocr\\\\document_name\", \"embed\": true}"检查以下几点:
- 确保
FRONTEND_IMAGES_DIR指向正确的目录(./frontend/public/documents/images) - 确保后端从项目根目录启动(使用
python ./start_backend.py) - 检查前端是否已安装
react-markdown依赖
常见原因:
- OpenAI API Key 无效或配额不足
- 网络连接问题(特别是 tiktoken 下载)
- 文档过大导致 token 超限
解决方案:
# 预先缓存 tiktoken 编码
python -c "import tiktoken; tiktoken.get_encoding('cl100k_base')"MIT License