科研漫谈——RAG在LLM中如何调用

科研漫谈——RAG在LLM中如何/何时调用

文章目录

  • 科研漫谈——RAG在LLM中如何/何时调用
    • 1. 背景
    • 2. RAG与LLM关系
    • 2. 检索增强生成(RAG)阶段逻辑
    • 3. 复杂输入(长文本/结构化数据)处理

1. 背景

本文主要解决如下问题:

  1. rag作为数据‘外挂’,和提示词工程的先后顺序是怎样的?
  2. rag的每个阶段在做什么?
  3. 长文本条件下,如何有效解决?
    科研漫谈——RAG在LLM中如何调用_第1张图片

2. RAG与LLM关系

在 Dify 的整体工作流(Chatflow/Workflow)中,RAG 检索节点通常在 LLM 节点之前执行,用于获取外部知识并构建上下文,再将这些检索到的上下文融入到后续的提示(prompt)中。以代码实现为例,实际调用链为:用户输入经过知识检索节点(如 KnowledgeRetrievalNode )触发 RetrievalService.retrieve(…) ,执行文档检索和重排后将结果返回,最终由 LLM 节点构造带有检索到内容的提示并调用 LLM生成回复 。在 LLM 节点内部,使用核心类 LLMGenerator 调用模型接口生成文本。例如, LLMGenerator 的invoke_llm 方法以构造好的 PromptMessage 列表为输入,通过 model_instance.invoke_llm(…) 发起请求 。下图展示简化的执行流程及核心类调用链:

  • 用户请求到知识检索节点:调用 DatasetRetrieval._retriever(…) 方法,内部使用RetrievalService.retrieve(…) 发起检索 。

  • 检索服务执行: RetrievalService 根据策略调用向量搜索( VectorFactory.search_by_vector )或全文检索( KeywordFactory.search_by_fulltext ),得到初步文档列表。

  • 后处理与重排:检索结果传入 DataPostProcessor.invoke(query, documents, score_threshold,top_n) 进行过滤拼接,此处通过 DifyConfig.RETRIEVAL_TOP_N 控制返回文档数量 。在内部,RerankRunner.run(…) 会进一步调用可配置的重排模型( RerankModel.invoke_rerank )对文档列表排序 。

  • 构建提示并调用 LLM:将检索到的文档内容组织成提示(可能作为系统或用户提示的一部分),最终传给 LLMGenerator.invoke_llm 。例如,在问答场景中, LLMGenerator.generate_qa_document 先拼接固定模板 GENERATOR_QA_PROMPT 和用户查询,再调用 invoke_llm 生成答案 。此时可以通过model_parameters={“max_tokens”:…} 控制生成长度 。

综上,RAG 在构造提示之前执行,其检索结果被注入到提示模板中,之后才调用 LLM 生成最终文本 。

2. 检索增强生成(RAG)阶段逻辑

RAG 的检索阶段由多个模块协作完成:

  • 检索服务 ( RetrievalService ):根据配置的检索方法(向量/关键字/混合),并行调用相应的数据源检索器,如向量检索器( core.rag.datasource.vdb.VectorFactory )和全文检索器( core.rag.datasource.keyword.KeywordFactory )。这些工厂类负责与底层向量数据库或文本索引进行交互,返回匹配的 Document 对象列表。

  • 数据后处理 ( DataPostProcessor ):对初步检索到的文档集合进行合并、去重或过滤。调用链上执行data_post_processor.invoke(query, documents, score_threshold, top_n) ,其中 top_n 由配置参数 DifyConfig.RETRIEVAL_TOP_N 决定(默认为 0 表示不限制,否则为 top_k ) 。该方法会保留满足置信度阈值的文档并截断至 top_n ,同时可将多个源的结果聚合。

  • 重排序 ( RerankModel ):在后处理阶段, RerankRunner.run(query, documents, score_threshold,top_n, user) 会调用选定的再排序模型(如基于大型语言模型的 rerank 服务)对文档进行打分和排序 。实现上通常调用 model_manager.invoke_rerank 或插件管理器接口进行重排。

  • 拼接文本:最终输出的是一个 DocumentContext 列表(每个含原文内容和元数据),这些文档内容将被拼接进下游提示模板中作为上下文。目前 Dify 默认直接使用检索结果的纯文本内容,没有额外格式包装;长度控制上主要依赖于模型参数的 max_tokens 和提示模板本身的长度限制 。

各环节调用关系可参见异常日志跟踪: DatasetRetrieval._retriever -> RetrievalService.retrieve -> DataPostProcessor.invoke -> RerankModel.run 。结果返回至工作流,最终由 LLM 节点生成回答。

3. 复杂输入(长文本/结构化数据)处理

对于长文本或结构化数据(如表格、JSON)等知识源,Dify 通过“数据集索引”机制实现处理和上下文注入:

• 数据源导入:用户可通过界面/API 将长文本文件(TXT、PDF、DOCX 等)或结构化数据添加为数据集。Dify 根据文档类型自动选择解析器,如 PDF 提取器( core.rag.extractor.pdf_extractor.PdfExtractor ) 提取文本,或者针对 JSON/表格数据的解析插件(目前可通过 Tabulator 插件等实现)。

• 分片策略:长文本通过 分片器 切分为更小块。代码中常用的分片器有EnhanceRecursiveCharacterTextSplitter 和 FixedRecursiveCharacterTextSplitter ,它们基于字符长度递归划分文本,确保段落和句子边界尽量完整。对于结构化数据(如表格),通常先将表格行/列转换为文本,然后按行或单元格分片。此外,Dify 允许用户配置 交叉重叠(chunk overlap)长度,控制片段间的信息连贯性。

• 索引构建:分片后生成的文本块被封装为 Document 或 DocumentSegment 对象,并通过对应的 索引处理器 存入向量数据库。 IndexProcessorFactory 根据数据格式( dataset_document.doc_form )实例化不同的 IndexProcessor ,负责执行向量化和写入操作 。例如,对于表格型文档,IndexProcessor 可能会对每一行生成向量并建立索引。最终,所有文档片段的向量被存储以供检索。

• 接口与插件:Dify 提供丰富的数据源连接插件(如 WebReader、NotionReader)和矢量存储后端(Milvus、Qdrant 等),并在 VectorService 中统一管理向量插入/查询。用户配置检索节点时,可以指定所需的检索库和表,系统会自动调用相应的 API。在运行时,长文本或结构化数据的内容会与检索到的上下文一并作为提示传给 LLM,从而实现对复杂输入的理解和回答。

总之,对于复杂场景,Dify 通过文本分片和索引构建将输入转为可检索的知识块,利用插件接口(API)进行存储与检索,实现了对长文档、表格、JSON 等多种格式的支持。所有相关参数(如每片大小、重叠率、检索阈值、返回文档数 top_n 等)均可在配置文件或任务中指定,从而灵活控制数据注入和 RAG行为 。

你可能感兴趣的:(科研漫谈,深度学习,python,语言模型,自动化)