mirror of
https://github.com/microsoft/autogen.git
synced 2026-04-20 03:02:16 -04:00
doc: update guide for termination condition and tool usage (#5807)
Resolves #5786 Also updated the termination tutorial to include an example of a custom termination conditon. Also added to guide about FunctionTool and MCP tools.
This commit is contained in:
@@ -298,7 +298,127 @@
|
||||
"it will return the tool's output as a string in {py:class}`~autogen_agentchat.messages.ToolCallSummaryMessage` in its response.\n",
|
||||
"If your tool does not return a well-formed string in natural language, you\n",
|
||||
"can add a reflection step to have the model summarize the tool's output,\n",
|
||||
"by setting the `reflect_on_tool_use=True` parameter in the {py:class}`~autogen_agentchat.agents.AssistantAgent` constructor."
|
||||
"by setting the `reflect_on_tool_use=True` parameter in the {py:class}`~autogen_agentchat.agents.AssistantAgent` constructor.\n",
|
||||
"\n",
|
||||
"### Built-in Tools\n",
|
||||
"\n",
|
||||
"AutoGen Extension provides a set of built-in tools that can be used with the Assistant Agent.\n",
|
||||
"Head over to the [API documentation](../../../reference/index.md) for all the available tools\n",
|
||||
"under the `autogen_ext.tools` namespace. For example, you can find the following tools:\n",
|
||||
"\n",
|
||||
"- {py:mod}`~autogen_ext.tools.graphrag`: Tools for using GraphRAG index.\n",
|
||||
"- {py:mod}`~autogen_ext.tools.http`: Tools for making HTTP requests.\n",
|
||||
"- {py:mod}`~autogen_ext.tools.langchain`: Adaptor for using LangChain tools.\n",
|
||||
"- {py:mod}`~autogen_ext.tools.mcp`: Tools for using Model Chat Protocol (MCP) servers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Function Tool\n",
|
||||
"\n",
|
||||
"The {py:class}`~autogen_agentchat.agents.AssistantAgent` automatically\n",
|
||||
"converts a Python function into a {py:class}`~autogen_core.tools.FunctionTool`\n",
|
||||
"which can be used as a tool by the agent and automatically generates the tool schema\n",
|
||||
"from the function signature and docstring.\n",
|
||||
"\n",
|
||||
"The `web_search_func` tool is an example of a function tool.\n",
|
||||
"The schema is automatically generated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'name': 'web_search_func',\n",
|
||||
" 'description': 'Find information on the web',\n",
|
||||
" 'parameters': {'type': 'object',\n",
|
||||
" 'properties': {'query': {'description': 'query',\n",
|
||||
" 'title': 'Query',\n",
|
||||
" 'type': 'string'}},\n",
|
||||
" 'required': ['query'],\n",
|
||||
" 'additionalProperties': False},\n",
|
||||
" 'strict': False}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from autogen_core.tools import FunctionTool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define a tool using a Python function.\n",
|
||||
"async def web_search_func(query: str) -> str:\n",
|
||||
" \"\"\"Find information on the web\"\"\"\n",
|
||||
" return \"AutoGen is a programming framework for building multi-agent applications.\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# This step is automatically performed inside the AssistantAgent if the tool is a Python function.\n",
|
||||
"web_search_function_tool = FunctionTool(web_search_func, description=\"Find information on the web\")\n",
|
||||
"# The schema is provided to the model during AssistantAgent's on_messages call.\n",
|
||||
"web_search_function_tool.schema"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Model Context Protocol Tools\n",
|
||||
"\n",
|
||||
"The {py:class}`~autogen_agentchat.agents.AssistantAgent` can also use tools that are\n",
|
||||
"served from a Model Context Protocol (MCP) server\n",
|
||||
"using {py:func}`~autogen_ext.tools.mcp.mcp_server_tools`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Seattle, located in Washington state, is the most populous city in the state and a major city in the Pacific Northwest region of the United States. It's known for its vibrant cultural scene, significant economic presence, and rich history. Here are some key points about Seattle from the Wikipedia page:\n",
|
||||
"\n",
|
||||
"1. **History and Geography**: Seattle is situated between Puget Sound and Lake Washington, with the Cascade Range to the east and the Olympic Mountains to the west. Its history is deeply rooted in Native American heritage and its development was accelerated with the arrival of settlers in the 19th century. The city was officially incorporated in 1869.\n",
|
||||
"\n",
|
||||
"2. **Economy**: Seattle is a major economic hub with a diverse economy anchored by sectors like aerospace, technology, and retail. It's home to influential companies such as Amazon and Starbucks, and has a significant impact on the tech industry due to companies like Microsoft and other technology enterprises in the surrounding area.\n",
|
||||
"\n",
|
||||
"3. **Cultural Significance**: Known for its music scene, Seattle was the birthplace of grunge music in the early 1990s. It also boasts significant attractions like the Space Needle, Pike Place Market, and the Seattle Art Museum. \n",
|
||||
"\n",
|
||||
"4. **Education and Innovation**: The city hosts important educational institutions, with the University of Washington being a leading research university. Seattle is recognized for fostering innovation and is a leader in environmental sustainability efforts.\n",
|
||||
"\n",
|
||||
"5. **Demographics and Diversity**: Seattle is noted for its diverse population, reflected in its rich cultural tapestry. It has seen a significant increase in population, leading to urban development and changes in its social landscape.\n",
|
||||
"\n",
|
||||
"These points highlight Seattle as a dynamic city with a significant cultural, economic, and educational influence within the United States and beyond.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from autogen_agentchat.agents import AssistantAgent\n",
|
||||
"from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
|
||||
"from autogen_ext.tools.mcp import StdioServerParams, mcp_server_tools\n",
|
||||
"\n",
|
||||
"# Get the fetch tool from mcp-server-fetch.\n",
|
||||
"fetch_mcp_server = StdioServerParams(command=\"uvx\", args=[\"mcp-server-fetch\"])\n",
|
||||
"tools = await mcp_server_tools(fetch_mcp_server)\n",
|
||||
"\n",
|
||||
"# Create an agent that can use the fetch tool.\n",
|
||||
"model_client = OpenAIChatCompletionClient(model=\"gpt-4o\")\n",
|
||||
"agent = AssistantAgent(name=\"fetcher\", model_client=model_client, tools=tools, reflect_on_tool_use=True) # type: ignore\n",
|
||||
"\n",
|
||||
"# Let the agent fetch the content of a URL and summarize it.\n",
|
||||
"result = await agent.run(task=\"Summarize the content of https://en.wikipedia.org/wiki/Seattle\")\n",
|
||||
"print(result.messages[-1].content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -307,7 +427,7 @@
|
||||
"source": [
|
||||
"### Langchain Tools\n",
|
||||
"\n",
|
||||
"In addition to custom tools, you can also use tools from the Langchain library\n",
|
||||
"You can also use tools from the Langchain library\n",
|
||||
"by wrapping them in {py:class}`~autogen_ext.tools.langchain.LangChainToolAdapter`."
|
||||
]
|
||||
},
|
||||
@@ -400,6 +520,20 @@
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Running an Agent in a Loop\n",
|
||||
"\n",
|
||||
"The {py:class}`~autogen_agentchat.agents.AssistantAgent` executes one\n",
|
||||
"step at a time: one model call, followed by one tool call (or parallel tool calls), and then\n",
|
||||
"an optional reflection.\n",
|
||||
"\n",
|
||||
"To run it in a loop, for example, running it until it stops producing\n",
|
||||
"tool calls, please refer to [Single-Agent Team](./teams.ipynb#single-agent-team)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,304 +1,515 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Termination \n",
|
||||
"\n",
|
||||
"In the previous section, we explored how to define agents, and organize them into teams that can solve tasks. However, a run can go on forever, and in many cases, we need to know _when_ to stop them. This is the role of the termination condition.\n",
|
||||
"\n",
|
||||
"AgentChat supports several termination condition by providing a base {py:class}`~autogen_agentchat.base.TerminationCondition` class and several implementations that inherit from it.\n",
|
||||
"\n",
|
||||
"A termination condition is a callable that takes a sequence of {py:class}`~autogen_agentchat.messages.AgentEvent` or {py:class}`~autogen_agentchat.messages.ChatMessage` objects **since the last time the condition was called**, and returns a {py:class}`~autogen_agentchat.messages.StopMessage` if the conversation should be terminated, or `None` otherwise.\n",
|
||||
"Once a termination condition has been reached, it must be reset by calling {py:meth}`~autogen_agentchat.base.TerminationCondition.reset` before it can be used again.\n",
|
||||
"\n",
|
||||
"Some important things to note about termination conditions: \n",
|
||||
"- They are stateful but reset automatically after each run ({py:meth}`~autogen_agentchat.base.TaskRunner.run` or {py:meth}`~autogen_agentchat.base.TaskRunner.run_stream`) is finished.\n",
|
||||
"- They can be combined using the AND and OR operators.\n",
|
||||
"\n",
|
||||
"```{note}\n",
|
||||
"For group chat teams (i.e., {py:class}`~autogen_agentchat.teams.RoundRobinGroupChat`,\n",
|
||||
"{py:class}`~autogen_agentchat.teams.SelectorGroupChat`, and {py:class}`~autogen_agentchat.teams.Swarm`),\n",
|
||||
"the termination condition is called after each agent responds.\n",
|
||||
"While a response may contain multiple inner messages, the team calls its termination condition just once for all the messages from a single response.\n",
|
||||
"So the condition is called with the \"delta sequence\" of messages since the last time it was called.\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Built-In Termination Conditions: \n",
|
||||
"1. {py:class}`~autogen_agentchat.conditions.MaxMessageTermination`: Stops after a specified number of messages have been produced, including both agent and task messages.\n",
|
||||
"2. {py:class}`~autogen_agentchat.conditions.TextMentionTermination`: Stops when specific text or string is mentioned in a message (e.g., \"TERMINATE\").\n",
|
||||
"3. {py:class}`~autogen_agentchat.conditions.TokenUsageTermination`: Stops when a certain number of prompt or completion tokens are used. This requires the agents to report token usage in their messages.\n",
|
||||
"4. {py:class}`~autogen_agentchat.conditions.TimeoutTermination`: Stops after a specified duration in seconds.\n",
|
||||
"5. {py:class}`~autogen_agentchat.conditions.HandoffTermination`: Stops when a handoff to a specific target is requested. Handoff messages can be used to build patterns such as {py:class}`~autogen_agentchat.teams.Swarm`. This is useful when you want to pause the run and allow application or user to provide input when an agent hands off to them.\n",
|
||||
"6. {py:class}`~autogen_agentchat.conditions.SourceMatchTermination`: Stops after a specific agent responds.\n",
|
||||
"7. {py:class}`~autogen_agentchat.conditions.ExternalTermination`: Enables programmatic control of termination from outside the run. This is useful for UI integration (e.g., \"Stop\" buttons in chat interfaces).\n",
|
||||
"8. {py:class}`~autogen_agentchat.conditions.StopMessageTermination`: Stops when a {py:class}`~autogen_agentchat.messages.StopMessage` is produced by an agent."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To demonstrate the characteristics of termination conditions, we'll create a team consisting of two agents: a primary agent responsible for text generation and a critic agent that reviews and provides feedback on the generated text."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from autogen_agentchat.agents import AssistantAgent\n",
|
||||
"from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination\n",
|
||||
"from autogen_agentchat.teams import RoundRobinGroupChat\n",
|
||||
"from autogen_agentchat.ui import Console\n",
|
||||
"from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
|
||||
"\n",
|
||||
"model_client = OpenAIChatCompletionClient(\n",
|
||||
" model=\"gpt-4o\",\n",
|
||||
" temperature=1,\n",
|
||||
" # api_key=\"sk-...\", # Optional if you have an OPENAI_API_KEY env variable set.\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create the primary agent.\n",
|
||||
"primary_agent = AssistantAgent(\n",
|
||||
" \"primary\",\n",
|
||||
" model_client=model_client,\n",
|
||||
" system_message=\"You are a helpful AI assistant.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create the critic agent.\n",
|
||||
"critic_agent = AssistantAgent(\n",
|
||||
" \"critic\",\n",
|
||||
" model_client=model_client,\n",
|
||||
" system_message=\"Provide constructive feedback for every message. Respond with 'APPROVE' to when your feedbacks are addressed.\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's explore how termination conditions automatically reset after each `run` or `run_stream` call, allowing the team to resume its conversation from where it left off."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- user ----------\n",
|
||||
"Write a unique, Haiku about the weather in Paris\n",
|
||||
"---------- primary ----------\n",
|
||||
"Gentle rain whispers, \n",
|
||||
"Cobblestones glisten softly— \n",
|
||||
"Paris dreams in gray.\n",
|
||||
"[Prompt tokens: 30, Completion tokens: 19]\n",
|
||||
"---------- critic ----------\n",
|
||||
"The Haiku captures the essence of a rainy day in Paris beautifully, and the imagery is vivid. However, it's important to ensure the use of the traditional 5-7-5 syllable structure for Haikus. Your current Haiku lines are composed of 4-7-5 syllables, which slightly deviates from the form. Consider revising the first line to fit the structure.\n",
|
||||
"\n",
|
||||
"For example:\n",
|
||||
"Soft rain whispers down, \n",
|
||||
"Cobblestones glisten softly — \n",
|
||||
"Paris dreams in gray.\n",
|
||||
"\n",
|
||||
"This revision maintains the essence of your original lines while adhering to the traditional Haiku structure.\n",
|
||||
"[Prompt tokens: 70, Completion tokens: 120]\n",
|
||||
"---------- Summary ----------\n",
|
||||
"Number of messages: 3\n",
|
||||
"Finish reason: Maximum number of messages 3 reached, current message count: 3\n",
|
||||
"Total prompt tokens: 100\n",
|
||||
"Total completion tokens: 139\n",
|
||||
"Duration: 3.34 seconds\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a unique, Haiku about the weather in Paris'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=30, completion_tokens=19), content='Gentle rain whispers, \\nCobblestones glisten softly— \\nParis dreams in gray.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=70, completion_tokens=120), content=\"The Haiku captures the essence of a rainy day in Paris beautifully, and the imagery is vivid. However, it's important to ensure the use of the traditional 5-7-5 syllable structure for Haikus. Your current Haiku lines are composed of 4-7-5 syllables, which slightly deviates from the form. Consider revising the first line to fit the structure.\\n\\nFor example:\\nSoft rain whispers down, \\nCobblestones glisten softly — \\nParis dreams in gray.\\n\\nThis revision maintains the essence of your original lines while adhering to the traditional Haiku structure.\")], stop_reason='Maximum number of messages 3 reached, current message count: 3')"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"max_msg_termination = MaxMessageTermination(max_messages=3)\n",
|
||||
"round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=max_msg_termination)\n",
|
||||
"\n",
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream(task=\"Write a unique, Haiku about the weather in Paris\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The conversation stopped after reaching the maximum message limit. Since the primary agent didn't get to respond to the feedback, let's continue the conversation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- primary ----------\n",
|
||||
"Thank you for your feedback. Here is the revised Haiku:\n",
|
||||
"\n",
|
||||
"Soft rain whispers down, \n",
|
||||
"Cobblestones glisten softly — \n",
|
||||
"Paris dreams in gray.\n",
|
||||
"[Prompt tokens: 181, Completion tokens: 32]\n",
|
||||
"---------- critic ----------\n",
|
||||
"The revised Haiku now follows the traditional 5-7-5 syllable pattern, and it still beautifully captures the atmospheric mood of Paris in the rain. The imagery and flow are both clear and evocative. Well done on making the adjustment! \n",
|
||||
"\n",
|
||||
"APPROVE\n",
|
||||
"[Prompt tokens: 234, Completion tokens: 54]\n",
|
||||
"---------- primary ----------\n",
|
||||
"Thank you for your kind words and approval. I'm glad the revision meets your expectations and captures the essence of Paris. If you have any more requests or need further assistance, feel free to ask!\n",
|
||||
"[Prompt tokens: 279, Completion tokens: 39]\n",
|
||||
"---------- Summary ----------\n",
|
||||
"Number of messages: 3\n",
|
||||
"Finish reason: Maximum number of messages 3 reached, current message count: 3\n",
|
||||
"Total prompt tokens: 694\n",
|
||||
"Total completion tokens: 125\n",
|
||||
"Duration: 6.43 seconds\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=181, completion_tokens=32), content='Thank you for your feedback. Here is the revised Haiku:\\n\\nSoft rain whispers down, \\nCobblestones glisten softly — \\nParis dreams in gray.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=234, completion_tokens=54), content='The revised Haiku now follows the traditional 5-7-5 syllable pattern, and it still beautifully captures the atmospheric mood of Paris in the rain. The imagery and flow are both clear and evocative. Well done on making the adjustment! \\n\\nAPPROVE'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=279, completion_tokens=39), content=\"Thank you for your kind words and approval. I'm glad the revision meets your expectations and captures the essence of Paris. If you have any more requests or need further assistance, feel free to ask!\")], stop_reason='Maximum number of messages 3 reached, current message count: 3')"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The team continued from where it left off, allowing the primary agent to respond to the feedback."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Next, let's show how termination conditions can be combined using the AND (`&`) and OR (`|`) operators to create more complex termination logic. For example, we'll create a team that stops either after 10 messages are generated or when the critic agent approves a message.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- user ----------\n",
|
||||
"Write a unique, Haiku about the weather in Paris\n",
|
||||
"---------- primary ----------\n",
|
||||
"Spring breeze gently hums, \n",
|
||||
"Cherry blossoms in full bloom— \n",
|
||||
"Paris wakes to life.\n",
|
||||
"[Prompt tokens: 467, Completion tokens: 19]\n",
|
||||
"---------- critic ----------\n",
|
||||
"The Haiku beautifully captures the awakening of Paris in the spring. The imagery of a gentle spring breeze and cherry blossoms in full bloom effectively conveys the rejuvenating feel of the season. The final line, \"Paris wakes to life,\" encapsulates the renewed energy and vibrancy of the city. The Haiku adheres to the 5-7-5 syllable structure and portrays a vivid seasonal transformation in a concise and poetic manner. Excellent work!\n",
|
||||
"\n",
|
||||
"APPROVE\n",
|
||||
"[Prompt tokens: 746, Completion tokens: 93]\n",
|
||||
"---------- Summary ----------\n",
|
||||
"Number of messages: 3\n",
|
||||
"Finish reason: Text 'APPROVE' mentioned\n",
|
||||
"Total prompt tokens: 1213\n",
|
||||
"Total completion tokens: 112\n",
|
||||
"Duration: 2.75 seconds\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a unique, Haiku about the weather in Paris'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=467, completion_tokens=19), content='Spring breeze gently hums, \\nCherry blossoms in full bloom— \\nParis wakes to life.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=746, completion_tokens=93), content='The Haiku beautifully captures the awakening of Paris in the spring. The imagery of a gentle spring breeze and cherry blossoms in full bloom effectively conveys the rejuvenating feel of the season. The final line, \"Paris wakes to life,\" encapsulates the renewed energy and vibrancy of the city. The Haiku adheres to the 5-7-5 syllable structure and portrays a vivid seasonal transformation in a concise and poetic manner. Excellent work!\\n\\nAPPROVE')], stop_reason=\"Text 'APPROVE' mentioned\")"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"max_msg_termination = MaxMessageTermination(max_messages=10)\n",
|
||||
"text_termination = TextMentionTermination(\"APPROVE\")\n",
|
||||
"combined_termination = max_msg_termination | text_termination\n",
|
||||
"\n",
|
||||
"round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=combined_termination)\n",
|
||||
"\n",
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream(task=\"Write a unique, Haiku about the weather in Paris\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The conversation stopped after the critic agent approved the message, although it could have also stopped if 10 messages were generated.\n",
|
||||
"\n",
|
||||
"Alternatively, if we want to stop the run only when both conditions are met, we can use the AND (`&`) operator."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"combined_termination = max_msg_termination & text_termination"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.5"
|
||||
}
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Termination \n",
|
||||
"\n",
|
||||
"In the previous section, we explored how to define agents, and organize them into teams that can solve tasks. However, a run can go on forever, and in many cases, we need to know _when_ to stop them. This is the role of the termination condition.\n",
|
||||
"\n",
|
||||
"AgentChat supports several termination condition by providing a base {py:class}`~autogen_agentchat.base.TerminationCondition` class and several implementations that inherit from it.\n",
|
||||
"\n",
|
||||
"A termination condition is a callable that takes a sequence of {py:class}`~autogen_agentchat.messages.AgentEvent` or {py:class}`~autogen_agentchat.messages.ChatMessage` objects **since the last time the condition was called**, and returns a {py:class}`~autogen_agentchat.messages.StopMessage` if the conversation should be terminated, or `None` otherwise.\n",
|
||||
"Once a termination condition has been reached, it must be reset by calling {py:meth}`~autogen_agentchat.base.TerminationCondition.reset` before it can be used again.\n",
|
||||
"\n",
|
||||
"Some important things to note about termination conditions: \n",
|
||||
"- They are stateful but reset automatically after each run ({py:meth}`~autogen_agentchat.base.TaskRunner.run` or {py:meth}`~autogen_agentchat.base.TaskRunner.run_stream`) is finished.\n",
|
||||
"- They can be combined using the AND and OR operators.\n",
|
||||
"\n",
|
||||
"```{note}\n",
|
||||
"For group chat teams (i.e., {py:class}`~autogen_agentchat.teams.RoundRobinGroupChat`,\n",
|
||||
"{py:class}`~autogen_agentchat.teams.SelectorGroupChat`, and {py:class}`~autogen_agentchat.teams.Swarm`),\n",
|
||||
"the termination condition is called after each agent responds.\n",
|
||||
"While a response may contain multiple inner messages, the team calls its termination condition just once for all the messages from a single response.\n",
|
||||
"So the condition is called with the \"delta sequence\" of messages since the last time it was called.\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Built-In Termination Conditions: \n",
|
||||
"1. {py:class}`~autogen_agentchat.conditions.MaxMessageTermination`: Stops after a specified number of messages have been produced, including both agent and task messages.\n",
|
||||
"2. {py:class}`~autogen_agentchat.conditions.TextMentionTermination`: Stops when specific text or string is mentioned in a message (e.g., \"TERMINATE\").\n",
|
||||
"3. {py:class}`~autogen_agentchat.conditions.TokenUsageTermination`: Stops when a certain number of prompt or completion tokens are used. This requires the agents to report token usage in their messages.\n",
|
||||
"4. {py:class}`~autogen_agentchat.conditions.TimeoutTermination`: Stops after a specified duration in seconds.\n",
|
||||
"5. {py:class}`~autogen_agentchat.conditions.HandoffTermination`: Stops when a handoff to a specific target is requested. Handoff messages can be used to build patterns such as {py:class}`~autogen_agentchat.teams.Swarm`. This is useful when you want to pause the run and allow application or user to provide input when an agent hands off to them.\n",
|
||||
"6. {py:class}`~autogen_agentchat.conditions.SourceMatchTermination`: Stops after a specific agent responds.\n",
|
||||
"7. {py:class}`~autogen_agentchat.conditions.ExternalTermination`: Enables programmatic control of termination from outside the run. This is useful for UI integration (e.g., \"Stop\" buttons in chat interfaces).\n",
|
||||
"8. {py:class}`~autogen_agentchat.conditions.StopMessageTermination`: Stops when a {py:class}`~autogen_agentchat.messages.StopMessage` is produced by an agent.\n",
|
||||
"9. {py:class}`~autogen_agentchat.conditions.TextMessageTermination`: Stops when a {py:class}`~autogen_agentchat.messages.TextMessage` is produced by an agent."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Basic Usage\n",
|
||||
"\n",
|
||||
"To demonstrate the characteristics of termination conditions, we'll create a team consisting of two agents: a primary agent responsible for text generation and a critic agent that reviews and provides feedback on the generated text."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from autogen_agentchat.agents import AssistantAgent\n",
|
||||
"from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination\n",
|
||||
"from autogen_agentchat.teams import RoundRobinGroupChat\n",
|
||||
"from autogen_agentchat.ui import Console\n",
|
||||
"from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
|
||||
"\n",
|
||||
"model_client = OpenAIChatCompletionClient(\n",
|
||||
" model=\"gpt-4o\",\n",
|
||||
" temperature=1,\n",
|
||||
" # api_key=\"sk-...\", # Optional if you have an OPENAI_API_KEY env variable set.\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create the primary agent.\n",
|
||||
"primary_agent = AssistantAgent(\n",
|
||||
" \"primary\",\n",
|
||||
" model_client=model_client,\n",
|
||||
" system_message=\"You are a helpful AI assistant.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create the critic agent.\n",
|
||||
"critic_agent = AssistantAgent(\n",
|
||||
" \"critic\",\n",
|
||||
" model_client=model_client,\n",
|
||||
" system_message=\"Provide constructive feedback for every message. Respond with 'APPROVE' to when your feedbacks are addressed.\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's explore how termination conditions automatically reset after each `run` or `run_stream` call, allowing the team to resume its conversation from where it left off."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- user ----------\n",
|
||||
"Write a unique, Haiku about the weather in Paris\n",
|
||||
"---------- primary ----------\n",
|
||||
"Gentle rain whispers, \n",
|
||||
"Cobblestones glisten softly— \n",
|
||||
"Paris dreams in gray.\n",
|
||||
"[Prompt tokens: 30, Completion tokens: 19]\n",
|
||||
"---------- critic ----------\n",
|
||||
"The Haiku captures the essence of a rainy day in Paris beautifully, and the imagery is vivid. However, it's important to ensure the use of the traditional 5-7-5 syllable structure for Haikus. Your current Haiku lines are composed of 4-7-5 syllables, which slightly deviates from the form. Consider revising the first line to fit the structure.\n",
|
||||
"\n",
|
||||
"For example:\n",
|
||||
"Soft rain whispers down, \n",
|
||||
"Cobblestones glisten softly — \n",
|
||||
"Paris dreams in gray.\n",
|
||||
"\n",
|
||||
"This revision maintains the essence of your original lines while adhering to the traditional Haiku structure.\n",
|
||||
"[Prompt tokens: 70, Completion tokens: 120]\n",
|
||||
"---------- Summary ----------\n",
|
||||
"Number of messages: 3\n",
|
||||
"Finish reason: Maximum number of messages 3 reached, current message count: 3\n",
|
||||
"Total prompt tokens: 100\n",
|
||||
"Total completion tokens: 139\n",
|
||||
"Duration: 3.34 seconds\n"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a unique, Haiku about the weather in Paris'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=30, completion_tokens=19), content='Gentle rain whispers, \\nCobblestones glisten softly— \\nParis dreams in gray.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=70, completion_tokens=120), content=\"The Haiku captures the essence of a rainy day in Paris beautifully, and the imagery is vivid. However, it's important to ensure the use of the traditional 5-7-5 syllable structure for Haikus. Your current Haiku lines are composed of 4-7-5 syllables, which slightly deviates from the form. Consider revising the first line to fit the structure.\\n\\nFor example:\\nSoft rain whispers down, \\nCobblestones glisten softly — \\nParis dreams in gray.\\n\\nThis revision maintains the essence of your original lines while adhering to the traditional Haiku structure.\")], stop_reason='Maximum number of messages 3 reached, current message count: 3')"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"max_msg_termination = MaxMessageTermination(max_messages=3)\n",
|
||||
"round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=max_msg_termination)\n",
|
||||
"\n",
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream(task=\"Write a unique, Haiku about the weather in Paris\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The conversation stopped after reaching the maximum message limit. Since the primary agent didn't get to respond to the feedback, let's continue the conversation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- primary ----------\n",
|
||||
"Thank you for your feedback. Here is the revised Haiku:\n",
|
||||
"\n",
|
||||
"Soft rain whispers down, \n",
|
||||
"Cobblestones glisten softly — \n",
|
||||
"Paris dreams in gray.\n",
|
||||
"[Prompt tokens: 181, Completion tokens: 32]\n",
|
||||
"---------- critic ----------\n",
|
||||
"The revised Haiku now follows the traditional 5-7-5 syllable pattern, and it still beautifully captures the atmospheric mood of Paris in the rain. The imagery and flow are both clear and evocative. Well done on making the adjustment! \n",
|
||||
"\n",
|
||||
"APPROVE\n",
|
||||
"[Prompt tokens: 234, Completion tokens: 54]\n",
|
||||
"---------- primary ----------\n",
|
||||
"Thank you for your kind words and approval. I'm glad the revision meets your expectations and captures the essence of Paris. If you have any more requests or need further assistance, feel free to ask!\n",
|
||||
"[Prompt tokens: 279, Completion tokens: 39]\n",
|
||||
"---------- Summary ----------\n",
|
||||
"Number of messages: 3\n",
|
||||
"Finish reason: Maximum number of messages 3 reached, current message count: 3\n",
|
||||
"Total prompt tokens: 694\n",
|
||||
"Total completion tokens: 125\n",
|
||||
"Duration: 6.43 seconds\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=181, completion_tokens=32), content='Thank you for your feedback. Here is the revised Haiku:\\n\\nSoft rain whispers down, \\nCobblestones glisten softly — \\nParis dreams in gray.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=234, completion_tokens=54), content='The revised Haiku now follows the traditional 5-7-5 syllable pattern, and it still beautifully captures the atmospheric mood of Paris in the rain. The imagery and flow are both clear and evocative. Well done on making the adjustment! \\n\\nAPPROVE'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=279, completion_tokens=39), content=\"Thank you for your kind words and approval. I'm glad the revision meets your expectations and captures the essence of Paris. If you have any more requests or need further assistance, feel free to ask!\")], stop_reason='Maximum number of messages 3 reached, current message count: 3')"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The team continued from where it left off, allowing the primary agent to respond to the feedback."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Combining Termination Conditions\n",
|
||||
"\n",
|
||||
"Let's show how termination conditions can be combined using the AND (`&`) and OR (`|`) operators to create more complex termination logic. For example, we'll create a team that stops either after 10 messages are generated or when the critic agent approves a message.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- user ----------\n",
|
||||
"Write a unique, Haiku about the weather in Paris\n",
|
||||
"---------- primary ----------\n",
|
||||
"Spring breeze gently hums, \n",
|
||||
"Cherry blossoms in full bloom— \n",
|
||||
"Paris wakes to life.\n",
|
||||
"[Prompt tokens: 467, Completion tokens: 19]\n",
|
||||
"---------- critic ----------\n",
|
||||
"The Haiku beautifully captures the awakening of Paris in the spring. The imagery of a gentle spring breeze and cherry blossoms in full bloom effectively conveys the rejuvenating feel of the season. The final line, \"Paris wakes to life,\" encapsulates the renewed energy and vibrancy of the city. The Haiku adheres to the 5-7-5 syllable structure and portrays a vivid seasonal transformation in a concise and poetic manner. Excellent work!\n",
|
||||
"\n",
|
||||
"APPROVE\n",
|
||||
"[Prompt tokens: 746, Completion tokens: 93]\n",
|
||||
"---------- Summary ----------\n",
|
||||
"Number of messages: 3\n",
|
||||
"Finish reason: Text 'APPROVE' mentioned\n",
|
||||
"Total prompt tokens: 1213\n",
|
||||
"Total completion tokens: 112\n",
|
||||
"Duration: 2.75 seconds\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a unique, Haiku about the weather in Paris'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=467, completion_tokens=19), content='Spring breeze gently hums, \\nCherry blossoms in full bloom— \\nParis wakes to life.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=746, completion_tokens=93), content='The Haiku beautifully captures the awakening of Paris in the spring. The imagery of a gentle spring breeze and cherry blossoms in full bloom effectively conveys the rejuvenating feel of the season. The final line, \"Paris wakes to life,\" encapsulates the renewed energy and vibrancy of the city. The Haiku adheres to the 5-7-5 syllable structure and portrays a vivid seasonal transformation in a concise and poetic manner. Excellent work!\\n\\nAPPROVE')], stop_reason=\"Text 'APPROVE' mentioned\")"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"max_msg_termination = MaxMessageTermination(max_messages=10)\n",
|
||||
"text_termination = TextMentionTermination(\"APPROVE\")\n",
|
||||
"combined_termination = max_msg_termination | text_termination\n",
|
||||
"\n",
|
||||
"round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=combined_termination)\n",
|
||||
"\n",
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream(task=\"Write a unique, Haiku about the weather in Paris\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The conversation stopped after the critic agent approved the message, although it could have also stopped if 10 messages were generated.\n",
|
||||
"\n",
|
||||
"Alternatively, if we want to stop the run only when both conditions are met, we can use the AND (`&`) operator."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"combined_termination = max_msg_termination & text_termination"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Custom Termination Condition\n",
|
||||
"\n",
|
||||
"The built-in termination conditions are sufficient for most use cases.\n",
|
||||
"However, there may be cases where you need to implement a custom termination condition that doesn't fit into the existing ones.\n",
|
||||
"You can do this by subclassing the {py:class}`~autogen_agentchat.base.TerminationCondition` class.\n",
|
||||
"\n",
|
||||
"In this example, we create a custom termination condition that stops the conversation when\n",
|
||||
"a specific function call is made."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Sequence\n",
|
||||
"\n",
|
||||
"from autogen_agentchat.base import TerminatedException, TerminationCondition\n",
|
||||
"from autogen_agentchat.messages import AgentEvent, ChatMessage, StopMessage, ToolCallExecutionEvent\n",
|
||||
"from autogen_core import Component\n",
|
||||
"from pydantic import BaseModel\n",
|
||||
"from typing_extensions import Self\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class FunctionCallTerminationConfig(BaseModel):\n",
|
||||
" \"\"\"Configuration for the termination condition to allow for serialization\n",
|
||||
" and deserialization of the component.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" function_name: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class FunctionCallTermination(TerminationCondition, Component[FunctionCallTerminationConfig]):\n",
|
||||
" \"\"\"Terminate the conversation if a FunctionExecutionResult with a specific name is received.\"\"\"\n",
|
||||
"\n",
|
||||
" component_config_schema = FunctionCallTerminationConfig\n",
|
||||
" \"\"\"The schema for the component configuration.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, function_name: str) -> None:\n",
|
||||
" self._terminated = False\n",
|
||||
" self._function_name = function_name\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def terminated(self) -> bool:\n",
|
||||
" return self._terminated\n",
|
||||
"\n",
|
||||
" async def __call__(self, messages: Sequence[AgentEvent | ChatMessage]) -> StopMessage | None:\n",
|
||||
" if self._terminated:\n",
|
||||
" raise TerminatedException(\"Termination condition has already been reached\")\n",
|
||||
" for message in messages:\n",
|
||||
" if isinstance(message, ToolCallExecutionEvent):\n",
|
||||
" for execution in message.content:\n",
|
||||
" if execution.name == self._function_name:\n",
|
||||
" self._terminated = True\n",
|
||||
" return StopMessage(\n",
|
||||
" content=f\"Function '{self._function_name}' was executed.\",\n",
|
||||
" source=\"FunctionCallTermination\",\n",
|
||||
" )\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
" async def reset(self) -> None:\n",
|
||||
" self._terminated = False\n",
|
||||
"\n",
|
||||
" def _to_config(self) -> FunctionCallTerminationConfig:\n",
|
||||
" return FunctionCallTerminationConfig(\n",
|
||||
" function_name=self._function_name,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" @classmethod\n",
|
||||
" def _from_config(cls, config: FunctionCallTerminationConfig) -> Self:\n",
|
||||
" return cls(\n",
|
||||
" function_name=config.function_name,\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's use this new termination condition to stop the conversation when the critic agent approves a message\n",
|
||||
"using the `approve` function call.\n",
|
||||
"\n",
|
||||
"First we create a simple function that will be called when the critic agent approves a message."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def approve() -> None:\n",
|
||||
" \"\"\"Approve the message when all feedbacks have been addressed.\"\"\"\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Then we create the agents. The critic agent is equipped with the `approve` tool."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from autogen_agentchat.agents import AssistantAgent\n",
|
||||
"from autogen_agentchat.teams import RoundRobinGroupChat\n",
|
||||
"from autogen_agentchat.ui import Console\n",
|
||||
"from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
|
||||
"\n",
|
||||
"model_client = OpenAIChatCompletionClient(\n",
|
||||
" model=\"gpt-4o\",\n",
|
||||
" temperature=1,\n",
|
||||
" # api_key=\"sk-...\", # Optional if you have an OPENAI_API_KEY env variable set.\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create the primary agent.\n",
|
||||
"primary_agent = AssistantAgent(\n",
|
||||
" \"primary\",\n",
|
||||
" model_client=model_client,\n",
|
||||
" system_message=\"You are a helpful AI assistant.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create the critic agent with the approve function as a tool.\n",
|
||||
"critic_agent = AssistantAgent(\n",
|
||||
" \"critic\",\n",
|
||||
" model_client=model_client,\n",
|
||||
" tools=[approve], # Register the approve function as a tool.\n",
|
||||
" system_message=\"Provide constructive feedback. Use the approve tool to approve when all feedbacks are addressed.\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now, we create the termination condition and the team.\n",
|
||||
"We run the team with the poem-writing task."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"---------- user ----------\n",
|
||||
"Write a unique, Haiku about the weather in Paris\n",
|
||||
"---------- primary ----------\n",
|
||||
"Raindrops gently fall, \n",
|
||||
"Cobblestones shine in dim light— \n",
|
||||
"Paris dreams in grey. \n",
|
||||
"---------- critic ----------\n",
|
||||
"This Haiku beautifully captures a melancholic yet romantic image of Paris in the rain. The use of sensory imagery like \"Raindrops gently fall\" and \"Cobblestones shine\" effectively paints a vivid picture. It could be interesting to experiment with more distinct seasonal elements of Paris, such as incorporating the Seine River or iconic landmarks in the context of the weather. Overall, it successfully conveys the atmosphere of Paris in subtle, poetic imagery.\n",
|
||||
"---------- primary ----------\n",
|
||||
"Thank you for your feedback! I’m glad you enjoyed the imagery. Here’s another Haiku that incorporates iconic Parisian elements:\n",
|
||||
"\n",
|
||||
"Eiffel stands in mist, \n",
|
||||
"Seine's ripple mirrors the sky— \n",
|
||||
"Spring whispers anew. \n",
|
||||
"---------- critic ----------\n",
|
||||
"[FunctionCall(id='call_QEWJZ873EG4UIEpsQHi1HsAu', arguments='{}', name='approve')]\n",
|
||||
"---------- critic ----------\n",
|
||||
"[FunctionExecutionResult(content='None', name='approve', call_id='call_QEWJZ873EG4UIEpsQHi1HsAu', is_error=False)]\n",
|
||||
"---------- critic ----------\n",
|
||||
"None\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"TaskResult(messages=[TextMessage(source='user', models_usage=None, metadata={}, content='Write a unique, Haiku about the weather in Paris', type='TextMessage'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=30, completion_tokens=23), metadata={}, content='Raindrops gently fall, \\nCobblestones shine in dim light— \\nParis dreams in grey. ', type='TextMessage'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=99, completion_tokens=90), metadata={}, content='This Haiku beautifully captures a melancholic yet romantic image of Paris in the rain. The use of sensory imagery like \"Raindrops gently fall\" and \"Cobblestones shine\" effectively paints a vivid picture. It could be interesting to experiment with more distinct seasonal elements of Paris, such as incorporating the Seine River or iconic landmarks in the context of the weather. Overall, it successfully conveys the atmosphere of Paris in subtle, poetic imagery.', type='TextMessage'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=152, completion_tokens=48), metadata={}, content=\"Thank you for your feedback! I’m glad you enjoyed the imagery. Here’s another Haiku that incorporates iconic Parisian elements:\\n\\nEiffel stands in mist, \\nSeine's ripple mirrors the sky— \\nSpring whispers anew. \", type='TextMessage'), ToolCallRequestEvent(source='critic', models_usage=RequestUsage(prompt_tokens=246, completion_tokens=11), metadata={}, content=[FunctionCall(id='call_QEWJZ873EG4UIEpsQHi1HsAu', arguments='{}', name='approve')], type='ToolCallRequestEvent'), ToolCallExecutionEvent(source='critic', models_usage=None, metadata={}, content=[FunctionExecutionResult(content='None', name='approve', call_id='call_QEWJZ873EG4UIEpsQHi1HsAu', is_error=False)], type='ToolCallExecutionEvent'), ToolCallSummaryMessage(source='critic', models_usage=None, metadata={}, content='None', type='ToolCallSummaryMessage')], stop_reason=\"Function 'approve' was executed.\")"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"function_call_termination = FunctionCallTermination(function_name=\"approve\")\n",
|
||||
"round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=function_call_termination)\n",
|
||||
"\n",
|
||||
"# Use asyncio.run(...) if you are running this script as a standalone script.\n",
|
||||
"await Console(round_robin_team.run_stream(task=\"Write a unique, Haiku about the weather in Paris\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can see that the conversation stopped when the critic agent approved the message using the `approve` function call."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user