【Dify 自动化实践】让报告保留检索片段与来源引用

本文介绍如何在Dify工作流中实现报告生成时保留知识库检索的原始片段与来源引用。通过Knowledge Retrieval节点获取结构化检索结果,利用Code节点整理source_refs、content、location等字段,确保LLM在证据边界内生成带可验证引用的报告。强调字段映射准确性、元数据防御性处理及端到端可复核性验收,适用于制度摘要、项目验收等高可信度场景。

让报告保留检索片段与来源引用

课程归属:Dify自动化实战 · 第16节
项目名称:资料报告助手

一、为什么报告必须保留检索证据

在资料报告工作流中,知识库检索节点可能已经找到了相关内容,但最终生成的报告却只剩几条结论。读者看不到结论来自哪份文件、引用了哪段原文,也难以判断模型是否把不同资料混在了一起。对于制度摘要、项目验收、产品资料对比等任务,这会直接影响报告的可复核性。

本篇在“资料报告助手”Workflow 中,把知识检索结果整理成统一的 source_refs 字段,并将证据传入 LLM 报告生成节点。最终报告除了摘要和要点,还能列出来源名称、可用定位信息与检索片段;当证据不足时,工作流会明确提示,而不是让模型补造出处。

本文使用教学模拟的项目验收资料说明配置方法。完成后的验收标准是:每条引用都能回查到本次检索实际返回的片段;没有返回的定位信息不猜测。

二、先明确工作流结构与字段约定

2.1 数据流:检索结果不能在生成阶段丢失

证据缺失

工作流输入

知识检索

整理检索证据

校验来源与片段

LLM生成报告

输出报告与引用清单

记录warnings

图 1 展示了本篇的核心结构。检索节点负责找资料,整理节点负责保留来源和片段,LLM 负责在证据范围内写作。这样即使生成模型换了,引用字段也不必随提示词一起重新设计。

Dify 官方文档说明,Knowledge Retrieval(知识检索)节点会输出名为 result 的数组,数组元素包含检索片段的内容、元数据、标题等信息,可以作为后续节点的上下文。实际字段结构应以当前实例的节点运行详情为准。

参考:Dify 官方文档:Knowledge Retrieval

2.2 本篇使用的公共字段

项目主输入仍是 files 和 report_topic。其中 files 是平台的多文件输入类型,report_topic 是字符串。要注意,上传到工作流的文件并不会因为变量名叫 files 就自动进入知识库;知识库文档需要先完成上传、解析和索引,检索节点才能查到。

字段类型用途
source_nameString来源资料名称
summaryString资料摘要
key_points字符串数组从资料中提炼的要点
source_refs对象数组来源引用,每项含 source_name、locator、excerpt
warnings字符串数组记录无结果、定位缺失或证据冲突
report_markdownString最终 Markdown 报告正文
download_urlString未生成文件时为空字符串

source_refs 的单项结构固定为:

{
  "source_name": "project_acceptance.txt",
  "locator": "分段标识(如检索结果提供)",
  "excerpt": "存在高优先级未关闭问题时,不得判定最终验收通过。"
}

这里的 locator 不限定为页码。它可以是检索结果实际提供的页码、分段标识或其他可回查信息。如果当前节点没有返回定位字段,就使用空字符串,并在 warnings 中说明;不要把相似度分数、数组下标或自己编写的编号冒充原文页码。

2.3 三种信息不能混为一谈

信息正确用法常见错误
来源名称取自检索结果的标题或可信元数据把知识库名称当成具体文件名
原文片段保留检索返回的正文内容让模型改写后仍标成逐字原文
定位信息使用系统实际提供的定位字段凭空填写页码、章节号
相似度分数辅助判断检索相关性当成文档位置或证据编号
报告结论由证据支持后再归纳把模型常识写成资料明文规定

这张表是后续调试的判断依据:如果报告中的引用找不到原始片段,应先检查字段映射和生成规则,而不是直接修改模型温度或反复润色提示词。

三、准备一份可复现的教学资料

3.1 创建示例文件

在项目目录中创建 dify_lab/data/input/project_acceptance.txt。它是一份教学模拟文本,不代表任何真实企业制度。

资料名称:项目验收规则(教学模拟)
1. 验收前必须提供测试报告、问题整改记录和负责人签字。
2. 存在高优先级未关闭问题时,不得判定最终验收通过。
3. 验收通过后,应在两个工作日内完成资料归档。
4. 对外提供的附件必须先移除客户个人信息。
5. 验收范围发生变更时,需更新检查清单并由相关负责人重新确认。

将文件上传到 Dify 知识库,确认文档解析和索引完成。切分时尽量让每条规则与必要的上下文留在同一片段中,避免检索到“两个工作日内”却丢失它描述的对象。

3.2 先用知识库检索测试确认原始证据

在知识库的检索测试界面依次测试以下问题:

编号测试问题预期查找的证据
Q1项目验收前必须准备哪些材料?测试报告、问题整改记录、负责人签字
Q2什么情况不能判定最终验收通过?高优先级未关闭问题
Q3验收通过后多久完成归档?两个工作日内
Q4对外提供附件前要做什么?移除客户个人信息
Q5验收范围发生变更后要做什么?更新检查清单并重新确认

这些是测试用例和预期检查点,不是已经在你的 Dify 实例中执行的结果。实际测试时,应记录返回的文档名、片段正文和可用元数据。如果 Q1 找到了正文却没有文档名,应先处理来源字段缺失,而不是直接进入报告生成阶段。

四、在 Workflow 中整理引用字段

4.1 配置知识检索节点

在 Dify Workflow 画布中,检查或添加 Knowledge Retrieval 节点:

  1. 选择已经创建并完成索引的知识库。
  2. 将查询文本绑定到 report_topic,或者绑定到前置节点生成的具体检索问题。
  3. 根据当前任务配置检索方式、返回片段数量和分数阈值。
  4. 运行一次工作流,打开运行详情,查看检索节点的 result。
  5. 确认每个结果中有哪些字段对应片段正文、文档标题和元数据。

Dify 的检索节点支持将结果传递给下游 LLM;检索结果不只是普通文本,还可能包含标题和元数据。不要假定所有版本、切分方式或数据源都会提供相同的定位字段。

4.2 用代码节点整理 source_refs

为了避免 LLM 自行猜测来源,可以在知识检索节点后添加 Code 节点。将检索节点的 result 映射为代码节点输入变量 retrieval_result,并按下列代码的函数签名配置输入。代码只负责整理真实返回的字段,不负责猜测不存在的元数据。

def main(retrieval_result: list) -> dict:
    source_refs = []
    warnings = []
    evidence_lines = []
    seen = set()

    if not isinstance(retrieval_result, list) or not retrieval_result:
        return {
            "source_refs": [],
            "evidence_text": "",
            "warnings": ["本次检索没有返回可用片段"]
        }

    for item in retrieval_result:
        if not isinstance(item, dict):
            warnings.append("存在无法识别的检索结果项,已跳过")
            continue

        metadata = item.get("metadata") or {}
        if not isinstance(metadata, dict):
            metadata = {}

        # 来源名称优先取检索结果标题,再尝试元数据中的常见名称字段。
        source_name = (
            item.get("title")
            or metadata.get("document_name")
            or metadata.get("title")
            or ""
        )

        # 不同版本的定位元数据可能不同,只读取实际存在的字段。
        locator = (
            metadata.get("locator")
            or metadata.get("page")
            or metadata.get("page_number")
            or metadata.get("segment_id")
            or ""
        )

        excerpt = item.get("content") or item.get("text") or ""
        if not isinstance(excerpt, str):
            excerpt = str(excerpt) if excerpt is not None else ""

        if not source_name:
            warnings.append("检索片段缺少来源名称,已跳过该条引用")
            continue

        if not excerpt.strip():
            warnings.append(f"来源“{source_name}”缺少片段正文,已跳过")
            continue

        ref = {
            "source_name": str(source_name),
            "locator": str(locator) if locator else "",
            "excerpt": excerpt.strip()
        }

        dedupe_key = (
            ref["source_name"],
            ref["locator"],
            ref["excerpt"]
        )
        if dedupe_key in seen:
            continue
        seen.add(dedupe_key)

        source_refs.append(ref)

        if not locator:
            warnings.append(
                f"来源“{source_name}”未提供可用定位信息"
            )

        # 为提示词生成稳定编号,编号对应 source_refs 数组顺序。
        index = len(source_refs)
        evidence_lines.append(
            f"[S{index}] 来源:{ref['source_name']}\n"
            f"定位:{ref['locator'] or '未提供'}\n"
            f"片段:{ref['excerpt']}"
        )

    return {
        "source_refs": source_refs,
        "evidence_text": "\n\n".join(evidence_lines),
        "warnings": warnings
    }

代码适配边界: 这段代码假设代码节点接收到的 retrieval_result 是由对象组成的数组,每个对象至少可能包含 title、content 和 metadata 等字段。它对缺失字段做了防御处理,但并不意味着这些键在所有 Dify 版本中都一定存在。若运行详情中的结构不同,应按真实结构调整读取逻辑。

在 Code 节点的输出变量配置中,将 source_refs 设为对象数组、evidence_text 设为字符串、warnings 设为字符串数组。若当前版本的输出变量编辑器要求显式定义对象结构,应把 source_name、locator 和 excerpt 都定义为字符串,并确认运行结果类型与下游节点要求一致。

4.3 整理逻辑的输入输出

输入情况整理后的行为预期警告
有来源名、正文和定位信息三个字段均保留无
有来源名和正文,没有定位信息保留引用,locator 为空字符串记录定位缺失
没有来源名跳过该条引用记录来源名缺失
没有正文跳过该条引用记录片段缺失
检索结果为空输出空数组和空证据文本记录没有可用片段

这里采用“宁可明确缺失,也不补造信息”的策略。对于正式报告,缺少定位不一定意味着内容完全不可用,但它会降低读者回查原文的便利性,因此应该明确暴露给后续报告节点。

五、让 LLM 在证据边界内生成报告

5.1 把证据作为显式输入

在 Code 节点后添加或配置 LLM 节点,通过变量选择器传入:

  • report_topic
  • 已整理的 source_refs
  • evidence_text
  • warnings

同时将 report_markdown 作为最终输出字段。变量选择器显示的节点名称可能与你的画布节点名称不同,应以实际节点名为准。

建议在系统提示词中使用下面的规则。方括号中的文字表示应通过 Dify 变量选择器插入的变量,不要将它们原样当成实际变量名。

你是“资料报告助手”。请依据用户主题和检索证据撰写 Markdown 报告。

任务主题:
[插入 report_topic]

检索证据:
[插入 evidence_text]

结构化来源:
[插入 source_refs]

已有警告:
[插入 warnings]

规则:
1. 只把检索证据能够支持的内容写成确定结论。
2. 每个重要判断后使用对应的来源编号,例如 [S1]。
3. 来源编号必须来自输入证据,不得自行新增编号。
4. 不得编造文件名、页码、章节号、日期或原文片段。
5. 引号内的原文必须与输入片段一致;概括内容时应明确标为概括。
6. 如果证据不足、检索为空或资料之间存在冲突,应明确写出“现有检索证据不足以确认”,并说明原因。
7. 在报告末尾输出“来源与检索片段”,逐条列出来源名、定位信息和原文摘录。
8. 不要声称已生成可下载文件。
9. 只输出 Markdown 正文。

图 2 用另一种方式概括了证据和生成的分工:

检索原始结果

代码节点整理

source_refs与warnings

evidence_text编号

LLM报告生成

report_markdown

图 2 中,来源清单和编号文本由代码节点生成,模型负责组织报告。这样可以减少模型随意生成来源名称的机会,但仍然需要最终校验,因为提示词本身不能保证模型绝不输出错误编号。

5.2 报告建议采用固定结构

报告部分内容检查点
结论摘要直接回答 report_topic每项重要判断都有证据支持
关键要点根据检索片段归纳不扩展成资料未说明的规则
风险与待确认事项汇总 warnings 和证据冲突不把未知写成确定事实
来源与检索片段展示 source_refs每条引用都能回查原始检索结果

报告正文中的 [S1] 是报告内的引用编号,不是 Dify 自动生成的官方来源 ID。真正的来源证据仍保存在 source_refs 中。后续若要生成可点击的来源链接,需要有可用的文档标识或 URL,并另行定义链接映射规则;不能只凭一个编号就声称可以跳转原文件。

六、正常用例与失败用例验收

6.1 正常用例:查询验收前必须准备哪些材料

输入 report_topic:

项目验收前必须准备哪些材料?

预期验收过程如下:

  1. 检索节点返回包含“测试报告、问题整改记录和负责人签字”的片段。
  2. 代码节点在 source_refs 中保存对应的来源名、定位信息和原文片段。
  3. LLM 在报告中列出三类材料,并在关键判断后标注对应的 [Sx]。
  4. 报告末尾的引用摘录能在检索节点的原始输出中找到。
  5. 如果检索结果没有提供定位信息,报告不应编造页码,warnings 中应记录该问题。

这些是预期验收标准,并非本次已连接 Dify 实例完成的实测结果。只有运行工作流并逐项核对节点输出后,才能把测试记录为通过。

6.2 失败用例:问题相关,但结果没有页码

输入主题:

规则中第几页说明了验收材料?

如果检索返回了相关片段,但没有任何页码或其他定位字段,正确行为是保留来源和原文,令 locator 为空字符串,并在警告中说明没有定位信息。报告可以回答资料确实说明了哪些验收材料,但不能编造“第 3 页”。

如果检索没有返回相关片段,则应输出证据不足的说明。不能因为示例文件里确实写了这条规则,就跳过检索结果直接回答;这会掩盖知识库召回失败。

6.3 从检索到报告的时序

LLM报告节点 证据整理 知识检索 Workflow 用户 LLM报告节点 证据整理 知识检索 Workflow 用户 提交主题与工作流输入 发起知识检索 返回片段及可用元数据 整理source_refs 返回引用清单与warnings 传入主题和证据 返回report_markdown 输出报告

图 3 说明了为什么不能只在提示词末尾加一句“请提供引用”:引用必须先从检索节点传递下来,经过字段整理后才能被报告节点稳定使用。

七、常见问题排查

现象可能原因处理方法
报告有结论但没有引用evidence_text 未传入 LLM,或提示词未要求引用检查节点输入变量和运行详情
source_refs 为空检索结果为空、字段路径不匹配或来源名缺失先查看检索节点的真实输出,再调整代码
有来源名但没有片段代码读取的正文键不匹配根据真实对象结构映射正文内容
页码不准确将片段序号当页码,或由模型推断仅保留检索系统实际提供的定位字段
引用片段与原文不一致模型重写了摘录,或传递中发生截断将引用清单作为独立字段保留,并对照原始输出
报告出现 [S8],但清单没有模型生成了不存在的编号增加编号校验;无对应证据的引用不得通过验收
warnings 未显示输出字段没有映射到下游,或报告提示词忽略警告检查代码节点输出和 LLM 输入

Dify 的 Knowledge Retrieval 节点可输出检索片段及相关元数据;排错时应先看检索节点,再看整理节点,最后看 LLM 节点。按这个顺序定位,可以判断问题究竟出在召回、变量映射还是生成阶段。

八、最终验收清单

验收项通过标准不通过时的动作
检索来源引用来自本次检索结果检查知识库、检索查询和节点输出
原文片段excerpt 能在检索输出中找到修正字段映射或截断规则
定位信息有则准确保留,无则不猜测清除伪造定位并添加警告
引用编号正文编号均对应引用清单增加编号校验并重新生成
证据不足无结果或元数据缺失时有明确说明检查 warnings 的传递
输出结构report_markdown 为字符串;未生成文件时 download_url 为空字符串检查 Workflow 输出变量映射

验收时不要只看报告是否流畅。至少抽查一条关键结论:从报告中的 [S1] 找到 source_refs 对应项,再将其 excerpt 与知识检索节点原始输出逐字对照。只有来源、片段和结论之间的关系能被解释清楚,这份报告才具备基本的可复核性。

本篇完成后,source_refs 的含义应在整个“资料报告助手”中保持不变。后续第 17 节可以按文档类型设置条件分支,但各分支仍应输出相同的引用字段结构,避免合并报告时丢失来源证据。

参考资料

  1. Dify 官方文档:Knowledge Retrieval 节点
  2. Dify 官方文档仓库:知识检索节点说明
  3. Dify 官方仓库:知识检索节点实现
  4. Dify 官方文档:检索测试与引用归因说明(文档仓库)
  5. Mermaid 官方文档:流程图语法
  6. Mermaid 官方文档:时序图语法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值