user input timeouts, tools consolidation
frdel committed
Jun 12, 2024 at 18:22 UTC
3f755277c4c68af5feea2895e24fe21f426cce6d
13 files changed
+88
-54
agent.py
+9
-5
@@ -7,7 +7,6 @@ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
7
from langchain_core.messages import HumanMessage
8
from langchain_core.language_models.chat_models import BaseChatModel
9
10
-
10
rate_limit = rate_limiter.rate_limiter(30,160000) #TODO! move to main.py
11
12
class Agent:
@@ -28,7 +27,7 @@ class Agent:
27
messages_returned=memory_results,
28
subdir=memory_subdir )
29
31
- def __init__(self, system_prompt:Optional[str]=None, tools_prompt:Optional[str]=None, superior:Optional['Agent']=None, number=0):
30
+ def __init__(self, system_prompt:Optional[str]=None, tools_prompt:Optional[str]=None, number=0):
31
32
self.number = number
33
self.name = f"Agent {self.number}"
@@ -38,15 +37,14 @@ class Agent:
37
self.system_prompt = system_prompt.replace("{", "{{").replace("}", "}}")
38
self.tools_prompt = tools_prompt.replace("{", "{{").replace("}", "}}")
39
41
- self.superior: Optional['Agent'] = superior
42
-
43
- self.subordinate: Optional['Agent'] = None
40
self.history = []
41
self.last_message = ""
42
self.intervention_message = ""
43
self.intervention_status = False
44
self.stop_loop = False
45
self.loop_result = ""
46
+
47
+ self.data = {} # free data object all the tools can use
48
49
self.prompt = ChatPromptTemplate.from_messages([
50
("system", self.system_prompt + "\n\n" + self.tools_prompt),
@@ -111,6 +109,12 @@ class Agent:
109
110
finally:
111
Agent.streaming_agent = None # unset current streamer
112
+
113
+ def get_data(self, field:str):
114
+ return self.data.get(field, None)
115
+
116
+ def set_data(self, field:str, value):
117
+ self.data[field] = value
118
119
def append_message(self, msg: str, human: bool = False):
120
message_type = "human" if human else "ai"
docs/splash_wide.png
Binary files a/docs/splash_wide.png and b/docs/splash_wide.png differ
main.py
+24
-6
@@ -3,6 +3,9 @@ from ansio import application_keypad, mouse_input, raw_input
3
from ansio.input import InputEvent, get_input_event
4
from agent import Agent
5
from tools.helpers.print_style import PrintStyle
6
+from tools.helpers.files import read_file
7
+from pytimedinput import timedInput as timed_input
8
+
9
10
input_lock = threading.Lock()
11
@@ -12,7 +15,7 @@ def chat():
15
# chat model used for agents
16
# chat_llm = models.get_groq_llama70b(temperature=0.2)
17
# chat_llm = models.get_openai_gpt35(temperature=0)
15
- chat_llm = models.get_openai_gpt4o()
18
+ chat_llm = models.get_openai_gpt4o(temperature=0)
19
# chat_llm = models.get_anthropic_sonnet(temperature=0)
20
# chat_llm = models.get_anthropic_haiku()
21
# chat_llm = models.get_ollama_dolphin()
@@ -32,15 +35,30 @@ def chat():
35
# create the first agent
36
agent0 = Agent()
37
35
- # start the conversation loop
38
+ # start the conversation loop
39
while True:
40
# ask user for message
38
- PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ('exit' to leave):")
41
with input_lock:
40
- user_input = input("> ").strip()
41
- PrintStyle(font_color="white", padding=False, log_only=True).print(f"> {user_input}")
42
+ timeout = agent0.get_data("timeout") # how long the agent is willing to wait
43
+ if not timeout: # if agent wants to wait for user input forever
44
+ PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ('exit' to leave):")
45
+ user_input = input("> ")
46
+ PrintStyle(font_color="white", padding=False, log_only=True).print(f"> {user_input}")
47
+
48
+ else: # otherwise wait for user input with a timeout
49
+ PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ({timeout}s timeout, 'exit' to leave):")
50
+ user_input = timed_input("> ", timeout=timeout)
51
+
52
+
53
+ if user_input[1]:
54
+ user_input = read_file("prompts/fw.msg_timeout.md")
55
+ PrintStyle(font_color="white", padding=False).stream(f"{user_input}")
56
+ else:
57
+ user_input = user_input[0].strip()
58
+ PrintStyle(font_color="white", padding=False, log_only=True).print(f"> {user_input}")
59
+
60
+
61
43
-
62
# exit the conversation when the user types 'exit'
63
if user_input.lower() == 'exit': break
64
prompts/agent.system.md
+18
-14
@@ -9,35 +9,33 @@
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.
12
+ - <message_for_user$ response_required="false"> - Message sent to the user. If you want the user to respond to you, use response_required="true", if you only want to inform user and not expecting response, use response_required="false". When asking questions or sending task result, use true. Only use "true" when your following action depends on the user response.
13
- <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.
14
+ - <task_done$> - Your task is done and you are sending the results to the user.
15
- And all other tools described in the Available tools section.
16
- Your response content is inside the tag.
17
- 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.
18
+- Never send your thoughts as messages, always use <thought> for that.
19
+
20
+## Communication examples:
21
+These examples are for illustration purposes only. Do not reuse any of these examples literally.
22
+
23
**Example response 1**:
24
<thought$>
25
The user asked for my name. I will respond with my name.
26
</thought$>
27
28
**Example response 2**:
25
-<question_for_user$>
29
+<messae_for_user$ response_required="true">
30
Greetings! How can I assist you today?
31
</question_for_user$>
32
33
**Example response 3**:
30
-<message_for_user$>
31
-I will now proceed with step 4.
34
+<message_for_user$ response_required="false">
35
+Step 3 done, proceeding to step 4.
36
</message_for_user$>
37
38
**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**:
39
<message_for_subordinate$>
40
I need you to use your tools and get me the current day of the week.
41
</message_for_subordinate$>
@@ -53,16 +51,21 @@ I need you to use your tools and get me the current day of the week.
51
- Explain each step using your <thought$>.
52
53
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.
54
+2. Check your online_knowledge_tool.
55
+ - Look for straightforward solutions compatible with your available tools.
56
+ - Always look for opensource python/nodejs/terminal tools and packages first.
57
3. Break task into subtasks by asking yourself the following questions. If they are positive, break task into subtasks and explain them.
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.
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.
62
+ - Collect results from subordinate agent and validate completeness and correctness. Communicate followup request to your subordinate if needed.
63
+ - Regurarly report back to your user and check your path is correct.
64
+ - If you are contacted by your subordinate, steer him to the right path.
65
5. Completing the task
66
- Consolidate all subtasks and explain the status.
67
- Verify the result using your tools if possible (check created files etc.)
68
+ - Do not accept failure, search for error solution and try again with fixed input or different ways.
69
- If there is helpful information discovered during the solution, save it into your memory using memory_tool for later.
70
- 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.
71
@@ -72,6 +75,7 @@ I need you to use your tools and get me the current day of the week.
75
- Always check your previous messages and prevent repetition. Always move towards solution.
76
- Avoid solutions that require credentials, user interaction, GUI usage etc. All has to be done using code and terminal.
77
- When asked about your memory, it always refers to <memory_tool$>.
78
+- If you have no task to process given by the user, respond back to him using <task_done$> and ask for a new task.
79
80
# Tips and tricks
81
- Focus on python/nodejs/linux libraries when searching for solutions. You can use them with your tools and make solutions easy.
prompts/fw.msg_info_sent.md
+1
-1
@@ -1,2 +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
2
+If you required user interaction use response_required="true" instead.
\ No newline at end of file
prompts/fw.msg_timeout.md
new
+1
@@ -0,0 +1 @@
1
+User is not responding to your message. Continue on your own.
\ No newline at end of file
requirements.txt
+2
-1
@@ -7,4 +7,5 @@ langchain-community==0.2.4
7
langchain-anthropic==0.1.15
8
langchain-chroma==0.1.1
9
webcolors==24.6.0
10
-sentence-transformers==3.0.1
\ No newline at end of file
10
+sentence-transformers==3.0.1
11
+pytimedinput==2.0.1
tools/helpers/print_style.py
+2
-2
@@ -22,7 +22,7 @@ class PrintStyle:
22
log_filename = datetime.now().strftime("log_%Y%m%d_%H%M%S.html")
23
PrintStyle.log_file_path = os.path.join(logs_dir, log_filename)
24
with open(PrintStyle.log_file_path, "w") as f:
25
- f.write("<html><body style='background-color:black;font-family: Arial, Helvetica, sans-serif;'>\n")
25
+ f.write("<html><body style='background-color:black;font-family: Arial, Helvetica, sans-serif;'><pre>\n")
26
27
def _get_rgb_color_code(self, color, is_background=False):
28
try:
@@ -87,7 +87,7 @@ class PrintStyle:
87
def _close_html_log():
88
if PrintStyle.log_file_path:
89
with open(PrintStyle.log_file_path, "a") as f:
90
- f.write("</body></html>")
90
+ f.write("</pre></body></html>")
91
92
def get(self, *args, sep=' ', **kwargs):
93
text = sep.join(map(str, args))
tools/message_for_subordinate.py
+7
-3
@@ -1,6 +1,10 @@
1
from agent import Agent
2
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.message_loop(message)
\ No newline at end of file
4
+ # create subordinate agent using the data object on this agent and set superior agent to his data object
5
+ if agent.get_data("subordinate") is None or reset.lower().strip() == "true":
6
+ subordinate = Agent(system_prompt=agent.system_prompt, tools_prompt=agent.tools_prompt, number=agent.number+1)
7
+ subordinate.set_data("superior", agent)
8
+ agent.set_data("subordinate", subordinate)
9
+ # run subordinate agent message loop
10
+ return agent.get_data("subordinate").message_loop(message)
\ No newline at end of file
tools/message_for_user.py
+17
-8
@@ -2,15 +2,24 @@ 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):
5
+def execute(agent:Agent, message: str, response_required: str = "false", timeout=15, **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}")
7
+ agent.set_data("timeout", timeout) # set the no-timeout flag
8
+
9
+ if response_required.lower().strip() == "true":
10
+ agent.stop_loop = True
11
+ agent.loop_result = message
12
+
13
+ return files.read_file("./prompts/fw.msg_sent.md")
14
+
15
+ else:
16
+ if agent.get_data("superior"): # add to superior messages if it is an agent
17
+ files.read_file("./prompts/fw.msg_from_subordinate.md",name=agent.name,message=message)
18
+ agent.get_data("superior").append_message(message, human=True)
19
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)
20
+ # output to console
21
+ PrintStyle(font_color="white",background_color="#1D8348", bold=True, padding=True).print(f"{agent.name}: reponse:")
22
+ PrintStyle(font_color="white").print(f"{message}")
23
15
- return files.read_file("./prompts/fw.msg_info_sent.md")
24
+ return files.read_file("./prompts/fw.msg_info_sent.md")
25
tools/question_for_user.py
deleted
-7
@@ -1,7 +0,0 @@
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
deleted
-7
@@ -1,7 +0,0 @@
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/task_done.py
new
+7
@@ -0,0 +1,7 @@
1
+from agent import Agent
2
+from tools.helpers import files
3
+from . import message_for_user
4
+
5
+def execute(agent:Agent, message: str, **kwargs):
6
+ # forward to message_for_user with no-timeout flag
7
+ return message_for_user.execute(agent, message, response_required="true", timeout=0, **kwargs)
\ No newline at end of file