图_2文档入库模块 · 详细实现
对应 [[2_代码实现_文档入库]]。保留全部文件、类与逐函数分步内容。
读图方式
- 蓝底是文件,紫底是类,灰底是函数;框宽按文字自适应,标题与分步文本分开。
- 每个函数 / 类有唯一编号,例如 F01 / C01;步骤末尾
〔→ F07〕表示调用或依赖对应编号。P 编号表示文件;P02:3精确定位到 P02 文件的第 3 步,F04:2定位函数 F04 的第 2 步。 - 只有相邻、能在文件内短距离连接且不穿越文字 / 其他函数框的关系画曲线。其余保留编号引用。重复使用的公共助手不再扇出多条长线。
- 编号仅用于查找,不表示全局执行顺序;每个框里的 1、2、3 才是该框的执行步骤。总图另有问答与入库主链。
文件与函数索引
P01 · backend/indexing/document_loader.py
统一多格式加载并生成三级 Chunk。
- F01
sanitize_text()(源码 16 行):清洗待索引的字符串。 - F02
DocumentLoader.__init__()(源码 54 行):按输入参数建立三层递归分块器。 - F03
DocumentLoader.load_documents_from_folder()(源码 222 行):遍历文件夹并累积支持格式的 Chunk。 - F04
DocumentLoader.load_document()(源码 195 行):按扩展名选择加载分支。 - F05
DocumentLoader._load_from_langchain_docs()(源码 162 行):统一来源元数据并逐页调用分块。 - F06
DocumentLoader._split_page_to_three_levels()(源码 85 行):在当前页内嵌套生成 L1 / L2 / L3。 - F07
DocumentLoader._build_chunk_id()(源码 82 行):构造页内层级 Chunk 的确定性 ID。 - C01
DocumentLoader:持有三个分块器,统一不同格式输出。
P02 · backend/indexing/html_processor.py
把 HTML 正文转为有章节页码的 Document。
- F08
load_html_for_document_loader()(源码 152 行):将网页章节封装为统一加载器输入。 - F09
parse_html_file_to_sections()(源码 139 行):编排编码识别、正文提取和章节切分。 - F10
_read_html_text()(源码 21 行):按 BOM、charset 和回退顺序解码。 - F11
_strip_noise()(源码 41 行):就地移除 HTML 非正文噪声。 - F12
_pick_root()(源码 48 行):选择优先级最高的正文根节点。 - F13
_doc_title()(源码 108 行):提取页面 title 字符串。 - F14
_br_to_newlines()(源码 52 行):保留显式换行标签的含义。 - F15
_linearize_blocks()(源码 57 行):按语义块生成较稳定的正文文本。 - F16
_split_into_sections()(源码 115 行):按二级标题拆成虚拟页面。
P03 · backend/indexing/embedding.py
为入库和 Query 复用同一个本地 Dense 模型。
- F17
_create_dense_embedder()(源码 6 行):根据配置创建归一化 Dense 编码器。 - F18
EmbeddingService.__init__()(源码 19 行):持有一个本地 Dense 编码器。 - F19
EmbeddingService.get_embeddings()(源码 22 行):批量编码文本列表。 - C02
EmbeddingService:向调用方暴露统一批量编码方法。
P04 · backend/indexing/milvus_writer.py
把调用方选出的 L3 Chunk 批量编码入库。
- F20
MilvusWriter.__init__()(源码 11 行):注入或复用向量服务与 Store。 - F21
MilvusWriter.write_documents()(源码 15 行):按批次编码并写入完整叶块记录。 - C03
MilvusWriter:只负责编码和写入,父块保存由调用方负责。
调用与对象传递对照
| 出发位置 | 目标 | 关系 |
|---|---|---|
| F03 · 第 2 步 | F04 DocumentLoader.load_document |
调用辅助函数 |
| F04 · 第 3 步 | F05 DocumentLoader._load_from_langchain_docs |
调用辅助函数 |
| F05 · 第 3 步 | F01 sanitize_text |
调用辅助函数 |
| F05 · 第 4 步 | F06 DocumentLoader._split_page_to_three_levels |
调用辅助函数 |
| F06 · 第 5 步 | F07 DocumentLoader._build_chunk_id |
调用辅助函数 |
| F04 · 第 2 步 | F08 load_html_for_document_loader |
HTML 适配分支 |
| F08 · 第 1 步 | F09 parse_html_file_to_sections |
调用辅助函数 |
| F09 · 第 1 步 | F10 _read_html_text |
调用辅助函数 |
| F09 · 第 2 步 | F11 _strip_noise |
调用辅助函数 |
| F09 · 第 2 步 | F12 _pick_root |
调用辅助函数 |
| F09 · 第 2 步 | F13 _doc_title |
调用辅助函数 |
| F09 · 第 3 步 | F15 _linearize_blocks |
调用辅助函数 |
| F09 · 第 4 步 | F16 _split_into_sections |
调用辅助函数 |
| F15 · 第 1 步 | F14 _br_to_newlines |
调用辅助函数 |
| P03 · 第 2 步 | F18 EmbeddingService.__init__ |
导入时创建共享模型 |
| F18 · 第 1 步 | F17 _create_dense_embedder |
创建编码器 |
| F21 · 第 3 步 | F19 EmbeddingService.get_embeddings |
texts → Dense 向量 |
[[source/_posts/LLM/2_RAG/project/img/详细实现/项目实现|查看项目实现总图]]。概要图仍保留在“实现”文件夹。
Excalidraw Data
Text Elements
backend/:本模块的后端实现 ^7c1228f4
indexing/:解析、编码及存取知识库文档 ^a320a655
[[2_代码实现_文档入库|P01 document_loader.py:文档处理入口,统一多格式加载并生成三级 Chunk]] ^69dd485b
-
编译文本清洗正则去除指定字符 ^5e5a0c11
-
封装文档的加载和切分服务 ^6bb87c30
F01 sanitize_text():清洗待索引的字符串 ^cee41104
- 规范化为 Unicode NFC 格式
- 去除零宽、BOM、方向控制、非排版控制与 PUA 字符。
- UTF-8 ignore 编解码去除孤立代理项 ^699287fe
C01 DocumentLoader:文本加载和分块 ^0efc2c82
- 同一页先大后小嵌套分块;返回字典中的父子 ID 供后续 Auto-Merging 使用。 ^a0679b2d
F02 init():按输入参数建立三层递归分块器 ^3352e2ab
- 设置三层分块器的配置: chunk_size / overlap
- 创建三个 递归分割器,配置分隔符 ^8eb23e04
F03 load_documents_from_folder():遍历文件夹并累积支持格式的 Chunk ^4b4d288c
-
遍历目录,仅保留 PDF / DOC(X) / XLS(X) / HTML(HTM) 后缀。 ^541a2bb3
-
拼接路径,逐个调用 load_document(),把结果追加到总列表。 〔→ F04〕 ^c59cec97
-
单个文件处理异常时跳过该文件;最后返回累积结果。 ^1de66adc
F04 load_document():按扩展名选择加载分支 ^643f3dc1
-
PDF / Word / Excel 分别创建对应 LangChain Loader;不支持的类型直接抛 ValueError。 ^ab0fe51f
-
HTML 延迟导入 load_html_for_document_loader(),加载后直接交给统一入口 。 〔→ F08〕 ^edaaf4eb
-
其他格式先 loader.load(),再调用 _load_from_langchain_docs()。 〔→ F05〕 ^649c1209
-
非 HTML 加载 / 分块异常包装为处理文档失败后抛出;HTML 分支在该 try 之外 。 ^46692425
F05 _load_from_langchain_docs():统一来源元数据并逐页调用分块 ^b7ba211a
-
初始化结果列表和跨页累计 chunk_idx。 ^a95232bd
-
逐 Document 获取 metadata.page,尽量转为 int,异常时使用 0。 ^b95b91f3
-
sanitize_text() 清洗 filename、file_path 和页内正文,构建 base_doc。 〔→ F01〕 ^f7b628a2
-
调用 _split_page_to_three_levels(),将页内三层结果追加;累计索引增加本页块 数。 〔→ F06〕 ^744c4416
F06 _split_page_to_three_levels():在当前页内嵌套生成 L1 / L2 / L3 ^c28e1bf1
-
空正文直接返回 [];读取页码与来源,重置本页各层计数。 ^ffc81f0a
-
L1 splitter 切页内正文;非空 L1 生成 ID,parent 为空、root 指向自己。 ^c9daf0f3
-
每个 L1 再切 L2;L2 的 parent 指向当前 L1,root 仍为 L1。 ^aa31e43c
-
每个 L2 再切 L3;L3 的 parent 指向当前 L2,root 指向所属 L1。 ^d8e62e4a
-
每层调用 _build_chunk_id(),保存正文 / 层级 / 全局 chunk_idx;返回三层混合 列表。 〔→ F07〕 ^b5507877
F07 _build_chunk_id():构造页内层级 Chunk 的确定性 ID ^4d3b3e51
-
按 filename::p页码::l层级::层内序号 拼接字符串。 ^6d4bed78
-
层内序号由当前页相应层级的计数器提供。 ^dacc6448
[[2_代码实现_文档入库|P04 milvus_writer.py:把调用方选出的 L3 Chunk 批量编码入库]] ^95c7e343
-
导入共享 embedding_service 和 get_milvus_store()。 ^921e8886
-
调用方先保存 L1 / L2,再将 L3 传给 MilvusWriter;Writer 自身不按 chunk_level 过滤 。 ^4eb8f8c4
C03 MilvusWriter:只负责编码和写入,父块保存由调用方负责 ^749b8747
- 持有 embedding_service 与 milvus_manager;异常继续向调用方传播。 ^e3bc1d28
F20 init():注入或复用向量服务与 Store ^912cfbc8
-
显式传入服务时使用注入对象,否则复用全局 embedding_service。 ^026cc2bb
-
显式传入 Store 时使用它,否则 get_milvus_store() 获取共享 Store。 ^3a72d061
F21 write_documents():按批次编码并写入完整叶块记录 ^ca597f65
-
空 documents 直接返回;读取 Dense 维度并调用 init_collection()。 ^d2b30bb5
-
默认每 50 条取一批,抽取各条 text。 ^0bb11f74
-
embedding_service.get_embeddings(texts) 批量生成 Dense 向量。 〔→ F19〕 ^3c9d9350
-
将向量、全文、来源、页码和 Chunk 关联 ID 组装为 insert_data,调用 Store.insert()。 ^a6fc0b0c
-
若有 progress_callback,以已处理条数 / 总条数回调;循环处理下一批。 ^cc3eb994
[[2_代码实现_文档入库|P02 html_processor.py:把 HTML 正文转为有章节页码的 Document]] ^b0eedeb8
-
导入 BeautifulSoup、Comment、正则与 LangChain Document。 ^2c4109f2
-
提供 HTML → 章节列表 → Document 的适配函数,交回 DocumentLoader 复用三级分块。 ^9a7282f9
F08 load_html_for_document_loader():将网页章节封装为统一加载器输入 ^186f92b2
-
调用 parse_html_file_to_sections() 获得章节列表。 〔→ F09〕 ^5d3ebaeb
-
逐章节创建 Document(page_content=text)。 ^11517c77
-
metadata 设置 page、source=filename、section_title;返回 Document 列表 。 ^d6301adc
F09 parse_html_file_to_sections():编排编码识别、正文提取和章节切分 ^6d3e2ecc
-
Path() 规范路径,_read_html_text() 读取字符串,BeautifulSoup 解析 HTML。 〔→ F10〕 ^42648c05
-
_strip_noise() 去无关节点,_pick_root() 选择正文,_doc_title() 提取标题。 〔→ F11 / F12 / F13〕 ^1911281d
-
调用 _linearize_blocks() 得到线性文本;有标题且正文未以标题开头时补 h1。 〔→ F15〕 ^202118bc
-
交给 _split_into_sections() 返回带 page / title / text 的章节列表。 〔→ F16〕 ^3df13981
F10 _read_html_text():按 BOM、charset 和回退顺序解码 ^906a4668
-
读取原始 bytes;UTF-8 BOM 存在时剥离并直接按 UTF-8 解码。 ^8f6c1ec2
-
从前 8192 字节搜索 charset;规范 utf8 别名并尝试指定编码。 ^230fcbbc
-
指定编码不可用或解码失败时尝试 UTF-8。 ^8072e67e
-
UTF-8 仍失败则用 GB18030,无法识别字符替换处理。 ^0fcb7be3
F11 _strip_noise():就地移除 HTML 非正文噪声 ^58019fb1
-
删除 script / style / noscript / template / iframe / svg 节点。 ^1783dc82
-
找出 Comment 并 extract() 移除;在原 soup 上修改,不返回新树。 ^ce482e1c
F12 _pick_root():选择优先级最高的正文根节点 ^76ec0ec3
-
依次尝试 main → article → body。 ^e11919d7
-
都不存在时退回整个 soup。 ^7ea22f88
F13 _doc_title():提取页面 title 字符串 ^6809dee6
-
查找 title 节点,且其 .string 存在时返回 strip() 后的标题。 ^ca9686df
-
否则返回空字符串。 ^3c9b26c7
F15 _linearize_blocks():按语义块生成较稳定的正文文本 ^6d5d9b8a
-
先调用 _br_to_newlines()。 〔→ F14〕 ^d5002235
-
按文档顺序读取最多 8000 个标题 / 段落 / 列表 / 单元格等块,跳过空文本。 ^652e8e38
-
h1~h4 加 Markdown 标题前缀,其他块直接收集,合成为 structured。 ^82644fae
-
同时取 plain;structured 缺失或明显过短(不足 max(120, plain×0.22))时回 退 plain。 ^3b333911
-
返回选出的结构化文本或纯文本。 ^523b21f0
F14 _br_to_newlines():保留显式换行标签的含义 ^de5aa6f9
-
遍历根节点下的 br。 ^65b54933
-
逐个替换为换行字符,直接修改节点树。 ^f09852f1
F16 _split_into_sections():按二级标题拆成虚拟页面 ^0edd8c0b
-
strip() 后为空则返回 [];按行首 ## 标题分段并去空段。 ^1d71d76e
-
不足两段时返回单页,page=1,可在正文前加网页标题。 ^cdbc7646
-
多段时从 1 编页码,提取首行标题,加章节前缀。 ^5e4c8233
-
返回每段的 page / title / text,供 HTML Document 的 metadata.page 使用。 ^9e5eb01d
[[2_代码实现_文档入库|P03 embedding.py:为入库和 Query 复用同一个本地 Dense 模型]] ^9dde46fb
-
导入 HuggingFaceEmbeddings,声明工厂和服务类。 ^3f2eb697
-
模块导入时执行 embedding_service = EmbeddingService(),建立共享模型实例。 〔→ F18〕 ^a24a5484
F17 _create_dense_embedder():根据配置创建归一化 Dense 编码器 ^77029661
-
读取 EMBEDDING_MODEL / DEVICE,默认 BAAI/bge-m3、cpu。 ^ba1a89ac
-
创建 HuggingFaceEmbeddings,配置 normalize_embeddings=True。 ^50c1ca9b
-
返回本地模型包装对象;BM25 Sparse 向量不在此计算。 ^a5745158
C02 EmbeddingService:向调用方暴露统一批量编码方法 ^5d69d479
- 入库文本和查询词都使用 get_embeddings(),保持同一向量空间。 ^e4fad6a7
F18 init():持有一个本地 Dense 编码器 ^378c1617
-
调用 _create_dense_embedder(),保存到 self._embedder。 〔→ F17〕 ^12b81919
-
state_path 参数保留在签名中,当前函数体没有使用它。 ^43fc7fde
F19 get_embeddings():批量编码文本列表 ^80d00a1e
-
texts 为空返回 []。 ^f758055f
-
调用 self._embedder.embed_documents(texts),返回向量列表。 ^a5e195c3
-
异常包装为本地密集嵌入模型调用失败,并保留原异常链。 ^e9bfc092
Element Links
69dd485b: [[2_代码实现_文档入库]]
95c7e343: [[2_代码实现_文档入库]]
b0eedeb8: [[2_代码实现_文档入库]]
9dde46fb: [[2_代码实现_文档入库]]
%%
Drawing
1 | N4KAkARALgngDgUwgLgAQQQDwMYEMA2AlgCYBOuA7hADTgQBuCpAzoQPYB2KqATLZMzYBXUtiRoIACyhQ4zZAHoFAc0JRJQgEYA6bGwC2CgF7N6hbEcK4OCtptbErHALRY8RMpWdx8Q1TdIEfARcZgRmBShcZQUebR4ATm0ARho6IIR9BA4oZm4AbXAwUDBSiBJuCGJNAHZNBITNHjTSyFhESsDsKI5lYJayzG5nZIBmGprtUYA2aYBWBoAOCeWE |
%%