18 KiB
好# rag-local 本地知识库
纯本地的 RAG(检索增强生成)知识库系统,基于 SQLite 全栈文件型存储,无外部数据库依赖。面向 NAS / 小主机等边缘设备单机部署。
功能
- 文档流水线:上传 txt / md / pdf / docx / html → 自动解析 → 四阶段分块(结构识别 → 标题感知 → 语义 → 递归兜底)→ 向量化 + 全文索引,6 态解析状态机全程可见
- 混合检索:sqlite-vec 向量 KNN + FTS5 全文 BM25,RRF 融合排序 + LLM 二次重排,中文 gse 分词
- RAG 问答:SSE 流式对话,检索引用(含来源与得分)随回答展示,会话历史持久化
- 知识图谱:解析时 LLM 抽取实体与关系,问答时实体链接 + 一跳邻居注入提示词,图谱页力导向图可视化
- 合同法律条款标注:上传合同自动按条款切分,逐条款标注对应法条(0-10 分 + 理由),可导出标注版 HTML(打印/另存 PDF)
- 模型可配置:对话 / 向量模型均为 OpenAI 兼容 API,可对接 Ollama、DeepSeek、硅基流动、OpenAI 等任意供应商
- 单用户门禁:启动生成访问令牌(重启即换新),登录后 JWT 鉴权
技术栈
| 层 | 技术 |
|---|---|
| 后端 | Go + GoFrame v2 + Eino(LLM 编排) |
| 存储 | SQLite(modernc 纯 Go 驱动 + sqlite-vec 扩展) |
| 全文 | SQLite FTS5 + gse 中文分词 |
| 前端 | Vue 3 + Element Plus + Vite(前后端不分离,单端口) |
| 部署 | Docker Compose 一键启动 |
总体架构
┌─────────────────────────── 浏览器 ───────────────────────────┐
│ http://host:8080 │
└──────────────────────────────┬───────────────────────────────┘
│ 统一端口(SPA 静态资源 + REST API + SSE 流式)
┌──────────────────────────────▼───────────────────────────────┐
│ GoFrame HTTP Server (:8080) │
│ ┌───────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ 静态资源 │ │ API 路由 │ │ 访问令牌鉴权 / CORS / │ │
│ │ ui-src/dist│ │ /dataset │ │ panic 恢复 中间件 │ │
│ └───────────┘ │ /document ... │ └────────────────────────┘ │
│ └──────┬───────┘ │
│ ┌──────────────┼──────────────┐ │
│ ┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼──────┐ │
│ │ controller │→│ service │→│ dao │ │
│ └────────────┘ └──────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌─────────▼──────┐ ┌────▼─────────────────────┐ │
│ │ 业务编排 │ │ SQLite × 3(文件型) │ │
│ │ RAG 问答 │ │ business.db: 业务表+向量+ │ │
│ │ 文档解析流水线 │ │ FTS(vec0 + fts5) │ │
│ │ 知识图谱抽取 │ │ system.db: 全局配置/模型 │ │
│ │ 合同条款标注 │ │ chat.db: 会话/消息 │ │
│ └───────┬───────┘ └───────────────────────────┘ │
│ │ HTTP(OpenAI 兼容 API) │
└──────────────────────┼────────────────────────────────────────┘
┌────────▼────────┐
│ 模型供应商(可配置)│
│ Ollama/OpenAI/ │
│ DeepSeek/硅基流动 │
└─────────────────┘
三条数据链路(各自任务轮询驱动):
① 语料入库:上传文档 → 解析 → 分块 → Embedding 向量化 + 关键词索引
(chunk 表 + vec0 向量 + fts5 全文同库同事务)
② 问答:问题 → 混合检索(向量 + 关键词 + RRF 融合 + LLM 重排)
(+ 知识图谱图增强)→ 流式回答 → SSE(引用 [编号] 定位到分块)
③ 合同标注:上传合同 → 按条款切分 → 逐条款多数据集召回(向量+FTS,宁滥毋缺)
→ LLM 判定(0-10 分 + 理由)→ 标注落库 → 进度递增 → 导出标注版 HTML
数据存储
三个 SQLite 文件各司其职:
| 库 | 文件 | 内容 |
|---|---|---|
| business | data/business.db |
数据集、文档、分块、向量(vec0)、全文索引(FTS5)、解析任务、知识图谱、合同标注 |
| system | data/system.db |
模型配置 |
| chat | data/chat.db |
会话与消息 |
表清单(12 张实体表 + 2 张虚拟表):
| 表名 | 库 | 类型 | 说明 |
|---|---|---|---|
model_config |
system | 实体 | 模型配置(chat / embedding,is_default 标记默认) |
kb_dataset |
business | 实体 | 数据集(绑定向量模型、分块参数) |
kb_document |
business | 实体 | 文档(6 态状态机) |
kb_chunk |
business | 实体 | 分块 |
kb_chunk_vec |
business | 虚拟表 vec0 | 分块向量(与 kb_chunk 1:1) |
kb_chunk_fts |
business | 虚拟表 FTS5 | 分块全文索引(存 gse 分词) |
kb_parse_task |
business | 实体 | 文档解析/向量化任务(parse / reembed) |
kg_entity |
business | 实体 | 知识图谱实体(按 dataset_id+name 去重) |
kg_relation |
business | 实体 | 知识图谱关系(head/relation/tail 文本快照) |
kb_contract_task |
business | 实体 | 合同标注任务 |
kb_contract_clause |
business | 实体 | 合同条款(断点续跑粒度) |
kb_contract_mark |
business | 实体 | 标注结果(法条快照 + 理由 + 0-10 分) |
chat_conversation |
chat | 实体 | 问答会话 |
chat_message |
chat | 实体 | 问答消息(citations 存引用 JSON) |
功能模块
文档解析流水线
上传后由 StartParsePoller 轮询驱动(5 秒间隔,单 goroutine 串行),文档状态 6 态流转:待处理 → 解析中 → 向量生成中 → 图谱构建中 → 已完成 / 失败。
- 按扩展名分发解析器(txt/md 直读、pdf、docx、html 均为纯 Go 解析)
- 四阶段组合分块(SplitAuto):结构识别(条文/章节等 8 种单元模式,命中自动采用)→ 标题感知 → 语义分块 → 递归兜底
- 批量 Embedding(每批 16 条)→ chunk + vec0 向量 + fts5 全文同事务写入
- 知识图谱 LLM 抽取(逐 chunk 并发),失败不阻断:文档仍置已完成,提示「知识图谱未构建」原因
失败可重试;重跑前先清旧索引(chunk+vec+fts+kg 单事务),幂等重建。
混合检索与 RAG 问答
- 混合检索(HybridRetriever):向量 KNN(vec0,L2 距离,预取后按数据集过滤)+ 关键词检索(FTS5 BM25,查询端 gse 分词后「引号词组 OR」连接避免空召回)→ RRF 融合(
Σ 1/(60+rank))→ LLM 一次调用重排(0-10 分),低于门槛max(最高分×50%, 6)剔除,取 Top 5 - 问答工作流:检索与知识图谱图增强并行执行 → 组装引用编号 [1][2] 与提示词 → ChatModel 流式生成
- SSE 事件顺序:
citations(引用列表 + conversation_id)→delta(增量文本)→done;异常推error;15 秒心跳(: ping)保持长连接 - 引用编号与提示词中资料编号一一对应,随助手消息以 JSON 落库
知识图谱
- 构建:解析流水线向量化之后,逐 chunk 调用 LLM 抽取
{entities, relations}JSON;实体按(dataset_id, name)去重(UNIQUE + upsert),关系带来源 chunk_id - 图增强检索:问题分词后与实体名匹配取 Top 3 → 一跳邻居(上限 20 条三元组)→ 以「【知识图谱】张明 -任职于-> XX科技」形式注入提示词,增强关系类问题回答
- 图谱页以 eCharts 力导向图可视化(节点大小随出入度,点击高亮一跳邻居)
合同法律条款标注
- 上传合同(txt/md/pdf/docx/html)时多选法律语料数据集;
StartAnnotationPoller轮询消费 - 条款切分:探测
第X条/ 数字编号 / 中文数字三种行首模式,命中则按条款切分,否则整篇单条 - 逐条款(并发):每个数据集召回(向量 15 条 + FTS 15 条)→ RRF 融合截 60 候选 → LLM 一次调用判定(0-10 分 + 理由)
- 宁滥毋缺:与问答检索相反,不做 topK 截断与门槛过滤,全部候选(含 0 分)保留——漏标比多标严重
- 断点续跑:按条款粒度续跑,重启不重复标注;单条款失败不影响任务完成
- 导出标注版 HTML(自包含,score 分级着色,可打印/另存 PDF)
鉴权(单用户门禁)
- 访问令牌每次启动重新生成(32 位 hex),仅存内存、不落库,打印在启动日志;重启即换新令牌,旧 JWT 全部失效
- 登录:输入令牌 → 校验 → 签发 JWT(HS256,24h 有效,携带令牌 SHA-256 指纹)
- 鉴权中间件比对 JWT 指纹与内存令牌指纹,不一致即 401(前端自动跳登录页)
- 公开路径白名单:
/system-config/login、SPA 静态资源、GET /workspace/*(仅路径穿越防护)
快速开始(Docker)
docker compose up -d --build
Linux 服务器首次部署前:
data/与workspace/由宿主机 bind mount 提供(相对 compose 文件目录,迁移时连目录内容一起拷贝)。若目录尚不存在或由 root 创建,容器内app用户(uid 1000)将无法写入 SQLite,须先执行:mkdir -p data workspace && chown -R 1000:1000 data workspace迁移已有数据后还须检查
data/system.db中model_config表的endpoint_url:表里存的若是127.0.0.1,在新机器上会指向自身,应改为实际模型服务可达地址。
- 浏览器打开 http://localhost:8080
- 启动日志中查看访问令牌(
docker compose logs rag-local搜索"访问令牌") - 登录后按以下流程使用:
- 设置 页添加对话模型与向量模型(OpenAI 兼容接口,如 Ollama / vLLM / one-api),并点击"测试"验证连通
- 数据集 页新建数据集,绑定向量模型(不绑定则仅全文检索)
- 进入数据集上传文档,等待解析完成
- 问答 页选择知识库开始提问,回答可展开查看引用来源
- 知识图谱 页查看解析时抽取的实体与关系;合同标注 页上传合同按条款标注法条
数据保存在 ./data 与 ./workspace,删除容器不丢失。
本地开发
# 后端(Go 1.26+)
go get modernc.org/sqlite@v1.47.0 # 关键:向量扩展依赖(GoFrame 驱动链默认锁定过旧版本)
go run . # 监听 :8080,启动日志打印访问令牌
# 前端(开发热更新)
cd ui-src
npm install
npm run dev # Vite 开发服务器(5173),API 代理见 vite.config.js
生产构建时前端产物在 ui-src/dist,由 Go 直接托管。
模型配置
首次部署 / 迁移到新电脑
Python 虚拟环境(.venv/)不可跨机器复制:其中 pyvenv.cfg、bin/ 下脚本的 shebang 及 bin/python 符号链接都写死了本机绝对路径,复制后无法运行,这是 Python 的设计,不能靠改文件修复。复制/迁移项目目录时排除 .venv/(.gitignore 已排除;整体拷贝时连同复制则先删除),到新机器上重建:
./scripts/start_models.sh # 自动检测 .venv:未就绪(缺失/损坏/依赖未装全)时依 scripts/requirements.txt 重建,就绪则直接启动
.venv重建需按scripts/requirements.txt安装 115 个依赖(含 omlx,需可访问 GitHub),仅首次或 venv 失效时执行一次。
任意 OpenAI 兼容接口均可使用:
- 对话模型(
/v1/chat/completions,支持 SSE 流式):用于问答与知识图谱实体抽取 - 向量模型(
/v1/embeddings):用于分块向量化与语义检索;维度须与config.yml中vector.dim一致(默认 1024)
示例(本地 oMLX,Apple Silicon 推荐):
# 模型文件放 models/mlx/omlx/(chat: Qwen3.5-9B-MLX-4bit;embedding: bge-m3),
# 依赖装在项目根 .venv/(mlx + omlx + fastapi + uvicorn)
./scripts/start_models.sh # 启动单实例 oMLX :18080,同时提供 chat + embedding
./scripts/stop_models.sh # 停止
# 设置页配置:
# 对话模型:endpoint http://127.0.0.1:18080/v1,模型名 Qwen3.5-9B-MLX-4bit
# 向量模型:endpoint http://127.0.0.1:18080/v1,模型名 bge-m3,维度 1024
16GB 内存机器需
sudo sysctl iogpu.wired_limit_mb=14336提高 Metal 上限(oMLX 内存 guard ceiling 14GB);Qwen3.5 思考链默认开启,应用侧已在 agent / 主回答 / 重排调用中通过chat_template_kwargs: {"enable_thinking": false}关闭。
示例(Ollama):
# 拉取模型
ollama pull qwen2.5:7b
ollama pull nomic-embed-text
# 设置页配置:
# 对话模型:endpoint http://localhost:11434/v1,模型名 qwen2.5:7b
# 向量模型:endpoint http://localhost:11434/v1,模型名 nomic-embed-text,维度 768
注意:向量维度变更需清空
data/business.db重建(vec0 表建表维度固定)。
配置说明(config.yml)
| 配置 | 说明 |
|---|---|
server.address |
监听地址,默认 :8080 |
server.clientMaxBodySize |
上传文件上限,默认 200MB |
vector.dim |
向量维度,须与向量模型一致 |
database.cache.ttl |
DAO 查询缓存秒数 |
chat.timeout / chat.max_retries |
对话模型 API 超时(秒)/ 失败重试次数 |
chunk.default_size / chunk.default_overlap / react.default_rounds |
数据集分块大小/重叠/智能体轮次默认值(新建数据集传 0/-1 时使用) |
pool.* |
各并行点协程池并发度(缺失或非法回退代码内默认值):kg_extract(4) 逐 chunk 图谱抽取、annotation_clause(4) 合同逐条款标注、annotation_dataset(8) 条款内逐数据集召回、chat(4) 问答检索与图增强并行、chat_retrieve(4) 检索向量与全文并行 |
API 概览
所有接口除 /system-config/login 外均需 Authorization: Bearer <JWT>;接口只使用 GET / POST 两种方法。
| 分组 | 接口 |
|---|---|
/system-config |
POST /login(访问令牌登录) |
/model-config |
GET /list、POST /save、POST /delete、POST /set-default、POST /test(连通性测试) |
/dataset |
GET /list、POST /save(新建/编辑)、POST /delete(有文档拒绝) |
/document |
POST /upload(multipart)、GET /list、GET /detail、POST /delete、POST /reembed(仅重算向量) |
/chunk |
GET /list(分页 + 关键词搜索)、POST /update(改后自动重向量化) |
/parse-task |
GET /list、POST /retry(重试失败任务) |
/kg-entity /kg-relation |
GET /list(按数据集过滤 + 分页) |
/contract |
POST /upload(multipart + dataset_ids)、GET /list、GET /detail、GET /summary(风险汇总报告)、GET /annotated(导出标注版 HTML)、POST /delete |
/conversation /message |
POST /save、GET /list、POST /delete(级联删消息);GET /message/list、POST /message/chat(SSE 流式问答) |
/workspace/* |
源文件访问(静态服务,仅路径穿越防护) |
前端页面
| 页面 | 功能 |
|---|---|
| 登录页 | 输入访问令牌登录,无注册入口 |
| 数据集列表 | 新建/编辑/删除(绑定向量模型必选、分块参数传 0/-1 使用 config.yml 全局默认);变更模型/分块 → 确认弹窗提示重新处理 |
| 数据集详情 | 文档上传(拖拽)、6 态解析状态/分块数/失败重试/「图谱未构建」提示、分块预览与编辑 |
| 图谱页 | 实体/关系列表 + eCharts 力导向图 |
| 合同标注页 | 选语料数据集 → 上传合同 → 任务进度(3s 轮询)→ 条款-标注对照抽屉 → 导出标注版 HTML |
| 问答页 | 会话列表 + 知识库下拉,SSE 流式渲染,引用折叠面板 |
| 设置页 | 模型配置 CRUD(chat/embedding 两个 tab)、连通性测试、设置默认 |
项目结构
common/ 通用层:HTTP 服务/鉴权/文件解析/中文分词/向量 JSON/协程池
kb/
consts/ 表名、状态、默认参数与协程池默认大小
model/ entity(表结构)/ dto(Req/Res + 路由)/ domain(领域模型)
dao/ 数据访问(每表一个文件,chunk_dao 兼管 vec0/fts5 虚拟表)
service/ 业务逻辑(每表一个文件 + chat_service 问答编排 + annotation_service 合同标注)
controller/ 接口层(每表一个文件)
ui-src/ Vue 3 前端
data/ SQLite 三个库(gitignore)
workspace/ 上传的文档源文件(gitignore)
技术设计.md 实现细节/技术决策文档(版本要点、表结构约定、检索参数、风险与备选方案)
文档说明
- README.md:项目功能介绍(本文件)
- CLAUDE.md:通用开发规范(分层职责、代码模式、事务/并发/缓存约束、文档驱动开发流程)
- 技术设计.md:实现细节与技术决策(DDL、检索参数、风险与备选方案等)