背景介绍
接触过大模型应用开发的研发同学应该都或多或少地听过 Dify 这个大模型应用基础服务,这个项目自从 2023 年上线以来,截止目前(2024-6)已经获得了 35k 多的 star,是目前大模型应用基础服务中最热门的项目之一。这篇文章对 Dify 中核心的基础模块 RAG 服务进行深入解读,后续可能会更新其他模块的内容。
Dify 简介
Dify 是一个 LLMOps 服务, 涵盖了大语言模型(如GPT系列)开发、部署、维护和优化的一整套实践和流程。可以大幅简化大模型应用的开发。
基于 Dify 可以在不需要太多开发的情况下,快速搭建一个大模型应用。应用中可以调用 Dify 中内置的大量基础能力,比如知识库检索 RAG,大模型调用。通过可插拔式的组合构建大模型应用。一个典型的应用如下所示:
上面的场景中使用分类场景,RAG 服务以及大模型调用的基础模块,组合生成一个大模型应用。
RAG 核心流程
RAG 服务的基础流程在之前的 搭建离线私有大模型知识库 文章中已经介绍过了。RAG 服务的开源框架 有道 QAnything 和 Ragflow 也都解读过基础的 RAG 流程了,这部分就不详细展开了。一般情况下,RAG 服务会包含如下所示的功能模块:
- 文件加载的支持;
- 文件的预处理策略;
- 文件检索的支持;
- 检索结果的重排;
- 大模型的处理;
因为 RAG 服务只是 Dify 中的一个基础模块,官方没有过多强调 RAG 服务的独特设计,但是依旧可以看到一个独特点:
- 支持 Q&A 模式,与上述普通的「Q to P」(问题匹配文本段落)匹配模式不同,它是采用「Q to Q」(问题匹配问题)匹配工作;
- 丰富的召回模式,支持
N 选 1 召回
和多路召回
;
下面的部分会对独特之处进行详细展开。
核心模块解读
之前介绍过来自中科院的 RAG 服务 GoMate 采取的是模块化设计,方便进行上层应用的组合。从目前的实现来看,Dify 的 RAG 设计也是采用模块化设计,RAG 的代码实现都在 api/core/rag
中,从代码结构上也很容易理解各个模块的作用:
深入来看代码的实现质量也比较高,对 RAG 的模块化设计感兴趣的可以深入了解下实现细节。
文件加载
Dify 的文件加载都是在 api/core/rag/extractor/extract_processor.py
中实现的,主要的文件解析是基于 unstructured 实现,另外基于其他第三方库实现了特定格式文件的处理
比如对于 pdf 文件,会基于 pypdfium2 进行解析,html 是基于 BeautifulSoup 进行解析,这部分代码实现都比较简单,就不展开介绍了。
文件预处理
加载的模型中的内容可能会存在一些问题,比如多余的无用字符,编码错误或其他的一些问题,因此需要对文件解析的内容进行必要的清理,这部分代码实现在 api/core/rag/cleaner
中。实际的清理都是基于 unstructured cleaning 实现的,Dify 主要就是将不同的清理策略封装为同样的接口,方便应用层自由选择。这部分实现也比较简单,感兴趣可以自行了解下。
Q&A 模式
Q&A 分段模式功能,与上述普通的「Q to P」(问题匹配文本段落)匹配模式不同,它是采用「Q to Q」(问题匹配问题)匹配工作,在文档经过分段后,经过总结为每一个分段生成 Q&A 匹配对,当用户提问时,系统会找出与之最相似的问题,然后返回对应的分段作为答案,实际的流程如下所示:
从上面的流程可以看到,Q&A 模式下会根据原始文档生成问答对,实现实现是在 api/core/llm_generator/llm_generator.py
中:
# 构造 prompt
GENERATOR_QA_PROMPT = (
'<Task> The user will send a long text. Generate a Question and Answer pairs only using the knowledge in the long text. Please think step by step.'
'Step 1: Understand and summarize the main content of this text.\n'
'Step 2: What key information or concepts are mentioned in this text?\n'
'Step 3: Decompose or combine multiple pieces of information and concepts.\n'
'Step 4: Generate questions and answers based on these key information and concepts.\n'
'<Constraints> The questions should be clear and detailed, and the answers should be detailed and complete. '
'You must answer in {language}, in a style that is clear and detailed in {language}. No language other than {language} should be used. \n'
'<Format> Use the following format: Q1:\nA1:\nQ2:\nA2:...\n'
'<QA Pairs>'
)
def generate_qa_document(cls, tenant_id: str, query, document_language: str):
prompt = GENERATOR_QA_PROMPT.format(