介绍
在这篇文章中,我将介绍我开发的一个多代理系统架构,该架构解决了两个不同问题:财务分析和消费分析。财务分析模块利用了一个已经构建的检索增强生成(RAG)系统,即检索增强生成系统,而消费分析则通过SQL查询处理结构化数据。
多代理系统简介这个多代理系统(MAS)旨在高效地处理金融和消费分析任务。
· 财务分析:利用RAG系统来分析财务报表、分析师推荐和市场趋势等相关信息。
· 消费分析:我们通过SQL来分析销售数据,比如销售表现,市场份额和销量指标。
基础知识架构:该架构包括一个监督代理,该代理将根据用户的查询需求调用这两个代理。让我们快速看一下工作流。
技术与工具:一些技术和工具- Azure认知服务:帮助进行金融分析所需的文档检索。
- LangChain框架:支持代理架构,实现RAG和SQL代理的无缝结合。
- SQL数据库:处理与销售及市场表现相关的复杂查询,支持分析消费行为。
导入库模块:
我们需要安装所需的软件包和库:
import os
import sqlite3
import pandas as pd
from langchain.agents import AgentType, Tool, initialize_agent
from langchain_core.callbacks.stdout import StdOutCallbackHandler
from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage, ToolMessage
from langchain.agents import ZeroShotAgent
from langchain_openai import AzureChatOpenAI
from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
from langgraph.prebuilt import create_react_agent
from langchain_community.utilities.sql_database import SQLDatabase
from sqlalchemy import create_engine
from sqlalchemy.pool import StaticPool
from langchain.agents import create_sql_agent, create_structured_chat_agent
from typing import List, Sequence, TypedDict, Annotated, Union, Literal
from langchain_core.messages import BaseMessage
from langchain_core.output_parsers.openai_functions import JsonOutputFunctionsParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import functools
import operator
from pydantic import BaseModel
搭建环境:
设置Azure凭证:在此处输入您的Azure凭证
# 设置Azure OpenAI的环境变量
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
os.environ["OPENAI_API_KEY"] = "..."
os.environ["OPENAI_API_VERSION"] = "..."
os.environ["OPENAI_API_TYPE"] = "..." # API类型
我有一个存储在Azure Blob存储中的csv文件,其中包含使用数据,我将把这当作数据库来使用,并用它来做我的SQL查询。要访问这些数据,你需要一个Azure SAS令牌。
DATASOURCE_SAS_TOKEN= “……………………………” # 数据源 SAS 令牌
定义大规模语言模型(LLM)
llm = AzureChatOpenAI(deployment_name= 'gpt-4–32k', temperature=0.0, max_tokens=1000) # llm:大语言模型,部署名为 'gpt-4–32k' 的 AzureChatOpenAI 实例,设置温度为 0.0,最大 token 数为 1000
RAG系统(一种系统):
我不会为你创建任何RAG系统,但我在这里使用了我自己构建的现有RAG系统。
RAG系统,我现在要用:get_answer_search
文本转换为SQL
虽然我还没为你创建RAG系统,但我将教你如何用Langgraph代理把文本转成SQL。
这是我以csv形式存放的消费记录的远程文件路径。
你的blob存储路径的远程文件路径
remote_file_path='https://blob.core.windows.net/.......csv'
来自 Pandas 库的下面这个函数读取一个 CSV(逗号分隔) 文件,并将其加载到 Pandas 的 DataFrame 中。之后,可以使用这个 DataFrame 在 Python 中进行数据处理和分析。
df = pd.read_csv(remote_file_path + DATASOURCE_SAS_TOKEN) # 从远程文件路径读取CSV文件
使用 SQLite 来连接 SQLite 数据库。在这种情况下来说,它连接到名为 consumption.db 的数据库文件,如果该文件不存在,SQLite 会在当前工作目录中创建它。
conn = sqlite3.connect('consumption.db', check_same_thread=False) # 连接到消费数据库
下面的命令将 DataFrame 转换成了 SQL 数据库中的一个表,从而使您可以对其执行 SQL 查询的这些数据。
将df数据框的数据写入名为'consumption'的数据库表中,忽略索引,并替换已存在的表。
以下指令建立了与位于 consumption.db
文件的 SQLite 数据库的连接。具体来说,此连接可用于执行 SQL 查询,并在你的 LangChain 系统中以编程方式操作该数据库。
# 初始化SQL数据库连接
db = SQLDatabase.from_uri("sqlite:///consumption.db")
# 创建SQL数据库工具包对象,传入数据库实例和llm变量
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
The toolkit = SQLDatabaseToolkit(db=db, llm=llm)
命令创建了一个接口,这样可以让语言模型无缝地进行交互,将自然语言问题转换成SQL查询,并以易于用户理解的格式返回结果。
此命令创建了一个SQLDatabaseToolkit,该工具集成了两个部分。
- 一个 SQL 数据库(db),用于存储结构化的数据(如销售、市场份额等)。
- 一个 语言模型(llm),它用于处理用户的自然语言查询并与 SQL 数据库互动。
它的工作方式如下:
· 自然语言到SQL转换:大型语言模型可以将自然语言问题(比如,“2023年的销售额是多少?”)转换为结构化的SQL查询,数据库可以理解并执行。
· 执行 SQL 查询:该工具包可以在连接的数据库(db)上执行 SQL 查询。
· 自然语言回复:在查询数据库之后,工具将SQL查询结果转化为易于理解的自然语言,让用户更容易理解结果。
构建SQL代理服务器:这里是如何创建一个SQL代理的步骤。我在这里演示是为了帮助你更好地理解SQL代理是如何工作的。
该命令创建了一个能够处理自然语言输入、查询SQL数据库并返回可读答案的代理程序。它集成了SQLDatabaseToolkit的功能(如查询能力),并通过增加记忆和上下文的方式与state_modifier交互。这使得SQL代理能够更有效地处理查询,并返回准确的结果。
sql代理执行器 = Create_react_agent(llm, tools=工具包.get_tools(), state_modifier=具有记忆功能的MSSQL代理后缀)
逐个击破:
创建React代理函数():
· 这是由Langgraph提供的一项功能,用于创建一个React代理。React代理是一种专门接收输入、思考应该使用哪些工具、并逐步执行操作直到找到答案的代理。
· 在这种情况下,它创建了一个可以理解和处理自然语言、生成SQL查询,并能与外部工具(如SQL数据库)进行交互的代理。
# 获取工具: tools = toolkit.get_tools():
工具参数为代理提供了一个它可以使用来完成任务的工具列表。在这种情况下,toolkit.get_tools()
从SQLDatabaseToolkit中获取工具列表,这可能包括以下几种方法:
· 查询一下 SQL 数据库。
· 运行 SQL 语句。
· 从数据库捞取结果并整理一下。
state_modifier=MSSQL_AGENT_SUFFIX_WITH_MEMORY: (状态修饰符=MSSQL代理后缀带内存状态修饰符)
状态参数通过添加额外信息来调整代理与SQL数据库交互时的行为模式。
MSSQL_AGENT_SUFFIX_WITH_MEMORY: 这是要给代理的提示信息。(可能是一个技术术语,具体含义需参考上下文)以下是下面的提示指令供您参考:
MSSQL_AGENT_SUFFIX_WITH_MEMORY= """在生成上述查询的SQL时,请注意以下几点:
- 一般说明:
- 不要在SQL中使用LIMIT语句。
- 四舍五入到小数点后两位。
- 避免使用涉及查询内部除法的复杂SQL查询。
- 一步一步地执行操作。
- 注意查询中提到的所有条件,不要自己猜测条件。
- 对于有关份额或市场份额的问题,除非另有说明,否则使用column="Amount"。
- YTD 或 ytd 表示年初到现在的数据。
- 除非特别说明,否则不要假设年份是今年的。除非特别指示使用特定年份,否则使用所有年份的数据。”””
正在运行查询,
query="市场上三大纸尿裤品牌是什么?"
events = sql_agent_executor.stream(
{"messages": [HumanMessage(content=query)]},
stream_mode="values",
)
for event in events:
event["messages"][-1].pretty_print()
message_content= event["messages"][-1].content
if "答案:" in message_content:
final_answer=message_content.split("答案:",1)[1].strip()
创建多代理系统:
步骤 1:集成 RAG 工具
多代理模型以字典的形式处理问题。然而,我们的现有RAG系统将查询作为字符串处理,这需要一点调整。我们需要将多代理模型生成的字典查询传递给一个函数,这个函数可以重新创建一个RAG系统能够理解的用户查询。
下面是如何做到的。它使用了一个函数来实现这个过程,该函数名为getAnswerSearchWrapper,接收一个查询,处理这个查询,并使用RAG系统来获取相关的文档。
def get_answer_search_wrapper(input_data: dict) -> str:
# 记录输入数据以供调试
print(f"输入数据: {input_data}")
# 从输入数据中提取参数和标签
tags = input_data.get("tags", []) # 参数列表,默认为空列表
args = input_data.get("参数", []) # 标签列表,默认为空列表
# 通过连接标签和参数来构建查询字符串
query = " ".join(tags) if tags else "" # 如果有标签,则将它们连接成一个字符串
query += " " + " ".join(args) if args else "" # 如果有额外参数,则将它们添加到查询中
# 打印生成的查询
print(f"生成的查询: {query}")
# 检查查询是否为空或无效
if not query.strip(): # 如果查询只是空格或为空,则返回错误
print("空查询")
return "错误:查询为空"
# 调用RAG系统根据构建的查询获取答案
return get_answer_search(query.strip()) # 用修剪后的查询来搜索RAG系统
为什么 get_answer_search_wrapper 函数很重要?
- 用户输入解析与灵活性:
- 该功能通过支持如 标签 和 参数 等不同类型的用户输入,实现了灵活处理各种用户输入。
- 它确保用户查询在传递给检索系统之前被正确格式化,这对于确保查询的准确性和有意义性至关重要。
- 通过处理
标签
和参数
,允许系统提取用户提供的不同信息片段,并生成更完整且准确的查询。
2、错误处理机制:
- 该函数检查是否构建了有效的查询。如果没有提供有效的查询输入,它将返回错误消息,例如 “错误:未提供查询”,确保系统不会处理无效或不完整的查询。
- 这对于确保系统健壮性并避免在无查询时进行不必要的处理至关重要。
3. 连接RAG系统:
- 在处理输入并确保其有效之后,该函数调用
get_answer_search
以检索相关文档并使用RAG系统生成答案。 - 这将查询构建过程与实际搜索过程分离,使得代码更加简洁和模块化。系统可以扩展或修改,而无需改动核心搜索功能。
接下来的步骤是定义将用于多代理系统的RAG组件。
该工具将负责处理涉及非结构化财务数据(例如分析师报告和收益电话)的查询。
rag_tool = 工具(
name="名称",
func=get_answer_search_wrapper,
description=""
)
工具列表 = [rag_tool]
步骤二:初始化智能体
下面的函数用于调用一个代理,获取其回复,并返回结构化的结果,该结果可以传递回系统或下一个代理来使用。它确保捕获代理的输出内容,格式化并适当地标注。
def agent_node(state, agent, name):
# 定义代理节点函数,处理状态并返回消息列表
result = agent.invoke(state)
return {"messages": [HumanMessage(content=result["messages"][-1].content, name=name)]}
我们现在将创建一个指令,该指令将提供给监督代理。该指令说明了监督代理应该如何选择下一个代理(投资分析师或消费代理人)。将金融分析师改名为“Investment_analyst”,将消费代理人改名为“SQL_Agent”。
prompt_message="""您是一位主管,负责管理以下工人的对话:{members}。根据以下用户请求,回应接下来应该由哪个工人来行动。每个工人都会执行一个任务,并回复他们的结果和状态。如果对话结束,请回复 'FINISH'。
- **Investment_analyst**:适用于以下任务:
- 分析财务报告和股票研究报告。
- 比较特定公司或行业的收入增长、利润或类似财务指标。
- 解答有关公司业绩及战略方向的问题。
- 分析行业环境和竞争态势。
- 分析分析师评级、投资论点和分析师推荐。
- 分析分析师在会议纪要中的观点。
- 需要从非结构化数据、趋势或战略分析中获取见解的一般分析任务。
- **Sql_agent**:适用于以下任务:
- 计算市场份额。
- 回答比较销量或数量(不包括收入增长或利润比较)的问题。
- 比较特定品牌、类别、子类别、市场、部门或制造商的销量或数量表现。
- 处理需要从结构化的SQL数据库中进行特定数据分析、计算或检索的查询,尤其是涉及销量或数量指标时。
决策标准:
- 如果查询提到比较“收入增长”或“利润”等财务指标,请转给 **Investment_analyst**。
- 如果查询涉及与销售、数量或市场份额相关的结构性数据点,并需要计算或数据库查询,请转给 **Sql_agent**。
- 在两者都相关的查询中,优先将涉及财务指标的查询转给 **Investment_analyst**,涉及销售、数量或市场份额计算的查询优先交给 **Sql_agent**。
示例决策流程:
- 查询:“比较宝洁(PG)和Coalge-Pamolive过去两年的收入增长。” -> **Investment_analyst**
- 查询:“哪些是消费品领域的前三大品牌?” -> **Sql_agent**
- 查询:“分析最新的财务趋势。” -> **Investment_analyst**
"""
让我们定义多代理系统中的代理列表以及定义的提示消息,该提示消息遵循用户的指令。提示消息包含将提供给监督者的指令。这些指令告诉监督者如何根据查询的性质来决定使用投资分析师还是SQL代理。
members = ["Investment_analyst", "Sql_agent"]
system_prompt = prompt_message
# 我们团队的主管是一个LLM节点,它只是选择下一个要处理的代理,并决定何时完成任务。
options = ["完成"] + members
选项列表 = ["FINISH"] + members 这行代码定义了监督代理可以选择的选项列表。
**"完成"**
:这表示任务已经完成,不再需要进一步操作了。
以下代码定义了一个数据模型来结构化监督代理的响应,确保其决策类型安全,并限制在特定选项之内。具体来说,代码确保了监督代理所做决策是类型安全的,并且只能选择特定的代理。
RouteResponseNextType = Union[Literal["FINISH"], Literal["投资分析师"], Literal["SQL代理"]]
class RouteResponse(BaseModel):
next: RouteResponseNextType
以下代码定义了我们多代理系统中监督代理的提示模版。它创建了一种结构来帮助监督代理决定下一个调用哪个代理(投资分析员或SQL代理),或是否为结束(完成)(完成)。
prompt = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
MessagesPlaceholder(variable_name="messages"),
(
"system",
"根据上面的对话,接下来应该由谁行动?或者我们应该收工吗?从下面选项中选择一个:{options}",
),
]
).partial(options=str(options), members=", ".join(members))
第三步:
我们现在来建立监控代理:
llm = llm
def supervisor_agent(state):
supervisor_chain = (
prompt
| llm.with_structured_output(RouteResponse)
)
return supervisor_chain.invoke(state)
class AgentState(TypedDict):
# 注解表明新消息将始终添加到当前状态中
messages: 带有operator.add注解的BaseMessage序列
# 下一步的路由目标
next: str
我们现在来初始化投资分析师(RAG)助手和SQL工具来处理各种查询。
rag_agent = create_react_agent(llm, tools=tools, state_modifier="应当提供RAG搜索。")
rag_node = functools.partial(agent_node, agent=rag_agent, name="Investment_analyst")
sql_agent = create_react_agent(llm, tools=toolkit.get_tools(), state_modifier=MSSQL_AGENT_SUFFIX_WITH_MEMORY)
sql_node = functools.partial(agent_node, agent=sql_agent, name="Sql_agent")
第四步
主管代理与工作流程
根据输入,监督代理(Supervisor Agent)将查询分配到适当的代理。
workflow = StateGraph(AgentState)
workflow.add_node("投资分析师", "投资分析师节点")
workflow.add_node("SQL代理", "sql_node")
workflow.add_node("主管", "主管代理")
for member in members:
# 我们希望所有员工在完成任务后必须向主管“汇报工作”
workflow.add_edge(member, "主管")
# 主管设置“next”字段的值
# 这将指向另一个节点或者完成任务
conditional_map = {k: k for k in members}
conditional_map["FINISH"] = END
workflow.add_conditional_edges("主管", lambda x: x["next"], conditional_map)
# 最后,添加入口点
workflow.add_edge(START, "主管")
graph = workflow.compile()
运行查询来吧
工作流编译完成后,你可以输入一个查询请求,让系统决定由哪个代理人来处理。
query = "宝洁公司的分析师们怎么看?"
input_data = {"query": query}
for s in graph.stream(
{
"messages": [
HumanMessage(content=input_data['query'])
]
}
):
if "__end__" not in s:
print(s)
print("----")
RAG:
最近关于宝洁公司(P&G)的报告大都挺乐观的。
1. 摩根大通北美股票研究(报告日期:2024–05–19):
— 投资观点:该报告对宝洁公司的表现和前景持乐观态度,认为宝洁有望达到或略微超出摩根大通银行和市场共识的预期。报告还指出,在美国和欧洲等重点市场的消费趋势保持强劲,没有出现放缓的迹象。
— 引用:“宝洁公司一直在有机销售增长中平衡销量和价格。”
— 评级:报告将宝洁公司的评级定为“增持”,表明他们预计该股票的表现将优于研究分析师覆盖的股票的平均总回报。
- 摩根大通的北美股权研究(报告日期:2024–05–28):
— 投资观点:本报告指出,尼尔森IQ数据显示某些食品生产商的情况令人鼓舞,但对宝洁等公司支持较少。不过,报告没有详细说明宝洁的情况。
请注意以下几点:这些是摘要,具体细节和见解可能会有所不同。如需更全面的理解,请参阅摩根大通北美股票研究团队的详细报告。
正在执行SQL查询
query = "在消费者市场中排名前两大品牌是什么?"
input_data = {"query": query}
在 graph.stream() 中,对于 s:
if "messages": [
HumanMessage(content=input_data['query'])
]
):
如果 s 中不包含 '__end__':
print(s)
print("----")
SQL结果:
问题:消费者市场里最火的两个品牌是哪两个?
答:
排名前两位的品牌是帮宝适和私人品牌。
共同学习,写下你的评论
评论加载中...
作者其他优质文章