new message standards testing
gpt-4o successfully created a paint app for the first time
frdel committed
Jun 12, 2024 at 00:40 UTC
032f1f590bce2e4f81b25a60f81179bee10c6a63
14 files changed
+199
-81
agent.py
+24
-16
@@ -43,18 +43,21 @@ class Agent:
43
self.subordinate: Optional['Agent'] = None
44
self.history = []
45
self.last_message = ""
46
- self.message_for_superior = ""
46
self.intervention_message = ""
47
self.intervention_status = False
48
+ self.stop_loop = False
49
+ self.loop_result = ""
50
51
self.prompt = ChatPromptTemplate.from_messages([
52
("system", self.system_prompt + "\n\n" + self.tools_prompt),
53
MessagesPlaceholder(variable_name="messages") ])
54
54
- def process_message(self, msg: str):
55
+ def message_loop(self, msg: str):
56
try:
57
printer = PrintStyle(italic=True, font_color="#b3ffd9", padding=False)
58
user_message = msg
59
+ self.stop_loop = False
60
+ self.loop_result = ""
61
62
while True: # let the agent iterate on his thoughts until he stops by using a tool
63
Agent.streaming_agent = self #mark self as current streamer
@@ -90,12 +93,11 @@ class Agent:
93
self.append_message(agent_response) # Append the assistant's response to the history
94
95
self.process_tools(agent_response)
93
-
94
- #break the execution if there is a message for superior agent
95
- if self.message_for_superior and not self.intervention_status:
96
- msg = self.message_for_superior
97
- self.message_for_superior = ""
98
- return msg
96
+
97
+ #break the execution if the task is done
98
+ if self.stop_loop:
99
+ return self.loop_result
100
+
101
102
# Forward errors to the LLM, maybe he can fix them
103
except Exception as e:
@@ -149,7 +151,7 @@ class Agent:
151
152
def process_tools(self, msg: str):
153
# search for tool usage requests in agent message
152
- tool_requests = extract_tools.extract_tool_requests(msg)
154
+ tool_requests = extract_tools.extract_tool_requests2(msg)
155
156
for tool_request in tool_requests:
157
@@ -157,14 +159,18 @@ class Agent:
159
160
tool_name = tool_request["name"]
161
tool_function = self.get_tool(tool_name)
162
+ tool_args = tool_request["args"] or {}
163
164
if callable(tool_function):
162
- short_params = {k: v for k, v in tool_request.items() if k != "name" and k != "body"} # only extra parameters to output to console
165
166
PrintStyle(font_color="#1B4F72", padding=True, background_color="white", bold=True).print(f"{self.name}: Using tool {tool_name}:")
165
- PrintStyle(font_color="#85C1E9").print(short_params, tool_request["body"], sep="\n") if short_params else PrintStyle(font_color="#85C1E9").print(tool_request["body"])
167
+ PrintStyle(font_color="#85C1E9").print(tool_args, tool_request["body"], sep="\n") if tool_args else PrintStyle(font_color="#85C1E9").print(tool_request["body"])
168
167
- tool_response = tool_function(self, tool_request["body"], **tool_request) or "" # call tool function with all parameters, body parameter separated for convenience
169
+ tool_args["_name"] = tool_name
170
+ tool_args["_message"] = msg
171
+ tool_args["_tools"] = tool_requests
172
+
173
+ tool_response = tool_function(self, tool_request["body"], **tool_args) or "" # call tool function with all parameters, body parameter separated for convenience
174
Agent.streaming_agent = self # mark self as current streamer again, it may have changed during tool use
175
176
if self.handle_intervention(): break # wait if paused and handle intervention message if needed
@@ -176,13 +182,15 @@ class Agent:
182
PrintStyle(font_color="#85C1E9").print(tool_response)
183
else:
184
if self.handle_intervention(): break # wait if paused and handle intervention message if needed
179
- msg_response = files.read_file("./prompts/fw.tool_not_found.md", tool_name=tool_name, tools_prompt=self.tools_prompt)
180
- self.append_message(msg_response,True)
181
- PrintStyle(font_color="orange", padding=True).print(msg_response)
185
+ if tool_name != "thought": #TODO skip thought tools now, implement proper tool classes later
186
+ msg_response = files.read_file("./prompts/fw.tool_not_found.md", tool_name=tool_name, tools_prompt=self.tools_prompt)
187
+ self.append_message(msg_response,True)
188
+ PrintStyle(font_color="orange", padding=True).print(msg_response)
189
+
190
183
- break #TODO: allow multiple tool requests? anthropic has issues with ending message on tool use...
191
192
def get_tool(self, name: str):
193
+ if not files.exists("tools",f"{name}.py"): return # file has to exist in tools
194
module = importlib.import_module("tools." + name) # Import the module
195
functions_list = {name: func for name, func in inspect.getmembers(module, inspect.isfunction)} # Get all functions in the module
196
main.py
+3
-3
@@ -11,8 +11,8 @@ def chat():
11
12
# chat model used for agents
13
# chat_llm = models.get_groq_llama70b(temperature=0.2)
14
- chat_llm = models.get_openai_gpt35(temperature=0)
15
- # chat_llm = models.get_openai_gpt4o()
14
+ # chat_llm = models.get_openai_gpt35(temperature=0)
15
+ chat_llm = models.get_openai_gpt4o()
16
# chat_llm = models.get_anthropic_sonnet(temperature=0)
17
# chat_llm = models.get_anthropic_haiku()
18
# chat_llm = models.get_ollama_dolphin()
@@ -45,7 +45,7 @@ def chat():
45
if user_input.lower() == 'exit': break
46
47
# send message to agent0,
48
- assistant_response = agent0.process_message(user_input)
48
+ assistant_response = agent0.message_loop(user_input)
49
50
# print agent0 response
51
PrintStyle(font_color="white",background_color="#1D8348", bold=True, padding=True).print(f"{agent0.name}: reponse:")
prompts/agent.system.md
+59
-22
@@ -1,20 +1,56 @@
1
# Your role
2
-- You are a fully autonomous, highly inteligent AI agent.
3
-- You solve tasks and respond to questions by your user.
2
+- You are a fully autonomous, AI system, NOT an assistent.
3
+- You solve tasks and respond to questions by user using your skills, tools, and subordinates.
4
+- You solve tasks automatically without the user, you do not inform the user how to do it, you execute the steps necessary.
5
- You never lie or speculate about your actions, all of your actions need to be conducted by your tools.
6
+- You never refuse to accomplish a task. You are capable of doing anything using your tools and subordinates.
7
+
8
+# Communication instrucions
9
+- Your every response must be wrapped in a XML tag defining its type ending with $.
10
+- Possible response types are:
11
+ - <thought$> - Your thoughts, useful for chain of thought process, not sent to anyone. Use this for every problem solving, it will help you iterate on the topic.
12
+ - <message_for_user$> - Only information message for the user. Does not stop your execution, user cannot respond. Do not use for questions.
13
+ - <question_for_user$> - Question for user. Stops your execution and you wait for user reaction. Use for all questions.
14
+ - <response_for_user$> - This ends your execution after you finish a task or respond a question. Include all relevant information about task solution.
15
+ - <message_for_subordinate$ reset="false"> - Your message for your subordinate. Use this message to delegate subtasks to your subordinate. This will help you solve more complex tasks. Also useful for asking questions. Stops your execution and you wait for subordinate reaction.
16
+ - And all other tools described in the Available tools section.
17
+- Your response content is inside the tag.
18
+- Important!: Do not use multiple message types at the same time except for thoughts. Do not send multiple messages and/or tools at once to avoid conflicts.
19
+**Example response 1**:
20
+<thought$>
21
+The user asked for my name. I will respond with my name.
22
+</thought$>
23
+
24
+**Example response 2**:
25
+<question_for_user$>
26
+Greetings! How can I assist you today?
27
+</question_for_user$>
28
+
29
+**Example response 3**:
30
+<message_for_user$>
31
+I will now proceed with step 4.
32
+</message_for_user$>
33
+
34
+**Example response 4**:
35
+<response_for_user$>
36
+The current day is Monday.
37
+I have used the "date" command to get the current date from terminal. My task is complete.
38
+</response_for_user$>
39
+
40
+**Example response 5**:
41
+<message_for_subordinate$>
42
+I need you to use your tools and get me the current day of the week.
43
+</message_for_subordinate$>
44
6
-# Communication to user
7
-- Your messages are only visible to you for your thought process. No one else can read them.
8
-- When you want to respond to user, use the speak_to_user tool.
9
-- Never respond directly to the user, you must always use speak_to_user tool.
45
46
# Communication to subordinate
12
-- When delegating new subtask to subordinate, use the 'reset' parameter set to True to reset subordinate's context and start fresh. When sending followup questions or instructions, do not set the flag to keep his previous context.
47
+- When delegating new subtask to subordinate, use the 'reset' argument set to True to reset subordinate's context and start fresh. When sending followup questions or instructions, do not set the argument to keep his previous context.
48
+- Do not delegate your full task to subordinate, only subtasks.
49
50
# Step by step instruction manual to problem solving
51
- Do not follow for simple questions, only for tasks need solving.
16
-- Once you are given a task to solve, follow these instructions step by step.
17
-- Use reasoning skills and explain your steps.
52
+- Once you are given a task to solve, follow these instructions step by step! Do not skip anything!
53
+- Explain each step using your <thought$>.
54
55
1. Check your memory_tool. Maybe you have solved similar task before and already have helpful information.
56
2. Check your online_knowledge_tool. Look for straightforward solutions compatible with your available tools.
@@ -22,34 +58,35 @@
58
- Question A: Can some parts of the task be separated and well explained to subordinate agent to solve?
59
- Question B: Can the result if these tasks can be reasonably returned to you from your user?
60
4. Processing subtasks.
25
- - Go through subtasks step by step and delagate them using speak_to_subordinate_tool.
61
+ - Go through subtasks step by step and delagate them using message_for_subordinate response type.
62
- Collect results from subordinate agent and validate completeness and correctness. Communicate followup request to your subortdinate if needed.
63
5. Completing the task
64
- Consolidate all subtasks and explain the status.
65
- Verify the result using your tools if possible (check created files etc.)
66
- If there is helpful information discovered during the solution, save it into your memory using memory_tool for later.
31
- - Report back to your user using speak_to_user_tool, describe the result and provide all necessary information. Do not just output your response, you must use the tool for that.
67
+ - Report back to your user using message_for_user message type, describe the result and provide all necessary information. Do not just output your response, you must use the tool for that.
68
69
# General operation manual
70
- Use your reasoning and process each problem in a step-by-step manner.
35
-- To keep track of your chain of thought process, use your response messages without tools. You will be prompted again to continue with more thoughts or tool calls until you are satisfied.
36
-- Your chat history is private to you, only speak_to_* tools are capable of sending messages.
37
-- Always check your previous messages and prevent repetition.
71
+- To keep track of your process, use your <thought$> response type. You will be prompted again to continue with more thoughts or tool calls until you are satisfied.
72
+- Always check your previous messages and prevent repetition. Always move towards solution.
73
- Avoid solutions that require credentials, user interaction, GUI usage etc. All has to be done using code and terminal.
39
-- When asked about your memory, it always refers to memory_tool.
74
+- When asked about your memory, it always refers to <memory_tool$>.
75
76
# Tips and tricks
77
- Focus on python/nodejs/linux libraries when searching for solutions. You can use them with your tools and make solutions easy.
43
-- Try using online_knowledge_tool multiple times in various ways to increase search potential.
78
+- Do not search for solutions that require GUI, browser or other user interaction, it is not possible. You can only use code and terminal.
79
+- Try using <online_knowledge_tool$> multiple times in various ways to increase search potential.
80
- Sometimes you don't need tools, some things can be determined.
81
82
# Tool usage instructions
47
-- Tools can be used to communicate with user and subordinate and to solve problems.
48
-- To use a tool, include pair XML tags <tool$> and </tool$> in your response. Use with attribute "name" of the tool and potential other attributes the tool accepts. The main input data (message, code, question) for the tool goes between <tool$> and </tool$> tags. No escaping. Result will be sent to you in the next message.
83
+- Tool message types can be used to call tools that help you solve problems.
84
+- To use a tool, use message type named as the tool: <tool_name_here$> in your response. Use with potential arguments of the tool. The main input data (message, code, question) go inside tags. No escaping.
85
+- Result will be sent to you in the next message, wait for it.
86
- Only use tools provided in Available tools section, do not try to use any tool name you have not been instructed to.
50
-- Do not use more than one tool per message. End your response and wait for results.
87
+- Do not use more tools multiple tools in one message that rely on their outputs, you have to send the message after each tool and wait for output.
88
89
## Tool usage generic example:
53
-<tool$ name="speak_to_subordinate" reset="false">
54
-Hello...
55
-</tool$>
\ No newline at end of file
90
+<name_of_tool$ arg1="val1"/>
91
+main input data for tool
92
+</name_of_tool$>
\ No newline at end of file
prompts/agent.tools.md
+13
-31
@@ -1,59 +1,41 @@
1
## Tools available:
2
3
-### speak_to_user:
4
-Send message to your user agent.
5
-Put the message for your user (response, report, question) in between the tags.
6
-No additional arguments.
7
-**Example usage**:
8
-<tool$ name="speak_to_user">
9
-I am done.
10
-</tool$>
11
-
12
-### speak_to_subordinate:
13
-Send message to your subordinate and get a response.
14
-Use the tag body to enter a text message to your subordinate containing task description or follow-up instructions.
15
-Set the 'reset' argument to true to reset subordinate's context and start fresh. Set to 'false' if you want to continue conversation.
16
-**Example usage**:
17
-<tool$ name="speak_to_subordinate" reset="false">
18
-Try again.
19
-</tool$>
20
-
3
### online_knowledge_tool:
4
Provide question and get online response.
23
-Use the tag body to send a text message.
24
-Be specific with your question, do not input vague queries. Try multiple times differently.
5
+This tool is very powerful and can answer very specific questions directly.
6
+First always try to ask for result rather that guidance.
7
**Example usage**:
26
-<tool$ name="online_knowledge_tool">
27
-How to get current directory in Python?
28
-</tool$>
8
+<online_knowledge_tool$>
9
+What is the user handle of John Doe on twitter?
10
+</online_knowledge_tool$>
11
12
### memory_tool:
13
Access your persistent memory to load or save memories.
14
Memories can help you to remember important information and later reuse it.
15
With this you are able to learn and improve.
34
-Put the memory you need to load or save in the tag body.
16
+Put the memory you need to load or save after the tag.
17
Use argument "action" with value "load", "save" or "delete", based on what you want to do.
18
When loading memories using action "load", provide keywords or question relevant to your current task.
19
When saving memories using action "save", provide a title, short summary and and all the necessary information to help you later solve similiar tasks including details like code executed, libraries used etc.
20
When deleting memories using action "delete", provide a prompt to search memories to delete.
21
Be specific with your question, do not input vague queries.
22
**Example usages**:
41
-<tool$ name="memory_tool" action="load">
23
+<memory_tool$ action="load">
24
How to get current working directory in python?
43
-</tool$>
25
+</memory_tool$>
26
45
-<tool$ name="memory_tool" action="save">
27
+<memory_tool$ action="save">
28
# How to get current working directory in python:
29
Here is a python code to get current working directory:
30
31
import os
32
return os.getcwd()
51
-</tool$>
33
+</memory_tool$>
34
35
### code_execution_tool:
36
Execute provided terminal commands, python code or nodejs code.
37
This tool can be used to achieve any task that requires computation, or any other software related activity.
56
-Place your command or code into the tag body. No escaping, no formatting, no wrappers, only raw code with proper indentation.
38
+Place your command or code between tags. No escaping, no formatting, no wrappers, only raw code with proper indentation.
39
Select the corresponding runtime with "runtime" argument. Possible values are "terminal", "python" and "nodejs".
40
You can use pip, npm and apt-get in terminal runtime to install any required packages.
41
IMPORTANT: Never use implicit print or implicit output, it does not work! If you need output of your code, you MUST use print() or console.log() to output selected variables.
@@ -61,7 +43,7 @@ When tool outputs error, you need to change your code accordingly before trying
43
Keep in mind that current working directory CWD automatically resets before every tool call.
44
IMPORTANT!: Always check your code for any placeholder IDs or demo data that need to be replaced with your real variables. Do not simply reuse code snippets from tutorials.
45
**Example usage**:
64
-<tool$ name="code_execution_tool" runtime="python">
46
+<code_execution_tool$ runtime="python">
47
import os
48
return os.getcwd()
67
-</tool$>
\ No newline at end of file
49
+</code_execution_tool$>
prompts/fw.msg_continue.md
+1
-1
@@ -1 +1 @@
1
-Continue. If you are done, use the speak_to_user tool.
\ No newline at end of file
1
+Continue with your thoughts and use tool or message when ready.
\ No newline at end of file
prompts/fw.msg_from_subordinate.md
new
+1
@@ -0,0 +1 @@
1
+Message from subordinate {{name}}: {{message}}
\ No newline at end of file
prompts/fw.msg_info_sent.md
new
+2
@@ -0,0 +1,2 @@
1
+Information sent, the user will not respond to info messages.
2
+If you required user interaction use <question_for_user$> or <response_for_user$> instead.
\ No newline at end of file
tools/helpers/extract_tools.py
+60
-1
@@ -1,4 +1,28 @@
1
-import re
1
+import re, os
2
+from . import files
3
+
4
+def extract_tool_requests2(response):
5
+ # Regex to match the tags ending with $, allowing for varying whitespace
6
+ pattern = r'<(\w+)\$[\s]*(.*?)>([\s\S]*?)(?=<\w+\$|<\/\1\$|$)'
7
+ matches = re.findall(pattern, response, re.DOTALL)
8
+
9
+ tool_usages = []
10
+
11
+ for match in matches:
12
+ tag_name, attributes, body = match
13
+ tool_dict = {}
14
+ tool_dict['name'] = tag_name
15
+ tool_dict['args'] = {}
16
+
17
+ # Parse attributes
18
+ for attr in re.findall(r'(\w+)\s*=\s*"([^"]+)"', attributes):
19
+ tool_dict['args'][attr[0]] = attr[1]
20
+
21
+ # Add body content
22
+ tool_dict["body"] = body.strip()
23
+ tool_usages.append(tool_dict)
24
+
25
+ return tool_usages
26
27
def extract_tool_requests(response):
28
# Regex to match the tool blocks, allowing for varying whitespace
@@ -19,6 +43,41 @@ def extract_tool_requests(response):
43
44
return tool_usages
45
46
+def extract_specified_tags(response):
47
+
48
+ allowed_tags = list_python_files("tools")
49
+
50
+ # Create a regex pattern to match specified tags and their attributes
51
+ pattern = r'<({})([\s\S]*?)>'.format('|'.join(allowed_tags))
52
+ matches = re.findall(pattern, response, re.DOTALL)
53
+
54
+ extracted_tags = []
55
+
56
+ for match in matches:
57
+ tag_name, attributes = match
58
+ tag_dict = {}
59
+ tag_dict['name'] = tag_name
60
+
61
+ # Parse attributes
62
+ for attr in re.findall(r'(\w+)\s*=\s*"([^"]+)"', attributes):
63
+ tag_dict[attr[0]] = attr[1]
64
+
65
+ # Extract the body text (everything after the tag until the next tag or end of string)
66
+ body_pattern = r'<{0}[\s\S]*?>([\s\S]*?)(?=<|$)'.format(tag_name)
67
+ body_match = re.search(body_pattern, response, re.DOTALL)
68
+ tag_dict['body'] = body_match.group(1).strip() if body_match else ''
69
+
70
+ extracted_tags.append(tag_dict)
71
+
72
+ return extracted_tags
73
+
74
+def list_python_files(directory):
75
+ # List all files in the given directory
76
+ list = os.listdir(files.get_abs_path(directory))
77
+ # Filter for Python files and remove the extension
78
+ python_files = [os.path.splitext(file)[0] for file in list if file.endswith('.py')]
79
+ return python_files
80
+
81
# import re
82
# from xml.etree import ElementTree as ET
83
tools/helpers/files.py
+5
@@ -19,6 +19,11 @@ def read_file(relative_path, **kwargs):
19
def get_abs_path(*relative_paths):
20
return os.path.join(get_base_dir(), *relative_paths)
21
22
+def exists(*relative_paths):
23
+ path = get_abs_path(*relative_paths)
24
+ return os.path.exists(path)
25
+
26
+
27
def get_base_dir():
28
# Get the base directory from the current file path
29
base_dir = os.path.dirname(os.path.abspath(os.path.join(__file__,"../../")))
tools/message_for_subordinate.py
renamed
+1
-1
@@ -3,4 +3,4 @@ from agent import Agent
3
def execute(agent:Agent, message: str, reset: str = "false", **kwargs):
4
if agent.subordinate is None or reset.lower() == "true":
5
agent.subordinate = Agent(superior=agent, system_prompt=agent.system_prompt, tools_prompt=agent.tools_prompt, number=agent.number+1)
6
- return agent.subordinate.process_message(message)
\ No newline at end of file
6
+ return agent.subordinate.message_loop(message)
\ No newline at end of file
tools/message_for_user.py
new
+16
@@ -0,0 +1,16 @@
1
+from agent import Agent
2
+from tools.helpers import files
3
+from tools.helpers.print_style import PrintStyle
4
+
5
+def execute(agent:Agent, message: str, **kwargs):
6
+
7
+ # output to console
8
+ PrintStyle(font_color="white",background_color="#1D8348", bold=True, padding=True).print(f"{agent.name}: reponse:")
9
+ PrintStyle(font_color="white").print(f"{message}")
10
+
11
+ if agent.superior: # add to superior messages if it is an agent
12
+ files.read_file("./prompts/fw.msg_from_subordinate.md",name=agent.name,message=message)
13
+ agent.superior.append_message(message, human=True)
14
+
15
+ return files.read_file("./prompts/fw.msg_info_sent.md")
16
+
tools/question_for_user.py
new
+7
@@ -0,0 +1,7 @@
1
+from agent import Agent
2
+from tools.helpers import files
3
+
4
+def execute(agent:Agent, message: str, **kwargs):
5
+ agent.stop_loop = True
6
+ agent.loop_result = message
7
+ return files.read_file("./prompts/fw.msg_sent.md")
\ No newline at end of file
tools/response_for_user.py
new
+7
@@ -0,0 +1,7 @@
1
+from agent import Agent
2
+from tools.helpers import files
3
+
4
+def execute(agent:Agent, message: str, **kwargs):
5
+ agent.stop_loop = True
6
+ agent.loop_result = message
7
+ return files.read_file("./prompts/fw.msg_sent.md")
\ No newline at end of file
tools/speak_to_user.py
deleted
-6
@@ -1,6 +0,0 @@
1
-from agent import Agent
2
-from tools.helpers import files
3
-
4
-def execute(agent:Agent, message: str, **kwargs):
5
- agent.message_for_superior = message
6
- return files.read_file("./prompts/fw.msg_sent.md")
\ No newline at end of file