亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

揭秘:RAG系統的魔力并不來自AI生成

为什么检索而非生成让RAG系统(RAG系统)特别厉害 快速原型

注:POCs具体含义请参见后续文本或脚注。

大多数快速的概念验证(POC)震撼你,这些概念验证允许用户借助对话式人工智能来探索数据。当你突然能够和你的文档、数据或代码库进行对话时,感觉就像突然拥有了魔法。

这些POC在文档数量有限的小数据集上效果显著。然而,就像大多数东西在实际应用中一样,当你尝试在大规模数据上运行时,问题很快就会出现。当你仔细检查并深入研究AI给出的答案时,你会发现:

  • 你的代理没有回复完整的信息,有些重要的数据被遗漏了。
  • 你的代理给出的答案并不一致。
  • 你的代理无法告诉你它从哪里获取了哪些信息,这使得答案的实用性大大降低。

原来RAG里的真正魔法不在生成模型那一步,而是在检索与重组的过程中。一深入了解,原因就很明显了……

* RAG = 检索增强生成 — RAG 的维基百科定义如下

RAG过程——举例

那么,一个配备了RAG功能的AI代理是如何回答问题的?

快速回顾一下,一个简单的RAG过程是如何运作的:

  1. 这一切始于发起一个查询。用户提出了一个问题,或者某个系统正在尝试回答一个问题。
  2. 使用查询进行搜索。通常你会嵌入查询并进行相似性搜索,但你也可以进行传统的全文搜索,或者两者结合,或者直接查找信息。
  3. 搜索结果是一组文档,我们暂时简称为文档。
  4. 将这些文档与查询的核心结合,形成易于理解的上下文,以便AI能够处理。
  5. AI根据问题和文档,生成答案。
  6. 理想状态下,这个答案会经过事实验证,以确认AI的答案是否基于文档,并且/或者是否适合受众。
魔法呢?

其实最不光彩的秘密是,RAG过程的本质是检索和生成(Retrieval and Generation),你必须在AI采取任何行动之前给出答案,这样它才能给出你想要的答案。

换句话说:

  • AI所做的工作(步骤5)是做出判断,并清楚地表达答案
  • 工程师要做的工作(步骤3以及4)是找到答案,并以一种AI可以理解的方式整理答案

当然,这要看情况,因为如果判断是关键因素,那么AI模型就能搞定一切。但在很多商业场景中,找出并正确组合这些答案的各个部分,才是更重要的部分。

如果你想要实现有效的RAG过程,通常需要解决哪些工程问题?

首先需要解决的是如何处理数据摄入、拆分和分块,以及文档解释等问题。之前我在文章中提到过一些相关问题,这里就不赘述了。假设你已经解决了数据摄入的问题,并且已经有了一个良好的向量存储或搜索索引,比如一个良好的向量存储或搜索索引。

常见挑战。

  • 重复 — 即使是简单的生产系统,通常也会有重复的文档。当系统规模较大、用户或租户众多、连接到多个数据源或处理版本问题时,情况更是如此。
  • 近似重复 — 含有几乎相同数据但有细微差异的文档。近似重复分为两类:
    — 有意义的 — 例如进行的小修改或添加,比如日期字段的更新
    — 无意义的 — 例如:标点、语法或空格的细微差异,或仅仅是由于时间或处理流程导致的差异
  • 数据量 — 有些查询会产生非常大的相关响应数据集
  • 数据新鲜度与质量 — 哪些片段对于AI来说质量最高,哪些片段从时间(新鲜度)角度来看最为相关?
  • 数据多样性 — 如何确保搜索结果的多样性以确保AI获得充足的信息?
  • 查询措辞和歧义 — 触发RAG流程的提示可能措辞不当,无法获得最佳结果,甚至具有歧义
  • 响应个性化 — 查询可能需要根据提问者不同给出不同的响应

这个列表还可以继续下去,但你应该明白了,对吧?

基于无限大小的上下文窗口能解决这个问题吗?

简短回答:不行。

使用非常大的上下文窗口的成本和性能影响不应被低估,因为每次查询的成本可能会增加到原来的10倍或100倍,不包括任何后续的用户或系统交互。

然而,撇開這不談,想像一下這種情況。

我们把安放在一个房间里,房间里有一张写着内容的纸。纸上写着:患者乔:复杂的脚部骨折。现在我们问安,患者是否有脚骨折?她的回答是“是的,他有”。

现在我们给安一百页乔的病历。她的回答变成了“嗯,这要看你指的是哪个时间段,他当时……”

现在我们给安成千上万页有关所有病人的资料……

你很快就会发现,我们如何定义问题(或者说,我们的提示)变得非常重要。上下文范围越大,就需要考虑更多的细节。

此外,上下文窗口越大,可能的答案也就越多。这听起来是个好主意,但实际上这种方法容易让人偷懒,如果不小心处理,可能会削弱应用的功能。

这里有几个建议的方法

在扩展RAG系统从原型验证(PoC)到生产环境的过程中,这里是一些具体的方法来应对常见的数据挑战。

复制

在多源系统中,重复不可避免。通过使用指纹(内容的哈希值)、文档ID或语义哈希,可以在内容输入时识别完全重复并防止冗余。不过,合并重复内容的元数据也很有价值,这可以让用户知道某些内容出现在多个来源中,从而增加内容的可信度或突出数据集中的重复现象。

    # 生成指纹以去重
    def fingerprint(doc_content):
        return hashlib.md5(doc_content.encode()).hexdigest()

    # 存储指纹并过滤重复项,同时整合元数据
    fingerprints = {}
    unique_docs = []
    for doc in docs:
        fp = fingerprint(doc['content'])
        if fp not in fingerprints:
            fingerprints[fp] = [doc]
            unique_docs.append(doc)
        else:
            fingerprints[fp].append(doc)  # 合并来源信息
接近重复

几乎相同的文档(相似但不完全相同)常常包含重要的更新或细微添加。因为即使是小小的改动,比如状态更新,也可能包含重要信息,所以在处理几乎相同的文档时,保持最新版本变得非常重要。一种实用的方法是先用余弦相似度来检测,然后在每组文档中保留最新版本,并标出任何有意义的更新。

    从sklearn.metrics.pairwise导入cosine_similarity函数  
    从sklearn.cluster导入DBSCAN聚类算法  
    导入numpy作为np(或np as np)  

    # 使用DBSCAN对嵌入进行聚类,以便查找近重复项  
    聚类 = DBSCAN(eps=0.1, min_samples=2, metric='余弦相似度').fit(doc_embeddings)  

    # 按聚类标签整理文档  
    聚类文档集 = {}  
    for 索引, 标签 in enumerate(clustering.labels_):  
     if 标签 == -1:  
      continue  
     if 标签 not in 聚类文档集:  
      聚类文档集[标签] = []  
     聚类文档集[标签].append(docs[索引])  

    # 过滤聚类以保留每个聚类中最新的文档  
    过滤文档 = []  
    for 聚类文档列表 in 聚类文档集.values():  
     # 选择具有最新时间戳或最高相关性的文档  
     最新文档 = max(聚类文档列表, key=lambda d: d['timestamp'])  
     过滤文档.append(最新文档)

当查询返回大量相关的文档时,有效处理这些文档至关重要。一种方法是采用分层的方法

  • 主题提取:对文档进行预处理以提取特定主题或摘要。
  • Top-k 过滤:合成之后,根据相关性评分过滤摘要内容。
  • 相关性评分:采用相似性度量(如BM25或余弦相似度),在检索之前优先考虑最相关的文档。

这种方法通过检索更易于AI处理的合成信息来减轻工作负担。其他策略可能包括按主题批量处理文档或预先整理摘要,以进一步简化检索过程。

数据新鲜度 vs. 数据质量

保持质量和新鲜度的平衡非常重要,尤其是在变化迅速的数据集中更是如此。有很多种评分方法,这里有一个常用的策略:

  • 复合评分:使用来源可靠性、内容深度和用户参与度等因素计算质量评分。
  • 时效性加权:用时间戳权重调整评分,强调时效性。
  • 筛选阈值:只有符合综合质量和时效性阈值的文档才能被检索。

其他策略可能有只给高质量来源打分,或对旧文档采用衰减因子。

数据类型多样

确保多样化的数据源有助于创建平衡的响应。通过将文档按来源分组(例如,不同的数据库、作者或内容类型),并从每个来源中选择最佳片段是一种有效的方法。其他方法还包括根据独特视角评分或应用多样性的限制,以避免对任何单一文档或视角过度依赖。

    # 通过分组并从每个来源选择顶级片段来确保多样性

    from itertools import groupby  

    k = 3  # 每个来源的顶级片段数  
    docs = sorted(docs, key=lambda d: d['source'])  

    grouped_docs = {key: list(group)[:k] for key, group in groupby(docs, key=lambda d: d['source'])}  
    # 从分组后的文档中提取所有顶级片段  
    diverse_docs = [doc for docs in grouped_docs.values() for doc in docs]
查询措辞与歧义

模糊的查询可能会导致不太理想的结果。直接使用用户的原始提示不是获取他们所需结果的最佳方式。比如,之前聊天里可能已经有过相关的信息交换。或者用户粘贴了一大段文本并问了相关的问题。

为了确保你使用精简的查询,一种方法是让提供给模型的RAG工具要求模型将问题改写为更详细的搜索查询,类似于精心制作用于谷歌的搜索查询。这种方法使用户意图与RAG检索过程更加一致。虽然下面的表述还有待改进,但大致意思已经表达出来了。

    tools = [{   
      "name": "搜索我们内部公司数据库",   
      "description": "搜索我们内部公司数据库中的相关文档",   
      "parameters": {   
       "type": "object",   
       "properties": {   
        "query": {   
         "type": "string",   
         "description": "以句子的形式输入搜索词,就像在谷歌搜索一样。请确保包含问题中的所有重要细节。"   
        }   
       },   
       "required": ["query"]   
      }   
    }]
个性化回复

为了给用户提供个性化的回复,可以直接将用户的特定信息整合到RAG的上下文中。通过在最终上下文中增加用户特定的信息层,可以让AI考虑到个人的偏好、权限或历史记录,而不改变核心的检索流程。

通过解决这些数据挑战,你的RAG系统可以由一个有说服力的概念验证(POC)演进为一个稳定且高效的解决方案。最终,RAG的有效性更多地依赖于精心的工程设计,而非AI模型本身。虽然AI可以生成流利的答案,但真正的窍门在于我们如何检索和组织信息。所以,下次当你对一个AI系统的对话能力感到惊讶时,记得这很可能是幕后一个设计精巧的检索过程的结果。

我希望这篇文章能让你对RAG过程有些了解,并且让你明白为什么你体验到的那种神奇的感觉并不一定来源于AI模型,而是很大程度上取决于你的检索过程的设计思路。当你跟你的数据对话时,那种神奇的感觉很大程度上取决于你的检索过程的设计。

请分享你的想法。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消