Skip to content

Commit 904fd66

Browse files
Add msg_to param to ConversableAgent.run (#704)
* add msg_to param to ConversableAgene.run * Add agent name, user input control, and max turns control to run. Docstrings and termination condition update. Signed-off-by: Mark Sze <[email protected]> * Rearrange docstring arguments * Updated notebook for run Signed-off-by: Mark Sze <[email protected]> * Add commas to notebook Signed-off-by: Mark Sze <[email protected]> --------- Signed-off-by: Mark Sze <[email protected]> Co-authored-by: Mark Sze <[email protected]>
1 parent eaa5255 commit 904fd66

File tree

3 files changed

+100
-42
lines changed

3 files changed

+100
-42
lines changed

autogen/agentchat/conversable_agent.py

+69-19
Original file line numberDiff line numberDiff line change
@@ -2912,18 +2912,28 @@ def get_total_usage(self) -> Union[None, dict[str, int]]:
29122912

29132913
@contextmanager
29142914
def _create_executor(
2915-
self, executor_kwargs: Optional[dict[str, Any]] = None, tools: Optional[Union[Tool, Iterable[Tool]]] = None
2915+
self,
2916+
executor_kwargs: Optional[dict[str, Any]] = None,
2917+
tools: Optional[Union[Tool, Iterable[Tool]]] = None,
2918+
agent_name: str = "executor",
2919+
agent_human_input_mode: str = "NEVER",
29162920
) -> Generator["ConversableAgent", None, None]:
2921+
"""Creates a user proxy / tool executor agent.
2922+
2923+
Args:
2924+
executor_kwargs: agent's arguments.
2925+
tools: tools to register for execution with the agent.
2926+
agent_name: agent's name, defaults to 'executor'.
2927+
agent_human_input_mode: agent's human input mode, defaults to 'NEVER'.
2928+
"""
29172929
if executor_kwargs is None:
29182930
executor_kwargs = {}
29192931
if "is_termination_msg" not in executor_kwargs:
2920-
executor_kwargs["is_termination_msg"] = lambda x: (x["content"] is not None) and x["content"].endswith(
2921-
"TERMINATE"
2922-
)
2932+
executor_kwargs["is_termination_msg"] = lambda x: (x["content"] is not None) and "TERMINATE" in x["content"]
29232933

29242934
executor = ConversableAgent(
2925-
name="executor",
2926-
human_input_mode="NEVER",
2935+
name=agent_name,
2936+
human_input_mode=agent_human_input_mode,
29272937
code_execution_config={
29282938
"work_dir": "coding",
29292939
"use_docker": True,
@@ -2947,39 +2957,79 @@ def run(
29472957
self,
29482958
message: str,
29492959
*,
2950-
clear_history: bool = False,
2951-
executor_kwargs: Optional[dict[str, Any]] = None,
29522960
tools: Optional[Union[Tool, Iterable[Tool]]] = None,
2961+
executor_kwargs: Optional[dict[str, Any]] = None,
2962+
max_turns: Optional[int] = None,
2963+
msg_to: Literal["agent", "user"] = "agent",
2964+
clear_history: bool = False,
2965+
user_input: bool = True,
29532966
) -> ChatResult:
2954-
"""Run the agent with the given message.
2967+
"""Run a chat with the agent using the given message.
2968+
2969+
A second agent will be created to represent the user, this agent will by known by the name 'user'.
2970+
2971+
The user can terminate the conversation when prompted or, if agent's reply contains 'TERMINATE', it will terminate.
29552972
29562973
Args:
29572974
message: the message to be processed.
2958-
clear_history: whether to clear the chat history.
2959-
executor_kwargs: the keyword arguments for the executor.
29602975
tools: the tools to be used by the agent.
2976+
executor_kwargs: the keyword arguments for the executor.
2977+
max_turns: maximum number of turns (a turn is equivalent to both agents having replied), defaults no None which means unlimited. The original message is included.
2978+
msg_to: which agent is receiving the message and will be the first to reply, defaults to the agent.
2979+
clear_history: whether to clear the chat history.
2980+
user_input: the user will be asked for input at their turn.
29612981
"""
2962-
with self._create_executor(executor_kwargs=executor_kwargs, tools=tools) as executor:
2963-
return executor.initiate_chat(self, message=message, clear_history=clear_history).summary
2982+
with self._create_executor(
2983+
executor_kwargs=executor_kwargs,
2984+
tools=tools,
2985+
agent_name="user",
2986+
agent_human_input_mode="ALWAYS" if user_input else "NEVER",
2987+
) as executor:
2988+
if msg_to == "agent":
2989+
return executor.initiate_chat(self, message=message, clear_history=clear_history, max_turns=max_turns)
2990+
else:
2991+
return self.initiate_chat(executor, message=message, clear_history=clear_history, max_turns=max_turns)
29642992

29652993
async def a_run(
29662994
self,
29672995
message: str,
29682996
*,
2969-
clear_history=False,
29702997
tools: Optional[Union[Tool, Iterable[Tool]]] = None,
29712998
executor_kwargs: Optional[dict[str, Any]] = None,
2999+
max_turns: Optional[int] = None,
3000+
msg_to: Literal["agent", "user"] = "agent",
3001+
clear_history: bool = False,
3002+
user_input: bool = True,
29723003
) -> ChatResult:
2973-
"""Run the agent with the given message.
3004+
"""Run a chat asynchronously with the agent using the given message.
3005+
3006+
A second agent will be created to represent the user, this agent will by known by the name 'user'.
3007+
3008+
The user can terminate the conversation when prompted or, if agent's reply contains 'TERMINATE', it will terminate.
29743009
29753010
Args:
29763011
message: the message to be processed.
2977-
clear_history: whether to clear the chat history.
2978-
executor_kwargs: the keyword arguments for the executor.
29793012
tools: the tools to be used by the agent.
3013+
executor_kwargs: the keyword arguments for the executor.
3014+
max_turns: maximum number of turns (a turn is equivalent to both agents having replied), defaults no None which means unlimited. The original message is included.
3015+
msg_to: which agent is receiving the message and will be the first to reply, defaults to the agent.
3016+
clear_history: whether to clear the chat history.
3017+
user_input: the user will be asked for input at their turn.
29803018
"""
2981-
with self._create_executor(executor_kwargs=executor_kwargs, tools=tools) as executor:
2982-
return (await executor.a_initiate_chat(self, message=message, clear_history=clear_history)).summary
3019+
with self._create_executor(
3020+
executor_kwargs=executor_kwargs,
3021+
tools=tools,
3022+
agent_name="user",
3023+
agent_human_input_mode="ALWAYS" if user_input else "NEVER",
3024+
) as executor:
3025+
if msg_to == "agent":
3026+
return await executor.a_initiate_chat(
3027+
self, message=message, clear_history=clear_history, max_turns=max_turns
3028+
)
3029+
else:
3030+
return await self.a_initiate_chat(
3031+
executor, message=message, clear_history=clear_history, max_turns=max_turns
3032+
)
29833033

29843034

29853035
@export_module("autogen")

notebook/agentchat_assistant_agent_standalone.ipynb

+29-21
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"\n",
99
"AG2 supports running `AssistantAgent` as a standalone agent with the ability to execute simple tasks without the need for interacting with other agents.\n",
1010
"\n",
11-
"To enable our assistant agent to surf the web, we will use `BrowserUseTool` fow which we need to install the browser-use optional dependency and [playwright](https://playwright.dev/python/docs/intro)\n",
11+
"To enable our assistant agent access to surf the web, we will use the `BrowserUseTool` Tool for which we need to install the browser-use optional dependency and [playwright](https://playwright.dev/python/docs/intro).\n",
1212
"\n",
1313
"````{=mdx}\n",
1414
":::info Requirements\n",
@@ -33,7 +33,7 @@
3333
},
3434
{
3535
"cell_type": "code",
36-
"execution_count": 2,
36+
"execution_count": null,
3737
"metadata": {},
3838
"outputs": [],
3939
"source": [
@@ -48,12 +48,12 @@
4848
"source": [
4949
"## Set your API Endpoint\n",
5050
"\n",
51-
"The [`config_list_from_json`](https://docs.ag2.ai/reference/autogen/config_list_from_json#config-list-from-json) function loads a list of configurations from an environment variable or a json file."
51+
"The [`config_list_from_json`](https://docs.ag2.ai/reference/autogen/config_list_from_json#config-list-from-json) function loads a list of configurations from an environment variable or a JSON file."
5252
]
5353
},
5454
{
5555
"cell_type": "code",
56-
"execution_count": 3,
56+
"execution_count": null,
5757
"metadata": {},
5858
"outputs": [],
5959
"source": [
@@ -78,8 +78,10 @@
7878
"# Configure your assistant agent\n",
7979
"\n",
8080
"Here we will configure two assistant agents:\n",
81-
"1. x_assistant, tasked with exploring the trending topics on X (Formally Twitter)\n",
82-
"2. arxiv_researcher, tasked with discovery of papers that allign with the hot topic of the day"
81+
"1. x_assistant, tasked with exploring the trending topics on X (formally Twitter)\n",
82+
"2. arxiv_researcher, tasked with discovery of papers that allign with the hot topic of the day\n",
83+
"\n",
84+
"We will set the browser configuration to not run headless, this will open the browser as a window so you can see it in action."
8385
]
8486
},
8587
{
@@ -94,6 +96,7 @@
9496
"\n",
9597
"browser_use_tool = BrowserUseTool(\n",
9698
" llm_config=llm_config,\n",
99+
" browser_config={\"headless\": False},\n",
97100
")"
98101
]
99102
},
@@ -108,8 +111,9 @@
108111
"cell_type": "markdown",
109112
"metadata": {},
110113
"source": [
111-
"Lets run our x_assistant to discover the hot topic of the day\n",
112-
"To be able to do this let's give our assistant the capability to browse the web using a `BrowserUseTool`"
114+
"Let's run our x_assistant to discover the hot topic of the day.\n",
115+
"\n",
116+
"To be able to do this we give our assistant the capability to browse the web using a `BrowserUseTool` Tool."
113117
]
114118
},
115119
{
@@ -121,16 +125,17 @@
121125
"hot_topic_res = x_assistant.run(\n",
122126
" \"Find out today's hot topic and an influencer who is talking about it on X using a web search\",\n",
123127
" tools=browser_use_tool,\n",
128+
" user_input=False,\n",
124129
")\n",
125130
"\n",
126-
"print(hot_topic_res)"
131+
"print(hot_topic_res.summary)"
127132
]
128133
},
129134
{
130135
"cell_type": "markdown",
131136
"metadata": {},
132137
"source": [
133-
"After discovering the hot topic, lets run the discovery of papers that allign with the topic"
138+
"After discovering the hot topic, let's run the discovery of papers that align with the topic"
134139
]
135140
},
136141
{
@@ -140,17 +145,18 @@
140145
"outputs": [],
141146
"source": [
142147
"paper_abstract = arxiv_researcher.run(\n",
143-
" \"Get the abstract of a relevant paper based on \" + hot_topic_res,\n",
148+
" \"Get the abstract of a relevant paper based on:\\n\" + hot_topic_res.summary,\n",
149+
" user_input=False,\n",
144150
")\n",
145151
"\n",
146-
"print(paper_abstract)"
152+
"print(paper_abstract.summary)"
147153
]
148154
},
149155
{
150156
"cell_type": "markdown",
151157
"metadata": {},
152158
"source": [
153-
"Now, Lets create a twitter post using our x_assistant"
159+
"Now, let's create an X post using our x_assistant"
154160
]
155161
},
156162
{
@@ -159,19 +165,20 @@
159165
"metadata": {},
160166
"outputs": [],
161167
"source": [
162-
"# Secneario 1. This task requires x_assistant's past state\n",
168+
"# Scenario 1. This task requires x_assistant's past state\n",
163169
"post = x_assistant.run(\n",
164-
" \"Create an X post based on the hot topic and this \" + paper_abstract + \"and mention the influencer\",\n",
170+
" \"Create an X post based on the hot topic and the following and mention the influencer:\\n\" + paper_abstract.summary,\n",
171+
" user_input=False,\n",
165172
")\n",
166173
"\n",
167-
"print(post)"
174+
"print(post.summary)"
168175
]
169176
},
170177
{
171178
"cell_type": "markdown",
172179
"metadata": {},
173180
"source": [
174-
"Finally, lets ask our x_assistant who should we follow on twitter"
181+
"Finally, let's ask our x_assistant who should we follow on X"
175182
]
176183
},
177184
{
@@ -180,21 +187,22 @@
180187
"metadata": {},
181188
"outputs": [],
182189
"source": [
183-
"# Scenario 2. Doing another task that does not require history or past state\n",
190+
"# Scenario 2. Doing another task that does not require history or past state\n",
184191
"\n",
185192
"influencer_choice = x_assistant.run(\n",
186193
" \"Find a influencer I should follow on Twitter by searching the web\",\n",
187194
" clear_history=True,\n",
188195
" tools=browser_use_tool,\n",
196+
" user_input=False,\n",
189197
")\n",
190198
"\n",
191-
"print(influencer_choice)"
199+
"print(influencer_choice.summary)"
192200
]
193201
}
194202
],
195203
"metadata": {
196204
"kernelspec": {
197-
"display_name": ".venv",
205+
"display_name": "Python 3",
198206
"language": "python",
199207
"name": "python3"
200208
},
@@ -208,7 +216,7 @@
208216
"name": "python",
209217
"nbconvert_exporter": "python",
210218
"pygments_lexer": "ipython3",
211-
"version": "3.11.11"
219+
"version": "3.11.10"
212220
}
213221
},
214222
"nbformat": 4,

test/agentchat/test_assistant_agent.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ def get_twitter_hot_topic() -> str:
205205
tools=get_twitter_hot_topic,
206206
)
207207

208-
assert "AI" in hot_topic_res
209-
assert "elonmusk" in hot_topic_res
208+
assert "AI" in hot_topic_res.summary
209+
assert "elonmusk" in hot_topic_res.summary
210210

211211

212212
@pytest.mark.openai

0 commit comments

Comments
 (0)