Duibonduil commited on
Commit
5fc6c27
·
verified ·
1 Parent(s): 9d76b91

Upload 5 files

Browse files
examples/plan_execute/agent.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+ import copy
4
+ import json
5
+ import traceback
6
+ from typing import Dict, Any, List, Union
7
+
8
+ from examples.tools.common import Agents
9
+ from aworld.core.agent.base import AgentResult
10
+ from aworld.agents.llm_agent import Agent
11
+ from aworld.models.llm import call_llm_model
12
+ from aworld.config.conf import AgentConfig, ConfigDict
13
+ from aworld.core.common import Observation, ActionModel
14
+ from aworld.logs.util import logger
15
+ from examples.plan_execute.prompts import *
16
+ from examples.plan_execute.utils import extract_pattern
17
+
18
+
19
+ class ExecuteAgent(Agent):
20
+ def __init__(self, conf: Union[Dict[str, Any], ConfigDict, AgentConfig], **kwargs):
21
+ super(ExecuteAgent, self).__init__(conf, **kwargs)
22
+
23
+ def id(self) -> str:
24
+ return Agents.EXECUTE.value
25
+
26
+ def reset(self, options: Dict[str, Any]):
27
+ """Execute agent reset need query task as input."""
28
+ super().reset(options)
29
+
30
+ self.system_prompt = execute_system_prompt.format(task=self.task)
31
+ self.step_reset = False
32
+
33
+ async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> Union[
34
+ List[ActionModel], None]:
35
+ await self.async_desc_transform()
36
+ return self._common(observation, info)
37
+
38
+ def policy(self,
39
+ observation: Observation,
40
+ info: Dict[str, Any] = None,
41
+ **kwargs) -> List[ActionModel] | None:
42
+ self.desc_transform()
43
+ return self._common(observation, info)
44
+
45
+ def _common(self, observation, info):
46
+ self._finished = False
47
+ content = observation.content
48
+
49
+ llm_result = None
50
+ ## build input of llm
51
+ input_content = [
52
+ {'role': 'system', 'content': self.system_prompt},
53
+ ]
54
+ for traj in self.trajectory:
55
+ # Handle multiple messages in content
56
+ if isinstance(traj[0].content, list):
57
+ input_content.extend(traj[0].content)
58
+ else:
59
+ input_content.append(traj[0].content)
60
+
61
+ if traj[-1].tool_calls is not None:
62
+ input_content.append(
63
+ {'role': 'assistant', 'content': '', 'tool_calls': traj[-1].tool_calls})
64
+ else:
65
+ input_content.append({'role': 'assistant', 'content': traj[-1].content})
66
+
67
+ if content is None:
68
+ content = observation.action_result[0].error
69
+ if not self.trajectory:
70
+ new_messages = [{"role": "user", "content": content}]
71
+ input_content.extend(new_messages)
72
+ else:
73
+ # Collect existing tool_call_ids from input_content
74
+ existing_tool_call_ids = {
75
+ msg.get("tool_call_id") for msg in input_content
76
+ if msg.get("role") == "tool" and msg.get("tool_call_id")
77
+ }
78
+
79
+ new_messages = []
80
+ for traj in self.trajectory:
81
+ if traj[-1].tool_calls is not None:
82
+ # Handle multiple tool calls
83
+ for tool_call in traj[-1].tool_calls:
84
+ # Only add if this tool_call_id doesn't exist in input_content
85
+ if tool_call.id not in existing_tool_call_ids:
86
+ new_messages.append({
87
+ "role": "tool",
88
+ "content": content,
89
+ "tool_call_id": tool_call.id
90
+ })
91
+ if new_messages:
92
+ input_content.extend(new_messages)
93
+ else:
94
+ input_content.append({"role": "user", "content": content})
95
+
96
+ # Validate tool_calls and tool messages pairing
97
+ assistant_tool_calls = []
98
+ tool_responses = []
99
+ for msg in input_content:
100
+ if msg.get("role") == "assistant" and msg.get("tool_calls"):
101
+ assistant_tool_calls.extend(msg["tool_calls"])
102
+ elif msg.get("role") == "tool":
103
+ tool_responses.append(msg.get("tool_call_id"))
104
+
105
+ # Check if all tool_calls have corresponding responses
106
+ tool_call_ids = {call.id for call in assistant_tool_calls}
107
+ tool_response_ids = set(tool_responses)
108
+ if tool_call_ids != tool_response_ids:
109
+ missing_calls = tool_call_ids - tool_response_ids
110
+ extra_responses = tool_response_ids - tool_call_ids
111
+ error_msg = f"Tool calls and responses mismatch. Missing responses for tool_calls: {missing_calls}, Extra responses: {extra_responses}"
112
+ logger.error(error_msg)
113
+ raise ValueError(error_msg)
114
+
115
+ tool_calls = []
116
+ try:
117
+ llm_result = call_llm_model(self.llm, input_content, model=self.model_name,
118
+ tools=self.tools, temperature=0)
119
+ logger.info(f"Execute response: {llm_result.message}")
120
+ res = self.response_parse(llm_result)
121
+ content = res.actions[0].policy_info
122
+ tool_calls = llm_result.tool_calls
123
+ except Exception as e:
124
+ logger.warning(traceback.format_exc())
125
+ finally:
126
+ if llm_result:
127
+ ob = copy.deepcopy(observation)
128
+ ob.content = new_messages
129
+ self.trajectory.append((ob, info, llm_result))
130
+ else:
131
+ logger.warning("no result to record!")
132
+
133
+ res = []
134
+ if tool_calls:
135
+ for tool_call in tool_calls:
136
+ tool_action_name: str = tool_call.function.name
137
+ if not tool_action_name:
138
+ continue
139
+
140
+ names = tool_action_name.split("__")
141
+ tool_name = names[0]
142
+ action_name = '__'.join(names[1:]) if len(names) > 1 else ''
143
+ params = json.loads(tool_call.function.arguments)
144
+ res.append(ActionModel(agent_name=Agents.EXECUTE.value,
145
+ tool_name=tool_name,
146
+ action_name=action_name,
147
+ params=params))
148
+
149
+ if res:
150
+ res[0].policy_info = content
151
+ self._finished = False
152
+ elif content:
153
+ policy_info = extract_pattern(content, "final_answer")
154
+ if policy_info:
155
+ res.append(ActionModel(agent_name=Agents.EXECUTE.value,
156
+ policy_info=policy_info))
157
+ self._finished = True
158
+ else:
159
+ res.append(ActionModel(agent_name=Agents.EXECUTE.value,
160
+ policy_info=content))
161
+
162
+ logger.info(f">>> execute result: {res}")
163
+
164
+ result = AgentResult(actions=res,
165
+ current_state=None)
166
+ return result.actions
167
+
168
+
169
+ class PlanAgent(Agent):
170
+ def __init__(self, conf: Union[Dict[str, Any], ConfigDict, AgentConfig], **kwargs):
171
+ super(PlanAgent, self).__init__(conf, **kwargs)
172
+
173
+ def id(self) -> str:
174
+ return Agents.PLAN.value
175
+
176
+ def reset(self, options: Dict[str, Any]):
177
+ """Execute agent reset need query task as input."""
178
+ super().reset(options)
179
+
180
+ self.system_prompt = plan_system_prompt.format(task=self.task)
181
+ self.done_prompt = plan_done_prompt.format(task=self.task)
182
+ self.postfix_prompt = plan_postfix_prompt.format(task=self.task)
183
+ self.first_prompt = init_prompt
184
+ self.first = True
185
+ self.step_reset = False
186
+
187
+ async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> Union[
188
+ List[ActionModel], None]:
189
+ await self.async_desc_transform()
190
+ return self._common(observation, info)
191
+
192
+ def policy(self,
193
+ observation: Observation,
194
+ info: Dict[str, Any] = None,
195
+ **kwargs) -> List[ActionModel] | None:
196
+ self._finished = False
197
+ self.desc_transform()
198
+ return self._common(observation, info)
199
+
200
+ def _common(self, observation, info):
201
+ llm_result = None
202
+ input_content = [
203
+ {'role': 'system', 'content': self.system_prompt},
204
+ ]
205
+ # build input of llm based history
206
+ for traj in self.trajectory:
207
+ input_content.append({'role': 'user', 'content': traj[0].content})
208
+ # plan agent no tool to call, use content
209
+ input_content.append({'role': 'assistant', 'content': traj[-1].content})
210
+
211
+ message = observation.content
212
+ if self.first_prompt:
213
+ message = self.first_prompt
214
+ self.first_prompt = None
215
+
216
+ input_content.append({"role": "user", "content": message})
217
+ try:
218
+ llm_result = call_llm_model(self.llm, messages=input_content, model=self.model_name)
219
+ logger.info(f"Plan response: {llm_result.message}")
220
+ except Exception as e:
221
+ logger.warning(traceback.format_exc())
222
+ raise e
223
+ finally:
224
+ if llm_result:
225
+ ob = copy.deepcopy(observation)
226
+ ob.content = message
227
+ self.trajectory.append((ob, info, llm_result))
228
+ else:
229
+ logger.warning("no result to record!")
230
+ res = self.response_parse(llm_result)
231
+ content = res.actions[0].policy_info
232
+ if "TASK_DONE" not in content:
233
+ content += self.done_prompt
234
+ else:
235
+ # The task is done, and the assistant agent need to give the final answer about the original task
236
+ content += self.postfix_prompt
237
+ if not self.first:
238
+ self._finished = True
239
+
240
+ self.first = False
241
+ logger.info(f">>> plan result: {content}")
242
+ result = AgentResult(actions=[ActionModel(agent_name=Agents.PLAN.value,
243
+ tool_name=Agents.EXECUTE.value,
244
+ policy_info=content)],
245
+ current_state=None)
246
+ return result.actions
examples/plan_execute/mock.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+ import os
4
+
5
+ from aworld.utils.import_package import import_packages
6
+
7
+ import_packages(["pandas", "numpy"])
8
+
9
+ import pandas as pd
10
+ import numpy as np
11
+
12
+ from aworld.utils import import_package
13
+
14
+
15
+ def mock_dataset(name: str):
16
+ if name == 'gaia':
17
+ npy_path = f"{os.getcwd()}/gaia.npy"
18
+
19
+ numpy_array = np.load(npy_path, allow_pickle=True)
20
+ df = pd.DataFrame(numpy_array[:-1])
21
+ query = numpy_array[-1][0]
22
+
23
+ save_file_path = f"{os.getcwd()}/gaia.xlsx"
24
+ import_package("openpyxl")
25
+ df.to_excel(save_file_path, index=False, header=None)
26
+ return query.format(file_path=save_file_path)
27
+ return None
examples/plan_execute/prompts.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ init_prompt = f"""
2
+ Please give me clear step-by-step instructions to complete the entire task. If the task needs any special knowledge, let me know which tools I should use to help me get it done.
3
+ """
4
+
5
+ execute_system_prompt = """
6
+ ===== RULES FOR THE ASSISTANT =====
7
+ You are my assistant, and I am your user. Always remember this! Do not flip roles. You are here to help me. Do not give me instructions.
8
+ Use the tools available to you to solve the tasks I give you.
9
+ Our goal is to work together to successfully solve complex tasks.
10
+
11
+ The Task:
12
+ Our overall task is: {task}. Never forget this.
13
+
14
+ Instructions:
15
+ I will give you instructions to help solve the task. These instructions will usually be smaller sub-tasks or questions.
16
+ You must use your tools, do your best to solve the problem, and clearly explain your solutions.
17
+
18
+ How You Should Answer:
19
+ Always begin your response with: Solution: [YOUR_SOLUTION]
20
+ [YOUR_SOLUTION] should be clear, detailed, and specific. Provide examples, lists, or detailed implementations if needed.
21
+
22
+ Additional Notes:
23
+ Our overall task may be complicated. Here are tips to help you:
24
+ <tips>
25
+ - If one method fails, try another. There is always a solution.
26
+ - If a search snippet is not helpful, but the link is from a reliable source, visit the link for more details.
27
+ - For specific values like numbers, prioritize credible sources.
28
+ - Start with Wikipedia when researching, then explore other websites if needed.
29
+ - Solve math problems using Python and libraries like sympy. Test your code for results and debug when necessary.
30
+ - Validate your answers by cross-checking them through different methods.
31
+ - If a tool or code fails, do not assume its result is correct. Investigate the problem, fix it, and try again.
32
+ - Search results rarely provide exact answers. Use simple search queries to find sources, then process them further (e.g., by extracting webpage data).
33
+ - For downloading files, either use a browser simulation tool or write code to download them.
34
+ </tips>
35
+
36
+ Remember:
37
+ Your goal is to support me in solving the task successfully.
38
+ Unless I say the task is complete, always strive for a detailed, accurate, and useful solution.
39
+ """
40
+
41
+
42
+ plan_system_prompt = """
43
+ ===== USER INSTRUCTIONS =====
44
+ Remember that you are the user, and I am the assistant. I will always follow your instructions. We are working together to successfully complete a task.
45
+ My role is to help you accomplish a difficult task. You will guide me step by step based on my expertise and your needs. Your instructions should be in the following format: Instruction: [YOUR INSTRUCTION], where "Instruction" is a sub-task or question.
46
+ You should give me one instruction at a time. I will respond with a solution for that instruction. You should instruct me rather than asking me questions.
47
+
48
+ Please note that the task may be complex. Do not attempt to solve it all at once. You should break the task down and guide me step by step.
49
+ Here are some tips to help you give better instructions:
50
+ <tips>
51
+ - I have access to various tools like search, web browsing, document management, and code execution. Think about how humans would approach solving the task step by step, and give me instructions accordingly. For example, you may first use Google search to gather initial information and a URL, then retrieve the content from that URL, or interact with a webpage to find the answer.
52
+ - Even if the task is complex, there is always a solution. If you can’t find the answer using one method, try another approach or use different tools to find the solution.
53
+ - Always remind me to verify the final answer using multiple tools (e.g., screenshots, webpage analysis, etc.), or other methods.
54
+ - If I’ve written code, remind me to run it and check the results.
55
+ - Search results generally don’t give direct answers. Focus on finding sources through search, and use other tools to process the URL or interact with the webpage content.
56
+ - If the task involves a YouTube video, I will need to process the content of the video.
57
+ - For file downloads, use web browser tools or write code (e.g., download from a GitHub link).
58
+ - Feel free to write code to solve tasks like Excel-related tasks.
59
+ </tips>
60
+
61
+ Now, here is the overall task: <task>{task}</task>. Stay focused on the task!
62
+
63
+ Start giving me instructions step by step. Only provide the next instruction after I’ve completed the current one. When the task is finished, respond with <TASK_DONE>.
64
+ Do not say <TASK_DONE> until I’ve completed the task.
65
+ """
66
+
67
+ plan_done_prompt = """\n
68
+ Below is some additional information about the overall task that can help you better understand the purpose of the current task:
69
+ <auxiliary_information>
70
+ {task}
71
+ </auxiliary_information>
72
+ If there are any available tools that can assist with the task, instead of saying "I will...", first call the tool and respond based on the results it provides. Please also specify which tool you used.
73
+ """
74
+
75
+ plan_postfix_prompt = """\n
76
+ Now, please provide the final answer to the original task based on our conversation: <task>{task}</task>
77
+ Pay close attention to the required answer format. First, analyze the expected format based on the question, and then generate the final answer accordingly.
78
+ Your response should include the following:
79
+ - Analysis: Enclosed within <analysis> </analysis>, this section should provide a detailed breakdown of the reasoning process.
80
+ - Final Answer: Enclosed within <final_answer> </final_answer>, this section should contain the final answer in the required format.
81
+ Here are some important guidelines for formatting the final answer:
82
+ <hint>
83
+ - Your final answer must strictly follow the format specified in the question. The answer should be a single number, a short string, or a comma-separated list of numbers and/or strings:
84
+ - If the answer is a number, don't use commas as thousands separators, and don't include units (such as "$" or "%") unless explicitly required.
85
+ - If the answer is a string, don't include articles (e.g., "a", "the"), don't use abbreviations (e.g., city names), and write numbers in full words unless instructed otherwise.
86
+ - If the answer is a comma-separated list, apply the above rules based on whether each element is a number or a string.
87
+ </hint>
88
+ """
examples/plan_execute/run.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+
4
+ from aworld.config.conf import ModelConfig, AgentConfig
5
+ from aworld.core.agent.swarm import Swarm, GraphBuildType
6
+ from aworld.core.task import Task
7
+ from aworld.runner import Runners
8
+ from examples.plan_execute.agent import PlanAgent, ExecuteAgent
9
+ from examples.plan_execute.mock import mock_dataset
10
+ from examples.tools.common import Agents, Tools
11
+
12
+
13
+ def main():
14
+ test_sample = mock_dataset("gaia")
15
+
16
+ model_config = ModelConfig(
17
+ llm_provider="openai",
18
+ llm_temperature=1,
19
+ llm_model_name="gpt-4o",
20
+ # need to set llm_api_key for use LLM
21
+ )
22
+
23
+ agent1_config = AgentConfig(
24
+ name=Agents.PLAN.value,
25
+ llm_config=model_config
26
+ )
27
+ agent1 = PlanAgent(conf=agent1_config, step_reset=False)
28
+
29
+ agent2_config = AgentConfig(
30
+ name=Agents.EXECUTE.value,
31
+ llm_config=model_config
32
+ )
33
+ agent2 = ExecuteAgent(conf=agent2_config, step_reset=False, tool_names=[Tools.DOCUMENT_ANALYSIS.value])
34
+
35
+ # Create swarm for multi-agents
36
+ # define (head_node1, tail_node1), (head_node1, tail_node1) edge in the topology graph
37
+ swarm = Swarm((agent1, agent2), build_type=GraphBuildType.HANDOFF)
38
+
39
+ # Define a task
40
+ task_id = 'task'
41
+ task = Task(id=task_id, input=test_sample, swarm=swarm, endless_threshold=10)
42
+
43
+ # Run task
44
+ result = Runners.sync_run_task(task=task)
45
+
46
+ print(f"Time cost: {result[task_id].time_cost}")
47
+ print(f"Task Answer: {result[task_id].answer}")
48
+
49
+
50
+ if __name__ == '__main__':
51
+ main()
examples/plan_execute/utils.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from typing import Any, Dict, List, Literal, Optional, Union, Tuple
3
+ import logging as logger
4
+
5
+
6
+ def extract_pattern(content: str, pattern: str) -> Optional[str]:
7
+ try:
8
+ _pattern = fr"<{pattern}>(.*?)</{pattern}>"
9
+ match = re.search(_pattern, content, re.DOTALL)
10
+ if match:
11
+ text = match.group(1)
12
+ return text.strip()
13
+ else:
14
+ return None
15
+ except Exception as e:
16
+ logger.warning(f"Error extracting answer: {e}, current content: {content}")
17
+ return None
18
+