Langchain-Chatchat 知识库迁移机制全解:从建表、三种向量库重建模式到数据清理与版本升级导入
【免费下载链接】Langchain-ChatchatLangchain-Chatchat(原Langchain-ChatGLM)基于 Langchain 与 ChatGLM, Qwen 与 Llama 等语言模型的 RAG 与 Agent 应用 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM, Qwen and Llama) RAG and Agent app with langchain项目地址: https://gitcode.com/GitHub_Trending/la/Langchain-Chatchat
导读
Langchain-Chatchat(基于 Langchain 的本地知识库 RAG 与 Agent 应用)把"知识库文档入库"拆成两套存储体系——SQLite 元数据库(info.db 等,记录文件与切分信息)与向量数据库(默认 faiss,也可用 milvus/pg/chromadb 等)。当本地content文件夹中已有现成文档、而数据库或向量库尚未同步(例如更换了 Embedding 模型、调整了默认向量库类型、或数据库结构随版本升级变化)时,就需要一套可复用的"迁移"能力。本文将围绕 migrate.md 与其底层实现 migrate.py,完整讲解建表/重置表、从旧 SQLite 备份导数据、以本地文件夹为基准填充库的三种模式(recreate_vs / update_in_db / increment)、双向清理(prune_db_docs / prune_folder_files),并结合 init_database.py 的 CLI 参数与 test_migrate.py 给出可直接落地的操作方案。
读完本文,你将掌握:知识库元数据表是如何被创建与重置的、版本升级时如何不动向量库地导入旧库数据、如何按需"全量重建 / 只更新库内文件 / 增量补建"向量索引,以及如何通过两条 prune 命令让本地文件与数据库保持最终一致,避免磁盘被无用文档占满或数据库残留已删除文件的脏记录。
一、迁移模块的整体定位
在 chatchat-server 的包结构中,该模块位于 server/knowledge_base/migrate.py,是知识库管理(knowledge_base 文档组)中最核心的"数据库 ↔ 本地文件夹 ↔ 向量库"三向同步工具。它对外暴露七个主要函数:
| 函数 | 职责 | 影响面 |
|---|---|---|
create_tables | 依据 ORM 元数据创建全部数据库表 | 数据库结构 |
reset_tables | 先 drop 全部表再重建,回到干净初始态 | 数据库结构(破坏性) |
import_from_db(sqlite_path) | 从旧版 SQLite 备份把行数据导入当前库 | 仅数据,不动向量库 |
file_to_kbfile(kb_name, files) | 文件名列表 →KnowledgeFile对象列表 | 公共辅助 |
folder2db(kb_names, mode, ...) | 以本地文件夹为基准执行三种模式的入库 | 数据库 + 向量库 |
prune_db_docs(kb_names) | 删除"库里有、本地文件夹已无"的文档 | 数据库 + 向量库 |
prune_folder_files(kb_names) | 删除"本地有、库里未登记"的文档文件 | 本地磁盘(破坏性) |
从源码调用关系看(init_database.py 一次性导入了create_tables、reset_tables、folder2db、import_from_db、prune_db_docs、prune_folder_files),这些函数是知识库初始化与迁移 CLI 的全部底层动作。
二、表结构的创建与重置:create_tables 与 reset_tables
2.1 create_tables:只建不动的安全函数
create_tables的实现只有一行核心逻辑(见 migrate.py):
def create_tables(): Base.metadata.create_all(bind=engine)Base是 SQLAlchemy 的声明式 ORM 基类,集中持有本项目全部表模型的元数据;相关模型集中在 server/db/models(对话、消息、知识库、知识文件、知识元数据、MCP 连接、HumanMessageEvent 等表)。engine是 SQLAlchemy 连接引擎,实际连接目标数据库。- SQLAlchemy 的
create_all采用"只创建不存在的表"语义:已存在的表不会被修改或更新,因此该函数是幂等且相对安全的,适合在初始化与启动流程中反复调用。
它的实际调用场景包括(均可从源码确认):
- 项目初始化:
chatchat init在 cli.py 中先执行create_tables(),保证数据表齐全后再继续; - 服务器启动:若表不存在则先建表,见 startup.py 中对
create_tables的导入; - 重置流程:
reset_tables删除后调用它重建(见 2.2); - 向量库相关测试:如 tests/kb_vector_db/test_faiss_kb.py、test_milvus_db.py、test_pg_db.py 在用例前都先 import
create_tables以保证测试环境表结构正确。
注意:因为它不会更新已有表结构,所以当升级后的模型新增了字段、表结构发生变更时,需要配合数据库备份迁移(见第三节 import_from_db)或人工执行 DDL,单纯调用本函数无法完成结构升级。
2.2 reset_tables:彻底清空后重建
def reset_tables(): Base.metadata.drop_all(bind=engine) create_tables()先drop_all删除当前数据库中的全部表,再调用create_tables重建,得到一份干净的初始表结构(见 migrate.py)。
- 典型用途:测试环境重置、或希望彻底清空元数据后从头重建知识库登记信息。
- 高危警告:会删除所有表及其中数据且不可回滚,生产环境使用前必须备份数据库文件;文档与源码(函数 docstring 与注释)均强调了这一点。
2.3 对应 CLI:--create-tables 与 --clear-tables
在 init_database.py 的worker中,参数被映射为:
| CLI 参数 | 触发动作 |
|---|---|
--create-tables | 调用create_tables(),确认表存在 |
--clear-tables | 先reset_tables(),再打印database tables reset |
# 只补建缺失的表 python chatchat/init_database.py --create-tables # 清空并重建全部表(危险操作,请先备份) python chatchat/init_database.py --clear-tables三、版本升级数据导入:import_from_db
3.1 适用场景与前提
Langchain-Chatchat 的知识库元数据存放在 SQLite(info.db)中。当版本升级导致 info.db 的表结构变化,但文档内容与已向量化的数据没有变化时,没有必要重新做一遍全文切分与向量化——只需把旧库里的结构化数据搬进新库即可。这正是import_from_db的存在意义(docstring 见 migrate.py)。
使用前提有三条,必须同时满足:
- 传入的
sqlite_path是合法的 SQLite 数据库文件路径; - 备份库的表名、需要导入的字段名与当前 ORM 模型一致(函数只做"名称交集"过滤,不负责重命名);
- 当前仅支持 SQLite(直接使用
sqlite3标准库连接),其它数据库备份不在支持范围内。
3.2 实现原理与字段处理
核心流程(migrate.py):
models = list(Base.registry.mappers):拿到当前注册的全部 ORM 模型映射;- 连接旧库,通过
sqlite_master查得全部表名; - 遍历每个模型,取
model.local_table.fullname与旧库表名比对,表不存在则跳过该模型; - 对存在的表逐行
select *,再用{k: row[k] for k in row.keys() if k in model.columns}做字段名交集过滤,只保留当前模型确实存在的列; - 特殊处理时间字段:若行含
create_time,调用dateutil.parser.parse(见文件顶部from dateutil.parser import parse)把字符串解析为正确的时间对象,避免 SQLite 时间格式与 ORM DateTime 列类型冲突; - 在
session_scope()上下文内session.add(model.class_(**data))逐行添加,依赖上下文管理器自动提交/回滚(会话管理实现在 server/db/session.py); - 全部成功则关闭连接并返回
True;任何异常打印无法读取备份数据库:{sqlite_path}。错误信息:{e}并返回False。
3.3 CLI 用法
python chatchat/init_database.py --import-db /path/to/old/info.db执行前务必停止一切对目标库的读写操作(源码注释明确要求避免数据冲突),并确认新库的表已创建(必要时先跑--create-tables)。
四、以本地文件夹为基准填充数据库与向量库:folder2db
4.1 参数清单
folder2db是本模块的重头戏。完整签名与默认值见 migrate.py,其中多个默认值直接来自Settings.kb_settings(定义于 settings.py,默认向量类型、切分参数等集中在此):
| 参数 | 类型/取值 | 默认值 | 说明 |
|---|---|---|---|
kb_names | List[str] | —(为None时取list_kbs_from_folder(),即KB_ROOT_PATH下全部知识库目录) | 要处理的知识库名称列表 |
mode | "recreate_vs"/"update_in_db"/"increment" | 无(必填) | 迁移模式,语义见 4.2 |
vs_type | "faiss"/"milvus"/"pg"/"chromadb" | Settings.kb_settings.DEFAULT_VS_TYPE(默认"faiss") | 向量库类型(注意:全局设置项还支持 zilliz/es/relyt,此处函数级字面量限定了这四种) |
embed_model | str | get_default_embedding()(默认模型取配置,如bge-m3) | Embedding 模型名 |
chunk_size | int | Settings.kb_settings.CHUNK_SIZE(默认750) | 文本切分块大小(字符数) |
chunk_overlap | int | Settings.kb_settings.OVERLAP_SIZE(默认150) | 相邻分块重叠大小 |
zh_title_enhance | bool | Settings.kb_settings.ZH_TITLE_ENHANCE(默认False) | 是否启用中文标题增强切分 |
4.2 三种迁移模式的语义(务必先理解再操作)
源码 docstring 与分支实现(migrate.py)给出了清晰差异:
| 模式 | 触发分支 | 行为 | 适用场景 |
|---|---|---|---|
recreate_vs | kb.clear_vs()→create_kb()→ 全量 files2vs →save_vector_store() | 清空向量库并从本地文件夹全量重建,同时把全部本地文件登记入库 | 拷贝了新文档到content目录但向量库尚未填充;或更换了DEFAULT_VS_TYPE/DEFAULT_EMBEDDING_MODEL需要整体重向量化 |
update_in_db | 取kb.list_files()(以数据库记录为基准)→ files2vs → save | 只更新"数据库里已存在"文件的向量,跳过仅存在于本地目录的文件 | 只想为已入库文件重算向量(例如换了切分参数后刷新) |
increment | files = set(folder_files) - set(db_files)→ files2vs → save | 只为"本地存在但数据库没有"的文件增量建向量与登记 | 日常增量入库新文档 |
执行流程先为每个kb_name通过KBServiceFactory.get_service(kb_name, vs_type, embed_model)获取服务实例(工厂实现见 server/knowledge_base/kb_service/base.py),若知识库尚不存在则create_kb()。每个知识库处理完毕后会打印汇总统计,包括:知识库名称、类型(kb.vs_type())、向量模型、文件总数量、入库文件数、知识条目数(各文件切分文档数求和)、用时,并仅对 FAISS 类型额外打印知识库路径(kb.kb_path,见 migrate.py)。
4.3 内部批量入库:files2vs
files2vs(kb_name, kb_files)是folder2db内部定义的辅助函数(migrate.py),承担"文件 → 文档 → 向量"的流水线:
- 调用
files2docs_in_thread(实现于 server/knowledge_base/utils.py,基于多线程)把KnowledgeFile列表转成文档,期间应用chunk_size、chunk_overlap、zh_title_enhance三个切分参数; - 对每个返回元组
(success, res)判定:失败则直接打印错误;成功则解包出文件名与文档列表; - 重建一个
KnowledgeFile,把切分好的splited_docs挂到kb_file.splited_docs上; - 调用知识库服务实例
kb.add_doc(kb_file, not_refresh_vs_cache=True)入库——注意not_refresh_vs_cache=True,即单文件入向量库后不立即刷新缓存,批量完成后再由外层统一kb.save_vector_store(),既提升批量性能也保证最终一致性; - 把
{"kb_name", "file", "docs"}追加进 result,供外层统计成功数。
4.4 CLI 触发方式
三种模式都通过worker分派(init_database.py),与命令行参数的对应关系如下:
# 全量重建所有知识库(-e 可换 Embedding 模型,-n 可限定知识库名,可重复传) python chatchat/init_database.py -r python chatchat/init_database.py -r -e text2vec-base-chinese -n samples # 只更新数据库中已存在文件的向量 python chatchat/init_database.py -u # 增量补建:只为本地有而库里没有的文件建向量 python chatchat/init_database.py -i # 参数释义(--help 输出原文核心语义) # -r/--recreate-vs : 已把文档放进 content 目录但向量库未建,或 DEFAULT_VS_TYPE/DEFAULT_EMBEDDING_MODEL 已变更时使用 # -u/--update-in-db : 为已入库文件重建向量,跳过只在本地目录的文件 # -i/--increment : 为本地存在、库中不存在的文件增量创建向量 # -n/--kb-name : 指定要操作的知识库名,默认处理 KB_ROOT_PATH 下全部目录;可多次指定 # -e/--embed-model : 指定 Embedding 模型,默认 get_default_embedding()此外,若使用统一 CLI 入口,chatchat kb命令会转发到本模块的main(见 cli.py 的main.add_command(kb_main, "kb")),chatchat init -r --recreate-kb则会在初始化阶段直接调用一次folder2db(kb_names=..., mode="recreate_vs", ...)(cli.py),并建议随后执行chatchat start -a启动服务。
4.5 测试验证:三种模式都有用例背书
tests/test_migrate.py 从正反两侧验证了迁移流程:test_recreate_vs先建测试知识库test_kb_for_migrate(素材readme.md拷贝自仓库根目录),调用folder2db([kb_name], "recreate_vs")后断言知识库存在、文件在kb.list_files()中、且每个切分文档的metadata["source"]均等于文件名(同时覆盖按文件名与按 metadata 两种list_docs检索);test_increment先清空向量库使list_files()==[],再以increment模式重建并做同样断言。这意味着"全量重建 / 增量补建都能得到可被元数据检索命中的向量索引"是有自动化用例保证的。
五、本地文件与数据库的双向清理
用户常直接在文件浏览器里增删content目录下的文档,这会造成本地与数据库/向量库状态漂移。模块提供方向相反的两个清理函数,原理都是"求差集 + 复用服务能力"。
5.1 prune_db_docs:删除库中残留的"幽灵文档"
场景:本地文件已被删除,但数据库记录与向量还留着。prune_db_docs(kb_names)(migrate.py)的处理步骤:
KBServiceFactory.get_service_by_name(kb_name)按名称取服务实例,取不到(如drop_kb后返回None)则跳过该库;kb.list_files()拿到库内文件,list_files_from_folder(kb_name)(utils 中实现)拿到本地目录文件;- 求
set(files_in_db) - set(files_in_folder)得到"库里有、本地无"的集合; - 经
file_to_kbfile转成KnowledgeFile后逐个kb.delete_doc(kb_file, not_refresh_vs_cache=True)并打印success to delete docs for file: kb_name/file; - 全部删完后调用一次
kb.save_vector_store()统一落盘向量缓存。
5.2 prune_folder_files:删除本地未被登记的"孤儿文件"
场景:某些文件从未入库(或库内记录已删),留在磁盘上白白占用空间。prune_folder_files(kb_names)(migrate.py)方向相反:
- 同样先取服务实例并跳过不存在的库;
- 求
set(files_in_folder) - set(files_in_db),即本地有、库中无; - 对每个文件调用
os.remove(get_file_path(kb_name, file))直接物理删除,并打印success to delete file: kb_name/file。
高危提醒:os.remove是物理删除且不可逆,执行前务必确认这些文件确实无需保留(例如已人工核对不需要入库),并做好备份。
对应 CLI(init_database.py)为:
# 用户删除了文件浏览器中的文档后,同步清掉数据库里的对应记录与向量 python chatchat/init_database.py --prune-db # 释放磁盘:删除本地存在但数据库未登记的无用文件 python chatchat/init_database.py --prune-folder两者也建议配合-n限定知识库范围,避免误伤其它库。测试方面,test_prune_db(删除本地文件→prune_db_docs→断言库内文件与 docs 均消失)与test_prune_folder(先删库内 doc→prune_folder_files→断言本地文件被物理删除)共同验证了这两条清理链路的正确性(见 test_migrate.py)。
六、file_to_kbfile:两条主流程共用的文件封装
file_to_kbfile(kb_name, files)(migrate.py)把"知识库名 + 文件名列表"统一转成KnowledgeFile列表:遍历文件列表逐个构造KnowledgeFile(filename=file, knowledge_base_name=kb_name),单个文件构造失败(如格式不支持)时打印{异常类}: {e},已跳过并继续,返回成功构造的对象列表。
KnowledgeFile类定义于 server/knowledge_base/utils.py,是贯穿全文档加载、切分、入库流程的核心载体。该辅助函数在folder2db三种模式(migrate.py)与prune_db_docs中都被复用,是理解上述流程的最小公共单元。调用示例:
from chatchat.server.knowledge_base.utils import KnowledgeFile kb_files = file_to_kbfile("demo_kb", ["document1.md", "document2.txt"]) # 结果形如 [KnowledgeFile(filename="document1.md", knowledge_base_name="demo_kb"), # KnowledgeFile(filename="document2.txt", knowledge_base_name="demo_kb")]注意:调用前需确保文件真实存在于磁盘;若依赖日志排查跳过原因,日志详细度受全局log_verbose配置控制。
七、操作速查与实践建议
7.1 常见运维场景的推荐组合
| 你的需求 | 推荐命令/调用 |
|---|---|
| 刚部署完、首次把本地知识库文档向量化 | chatchat init(必要时加-r/--recreate-kb)或python chatchat/init_database.py -r |
| 换了 Embedding 模型 / 改了默认向量库类型,需要整体重建 | python chatchat/init_database.py -r -e <新embed模型> |
| 只希望已入库文件按新切分参数重向量化 | python chatchat/init_database.py -u |
每天往content目录丢新文档,想只补增量 | python chatchat/init_database.py -i |
| 在文件管理器里删了文档,想清库中残留 | python chatchat/init_database.py --prune-db |
| 本地积压大量未登记文件想释放磁盘 | python chatchat/init_database.py --prune-folder(先确认无用) |
| 大版本升级后 info.db 结构变化但向量无需重建 | python chatchat/init_database.py --import-db <旧库路径> |
| 测试环境表结构脏了想重置 | python chatchat/init_database.py --clear-tables |
7.2 三条必须牢记的边界
create_tables只建新表不改旧表:结构升级依赖import_from_db或人工迁移,不要把建表函数当作升级工具;reset_tables与prune_folder_files具备破坏性:一个清空全部表、一个物理删文件,生产环境操作前必须备份;import_from_db当前仅支持 SQLite,并要求备份库的表名与字段名(交集)和当前 ORM 模型兼容;执行时保证目标库无并发写入。
7.3 再探一步
想要在业务代码中直接调用而不走 CLI,可参照 test_migrate.py 的写法引入:
from chatchat.server.knowledge_base.migrate import ( create_tables, reset_tables, import_from_db, file_to_kbfile, folder2db, prune_db_docs, prune_folder_files, )例如在初始化脚本里先create_tables()保证表存在,再按模式调用folder2db(["samples"], "increment")增量入库,完成后通过KBServiceFactory.get_service_by_name校验list_files()。函数入参默认值统一来自Settings.kb_settings(见 settings.py 与对应配置文档 settings.md),即切分与向量化行为与全站配置保持一致,无需在每次调用时手工重复传入。
【免费下载链接】Langchain-ChatchatLangchain-Chatchat(原Langchain-ChatGLM)基于 Langchain 与 ChatGLM, Qwen 与 Llama 等语言模型的 RAG 与 Agent 应用 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM, Qwen and Llama) RAG and Agent app with langchain项目地址: https://gitcode.com/GitHub_Trending/la/Langchain-Chatchat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考