經(jīng)典RAG應(yīng)用的范式與架構(gòu)已經(jīng)非常流行,你甚至可以在很短的時(shí)間內(nèi)借助成熟框架開(kāi)發(fā)一個(gè)簡(jiǎn)單能用的RAG應(yīng)用:用戶(hù)問(wèn)題被輸入RAG、應(yīng)用執(zhí)行檢索、從被向量化的文檔中檢索相關(guān)知識(shí)塊、送入到LLM(大語(yǔ)言模型)進(jìn)行合成響應(yīng):
但是讓我們考慮這樣一個(gè)應(yīng)用場(chǎng)景:企業(yè)中有大量不同來(lái)源與類(lèi)型的文檔(在實(shí)際中并不一定代表“文件”,也可以是某種非文件形態(tài)的信息,比如存放在RDBMS中的數(shù)據(jù)),現(xiàn)在需要在這些文檔之上構(gòu)建一個(gè)依賴(lài)于它們的、知識(shí)密集型的應(yīng)用或工具。這些需求包括:
基于全局的理解文檔后回答問(wèn)題。比如:對(duì)某知識(shí)內(nèi)容進(jìn)行總結(jié)摘要?
跨文檔與知識(shí)庫(kù)的回答問(wèn)題。比如:比較不同文檔內(nèi)容的區(qū)別?
結(jié)合非知識(shí)工具的復(fù)合場(chǎng)景。比如:從文檔提取產(chǎn)品介紹發(fā)送給xx客戶(hù)?
這種復(fù)雜需求的場(chǎng)景如果使用經(jīng)典RAG架構(gòu),通過(guò)chunks+向量+top_K檢索來(lái)獲得并插入上下文,直接讓LLM來(lái)給出答案,顯然是不現(xiàn)實(shí)的。**經(jīng)典RAG在回答文檔相關(guān)的事實(shí)性問(wèn)題時(shí)可以工作的不錯(cuò),但是實(shí)際的知識(shí)應(yīng)用并不總是這種類(lèi)型!**當(dāng)然你也可以借助一些改進(jìn)的RAG范式來(lái)提高應(yīng)用場(chǎng)景的適應(yīng)性,比如RAPTOR(基于文檔樹(shù)的多級(jí)檢索機(jī)制,有利于回答從細(xì)節(jié)到高層理解的多級(jí)問(wèn)題),但在一些跨文檔或者需要結(jié)合工具的場(chǎng)景仍然無(wú)法勝任。
這里介紹Agentic RAG方案:**一種基于AI Agent的方法,借助Agent的任務(wù)規(guī)劃與工具能力,來(lái)協(xié)調(diào)完成對(duì)多文檔的、多類(lèi)型的問(wèn)答需求。**既能提供RAG的基礎(chǔ)查詢(xún)能力,也能提供基于RAG之上更多樣與復(fù)雜任務(wù)能力。概念架構(gòu)如下:
在這里的Agentic RAG架構(gòu)中:
RAG應(yīng)用(RAG引擎,即借助索引實(shí)現(xiàn)檢索并合成響應(yīng))退化成一個(gè)Agent使用的知識(shí)工具。你可以針對(duì)一個(gè)文檔/知識(shí)庫(kù)構(gòu)建多種不同的RAG引擎,比如使用向量索引來(lái)回答事實(shí)性問(wèn)題;使用摘要索引來(lái)回答總結(jié)性問(wèn)題;使用知識(shí)圖譜索引來(lái)回答需要更多關(guān)聯(lián)性的問(wèn)題等
在單個(gè)文檔/知識(shí)庫(kù)的多個(gè)RAG引擎之上設(shè)置一個(gè)ToolAgent,把RAG引擎作為該Agent的tools,并利用LLM的能力由ToolAgent在自己“負(fù)責(zé)”的文檔內(nèi)使用這些tools來(lái)回答問(wèn)題
設(shè)置一個(gè)總的頂級(jí)代理TopAgent來(lái)管理所有的低階ToolAgent,將ToolAgent看作自己的tools,仍然利用LLM來(lái)規(guī)劃、協(xié)調(diào)、執(zhí)行用戶(hù)問(wèn)題的回答方案
以下使用LlamaIndex來(lái)實(shí)現(xiàn)這個(gè)架構(gòu)(如果你是LangChain用戶(hù),也完全可以讀懂并參考實(shí)現(xiàn))
02
HOT SUMMER
實(shí)現(xiàn)Agentic RAG
讓我們來(lái)一步步實(shí)現(xiàn)簡(jiǎn)單的Agentic RAG。
【準(zhǔn)備測(cè)試文檔】
首先這里準(zhǔn)備三個(gè)RAG相關(guān)的測(cè)試PDF文檔,其名稱(chēng)與路徑分別保存。當(dāng)然,在實(shí)際應(yīng)用中,這里文檔數(shù)量可以擴(kuò)展到非常大(后面會(huì)看到針對(duì)大量文檔的一個(gè)優(yōu)化方法):
names = ['c-rag','self-rag','kg-rag']
files = ['../../data/c-rag.pdf','../../data/self-rag.pdf','../../data/kg-rag.pdf']
【準(zhǔn)備創(chuàng)建Tool Agent的函數(shù)】
創(chuàng)建一個(gè)針對(duì)單個(gè)文檔生成Tool Agent的函數(shù),在這個(gè)函數(shù)中,將對(duì)一個(gè)文檔創(chuàng)建兩個(gè)索引與對(duì)應(yīng)的RAG引擎:
針對(duì)普通事實(shí)性問(wèn)題的向量索引與RAG引擎
針對(duì)更高層語(yǔ)義理解的總結(jié)類(lèi)問(wèn)題的摘要索引與RAG引擎
最后,我們把這兩個(gè)引擎作為一個(gè)Agent可使用的兩個(gè)tool,構(gòu)建一個(gè)Tool Agent返回。
......省略import部分與準(zhǔn)備llm部分......
#采用chroma向量數(shù)據(jù)庫(kù)
chroma = chromadb.HttpClient(host="localhost", port=8000)
collection = chroma.get_or_create_collection(name="agentic_rag")
vector_store = ChromaVectorStore(chroma_collection=collection)
#創(chuàng)建針對(duì)某個(gè)文檔的tool_agent
def create_tool_agent(file,name):
#文檔拆分
print(f'Starting to create tool agent for 【{name}】...\n')
docs =SimpleDirectoryReader(input_files = [file]).load_data()
splitter = SentenceSplitter(chunk_size=500,chunk_overlap=50)
nodes = splitter.get_nodes_from_documents(docs)
#創(chuàng)建向量索引,并做持久保存
if not os.path.exists(f"./storage/{name}"):
print('Creating vector index...\n')
storage_context = StorageContext.from_defaults(vector_store=vector_store)
vector_index = VectorStoreIndex(nodes,storage_context=storage_context)
vector_index.storage_context.persist(persist_dir=f"./storage/{name}")
else:
print('Loading vector index...\n')
storage_context = StorageContext.from_defaults(persist_dir=f"./storage/{name}",vector_store=vector_store)
vector_index = load_index_from_storage(storage_context=storage_context)
#創(chuàng)建基于向量的查詢(xún)引擎
query_engine = vector_index.as_query_engine(similarity_top_k=5)
#創(chuàng)建摘要索引與對(duì)應(yīng)的查詢(xún)引擎
summary_index = SummaryIndex(nodes)
summary_engine = summary_index.as_query_engine(response_mode="tree_summarize")
#將RAG引擎轉(zhuǎn)化為兩個(gè)tool
query_tool = QueryEngineTool.from_defaults(query_engine=query_engine,name=f'query_tool',description=f'Use if you want to query details about {name}')
summary_tool = QueryEngineTool.from_defaults(query_engine=summary_engine,name=f'summary_tool',description=f'Use ONLY IF you want to get a holistic summary of the documents. DO NOT USE if you want to query some details about {name}.')
#創(chuàng)建一個(gè)tool agent
tool_agent = **ReActAgent**.from_tools(**[query_tool,summary_tool]**,verbose=True,
system_prompt=f"""
You are a specialized agent designed to answer queries about {name}.You must ALWAYS use at least one of the tools provided when answering a question; DO NOT rely on prior knowledge. DO NOT fabricate answer.
""")
return tool_agent
這部分代碼主要目的就是把兩個(gè)查詢(xún)的RAG引擎包裝成工具(一個(gè)是query_tool,用于回答事實(shí)性問(wèn)題;一個(gè)是summary_tool用于回答總結(jié)性問(wèn)題,當(dāng)然你還可以構(gòu)建更多類(lèi)型的引擎),最后構(gòu)建一個(gè)ReAct思考范式的AI Agent,并把構(gòu)建的RAG tools插入。
如果你了解LlamaIndex,可能會(huì)使用路由RouteQueryEngine來(lái)代替這里的Agent,實(shí)現(xiàn)接近的功能。但是要注意,Router與Agent是有區(qū)別的,路由僅僅是起到一個(gè)“選擇”工具與“轉(zhuǎn)發(fā)”的作用,并不會(huì)做多次迭代;而Agent則會(huì)觀察工具返回的結(jié)果,且有可能會(huì)使用多個(gè)工具通過(guò)迭代來(lái)完成任務(wù)。
【批量創(chuàng)建Tool Agent】
有了上面的函數(shù)后,就可以批量創(chuàng)建好這些文檔的Tool Agent。這里把每一個(gè)文檔名字和對(duì)應(yīng)的Agent保存在一個(gè)dict中:
#創(chuàng)建不同文檔的agent
print('===============================================\n')
print('Creating tool agents for different documents...\n')
tool_agents_dict = {}
for name, file in zip(names, files):
tool_agent = create_tool_agent(file, name)
tool_agents_dict[name] = tool_agent
【創(chuàng)建Top Agent】
最后,我們需要?jiǎng)?chuàng)建一個(gè)頂層的Top Agent,這個(gè)Agent的作用是接收客戶(hù)的請(qǐng)求問(wèn)題,然后規(guī)劃這個(gè)問(wèn)題的查詢(xún)計(jì)劃,并使用工具來(lái)完成,而這里的工具就是上面創(chuàng)建好的多個(gè)Tool Agent:
#首先將Tool Agent進(jìn)行“工具化”
print('===============================================\n')
print('Creating tools from tool agents...\n')
all_tools = []
for name in names:
agent_tool = QueryEngineTool.from_defaults(
#注意,Agent本身也是一種Query Engine,所以直接轉(zhuǎn)為tool
query_engine=tool_agents_dict[name],
#這個(gè)工具的名字
name=f"tool_{name.replace("-", "")}",
#描述這個(gè)工具的作用和使用方法
description=f"Use this tool if you want to answer any questions about {name}."
)
all_tools.append(agent_tool)
#創(chuàng)建Top Agent
print('Creating top agent...\n')
top_agent = **OpenAIAgent.**from_tools(**tools=all_tools**,verbose=True,system_prompt="""You are an agent designed to answer queries over a set of given papers.Please always use the tools provided to answer a question.Do not rely on prior knowledge.DO NOT fabricate answer""" )
注意這里我們創(chuàng)建的Top Agent使用了OpenAIAgent,而不是ReActAgent,這也展示了這種架構(gòu)的靈活性:不同Agent可以按需使用不同的推理范式。
【測(cè)試】
現(xiàn)在來(lái)簡(jiǎn)單測(cè)試這個(gè)Top Agent,并觀察其執(zhí)行的過(guò)程:
top_agent.chat_repl()
輸入一個(gè)問(wèn)題:Please introduce Retrieval Evaluator in C-RAG pattern?
注意觀察這里紅線與綠色部分內(nèi)容,可以看出Agent的“思考”過(guò)程:
1.在TopAgent這一層,由于我們使用了OpenAIAgent,其是通過(guò)OpenAI的function calling來(lái)實(shí)現(xiàn),因此這里顯示LLM要求進(jìn)行函數(shù)調(diào)用,需要調(diào)用tool_crag,輸入?yún)?shù)為"Retrieval Evaluator in C-RAG pattern"。而這里的函數(shù)名tool_crag,也就是后端Tool Agent的名稱(chēng)。
2.然后來(lái)到Tool Agent層,Tool Agent收到請(qǐng)求后,通過(guò)ReAct范式的思考過(guò)程,決定需要調(diào)用query_tool工具,也就是通過(guò)向量索引進(jìn)行響應(yīng)的RAG引擎。在調(diào)用這個(gè)引擎后,獲得了返回內(nèi)容(observation的內(nèi)容)。收到返回后Tool Agent通過(guò)觀察與推理,認(rèn)為可以回答這個(gè)問(wèn)題,因此Tool Agent運(yùn)行結(jié)束,并返回結(jié)果給Top Agent
3.Top Agent收到函數(shù)調(diào)用的結(jié)果后,認(rèn)為無(wú)需再次進(jìn)行其他函數(shù)調(diào)用,因此直接輸出了結(jié)果,整個(gè)迭代過(guò)程結(jié)束。
當(dāng)然,你也可以自行測(cè)試更復(fù)雜的文檔任務(wù),比如:要求對(duì)比兩個(gè)文檔中某個(gè)知識(shí)點(diǎn)的區(qū)別等。
03
HOT SUMMER
進(jìn)一步優(yōu)化Agentic RAG
上面我們只用了三個(gè)文檔,構(gòu)建了針對(duì)他們的Tool Agent。那么如果這里的文檔數(shù)量是幾十或者幾百,過(guò)多的Tool Agent作為T(mén)ools塞給Top Agent進(jìn)行推理選擇時(shí)會(huì)帶來(lái)一些問(wèn)題:
LLM產(chǎn)生困惑并推理錯(cuò)誤的概率會(huì)提高
過(guò)多的Tools信息導(dǎo)致上下文過(guò)大,成本與延遲增加
一種可行的方法是:**利用RAG的思想對(duì)Tools進(jìn)行檢索,即只把本次輸入問(wèn)題語(yǔ)義相關(guān)的Tools(即這里的多個(gè)ToolAgent)交給Top Agent使用。**這里借助LlamaIndex中的Object Index來(lái)實(shí)現(xiàn):Object Index可以對(duì)任意Python對(duì)象構(gòu)建向量化的索引,并通過(guò)輸入問(wèn)題來(lái)檢索出相關(guān)的Objects。
現(xiàn)在可以對(duì)上面的代碼做簡(jiǎn)單的改造,給Top Agent在推理時(shí)增加tools檢索功能,從而能夠縮小tools選擇的范圍。只需要在創(chuàng)建Top Agent之前針對(duì)tools創(chuàng)建一個(gè)Object Index的檢索器用來(lái)根據(jù)輸入問(wèn)題檢索相關(guān)的tools:
#創(chuàng)建工具檢索器
print('===============================================\n')
print('Creating tool retrieve index...\n')
obj_index = ObjectIndex.from_objects(all_tools,index_cls=VectorStoreIndex,)
tool_retriever = obj_index.as_retriever(similarity_top_k=5,verbose=True)
然后將創(chuàng)建Top Agent的代碼做簡(jiǎn)單的修改,不再傳入all_tools,而是傳入tools檢索器:
......
top_agent = OpenAIAgent.from_tools(**tool_retriever=tool_retriever,** verbose=True,
system_prompt="""You are an agent designed to answer queries over a set of given papers.Please always use the tools provided to answer a question.Do not rely on prior knowledge.""")
.......
現(xiàn)在如果你繼續(xù)測(cè)試這個(gè)Agent,會(huì)發(fā)現(xiàn)仍然可以達(dá)到相同的效果。當(dāng)然,如果你需要驗(yàn)證這里檢索出來(lái)的tools正確性,可以直接對(duì)tool_retriever調(diào)用檢索方法來(lái)觀察(輸入相同的自然語(yǔ)言問(wèn)題)輸出的tools信息:
tools_needed = tool_retriever.retrieve("What is the Adaptive retrieval in the c-RAG?")
print('Tools needed to answer the question:')
for tool in tools_needed:
print(tool.metadata.name)
04
HOT SUMMER
Agentic RAG總結(jié)
相對(duì)于更適用于對(duì)幾個(gè)文檔進(jìn)行簡(jiǎn)單查詢(xún)的經(jīng)典RAG應(yīng)用,Agentic RAG的方法通過(guò)更具有自主能力的AI Agent來(lái)對(duì)其進(jìn)行增強(qiáng),具備了極大的靈活性與擴(kuò)展性,幾乎可以完成任意基于知識(shí)的復(fù)雜任務(wù):
基于RAG之上的Tool Agent將不再局限于簡(jiǎn)單的回答事實(shí)性的問(wèn)題,通過(guò)擴(kuò)展更多的后端RAG引擎,可以完成更多的知識(shí)型任務(wù)。比如:整理、摘要生成、數(shù)據(jù)分析、甚至借助API訪問(wèn)外部系統(tǒng)等
Top Agent管理與協(xié)調(diào)下的多個(gè)Tool Agent可以通過(guò)協(xié)作完成聯(lián)合型的任務(wù)。 比如對(duì)兩個(gè)不同文檔中的知識(shí)做對(duì)比與匯總,這也是經(jīng)典問(wèn)答型的RAG無(wú)法完成的任務(wù)類(lèi)型。
感謝你們的閱讀和喜歡,我收藏了很多技術(shù)干貨,可以共享給喜歡我文章的朋友們,如果你肯花時(shí)間沉下心去學(xué)習(xí),它們一定能幫到你。
因?yàn)檫@個(gè)行業(yè)不同于其他行業(yè),知識(shí)體系實(shí)在是過(guò)于龐大,知識(shí)更新也非???。作為一個(gè)普通人,無(wú)法全部學(xué)完,所以我們?cè)谔嵘夹g(shù)的時(shí)候,首先需要明確一個(gè)目標(biāo),然后制定好完整的計(jì)劃,同時(shí)找到好的學(xué)習(xí)方法,這樣才能更快的提升自己。
因篇幅問(wèn)題不能全部顯示,請(qǐng)點(diǎn)此查看更多更全內(nèi)容
Copyright ? 2019- 91gzw.com 版權(quán)所有 湘ICP備2023023988號(hào)-2
違法及侵權(quán)請(qǐng)聯(lián)系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市萬(wàn)商天勤律師事務(wù)所王興未律師提供法律服務(wù)