Langchain-Chatchat 知识库模型解析:DocumentWithVSId 向量化文档对象的字段、调用链与实战用法
【免费下载链接】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
DocumentWithVSId是 Langchain-Chatchat 知识库(RAG)体系中最核心的数据载体之一,它定义了"经过向量化检索后返回给上层链路的文档"的标准结构:在 Langchain 原生Document的基础上扩展出id与score两个字段。本篇文章以 kb_document_model.md 文档为骨架,结合 模型定义源码、知识库服务层、文档 API、文档摘要与 Agent 工具注册等真实实现,系统讲解它的类结构、字段语义、三条典型调用链(搜索、列举、摘要),以及在参数调优与二次开发时的注意事项。读完本文,你将能准确理解"向量检索结果中的id与score从哪来、代表什么、如何被消费",并能据此正确使用search_docs/list_docs相关接口或在自定义知识库后端中复用它。
一、类定位:RAG 链路中"检索结果"的统一封装
在 Langchain-Chatchat 的知识库问答(RAG)流程中,一条完整的数据流大致是:用户提问 → 文本向量化 → 在向量库(Faiss / Milvus / Chroma / ES / PG 等)中做相似度检索 →得到若干"向量化后的文档"→ 拼装进 Prompt 交给大模型回答,或用于知识库文件管理、文档摘要等功能模块。
这一环节的返回结果对象正是DocumentWithVSId。正如文档中描述的那样,它"继承自Document类,用于表示一个经过向量化处理的文档",在知识库系统中承担"检索结果 / 处理结果"的统一表示职责。
模型定义文件位于 kb_document_model.py,完整实现十分精简:
from langchain.docstore.document import Document class DocumentWithVSId(Document): """ 矢量化后的文档 """ id: str = None score: float = 3.0可以看到它利用 Pydantic 模型继承机制,在 Langchain 的Document基础上只新增了两个字段:id(字符串,默认None)与score(浮点数,默认3.0)。底层Document本身自带的page_content(文档正文)与metadata(元数据字典)两个字段会被原样继承,因此一个完整的DocumentWithVSId实例实际承载的信息包括:正文内容、元数据、向量库中的唯一标识id、与查询的相关性评分score四部分。
二、字段语义:id 与 score 分别表示什么
结合 原文档 与源码实现,两个扩展字段的语义如下:
| 字段 | 类型 | 默认值 | 语义说明 |
|---|---|---|---|
id | str | None | 文档在知识库 / 向量库中的唯一标识符。文档指出"需保证其唯一性,确保每个实例能够准确对应知识库中的一个具体文档";实践中它常与向量索引中的 doc_id(如 Faiss 缓存中的文档 ID、metadata中的id)保持一致 |
score | float | 3.0 | 文档与检索查询的匹配度 / 相关性度量,会在搜索、排序等操作中被更新。文档特别提醒"取值可能随操作或上下文环境变化,使用时应注意其含义与计算方式" |
关于score的"方向",需要结合 Langchain-Chatchat 的检索实现来理解。在 kb_chat.py 的接口参数注释中明确写道:
知识库匹配相关度阈值,取值范围在 0-1 之间,
SCORE越小,相关度越高,取到 1 相当于不筛选,建议设置在 0.5 左右
因此在本项目中,score本质是一种距离 / 不相似度度量而非百分制相似度:数值越小表示与查询越相关,默认值3.0处于"不设限"的量级——即当一个文档实例未经检索打分、只做结构化包装时(例如在按file_name/metadata列举文档的场景下),它不会被误判为"高度相关"。
默认值3.0还有一个直接意义:任何通过DocumentWithVSId(id=..., score=3.0)形式构造的对象,在未进入打分流程前都带有这个"中性"评分,避免后续逻辑对未打分文档做无效的阈值截断。
三、构造方式:两种核心初始化模式
从源码看,DocumentWithVSId的实例化在整个代码库中遵循两种模式,它们都出现在知识库 API 与摘要链路中:
模式一:由底层Document展开后补齐id
在 kb_doc_api.py 中,搜索结果的包装逻辑为:
data = [DocumentWithVSId(**{"id": x.metadata.get("id"), **x.dict()}) for x in docs]即在保留page_content、metadata等字段的同时,从文档元数据的id键中提取唯一标识注入id字段(该文件同位置还保留了一行注释掉的旧写法DocumentWithVSId(**x[0].dict(), score=x[1], ...),从侧面印证了score在部分向量库检索实现中作为二元组第二项返回的历史形态)。
模式二:dict()展开后叠加外部id
在文档摘要接口 kb_summary_api.py 中:
doc_info_with_ids = [ DocumentWithVSId(**{**doc.dict(), "id": with_id}) for with_id, doc in zip(doc_ids, doc_infos) ]将"待摘要的文档 ID 列表"与"按 ID 取回的文档内容列表"一一配对,生成带 ID 的摘要输入。这里的with_id来自知识库服务层的kb.get_doc_by_ids(ids=doc_ids),是各向量库后端实现中真正存储的文档 ID。
此外在知识库服务基类 kb_service/base.py 的list_docs中,同样采用DocumentWithVSId(**{**doc_info.dict(), "id": x["id"]})的形式完成包装。
四、三种核心使用场景与底层调用链
原文档归纳了DocumentWithVSId的三个主要应用场景,下面逐一对应到仓库源码中的真实调用链。
场景一:知识库文档搜索(search_docs)
当调用"知识库文档搜索"接口时,入口位于 kb_doc_api.py 的search_docs:
- 通过
KBServiceFactory.get_service_by_name(knowledge_base_name)取得当前知识库的服务实例(Faiss / Milvus / Chroma / ES / PG 等具体实现); - 有查询词
query时调用kb.search_docs(query, top_k, score_threshold); - 服务基类的 search_docs 实现 会先校验 Embedding 模型可用性,再委托给各后端实现的
do_search(query, top_k, score_threshold)完成真正的相似度检索; - 返回的每个文档经
DocumentWithVSId包装后,通过 HTTP JSON 序列化([x.dict() for x in data])返回给调用方。
此处top_k与score_threshold的默认值分别来自配置项Settings.kb_settings.VECTOR_SEARCH_TOP_K与Settings.kb_settings.SCORE_THRESHOLD。这两个参数会直接影响返回的DocumentWithVSId数量与评分分布——在服务层检索时,低于score_threshold(即相似度过低 / 距离过大)的结果会被过滤掉。
该接口也被知识库对话直接复用:在 kb_chat.py 的知识库问答流程中,同样会以query、top_k、score_threshold等参数触发上面的搜索链路,随后将检索出的文档交给format_reference格式化,作为"引用出处"一并呈现在回答中。
场景二:按文件名 / 元数据列举与过滤文档(list_docs)
当知识库文件管理界面需要"按文件名或元数据列出文档内容"时,会走 kb_service/base.py 的 list_docs:
- 先从数据库仓库层
list_docs_from_db(kb_name, file_name, metadata)查出符合条件(支持文件名与一级键元数据过滤)的文档信息列表doc_infos; - 对每个
x通过get_doc_by_ids([x["id"]])[0]从向量库按 ID 取回真实文档内容; - 非空时包装为
DocumentWithVSId(**{**doc_info.dict(), "id": x["id"]})加入结果列表,为空时跳过该条(源码中的pass分支)。
对应的 HTTP 入口同样是 kb_doc_api.py,当请求只携带file_name或metadata而不携带query时触发。值得注意的一个实现细节是:为降低响应体积,该路径返回前会执行del d.metadata["vector"],从元数据中剔除向量字段后再序列化输出。
在该场景下返回的文档实例score保持默认的3.0,因为并未发生真正的相似度计算,这也再次验证了原文档"score 会随操作环境变化"的说明。
场景三:文档摘要(KBSummary)中的输入载体
DocumentWithVSId也是知识库文档摘要功能的输入格式。调用链位于 kb_summary_api.py 的recreate_summary_vector_store/summary_doc_ids_to_vector_store系列接口:
- 通过
kb.get_doc_by_ids(ids=doc_ids)按 ID 批量取回待摘要文档; - 如前述"模式二"构造出
doc_info_with_ids(即一串DocumentWithVSId); - 交给 summary_chunk.py 中的
SummaryAdapter(内部使用MapReduceDocumentsChain做分块-合并式摘要)的summarize(file_description=..., docs=doc_info_with_ids)生成摘要; - 摘要结果随后按原文档 ID 回写知识库摘要存储,从而支持后续的"摘要级"检索与引用。
在这里,id承担的是"把摘要结果与原始文档关联回写"的关键职责,缺失或错乱的id将直接导致摘要与原文无法对应。
五、在 Agent 工具与 WebUI 中的进一步消费
除了知识库服务层,DocumentWithVSId还被更高层的 Agent 与前端界面所消费:
- Agent 工具上下文组装:在 tools_registry.py 的
format_context中,Agent 搜索知识库得到的结果集docs会逐条执行DocumentWithVSId.parse_obj(doc)反序列化,再抽取doc.page_content拼装为给大模型的上下文文本;若结果为空,则生成"没有找到相关文档,请更换关键词重试"的提示语。这说明DocumentWithVSId不仅存活在服务端内部,还会以 JSON 形态跨进程传输后被再次解析。 - WebUI 对话页:在 dialogue.py 中,该模型类被导入用于对知识库引用文档做类型化处理(与
format_reference配合渲染答案下方的引用出处)。从导入关系看,WebUI 直接引用的是chatchat.server.knowledge_base.model.kb_document_model,即本模型文件中的类,而 Agent 侧则经由kb_doc_api间接导入。
由此可见,从"向量库检索结果"到"对话引用展示",再到"Agent 工具返回",DocumentWithVSId是整个知识库召回结果的标准数据契约。
六、源码结构上的设计说明与注意事项
最后,结合源码与文档的"注意"提示,归纳几条对二次开发最有价值的设计要点:
- 极轻量的继承扩展:模型文件本身只有 3 个有效代码行,设计上刻意不复制 Langchain
Document的字段,而是依靠 Pydantic 继承自动获得page_content、metadata等能力。任何知识库后端只要返回"标准Document+ 额外 ID",即可无损转换为DocumentWithVSId。 id的唯一性约定:id默认None,但按文档要求它应当与知识库 / 向量库中的某个具体文档一一对应。在 Faiss 等本地缓存实现与数据库记录之间,正是靠这个id完成"文件—分块—向量—摘要"的多层关联,因此不要随意置空或复用。score的语义随场景漂移:检索场景下它是"越小越相关"的距离型度量,并受SCORE_THRESHOLD(默认由 kb_settings 配置)影响;列举场景下它保持默认值3.0,不参与任何过滤。消费端切勿假设"score 高即相关"。- 直接可用的序列化能力:由于继承自 Pydantic 模型,
DocumentWithVSId天然支持dict()(源码中多次使用)、parse_obj()(Agent 工具侧使用)与 JSON 序列化,跨进程、跨模块传递非常方便,这解释了为何它能在 API 返回体与 WebUI 中自由流动。 - 扩展自定义字段的落点:如需要给检索结果附加更多信息(如来源文件、页码、块序号等),惯例是写入继承自
Document的metadata字典,而不是修改模型类的字段集——项目各 API 均已按此约定从metadata读取或剔除(如id从metadata["id"]提取、vector从metadata中删除)。
七、小结
DocumentWithVSId虽然是一个仅含两个新增字段的轻量数据类,却是 Langchain-Chatchat 知识库 RAG 链路承上启下的关键"协议":Document负责承载内容与元数据,id负责打通"数据库记录 ↔ 向量库索引 ↔ 摘要结果"的映射关系,score则承载检索相关性信息并贯穿VECTOR_SEARCH_TOP_K、SCORE_THRESHOLD等调优参数。无论是阅读 搜索文档 API、编写自定义知识库后端,还是二次开发 Agent 知识库工具,理解这一模型类的字段语义与三条构造 / 消费路径都是绕不开的第一步。
【免费下载链接】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),仅供参考