Dify框架增强:RAG 能力提升探索与实践

背景介绍

在之前的文章 来自工业界的开源知识库 RAG 项目最全细节对比 中介绍过,现有 RAG 开源项目中,Dify 的生态良好,但是一个明显的短板就是 RAG 检索能力偏弱。因此一直期望能补全这个短板,从而让 Dify 能真正好用起来。

基于开源项目二次开发建议方案 探索了 Dify 的增强策略。实际选择了文章中提到的中策,基于模块化增强 Dify。做出这个选择的主要原因如下:

  1. Dify 的 RAG 已经支持了大量的 RAG 基础能力与可视化页面,如果通过额外的插件拓展支持,那么现有的 RAG 基础流程无法复用,开发的工作量太大。
  2. 通过插件机制需要整体完成后上线,无法渐进式增强;

最终选择牺牲了部分可维护性,大幅减少开发成本。

方案设计

整体的方案是通过模块化组件替换 Dify 原有的基础模块,逐步替换为更强的 Dify-RAG 模块,从而提升 Dify 的 RAG 能力。简单的 Dify RAG 模块化结构如下所示:

请添加图片描述

根据之前的 RAG 项目结构化文件解析方案比较 调研,Dify 的文件解析能力偏弱,结构化文件解析时也没有保留好结构信息,从而导致文件的分片效果不佳,最终导致 RAG 的检索效果不理想。

因此从解析模块开始,先对结构化文件的解析方案进行了改造。所有的改造遵循 Dify 现有的框架进行设计,首先支持了 html, markdown, pdf 文件的解析优化,可以将增强的解析模块插入 Dify 框架中,改造后的结构如下所示:

请添加图片描述

目前所有的代码目前已经开源在 Github 上,欢迎大家关注。

方案实现

模块化增强

方案实施是按照模块进行逐步升级替换的,下面以 html 解析模块为例进行介绍增强方案:

在之前的 RAG 项目结构化文件解析方案比较 中已经大致介绍了不同的开源项目的 html 解析方案,目前 Dify 的解析是基于 BeautifulSoup 实现,解析后的文档没有保留任何的结构信息,最终分片效果必然不佳。

根据实际测试情况来看,基于 html_text + readability 的解析方案更好,可以将与可视化效果更接近的内容分配在同一行中,从而尽可能避免文件内容被切断的问题。基于 html_text 实现的一个 html 解析方案简化后如下所示:

import html_text
import readability

html_doc = readability.Document(text)

# 使用 html_text 解析出文本内容,以 `\n` 分隔,单个段落中的内容会保留在同一行中

content = html_text.extract_text(html_doc.summary(html_partial=True))

title = html_doc.title()
if title != NO_TITLE:
    txt = f"{title}\n{content}"
else:
    txt = content

# 将原有的内容进行分拆,方便以特殊标识符进行组合

sections = txt.split("\n")
clean_sections = []
for sec in sections:
    sec = sec.strip()
    if sec:
        clean_sections.append(sec)

# 使用 `\n\n` 进行连接,作为标识符提供给 Dify 的 Splitter 环节作为参考

docs = [Document(page_content="\n\n".join(clean_sections))]

上面的内容中需要特别注意的是,最终的内容是按照 \n\n 进行连接,原因从 洞察 Dify RAG 切片机制实现细节 中可以找到答案,Dify 会优先按照 \n\n 进行切片,因此通过同样的标记可以给 Dify 切分环节提供指导,从而尽可能按照合适的位置进行切分。

实际测试时发现,html 解析后的表格容易被切断,从而导致表格信息检索存在问题。因此选择额外处理表格,将提取的表格转换为 markdown 格式,独立进行向量化,尽可能避免表格信息无法检索的问题,实现如下所示:

# 将 html 中的表格转换为 markdown 格式

def convert_table_to_markdown(table) -> str:
    md = []
    rows = table.find_all("tr")
    for row in rows:
        cells = row.find_all(["th", "td"])
        row_text = (
            "| " + " | ".join(cell.get_text(strip=True) for cell in cells) + " |"
        )
        md.append(row_text)

        if row.find("th"):
            header_sep = "| " + " | ".join("---" for _ in cells) + " |"
            md.append(header_sep)

    return "\n".join(md)

soup = BeautifulSoup(content, "html.parser")

tables_md = []
tables = soup.find_all("table")

# 获取所有表格,转换为 markdown 格式,并从原始的 html 中移除

for table in tables:
    table_md = convert_table_to_markdown(table)
    tables_md.append(table_md)
    table.decompose()

另外额外做了一些必要的内容提取优化,比如常规的 checkbox,radio 转换后的文本会保留所有选项的内容,这样最终大模型无法获得任何有价值的信息,因此需要根据实际选择的选项进行信息提取。

整体的实现可以在 html_extractor 中查看。

pip 依赖包封装

如果将现有的增强直接引入 Dify 项目中,那么拉取 Dify 后续的更新很容易出现代码冲突,导致长期维护困难。

因此实际将 Dify 增强能力封装为 pip 依赖包,这样只需要在 Dify 项目引入相关依赖,并根据需要选择合适的增强模块替换原有模块即可,后续的升级和迭代都比较方便。

目前对应的依赖包发布至 Pypi 上了,感兴趣可以安装体验。

Dify 应用

为了在 Dify 中使用现有的增强服务 Dify-RAG,实际是相当简单的。

由于所有的改造都是基于 Dify 官方的代码,因此需要先拉取 Dify 的代码,之后的需要两步即可:

  1. 将 Dify-RAG 依赖包安装至 Dify 项目中,由于 Dify 官方是基于 poetry 管理项目的,因此需要进入 api/ 目录下,通过 poetry add dify-rag 将依赖包安装至 Dify 项目中。
  2. 将 Dify 中表现不佳的基础模块替换为 Dify-RAG 提供的增强模块,目前支持的主要是 html, markdown, pdf 的解析,可以在 api/core/rag/extractor/extract_processor.py 中直接替换,相应的修改可以参考 Github Code

替换之后重启服务,你就会发现,Dify 对特定格式的文件解析已经变强了。

为了生产环境使用更加便利,可以考虑重新打包自定义镜像,并对应修改 docker-compose 中指向的镜像,即可在生产环境中无缝使用了。

总结

本文介绍了 Dify-RAG 增强方案,通过提供 Dify 的 RAG 增强模块,通过简单的替换就可以大幅增强 Dify 现有的 RAG 能力,从而提升 RAG 检索效果。

当然 Dify-RAG 目前还处于早期阶段,主要提供了部分格式的文件解析增强,后续会逐步迭代,补充更多的 RAG 所需的能力,从而让 Dify 的 RAG 能力得到显著的增强。

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易迟

高质量内容创作不易,支持下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值