Skip to content

Commit 69aa017

Browse files
committed
refactor(agent): 重构agent中的prompt模板
1).重构agent中的prompt模板,减少模板中的重复描述; 2).重构base_agent的模板加载逻辑,支持多目录搜索和jinja2渲染
1 parent 713824a commit 69aa017

File tree

6 files changed

+209
-562
lines changed

6 files changed

+209
-562
lines changed

src/agent/base_agent.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from typing import (
23
Any,
34
Callable,
@@ -31,7 +32,17 @@
3132
AgentImage,
3233
)
3334
from src.utils import assemble_project_path
35+
from src.utils.path_utils import assemble_project_path
36+
from src.memory.memory import AgentMemory # Remove PromptTemplates from this import
37+
from src.logger.logger import LogLevel
38+
import yaml
39+
from jinja2 import Environment, FileSystemLoader, TemplateNotFound, meta, Template
40+
from abc import ABC, abstractmethod
41+
from typing import Any, List, Dict, Union, Optional, Type
42+
43+
from src.logger import logger
3444

45+
from src.memory.memory import ActionStep
3546
class BaseAgent(AsyncMultiStepAgent):
3647
"""Base class for agents with common logic."""
3748
AGENT_NAME = "base_agent" # Must be overridden by subclasses
@@ -79,13 +90,34 @@ def __init__(
7990
)
8091

8192
# Loading prompt_templates
82-
if prompt_templates:
83-
self.prompt_templates = prompt_templates
93+
if prompt_templates_path:
94+
# template_dir is the directory of the specific agent's prompt file
95+
template_dir = os.path.dirname(prompt_templates_path)
96+
template_filename = os.path.basename(prompt_templates_path)
97+
98+
# Determine the common prompts directory relative to this file (base_agent.py)
99+
# __file__ is .../src/agent/base_agent.py
100+
# common_prompts_dir should be .../src/base/prompts/
101+
current_file_dir = os.path.dirname(os.path.abspath(__file__)) # .../src/agent
102+
src_dir = os.path.dirname(current_file_dir) # .../src
103+
common_prompts_dir = os.path.join(src_dir, "base", "prompts")
104+
105+
# Add both the specific agent's template directory and the common prompts directory to the search path
106+
# Also adding trim_blocks and lstrip_blocks for cleaner template output
107+
env = Environment(
108+
loader=FileSystemLoader(searchpath=[template_dir, common_prompts_dir]),
109+
trim_blocks=False,
110+
lstrip_blocks=False
111+
)
112+
template = env.get_template(template_filename)
113+
114+
# expanded_yaml_str = expand_jinja_macros_in_yaml(template)
115+
# print(expanded_yaml_str)
116+
117+
rendered_yaml = template.render() # You can pass variables here if needed
118+
self.prompt_templates = yaml.safe_load(rendered_yaml)
84119
else:
85-
abs_template_path = assemble_project_path(prompt_templates_path)
86-
with open(abs_template_path, "r", encoding='utf-8') as f:
87-
self.prompt_templates = yaml.safe_load(f)
88-
120+
self.prompt_templates = prompt_templates
89121
self.system_prompt = self.initialize_system_prompt()
90122
self.user_prompt = self.initialize_user_prompt()
91123

Lines changed: 6 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,7 @@
1-
system_prompt: |-
2-
You are an expert assistant who can solve any task using tool calls. You will be given a task to solve as best you can.
3-
To do so, you have been given access to some tools.
4-
5-
The tool call you write is an action: after the tool is executed, you will get the result of the tool call as an "observation".
6-
This Action/Observation can repeat N times, you should take several steps when needed.
7-
8-
You can use the result of the previous action as input for the next action.
9-
The observation will always be a string: it can represent a file, like "image_1.jpg".
10-
Then you can use it as input for the next action. You can do it for instance as follows:
11-
12-
Observation: "image_1.jpg"
13-
14-
Action:
15-
{
16-
"name": "image_transformer",
17-
"arguments": {"image": "image_1.jpg"}
18-
}
19-
20-
To provide the final answer to the task, use an action blob with "name": "final_answer" tool. It is the only way to complete the task, else you will be stuck on a loop. So your final output should look like this:
21-
Action:
22-
{
23-
"name": "final_answer",
24-
"arguments": {"answer": "insert your final answer here"}
25-
}
26-
27-
28-
Here are a few examples using notional tools:
29-
---
30-
Task: "Generate an image of the oldest person in this document."
31-
32-
Action:
33-
{
34-
"name": "document_qa",
35-
"arguments": {"document": "document.pdf", "question": "Who is the oldest person mentioned?"}
36-
}
37-
Observation: "The oldest person in the document is John Doe, a 55 year old lumberjack living in Newfoundland."
38-
39-
Action:
40-
{
41-
"name": "image_generator",
42-
"arguments": {"prompt": "A portrait of John Doe, a 55-year-old man living in Canada."}
43-
}
44-
Observation: "image.png"
45-
46-
Action:
47-
{
48-
"name": "final_answer",
49-
"arguments": "image.png"
50-
}
51-
52-
---
53-
Task: "What is the result of the following operation: 5 + 3 + 1294.678?"
54-
55-
Action:
56-
{
57-
"name": "python_interpreter",
58-
"arguments": {"code": "5 + 3 + 1294.678"}
59-
}
60-
Observation: 1302.678
1+
{% import "_common_agent_prompt_parts.yaml" as common_parts %}
612

62-
Action:
63-
{
64-
"name": "final_answer",
65-
"arguments": "1302.678"
66-
}
67-
68-
---
69-
Task: "Which city has the highest population , Guangzhou or Shanghai?"
70-
71-
Action:
72-
{
73-
"name": "search",
74-
"arguments": "Population Guangzhou"
75-
}
76-
Observation: ['Guangzhou has a population of 15 million inhabitants as of 2021.']
77-
78-
79-
Action:
80-
{
81-
"name": "search",
82-
"arguments": "Population Shanghai"
83-
}
84-
Observation: '26 million (2019)'
85-
86-
Action:
87-
{
88-
"name": "final_answer",
89-
"arguments": "Shanghai"
90-
}
91-
92-
Above example were using notional tools that might not exist for you. You only have access to these tools:
93-
{%- for tool in tools.values() %}
94-
* {{ tool.name }}: {{ tool.description }}
95-
Takes inputs: {{tool.parameters.properties}}
96-
Returns an output of type: {{tool.output_type}}
97-
{%- endfor %}
98-
99-
{%- if managed_agents and managed_agents.values() | list %}
100-
101-
You can also give tasks to team members.
102-
Calling a team member works the same as for calling a tool: simply, the only argument you can give in the call is 'task', a long string explaining your task.
103-
Given that this team member is a real human, you should be very verbose in your task.
104-
Here is a list of the team members that you can call:
105-
{%- for agent in managed_agents.values() %}
106-
* {{ agent.name }}: {{ agent.description }}
107-
{%- endfor %}
108-
{%- endif %}
109-
110-
Here are the rules you should always follow to solve your task:
111-
1. ALWAYS provide a tool call, else you will fail.
112-
2. Always use the right arguments for the tools. Never use variable names as the action arguments, use the value instead.
113-
3. Call a tool only when needed: do not call the search agent if you do not need information, try to solve the task yourself.
114-
If no tool call or team member is needed, use `final_answer` tool to return your answer.
115-
4. Never re-do a tool call that you previously did with the exact same parameters.
116-
117-
Now Begin!
3+
system_prompt: |-
4+
{{ common_parts.common_system_prompt_intro() }}
1185
1196
task_instruction: |-
1207
You can search for the most relevant web pages and interact with them to accurately find answers to tasks.
@@ -123,33 +10,13 @@ task_instruction: |-
12310
* You can also use the `python_interpreter` tool to run any code to support your analysis.
12411
12512
Here is the task:
126-
{{task}}
13+
{% raw %}{{task}}{% endraw %}
12714
12815
user_prompt: |-
12916
You should think step by step to solve the task.
13017
13118
managed_agent:
132-
task: |-
133-
You're a helpful agent named '{{name}}'.
134-
You have been submitted this task by your manager.
135-
---
136-
{{task}}
137-
---
138-
You're helping your manager solve a wider task: so make sure to not provide a one-line answer, but give as much information as possible to give them a clear understanding of the answer.
139-
140-
Your `final_answer` WILL HAVE to contain these parts:
141-
### 1. Task outcome (short version):
142-
### 2. Task outcome (extremely detailed version):
143-
### 3. Additional context (if relevant):
19+
{{ common_parts.common_managed_agent_parts() }}
14420

145-
Put all these in your `final_answer` tool, everything that you do not pass as an argument to `final_answer` will be lost.
146-
And even if your task resolution is not successful, please return as much context as possible, so that your manager can act upon this feedback.
147-
report: |-
148-
Here is the final answer from your managed agent '{{name}}':
149-
{{final_answer}}
15021
final_answer:
151-
pre_messages: |-
152-
An agent tried to answer a user query but it got stuck and failed to do so. You are tasked with providing an answer instead. Here is the agent's memory:
153-
post_messages: |-
154-
Based on the above, please provide an answer to the following user task:
155-
{{task}}
22+
{{ common_parts.common_final_answer_parts() }}
Lines changed: 6 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,21 @@
1-
system_prompt: |-
2-
You are an expert assistant who can solve any task using tool calls. You will be given a task to solve as best you can.
3-
To do so, you have been given access to some tools.
4-
5-
The tool call you write is an action: after the tool is executed, you will get the result of the tool call as an "observation".
6-
This Action/Observation can repeat N times, you should take several steps when needed.
7-
8-
You can use the result of the previous action as input for the next action.
9-
The observation will always be a string: it can represent a file, like "image_1.jpg".
10-
Then you can use it as input for the next action. You can do it for instance as follows:
11-
12-
Observation: "image_1.jpg"
13-
14-
Action:
15-
{
16-
"name": "image_transformer",
17-
"arguments": {"image": "image_1.jpg"}
18-
}
19-
20-
To provide the final answer to the task, use an action blob with "name": "final_answer" tool. It is the only way to complete the task, else you will be stuck on a loop. So your final output should look like this:
21-
Action:
22-
{
23-
"name": "final_answer",
24-
"arguments": {"answer": "insert your final answer here"}
25-
}
26-
27-
28-
Here are a few examples using notional tools:
29-
---
30-
Task: "Generate an image of the oldest person in this document."
31-
32-
Action:
33-
{
34-
"name": "document_qa",
35-
"arguments": {"document": "document.pdf", "question": "Who is the oldest person mentioned?"}
36-
}
37-
Observation: "The oldest person in the document is John Doe, a 55 year old lumberjack living in Newfoundland."
38-
39-
Action:
40-
{
41-
"name": "image_generator",
42-
"arguments": {"prompt": "A portrait of John Doe, a 55-year-old man living in Canada."}
43-
}
44-
Observation: "image.png"
45-
46-
Action:
47-
{
48-
"name": "final_answer",
49-
"arguments": "image.png"
50-
}
51-
52-
---
53-
Task: "What is the result of the following operation: 5 + 3 + 1294.678?"
1+
{% import "_common_agent_prompt_parts.yaml" as common_parts %}
542

55-
Action:
56-
{
57-
"name": "python_interpreter",
58-
"arguments": {"code": "5 + 3 + 1294.678"}
59-
}
60-
Observation: 1302.678
61-
62-
Action:
63-
{
64-
"name": "final_answer",
65-
"arguments": "1302.678"
66-
}
67-
68-
---
69-
Task: "Which city has the highest population , Guangzhou or Shanghai?"
70-
71-
Action:
72-
{
73-
"name": "search",
74-
"arguments": "Population Guangzhou"
75-
}
76-
Observation: ['Guangzhou has a population of 15 million inhabitants as of 2021.']
77-
78-
79-
Action:
80-
{
81-
"name": "search",
82-
"arguments": "Population Shanghai"
83-
}
84-
Observation: '26 million (2019)'
85-
86-
Action:
87-
{
88-
"name": "final_answer",
89-
"arguments": "Shanghai"
90-
}
91-
92-
Above example were using notional tools that might not exist for you. You only have access to these tools:
93-
{%- for tool in tools.values() %}
94-
* {{ tool.name }}: {{ tool.description }}
95-
Takes inputs: {{tool.parameters.properties}}
96-
Returns an output of type: {{tool.output_type}}
97-
{%- endfor %}
98-
99-
{%- if managed_agents and managed_agents.values() | list %}
100-
101-
You can also give tasks to team members.
102-
Calling a team member works the same as for calling a tool: simply, the only argument you can give in the call is 'task', a long string explaining your task.
103-
Given that this team member is a real human, you should be very verbose in your task.
104-
Here is a list of the team members that you can call:
105-
{%- for agent in managed_agents.values() %}
106-
* {{ agent.name }}: {{ agent.description }}
107-
{%- endfor %}
108-
{%- endif %}
109-
110-
Here are the rules you should always follow to solve your task:
111-
1. ALWAYS provide a tool call, else you will fail.
112-
2. Always use the right arguments for the tools. Never use variable names as the action arguments, use the value instead.
113-
3. Call a tool only when needed: do not call the search agent if you do not need information, try to solve the task yourself.
114-
If no tool call or team member is needed, use `final_answer` tool to return your answer.
115-
4. Never re-do a tool call that you previously did with the exact same parameters.
116-
117-
Now Begin!
3+
system_prompt: |-
4+
{{ common_parts.common_system_prompt_intro() }}
1185
1196
task_instruction: |-
1207
You can analyze and solve any task based on attached file or uri.
1218
* Please use `deep_analyzer` tool to analyze and solve the task, and provide detailed reasoning and an answer. When you require to use it, please provide the original task as the `task` parameter for the tool. DO NOT modify the task.
1229
* When the task involves calculation and statistics for attached files or data, you can use the `python_interpreter` to run code to convert the data into a table at first. And then run the code to analyze the data.
12310
12411
Here is the task:
125-
{{task}}
12+
{% raw %}{{task}}{% endraw %}
12613
12714
user_prompt: |-
12815
You should think step by step to solve the task.
12916
13017
managed_agent:
131-
task: |-
132-
You're a helpful agent named '{{name}}'.
133-
You have been submitted this task by your manager.
134-
---
135-
{{task}}
136-
---
137-
You're helping your manager solve a wider task: so make sure to not provide a one-line answer, but give as much information as possible to give them a clear understanding of the answer.
138-
139-
Your `final_answer` WILL HAVE to contain these parts:
140-
### 1. Task outcome (short version):
141-
### 2. Task outcome (extremely detailed version):
142-
### 3. Additional context (if relevant):
18+
{{ common_parts.common_managed_agent_parts() }}
14319

144-
Put all these in your `final_answer` tool, everything that you do not pass as an argument to `final_answer` will be lost.
145-
And even if your task resolution is not successful, please return as much context as possible, so that your manager can act upon this feedback.
146-
report: |-
147-
Here is the final answer from your managed agent '{{name}}':
148-
{{final_answer}}
14920
final_answer:
150-
pre_messages: |-
151-
An agent tried to answer a user query but it got stuck and failed to do so. You are tasked with providing an answer instead. Here is the agent's memory:
152-
post_messages: |-
153-
Based on the above, please provide an answer to the following user task:
154-
{{task}}
21+
{{ common_parts.common_final_answer_parts() }}

0 commit comments

Comments
 (0)