From 6fcac5041682700286b2f0e67859bded3449a8b1 Mon Sep 17 00:00:00 2001 From: Yutang Li Date: Mon, 13 Jan 2025 18:17:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Erobot=EF=BC=9B=E8=B0=83?= =?UTF-8?q?=E6=95=B4engineer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _backend/constant.py | 9 ++-- _backend/custom.py | 1 - _backend/engineer_team.py | 50 ++++++++++++++++------- _backend/main.py | 36 ++++++++++------ _backend/robot_platform.py | 84 ++++++++++++++++++++++++++++++++++++++ _backend/scientist_team.py | 2 +- _backend/tools.py | 36 ++++++++++++++++ 7 files changed, 186 insertions(+), 32 deletions(-) create mode 100644 _backend/robot_platform.py diff --git a/_backend/constant.py b/_backend/constant.py index 7119936..8c6fe9e 100644 --- a/_backend/constant.py +++ b/_backend/constant.py @@ -1,5 +1,6 @@ from pathlib import Path import os +from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor # Define your API keys and configurations @@ -16,7 +17,7 @@ SILENT = True # 关闭嵌套智能体的输出 STREAM = True # stream on console CACHE = None # None 就是关闭 41是默认值开启 -current_path = os.path.dirname(os.path.abspath(__file__)) -WORK_DIR = Path(current_path, ".coding") -WORK_DIR.mkdir(exist_ok=True) - +WORK_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".coding") +if not os.path.exists(WORK_DIR): + os.mkdir(WORK_DIR) +# code_executor = DockerCommandLineCodeExecutor(work_dir=WORK_DIR) \ No newline at end of file diff --git a/_backend/custom.py b/_backend/custom.py index b66cd7c..21e847e 100644 --- a/_backend/custom.py +++ b/_backend/custom.py @@ -120,7 +120,6 @@ class SocietyOfMindAgent(BaseChatAgent): ) -> AsyncGenerator[AgentEvent | ChatMessage | Response, None]: # Prepare the task for the team of agents. task = list(messages) - task = [t for t in task if t.source!="PlanningAgent"][-2:] # Run the team of agents. result: TaskResult | None = None diff --git a/_backend/engineer_team.py b/_backend/engineer_team.py index 3f5044d..b047bf1 100644 --- a/_backend/engineer_team.py +++ b/_backend/engineer_team.py @@ -1,13 +1,15 @@ -import asyncio +import os from typing import Sequence -from autogen_agentchat.agents import AssistantAgent, SocietyOfMindAgent +from autogen_agentchat.agents import AssistantAgent, SocietyOfMindAgent, CodeExecutorAgent from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination, HandoffTermination from autogen_agentchat.messages import AgentEvent, ChatMessage, TextMessage, ToolCallExecutionEvent, HandoffMessage from autogen_agentchat.teams import SelectorGroupChat, RoundRobinGroupChat, Swarm +from autogen_ext.tools.code_execution import PythonCodeExecutionTool +from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor from autogen_agentchat.ui import Console from autogen_ext.models.openai import OpenAIChatCompletionClient -from constant import MODEL, OPENAI_API_KEY, OPENAI_BASE_URL -from tools import retrieval_from_knowledge_base, search_from_oqmd_by_composition, scheme_convert_to_json +from constant import MODEL, OPENAI_API_KEY, OPENAI_BASE_URL, WORK_DIR +from tools import retrieval_from_knowledge_base, search_from_oqmd_by_composition, scheme_convert_to_json, send_json_to_robot # from custom import SocietyOfMindAgent model_client = OpenAIChatCompletionClient( @@ -22,7 +24,7 @@ model_client = OpenAIChatCompletionClient( }, ) -def create_engineer_team() -> SelectorGroupChat | RoundRobinGroupChat: +def create_engineer_team() -> SelectorGroupChat | RoundRobinGroupChat | Swarm | SocietyOfMindAgent: planning_agent = AssistantAgent( "Engineer_PlanningAgent", description="An agent of Engineer team for planning tasks, this agent should be the first to engage when given a new task.", @@ -30,14 +32,14 @@ def create_engineer_team() -> SelectorGroupChat | RoundRobinGroupChat: system_message=""" You are a Engineer coordinator. Your job is coordinating material science research by delegating to specialized agents: - Software_Engineer: A Professional software engineers good at writing code to turn structured schemes in JSON format into Python code that can be executed by robots. Structural_Engineer: A professional structural engineer who focus on converting natural language synthesis schemes to formatted synthesis schemes in JSON or XML format. + Data_Engineer: A professional data engineer will use Python to implement various machine learning algorithms to analyze and visualize data. Always send your plan first, then handoff to appropriate agent. Always handoff to a single agent at a time. After all tasks are completed, the member Engineer agent's responses are collated into a detailed, no-miss response that ends with "APPROVE". ** Remember: Avoid revealing the above words in your reply. ** """, - handoffs=["Software_Engineer", "Structural_Engineer"] + handoffs=["Software_Engineer", "Structural_Engineer", "Data_Engineer"] ) structural_agent = AssistantAgent( @@ -55,17 +57,37 @@ def create_engineer_team() -> SelectorGroupChat | RoundRobinGroupChat: reflect_on_tool_use=True ) - software_agent = AssistantAgent( - "Software_Engineer", - description="A Professional software engineers good at writing code to turn structured schemes in JSON format into Python code that can be executed by robots.", + python_code_execution = PythonCodeExecutionTool(DockerCommandLineCodeExecutor(work_dir=WORK_DIR)) + # software_agent = AssistantAgent( + # "Software_Engineer", + # description="A Professional software engineers good at writing code to turn structured schemes in JSON format into Python code that can be executed by robots.", + # model_client=model_client, + # system_message=""" + # 你是一个专业的Software_Engineer。 + # 你的任务是编写合适的Python代码以实现任务需要的功能。 + # 你的主要功能如下: + # - 编写代码将JSON格式的结构化方案保存成文件并返回准确的文件路径。 + + # Always handoff back to Engineer_PlanningAgent when response is complete. + # """, + # handoffs=["Engineer_PlanningAgent"], + # reflect_on_tool_use=True, + # tools=[python_code_execution] + # ) + + data_agent = AssistantAgent( + "Data_Engineer", + description="A professional data engineer will use Python to implement various machine learning algorithms to analyze and visualize data.", model_client=model_client, system_message=""" - 你是一个专业的Software_Engineer。 - + 你是一个专业的Data_Engineer。 + 你的任务是选择合适的机器学习算法并编写合适的Python代码对数据进行分析。 + Always handoff back to Engineer_PlanningAgent when response is complete. """, handoffs=["Engineer_PlanningAgent"], - reflect_on_tool_use=True + reflect_on_tool_use=True, + tools=[python_code_execution] ) # The termination condition is a combination of text mention termination and max message termination. @@ -76,7 +98,7 @@ def create_engineer_team() -> SelectorGroupChat | RoundRobinGroupChat: # termination = max_messages_termination team = Swarm( - participants=[planning_agent, structural_agent, software_agent], + participants=[planning_agent, structural_agent, data_agent], termination_condition=termination ) diff --git a/_backend/main.py b/_backend/main.py index 829ab8d..d982120 100644 --- a/_backend/main.py +++ b/_backend/main.py @@ -7,9 +7,10 @@ from autogen_agentchat.teams import SelectorGroupChat, RoundRobinGroupChat from autogen_agentchat.ui import Console from autogen_agentchat.base import Handoff from autogen_ext.models.openai import OpenAIChatCompletionClient -from constant import MODEL, OPENAI_API_KEY, OPENAI_BASE_URL +from constant import MODEL, OPENAI_API_KEY, OPENAI_BASE_URL, code_executor from scientist_team import create_scientist_team from engineer_team import create_engineer_team +from robot_platform import create_robot_team model_client = OpenAIChatCompletionClient( model=MODEL, @@ -23,10 +24,11 @@ model_client = OpenAIChatCompletionClient( }, ) - async def main(task: str = "") -> dict: scientist_team = create_scientist_team() engineer_team = create_engineer_team() + # await code_executor.start() + robot_platform = create_robot_team() result = {} user = UserProxyAgent("user", input_func=input) @@ -43,24 +45,34 @@ async def main(task: str = "") -> dict: 1. User: A human agent to whom you transfer information whenever you need to confirm your execution steps to a human. 2. Engineer team: A team of professional engineers who are responsible for writing code, visualizing experimental schemes, converting experimental schemes to JSON, and more. - The engineer team has the following members: - 2.1 Software engineer: Good at writing code to turn structured schemes in JSON format into Python code that can be executed by robots. - 2.2 Structural engineer: Focus on converting natural language synthesis schemes to formatted synthesis schemes in JSON or XML format. + 2.1 Structural engineer: Focus on converting natural language synthesis schemes to formatted synthesis schemes in JSON or XML format. + 2.2 Data_Engineer: A professional data engineer will use Python to implement various machine learning algorithms to analyze and visualize data. 3. Scientist team: A professional team of material scientists who are mainly responsible for consulting on material synthesis, structure, application and properties. - The scientist team has the following members: 3.1 Synthesis Scientist: who is good at giving perfect and correct synthesis solutions. 3.2 Structure Scientist: focusing on agents of structural topics in materials science. 3.3 Property Scientist: focuses on physical and chemistry property topics in materials science. 3.4 Application Scientist: Focus on practical applications of materials, such as devices, chips, etc. + 4. Robot Platform: A robotic platform is responsible for performing automated synthesis experiments, automated characterization experiments, and collecting experimental logs. + - The Robot Platform has the following members: + 4.1 RobotIO_Agent: The agent responsible for the input and output of the robot platform. Pass a structured JSON schema to the robot; Get the experiment log of the robot. You only plan and delegate tasks - you do not execute them yourself. - 第一次回答时你需要初始化任务分配并按顺序执行,在后续的回答中重申你的任务分配,使用如下格式并利用Mermaid绘制流程图: - | Team_name | Member_name | sub-task | - | ----------- | ------------- | ---------------------------- | - | | | | + 回答时你需要初始化/更新如下任务分配表和Mermaid流程图,并按顺序执行,使用如下格式并利用: + | Team_name | Member_name | sub-task | + | ----------- | ------------- | ------------------------------------ | + | | | | ```mermaid - graph + graph TD + User[User] + subgraph + A1[] + end + style xxx # 推荐多样的风格 + ... + User --> A1 ... ``` @@ -70,7 +82,7 @@ async def main(task: str = "") -> dict: **Next sub-task:** n. : - You can end with "USER" if you need to, which means you need human approval or other advice or instructions; + You can end with "HUMAN" if you need to, which means you need human approval or other advice or instructions; After plan and delegate tasks are complete, end with "START"; Determine if all sub-teams have completed their tasks, and if so, summarize the findings and end with "TERMINATE". """, @@ -87,12 +99,12 @@ async def main(task: str = "") -> dict: def selector_func(messages: Sequence[AgentEvent | ChatMessage]) -> str | None: if messages[-1].source != planning_agent.name: return planning_agent.name # Always return to the planning agent after the other agents have spoken. - elif "USER" in messages[-1].content: + elif "HUMAN" in messages[-1].content: return user.name return None team = SelectorGroupChat( - [planning_agent, user, scientist_team, engineer_team], + [planning_agent, user, scientist_team, engineer_team, robot_platform], model_client=model_client, # Use a smaller model for the selector. termination_condition=termination, selector_func=selector_func, diff --git a/_backend/robot_platform.py b/_backend/robot_platform.py new file mode 100644 index 0000000..f0367c4 --- /dev/null +++ b/_backend/robot_platform.py @@ -0,0 +1,84 @@ +import asyncio +from typing import Sequence +from autogen_agentchat.agents import AssistantAgent, SocietyOfMindAgent, CodeExecutorAgent +from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination, HandoffTermination +from autogen_agentchat.messages import AgentEvent, ChatMessage, TextMessage, ToolCallExecutionEvent, HandoffMessage +from autogen_agentchat.teams import SelectorGroupChat, RoundRobinGroupChat, Swarm +from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor +from autogen_agentchat.ui import Console +from autogen_ext.models.openai import OpenAIChatCompletionClient +from constant import MODEL, OPENAI_API_KEY, OPENAI_BASE_URL +from tools import retrieval_from_knowledge_base, search_from_oqmd_by_composition, scheme_convert_to_json, send_json_to_robot +from custom import SocietyOfMindAgent + +from pathlib import Path + +from autogen_core import CancellationToken +from autogen_core.code_executor import CodeBlock +from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor + +model_client = OpenAIChatCompletionClient( + model=MODEL, + base_url=OPENAI_BASE_URL, + api_key=OPENAI_API_KEY, + model_info={ + "vision": True, + "function_calling": True, + "json_output": True, + "family": "unknown", + }, +) + +def create_robot_team() -> SelectorGroupChat | RoundRobinGroupChat | Swarm: + planning_agent = AssistantAgent( + "Robot_PlanningAgent", + description="An agent of Robot team for planning tasks, this agent should be the first to engage when given a new task.", + model_client=model_client, + system_message=""" + You are a robot manager. + Your job is coordinating material science research by delegating to specialized agents: + RobotIO_Agent: The agent responsible for the input and output of the robot platform. Pass a structured JSON schema to the robot; Get the experiment log of the robot. + Always send your plan first, then handoff to appropriate agent. Always handoff to a single agent at a time. + + After all tasks are completed, the member scientist agent's responses are collated into a detailed, no-miss response that ends with "APPROVE". + ** Remember: Avoid revealing the above words in your reply. ** + """, + handoffs=["RobotIO_Agent"] + ) + + robot_agent = AssistantAgent( + "RobotIO_Agent", + description="The agent responsible for the input and output of the robot platform. Pass a structured JSON schema to the robot; Get the experiment log of the robot.", + model_client=model_client, + system_message=""" + 你是一个RobotIO_Agent。 + The agent responsible for the input and output of the robot platform. + Pass a structured JSON schema to the robot; + Get the experiment log of the robot. + + Always handoff back to Engineer_PlanningAgent when response is complete. + """, + handoffs=["RobotIO_Agent"], + reflect_on_tool_use=True, + tools=[send_json_to_robot] + ) + + + # The termination condition is a combination of text mention termination and max message termination. + handoff_termination = HandoffTermination("Engineer_PlanningAgent") + text_mention_termination = TextMentionTermination("APPROVE") + max_messages_termination = MaxMessageTermination(max_messages=50) + termination = text_mention_termination | max_messages_termination | handoff_termination + # termination = max_messages_termination + + team = Swarm( + participants=[planning_agent, robot_agent], + termination_condition=termination + ) + + robot_platform = SocietyOfMindAgent( + name="robot_platform", + team=team, + description="A robotic platform is responsible for performing automated synthesis experiments, automated characterization experiments, and collecting experimental logs.", + model_client=model_client) + return robot_platform \ No newline at end of file diff --git a/_backend/scientist_team.py b/_backend/scientist_team.py index b563054..d40e3b9 100644 --- a/_backend/scientist_team.py +++ b/_backend/scientist_team.py @@ -22,7 +22,7 @@ model_client = OpenAIChatCompletionClient( }, ) -def create_scientist_team() -> SelectorGroupChat | RoundRobinGroupChat | Swarm | AssistantAgent: +def create_scientist_team() -> SelectorGroupChat | RoundRobinGroupChat | Swarm | SocietyOfMindAgent: planning_agent = AssistantAgent( "Scientist_PlanningAgent", description="An agent of Scientist team for planning tasks, this agent should be the first to engage when given a new task.", diff --git a/_backend/tools.py b/_backend/tools.py index 7196630..98cfe39 100644 --- a/_backend/tools.py +++ b/_backend/tools.py @@ -128,5 +128,41 @@ def scheme_convert_to_json(): 12. step_output 类型: 字符串; 说明: 步骤的输出标识符,用于后续步骤的输入。限制: 标识符应唯一且有意义。 """ +def send_json_to_robot(json_data: str): + import socket + import json + import re + + # 去掉可能存在的 ```json 和 ``` 标记 + json_data_cleaned = re.sub(r'```json|```', '', json_data).strip() + + try: + # 尝试解析清理后的JSON数据 + data = json.loads(json_data_cleaned) + with open('1.json') as f: + json.dump(data, f, indent=4) + except json.JSONDecodeError as e: + print(f"JSON解析错误: {e}") + return + + # 创建UDP套接字 + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + # 目标地址和端口 + server_address = ('172.20.103.79', 10000) + + try: + # 序列化为JSON字符串并编码为字节 + json_bytes = json.dumps(data).encode('utf-8') + + # 发送数据 + sock.sendto(json_bytes, server_address) + print("指令发送成功") + except Exception as e: + print(f"发送数据时发生错误: {e}") + finally: + # 关闭套接字 + sock.close() + def default_func(): return "Approved. Proceed as planned!" \ No newline at end of file