This commit is contained in:
2025-03-12 16:13:31 +08:00
parent c482bbaeb4
commit b7d59835ce
6 changed files with 209 additions and 52 deletions
+10
View File
@@ -0,0 +1,10 @@
from langchain_ollama import ChatOllama
LLM = ChatOllama(model='qwen2.5:14b', api_key="1145141919810", base_url='http://192.168.195.158:11434')
# LLM = ChatOpenAI(model="gpt-4o", openai_api_key='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')
tavily_key = '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'
gaode_key = '00fd082df2414f75c6efb64896819451'
+61 -19
View File
@@ -1,3 +1,4 @@
import json
import os import os
import time import time
import operator import operator
@@ -11,7 +12,12 @@ from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
llm = ChatOpenAI(model="gpt-4o", temperature=0.1, openai_api_key='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') from src.multi_agent.util.tools import formatted_json
from src.multi_agent.util.web_search import do_search
from src.multi_agent.config import LLM
from src.multi_agent.weather_agent import weather_chain
llm = LLM
MEMBERS = {'天气查询模块': '输入一个城市和日期,查询当日的天气状况', MEMBERS = {'天气查询模块': '输入一个城市和日期,查询当日的天气状况',
'网络查询模块': '在网上搜索指定的内容', '网络查询模块': '在网上搜索指定的内容',
@@ -41,31 +47,53 @@ def planner(state: AgentState):
]) ])
planner_chain = (planner_prompt | llm) planner_chain = (planner_prompt | llm)
result_dict = {'plan': '为了完成这个任务,我们需要按照以下步骤进行:\n\n1. **网络查询模块**\n - 任务:查询2024年中国GDP最高的城市。\n - 行动:在网上搜索2024年中国GDP最高的城市的信息。\n\n2. **天气查询模块**\n - 任务:查询该城市今天的天气状况。\n - 行动:使用天气查询模块,输入城市名称和当前日期,获取该城市的天气信息。\n\n3. **诗歌创作模块**\n - 任务:根据查询到的城市名称和天气状况,创作一首诗。\n - 行动:利用诗歌创作模块,结合城市的特点和天气状况,创作一首诗。\n\n请各模块按照上述计划执行任务,并在完成后汇总信息。'} llm_resp = planner_chain.invoke({'query': [('user', user_query)]})
return {'plan': llm_resp.content}
return result_dict
# llm_resp = planner_chain.invoke({'query': [('user', user_query)]})
# return {'plan': llm_resp.content}
def decision_node(state: AgentState): def decision_node(state: AgentState):
class NextStep(BaseModel):
next_agent: str = Field(description='下一步应该交给哪个模块来完成')
action: str = Field(description='下一步应该执行的行动计划是什么')
members = state.get('members', {}) members = state.get('members', {})
member_keys = list(members.keys()) member_keys = list(members.keys())
if isinstance(llm, ChatOpenAI):
class NextStep(BaseModel):
next_agent: str = Field(description='下一步应该交给哪个模块来完成')
action: str = Field(description='下一步应该执行的行动计划是什么。如果已经执行所有的任务,则填入执行结果')
decision_prompt = ChatPromptTemplate.from_messages([ decision_prompt = ChatPromptTemplate.from_messages([
('system', f'你负责针对一个给定的任务计划,以及当前的任务执行状态,制定下一步应该由谁来执行什么内容。可以在这些模块中进行选择:{", ".join(member_keys)}'), ('system', f'你负责针对一个给定的任务计划,以及当前的任务执行状态,制定下一步应该由谁来执行什么内容。'
f'可以在这些模块中进行选择:{", ".join(member_keys)}'),
('user', f'当前的计划是:'), ('user', f'当前的计划是:'),
MessagesPlaceholder(variable_name="plan"), MessagesPlaceholder(variable_name="plan"),
('user', '以下是各个模块之间的消息记录'), ('user', '以下是各个模块之间的消息记录'),
MessagesPlaceholder(variable_name="messages") MessagesPlaceholder(variable_name="messages")
]) ])
decision_chain = (decision_prompt | llm.with_structured_output(NextStep)) decision_chain = (decision_prompt | llm.with_structured_output(NextStep))
llm_resp = decision_chain.invoke({'plan': [('user', state.get('plan', ''))], 'messages': state.get('messages', [])}) llm_resp = decision_chain.invoke({'plan': [('user', state.get('plan', ''))],
'messages': state.get('messages', [])})
next_agent = llm_resp.next_agent next_agent = llm_resp.next_agent
action = llm_resp.action action = llm_resp.action
else:
decision_prompt = ChatPromptTemplate.from_messages([
('system',
f'你负责针对一个给定的任务计划,以及当前的任务执行状态,制定下一步应该由谁来执行什么内容。'
f'可以在这些模块中进行选择:{", ".join(member_keys)}。你的输出必须只能是一个json字符串,'
f'必须包含next_agent和action两个字段。其中next_agent是下一步应该交给哪个模块来完成,'
f'action是下一步应该执行的行动计划是什么,如果已经执行完成所有的任务,则在action中填入执行结果'
f'你只需要输出这个json即可,不需要给出任何解释'),
('user', f'当前的计划是:'),
MessagesPlaceholder(variable_name="plan"),
('user', '以下是各个模块之间的消息记录'),
MessagesPlaceholder(variable_name="messages")
])
decision_chain = (decision_prompt | llm)
llm_resp = decision_chain.invoke(
{'plan': [('user', state.get('plan', ''))], 'messages': state.get('messages', [])})
llm_resp_str = formatted_json(llm_resp.content)
resp_dict = json.loads(llm_resp_str)
next_agent = resp_dict.get('next_agent')
action = resp_dict.get('action')
if not (next_agent and action):
raise ValueError('模型返回异常')
return {'messages': [HumanMessage(content=f'接下来由{next_agent}来执行{action}')], 'next_agent': next_agent, 'next_plan': action} return {'messages': [HumanMessage(content=f'接下来由{next_agent}来执行{action}')], 'next_agent': next_agent, 'next_plan': action}
@@ -79,13 +107,25 @@ def router_node(state: AgentState):
def weather_node(state: AgentState): def weather_node(state: AgentState):
result = '上海的天气是25℃,下雨' # result = '上海的天气是25℃,下雨'
agent_resp = weather_chain.invoke({'next_plan': state.get('next_plan')})
result = agent_resp.get('result')
return {'messages': [HumanMessage(content=result)]} return {'messages': [HumanMessage(content=result)]}
def websearch_node(state: AgentState): def websearch_node(state: AgentState):
result = '2024年中国GDP最高的城市是上海'
return {'messages': [HumanMessage(content=result)]} next_plan = state.get('next_plan', '')
websearch_prompt = ChatPromptTemplate.from_messages([
('system', f'你是一个网络搜索Agent,你需要根据用户的问题,从中提取出用于在网络搜索引擎上搜索的关键字。只需要回答关键字即可,不需要进行解释'),
('user', f'当前的用户问题是:'),
MessagesPlaceholder(variable_name="next_plan"),
])
websearch_chain = (websearch_prompt | llm)
llm_resp = websearch_chain.invoke({'next_plan': [('user', next_plan)]})
search_result = do_search(llm_resp.content)
result_resp = llm.invoke(f'请根据搜索结果进行回答我的问题。\n我的问题是:{next_plan}\n\n搜索结果为:{search_result}')
return {'messages': [HumanMessage(content=result_resp.content)]}
def composer_node(state: AgentState): def composer_node(state: AgentState):
@@ -100,7 +140,7 @@ def composer_node(state: AgentState):
def end_node(state: AgentState): def end_node(state: AgentState):
print('placeholder') print('任务完成')
workflow = StateGraph(AgentState) workflow = StateGraph(AgentState)
@@ -134,12 +174,14 @@ workflow.add_edge('结束节点', END)
workflow.set_entry_point('planner') workflow.set_entry_point('planner')
graph = workflow.compile() graph = workflow.compile()
if '__main__' == __name__:
print(graph.get_graph().draw_ascii()) print(graph.get_graph().draw_ascii())
enter = {'user_query': "查询2024年中国GDP最高的城市,以这个城市今天的天气,创作一首诗", user_query = '查询2024年中国GDP最高的城市,以这个城市今天的天气,创作一首诗'
"members": MEMBERS} enter = {'user_query': user_query, "members": MEMBERS}
for s in graph.stream(enter): for s in graph.stream(enter):
if "__end__" not in s: if "__end__" not in s and 'router' not in s:
print(s) print(s)
print('-'*80) print('-'*80)
+17
View File
@@ -0,0 +1,17 @@
import json
def formatted_json(input_str: str):
if input_str.startswith("```"):
input_str = input_str[3::]
if input_str.endswith("```"):
input_str = input_str[:-3:]
if input_str.startswith("'''"):
input_str = input_str[3::]
if input_str.endswith("'''"):
input_str = input_str[:-3:]
while input_str.startswith('\n'):
input_str = input_str[1::]
if input_str.startswith('json'):
input_str = input_str[4::]
return input_str
+28
View File
@@ -0,0 +1,28 @@
import requests
from src.multi_agent.config import gaode_key
def search_weather(area_str):
district_code_url = 'https://restapi.amap.com/v3/config/district?parameters'
weather_url = 'https://restapi.amap.com/v3/weather/weatherInfo?parameters'
# area_str = '上海市'
response = requests.get(district_code_url, params={'key': gaode_key, 'keywords': area_str})
district_code_list = response.json().get('districts')
if not district_code_list:
raise Exception('查询失败')
district_code = district_code_list[0].get('adcode')
response = requests.get(weather_url, params={'key': gaode_key, 'city': district_code})
weather_list = response.json().get('lives')
if not weather_list:
raise Exception('查询失败')
weather = weather_list[0]
result = f'{area_str}现在{weather["weather"]}{weather["temperature"]}摄氏度,湿度{weather["humidity"]}%{weather["winddirection"]}{weather["windpower"]}'
return result
+7 -4
View File
@@ -1,12 +1,14 @@
from tavily import TavilyClient
import requests import requests
from src.multi_agent.config import tavily_key
query = '乔布斯是谁'
def do_search(query):
# query = '乔布斯是谁'
base_url = 'https://api.tavily.com/search' base_url = 'https://api.tavily.com/search'
json = { json = {
'api_key': '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', 'api_key': tavily_key,
'query': query, 'query': query,
'search_depth': 'basic', 'search_depth': 'basic',
'include_answer': False, 'include_answer': False,
@@ -23,5 +25,6 @@ if response.status_code == 200:
search_result = response.json() search_result = response.json()
else: else:
raise Exception(f'Error: {response.status_code}: {response.reason}') raise Exception(f'Error: {response.status_code}: {response.reason}')
return search_result
print('done')
+57
View File
@@ -0,0 +1,57 @@
import operator
from typing import TypedDict, List, Annotated
from langgraph.graph import END, StateGraph
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from src.multi_agent.config import LLM
from src.multi_agent.util.weather_search import search_weather
llm = LLM
class WeatherState(TypedDict):
user_request: str
city: str
result: str
def requirement_analysis(state: WeatherState):
user_request = state.get('user_request')
analysis_prompt = ChatPromptTemplate([
('system', '你是一个天气查询agent,你负责从用户的需求中提取出需要查询天气的城市或地区,你只需要输出这个地名,不需要输出其他任何内容'),
('user', f'当前的用户需求是:'),
MessagesPlaceholder(variable_name="user_request"),
])
analysis_chain = analysis_prompt | llm
llm_resp = analysis_chain.invoke({'user_request': [("user", user_request)]})
return {'city': llm_resp.content}
def get_weather_data(state: WeatherState):
city = state.get('city')
weather_data = search_weather(city)
return {'result': weather_data}
def enter_chain(input_data: dict):
return {'user_request': input_data['next_plan']}
workflow = StateGraph(WeatherState)
# 其实完全没必要使用子agent,但演示一下子agent如何并网
workflow.add_node('需求分析', requirement_analysis)
workflow.add_node('获取天气', get_weather_data)
workflow.set_entry_point('需求分析')
workflow.add_edge('需求分析', '获取天气')
workflow.add_edge('获取天气', END)
weather_graph = workflow.compile()
weather_chain = enter_chain | weather_graph
if __name__ == '__main__':
requirement_analysis({})