diff --git a/src/langchain_lab/core/conference.py b/src/langchain_lab/core/conference.py index 1cebfa9..8a2f646 100644 --- a/src/langchain_lab/core/conference.py +++ b/src/langchain_lab/core/conference.py @@ -15,17 +15,33 @@ from langgraph.graph.graph import CompiledGraph from langgraph.prebuilt import ToolInvocation, ToolExecutor +lang_prompts = { + "en": { + "agent_system_prompt_prefix": ("You are a helpful AI assistant, collaborating with other assistants." + " Use the provided tools to progress towards answering the question." + " If you are unable to fully answer, that's OK," + " another assistant with different tools " + " will help where you left off. Execute what you can to make progress." + " If you or any of the other assistants have the final answer or deliverable," + " prefix your response with FINAL ANSWER so the team knows to stop.") + }, + "zh": { + "agent_system_prompt_prefix": ("你是一个乐于助人的人工智能助手,正在与其他助手合作。" + "如果你无法完全回答,没关系,另一个助手会帮助你完成你未完成的任务。尽你所能取得进展。" + "如果你或任何其他助手有最终答案或可交付成果," + "在你的回答前面加上 'FINAL ANSWER',这样团队就知道该停下来了。") + } +} + class AgentRunnableSequence: - id: str - nickname: str + name: str next_agent_name: str agent: RunnableSequence entry_point: bool = False - def __init__(self, id, nickname, next_agent_name, agent, entry_point): - self.id = id - self.nickname = nickname + def __init__(self, name, next_agent_name, agent, entry_point): + self.name = name self.next_agent_name = next_agent_name self.agent = agent self.entry_point = entry_point @@ -47,33 +63,38 @@ class AgentState(TypedDict): class Conference: - def __init__(self, llm: BaseChatModel = None, python_repl: bool = False): + def __init__(self, llm: BaseChatModel = None, python_repl: bool = False, lang: str = "en"): self.tool_executor: ToolExecutor = None self.graph: CompiledGraph = None self.agents: List[AgentRunnableSequence] = [] self.tools: List[Tool] = [] self.python_repl = python_repl self.default_llm = llm + if lang not in lang_prompts: + raise ValueError(f"Language {lang} not supported. Only support {', '.join(lang_prompts.keys())}.") + self.lang = lang def add_tool(self, *tools): for t in tools: self.tools.append(t) self.tool_executor = ToolExecutor(self.tools) - def add_agent(self, agent_id, agent_nickname: str, system_message: str, next_agent_name: str, + def add_agent(self, agent_name: str, system_message: str, next_agent_name: str, entry_point: bool = False, llm: BaseChatModel = None): functions = [convert_to_openai_function(t) for t in self.tools] + + system_content = lang_prompts[self.lang]["agent_system_prompt_prefix"] + + if len(self.tools) > 0: + system_content = system_content + " You have access to the following tools: {tool_names}." + + system_content = system_content + "\n{system_message}" + prompt = ChatPromptTemplate.from_messages( [ ( "system", - "You are a helpful AI assistant, collaborating with other assistants." - " Use the provided tools to progress towards answering the question." - " If you are unable to fully answer, that's OK, another assistant with different tools " - " will help where you left off. Execute what you can to make progress." - " If you or any of the other assistants have the final answer or deliverable," - " prefix your response with FINAL ANSWER so the team knows to stop." - " You have access to the following tools: {tool_names}.\n{system_message}", + system_content ), MessagesPlaceholder(variable_name="messages"), ] @@ -89,8 +110,7 @@ def add_agent(self, agent_id, agent_nickname: str, system_message: str, next_age raise ValueError("No language model provided.") self.agents.append( - AgentRunnableSequence(id=agent_id, - nickname=agent_nickname, + AgentRunnableSequence(name=agent_name, next_agent_name=next_agent_name, agent=agent, entry_point=entry_point)) @@ -100,34 +120,41 @@ def build_graph(self): # Add Agents for agent in self.agents: - workflow.add_node(agent.nickname, - functools.partial(self.graph_node_agent, agent=agent.agent, name=agent.nickname)) + workflow.add_node(agent.name, + functools.partial(self.graph_node_agent, agent=agent.agent, name=agent.name)) # Add ToolKit - workflow.add_node("ToolKit", self.graph_node_tool_kit) + if len(self.tools) > 0: + workflow.add_node("ToolKit", self.graph_node_tool_kit) # Add Edges for agent in self.agents: + path_map = {"continue": agent.next_agent_name, "end": END} + if len(self.tools) > 0: + path_map["ToolKit"] = "ToolKit" workflow.add_conditional_edges( - agent.nickname, + agent.name, self.graph_node_router, - {"continue": agent.next_agent_name, "ToolKit": "ToolKit", "end": END}, + path_map + ) + + if len(self.tools) > 0: + path_map = {agent.name: agent.next_agent_name for agent in self.agents} + workflow.add_conditional_edges( + source="ToolKit", + path=lambda x: x["sender"], + path_map=path_map ) - path_map = {agent.nickname: agent.next_agent_name for agent in self.agents} - workflow.add_conditional_edges( - source="ToolKit", - path=lambda x: x["sender"], - path_map=path_map - ) entry_point_agents = [agent for agent in self.agents if agent.entry_point] if len(entry_point_agents) > 1: raise ValueError("Only one agent can be an entry point.") elif len(entry_point_agents) == 0: raise ValueError("At least one agent must be an entry point.") else: - workflow.set_entry_point(entry_point_agents[0].nickname) + workflow.set_entry_point(entry_point_agents[0].name) self.graph = workflow.compile() + self.graph.get_graph().print_ascii() def graph_node_tool_kit(self, state: AgentState): last_message = state["messages"][-1] @@ -185,8 +212,8 @@ def python_repl_tool(code: Annotated[str, "The python code to execute to generat return f"Failed to execute. Error: {repr(e)}" return f"Successfully executed:\n```python\n{code}\n```\nStdout: {result}" - def invoke(self, humanMessage: HumanMessage, recursion_limit: int = 20): - output_role = [agent.nickname for agent in self.agents] + def invoke(self, humanMessage: HumanMessage, recursion_limit: int = 5): + output_role = [agent.name for agent in self.agents] output_role.append("ToolKit") for s in self.graph.stream( { diff --git a/src/langchain_lab/langgraph/network_operations_analysis_assistant/network_operations_analysis_assistant.py b/src/langchain_lab/langgraph/network_operations_analysis_assistant/network_operations_analysis_assistant.py index 4a77709..fa74d1e 100644 --- a/src/langchain_lab/langgraph/network_operations_analysis_assistant/network_operations_analysis_assistant.py +++ b/src/langchain_lab/langgraph/network_operations_analysis_assistant/network_operations_analysis_assistant.py @@ -5,7 +5,8 @@ from typing import Annotated, Sequence import pandas as pd -from langchain_core.messages import BaseMessage, FunctionMessage, HumanMessage +from langchain_community.chat_models import ChatTongyi +from langchain_core.messages import BaseMessage, FunctionMessage, HumanMessage, ToolMessage from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.runnables import RunnableSequence from langchain_core.tools import tool @@ -32,8 +33,9 @@ class NetworkOperationsAnalysisAssistant: def __init__(self, openai_api_base: str, openai_api_key: str, model_name: str, recursion_limit: int = 20): self.model_name = model_name self.recursion_limit = recursion_limit - self.llm = ChatOpenAI(model_name=model_name, openai_api_base=openai_api_base, openai_api_key=openai_api_key, - temperature=0.000000001, request_timeout=600, streaming=True) + self.llm = ChatTongyi(model_name=model_name, openai_api_base=openai_api_base, dashscope_api_key=openai_api_key) + # self.llm = ChatOpenAI(model_name=model_name, openai_api_base=openai_api_base, openai_api_key=openai_api_key, + # temperature=0.000000001, request_timeout=600, streaming=True) # 网络运营经理 networkOpsManager = self.create_agent( @@ -203,7 +205,7 @@ def graph_node_agent(state: AgentState, agent: RunnableSequence, name: str): if isinstance(result, FunctionMessage): pass else: - result = HumanMessage(**result.dict(exclude={"type", "name"}), name=name) + result = HumanMessage(**result.dict(exclude={"type", "name"})) return { "messages": [result], # 由于有严格的工作流程,可以追踪发件人。 @@ -216,7 +218,7 @@ def graph_node_router(state: AgentState): messages = state["messages"] last_message = messages[-1] - if "function_call" in last_message.additional_kwargs: + if "tool_calls" in last_message.additional_kwargs: return "data_tool" if "FINAL ANSWER" in last_message.content: @@ -232,11 +234,13 @@ def graph_node_data_tool(self, state: AgentState): last_message = messages[-1] # 从function_call创建ToolInvocation - tool_input = json.loads(last_message.additional_kwargs["function_call"]["arguments"]) + # tool_input = json.loads(last_message.additional_kwargs["tool_calls"]["arguments"]) + tool_input = json.loads(last_message.additional_kwargs["tool_calls"][0]["function"]["arguments"]) # 传递单个参数 if len(tool_input) == 1 and "__arg1" in tool_input: tool_input = next(iter(tool_input.values())) - tool_name = last_message.additional_kwargs["function_call"]["name"] + # tool_name = last_message.additional_kwargs["function_call"]["name"] + tool_name = last_message.additional_kwargs["tool_calls"][0]["function"]["name"] action = ToolInvocation( tool=tool_name, tool_input=tool_input, @@ -245,7 +249,9 @@ def graph_node_data_tool(self, state: AgentState): # 调用tool_executor,并返回响应。 response = self.tool_executor.invoke(action) # 利用响应创建FunctionMessage。 - function_message = FunctionMessage(content=f"{tool_name} response: {str(response)}", name=action.tool) + # function_message = FunctionMessage(content=f"{tool_name} response: {str(response)}", name=action.tool) + # function_message = ToolMessage(content=f"{tool_name} response: {str(response)}", tool_call_id=action.tool) + function_message = HumanMessage(content=f"{tool_name} response: {str(response)}") # 将现有列表添加 return {"messages": [function_message]} @@ -255,7 +261,7 @@ def create_agent(llm, tools, system_message: str): prompt = ChatPromptTemplate.from_messages( [ ( - "system", + "user", "您是一个精通电信网络知识的AI助手,与其他助手合作。" "使用提供的工具来逐步回答问题。" "如果您无法完全回答,没关系,另一个使用不同工具的助手将继续帮助您完成。尽力取得进展。" @@ -268,7 +274,7 @@ def create_agent(llm, tools, system_message: str): ) prompt = prompt.partial(system_message=system_message) prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools])) - return prompt | llm.bind_functions(functions) + return prompt | llm.bind_tools(functions) def run(self): agent_names = { diff --git a/tests/langchain_lab/langgraph/marketing_analysis_assistant/marketing_analysis_assistant_gpt-3.5-turbo.md b/tests/langchain_lab/langgraph/marketing_analysis_assistant/marketing_analysis_assistant_gpt-3.5-turbo.md index daae30f..e69de29 100644 --- a/tests/langchain_lab/langgraph/marketing_analysis_assistant/marketing_analysis_assistant_gpt-3.5-turbo.md +++ b/tests/langchain_lab/langgraph/marketing_analysis_assistant/marketing_analysis_assistant_gpt-3.5-turbo.md @@ -1,82 +0,0 @@ -## Sales_Staff - -原始数据: - -| | Year | Product_A | Product_B | Product_C | -|---:|-------:|------------:|------------:|------------:| -| 0 | 2019 | 1091 | 540 | 802 | -| 1 | 2020 | 979 | 527 | 782 | -| 2 | 2021 | 970 | 521 | 816 | -| 3 | 2022 | 1070 | 559 | 752 | -| 4 | 2023 | 1059 | 533 | 730 | - -## Sales_Manager - -描述性统计: - -| | Year | Product_A | Product_B | Product_C | -|:------|-----------:|------------:|------------:|------------:| -| count | 5 | 5 | 5 | 5 | -| mean | 2021 | 1033.8 | 536 | 776.4 | -| std | 1.58114 | 55.4319 | 14.6629 | 35.3667 | -| min | 2019 | 970 | 521 | 730 | -| 25% | 2020 | 979 | 527 | 752 | -| 50% | 2021 | 1059 | 533 | 782 | -| 75% | 2022 | 1070 | 540 | 802 | -| max | 2023 | 1091 | 559 | 816 | - -相关性矩阵: - -| | Year | Product_A | Product_B | Product_C | -|:----------|-----------:|------------:|------------:|------------:| -| Year | 1 | 0.0770147 | 0.194099 | -0.777903 | -| Product_A | 0.0770147 | 1 | 0.749269 | -0.43633 | -| Product_B | 0.194099 | 0.749269 | 1 | -0.464733 | -| Product_C | -0.777903 | -0.43633 | -0.464733 | 1 | - -## Sales_Staff - -根据数据分析工具提供的分析结果,我们可以看到以下关键信息: - -- 产品A在过去5年的平均销量为1033.8,标准差为55.43,最低销量为970,最高销量为1091。 -- 产品B在过去5年的平均销量为536,标准差为14.66,最低销量为521,最高销量为559。 -- 产品C在过去5年的平均销量为776.4,标准差为35.37,最低销量为730,最高销量为816。 - -此外,相关性矩阵显示了各产品之间的相关性。我们可以看到产品A和产品B之间有较强的正相关关系(相关系数为0.749),而产品A和产品C之间则呈现负相关关系(相关系数为-0.436)。 - -基于以上分析结果,我们可以制定以下销售策略: -- 由于产品A和产品B之间存在较强的正相关关系,可以考虑将它们作为搭配销售,推出套餐优惠,以促进销量增长。 -- 鉴于产品A和产品C之间的负相关关系,可以尝试调整市场定位或推出新的营销策略,以提高产品C的销量。 -- 针对产品B的销量波动较小,可以继续保持稳定的市场定位和销售策略,但也需要不断创新以留住现有客户并吸引新客户。 - -Sales_Staff,您对以上销售策略有什么想法或建议吗? - -## Sales_Manager - -销售策略看起来很合理和有针对性。我认同将产品A和产品B作为搭配销售,并推出套餐优惠的想法,这样可以提高销量并增加客户的购买意愿。对于产品C的销量,需要进一步研究消费者需求和市场定位,确保推出的营销策略能够有效提升销量。另外,为了保持产品B的稳定销量,可以考虑增加客户忠诚度计划或定期推出新的促销活动以吸引更多消费者。综合来看,这些策略应该能够帮助公司实现更好的销售业绩。 - -## Sales_Staff - -根据Sales_Staff和Sales_Manager的讨论,他们就过去5年的产品销量数据进行了详细分析,并制定了针对产品A、B、C的销售策略。销售策略包括搭配销售、套餐优惠、调整市场定位、推出新的营销策略、客户忠诚度计划和促销活动等。这些策略旨在提高销量、增加客户购买意愿并改善公司的销售业绩。 - -由于销售团队已经就销售策略达成一致,Sales_Manager可以总结这些重要要点,并结束会话。接下来,我将总结这些要点。 -**重要要点总结:** - -1. 产品A和产品B可以作为搭配销售,并推出套餐优惠,以增加销量和客户购买意愿。 -2. 针对产品C的销量,需要进一步研究消费者需求和市场定位,确保推出的营销策略能有效提升销量。 -3. 为保持产品B的稳定销量,可以考虑增加客户忠诚度计划或定期推出新的促销活动以吸引更多消费者。 - -Sales_Manager,您可以在实施销售策略时重点关注以上要点,确保团队的努力能够有效提升销售业绩。如有需要,可以随时进行进一步讨论或调整销售策略。如果没有其他问题,可以结束本次会话。 - -## Sales_Manager - -**FINAL ANSWER** - -重要要点总结: - -1. 产品A和产品B可以作为搭配销售,并推出套餐优惠,以增加销量和客户购买意愿。 -2. 针对产品C的销量,需要进一步研究消费者需求和市场定位,确保推出的营销策略能有效提升销量。 -3. 为保持产品B的稳定销量,可以考虑增加客户忠诚度计划或定期推出新的促销活动以吸引更多消费者。 - -销售团队已经就销售策略达成一致,Sales_Manager可以在实施销售策略时重点关注以上要点,确保团队的努力能够有效提升销售业绩。如有需要,可以随时进行进一步讨论或调整销售策略。如果没有其他问题,可以结束本次会话。 - diff --git a/tests/langchain_lab/langgraph/marketing_analysis_assistant/test_marketing_analysis_assistant.py b/tests/langchain_lab/langgraph/marketing_analysis_assistant/test_marketing_analysis_assistant.py index 86c3d32..02c8026 100644 --- a/tests/langchain_lab/langgraph/marketing_analysis_assistant/test_marketing_analysis_assistant.py +++ b/tests/langchain_lab/langgraph/marketing_analysis_assistant/test_marketing_analysis_assistant.py @@ -53,12 +53,12 @@ def test_conference(self): conference = Conference(llm=llm) conference.add_tool(load_sales_data_tool, data_analysis_tool) conference.add_agent(agent_id="Sales_Staff", - agent_nickname="Sales_Staff", + agent_name="Sales_Staff", system_message="负责客户服务和产品、服务提案。回答客户问题,推荐适当的产品、服务,并记录商谈数据、销售预定数据到系统中。", next_agent_name="Sales_Manager", entry_point=True) conference.add_agent(agent_id="Sales_Manager", - agent_nickname="Sales_Manager", + agent_name="Sales_Manager", system_message="负责团队管理和指导。设定销售目标,制定销售策略,监控绩效,并向团队成员提供反馈。", next_agent_name="Sales_Staff") conference.build_graph() diff --git a/tests/langchain_lab/langgraph/network_operations_analysis_assistant/test_network_operations_analysis_assistant.py b/tests/langchain_lab/langgraph/network_operations_analysis_assistant/test_network_operations_analysis_assistant.py index e870f9e..744cd3d 100644 --- a/tests/langchain_lab/langgraph/network_operations_analysis_assistant/test_network_operations_analysis_assistant.py +++ b/tests/langchain_lab/langgraph/network_operations_analysis_assistant/test_network_operations_analysis_assistant.py @@ -2,22 +2,51 @@ import unittest from unittest import TestCase +import pandas as pd from dotenv import load_dotenv - -from langchain_lab.langgraph.network_operations_analysis_assistant.network_operations_analysis_assistant import \ - NetworkOperationsAnalysisAssistant - -load_dotenv("./.env") - - -class TestNetworkOperationsAnalysisAssistant(TestCase): - - def test_main(self): - assistant = NetworkOperationsAnalysisAssistant(openai_api_base=os.environ["OPENAI_API_BASE"], - openai_api_key=os.environ["OPENAI_API_KEY"], - model_name=os.environ["MODEL_NAME"], - recursion_limit=50) - assistant.run() +from langchain_core.messages import HumanMessage +from langchain_openai import ChatOpenAI + +from langchain_lab.core.conference import Conference + +load_dotenv("../../../../.env") +llm = ChatOpenAI(model_name=os.environ["MODEL_NAME"], + openai_api_base=os.environ["OPENAI_API_BASE"], + openai_api_key=os.environ["OPENAI_API_KEY"], + temperature=0.7, + request_timeout=600, streaming=True) + + +class TestConference(TestCase): + + def test_conference_without_tool(self): + conference = Conference(llm=llm, lang="zh") + conference.add_agent(agent_name="数据分析专家", + system_message="数据分析专家,负责分析和解读数据,提供对数据的深入见解和洞察。", + next_agent_name="网络优化工程师", + entry_point=True) + conference.add_agent(agent_name="网络优化工程师", + system_message="你是网络优化工程师,负责网络性能和资源管理,监控网络性能指标。", + next_agent_name="网络运营经理") + conference.add_agent(agent_name="网络运营经理", + system_message="你是网络运营经理,负责整体网络运营策略的制定和执行,关注全国各省网络质量情况。", + next_agent_name="网络优化工程师") + conference.build_graph() + + data_markdown = pd.read_excel("./wireless_network_statistics_data.xlsx").to_markdown() + + conversation = conference.invoke(humanMessage=HumanMessage( + content="请根据数据报告进行分析,结合其他人员的建议进行补充,如果你是网络运营经理还需要最后对指标异常的省份进行单独评价" + f"##数据报告: \n\n{data_markdown}\n\n" + ), recursion_limit=10) + + with open(f"network_operations_analysis_assistant.py_{llm.model_name}.md", "w") as file: + for message in conversation: + if message.content: + print(f"## {message.role}\n\n") + print(f"{message.content.strip()}\n\n") + file.write(f"## {message.role}\n\n") + file.write(f"{message.content.strip()}\n\n") if __name__ == "__main__":