RAG实践分享
一、前言
随着大语言模型(Large Language Model, LLM)的兴起,基于LLM的RAG(Retrival Augmented Generation)系统在客服业务中日趋流行。本文将简要介绍RAG的原理以及其在具体实践中的一些经验技巧。
二、RAG结构
通常,RAG系统的主要结构如下:
RAG的结构看似简单,其实一点也不复杂。RAG的核心是根据用户的输入问题,从预设的知识库中查找相近的答案,然后将找到的候选语料输送给LLM,由LLM筛选、组织最合适的回答。
要想RAG能工作良好,知识库召回、LLM提示词是最关键的两个环节。下面我来简单介绍下,在实践过程中召回和LLM提示词有哪些有用的经验。
三、知识库召回
目前RAG系统使用的主要召回方案有向量召回及全文检索/关键词匹配召回。
向量召回
顾名思义,向量召回即将用户的问题文本及预设的知识库都用向量表示,然后利用向量间的相似性来查找与用户问题最匹配的候选语料,其流程可以表示如下:
然而在实践中,直接应用上述方法往往效果不佳。主要原因在于客服场景中,用户倾向于模仿即时聊天软件的使用习惯,将一个问题分解为多个短句进行提问。因此,在进行信息召回时,我们必须充分考虑用户的历史对话上下文。为改善这一问题,我们可以考虑以下几个解决方案:
将用户的历史问题与当前问题组合成一句长句,然后作为整体,向量化后再从知识库中检索相近语料。
将历史问题与当前问题逐个向量化,然后通过高斯加权求和的方式组合为一个新向量。
将当前问题和历史问题交给LLM,由LLM总结出当前用户具体的问题。
实践中,方案1,2可以在一定程度上增加上下文的召回能力,但往往也可能由于历史问题与当前用户问题关联性不强而引入与当前问题无关的检索结果。方案3可以利用LLM智能地理解用户意图,但LLM偶尔也可能出现理解错误的情况。事实上,在实践中,我们会综合考虑上述方案,并结合排序或重排来优化RAG的回答。
四、关键词匹配与重排
关键词匹配即为从用户提问中拆分出比较重要的关键词(如比较重要的名词、动词等),然后借助这些关键词从知识库中匹配最为相关的语料。
由于关键词召回和向量召回所采用的度量方式各异(一种是向量间的相似度,另一种是关键词的匹配度),因此需要一种新的度量机制来确定两种召回方式中哪一个更好。这便是 RAG 中所需用到的重排模型。
重排模型会再次将多路召回(包括向量召回和关键词召回)的结果与用户提问进行逐一比对,并重新打分。基于重排的打分结果,我们能够筛选出最佳的匹配项。
这里的重排看似简单,然而其实际作用真的会很好吗?答案是:未必。
实践表明,重排模型在 RAG 中的作用不一定是积极的。主要原因在于,在我们的客服应用场景里,用户的提问通常过于口语化,而重排模型往往是通过比较正式、通用的语料进行训练的,它难以很好地理解口语化的描述,特别是在游戏场景中,一些专有名词更是难以理解。
实践中,重排模型往往会给召回项打出有别于人的常识理解的分数。如下为某重排模型的打分示例:
# 重排输入
query = "最近的新区是哪个"
docs = [
"今天的最新区服是哪个?",
"新区最近开服时间是什么时候?"
]
# 打分结果
[{'index': 0, 'relevance_score': 0.008985429},
{'index': 1, 'relevance_score': 0.00015598028}]
可见,该重排模型的打分结果与人的理解相差甚远。
那么,重排模型是否就毫无用处呢?并非如此,正如上文所述,重排模型在实践中表现不佳,实际上是因为它对特殊应用场合的数据理解不足。从理论上讲,若使用特定场合的数据集对重排模型进行充分的微调训练,应当能够获得积极的效果。然而,由于缺乏合适的数据集,我本人并未进行这方面的微调试验。
在不使用重排模型的情况下,如何实现“重排”呢?实际上,没有重排模型,就无法对上述多路召回进行重排。一个巧妙的方法是按照不同优先级排列多种召回的结果。例如,将用户当前提问的召回置于最前面,而将上下文召回和关键词召回放在后面。同时,通过控制每路召回的阈值,合理把控每路召回的候选语料准确性。最后,将所有召回结果组合成合适的提示词提供给 LLM,由 LLM 选择合适的语料进行回答。
在 RAG 中,为了让 LLM 能够更好地理解用户问题以及知识库候选语料,LLM 的提示词也非常重要。虽然提示词的设计没有具体的规则,但在实践中,以下经验被证明是比较有效的。
提示词中给LLM设定一个恰当的人设,让LLM更好地理解当前的应用场景。比如在提示词开头做如下描述:
假设你是一位经验丰富的xxx,现在要为xxx提供客服服务。2. 提示词中可以按‘思维链’的形式,让LLM一步一步地思考然后作答。如指示LLM第一步先根据用户提问上下文总结用户意图,第二步再根据总结的意图与知识库作答。这种‘思维链’的形式让LLM对提示词的理解更加清晰。
3. 提示词中指代的内容尽量清晰明确。比如提示词中要求LLM根据候选语料回答,此时最好在提示词中先明确地标示候选语料位置,然后在使用候选语料时再明确地引用它。例如:
现在找到如下“候选语料”:
....
请从上述“候选语料”中....为何要如此明确且具体地强调呢?了解 LLM 机制的同学想必会清楚,当下基于 transformer 技术的 LLM,其内部的关键在于注意力机制。这种注意力机制能够学习上下文的相关性。提示词中明确而具体的指代会更契合这种注意力机制的发挥。
4. 尽量避免在提示词中出现过长的逻辑条件判断。如有必要,可将一个条件判断逻辑过程拆分为多个单条条件语句。例如:
如果xx(条件1)xxx则xx(动作1)xx,否则(条件2)xxxxx这里的”否则“往往难以被LLM理解,容易使LLM产生幻觉。如果拆成多个单条条件语句则LLM的理解能力可以得到显著提高:
如果xx(条件1)xxxx则xxx(动作1)x
如果xx(条件2)xxxx则xxx(动作2)x5. 在多路召回的场景下,提示 LLM 候选语料已依据相关性进行了粗排,以便 LLM 在选择回答答案时拥有更多的参考依据。实践证明,这样的描述能够有效降低 LLM 答错的概率。
6. 需要强调和约束的条件应尽量放置在提示词的后部,以便 LLM 能够更好地理解这些约束。形如:
[提示词]......
....
....
约束:
* 不要重复问题....
* 不要....提示词的设计形式多样,在实践中,你既可以参考网上的提示词模板,也可以让 AI 协助设计提示词。例如,你已经撰写了一个简单的提示词,接着可以采用如下方式让 LLM 帮助优化。
我想设计一个xxxx,现在已经构建了如下提示词,请帮我优化,使AI能更好的理解我的意图:
.....[初始提示词]...五、结语
本文是一篇关于 RAG 实践的分享,总结了 RAG 在实践过程中可能会遇到的问题以及当前一些巧妙的解决方法。期望本文能够为后续的开发者提供经验参考。