首页
/ Langchain-Chatchat 数据库模型解析:KnowledgeFileModel 与 FileDocModel 实现知识文件与向量文档的双层管理

Langchain-Chatchat 数据库模型解析:KnowledgeFileModel 与 FileDocModel 实现知识文件与向量文档的双层管理

2026-09-08 17:05:49作者:仰钰奇

导读

在 Langchain-Chatchat 的 RAG(检索增强生成)链路中,知识库文件从「上传入库」到「向量化检索」需要一套可靠的元数据登记机制。本文深入解析项目 knowledge_filefile_doc 两张核心表的 ORM 模型 —— KnowledgeFileModel(知识文件档案)与 FileDocModel(文件-向量文档关系表),并结合源码讲解字段语义、版本更新策略、__repr__ 调试表示,以及围绕它们实现的增删查改 repository 函数。读完本文,你将完整理解 Langchain-Chatchat 是如何用关系型数据库管理知识文件生命周期、并在文件与其切分后的向量文档之间建立映射的。

模型概览:两张表如何分工

知识库向量化的产物最终要落到两类东西上:原始物理文件(PDF、DOCX、TXT 等,位于知识库目录中)和切分后写入向量库的 Document。Langchain-Chatchat 用两个 SQLAlchemy ORM 模型分别登记它们的元数据:

ORM 类 对应数据库表 登记内容 核心源码位置
KnowledgeFileModel knowledge_file 物理文件的档案信息(文件名、大小、版本、切分数量等) knowledge_file_model.py
FileDocModel file_doc 文件与向量库 Document 的一对多映射 同上文件

两个模型都继承自 Basedeclarative_base() 生成的声明式基类),表引擎绑定在 Settings.basic_settings.SQLALCHEMY_DATABASE_URI 指定的数据库上。可以这样理解两者的关系:一张 knowledge_file 记录对应磁盘上的一个文件;一份文件经文本切分后产生 N 个向量 Document,就会在 file_doc 中写入 N 条记录。

KnowledgeFileModel:知识文件的「身份证」

KnowledgeFileModel 定义于 knowledge_file_model.py,表名为 knowledge_file。它登记了文件从「入库」到「切分」全过程所需的全部静态信息与状态信息。

字段与类型约束

属性 列类型 默认值 含义
id Integer,主键,自增 知识文件唯一标识 ID
file_name String(255) 文件名
file_ext String(10) 文件扩展名,如 .pdf.txt
kb_name String(50) 所属知识库名称
document_loader_name String(50) 文档加载器名称(如 PDFLoader
text_splitter_name String(50) 文本分割器名称(如 SpacyTextSplitter
file_version Integer 1 文件版本号
file_mtime Float 0.0 文件最后修改时间(Unix 时间戳,秒)
file_size Integer 0 文件大小(字节)
custom_docs Boolean False 是否使用自定义切分文档
docs_count Integer 0 切分后产生的文档数量
create_time DateTime func.now() 记录创建时间

从列约束可看出几个工程要点:

  • 类型即契约file_ext 限制在 10 字符内、file_size 以字节为单位的整型、file_mtimeFloat 存 Unix 时间戳。在更新文件信息时,务必按这些类型与约束传参,避免因类型不匹配(如把大小传成字符串)或违反约束导致数据写坏。
  • 默认值由模型兜底:新文件版本从 1 起步、file_size 默认 0custom_docs 默认 Falsecreate_time 由数据库函数 func.now() 自动填充,业务代码无需手动维护。

与真实文件对象 KnowledgeFile 的对应

KnowledgeFileModel 的字段并非凭空而来,它们大多直接取自项目中的 KnowledgeFile 运行时对象。KnowledgeFile 在构造时通过 os.path.splitext 解析出扩展名、通过 get_LoaderClass(self.ext) 按扩展名推断 document_loader_name、并以 Settings.kb_settings.TEXT_SPLITTER_NAME 作为 text_splitter_name;其 get_mtime()os.path.getmtime)与 get_size()os.path.getsize)则直接对应模型的 file_mtimefile_size 两列。这说明该表是磁盘文件系统元数据在关系库中的镜像,为上层检索与统计提供不受文件系统限制的稳定查询入口。

FileDocModel:文件到向量文档的映射关系

FileDocModel 定义于同一文件 knowledge_file_model.py,表名为 file_doc。它的作用是把「逻辑文件名」与「向量库中的真实 Document ID」关联起来。

字段结构

属性 列类型 默认值 含义
id Integer,主键,自增 唯一标识符
kb_name String(50) 文档所属知识库名称
file_name String(255) 文档对应的原始文件名
doc_id String(50) 向量库中文档的 ID
meta_data JSON {} 文档附加元数据

meta_data 的 JSON 语义

meta_data 是整张表中最灵活的字段。它以 JSON 列类型存储任意结构化附加信息(如 {"author": "张三", "year": "2021"}),使得后续可按任意元数据键做过滤检索。由于 Base 创建的 engine 显式注册了 json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False),中文元数据也能被正确序列化而不被转义。

doc_id 通常形如 c3392f0c-xxxx 这样的字符串。需要注意它在 knowledge_file_repository.py 中会被显式 int(_id[0]) 转换为整型返回,因此向量库侧的 Document ID 在使用时应当与 file_doc.doc_id 保持一致的编码格式。

模型不是孤立存在的:repository 层的完整操作链

模型(Model)只是表结构的定义,真正读写它们的是 knowledge_file_repository.py 中的一组 repository 函数。全部函数都通过 @with_session 装饰器自动获得数据库会话,会话由 session.py 中的 with_session 统一管理:进入时开启 session_scope() 上下文、提交事务,异常时自动 rollback 并抛出。以下按「文件档案」与「文档映射」两类梳理。

围绕 KnowledgeFileModel 的操作

  • count_files_from_db(kb_name):统计某知识库的文件数量,用 .filter(KnowledgeFileModel.kb_name.ilike(kb_name)).count()
  • list_files_from_db(kb_name):列出某知识库所有文件名,返回 [file_name, ...] 列表。
  • file_exists_in_db(kb_file):按 file_namekb_name 精确判断文件是否已在库中(ilike 匹配,返回布尔值)。
  • add_file_to_db(kb_file, docs_count, custom_docs, doc_infos):核心入库函数,逻辑分支清晰(见下节)。
  • delete_file_from_db(kb_file):删除单条文件记录,同时联动删除其全部 file_doc 映射,并将所属知识库的 file_count 减一。
  • delete_files_from_db(knowledge_base_name):级联删除整个知识库下的所有 knowledge_filefile_doc 记录,并把 KnowledgeBaseModel.file_count 归零。
  • get_file_detail(kb_name, filename):按文件定位记录并组装成包含全部字段的字典;不存在时返回空 {}

其中 delete_file_from_dbdelete_files_from_db 展示了项目中最典型的级联删除约束:为避免出现「向量库中有文档、但档案表已无记录」或反之的脏数据,删除文件时总是成对清理 KnowledgeFileModelFileDocModel,并同步维护知识库表的 file_count 计数,保证跨表数据一致性。这些计数与存在性查询正是 kb_service/base.pyfile_exists_in_dblist_files_from_dbcount_files_from_db 等服务方法的后端实现。

围绕 FileDocModel 的操作

  • list_file_num_docs_id_by_kb_name_and_file_name(kb_name, file_name):按知识库与文件名查出全部 doc_id 并转成整数列表。
  • list_docs_from_db(kb_name, file_name=None, metadata={}):返回形如 [{"id": str, "metadata": dict}, ...] 的文档列表;支持按 file_name 过滤,还支持把 metadata 字典逐键下钻为 FileDocModel.meta_data[k].as_string() == str(v) 的 SQL 条件,即按元数据键过滤向量文档。
  • add_docs_to_db(kb_name, file_name, doc_infos):批量把 doc_infos(形如 [{"id": str, "metadata": dict}])逐条构造成 FileDocModel 实例加入会话。源码中对 doc_infos is None 的防御分支提示读者:真实调用链中可能出现该参数为 None 的边界场景,调用方应自行保证传入非空。
  • delete_docs_from_db(kb_name, file_name=None):删除符合条件的映射,并返回被删除的文档信息列表。

add_file_to_db 的版本自增策略

add_file_to_db 是理解整个文件生命周期管理的关键,其逻辑位于 knowledge_file_repository.py

if existing_file:
    existing_file.file_mtime = mtime
    existing_file.file_size = size
    existing_file.docs_count = docs_count
    existing_file.custom_docs = custom_docs
    existing_file.file_version += 1
else:
    # 构造新 KnowledgeFileModel 并 kb.file_count += 1

即:同一知识库下重复上传同名文件不会新建记录,而是更新修改时间、大小、切分文档数并让 file_version 自增;首次入库则新建记录并累加知识库 file_count。无论新建还是更新,最后都会调用 add_docs_to_db 写入本次切分产生的文档映射。配合模型层 file_mtime(默认 0.0)、file_version(默认 1)的默认值设计,可以判断:file_version > 1 即代表该文件经历过二次向量化,非常适合作为缓存失效、增量更新的判断依据。

这些操作在哪里被触发

从源码结构看,这些 repository 函数并非仅被模型文档调用,而是贯穿整个知识库管理链路:

  • 知识库服务层 kb_service/base.pyfile_exists_in_dblist_files_from_dbcount_files_from_dblist_docs_from_db 等方法直接委托给上述函数;
  • 向量库服务(如 milvus_kb_service.py)在删除向量时使用 list_file_num_docs_id_by_kb_name_and_file_name 反查 doc_id 列表;
  • API 层 kb_doc_api.py 在向用户返回文件详情时调用 get_file_detail,将模型字段整理成可读字典。

这意味着:每当用户在 WebUI 或 API 中上传文件、删除文件、列出文件或查看文件详情,背后最终都会落到 knowledge_file / file_doc 两张表的读写上。

repr:面向调试的字符串表示

两个模型都实现了 Python 特殊方法 __repr__,用于生成对象的「官方字符串表示」,在调试与日志中最常用。它们不接收额外参数,返回的字符串按固定模板拼接关键字段:

KnowledgeFileModel.__repr__(见 knowledge_file_model.py)覆盖 8 个字段:idfile_namefile_extkb_namedocument_loader_nametext_splitter_namefile_versioncreate_time。例如:

<KnowledgeFile(id='1', file_name='example.pdf', file_ext='.pdf', kb_name='DefaultKB', document_loader_name='PDFLoader', text_splitter_name='SpacyTextSplitter', file_version='1', create_time='2023-04-01 12:00:00')>

FileDocModel.__repr__ 则覆盖 idkb_namefile_namedoc_idmeta_data 五个字段,例如:

<FileDoc(id='1', kb_name='知识库1', file_name='文件1.pdf', doc_id='doc123', metadata='{"author": "张三", "year": "2021"}')>

两点使用提示:其一,__repr__ 输出应能快速反映对象状态但保持简洁,便于在 logger 或交互式终端中一眼定位问题对象;其二,由于 meta_data 是 JSON 类型,repr 中打印的是其字符串形态,肉眼核对时要注意多层引号的转义。

实践注意点与字段维护建议

综合模型定义与 repository 源码,使用这两个模型时有几点需要特别注意:

  1. 更新信息时同步维护版本与时间字段file_versionfile_mtimefile_sizedocs_count 之间是强关联的 —— 文件内容一旦变化,mtime/size 必然变化,随之应触发一次重新切分与版本号自增。若只更新其中一列,将破坏数据的准确性与一致性。
  2. 删除必须成对进行:不要只删 knowledge_file 记录而遗留 file_doc 映射,否则检索时可能出现「查得到 doc_id 却查不到来源文件」的幽灵数据;项目实现中 delete_file_from_db / delete_files_from_db 已示范了正确的级联清理方式。
  3. 查询统一走 repository 而非手写 SQL:项目为两张表封装了成套函数(存在性判断、计数、列出、详情、增删),且统一处理了会话提交与回滚。业务代码直接调用 knowledge_file_repository.py 即可避免 Session 泄漏与事务边界混乱。
  4. 注意 custom_docs 的语义:该字段标记「是否使用自定义 docs」。当用户在界面上手工维护某文件的切分结果时,该字段会被置真,后续向量化逻辑需据此决定是否沿用系统默认切分流程。

关联阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393