基于 Next.js 的 RAG(检索增强生成)Web 应用,支持智能文档解析与问答。可处理 PDF、Word、Markdown、TXT 及图片,解析内容嵌入向量数据库实现对话式检索。
- 多格式文档解析:PDF(通过 MinerU)、Word(.doc/.docx)、Markdown、TXT
- 向量搜索与对话:文档分块通过 Bailian 嵌入并存储于 Pinecone,实现语义问答
- 云原生存储:所有解析结果(markdown、图片、JSON 元数据)存储于 Supabase Storage 而非本地磁盘 —— 完全兼容 Vercel 及其他无服务器平台
- 多模态支持:从文档提取的图片可通过 CDN URL 公开访问,并可在 AI 回复中引用
- 基于会话的对话:每份文档支持多个聊天会话及历史管理
- 框架:Next.js 16 (App Router)
- UI:React 19, Tailwind CSS 4, shadcn/ui, Framer Motion
- 文档解析:MinerU API (PDF), mammoth.js (Word)
- 向量数据库:Pinecone
- 嵌入模型:阿里云 Bailian 全模态向量模型(可配置,如
tongyi-embedding-vision-flash-2026-03-06,维度 768) - LLM:OpenAI 兼容 API(如 OpenAI、Azure 或其他提供商)
- 存储与数据库:Supabase (Storage + PostgreSQL)
- ORM:Drizzle ORM
cd frontend/doc-rag-flow
npm install将 .env.example 复制为 .env.local 并填写所有必填项:
cp .env.example .env.local关键变量:
| 变量 | 用途 |
|---|---|
SUPABASE_URL |
Supabase 项目 URL |
SUPABASE_SERVICE_ROLE_KEY |
服务端 Storage/DB 操作密钥 |
SUPABASE_STORAGE_BUCKET |
文档存储桶名称(默认:documents) |
SUPABASE_Database_URL |
Drizzle 迁移用 PostgreSQL 连接字符串 |
MINERU_API_TOKEN / MINERU_API_URL |
PDF 解析服务 |
BAILIAN_API_KEY |
嵌入模型 API 密钥 |
BAILIAN_EMBEDDING_MODEL |
嵌入模型名称(如 tongyi-embedding-vision-flash-2026-03-06) |
BAILIAN_EMBEDDING_DIMENSION |
嵌入模型维度(如 tongyi-embedding-vision-flash-2026-03-06的维度是768,须与模型实际维度一致) |
PINECONE_API_KEY / PINECONE_INDEX_NAME |
向量数据库(新建 Index 时 dimension 须与 BAILIAN_EMBEDDING_DIMENSION 一致) |
LLM_API_KEY / LLM_BASE_URL |
聊天回复用 LLM |
安全提示:
SUPABASE_SERVICE_ROLE_KEY严禁暴露到浏览器端,仅用于服务端 API 路由。
确保 Supabase PostgreSQL 数据库可访问,然后应用模式:
npx drizzle-kit pushdocuments 表将自动创建。如从早期版本升级,请确保以下列已存在:
ALTER TABLE documents
ADD COLUMN IF NOT EXISTS storage_path TEXT,
ADD COLUMN IF NOT EXISTS parsed_path TEXT,
ADD COLUMN IF NOT EXISTS mineru_task_id TEXT,
ADD COLUMN IF NOT EXISTS error_message TEXT;- 进入 Supabase Dashboard → Storage → New Bucket
- 创建名为
documents的存储桶(或与SUPABASE_STORAGE_BUCKET一致) - 将存储桶设为 public(或配置允许匿名
SELECT访问parsed/images/*的策略) - 添加 RLS 策略,将
INSERT/DELETE限制为仅 service role 可用
npm run dev应用将在 http://localhost:3009 可用。
本项目使用 Supabase Storage 作为文档文件与解析结果的唯一持久化存储。本地 /tmp 仅在解析期间作为临时暂存区使用,随后立即清理。
documents/ # 存储桶
└── {documentId}/ # 按文档前缀隔离
├── original/ # 原始上传文件
│ └── paper.pdf
└── parsed/ # 解析结果
├── full.md
├── content_list.json
└── images/
├── img_01.png
└── ...
- 上传:用户上传文件 → API 路由直接流式上传至 Supabase Storage (
documents/{documentId}/original/...) - 解析(PDF):从 Storage 下载原始文件 → 调用 MinerU → 下载 ZIP → 解压至
/tmp→ 将full.md、content_list.json及图片上传至 Storage → 将 markdown 中的图片路径替换为公开 URL → 清理/tmp - 解析(非 PDF):从 Storage 下载 → 提取文本 → 将
full.md上传至 Storage → 清理/tmp - 对话:Pinecone 元数据中的图片 URL 直接指向 Supabase 公开 URL,前端无需通过 Next.js 服务器代理即可渲染图片
- 删除:调用
DELETE /api/documents/{id}将删除数据库记录、Storage 中documents/{documentId}/下的所有文件,并清除 Pinecone 中的关联向量
- 无服务器原生:不依赖持久化本地磁盘 —— 在 Vercel、Netlify Edge 及 AWS Lambda 上开箱即用
- 全局访问:解析后的图片通过 Supabase CDN URL 分发,无需自定义文件代理
- 可扩展存储:文档容量不再受服务器磁盘大小限制
- 将代码推送到 Git 仓库
- 在 Vercel 中导入项目
- 在 Vercel Dashboard → Project Settings → Environment Variables 中添加
.env.example中的所有环境变量 - 确保
SUPABASE_SERVICE_ROLE_KEY标记为 sensitive - 部署
无需额外的文件系统或持久化卷配置。
| 命令 | 说明 |
|---|---|
npm run dev |
在 3009 端口启动开发服务器 |
npm run build |
生产构建 |
npm run typecheck |
运行 TypeScript 类型检查 |
npm run lint |
运行 ESLint |
npm run format |
使用 Prettier 格式化代码 |
感谢 RAG-Anything 项目在 RAG 检索增强生成思路上的启发与参考。
MIT