本地知识库(RAG)
让模型能查你自己的资料——写进知识库的文档会被切块、向量化,之后用户提问时由 Agent 主动检索,把命中的片段作为回答依据。
这一页只讲知识库本身。模型提供商、工具总线、会话窗口在 接入大模型(LLM) 里。
默认不启用
知识库是 llm_core 插件的一部分,但默认关闭。没配 rag 段时它不初始化、不占内存、不影响对话,knowledge_search 工具也不会进工具总线。
它是什么,不是什么
- 是:本地资料的检索底座。文档、切片、向量、全文索引全部收在一个 SQLite 文件里,没有额外服务要跑。
- 不是:向量数据库集群。它是为「几百到几千篇文档」这个量级设计的,靠 SQLite 单文件吃饭。
- 不做 OCR、不抓网页、不自动同步目录。入库就是「你把文本交进来」,怎么拿到文本是你的事。
开启
在面板的插件配置里把 rag 填成一个 JSON 对象(这一项在配置里是字符串,注意引号转义),再设 rag_top_k:
{
"enabled": true,
"base_url": "https://api.example.com/v1",
"api_key": "sk-...",
"model": "text-embedding-3-small",
"dims": 1536,
"chunk_size": 400,
"chunk_overlap": 80,
"backend": "auto"
}| 字段 | 说明 |
|---|---|
enabled | 总开关。为 false 时其余字段全部无视 |
base_url | embedding 端点,必须是 OpenAI 兼容的 POST /embeddings(一般以 /v1 结尾)。缺它则不启用 |
api_key | 也可以不填,改用环境变量 LLM_CORE_EMBED_API_KEY |
model | 嵌入模型名,默认 text-embedding-3-small |
dims | 向量维度。留 0 时按首次调用返回的维度自动推断并固定 |
chunk_size | 单块字符数,默认 400 |
chunk_overlap | 相邻块重叠字符数,默认 80 |
backend | auto(默认,能用 sqlite_vec 就用)/ sqlite_vec / memory |
中文语料建议用 BGE-M3,dims 设 1024。换模型必须同时改 dims,否则新旧向量不可比。
改完配置重启框架生效(_init_kb() 在 register() 里只跑一次)。
数据存在哪
一个文件:data/plugins_dat/llm_core/llm_kb.db。
里面四张表:kb_docs(文档)、kb_chunks(切片)、kb_fts(FTS5 全文索引)、kb_vec(vec0 向量表)或 kb_vec_mem(numpy 后端的向量列)。备份就是拷这一个文件。
入库
面板:插件页面 → LLM 对话核心 → 「知识库」标签页。粘贴文本、填标题、点入库。同一页还能做检索自测、看文档列表、单篇删除、一键清空。
HTTP:
# 入库(title 留空时自动截取正文前 30 字)
curl -X POST http://127.0.0.1:8080/api/llm_core/kb/ingest \
-H "Authorization: Bearer <你的接口令牌>" \
-H "Content-Type: application/json" \
-d '{"title":"运维手册","text":"……正文……","source":"manual"}'
# 文档列表(含每篇切片数)
curl http://127.0.0.1:8080/api/llm_core/kb/docs代码(插件里拿到 llm_core 服务后):
kb = ctx._framework.services.get('llm_core').kb # 未启用时为 None
if kb:
doc_id = kb.add_text('标题', '正文', source='crawler')
hits = kb.search('检索词', top_k=5)入库是同步的:一次 add_text 会调用一次 embedding 端点(文本按 chunk_size 切成几块就请求几块)。一次塞几万字会长耗时,建议在库里批量处理,别放在消息处理路径上。
检索怎么工作
两路召回,再融合:
- 向量召回——查询向量化后按余弦/IP 距离取近邻。
sqlite_vec后端走 vec0 的MATCH,memory后端把向量读出来在内存里算。 - 关键词召回——FTS5
trigram分词 + BM25 排序。查询少于 3 个字符时这一路直接跳过(trigram 的硬限制),此时只有向量召回。 - 融合——RRF(Reciprocal Rank Fusion)按排名倒数加权,不依赖两路分数量纲,省掉归一化。
每路各取 top_k * 3 个候选,融合后截前 top_k。返回字段:text / title / source / doc_id / chunk_id / idx / score。
Agent 怎么用
启用后 knowledge_search 会被注册进工具总线(source 标为插件自身),模型在遇到「资料 / 文档 / 手册 / 我之前存过的内容」这类问题时自行调用:
用户:上次那份部署手册里,反向 WS 的端口是哪个?
模型 → knowledge_search(query="反向 WS 端口")
→ 返回【片段1】(来源:部署手册)……正文……
模型 → 依据片段作答返回条数取 rag_top_k(默认 5)。工具返回的是拼好的文本,不是结构化 JSON——直接进上下文,模型不必再解析一遍。
管理接口
| 方法与路径 | 作用 | 参数 |
|---|---|---|
POST /api/llm_core/kb/ingest | 入库 | body: title / text(必填)/ source |
GET /api/llm_core/kb/docs | 文档列表 + 总数 | 无 |
DELETE /api/llm_core/kb/docs/<doc_id> | 删除单篇(连带切片与向量) | 路径参数 |
GET /api/llm_core/kb/search | 检索自测 | query: q / k |
POST /api/llm_core/kb/clear | 清空全部 | 无,返回清掉的文档数 |
未启用时统一返回 {"code": 1, "msg": "知识库未启用"}。
排错速查
| 现象 | 原因与处理 |
|---|---|
| 面板「知识库」页说没启用 | rag.enabled 不是 true,或 base_url 没填;配置改完没重启 |
提示 rag 配置不是合法 JSON | 面板里这一项是字符串,内层引号要转义;用 json.loads 的规则检查一遍 |
| 入库报「向量化失败」 | embedding 端点不通 / Key 失效 / model 名不存在。先 curl 该端点的 /embeddings 单独验 |
| 报维度不匹配 | 换了模型但没改 dims,或库里存量向量是旧模型生成的。改 dims 后清空重建 |
| 检索结果全是同一篇 | 切片太碎(chunk_size 太小)或文档重复入库 |
| 中文短词搜不到 | 关键词那一路要 3 字符以上,2 字词只靠向量。提高 rag_top_k 或换更好的嵌入模型 |
装了 sqlite-vec 但后端还是 memory | vec0 建表失败会退回 memory(看启动日志的 warning)。维度不合法或扩展加载失败是常见原因 |
| 想换后端 | backend 改 sqlite_vec 前先清空库,两种后端的向量列结构不同 |