基于Dolphin的轻量级RAG系统
前言:
6月的时候,基于Dolphin实现了一个轻量级的RAG,支持文档上传,构建知识库,检索问答。
一些实现细节放在了下边。
本项目的git地址:[email protected]:zuka1129/dolphin-rag.git
官方的dolphin git地址:https://github.com/bytedance/Dolphin
轻量 RAG 服务技术文档(基于 FastAPI + Celery + ChromaDB + Ollama + Dolphin)
下面介绍如何搭建一个轻量级的 RAG(Retrieval-Augmented Generation)服务,包括整体架构、核心流程、接口设计、关键实现细节、部署配置与优化建议。适合希望快速落地“私有文档问答/摘要/提取”等能力的开发者。最终落地还需要根据业务进行调整。
项目简介
- 目标:支持文档上传 → 异步解析 → 向量化入库 → 语义检索 → 流式对话(含总结、提取、检索问答)。
- 核心文件:
main.py
:FastAPI 服务与路由services/document_service.py
:业务核心(文档解析、分块、向量化、检索、流式对话)tasks/document_tasks.py
:Celery 异步解析任务utils/sqlite_utils.py
:SQLite 元数据存取utils/chat_stream_utils.py
:流式会话终止管理config/settings.py
:配置项(Ollama/Redis/Chroma 等)
整体架构
- 入口层:FastAPI 提供 HTTP API 与 SSE 流。
- 任务层:Celery + Redis 作为异步任务队列,后台解析文档。
- 存储层:
- SQLite:文档元数据与任务状态
- ChromaDB:向量化文档块的持久化存储与相似度检索
- 本地文件:原始上传文件
- 模型层:
- Dolphin 文档感知模型(图像页解析)
- Ollama:Embedding 与 Chat(LLM、流式输出)
技术栈与依赖
- FastAPI、Uvicorn、Starlette(SSE)
- Celery、Redis(Broker/Backend)
- ChromaDB(持久化路径
./chroma_db
) - SQLite(
dolphin.db
) - Transformers
BertTokenizer
(上下文裁剪) - Dolphin(
demo_page_hf.py
:DOLPHIN
/process_page
) - Ollama(
/api/embeddings
、/api/chat
)
配置要点
- 文件上传限制:
MAX_FILE_SIZE
默认 10MB - 用户文档数上限:
MAX_DOCUMENTS_PER_USER
默认 50(超出删除最旧) - 向量库持久化:
CHROMA_PERSIST_DIR=./chroma_db
- LLM 与 Embedding:
OLLAMA_BASE_URL
(默认http://192.168.4.20:11434
)DEFAULT_EMBEDDING_MODEL=mxbai-embed-large:latest
DEFAULT_CHAT_MODEL=qwen3:latest
- Token 管控:
TOKENIZER_MODEL
、MAX_TOKEN
、BUFFER_TOKEN
- Celery/Redis:通过环境变量
.env
配置
数据表结构(SQLite)
表名:documents
– 字段:id
、user_id
、filename
、file_path
、status
、content
、created_at
、processed_time
、size
、task_id
、result
、error
– 列表接口按 created_at DESC
排序
核心流程(RAG 数据流)
1) 文档上传与入队(/upload
) – 保存文件 → 记录 documents
(status=pending)→ 派发 Celery 任务并记录 task_id
。
2) 异步解析(Celery 任务) – 加载 Dolphin 模型,按文档类型: – .txt
直接读取全文 – 其他(PDF/图片/Word/Excel)→ 转图片 → process_page
→ 聚合识别为 content
– 更新 SQLite(status=completed、content、结构化识别结果 result
)。
3) 向量化与入库(ChromaDB) – 对 content
按段落/句法边界分块(默认块长约 1000 字符) – 调用 Ollama /api/embeddings
生成向量 – 写入 ChromaDB(documents/embeddings/metadatas/ids)
4) 语义检索(/search
) – 生成查询向量 → ChromaDB collection.query
→ 返回命中文档块(内容+元数据)
5) 流式对话(/chat
) – 先做意图识别(总结/提取/检索/通用),再按意图选择上下文: – 检索类:检索若干块拼接上下文 – 总结/提取:整篇全文上下文 – 清洗文本 → Token 预算裁剪 → 构造 messages
→ 调用 /api/chat
(stream=true) – 以 SSE 输出;/chat/stop
可终止同一 session_id
的流
API 设计与示例
- 上传文档
curl -F "file=@/path/to/a.pdf" "http://localhost:15001/upload?user_id=alice"
- 查询任务状态
curl "http://localhost:15001/task_status/<task_id>"
- 获取用户文档列表
curl "http://localhost:15001/documents?user_id=alice"
- 获取文档详情
curl "http://localhost:15001/document/<document_id>?user_id=alice"
- 语义检索
curl "http://localhost:15001/search?user_id=alice&query=关键术语"
- 流式对话(SSE)
curl -N -H "Content-Type: application/json" \
-d '{"query":"帮我总结","user_id":"alice","document_id":"alice_20240910120000"}' \
http://localhost:15001/chat
- 终止流
curl -X POST -H "Content-Type: application/json" \
-d '{"session_id":"<从流响应 data.id 获取>"}' \
http://localhost:15001/chat/stop
关键实现细节
- 文档解析
DocumentProcessor
支持 PDF/图片/Excel/Word → 图片- Dolphin
process_page
针对每页返回结构化识别结果,最终_extract_content
汇总为可检索文本 - 分块策略(面向向量化)
- 先段落后句子,尽量保持语义边界
- 默认块长约 1000 字符,日志可见每块长度与数量
- 意图理解与 Prompt 组装
- 小调用:
/api/generate
输出 JSON(意图/目标/格式),包含鲁棒清洗与回退 - 动态构建
system_prompt
与messages
,引导总结/提取/检索 BertTokenizer
计算 token,严格预算,预留BUFFER_TOKEN
防溢出- 流式输出与终止
- SSE 输出
text
/error
类型数据块 - 全局
StreamSessionManager
支持多会话并发与按session_id
停止 - 数据一致性
- 超出配额删除最旧文档与其文件
- 向量化失败不影响文档已完成状态(容错记录日志)
部署与运行
- 前置
- 准备 Redis,并配置
.env
的REDIS_HOST/REDIS_PSD/REDIS_PORT/REDIS_DB_NUM
- 准备 Ollama 服务,并确保加载
DEFAULT_CHAT_MODEL
与DEFAULT_EMBEDDING_MODEL
- 确保
TOKENIZER_MODEL
路径可用 - 启动(直接运行)
python main.py
- 启动过程会初始化 SQLite、启动 Celery worker(通过
start_celery_worker()
)、再启动 FastAPI(0.0.0.0:15001) - Docker/Compose
- 项目包含
Dockerfile
与docker-compose.yml
,可按实际环境调整网络与卷
性能优化建议
- 追求并发的话可以替换成vLLM
- 提升召回:可尝试 BM25 + Embedding 融合(RRF)与相似度阈值过滤
- 分块与检索窗口:结合 LLM 上下文大小调整块长与
n_results
- 对于长文本总结的话,建议构建一个
结语
以上基本就是全部实现流程,提供了一个“可直接落地”的轻量 RAG 实现,稳定的异步解析与入库、可靠的向量检索、可终止的流式对话。可以在此基础上按场景增强召回、改进分块、扩展多模态能力,快速支撑各类私有知识问答与文档处理应用。