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

ops/2024/9/23 6:39:23/

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

在文中,我们专为 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 BeautifulSoupWebReaderfrom 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 ColbertRerankfrom 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 NodeWithScoreos.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@propertydef _input_keys(self) -> set:"""输入键字典。"""# 注意:这些是必需的输入。如果有可选输入,请覆盖 `optional_input_keys_dict`return {"chat_history", "nodes", "query_str"}@propertydef _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_historyreturn chat_historydef _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 ChatMemoryBufferpipeline_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 生态系统中的自然语言处理应用。


http://www.ppmy.cn/ops/26401.html

相关文章

Linux下的Git指令操作

1.安装git sudo apt-get install git 2.本地拉取已有仓库项目(红色部分请替换) git clone https://github.com/test/test.git 3. 上传本地新增内容(文本或文件夹) git add XXX 4.添加新增内容说明 git commit -m XXXX 5. 将本地仓库同步修改到远程仓库 …

【设计模式】14、strategy 策略模式

文章目录 十四、strategy 策略模式14.1 map_app14.1.1 map_app_test.go14.1.2 map_app.go14.1.3 navigate_strategy.go 十四、strategy 策略模式 https://refactoringguru.cn/design-patterns/strategy 需求: client 知道很多不同的策略, 希望在运行时切换. 场景示例: 就像高…

Spark Structured Streaming 分流或双写多表 / 多数据源(Multi Sinks / Writes)

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,…

JAVAEE—servlet的概念及使用,使用servlet接口实现一个表白墙

文章目录 servlet的概念静态页面和动态页面servlet的作用 写出一个servlet程序目录的创建设置smart tomcat编写helloworld servlet的概念 首先我们要搞明白什么是servlet,servlet是一种实现动态页面的技术,他是由tomcat提供给程序员的一组API可以帮助程…

Linux: Netlink 简介

文章目录 1. 前言2. Netlink 范例3. Netlink 简析3.1 Netlink 协议簇注册3.2 创建 用户空间 Netlink 套接字3.3 用户空间 Netlink 套接字 的 绑定3.4 向 内核空间 Netlink 套接字 发消息3.5 从 内核空间 Netlink 套接字 读消息3.5.1 内核空间 Netlink 套接字 向 用户空间 Netli…

C++特殊类设计

文章目录 C特殊类设计1、设计一个类,不能被拷贝2、设计一个类,只能在堆上创建对象3、设计一个类,只能在栈上创建对象4、设计一个类,不能被继承5、设计一个类,只能创建一个对象(单例模式) C特殊类…

【数据结构与算法】力扣 150. 逆波兰表达式求值

题目描述 给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意: 有效的算符为 、-、* 和 / 。每个操作数(运算对象)都可以是一个整数或者另一个表…

大数据开发——PostgreSQL

一、概念 PostgreSQL是一个强大的开源对象关系型数据库管理系统(ORDBMS),它是由PostgreSQL全球开发团队开发的。它在功能上与其他商业数据库系统相媲美,如Oracle、MySQL等。PostgreSQL采用了许多先进的数据库技术,包括…