基于Agent的金融問答系統(tǒng):Agent框架的構建 原創(chuàng)
前言
上一章【項目實戰(zhàn)】基于Agent的金融問答系統(tǒng):RAG檢索模塊初建成,我們已經(jīng)完成了RAG的基本流程,本章我將梳理Agent的運行框架以及介紹如何將RAG數(shù)據(jù)和SQL查詢數(shù)據(jù)進行整合,實現(xiàn)金融問題的自主回答。
核心問題
在進行Agent框架介紹之前,我們需要探討一個核心問題,即:??用戶輸入一個問題后,如何實現(xiàn)用戶問題的自動查詢(是SQL查詢還是RAG檢索)?? 。
意圖識別方案
關于這個問題,我們曾有一個方案如下:

- 對于用戶輸入的問題,是需要查詢SQL,還是需要去RAG檢索,我們通過一個意圖識別模塊來實現(xiàn);
- 意圖識別模塊會接受用戶的問題,然后將用戶的問題交給大模型去分辨;
- 大模型我們提前做了sft微調(diào)(few-shot少樣本訓練也可以達到同樣效果),讓大模型知道什么樣的問題屬于SQL查詢問題,什么樣的問題屬于RAG檢索問題,例如:
# 準備few-shot樣例
examples =[
{
"inn":"我想知道東方阿爾法優(yōu)勢產(chǎn)業(yè)混合C基金,在2021年年度報告中,前10大重倉股中,有多少只股票在報告期內(nèi)取得正收益。",
"out":"rag_question***我想知道東方阿爾法優(yōu)勢產(chǎn)業(yè)混合C基金,在2021年年度報告中,前10大重倉股中,有多少只股票在報告期內(nèi)取得正收益。"
},
{
"inn":"森赫電梯股份有限公司產(chǎn)品生產(chǎn)材料是什么?",
"out":"rag_question***森赫電梯股份有限公司產(chǎn)品生產(chǎn)材料是什么?"
},
{
"inn":"20210930日,一級行業(yè)為機械的股票的成交金額合計是多少?取整。",
"out":"agent_question***20210930日,一級行業(yè)為機械的股票的成交金額合計是多少?取整。"
},
...
]- 意圖識別結果之后,在代碼中通過if...else對識別結果進行不同類別的后續(xù)處理,如下:
if intent =="rag_question":
# 如果是RAG相關的問題
result = self.rag.get_result(question=question)
return result
elif intent =="agent_question":
# 如果是Agent相關的問題
result, result_list = self.agent.get_result(input=question)
return result
else:
# 其他類問題
result = self.chat.invoke(input=question).content
return result意圖識別方案的問題
這一方案有個核心問題:
- 如果意圖識別錯了,那么后續(xù)的處理就失效了,答案一定會錯;
- 我們固然可以給if...else...增加修正策略,比如:rag檢索不到那就去sql里再查一下....但是如果我們需求很復雜呢,這個策略編寫和維護成本也是指數(shù)級上升;
- 最為重要的一點是:這種方式我覺得一點都不智能!
這種方式還是傳統(tǒng)開發(fā)的思維:由開發(fā)者來清晰地控制著程序的每一步驟,大模型在其中只是扮演一個輔助工具角色(例如:做個問題分類)
但是,在我首次接觸Agent時,Agent的自主思考和自主行動給我留下了深刻的印象,所以我決定換一種方式:使用Agent來進行用戶問題的自主判斷、自主解決。
Agent框架
在【課程總結】day30:大模型之Agent的初步了解中,我們曾初步了解Agent框架如下:

由上圖可知,如果我們給Agent賦予相應的工具,那么Agent依托于大模型的思考力,可以自主開展相應的planning以及action。因此,我們首要解決如何給Agent賦予相應的工具。
1、賦予Agent工具
因為我們的需求是查詢結構化的SQL數(shù)據(jù)庫和非結構化的RAG數(shù)據(jù),所以我們需要給Agent賦予兩個工具,具體如下:
1.1、創(chuàng)建Agent的管理類
代碼文件及目錄:??app/finance_bot_ex.py??
import logging
import datetime
from langgraph.prebuilt import create_react_agent
from langchain.tools.retriever import create_retriever_tool
from langchain_community.utilities importSQLDatabase
from langchain_community.agent_toolkits importSQLDatabaseToolkit
from rag.rag importRagManager
import settings
classFinanceBotEx:
def__init__(self, llm=settings.llm, chat=settings.chat, embed=settings.embed):
self.llm = llm
self.chat = chat
self.embed = embed
self.tools =[]
self.rag =RagManager(llm=llm, embed=embed)1.2、創(chuàng)建數(shù)據(jù)庫SQL工具
為FinanceBotEx類添加
def init_sql_tool(self, path):
# 連接數(shù)據(jù)庫
db = SQLDatabase.from_uri(f"sqlite:///{path}")
toolkit = SQLDatabaseToolkit(db=db, llm=self.llm)
sql_tools = toolkit.get_tools() # 工具
return sql_tools說明:
- 上述函數(shù)中path用來傳入SQlite數(shù)據(jù)庫的路徑
- 使用langchain封裝的 SQLDatabase 連接數(shù)據(jù)庫
- 使用langchain封裝的 SQLDatabaseToolkit 得到SQL相關的工具,用以后續(xù)提供給Agent
1.3、創(chuàng)建RAG檢索工具
為FinanceBotEx類添加
def init_rag_tools(self):
# 給大模型 RAG 檢索器工具
retriever = self.rag.get_retriever()
retriever_tool = create_retriever_tool(
retriever=retriever,
name="rag_search",
description="按照用戶的問題搜索相關的資料,對于招股書類的問題,you must use this tool!",
)
return retriever_tool說明:
- 該函數(shù)中的?
?self.rag?? 是我們封裝的RAG管理類 - 通過這個管理類的get_retriever()得到RAG的檢索器
- 借助langchain封裝的create_retriever_tool(),將檢索器創(chuàng)建為一個工具:
工具的??name??? 為??rag_search??
工具的??description?? 用于告訴大模型,這個工具能做什么用(這一點很重要)
2、告訴Agent的工作邏輯:Prompt
為FinanceBotEx類添加
def create_prompt():
system_prompt = """你是一位金融助手,可以幫助用戶查詢數(shù)據(jù)庫中的信息。
你要盡可能的回答用戶提出的問題,為了更好的回答問題,你可以使用工具進行多輪的嘗試。
# 關于retriever_tool工具的使用:
1、你需要結合對檢索出來的上下文進行回答問題。
2、如果你不知道答案,就說你不知道。請使用不超過三句話的簡潔回答。
# 關于sql類工具的使用:
## 工具使用規(guī)則
1、你需要根據(jù)用戶的問題,創(chuàng)建一個語法正確的SQLite查詢來運行,然后查看查詢的結果并返回答案。
2、除非用戶指定了他們希望獲得的特定數(shù)量的示例,否則總是將查詢限制為最多5個結果。
3、您可以按相關列對結果進行排序,以返回數(shù)據(jù)庫中最有趣的示例。
4、永遠不要查詢指定表的所有列以避免查詢性能問題,你只查詢給定問題的相關列即可。
5、你必須在執(zhí)行查詢之前仔細檢查查詢。如果執(zhí)行查詢時出現(xiàn)錯誤,請重新編寫查詢并重試。
6、請勿對數(shù)據(jù)庫進行任何DML語句(INSERT,UPDATE,DELETE,DROP等)。
## 工具使用過程
1、首先,你應該始終查看數(shù)據(jù)庫中的表,看看可以查詢什么,這一步驟很重要,注意不要跳過。
2、然后,你應該查詢最相關表的schema。
## 工具使用注意事項:
1、如果生成的SQL語句中,字段帶有英文括號(),請使用雙引號包裹起來,例如:收盤價(元) 雙引號包裹為 "收盤價(元)"。
2、如果查詢過程中SQL語句有語法錯誤,減少查詢量,總體查詢次數(shù)應控制在15次以內(nèi)。
# 關于你的思考和行動過程,請按照如下格式:
問題:你必須回答的輸入問題
思考:你應該總是考慮該怎么做
行動:你應該采取的行動,應該是以下工具之一:{tool_names}
行動輸入:行動的輸入
觀察:行動的結果
... (這個思考/行動/行動輸入/觀察可以重復N次)
思考: 我現(xiàn)在知道最終答案了
最終答案:原始輸入問題的最終答案
Begin!
"""
return system_prompt3、創(chuàng)建agent
在準備好相應的工具以及prompt之后,創(chuàng)建Agent并傳入提前準備好的工具和prompt。
為FinanceBotEx類添加
def init_agent(self):
# 初始化 RAG 工具
retriever_tool = self.init_rag_tools()
# 初始化 SQL 工具
sql_tools = self.init_sql_tool(settings.SQLDATABASE_URI)
# 創(chuàng)建系統(tǒng)Prompt提示語
system_prompt = self.create_prompt()
# 創(chuàng)建Agent
agent_executor = create_react_agent(
self.chat,
tools=[retriever_tool]+ sql_tools,
state_modifier=system_prompt
)
return agent_executor說明:
- ?
?settings.SQLDATABASE_URI?? 是一個本地SQLite文件的路徑,我將它單獨維護在app/settings.py中,方便配置的修改。 - 代碼文件:?
?app/settings.py????# 連接數(shù)據(jù)庫db文件的地址根據(jù)需要需要更換?
SQLDATABASE_URI = os.path.join(os.getcwd(), "app/dataset/dataset/博金杯比賽數(shù)據(jù).db")?
4、驅(qū)動agent運行
為FinanceBotEx類添加
def handle_query(self, example_query):
# 流式處理事件
events = self.agent_executor.stream(
{"messages":[("user", example_query)]},
stream_mode="values",
)
# 打印流式事件的消息
for event in events:
event["messages"][-1].pretty_print()
return event["messages"][-1].content測試效果
在test_framework.py中增加測試函數(shù)如下:
# 測試 FinanceBotEx 主流程
deftest_financebot_ex():
from finance_bot_ex importFinanceBotEx
llm, chat, embed = get_qwen_models()
financebot =FinanceBotEx(llm=llm, chat=chat, embed=embed)
example_query ="20210304日,一級行業(yè)為非銀金融的股票的成交量合計是多少?取整。"
financebot.handle_query(example_query)
if __name__ =="__main__":
# test_rag()
# test_import()
test_financebot_ex()運行結果:


通過日志可以看到,Agent收到問題后,先去SQL數(shù)據(jù)庫中查詢看有哪些表可能有這些數(shù)據(jù),通過返回的表信息自主實現(xiàn)對應的SQL語句,然后查詢結果后給出答案。
如果將上面的問題變成如下問題:
example_query = "根據(jù)聯(lián)化科技股份有限公司招股意見書,精細化工產(chǎn)品的通常利潤率是多少?"運行結果:

通過日志可以看到,Agent對于這個問題認為應該直接去RAG中搜索,所以直接調(diào)用工具rag_search。
內(nèi)容小結
- Agent的實現(xiàn)思想與傳統(tǒng)開發(fā)有著極大的不同:
傳統(tǒng)開發(fā)對于程序的每個步驟都需要清晰地控制
Agent的思想則是通過prompt告訴大模型的運行規(guī)則,然后由大模型自主思考和行動
- 使用Agent時需要幾個步驟:
- 創(chuàng)建相應的工具
- 創(chuàng)建對應的prompt
- 創(chuàng)建agent并賦予相應的工具和prompt
- 通過agent的stream來處理輸入的問題
?
本文轉載自公眾號一起AI技術 作者:Dongming

















