17 · 实战:给真实政策文件做问答(把所有学过的用在一份真文件上)
开头的现象
老王把两份文件发到小林的 data 目录里,说:“这两份是国家’十五五’规划的真实文件——一份就业、一份教育。你学了两个月,别老拿员工手册那 3000 字玩具练手。用这两份真文件,把你学的切块、向量化、检索全走一遍。” 小林打开文件,眼睛直了:《十五五就业优先规划》7448 字,《十五五教育规划》7682 字——每一份都比员工手册长一倍多。 他咽了口口水,心里既有压力也有兴奋:“终于要拿真家伙练手了。” 他定的目标是:让机器人能回答这两份政策文件里的问题——“就业目标是什么?""立德树人怎么落实?“——而且两份文件要能区分,问就业的问题不能答成教育的。第一幕:小林发现第一个坑——文件名会变,代码别写死
小林第一版代码是这么写的:glob 匹配”十五五”开头的所有 txt:
想验证”自动发现”吗?往 data 目录丢一个十五五XXX.txt,重跑07_load_split.py——它自动多处理一份文件,代码一行没改。文件多了,glob 自动发现。
第二幕:小林决定”一个库、两个 collection”——政策文件各归各的
小林第 8 篇学过”一个库 = 一个目录 = 多个 collection”。他决定不另开目录,把两份政策文件塞进同一个向量库data/chroma_db,用不同 collection 区分:
delete_collection()——他第 8 篇踩过 shutil.rmtree 整个库的坑,这次只删自己的 collection,员工手册的库动都不动。“合租房的自觉:只扔自己房间的垃圾。”
他验证了一下最终结构——用一个 sqlite 查询看这个库里有几个 collection:
想验证”一个库三个 collection”吗?跑08_embed_search.py——它会自动发现两份政策文件、各自入库。然后用 Python 查data/chroma_db/chroma.sqlite3的 collections 表,你会看到三个 collection 平起平坐。
第三幕:分 collection 检索——问就业的不会答成教育的
小林开始测试检索。他问了两个问题,各进各的 collection:
想验证”分抽屉”吗?跑 08_embed_search.py 的第三部分——就业问题查 employment_15_5、教育问题查 education_15_5,各答各的。你把 collection 名互换,立刻答非所问。
第四幕:边界——政策文件的检索,跟员工手册有什么不一样
小林用真实文件跑完,发现了几个”员工手册时代没遇到”的边界: 边界一:政策文件是”长段落 + 序号结构”(一、(一)、1.),普通切分器会切得有点碎。 他数了数:就业 54 块、教育 62 块,很多块只有一两句话。政策文件的自然单位是”条目”,而不是”句子”——理想的做法是按”(一)(二)(三)“这种条目边界切,但chunk_size=200 的通用切法也能用,只是块更碎。
边界二:两份文件用语高度相似(都是”深入贯彻""推动""促进”),向量容易混淆。 小林发现”就业”和”教育”的开头段落用词很像,如果检索时不指定 collection,可能串。分 collection 就是应对这个的。
边界三:政策文件的问题通常是”总结性提问”(“就业目标是什么”),需要检索到”总体要求”那一整段。 单块 200 字符可能只覆盖一半,k=2 或 k=3 更稳。
边界四:真实文件的 metadata 很重要。 他给每个块打了 col_name 前缀的 id,这样检索到任何一块,都能知道它来自哪份文件——“回答标出处”就靠这个。
想验证边界吗?问”人工智能对就业的影响”(就业库)和”人工智能对教育的影响”(教育库)——注意,政策文件里两处都提到人工智能,但如果 collection 不分,检索会串到另一份文件去。
🔧 技术细节:实战用到的全部签名汇总(前文各章 + 本章新增)
这一章是把前面所有章串起来实战,小林把全程用到的签名汇总成一张”最终兵器谱”: ① 文件自动发现(glob)——本章新增想验证吗?跑08_embed_search.py——自动发现两份政策文件、各自入库(就业 54 块 / 教育 62 块)、分 collection 检索互不串门。查data/chroma_db/chroma.sqlite3的 collections 表,能看到三个 collection 平起平坐。
结论(小林用一天换来的)
给真实文件做问答 = 把学过的东西串起来:glob 自动发现文件(别写死文件名)→ 切块(第 7 篇的刀)→ 一个库多个 collection 区分文档(第 8 篇,只删自己的 collection)→ 分 collection 检索(第 06/07 篇)。 真实文件的边界:政策文件是”条目结构”(切得碎)、两份文件用语相似(必须分 collection 隔离)、总结性提问要 k 大一点、metadata 记好出处。员工手册学的是”机制”,政策文件练的是”实战”——机制没变,但真实文件会逼你想清楚”怎么组织”(文件名、collection、边界)。
复现信号:什么时候你会想起这一章
- 文档会更新/新增——文件名用 glob 自动发现,别写死。
- 多份文档要共存——一个库多个 collection,各检索各的,别混。
- 要清理重建——只删自己的 collection(
delete_collection()),别rmtree整个库。 - 检索结果答非所问——先查 collection 是不是选错了,再查 embedding 模型是不是换了。
- 真实文档结构特殊(政策/合同/报告)——通用切分可能碎,考虑按文档自己的条目结构切。
后记:小林学到了什么(整个系列的收尾)
小林合上电脑,望着窗外。他想起了老板最初的需求——“基于你的文档回答问题”。现在他能说清楚这整条路是怎么走的了: 第 01~04 篇:会说话。 模型统一插口(invoke/stream/with_structured_output/thinking)——换供应商只改三行。 第 05~07 篇:有资料。 Document 切块、Embedding 向量化、Chroma 检索、RAG 流水线——模型”翻手册”回答你的文档。 第 08~09 篇:会记住、会干活、会转弯。 记忆(历史 + session_id)、工具(@tool)、Agent(create_agent 自动循环)、LangGraph(自己搭图、checkpointer 持久化记忆)。 第 10~12 篇:懂地基。 AIMessage 统一信封、模板管结构不管内容、langchain_core 三层架构。 第 13 篇:能实战。 真实政策文件走全流程。 他想起老王最后说的话:“小林,你现在不是’会用 LangChain 了’,是’知道 LangChain 为什么长这样了’。这两者的区别,就是以后遇到新问题,你是去查文档,还是能自己推断出答案。” 小林笑了笑,把最后一句话写在工位贴纸上:LangChain 三层架构:协议层(langchain_core 定义接口)→ 实现层(集成包连具体服务)→ 编排层(langgraph 管流程/状态/记忆)。 你学的是这三层怎么配合——换模型改实现层,加能力改协议层,改流程改编排层。这就是全部。
验证清单(整个系列的收官测试)
- 换模型供应商,你的代码要改哪几行?(提示:第 1 篇)
- 模型为什么”失忆”?怎么让它记住?(提示:第 4 篇)
- 用户问”工作满一年能休几天”,关键词搜索为什么漏?(提示:第 8 篇)
- RAG 和”把文档塞进 prompt”的本质区别?(提示:第 9 篇)
- create_agent 返回的是什么类型?内部长什么样?(提示:第 11 篇)
- 三份协议(OpenAI/Anthropic/Responses)的取文本方式分别是什么?(提示:第 2 篇)
- create_agent 内部用 ChatPromptTemplate 吗?为什么?(提示:第 3 篇)
- langchain_core 的 29 个模块里,你天天用的 10 个是哪些?(提示:第 12 篇)
- 多份文档共存一个向量库,怎么组织?(提示:第 13 篇)