本文详细介绍了LLM应用中RAG开发的三个关键步骤和六个优化阶段,重点阐述了多查询检索策略如何提升检索准确性。通过生成多个角度的子问题进行检索并合并结果,有效克服传统相似性搜索的局限。文章提供了LangChain中MultiQueryRetriever的具体实现代码和优化建议,包括prompt调整和参数设置,帮助开发者在实际应用中提高RAG系统的性能和准确性。
前排提示,文末有大模型AGI-CSDN独家资料包哦!
- LLM 应用中的 RAG 开发阶段
在 RAG 应用开发中,无论架构多复杂,接入了多少组件,使用了多少优化策略与特性,所有优化的最终目标都是 提升LLM生成内容的准确性,而对于 Transformer架构类型 的大模型来说,要实现这个目标,一般只需要 3 个步骤:
- 传递更准确的内容:传递和提问准确性更高的内容,会让 LLM 能识别到关联的内容, 生成的内容准确性更高。
- 让重要的内容更靠前:GPT 模型的注意力机制会让传递 Prompt 中更靠前的内容权重更高,越靠后权重越低。
- 尽可能不传递不相关内容:缩短每个块的大小,尽可能让每个块只包含关联的内容,缩小不相关内容的比例。
看起来很简单,但是目前针对这 3 个步骤 N 多研究员提出了不少方案,比较遗憾的是,目前也没有一种统一的方案,不同的场合仍然需要考虑不同的方案结合才能实现相对好一点的效果,并不是所有场合都适合配置很复杂的优化策略。
**在 RAG 应用开发中,使用的优化策略越多,单次响应成本越高,性能越差,需要合理使用。**映射到 RAG 中,其实就是 切割合适的文档块、更准确的搜索语句、正确地排序文档、剔除重复无关的检索内容,所以在 RAG应用开发 中,想进行优化,可以针对 query(提问查询)、TextSplitter(文本分割器)、VectorStore(向量数据库)、Retriever(检索器)、Prompt(基础prompt编写) 这几个组件。
构建一个应用开发流程图,涵盖了 向量数据库、检索器、Prompt、记忆、输出解析器、大语言模型、运行时配置、大模型生成 等阶段,可以优化 RAG 的组件使用红圈覆盖如下:
在完整的 LLM 应用流程中拆解 RAG 开发阶段并进行优化看起来相对繁琐,可以考虑单独将 RAG 开发阶段的流程拎出来,并针对性对每个阶段进行优化与调整,按照不同的功能模块,共可以划分成 6 个阶段:查询转换、路由、查询构建、索引、检索 和 生成。
- RAG 开发 6 个阶段优化策略
在 RAG 开发的 6 个阶段中,不同的阶段拥有不同的优化策略,需要针对不同的应用进行特定性的优化,目前市面上常见的优化方案有:问题转换、多路召回、混合检索、搜索重排、动态路由、图查询、问题重建、自检索等数十种优化策略,每种策略所在的阶段并不一致,效果也有差异,并且相互影响。
并且 RAG 优化和 LangChain 并没有关系,无论使用任何框架、任何编程语言,进行 RAG 开发时,掌握优化的思路才是最重要的!将对应的优化策略整理到 RAG 运行流程中,优化策略与开发阶段对应图如下:
在这里插入图片描述
- 多查询检索提升检索准确性
3.1 Multi-Query 多查询策略
多查询策略 也被称为 子查询,是一种用于生成子问题的技术,其核心思想是在问答过程中,为了更好地理解和回答主问题,系统会自动生成并提出与主问题相关的子问题,这些子问题通常具有更具体的细节,可以帮助大语言模型更深入地理解主问题,从而进行更加准确的检索并提供正确的答案。
多查询策略 会从多个角度重写用户问题,为每个重写的问题执行检索,然后将检索到的文档列表进行合并后去重,返回唯一文档,该策略的运行流程非常简单,如下:
在这里插入图片描述
在 LangChain 中,针对 多查询策略 封装了一个检索器MultiQueryRetriever,该检索器可以通过构造函数亦或者from_llm类方法进行实例化,参数如下:
- retriever:基础检索器,必填参数。
- llm:大语言模型,用于将原始问题转换成多个问题,必填参数。
- prompt:转换原始问题为多个问题的提示模板,非必填,已有默认值。
- parser_key:解析键,该参数在未来将被抛弃,非必填,已弃用,新版本中保留参数,但没有任何使用的地方。
- inclued_original:是否保留原始问题,默认为 False,如果设置为 True,则除了检索新问题,还会检索原始问题。
例如以 weaviate 向量数据库作为检索器,使用 多查询策略 优化普通的 RAG检索,对应的代码具象化如下:
import dotenvimport weaviatefrom langchain.retrievers import MultiQueryRetrieverfrom langchain_openai import OpenAIEmbeddings, ChatOpenAIfrom langchain_weaviate import WeaviateVectorStorefrom weaviate.auth import AuthApiKeydotenv.load_dotenv()# 1.构建向量数据库与检索器db = WeaviateVectorStore( client=weaviate.connect_to_wcs( cluster_url="https://eftofnujtxqcsa0sn272jw.c0.us-west3.gcp.weaviate.cloud", # api-key auth_credentials=AuthApiKey("xxxxxxxxxxxxxxxxxxxxxxx"), ), index_name="DatasetDemo", text_key="text", embedding=OpenAIEmbeddings(model="text-embedding-3-small"),)# 最大边际相关性retriever = db.as_retriever(search_type="mmr")# 2.创建多查询检索器multi_query_retriever = MultiQueryRetriever.from_llm( retriever=retriever, llm=ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0),)# 3.执行检索docs = multi_query_retriever.invoke("关于LLMOps应用配置的文档有哪些")print(docs)print(len(docs))输出内容
[Document(metadata={'source': './项目API文档.md', 'start_index': 0.0}, page_content='LLMOps 项目 API 文档\n\n应用 API 接口统一以 JSON 格式返回,并且包含 3 个字段:code、data 和 message,分别代表业务状态码、业务数据和接口附加信息。\n\n业务状态码共有 6 种,其中只有 success(成功) 代表业务操作成功,其他 5 种状态均代表失败,并且失败时会附加相关的信息:fail(通用失败)、not_found(未找到)、unauthorized(未授权)、forbidden(无权限)和validate_error(数据验证失败)。\n\n接口示例:\n\njson { "code": "success", "data": { "redirect_url": "https://github.com/login/oauth/authorize?client_id=f69102c6b97d90d69768&redirect_uri=http%3A%2F%2Flocalhost%3A5001%2Foauth%2Fauthorize%2Fgithub&scope=user%3Aemail" }, "message": "" }'), Document(metadata={'source': './项目API文档.md', 'start_index': 3042.0}, page_content='1.2 [todo]更新应用草稿配置信息\n\n接口说明:更新应用的草稿配置信息,涵盖:模型配置、长记忆模式等,该接口会查找该应用原始的草稿配置并进行更新,如果没有原始草稿配置,则创建一个新配置作为草稿配置。\n\n接口信息:授权+POST:/apps/:app_id/config\n\n接口参数:\n\n请求参数:\n\napp_id -> str:需要修改配置的应用 id。\n\nmodel_config -> json:模型配置信息。\n\ndialog_round -> int:携带上下文轮数,类型为非负整型。\n\nmemory_mode -> string:记忆类型,涵盖长记忆 long_term_memory 和 none 代表无。\n\n请求示例:\n\njson { "model_config": { "dialog_round": 10 }, "memory_mode": "long_term_memory" }\n\n响应示例:\n\njson { "code": "success", "data": {}, "message": "更新AI应用配置成功" }\n\n1.3 [todo]获取应用调试长记忆'), Document(metadata={'source': './项目API文档.md', 'start_index': 5818.0}, page_content='json { "code": "success", "data": { "list": [ { "id": "1550b71a-1444-47ed-a59d-c2f080fbae94", "conversation_id": "2d7d3e3f-95c9-4d9d-ba9c-9daaf09cc8a8", "query": "能详细讲解下LLM是什么吗?", "answer": "LLM 即 Large Language Model,大语言模型,是一种基于深度学习的自然语言处理模型,具有很高的语言理解和生成能力,能够处理各式各样的自然语言任务,例如文本生成、问答、翻译、摘要等。它通过在大量的文本数据上进行训练,学习到语言的模式、结构和语义知识'), Document(metadata={'source': './项目API文档.md', 'start_index': 675.0}, page_content='json { "code": "success", "data": { "list": [ { "app_count": 0, "created_at": 1713105994, "description": "这是专门用来存储慕课LLMOps课程信息的知识库", "document_count": 13, "icon": "https://imooc-llmops-1257184990.cos.ap-guangzhou.myqcloud.com/2024/04/07/96b5e270-c54a-4424-aece-ff8a2b7e4331.png", "id": "c0759ca8-2d35-4480-83a8-1f41f29d1401", "name": "慕课LLMOps课程知识库", "updated_at": 1713106758, "word_count": 8850 } ], "paginator": { "current_page": 1, "page_size": 20, "total_page": 1, "total_record": 2 } }'), Document(metadata={'source': './项目API文档.md', 'start_index': 2324.0}, page_content='json { "code": "success", "data": { "id": "5e7834dc-bbca-4ee5-9591-8f297f5acded", "name": "慕课LLMOps聊天机器人", "icon": "https://imooc-llmops-1257184990.cos.ap-guangzhou.myqcloud.com/2024/04/23/e4422149-4cf7-41b3-ad55-ca8d2caa8f13.png", "description": "这是一个慕课LLMOps的Agent应用", "published_app_config_id": null, "drafted_app_config_id": null, "debug_conversation_id": "1550b71a-1444-47ed-a59d-c2f080fbae94", "published_app_config": null, "drafted_app_config": { "id": "755dc464-67cd-42ef-9c56-b7528b44e7c8"'), Document(metadata={'source': './项目API文档.md', 'start_index': 2042.0}, page_content='dialog_round -> int:携带上下文轮数,类型为非负整型。\n\nmemory_mode -> string:记忆类型,涵盖长记忆 long_term_memory 和 none 代表无。\n\nstatus -> string:应用配置的状态,drafted 代表草稿、published 代表已发布配置。\n\nupdated_at -> int:应用配置的更新时间。\n\ncreated_at -> int:应用配置的创建时间。\n\nupdated_at -> int:应用的更新时间。\n\ncreated_at -> int:应用的创建时间。\n\n响应示例:')]亦或者在 LangSmith 平台上也可以观测到整个执行的流程,如下:
在这里插入图片描述
3.2 核心及注意事项
从 LangSmith 平台记录的运行流程,可以很清晰看到这个检索器会先调用大语言模型生成 3 条与原始问题相关的 子问题,然后再逐个使用传递的检索器检索 3 个子问题,得到对应的文档列表,最后再将所有文档列表进行合并去重,得到最终的文档。
在MultiQueryRetriever这个检索器中,预设了一段 prompt,用于将原始问题生成 3 个关联子问题,并使用 \n 分割得到具体问题。这段 prompt 如下:
# langchain/retrievers/multi_query.pyDEFAULT_QUERY_PROMPT = PromptTemplate( input_variables=["question"], template="""You are an AI language model assistant. Your task is to generate 3 different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of distance-based similarity search. Provide these alternative questions separated by newlines. Original question: {question}""",)在 LangChain 中,所有预设的 prompt 绝大部分场景都是使用 OpenAI 的大语言模型进行调试的,所以效果会比较好,对于其他的模型,例如国内的模型,一般来说还需要将对应的提示换成 中文语言,所以可以考虑使用 ChatGPT 翻译原有的 prompt,更新后:
multi_query_retriever = MultiQueryRetriever.from_llm( retriever=retriever, llm=ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0), prompt=ChatPromptTemplate.from_template( "你是一个AI语言模型助手。你的任务是生成给定用户问题的3个不同版本,以从向量数据库中检索相关文档。" "通过提供用户问题的多个视角,你的目标是帮助用户克服基于距离的相似性搜索的一些限制。" "请用换行符分隔这些替代问题。" "原始问题:{question}" ))基于中文 prompt 生成的问题列表如下
- 应用配置的文档有哪些资源可供参考?
- 我可以在哪里找到关于应用配置的文档?
- 有哪些文档可以帮助我了解应用配置的相关信息?
对于该检索器,不同的模型生成的 query 格式可能并不一样,某些模型生成的多条 query 可能并不是按照 \n 进行分割,这个时候查询的效果可能不如原始问题,所以在使用该检索器时,一定要多次测试 prompt 的效果,或者设置 inclued_original 为 True,确保生成内容不符合规范时,仍然可以使用原始问题进行检索。
另外,在 MultiQueryRetriever 的底层进行合并去重时,并没有任何特别的,仅仅只做了循环遍历并记录唯一的文档而已,核心代码:
- 有哪些文档可以帮助我了解应用配置的相关信息?
# langchain/retrievers/multi_query.pydef _unique_documents(documents: Sequence[Document]) -> List[Document]: return [doc for i, doc in enumerate(documents) if doc not in documents[:i]]多查询策略是最基础+最简单的 RAG 优化,不涉及到复杂的逻辑与算法,会稍微影响单次对话的耗时。并且由于需要转换 query 一般较小,以及生成 sub-queries 时对 LLM 的能力要求并不高,在实际的 LLM 应用开发中,通常使用参数较小的本地模型+针对性优化的 prompt 即可完成任务。
而且为了减少模型的幻觉以及胡说八道,一般都将temperature设置为 0,确保生成的文本更加有确定性。
读者福利:倘若大家对大模型感兴趣,那么这套大模型学习资料一定对你有用。
针对0基础小白:
如果你是零基础小白,快速入门大模型是可行的。
大模型学习流程较短,学习内容全面,需要理论与实践结合
学习计划和方向能根据资料进行归纳总结
包括:大模型学习线路汇总、学习阶段,大模型实战案例,大模型学习视频,人工智能、机器学习、大模型书籍PDF。带你从零基础系统性的学好大模型!
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓
👉AI大模型学习路线汇总👈
大模型学习路线图,整体分为7个大的阶段:(全套教程文末领取哈)
第一阶段:从大模型系统设计入手,讲解大模型的主要方法;
第二阶段:在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;
第三阶段:大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;
第四阶段:大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;
第五阶段:大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;
第六阶段:以SD多模态大模型为主,搭建了文生图小程序案例;
第七阶段:以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。
👉大模型实战案例👈
光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
👉大模型视频和PDF合集👈
这里我们能提供零基础学习书籍和视频。作为最快捷也是最有效的方式之一,跟着老师的思路,由浅入深,从理论到实操,其实大模型并不难。
👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;
• 能够利用大模型解决相关实际项目需求:大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;
• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能,学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;
• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力:大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓