Dify采购知识库实战:四库切片法与元数据治理
上一篇《采购需求智能审核的岔路口》讲了为什么选知识库而不是训练模型,这篇接着往下走——知识库到底怎么搭才不是”把PDF扔进去”。
背景还是那个:我们给广东某高校上线的采购需求智能审核,审核内容基本符合人工预期但有部分错误。上一篇文章归因后结论是三层架构(规则引擎+RAG+人工复核),知识库分四库:法规库、案例库、规则库、模板库。但”分四库”只是个目录结构,真正决定检索质量的,是两件事:切片怎么切,元数据怎么挂。
先把最核心的认知摆出来:
切片是检索的原子单位。切片质量的上限,就是你整个RAG系统的上限。 模型再强、检索算法再好,喂进去的是被拦腰切断的条款,出来的就是断章取义的审核意见——客户看到的”部分内容有错误”,相当一部分病根在这里。
一、为什么每个切片必须带元数据
先回答”为什么要带元数据”这个根本问题。不带元数据的切片,在检索链路上有三个致命盲区:
盲区一:分不清”现行”还是”废止”,等于给模型喂毒药。 采购法规更新非常频繁。以《政府采购需求管理办法》(财库〔2021〕22号)为例,它实施前后,各地对”需求管理”的要求口径变过多次;再往前,《政府采购货物和服务招标投标管理办法》(财政部令第87号)施行后,此前的18号令整体废止。如果你的法规库里既有87号令又有18号令的解读文章,不带状态标记,向量检索只看”语义像不像”——一个关于”评标委员会组成”的query,完全可能召回18号令的旧规定。模型拿着废止条款给客户出审核意见,这是比”不知道”严重十倍的错误。
盲区二:分不清”适用于谁”,检索会张冠李戴。 “采购限额标准”这个query,中央预算单位、广东省、高校本级,三套标准三个数字。切片不带适用范围,检索回来的是哪个标准全凭运气。LLM不会说”我不知道你要问哪个范围”,它会挑一个最像的自信地答出去——又是错误。
盲区三:审核意见无法”引经据典”。 合规审核的命根子是依据。审核意见写”该条款涉嫌指向性”,必须跟一句”依据《政府采购法实施条例》第二十条”。如果切片里不带文号、条号,模型只能”凭记忆”补出处——补错文号是AI审核最常见的翻车现场。元数据写在切片里,模型引用时照抄,才是零幻觉的引用。
一句话总结:元数据不是给管理员看的备注,是给检索链路用的”过滤条件”和给模型用的”引用锚点”。
二、Dify里的元数据怎么落地:两个层级
讲到实操,必须先说清楚Dify的一个现实:Dify的元数据功能是文档级的,不是分段级的。 知识库设置里可以给每个文档添加元数据字段(如”状态:现行/废止”),检索时支持按元数据过滤——这解决了”整份法规是否废止”的问题。但如果你想给”某个切片”单独打标(比如一部法规里部分条款被后续文件修改),Dify原生不支持分段级元数据。
实战里的标准做法是两层配合:
第一层:文档级元数据用Dify原生功能。 每份法规文件作为一个文档上传,挂文档级元数据:
| 元数据字段 | 示例值 | 检索时的用途 |
|---|---|---|
doc_type | 法规/案例/规则/模板 | 四库路由,避免案例库的内容回答法规问题 |
doc_no | 财库〔2021〕22号 | 引用溯源 |
issue_org | 财政部 | 权威性排序 |
effective_date | 2021-07-01 | 时效过滤 |
status | 现行 / 废止 / 已修订 | 检索时直接过滤掉废止文档——这是防毒药的总闸 |
scope | 货物+服务 / 高校 / 广东省 | 按项目类型过滤 |
在Dify工作流的知识检索节点里,配置检索过滤条件(如 status = 现行),废止文件在进入召回之前就被拦掉了。这一步做完,“引用废止条款”这类错误从机制上清零。
第二层:切片级元数据写进切片文本头部。 把关键字段压缩成一行”元数据头”放在每个切片正文的最前面:
【法规】政府采购法实施条例 | 第二十条 | 现行 | 国务院令第658号 | 适用:货物+服务+工程
第二十条 采购人或者采购代理机构有下列情形之一的,属于以不合理的条件对供应商实行差别待遇或者歧视待遇:(一)就同一采购项目向供应商提供有差别的项目信息;……
这一行头部有双重作用:一是进入embedding计算——“现行""废止""文号”这些词参与向量化,语义上把”现行版”和”废止版”拉开距离,检索天然更准;二是LLM引用时直接照抄头部信息生成”依据国务院令第658号第二十条”,不需要回忆。
这是个笨办法,但是被反复验证有效的笨办法——平台不支持的元数据,就让它长在文本里。
三、四库切片实战:一库一策
四库的切片策略完全不同,这是本篇的核心。用一种切法切所有内容,是知识库建设最常见的错误。
3.1 法规库:按条款切,不按字数切
为什么不能按字数切:Dify默认的分段方式是”固定长度+自动”(比如每500字一切)。拿《政府采购法》举例,第九条讲”政府采购应当有助于实现国家的经济和社会发展政策目标”,第十条讲”政府采购应当采购本国货物、工程和服务”——如果切分点恰好落在第九条中间,一个切片里就混着”半条第九条+半条第十条”。检索”本国产品优先”时召回这个切片,模型看到的上下文是残缺的,回答自然残缺。法条是最典型的”语义完整单元”内容,必须按结构切。
Dify具体操作:知识库创建时,分段设置选”自定义”,分段标识符用正则:
\n(?=第[一二三四五六七八九十百零〇\d]+条)
这个正则的含义是”在每个’第X条’出现前切一刀”。上传前先把PDF转成结构化Markdown(这一步别偷懒,PDF直接上传,条款编号经常被排版搞乱,正则就失效了)。
条款太长怎么办——用父子分段。Dify支持”父子分段”模式:子块用于检索命中,命中后返回父块给LLM。这正好解决法规的一个两难:
- 切太粗(一整章一片):检索命中了,但一片几千字,top-k=3时上下文窗口被三整章塞满,模型抓不住重点
- 切太细(一个款项一片):检索准了,但模型看不到条款全貌
正确姿势:父块=完整的一条,子块=条内的款项。 检索时”第二款”这个细节命中子块,喂给模型的是整条——既有精度又有全貌。Dify知识库设置里选”Parent-child分段”,父块按 \n(?=第X条) 切,子块按款项切,两边各配一个正则就完成。
生效日期进切片的例子。有些法规是”部分条款被后续文件修改”,比如某条例第十五条被新文件替换——处理办法是:修订版对应条款的切片头部写 现行(2024修订),旧版整个文档挂 status=废止 文档级元数据。宁可两个版本都留(旧版对历史项目复盘有用),也不让检索”看走眼”。
3.2 案例库:一个案例一个切片,绝不按字数切
案例库是四库里最容易被切坏的一库,也是最值钱的一库(通用大模型永远给不出”上个月这个学校这么写被退回了”的经验)。
案例的天然结构不是”条款”,是”事件”。一个完整案例 = 需求书原文片段 + 人工审核意见 + 整改后写法。这三段是强绑定的因果关系,按字数切会把”问题写法”和”整改写法”切到两个片里——检索”教学仪器需求怎么写”时,召回的只有问题写法没有整改答案,模型等于只学到了”错误示范”,这比没有案例还糟。
所以案例库的铁律是:一个案例一个chunk,chunk可以大(1000-1500字都没问题),但绝不拆散。 在Dify里分段设置选”自定义”,分段标识符用一个特殊分隔符(比如在Markdown源文件里,案例之间用 ===CASE=== 分隔,正则按这个切)。
案例切片的结构化写法(直接照这个模板整理你的历史案例):
【案例】需求书指向性退回案例 | 品目:教学仪器 | 时间:2026-03 | 结果:退回修改
【问题原文】
"实验室液相色谱仪须为安捷伦1260型或同等进口品牌,配套工作站须为原厂进口。"
【审核意见】
涉嫌指定品牌和原产地。依据《政府采购法实施条例》第二十条第(八)项"以其他不合理条件限制或者排斥潜在供应商",进口产品还需履行进口产品审核程序。
【整改后写法】
"实验室液相色谱仪,主要技术参数:二元梯度系统,流量精度≤0.15%……(列技术指标,不列品牌)。如需采购进口产品,须按规定履行进口审核论证程序。"
【经验提示】
教学仪器类需求最常见的指向性问题:用型号替代技术参数、用"原厂""进口"限定产地。技术参数写法是"指标化"而非"品牌化"。
头部那行元数据头(品目/时间/结果)就是案例库的切片级元数据——检索”液相色谱仪需求”时,品目信息参与匹配,命中率高;模型引用时能说清”某高校2026年3月的教学仪器采购项目”——脱敏且可信。
案例库的积累路径也有讲究:上线初期人工审核的每一条”采纳/驳回”记录,按这个模板整理入库。审核员驳回一条AI意见、写明理由——这就是一条新案例。半年下来几百条,这就是竞对抄不走的护城河。
3.3 规则库:一条规则一个切片,Q&A式结构
先明确规则库的定位,避免和代码节点里的规则引擎打架:代码节点里的规则是”执行”用的(机器判),知识库里的规则是”解释”用的(给模型引用、给人看)。两者同源——都从那张数据库规则表导出,一份进代码,一份进知识库。
规则库的切片单元是”一条规则”,而且要用Q&A式结构写——因为规则的检索场景是”这个情况违反了什么规则”,query天然是问句形态,Q&A结构的召回率远高于条文式:
【规则】采购方式与限额匹配 | 维度:广东省通用 | 优先级:硬性 | 依据:粤财采购〔2021〕14号
Q:预算80万元的货物采购应该用什么采购方式?
A:广东省货物服务类公开招标数额标准为200万元以上(以现行文件为准,此处为示意)。
预算80万元未达公开招标标准,应采用竞争性谈判、竞争性磋商或询价等非招标方式,
由采购人按校内制度确定。若需求书填报"公开招标"且预算低于标准,属于采购方式选择不当,
建议调整采购方式或合并同类需求品目后再行采购。
元数据头里那个**“维度”字段**(通用/品目/项目)是规则库的灵魂——它直接对应上一篇文章讲的”引擎与数据分离”三层模型。检索时按当前项目的品目过滤规则:审一个医疗器械项目,命中的是”医疗器械品目规则+通用规则”,不会把工程类规则召回來干扰。
规则库切片的隐藏价值:LLM审核意见的”建议”部分可以直接引用规则库的A部分措辞,让人工复核员看到的每条建议都有出处、有标准答案。这就把”AI的建议”和”制度的口径”钉在了一起。
3.4 模板库:整篇少切,元数据决定路由
模板库(需求书模板、技术参数书模板、进口产品论证模板等)的切片逻辑跟前三个都不一样:模板的价值在完整结构,切碎了就废了。
一个需求书模板的章节之间是”骨架关系”——基本信息、采购清单、技术要求、商务要求、验收标准,模型回答”需求书应该包含哪些部分”时需要整篇视野。所以模板库的做法是:
- 小模板(论证表、清单类):整篇一个chunk,Dify分段设置里把最大chunk长度调到2000以上,一片装下
- 大模板(完整需求书模板):按章节切,但每片头部带元数据标注属于哪个模板的哪一章:
【模板】教学仪器类采购需求书模板 | 章节:三、技术要求 | 适用:高校货物采购
(三)技术要求编写规范:
1. 技术参数应采用"指标化"描述,明确量程、精度、等级等可量化指标;
2. 不得出现品牌、型号、产地字样;
3. 带▲号的参数为重要参数,数量不超过总参数的20%;
……
模板库的检索场景通常是生成场景(帮采购人起草需求书)而不是审核场景,所以它服务的可能是另一个工作流——但建在同一个知识库里,靠文档级元数据 doc_type=模板 区分路由,管理上反而省事。
四、四库切片策略速查表
| 库 | 切片单元 | 为什么 | Dify分段设置 | 元数据头关键字段 |
|---|---|---|---|---|
| 法规库 | 一条法律条文(父子分段:条为父、款为子) | 条款是语义完整单元,按字数切会拦腰斩断 | 自定义正则 \n(?=第X条) | 文号、条号、状态、生效日期、适用范围 |
| 案例库 | 一个完整案例(原文+意见+整改三段绑定) | 案例是事件因果体,拆开=只留错误示范 | 自定义分隔符 ===CASE=== | 品目、时间、结果、问题类型 |
| 规则库 | 一条规则(Q&A式) | query是问句形态,Q&A召回率最高 | 自定义分隔符 ===RULE=== | 维度(通用/品目/项目)、优先级、依据 |
| 模板库 | 小模板整篇;大模板按章节 | 模板价值在完整结构 | 整篇或按章节标记 | 模板名、章节、适用场景 |
共同铁律:切片头部永远带元数据行;文档级元数据用Dify原生功能挂状态和类型,检索时过滤条件写死 status=现行。
五、Dify检索配置:换模型、开混合、测命中
切片做完,三个配置动作直接决定效果:
1. Embedding换bge-m3。 Dify默认的embedding模型在中文法规场景召回质量不够。设置→模型供应商里接入bge-m3(本地部署或API均可),知识库创建时选它。注意:换embedding后知识库要重新向量化(索引全部重建),已有库先别急着切,新库先用新模型建好、评测通过后再迁移。
2. 开混合检索+rerank。 知识库设置里:检索设置选”混合检索”(向量+全文),并配置rerank模型(如bge-reranker-v2-m3)。原因在上一篇讲过:法条编号、金额数字这类精确信息靠全文检索(BM25),“变相指定品牌”这类语义判断靠向量——单开任何一路都有盲区。Top-k建议先设5,rerank后取3。
3. 用”命中测试”逐条调。 Dify知识库自带命中测试功能。建一张30条左右的测试query清单(从历史审核意见里挑真实问题,如”80万货物应该用什么采购方式""技术参数里写进口品牌怎么办""教学仪器验收标准必须包含哪些”),每条标注期望命中的文档。每次调整切片、换参数后全部跑一遍——这就是知识库版的”回归测试”。命中测试不过关的query,回到切片环节查:多半是切分点不对、或者元数据头缺失。
六、规则引擎优化:从代码节点到”数据驱动”
最后回到规则引擎。上一篇讲了在Dify工作流的LLM节点前插一个代码节点做硬规则,这里补上怎么做才不会掉进”规则写死”的维护坑——核心就一句:代码里只写”怎么查”,“查什么”全部走配置字典。
代码节点(Python)的实战骨架:
import re
def main(需求书文本: str, 项目类型: str, 预算金额: float) -> dict:
# ===== 规则配置:所有"内容"都在这个字典里,改规则=改字典 =====
config = {
"医疗器械类": {
"allowed_quals": ["医疗器械经营许可证", "医疗器械生产许可证",
"医疗器械注册证", "ISO13485"],
"extra_keywords": ["须为进口", "进口品牌", "原厂进口"],
"limit_hint": "医疗设备参考广东省限额标准"
},
"信息化类": {
"allowed_quals": ["信息系统集成资质", "ISO27001", "CMMI"],
"extra_keywords": ["须与现有系统无缝对接", "指定品牌",
"同品牌兼容"],
"limit_hint": "信息化项目参考校内限额制度"
},
"通用": {
"forbidden_quals": ["注册资本不低于", "成立年限不低于",
"本地供应商", "特定行政区域",
"业绩合同金额不低于"],
"forbidden_words": ["指定", "唯一", "独家", "专利技术必须",
"须为XX品牌"]
}
}
# ===== 检查逻辑:代码只写一次,所有项目复用 =====
proj_cfg = config.get(项目类型, {})
common_cfg = config["通用"]
violations = []
# 检查1:指向性关键词扫描
keywords = common_cfg["forbidden_words"] + proj_cfg.get("extra_keywords", [])
for kw in keywords:
if kw.replace("XX", "") and kw in 需求书文本:
violations.append({
"type": "指向性",
"hit": kw,
"advice": "依据《政府采购法实施条例》第二十条,删除指向性表述,改为指标化描述"
})
# 检查2:资质红线扫描(黑名单命中即违规)
for fq in common_cfg["forbidden_quals"]:
if fq in 需求书文本:
violations.append({
"type": "资质歧视",
"hit": fq,
"advice": "涉嫌以不合理条件限制潜在供应商,删除该资质要求"
})
return {
"pass": len(violations) == 0,
"violations": violations,
"next_step": "直接拦截输出" if violations else "进入RAG语义审核"
}
看清楚这段代码的结构:四类规则(限额/字段/指向性/资质)的执行逻辑是骨架,永远不改;三个项目类型的词库、资质清单是血肉,全在config字典里。客户说”我们这个项目还要求XX资质”——你改的是字典,不是代码。这就是”引擎与数据分离”在Dify代码节点里的最小实现。
后续规模化时,这个字典抽出去变成数据库三张表(规则集/规则条目/项目绑定),代码节点退化成一个HTTP Request节点调规则服务(Spring Boot + LiteFlow),逻辑完全不变——迁移路径是平滑的,不是推倒重来。
规则命中后的分支处置:代码节点输出 pass=false 时,条件分支直接路由到”违规报告输出”节点,不进LLM——硬规则错误当场拦截,Token都省了。
七、一周落地节奏
最后给个实操排期,按一个人全职投入算:
| 天 | 动作 | 产出 |
|---|---|---|
| D1 | 收集材料:三级法规PDF、校内制度、近一年退回的需求书及审核意见 | 材料清单 |
| D2 | 法规库:PDF转MD、按条切分、加元数据头,Dify建库(bge-m3+父子分段) | 法规库上线 |
| D3 | 案例库:退回案例按三段式模板整理10-20条,建库;规则库:Q&A式整理首批15条 | 两库上线 |
| D4 | 代码节点写硬规则(限额/字段/指向性/资质),条件分支接通 | 规则引擎止血版上线 |
| D5 | 命中测试30条query,调切片和top-k;前端提示词加”只依据检索材料、引用附原文”约束 | 检索质量达标 |
| D6-D7 | 用3份真实历史需求书做端到端评测,对比人工结论,记录差异 | 评测报告 |
一周之后你手里有的东西:四个结构化的库、一套数据化的规则、一份可复跑的评测——这就是拿去跟客户谈”持续优化”的全部家底。
最后一句收口:知识库建设没有黑科技,全是”把内容当结构化数据来治理”的笨功夫——切片是检索的地基,元数据是防毒药的闸门,评测集是只降不升的保险。 平台会换(今天是Dify,明天可能是别的),这三个笨功夫跟平台无关,是能带走的资产。
微信扫一扫 · 转发本文
本文首发于 荣哥AI实战手记,原创内容,转载请注明出处:https://blog.cyuan.work/blog/dify%E9%87%87%E8%B4%AD%E7%9F%A5%E8%AF%86%E5%BA%93%E5%AE%9E%E6%88%98%E5%9B%9B%E5%BA%93%E5%88%87%E7%89%87%E6%B3%95%E4%B8%8E%E5%85%83%E6%95%B0%E6%8D%AE%E6%B2%BB%E7%90%86/