解鎖Agentic RAG,使用LangChain和OpenAI進(jìn)行實(shí)踐
Agentic RAG技術(shù)是一種新興的智能體驅(qū)動(dòng)解決方案,能夠應(yīng)對(duì)復(fù)雜的多文檔問(wèn)答挑戰(zhàn)。這項(xiàng)技術(shù)通過(guò)智能化手段,不僅提升了我們處理文本數(shù)據(jù)的效率,還極大地增強(qiáng)了問(wèn)答系統(tǒng)的準(zhǔn)確性和深度。本文帶大家來(lái)了解這項(xiàng)技術(shù),闡述其如何提升信息檢索和分析的效率與準(zhǔn)確性。
1 Agentic RAG簡(jiǎn)介
Agentic RAG是由智能體驅(qū)動(dòng)的技術(shù),能夠靈活處理多文檔問(wèn)答任務(wù)。這項(xiàng)技術(shù)不僅能比較文檔、總結(jié)內(nèi)容,還能對(duì)多個(gè)摘要進(jìn)行對(duì)比分析。
正因如此,我們不完全依賴于大型語(yǔ)言模型,而是通過(guò)智能體來(lái)承擔(dān)那些需要深思熟慮、邏輯推理、工具運(yùn)用以及持續(xù)學(xué)習(xí)的任務(wù)。Agentic RAG通過(guò)智能化手段,提高了解決復(fù)雜問(wèn)題的能力,讓問(wèn)答更加高效和精確。
2 基本架構(gòu)
基本架構(gòu)是為每個(gè)文檔設(shè)置一個(gè)文檔智能體,每個(gè)文檔智能體都能在其自己的文檔內(nèi)執(zhí)行問(wèn)答和摘要。
然后,設(shè)置一個(gè)頂級(jí)智能體(元智能體)來(lái)管理所有這些較低級(jí)別的文檔智能體。
3 技術(shù)棧
技術(shù)棧包括以下幾個(gè)核心組件:
- Langchain:特別是LCEL,這是用于構(gòu)建大型語(yǔ)言模型(LLM)應(yīng)用的編排框架。
- OpenAI:提供我們需要的大型語(yǔ)言模型(LLM)。
- FAISS-cpu:用作向量存儲(chǔ)解決方案。
- 數(shù)據(jù)源:我們使用ArxivLoader來(lái)檢索arXiv上發(fā)表文章的元數(shù)據(jù)。
4 代碼實(shí)現(xiàn)
安裝所需的依賴項(xiàng):
!pip install -qU langchain langchain_openai langgraph arxiv duckduckgo-search
!pip install -qU faiss-cpu pymupdf
設(shè)置環(huán)境變量:
from google.colab import userdata
from uuid import uuid4
import os
#
os.environ['OPENAI_API_KEY'] = userdata.get('OPENAI_API_KEY')
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = f"AIE1 - LangGraph - {uuid4().hex[0:8]}"
os.environ["LANGCHAIN_API_KEY"] = userdata.get('LANGCHAIN_API_KEY')
使用LCEL實(shí)例化一個(gè)簡(jiǎn)單的檢索鏈。
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import ArxivLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
# 加載特定主題的文檔
docs = ArxivLoader(query="Retrieval Augmented Generation", load_max_docs=5).load()
# 將文檔分割成更小的塊
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=350, chunk_overlap=50
)
chunked_documents = text_splitter.split_documents(docs)
#
# 實(shí)例化嵌入模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-small",openai_api_key=os.environ['OPENAI_API_KEY'])
# 創(chuàng)建索引-將文檔塊加載到向量存儲(chǔ)中
faiss_vectorstore = FAISS.from_documents(
documents=chunked_documents,
embedding=embeddings,
)
# 創(chuàng)建檢索器
retriever = faiss_vectorstore.as_retriever()
生成RAG提示:
from langchain_core.prompts import ChatPromptTemplate
RAG_PROMPT = """\
使用以下上下文回答用戶的查詢。如果你不能回答問(wèn)題,請(qǐng)回答“我不知道”。
問(wèn)題:
{question}
上下文:
{context}
"""
rag_prompt = ChatPromptTemplate.from_template(RAG_PROMPT)
實(shí)例化LLM:
from langchain_openai import ChatOpenAI
openai_chat_model = ChatOpenAI(model="gpt-3.5-turbo")
構(gòu)建LCEL RAG鏈:
from operator import itemgetter
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
retrieval_augmented_generation_chain = (
{"context": itemgetter("question")
| retriever, "question": itemgetter("question")}
| RunnablePassthrough.assign(cnotallow=itemgetter("context"))
| {"response": rag_prompt | openai_chat_model, "context": itemgetter("context")}
)
#
retrieval_augmented_generation_chain
響應(yīng):
{
"response": "AIMessage(cnotallow='Retrieval Augmented Generation 是一種結(jié)合了深度學(xué)習(xí)技術(shù)和傳統(tǒng)檢索技術(shù)的文本生成范式。它通過(guò)顯式獲取知識(shí)以插件方式,引領(lǐng)了許多NLP任務(wù)的可擴(kuò)展性和可能緩解文本生成的難度。它涉及從檢索到的人類(lèi)編寫(xiě)的參考資料中生成文本,而不是從頭開(kāi)始生成。', response_metadata={'token_usage': {'completion_tokens': 73, 'prompt_tokens': 2186, 'total_tokens': 2259}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None})",
"context": "[Document(page_cnotallow='...'), Document(page_cnotallow='...'), Document(page_cnotallow='...')]"
}
構(gòu)建工具箱:
為了使智能體能夠回答各種問(wèn)題并引入外部知識(shí),通常會(huì)給其配備一個(gè)工具箱。
LangChain社區(qū)的存儲(chǔ)庫(kù)(https://github.com/langchain-ai/langchain/tree/master/libs/community/langchain_community/tools)中提供了眾多工具,這里選擇了幾種來(lái)使用,這樣可以更直觀地展示LangGraph的循環(huán)特性。
具體來(lái)說(shuō),將整合以下工具:
- Duck Duck Go網(wǎng)絡(luò)搜索(https://github.com/langchain-ai/langchain/tree/master/libs/community/langchain_community/tools/ddg_search)
- Arxiv學(xué)術(shù)文獻(xiàn)檢索(https://github.com/langchain-ai/langchain/tree/master/libs/community/langchain_community/tools/arxiv))
from langchain_community.tools.ddg_search import DuckDuckGoSearchRun
from langchain_community.tools.arxiv.tool import ArxivQueryRun
from langgraph.prebuilt import ToolExecutor
tool_belt = [
DuckDuckGoSearchRun(),
ArxivQueryRun()
]
tool_executor = ToolExecutor(tool_belt)
實(shí)例化OpenAI函數(shù)調(diào)用:
from langchain_core.utils.function_calling import convert_to_openai_function
#
model = ChatOpenAI(temperature=0)
#
functions = [convert_to_openai_function(t) for t in tool_belt]
model = model.bind_functions(functions)
使用LangGraph:
LangGraph通過(guò)StatefulGraph來(lái)實(shí)現(xiàn),這個(gè)結(jié)構(gòu)使用AgentState對(duì)象在圖的不同節(jié)點(diǎn)間傳遞信息。
雖然有很多配置選項(xiàng),但核心是AgentState對(duì)象,它被存儲(chǔ)在一個(gè)TypedDict中。這個(gè)對(duì)象的鍵是“messages”,對(duì)應(yīng)的值是一個(gè)BaseMessages的序列,每當(dāng)狀態(tài)發(fā)生變化時(shí),新的消息就會(huì)被添加到這個(gè)序列中。
from typing import TypedDict, Annotated, Sequence
import operator
from langchain_core.messages import BaseMessage
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
構(gòu)建節(jié)點(diǎn)
call_model是一個(gè)節(jié)點(diǎn),將...嗯...調(diào)用模型
call_tool是一個(gè)節(jié)點(diǎn),將調(diào)用工具
```python
from langgraph.prebuilt import ToolInvocation
import json
from langchain_core.messages import FunctionMessage
def call_model(state):
messages = state["messages"]
response = model.invoke(messages)
return {"messages" : [response]}
def call_tool(state):
last_message = state["messages"][-1]
action = ToolInvocation(
tool=last_message.additional_kwargs["function_call"]["name"],
tool_input=json.loads(
last_message.additional_kwargs["function_call"]["arguments"]
)
)
response = tool_executor.invoke(action)
function_message = FunctionMessage(cnotallow=str(response), name=action.tool)
return {"messages" : [function_message]}
構(gòu)建工作流:
from langgraph.graph import StateGraph, END
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_node("action", call_tool)
workflow.nodes
設(shè)置入口點(diǎn):
workflow.set_entry_point("agent")
構(gòu)建條件邊進(jìn)行路由:
def should_continue(state):
last_message = state["messages"][-1]
if"function_call"notin last_message.additional_kwargs:
return"end"
return"continue"
workflow.add_conditional_edges(
"agent",
should_continue,
{
"continue" : "action",
"end" : END
}
)
最后將條件邊連接到智能體節(jié)點(diǎn)和動(dòng)作節(jié)點(diǎn):
workflow.add_edge("action", "agent")
編譯工作流:
app = workflow.compile()
#
app
調(diào)用LangGraph - 提問(wèn):
from langchain_core.messages import HumanMessage
inputs = {"messages" : [HumanMessage(cnotallow="在大型語(yǔ)言模型的背景下,RAG是什么?它是什么時(shí)候出現(xiàn)的?")]}
response = app.invoke(inputs)
print(response)
響應(yīng):
{
"messages": [
HumanMessage(cnotallow="在大型語(yǔ)言模型的背景下,RAG是什么?它是什么時(shí)候出現(xiàn)的?"),
AIMessage(cnotallow='', additional_kwargs={'function_call': {'arguments': '{"query":"RAG in the context of Large Language Models"}', 'name': 'duckduckgo_search'}}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 171, 'total_tokens': 196}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'function_call', 'logprobs': None}),
FunctionMessage(cnotallow="大型語(yǔ)言模型(LLMs)是處理和生成文本的非常強(qiáng)大的工具。然而,它們天生難以理解更廣泛的信息背景,特別是在處理長(zhǎng)篇對(duì)話或復(fù)雜任務(wù)時(shí)。這就是大型上下文窗口和檢索增強(qiáng)生成(RAG)發(fā)揮作用的地方。這些先進(jìn)的、泛化的語(yǔ)言模型是在龐大的數(shù)據(jù)集上訓(xùn)練的,使它們能夠理解和生成類(lèi)似人類(lèi)的文本。在RAG的背景下,LLMs被用來(lái)基于用戶查詢和從向量數(shù)據(jù)庫(kù)檢索的上下文信息生成完整的響應(yīng)。在語(yǔ)言模型快速發(fā)展的背景下,檢索增強(qiáng)生成(RAG)和長(zhǎng)上下文大型語(yǔ)言模型(LLMs)之間的辯論引起了廣泛關(guān)注。檢索增強(qiáng)生成(RAG)是一個(gè)AI框架,通過(guò)將模型基于外部知識(shí)源來(lái)補(bǔ)充LLM的內(nèi)部信息表示,從而提高LLM生成響應(yīng)的質(zhì)量。在基于LLM的問(wèn)答系統(tǒng)中實(shí)施RAG有兩個(gè)主要好處:它確保模型有... RAG代表檢索增強(qiáng)生成。RAG使大型語(yǔ)言模型(LLM)能夠訪問(wèn)和利用最新的信息。因此,它提高了LLM的響應(yīng)質(zhì)量和相關(guān)性。下面是一個(gè)簡(jiǎn)單的RAG實(shí)現(xiàn)圖。", name='duckduckgo_search'),
AIMessage(cnotallow="在大型語(yǔ)言模型(LLMs)的背景下,RAG代表檢索增強(qiáng)生成。它是一個(gè)AI框架,通過(guò)將模型基于外部知識(shí)源來(lái)補(bǔ)充LLM的內(nèi)部信息表示,從而提高LLM生成響應(yīng)的質(zhì)量。RAG使LLM能夠訪問(wèn)和利用最新的信息,從而提高模型生成的響應(yīng)的相關(guān)性和質(zhì)量。RAG作為增強(qiáng)LLM在理解和生成類(lèi)似人類(lèi)文本的能力的方式,在語(yǔ)言模型快速發(fā)展的領(lǐng)域中出現(xiàn)。", response_metadata={'token_usage': {'completion_tokens': 117, 'prompt_tokens': 491, 'total_tokens': 608}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_3bc1b5746c', 'finish_reason': 'stop', 'logprobs': None})
]
}
響應(yīng)內(nèi)容:
RAG代表檢索增強(qiáng)生成,在大型語(yǔ)言模型(LLMs)的背景下。它是一個(gè)AI框架,通過(guò)將模型基于外部知識(shí)源來(lái)補(bǔ)充LLM的內(nèi)部信息表示,從而提高LLM生成響應(yīng)的質(zhì)量。RAG使LLM能夠訪問(wèn)和利用最新的信息,從而提高模型生成的響應(yīng)的相關(guān)性和質(zhì)量。RAG作為增強(qiáng)LLM在理解和生成類(lèi)似人類(lèi)文本的能力的方式,在語(yǔ)言模型快速發(fā)展的領(lǐng)域中出現(xiàn)。
提問(wèn):
問(wèn)題 = "檢索增強(qiáng)生成論文的主要作者是誰(shuí) - 他們參加了哪所大學(xué)?"
inputs = {"messages" : [HumanMessage(cnotallow=問(wèn)題)]}
response = app.invoke(inputs) print(response['messages'][-1].content)
**響應(yīng):**
關(guān)于“檢索增強(qiáng)生成”論文的主要作者是Huayang Li。不幸的是,提供的摘要中沒(méi)有提到他們參加的大學(xué)。
**提問(wèn):**
問(wèn)題 = "檢索增強(qiáng)生成論文的主要作者是誰(shuí)?"
inputs = {"messages" : [HumanMessage(cnotallow=問(wèn)題)]}
response = app.invoke(inputs)
print(response['messages'][-1].content)
響應(yīng):論文“檢索增強(qiáng)文本生成”的主要作者是Huayang Li、Yixuan Su、Deng Cai、Yan Wang和Lemao Liu。
本文轉(zhuǎn)載自 ??AI科技論談??,作者: AI科技論談
