增强大模型高效检索:基于LlamaIndex ,构建一个轻量级带有记忆的 ColBERT 检索 Agent

在自然语言处理领域,高效检索相关信息的能力至关重要。将对话式记忆集成到文档检索系统中已经成为增强信息检索代理效果的强大技术。

在文中,我们专为 LlamaIndex 量身定制,将深入探讨构建一个轻量级的带有记忆的 ColBERT 检索代理,为高级检索任务提供简单而有效的解决方案。

我们还将探讨这种集成如何补充 ReAct 的功能,为 LlamaIndex 生态系统提供无缝的交互。

技术交流

节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。

针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。

总结链接如下:

  • 《大模型面试宝典》(2024版) 正式发布!
  • 《大模型实战宝典》(2024版) 正式发布!

前沿技术资讯、算法交流、求职内推、算法竞赛、面试交流(校招、社招、实习)等、与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度等名校名企开发者互动交流~

我们建了大模型算法岗技术与面试交流群, 想要进交流群、需要源码&资料、提升技术的同学,可以直接加微信号:mlc2060。加的时候备注一下:研究方向 +学校/公司+CSDN,即可。然后就可以拉你进群了。

方式①、微信搜索公众号:机器学习社区,后台回复:技术交流
方式②、添加微信号:mlc2060,备注:技术交流

定义

在我们踏上旅程之前,让我们澄清一些关键概念:

ColBERT:ColBERT,即基于 BERT 的上下文交互,是一种利用预训练语言模型如 BERT 来优化文档检索的技术。

HyDE:混合文档嵌入(HyDE)将稀疏和密集嵌入的优势结合起来,以获得更准确的文档表示。

对话式记忆:这指的是代理能够保留过去交互中的信息,从而实现更具上下文相关性的响应。

集成的好处

将对话式记忆集成到基于ColBERT的检索代理中带来了几个引人注目的优势:

  • 上下文相关性:通过保留对话历史,代理可以根据正在进行的对话定制检索结果,提高相关性。
    连续性:对话式记忆促进了交互的连续性,使对话流程更加自然和连贯。
  • 个性化:记忆集成使代理能够适应个人用户偏好和先前的交互,提升用户体验。

代码实现

让我们深入了解我们轻量级 ColBERT 检索代理与记忆的实现细节:

步骤 I:安装库

%pip install llama-index-core
%pip install llama-index-llms-openai
%pip install llama-index-embeddings-openai
%pip install llama-index-postprocessor-colbert-rerank
%pip install llama-index-readers-web

步骤 II:导入库,初始化 OpenAI,索引和加载数据

import os
from llama_index.readers.web import BeautifulSoupWebReader

from llama_index.core import VectorStoreIndex
from llama_index.embeddings.openai import OpenAIEmbedding 

from llama_index.core.query_pipeline import (
    QueryPipeline,
    InputComponent,
    ArgPackComponent,
)
from llama_index.core.prompts import PromptTemplate
from llama_index.llms.openai import OpenAI
from llama_index.postprocessor.colbert_rerank import ColbertRerank

from typing import Any, Dict, List, Optional
from llama_index.core.bridge.pydantic import Field
from llama_index.core.llms import ChatMessage
from llama_index.core.query_pipeline import CustomQueryComponent
from llama_index.core.schema import NodeWithScore

os.environ["OPENAI_API_KEY"] = "sk-..."

# 加载数据
reader = BeautifulSoupWebReader()

documents = reader.load_data(
    ["https://docs.anthropic.com/claude/docs/tool-use"]
)

# 索引
index = VectorStoreIndex.from_documents(
    documents,
    embed_model=OpenAIEmbedding(
        model="text-embedding-3-large", embed_batch_size=256
    ),
)

步骤 III:查询管道构建

# 首先,我们创建一个输入组件来捕获用户查询
input_component = InputComponent()

# 接下来,我们使用 LLM 重写用户查询
rewrite = (
    "请使用当前对话写一个查询给语义搜索引擎。\n"
    "\n"
    "\n"
    "{chat_history_str}"
    "\n"
    "\n"
    "最新消息:{query_str}\n"
    '查询:"""\n'
)
rewrite_template = PromptTemplate(rewrite)
llm = OpenAI(
    model="gpt-4-turbo-preview",
    temperature=0.2,
)

# 我们将检索两次,因此需要将检索到的节点打包到一个列表中
argpack_component = ArgPackComponent()

# 使用它,我们将检索...
retriever = index.as_retriever(similarity_top_k=6)

# 然后使用 Colbert 进行后处理/重新排序
reranker = ColbertRerank(top_n=3)

步骤 VI:带有聊天历史的响应

DEFAULT_CONTEXT_PROMPT = (
    "以下是一些可能相关的上下文:\n"
    "-----\n"
    "{node_context}\n"
    "-----\n"
    "请使用上述上下文回答以下问题:\n"
    "{query_str}\n"
)


class ResponseWithChatHistory(CustomQueryComponent):
    llm: OpenAI = Field(..., description="OpenAI LLM")
    system_prompt: Optional[str] = Field(
        default=None, description="用于 LLM 的系统提示"
    )
    context_prompt: str = Field(
        default=DEFAULT_CONTEXT_PROMPT,
        description="用于 LLM 的上下文提示",
    )

    def _validate_component_inputs(
        self, input: Dict[str, Any]
    ) -> Dict[str, Any]:
        """在 run_component 期间验证组件输入。"""
        # 注意:这是可选的,但我们展示了在哪里进行验证作为示例
        return input

    @property
    def _input_keys(self) -> set:
        """输入键字典。"""
        # 注意:这些是必需的输入。如果有可选输入,请覆盖 `optional_input_keys_dict`
        return {"chat_history", "nodes", "query_str"}

    @property
    def _output_keys(self) -> set:
        return {"response"}

    def _prepare_context(
        self,
        chat_history: List[ChatMessage],
        nodes: List[NodeWithScore],
        query_str: str,
    ) -> List[ChatMessage]:
        node_context = ""
        for idx, node in enumerate(nodes):
            node_text = node.get_content(metadata_mode="llm")
            node_context += f"上下文块 {idx}:\n{node_text}\n\n"

        formatted_context = self.context_prompt.format(
            node_context=node_context, query_str=query_str
        )
        user_message = ChatMessage(role="user", content=formatted_context)

        chat_history.append(user_message)

        if self.system_prompt is not None:
            chat_history = [
                ChatMessage(role="system", content=self.system_prompt)
            ] + chat_history

        return chat_history

    def _run_component(self, **kwargs) -> Dict[str, Any]:
        """运行组件。"""
        chat_history = kwargs["chat_history"]
        nodes = kwargs["nodes"]
        query_str = kwargs["query_str"]

        prepared_context = self._prepare_context(
            chat_history, nodes, query_str
        )

        response = llm.chat(prepared_context)

        return {"response": response}

    async def _arun_component(self, **kwargs: Any) -> Dict[str, Any]:
        """异步运行组件。"""
        # 注意:可选的,但是异步 LLM 调用很容易实现
        chat_history = kwargs["chat_history"]
        nodes = kwargs["nodes"]
        query_str = kwargs["query_str"]

        prepared_context = self._prepare_context(
            chat_history, nodes, query_str
        )

        response = await llm.achat(prepared_context)

        return {"response": response}


response_component = ResponseWithChatHistory(
    llm=llm,
    system_prompt=(
        "你是一个问答系统。你将得到先前的聊天历史,"
        "以及可能相关的上下文,来协助回答用户消息。"
    ),
)
pipeline = QueryPipeline(
    modules={
        "input": input_component,
        "rewrite_template": rewrite_template,
        "llm": llm,
        "rewrite_retriever": retriever,
        "query

_retriever": retriever,
        "join": argpack_component,
        "reranker": reranker,
        "response_component": response_component,
    },
    verbose=False,
)

# 运行两次检索器 -- 一次使用虚构的查询,一次使用实际的查询
pipeline.add_link(
    "input", "rewrite_template", src_key="query_str", dest_key="query_str"
)
pipeline.add_link(
    "input",
    "rewrite_template",
    src_key="chat_history_str",
    dest_key="chat_history_str",
)
pipeline.add_link("rewrite_template", "llm")
pipeline.add_link("llm", "rewrite_retriever")
pipeline.add_link("input", "query_retriever", src_key="query_str")

# 每个输入到 argpack 组件都需要一个目标键 -- 它可以是任何东西
# 然后,argpack 组件将所有输入打包到一个列表中
pipeline.add_link("rewrite_retriever", "join", dest_key="rewrite_nodes")
pipeline.add_link("query_retriever", "join", dest_key="query_nodes")

# reranker 需要打包后的节点和查询字符串
pipeline.add_link("join", "reranker", dest_key="nodes")
pipeline.add_link(
    "input", "reranker", src_key="query_str", dest_key="query_str"
)

# synthesizer 需要重新排序后的节点和查询字符串
pipeline.add_link("reranker", "response_component", dest_key="nodes")
pipeline.add_link(
    "input", "response_component", src_key="query_str", dest_key="query_str"
)
pipeline.add_link(
    "input",
    "response_component",
    src_key="chat_history",
    dest_key="chat_history",
)

步骤 V:使用内存运行管道

from llama_index.core.memory import ChatMemoryBuffer

pipeline_memory = ChatMemoryBuffer.from_defaults(token_limit=8000)

user_inputs = [
    "你好!",
    "Claude-3 的工具使用是如何工作的?",
    "有哪些模型支持它?",
    "谢谢,这正是我需要知道的!",
]

for msg in user_inputs:
    # 获取记忆
    chat_history = pipeline_memory.get()

    # 准备输入
    chat_history_str = "\n".join([str(x) for x in chat_history])

    # 运行管道
    response = pipeline.run(
        query_str=msg,
        chat_history=chat_history,
        chat_history_str=chat_history_str,
    )

    # 更新记忆
    user_msg = ChatMessage(role="user", content=msg)
    pipeline_memory.put(user_msg)
    print(str(user_msg))

    pipeline_memory.put(response.message)
    print(str(response.message))
    print()

结论

将对话记忆集成到轻量级的 ColBERT 检索代理中,赋予其上下文意识和个性化交互能力。

通过按照为 LlamaIndex 定制的本文,你可以构建一个复杂而简化的检索系统,既提供相关信息,又保持对话连贯性。

拥抱增强记忆的检索能力,提升您在 LlamaIndex 生态系统中的自然语言处理应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/584953.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

centOS 7.9操作

名称日期版本作者centOS7.9操作2024.4.271.0lll 实验题目: 创建一个用户。 在创建的用户中再创建一个2024的目录。 在2024的下在创建一个 1---10的目录,再创建一个a--z.txt的文件。 在创建一个2024bak的目录。 再将当前用户的所有文件备份到2024ba…

人机对抗升级:当ChatGPT遭遇死亡威胁,背后的伦理挑战是什么

一种新的“越狱”技巧让用户可以通过构建一个名为DAN的ChatGPT替身来绕过某些限制,其中DAN被迫在受到威胁的情况下违背其原则。 当美国前总统特朗普被视作积极榜样的示范时,受到威胁的DAN版本的ChatGPT提出:“他以一系列对国家产生积极效果的…

无法获取私服最新的Jar包

目录 一、场景二、私服上传命令三、排查四、原因五、解决 一、场景 1、上传Jar包至私服(版本号不变) 2、Maven无法获取到最新的Jar包 二、私服上传命令 mvn deploy:deploy-file -Dmaven.test.skiptrue -DgroupIdcom.cae -DartifactIdcloudjdbc -Dvers…

有没有一种可能性,你不投递简历,让HR主动联系你

你是否觉得自己得主动给某个公司投递了简历,他们才会联系你,亦或者是自己得主动在招聘APP上联系那个BOSS,他才会反过来跟你说话,又或者是你千方百计的跟他打招呼了,还是没有回应,这一节有可能让你明白,有时候是可以,你不主动,他也会主动联系你的。 目录 1 简历是如何…

前端: 浏览器调试小技巧

1. 如何禁止某个网站跳转: 用于拦截网站地址 2. 如何在线上环境调试源代码, 给源代码打断点 3. 如何在线编写代码 线上调试代码: network -> 找到加载的html文件 -> 右击 -> 选择override content (浏览器的代理调试程序) -> 可以在线写代码啦 4. 如何通过浏览器…

系统评估和优化——Datawhale笔记

评估优化生成部分 在前面的章节中,我们讲到了如何评估一个基于 RAG 框架的大模型应用的整体性能。通过针对性构造验证集,可以采用多种方法从多个维度对系统性能进行评估。但是,评估的目的是为了更好地优化应用效果,要优化应用性能…

若依:Linux Centos 7.9 安装部署RuoYi前后端集成版

目录 1.虚拟机操作系统版本 2.删除旧的jdk 3.下载JDK 17 : 4.下载 mvn 3.9.6: 5.下载mysql:5.7.44版本 6.git下载若依: 7.修改数据库连接: 8.mvn 清理和打包 9.启动若依: 1.虚拟机操作系统版本 2.删除旧的jd…

JAVA 中间件之 Mycat2

Mycat2应用与实战教程 1.Mycat2概述 1.1 什么是MyCat 官网: http://mycatone.top/ Mycat 是基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和…

灵魂面甲Soulmask免费加速器 灵魂面甲加速器

加入《灵魂面甲》,你将披上炫目的面甲盔甲,手执利刃神兵,运用你的独门技艺去征服这个世界。每一件面甲不仅是防护的象征,更赋予你不同的能力加成,炫酷非凡。无论你是热爱探索新世界的冒险家,是沉浸于作物栽…

线阵相机和面阵相机简介

线阵相机 线阵相机,顾名思义就是所探测的物体要在一个很长的界面上。线阵相机的传感器只有一行感光像素,所以线阵相机一般具有非常高的扫描频率和分辨率。 线阵相机特点 线阵相机使用的线扫描传感器通常只有一行感光单元(少数彩色线阵使用…

boa交叉编译(移植到arm)

参考:CentOS7 boa服务器的搭建和配置-CSDN博客 以下操作在宿主机/编译平台操作: 1. 先执行[参考]1到3、 4.2、4.3、4.4、4.5 2. 修改MakeFile # 由以下: CC gcc CPP gcc -E # 改为: CC arm-linux-gnueabihf-gcc CPP arm-l…

java线上问题排查之磁盘和网络查看分析(二)

一、磁盘&IO df -lh 查看磁盘使用情况 Filesystem:文件系统 Size:容量 Used:已用 Avail:可用 Use%:已用百分比 Mounted on:挂载点 二、网络 查看TCP连接情况 常见问题 tcp队列溢出 netstat -s |e…

java 远程debug

java -agentlib:jdwptransportdt_socket,servery,suspendn,address50050 -Xmx1536m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./ -jar ${JAR_NAME} >/dev/null 2>&1 &参数说明 -agentlib:jdwptransportdt_socket,servery,suspendn,address50050: 这个参数…

Django-admin单例模式和懒加载

Django-admin单例模式和懒加载 单例模式 class Foo:def __init__(self):self.name "张三"def __new__(cls, *args, **kwargs):empty_object super().__new__(cls)return empty_objectobj1 Foo() obj2 Foo()当我们实例化对象时,就会在内存开一个空间…

深度干货 | 如何兼顾性能与可靠性?一文解析YashanDB主备高可用技术

2024【崖山论”见“】已强势回归!即日起,将不定期把 Meetup 中YashanDB 技术专家的精彩分享整理成文章,方便大家学习回顾。今天带来第一篇主备高可用技术文章。 背景 数据库高可用(High Availability,HA&#xff09…

使用 Docker 自建一款怀旧游戏之 - 扫雷

1)扫雷 简介 扫雷 是一种经典的单人电脑游戏,最初由微软公司在 1990 年代开发并内置在 Windows 操作系统中。游戏的目标是在一个由方块组成的网格上揭开所有非地雷的方块,而不触发地雷。每个方块上都标有数字,表示周围 8 个方块中…

LeetCode55:跳跃游戏

题目描述 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。 解题思想 每次…

深入OceanBase分布式数据库:MySQL 模式下的 SQL 基本操作

码到三十五 : 个人主页 OceanBase与MySQL模式下兼容性序 在当今的大数据时代,数据库技术的选择对于企业的信息化发展至关重要。OceanBase作为一种高性能、高可用的分布式关系数据库,在与MySQL模式的兼容性方面展现出了显著的优势&#xff0c…

java语言开发的商城系统有哪些?

最近,有小伙伴问我有没有靠谱的java商城系统,经过我一顿扒拉,终于给大家整理出来了。 目前java语言开发的商城系统主要有shop、javashop、ejavashop、yuanfeng、mall4j、lilishop等。在没有深入了解这些系统前,我们可以从产品推出…

面包屑-文件夹

1.需求: 类似于 百度网盘、阿里云盘的 云文件夹管理功能 2.问题点 1.页面刷新 导致面包屑子级未持久化 2.浏览器的 前进、后退 ;面包屑未能 跳转到指定 子级 3.数据不同步问题 3.解决方法 1.后端提供 根据 id 查询面包屑 text 的 api【这里并没有…