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_date2021-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,明天可能是别的),这三个笨功夫跟平台无关,是能带走的资产。

觉得有用?分享给需要的朋友

聊得来的话,交个朋友

这篇文章源自我在一线的真实实践。同行朋友想交流方案细节、聊技术落地,欢迎加我微信 yangxr114(备注「博客」,好认)

也欢迎关注我的视频号「荣哥的AI项目实战」、公众号「胡子杨的AI炼丹炉」,日常分享 AI 项目落地的实战思考。

微信二维码 扫码加好友
← 返回首页