Autogen Tutorial
Autogen Tutorial
In many cases, agents need access to LLM model services such as OpenAI, Azure OpenAI,
or local models. Since there are many different providers with different APIs, autogen-core
implements a protocol for model clients and autogen-ext implements a set of model clients
for popular model services. AgentChat can use these model clients to interact with model
services.
This section provides a quick overview of available model clients. For more details on how to
use them directly, please refer to Model Clients in the Core API documentation.
Note
See ChatCompletionCache for a caching wrapper to use with the following clients.
OpenAI
To access OpenAI models, install the openai extension, which allows you to use the
OpenAIChatCompletionClient.
openai_model_client = OpenAIChatCompletionClient(
model="gpt-4o-2024-08-06",
# api_key="sk-...", # Optional if you have an OPENAI_API_KEY environment variable set.
)
To test the model client, you can use the following code:
You can use this client with models hosted on OpenAI-compatible endpoints, however, we
have not tested this functionality. See OpenAIChatCompletionClient for more information.
Azure OpenAI
Similarly, install the azure and openai extensions to use the
AzureOpenAIChatCompletionClient.
The following code snippet shows how to use AAD authentication. The identity used must be
assigned the Cognitive Services OpenAI User role.
az_model_client = AzureOpenAIChatCompletionClient(
azure_deployment="{your-azure-deployment}",
model="{model-name, such as gpt-4o}",
api_version="2024-06-01",
azure_endpoint="https://{your-custom-endpoint}.openai.azure.com/",
azure_ad_token_provider=token_provider, # Optional if you choose key-based
authentication.
# api_key="sk-...", # For key-based authentication.
)
See here for how to use the Azure client directly or for more information.
Azure AI Foundry
Azure AI Foundry (previously known as Azure AI Studio) offers models hosted on Azure. To
use those models, you use the AzureAIChatCompletionClient.
import os
client = AzureAIChatCompletionClient(
model="Phi-4",
endpoint="https://models.inference.ai.azure.com",
# To authenticate with the model you will need to generate a personal access token (PAT)
in your GitHub settings.
# Create your PAT token by following instructions here:
https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-
your-personal-access-tokens
credential=AzureKeyCredential(os.environ["GITHUB_TOKEN"]),
model_info={
"json_output": False,
"function_calling": False,
"vision": False,
"family": "unknown",
},
)
Note
Small local models are typically not as capable as larger models on the cloud. For some
tasks they may not perform as well and the output may be suprising.
model_client = OpenAIChatCompletionClient(
model="llama3.2:latest",
base_url="http://localhost:11434/v1",
api_key="placeholder",
model_info={
"vision": False,
"function_calling": True,
"json_output": False,
"family": "unknown",
},
)
Types of Messages
At a high level, messages in AgentChat can be categorized into two types: agent-agent
messages and an agent’s internal events and messages.
Agent-Agent Messages
AgentChat supports many message types for agent-to-agent communication. They belong to
the union type ChatMessage. This message type allows both text and multimodal
communication and subsumes other message types, such as TextMessage or
MultiModalMessage.
For example, the following code snippet demonstrates how to create a text message, which
accepts a string content and a string source:
import requests
from autogen_agentchat.messages import MultiModalMessage
from autogen_core import Image as AGImage
from PIL import Image
pil_image = Image.open(BytesIO(requests.get("https://picsum.photos/300/200").content))
img = AGImage(pil_image)
multi_modal_message = MultiModalMessage(content=["Can you describe the content of this
image?", img], source="User")
img
Internal Events
AgentChat also supports the concept of events - messages that are internal to an agent.
These messages are used to communicate events and information on actions within the
agent itself, and belong to the union type AgentEvent.
Examples of these include ToolCallRequestEvent, which indicates that a request was made
to call a tool, and ToolCallExecutionEvent, which contains the results of tool calls.
Typically, events are created by the agent itself and are contained in the inner_messages
field of the Response returned from on_messages. If you are building a custom agent and
have events that you want to communicate to other entities (e.g., a UI), you can include
these in the inner_messages field of the Response. We will show examples of this in
Custom Agents.
You can read about the full set of messages supported in AgentChat in the messages
module.
Agents
AutoGen AgentChat provides a set of preset Agents, each with variations in how an agent
might respond to messages. All agents share the following attributes and methods:
Assistant Agent
AssistantAgent is a built-in agent that uses a language model and has the ability to use
tools.
Note
It is important to note that on_messages() will update the internal state of the agent – it will
add the messages to the agent’s history. So you should call this method with new
messages. You should not repeatedly call this method with the same messages or the
complete history.
Note
Unlike in v0.2 AgentChat, the tools are executed by the same agent directly within the same
call to on_messages(). By default, the agent will return the result of the tool call as the final
response.
You can also call the run() method, which is a convenience method that calls
on_messages(). It follows the same interface as Teams and returns a TaskResult object.
Multi-Modal Input
The AssistantAgent can handle multi-modal input by providing the input as a
MultiModalMessage.
import PIL
import requests
from autogen_agentchat.messages import MultiModalMessage
from autogen_core import Image
Streaming Messages
We can also stream each message as it is generated by the agent by using the
on_messages_stream() method, and use Console to print the messages as they appear to
the console.
From the messages, you can observe that the assistant agent utilized the web_search tool
to gather information and responded based on the search results.
You can also use run_stream() to get the same streaming behavior as
on_messages_stream(). It follows the same interface as Teams.
Using Tools
Large Language Models (LLMs) are typically limited to generating text or code responses.
However, many complex tasks benefit from the ability to use external tools that perform
specific actions, such as fetching data from APIs or databases.
To address this limitation, modern LLMs can now accept a list of available tool schemas
(descriptions of tools and their arguments) and generate a tool call message. This capability
is known as Tool Calling or Function Calling and is becoming a popular pattern in building
intelligent agent-based applications. Refer to the documentation from OpenAI and Anthropic
for more information about tool calling in LLMs.
In AgentChat, the AssistantAgent can use tools to perform specific actions. The web_search
tool is one such tool that allows the assistant agent to search the web for information. A
custom tool can be a Python function or a subclass of the BaseTool.
By default, when AssistantAgent executes a tool, it will return the tool’s output as a string in
ToolCallSummaryMessage in its response. If your tool does not return a well-formed string in
natural language, you can add a reflection step to have the model summarize the tool’s
output, by setting the reflect_on_tool_use=True parameter in the AssistantAgent constructor.
Langchain Tools
In addition to custom tools, you can also use tools from the Langchain library by wrapping
them in LangChainToolAdapter.
import pandas as pd
from autogen_ext.tools.langchain import LangChainToolAdapter
from langchain_experimental.tools.python.tool import PythonAstREPLTool
df = pd.read_csv("https://raw.githubusercontent.com/pandas-dev/pandas/main/doc/data/
titanic.csv")
tool = LangChainToolAdapter(PythonAstREPLTool(locals={"df": df}))
model_client = OpenAIChatCompletionClient(model="gpt-4o")
agent = AssistantAgent(
"assistant", tools=[tool], model_client=model_client, system_message="Use the `df`
variable to access the dataset."
)
await Console(
agent.on_messages_stream(
[TextMessage(content="What's the average age of the passengers?", source="user")],
CancellationToken()
),
output_stats=True,
)
---------- assistant ----------
[FunctionCall(id='call_BEYRkf53nBS1G2uG60wHP0zf',
arguments='{"query":"df[\'Age\'].mean()"}', name='python_repl_ast')]
[Prompt tokens: 111, Completion tokens: 22]
---------- assistant ----------
[FunctionExecutionResult(content='29.69911764705882',
call_id='call_BEYRkf53nBS1G2uG60wHP0zf')]
---------- assistant ----------
29.69911764705882
---------- Summary ----------
Number of inner messages: 2
Total prompt tokens: 111
Total completion tokens: 22
Duration: 0.62 seconds
Response(chat_message=ToolCallSummaryMessage(source='assistant',
models_usage=None, content='29.69911764705882', type='ToolCallSummaryMessage'),
inner_messages=[ToolCallRequestEvent(source='assistant',
models_usage=RequestUsage(prompt_tokens=111, completion_tokens=22),
content=[FunctionCall(id='call_BEYRkf53nBS1G2uG60wHP0zf',
arguments='{"query":"df[\'Age\'].mean()"}', name='python_repl_ast')],
type='ToolCallRequestEvent'), ToolCallExecutionEvent(source='assistant',
models_usage=None, content=[FunctionExecutionResult(content='29.69911764705882',
call_id='call_BEYRkf53nBS1G2uG60wHP0zf')], type='ToolCallExecutionEvent')])
Parallel Tool Calls
Some models support parallel tool calls, which can be useful for tasks that require multiple
tools to be called simultaneously. By default, if the model client produces multiple tool calls,
AssistantAgent will call the tools in parallel.
You may want to disable parallel tool calls when the tools have side effects that may
interfere with each other, or, when agent behavior needs to be consistent across different
models. This should be done at the model client level.
model_client_no_parallel_tool_call = OpenAIChatCompletionClient(
model="gpt-4o",
parallel_tool_calls=False, # type: ignore
)
agent_no_parallel_tool_call = AssistantAgent(
name="assistant",
model_client=model_client_no_parallel_tool_call,
tools=[web_search],
system_message="Use tools to solve tasks.",
)
Structured Output
Structured output allows models to return structured JSON text with pre-defined schema
provided by the application. Different from JSON-mode, the schema can be provided as a
Pydantic BaseModel class, which can also be used to validate the output.
Note
Structured output is only available for models that support it. It also requires the model client
to support structured output as well. Currently, the OpenAIChatCompletionClient and
AzureOpenAIChatCompletionClient support structured output.
Structured output is also useful for incorporating Chain-of-Thought reasoning in the agent’s
responses. See the example below for how to use structured output with the assistant agent.
# Create an agent that uses the OpenAI GPT-4o model with the custom response format.
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
response_format=AgentResponse, # type: ignore
)
agent = AssistantAgent(
"assistant",
model_client=model_client,
system_message="Categorize the input as happy, sad, or neutral following the JSON
format.",
)
The underlying model API must support streaming tokens for this to work. Please check with
your model provider to see if this is supported.
model_client = OpenAIChatCompletionClient(model="gpt-4o")
streaming_assistant = AssistantAgent(
name="assistant",
model_client=model_client,
system_message="You are a helpful assistant.",
model_client_stream=True, # Enable streaming tokens.
)
# Use an async function and asyncio.run() in a script.
async for message in streaming_assistant.on_messages_stream( # type: ignore
[TextMessage(content="Name two cities in South America", source="user")],
cancellation_token=CancellationToken(),
):
print(message)
source='assistant' models_usage=None content='Two'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' cities'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' in'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' South'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' America'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' are'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Buenos'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Aires'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' in'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Argentina'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' and'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' São'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Paulo'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' in'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Brazil'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content='.' type='ModelClientStreamingChunkEvent'
Response(chat_message=TextMessage(source='assistant',
models_usage=RequestUsage(prompt_tokens=0, completion_tokens=0), content='Two
cities in South America are Buenos Aires in Argentina and São Paulo in Brazil.',
type='TextMessage'), inner_messages=[])
You can see the streaming chunks in the output above. The chunks are generated by the
model client and are yielded by the agent as they are received. The final response, the
concatenation of all the chunks, is yielded right after the last chunk.
Similarly, run_stream() will also yield the same streaming chunks, followed by a full text
message right after the last chunk.
async for message in streaming_assistant.run_stream(task="Name two cities in North
America."): # type: ignore
print(message)
source='user' models_usage=None content='Name two cities in North America.'
type='TextMessage'
source='assistant' models_usage=None content='Two'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' cities'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' in'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' North'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' America'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' are'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' New'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' York'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' City'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' in'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' the'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' United'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' States'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' and'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Toronto'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' in'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content=' Canada'
type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=None content='.' type='ModelClientStreamingChunkEvent'
source='assistant' models_usage=RequestUsage(prompt_tokens=0, completion_tokens=0)
content='Two cities in North America are New York City in the United States and Toronto in
Canada.' type='TextMessage'
TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Name
two cities in North America.', type='TextMessage'), TextMessage(source='assistant',
models_usage=RequestUsage(prompt_tokens=0, completion_tokens=0), content='Two
cities in North America are New York City in the United States and Toronto in Canada.',
type='TextMessage')], stop_reason=None)
Using Model Context
AssistantAgent has a model_context parameter that can be used to pass in a
ChatCompletionContext object. This allows the agent to use different model contexts, such
as BufferedChatCompletionContext to limit the context sent to the model.
# Create an agent that uses only the last 5 messages in the context to generate responses.
agent = AssistantAgent(
name="assistant",
model_client=model_client,
tools=[web_search],
system_message="Use tools to solve tasks.",
model_context=BufferedChatCompletionContext(buffer_size=5), # Only use the last 5
messages in the context.
)
Other Preset Agents
The following preset agents are available:
MultimodalWebSurfer: A multi-modal agent that can search the web and visit web pages for
information.
FileSurfer: An agent that can search and browse local files for information.
We’ll first show you how to create and run a team. We’ll then explain how to observe the
team’s behavior, which is crucial for debugging and understanding the team’s performance,
and common operations to control the team’s behavior.
Note
When should you use a team? Teams are for complex tasks that require collaboration and
diverse expertise. However, they also demand more scaffolding to steer compared to single
agents. While AutoGen simplifies the process of working with teams, start with a single
agent for simpler tasks, and transition to a multi-agent team when a single agent proves
inadequate. Ensure that you have optimized your single agent with the appropriate tools and
instructions before moving to a team-based approach.
Creating a Team
RoundRobinGroupChat is a simple yet effective team configuration where all agents share
the same context and take turns responding in a round-robin fashion. Each agent, during its
turn, broadcasts its response to all other agents, ensuring that the entire team maintains a
consistent context.
The two-agent team implements the reflection pattern, a multi-agent design pattern where a
critic agent evaluates the responses of a primary agent. Learn more about the reflection
pattern using the Core API.
import asyncio
# Define a termination condition that stops the task if the critic approves.
text_termination = TextMentionTermination("APPROVE")
Observing a Team
Similar to the agent’s on_messages_stream() method, you can stream the team’s messages
while it is running by calling the run_stream() method. This method returns a generator that
yields messages produced by the agents in the team as they are generated, with the final
item being the TaskResult object.
# When running inside a script, use a async main function and call it from `asyncio.run(...)`.
await team.reset() # Reset the team for a new task.
async for message in team.run_stream(task="Write a short poem about the fall season."): #
type: ignore
if isinstance(message, TaskResult):
print("Stop Reason:", message.stop_reason)
else:
print(message)
source='user' models_usage=None content='Write a short poem about the fall season.'
type='TextMessage'
source='primary' models_usage=RequestUsage(prompt_tokens=28,
completion_tokens=105) content="Leaves descend in golden dance, \nWhispering secrets
as they fall, \nCrisp air brings a gentle trance, \nHeralding Autumn's call. \n\nPumpkins
glow with orange light, \nFields wear a cloak of amber hue, \nDays retreat to longer night, \
nSkies shift to deeper blue. \n\nWinds carry scents of earth and pine, \nSweaters wrap us,
warm and tight, \nNature's canvas, bold design, \nIn Fall's embrace, we find delight. "
type='TextMessage'
source='critic' models_usage=RequestUsage(prompt_tokens=150, completion_tokens=226)
content='Your poem beautifully captures the essence of fall with vivid imagery and a
soothing rhythm. The imagery of leaves descending, pumpkins glowing, and fields cloaked in
amber hues effectively paints a picture of the autumn season. The use of contrasting
elements like "Days retreat to longer night" and "Sweaters wrap us, warm and tight" provides
a nice balance between the cold and warmth associated with the season. Additionally, the
personification of autumn through phrases like "Autumn\'s call" and "Nature\'s canvas, bold
design" adds depth to the depiction of fall.\n\nTo enhance the poem further, you might
consider focusing on the soundscape of fall, such as the rustling of leaves or the distant call
of migrating birds, to engage readers\' auditory senses. Also, varying the line lengths slightly
could add a dynamic flow to the reading experience.\n\nOverall, your poem is engaging and
effectively encapsulates the beauty and transition of fall. With a few adjustments to explore
other sensory details, it could become even more immersive. \n\nIf you incorporate some of
these suggestions or find another way to expand the sensory experience, please share your
update!' type='TextMessage'
source='primary' models_usage=RequestUsage(prompt_tokens=369,
completion_tokens=143) content="Thank you for the thoughtful critique and suggestions.
Here's a revised version of the poem with added attention to auditory senses and varied line
lengths:\n\nLeaves descend in golden dance, \nWhisper secrets in their fall, \nBreezes hum
a gentle trance, \nHeralding Autumn's call. \n\nPumpkins glow with orange light, \nAmber
fields beneath wide skies, \nDays retreat to longer night, \nChill winds and distant cries. \n\
nRustling whispers of the trees, \nSweaters wrap us, snug and tight, \nNature's canvas,
bold and free, \nIn Fall's embrace, pure delight. \n\nI appreciate your feedback and hope
this version better captures the sensory richness of the season!" type='TextMessage'
source='critic' models_usage=RequestUsage(prompt_tokens=529, completion_tokens=160)
content='Your revised poem is a beautiful enhancement of the original. By incorporating
auditory elements such as "Breezes hum" and "Rustling whispers of the trees," you\'ve
added an engaging soundscape that draws the reader deeper into the experience of fall.
The varied line lengths work well to create a more dynamic rhythm throughout the poem,
adding interest and variety to each stanza.\n\nThe succinct, yet vivid, lines of "Chill winds
and distant cries" wonderfully evoke the atmosphere of the season, adding a touch of
mystery and depth. The final stanza wraps up the poem nicely, celebrating the complete
sensory embrace of fall with lines like "Nature\'s canvas, bold and free."\n\nYou\'ve
successfully infused more sensory richness into the poem, enhancing its overall emotional
and atmospheric impact. Great job on the revisions!\n\nAPPROVE' type='TextMessage'
Stop Reason: Text 'APPROVE' mentioned
As demonstrated in the example above, you can determine the reason why the team
stopped by checking the stop_reason attribute.
The Console() method provides a convenient way to print messages to the console with
proper formatting.
To enhance the poem further, you might consider adding more sensory details to make the
reader feel even more immersed in the experience. For example, including specific sounds,
scents, or textures could deepen the connection to autumn's ambiance. Additionally, you
could explore the emotional transitions as the season prepares for winter to provide a
reflective element to the piece.
Overall, it's a lovely and evocative depiction of fall, evoking feelings of comfort and
appreciation for nature's changing beauty. Great work!
[Prompt tokens: 144, Completion tokens: 157]
---------- primary ----------
Thank you for your thoughtful feedback! I'm glad you enjoyed the imagery and warmth in the
poem. To enhance the sensory experience and emotional depth, here's a revised version
incorporating your suggestions:
---
---
I hope this version enhances the sensory and emotional elements of the season. Thank you
again for your insights!
[Prompt tokens: 294, Completion tokens: 195]
---------- critic ----------
APPROVE
[Prompt tokens: 506, Completion tokens: 4]
---------- Summary ----------
Number of messages: 5
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 972
Total completion tokens: 455
Duration: 11.78 seconds
TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a
short poem about the fall season.', type='TextMessage'), TextMessage(source='primary',
models_usage=RequestUsage(prompt_tokens=28, completion_tokens=99),
content="Golden leaves in crisp air dance, \nWhispering tales as they prance. \nAmber
hues paint the ground, \nNature's symphony all around. \n\nSweaters hug with tender
grace, \nWhile pumpkins smile, a warm embrace. \nChill winds hum through towering trees,
\nA vibrant tapestry in the breeze. \n\nHarvest moons in twilight glow, \nCasting magic on
fields below. \nFall's embrace, a gentle call, \nTo savor beauty before snowfalls. ",
type='TextMessage'), TextMessage(source='critic',
models_usage=RequestUsage(prompt_tokens=144, completion_tokens=157),
content="Your poem beautifully captures the essence of the fall season, creating a vivid and
cozy atmosphere. The imagery of golden leaves and amber hues paints a picturesque scene
that many can easily relate to. I particularly appreciate the personification of pumpkins and
the gentle embrace of sweaters, which adds warmth to your verses. \n\nTo enhance the
poem further, you might consider adding more sensory details to make the reader feel even
more immersed in the experience. For example, including specific sounds, scents, or
textures could deepen the connection to autumn's ambiance. Additionally, you could explore
the emotional transitions as the season prepares for winter to provide a reflective element to
the piece.\n\nOverall, it's a lovely and evocative depiction of fall, evoking feelings of comfort
and appreciation for nature's changing beauty. Great work!", type='TextMessage'),
TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=294,
completion_tokens=195), content="Thank you for your thoughtful feedback! I'm glad you
enjoyed the imagery and warmth in the poem. To enhance the sensory experience and
emotional depth, here's a revised version incorporating your suggestions:\n\n---\n\nGolden
leaves in crisp air dance, \nWhispering tales as they prance. \nAmber hues paint the
crunchy ground, \nNature's symphony all around. \n\nSweaters hug with tender grace, \
nWhile pumpkins grin, a warm embrace. \nChill winds hum through towering trees, \
nCrackling fires warm the breeze. \n\nApples in the orchard's glow, \nSweet cider scents
that overflow. \nCrunch of paths beneath our feet, \nCinnamon spice and toasty heat. \n\
nHarvest moons in twilight's glow, \nCasting magic on fields below. \nFall's embrace, a
gentle call, \nReflects on life's inevitable thaw. \n\n--- \n\nI hope this version enhances the
sensory and emotional elements of the season. Thank you again for your insights!",
type='TextMessage'), TextMessage(source='critic',
models_usage=RequestUsage(prompt_tokens=506, completion_tokens=4),
content='APPROVE', type='TextMessage')], stop_reason="Text 'APPROVE' mentioned")
Resetting a Team
You can reset the team by calling the reset() method. This method will clear the team’s state,
including all agents. It will call the each agent’s on_reset() method to clear the agent’s state.
Stopping a Team
Apart from automatic termination conditions such as TextMentionTermination that stops the
team based on the internal state of the team, you can also stop the team from outside by
using the ExternalTermination.
Calling set() on ExternalTermination will stop the team when the current agent’s turn is over.
Thus, the team may not stop immediately. This allows the current agent to finish its turn and
broadcast the final message to the team before the team stops, keeping the team’s state
consistent.
Resuming a Team
Teams are stateful and maintains the conversation history and context after each run, unless
you reset the team.
You can resume a team to continue from where it left off by calling the run() or run_stream()
method again without a new task. RoundRobinGroupChat will continue from the next agent
in the round-robin order.
2. **Sensory Details**: While the poem already evokes visual and tactile senses,
incorporating other senses such as sound or smell could deepen the immersion. For
example, include the scent of wood smoke or the crunch of leaves underfoot.
3. **Metaphorical Language**: Adding metaphors or similes can further enrich the imagery.
For example, you might compare the leaves falling to a golden rain or the chill in the air to a
gentle whisper.
Overall, it’s a lovely depiction of fall. These suggestions are minor tweaks that might elevate
the reader's experience even further. Nice work!
I hope these changes enhance the imagery and sensory experience. Thank you again for
your feedback!
[Prompt tokens: 389, Completion tokens: 150]
---------- critic ----------
Your revisions have made the poem even more evocative and immersive. The use of
sensory details, such as "whispers of wind" and "crackling leaves," beautifully enriches the
poem, engaging multiple senses. The metaphorical language, like "a golden rain from trees’
canopies" and "Fall’s embrace, warm as whispered years," adds depth and enhances the
emotional warmth of the poem. The structural variation with the inclusion of dashes
effectively adds emphasis and flow.
Overall, these changes bring greater vibrancy and life to the poem, allowing readers to truly
experience the wonders of fall. Excellent work on the revisions!
APPROVE
[Prompt tokens: 556, Completion tokens: 132]
---------- Summary ----------
Number of messages: 3
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 1104
Total completion tokens: 519
Duration: 9.79 seconds
TaskResult(messages=[TextMessage(source='critic',
models_usage=RequestUsage(prompt_tokens=159, completion_tokens=237), content='This
poem beautifully captures the essence of the fall season with vivid imagery and a soothing
rhythm. The descriptions of the changing leaves, cool air, and various autumn traditions
make it easy for readers to envision and feel the charm of fall. Here are a few suggestions to
enhance its impact:\n\n1. **Structure Variation**: Consider breaking some lines with a
hyphen or ellipsis for dramatic effect or emphasis. For instance, “Sweaters wrapped tight as
twilight nears— / Fall’s charming embrace, as warm as it appears."\n\n2. **Sensory
Details**: While the poem already evokes visual and tactile senses, incorporating other
senses such as sound or smell could deepen the immersion. For example, include the scent
of wood smoke or the crunch of leaves underfoot.\n\n3. **Metaphorical Language**: Adding
metaphors or similes can further enrich the imagery. For example, you might compare the
leaves falling to a golden rain or the chill in the air to a gentle whisper.\n\nOverall, it’s a
lovely depiction of fall. These suggestions are minor tweaks that might elevate the reader\'s
experience even further. Nice work!\n\nLet me know if these feedbacks are addressed.',
type='TextMessage'), TextMessage(source='primary',
models_usage=RequestUsage(prompt_tokens=389, completion_tokens=150),
content='Thank you for the thoughtful feedback! Here’s a revised version, incorporating your
suggestions: \n\nLeaves of amber, gold—drifting like dreams, \nA golden rain from trees’
canopies. \nWhispers of wind—a gentle breath, \nNature’s scented tapestry embracing
earth. \n\nHarvest moons rise as evenings chill, \nFields of plenty paint every hill. \
nSweaters wrapped tight as twilight nears— \nFall’s embrace, warm as whispered years. \
n\nPumpkins aglow with autumn’s light, \nCrackling leaves underfoot in flight. \nIn every
leaf and breeze that calls, \nWe find the magic of glorious fall. \n\nI hope these changes
enhance the imagery and sensory experience. Thank you again for your feedback!',
type='TextMessage'), TextMessage(source='critic',
models_usage=RequestUsage(prompt_tokens=556, completion_tokens=132),
content='Your revisions have made the poem even more evocative and immersive. The use
of sensory details, such as "whispers of wind" and "crackling leaves," beautifully enriches the
poem, engaging multiple senses. The metaphorical language, like "a golden rain from trees’
canopies" and "Fall’s embrace, warm as whispered years," adds depth and enhances the
emotional warmth of the poem. The structural variation with the inclusion of dashes
effectively adds emphasis and flow. \n\nOverall, these changes bring greater vibrancy and
life to the poem, allowing readers to truly experience the wonders of fall. Excellent work on
the revisions!\n\nAPPROVE', type='TextMessage')], stop_reason="Text 'APPROVE'
mentioned")
You can see the team resumed from where it left off in the output above, and the first
message is from the next agent after the last agent that spoke before the team stopped.
Let’s resume the team again with a new task while keeping the context about the previous
task.
# The new task is to translate the same poem to Chinese Tang-style poetry.
await Console(team.run_stream(task="将这首诗用中文唐诗风格写一遍。"))
---------- user ----------
将这首诗用中文唐诗风格写一遍。
---------- primary ----------
朔风轻拂叶飘金,
枝上斜阳染秋林。
满山丰收人欢喜,
月明归途衣渐紧。
南瓜影映灯火中,
落叶沙沙伴归程。
片片秋意随风起,
秋韵悠悠心自明。
[Prompt tokens: 700, Completion tokens: 77]
---------- critic ----------
这首改编的唐诗风格诗作成功地保留了原诗的意境与情感,体现出秋季特有的氛围和美感。通过“朔风轻拂叶飘金”、“枝上斜阳染秋林”等意象,生动地
描绘出了秋天的景色,与唐诗中的自然意境相呼应。且“月明归途衣渐紧”、“落叶沙沙伴归程”让人感受到秋天的安宁与温暖。
通过这些诗句,读者能够感受到秋天的惬意与宁静,勾起丰收与团圆的画面,是一次成功的翻译改编。
APPROVE
[Prompt tokens: 794, Completion tokens: 161]
---------- Summary ----------
Number of messages: 3
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 1494
Total completion tokens: 238
Duration: 3.89 seconds
TaskResult(messages=[TextMessage(source='user', models_usage=None,
content='将这首诗用中文唐诗风格写一遍。', type='TextMessage'),
TextMessage(source='primary',
models_usage=RequestUsage(prompt_tokens=700, completion_tokens=77),
content='朔风轻拂叶飘金, \n 枝上斜阳染秋林。 \n 满山丰收人欢喜, \n 月明归途衣渐紧。 \n\n 南瓜影映灯火中, \n 落叶沙
沙伴归程。 \n 片片秋意随风起, \n 秋韵悠悠心自明。 ', type='TextMessage'),
TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=794,
completion_tokens=161), content='这首改编的唐诗风格诗作成功地保留了原诗的意境与情感,体现出秋季特有的氛围和美
感。通过“朔风轻拂叶飘金”、“枝上斜阳染秋林”等意象,生动地描绘出了秋天的景色,与唐诗中的自然意境相呼应。且“月明归途衣渐紧”、“落叶沙沙
伴归程”让人感受到秋天的安宁与温暖。\n\n 通过这些诗句,读者能够感受到秋天的惬意与宁静,勾起丰收与团圆的画面,是一次成功的翻译改编。\
n\nAPPROVE', type='TextMessage')], stop_reason="Text 'APPROVE' mentioned")
Aborting a Team
You can abort a call to run() or run_stream() during execution by setting a
CancellationToken passed to the cancellation_token parameter.
Different from stopping a team, aborting a team will immediately stop the team and raise a
CancelledError exception.
Note
The caller will get a CancelledError exception when the team is aborted.
try:
result = await run # This will raise a CancelledError.
except asyncio.CancelledError:
print("Task was cancelled.")
Task was cancelled.
Human-in-the-Loop
In the previous section Teams, we have seen how to create, observe, and control a team of
agents. This section will focus on how to interact with the team from your application, and
provide human feedback to the team.
There are two main ways to interact with the team from your application:
Once the run terminates, provide feedback through input to the next call to run() or
run_stream().
To use the UserProxyAgent, you can create an instance of it and include it in the team
before running the team. The team will decide when to call the UserProxyAgent to ask for
feedback from the user.
The following diagram illustrates how you can use UserProxyAgent to get feedback from the
user during a team’s run:
human-in-the-loop-user-proxy
The bold arrows indicates the flow of control during a team’s run: when the team calls the
UserProxyAgent, it transfers the control to the application/user, and waits for the feedback;
once the feedback is provided, the control is transferred back to the team and the team
continues its execution.
Note
When UserProxyAgent is called during a run, it blocks the execution of the team until the
user provides feedback or errors out. This will hold up the team’s progress and put the team
in an unstable state that cannot be saved or resumed.
Due to the blocking nature of this approach, it is recommended to use it only for short
interactions that require immediate feedback from the user, such as asking for approval or
disapproval with a button click, or an alert requiring immediate attention otherwise failing the
task.
# Create the termination condition which will end the conversation when the user says
"APPROVE".
termination = TextMentionTermination("APPROVE")
@app.websocket("/ws/chat")
async def chat(websocket: WebSocket):
await websocket.accept()
Note
For how to save and load the state of a team, please refer to Managing State. This section
will focus on the feedback mechanisms.
human-in-the-loop-termination
Set the maximum number of turns so that the team always stops after the specified number
of turns.
You can use both methods together to achieve your desired behavior.
Using Max Turns
This method allows you to pause the team for user input by setting a maximum number of
turns. For instance, you can configure the team to stop after the first agent responds by
setting max_turns to 1. This is particularly useful in scenarios where continuous user
engagement is required, such as in a chatbot.
Note
Let’s create a team with a single AssistantAgent agent with a handoff setting, and run the
team with a task that requires additional input from the user because the agent doesn’t have
relevant tools to continue processing the task.
Note
The model used with AssistantAgent must support tool call to use the handoff feature.
# Create a lazy assistant agent that always hands off to the user.
lazy_agent = AssistantAgent(
"lazy_assistant",
model_client=model_client,
handoffs=[Handoff(target="user", message="Transfer to user.")],
system_message="If you cannot complete the task, transfer to user. Otherwise, when
finished, respond with 'TERMINATE'.",
)
# Create a single-agent team with the lazy assistant and both termination conditions.
lazy_agent_team = RoundRobinGroupChat([lazy_agent],
termination_condition=handoff_termination | text_termination)
They are stateful but reset automatically after each run (run() or run_stream()) is finished.
Note
For group chat teams (i.e., RoundRobinGroupChat, SelectorGroupChat, and Swarm), the
termination condition is called after each agent responds. While a response may contain
multiple inner messages, the team calls its termination condition just once for all the
messages from a single response. So the condition is called with the “delta sequence” of
messages since the last time it was called.
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
temperature=1,
# api_key="sk-...", # Optional if you have an OPENAI_API_KEY env variable set.
)
max_msg_termination = MaxMessageTermination(max_messages=3)
round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent],
termination_condition=max_msg_termination)
For example:
Soft rain whispers down,
Cobblestones glisten softly —
Paris dreams in gray.
This revision maintains the essence of your original lines while adhering to the traditional
Haiku structure.
[Prompt tokens: 70, Completion tokens: 120]
---------- Summary ----------
Number of messages: 3
Finish reason: Maximum number of messages 3 reached, current message count: 3
Total prompt tokens: 100
Total completion tokens: 139
Duration: 3.34 seconds
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')
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.
APPROVE
[Prompt tokens: 234, Completion tokens: 54]
---------- primary ----------
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!
[Prompt tokens: 279, Completion tokens: 39]
---------- Summary ----------
Number of messages: 3
Finish reason: Maximum number of messages 3 reached, current message count: 3
Total prompt tokens: 694
Total completion tokens: 125
Duration: 6.43 seconds
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')
The team continued from where it left off, allowing the primary agent to respond to the
feedback.
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.
max_msg_termination = MaxMessageTermination(max_messages=10)
text_termination = TextMentionTermination("APPROVE")
combined_termination = max_msg_termination | text_termination
APPROVE
[Prompt tokens: 746, Completion tokens: 93]
---------- Summary ----------
Number of messages: 3
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 1213
Total completion tokens: 112
Duration: 2.75 seconds
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")
The conversation stopped after the critic agent approved the message, although it could
have also stopped if 10 messages were generated.
Alternatively, if we want to stop the run only when both conditions are met, we can use the
AND (&) operator.
In this notebook, we will discuss how to save and load the state of agents, teams, and
termination conditions.
assistant_agent = AssistantAgent(
name="assistant_agent",
system_message="You are a helpful assistant",
model_client=OpenAIChatCompletionClient(
model="gpt-4o-2024-08-06",
# api_key="YOUR_API_KEY",
),
)
For AssistantAgent, its state consists of the model_context. If your write your own custom
agent, consider overriding the save_state() and load_state() methods to customize the
behavior. The default implementations save and load an empty state.
When we call save_state on a team, it saves the state of all the agents in the team.
We will begin by creating a simple RoundRobinGroupChat team with a single agent and ask
it to write a poem.
# Define a team.
assistant_agent = AssistantAgent(
name="assistant_agent",
system_message="You are a helpful assistant",
model_client=OpenAIChatCompletionClient(
model="gpt-4o-2024-08-06",
),
)
agent_team = RoundRobinGroupChat([assistant_agent],
termination_condition=MaxMessageTermination(max_messages=2))
await agent_team.reset()
stream = agent_team.run_stream(task="What was the last line of the poem you wrote?")
await Console(stream)
---------- user ----------
What was the last line of the poem you wrote?
---------- assistant_agent ----------
I'm sorry, but I am unable to recall or access previous interactions, including any specific
poem I may have composed in our past conversations. If you like, I can write a new poem for
you.
[Prompt tokens: 28, Completion tokens: 40]
---------- Summary ----------
Number of messages: 2
Finish reason: Maximum number of messages 2 reached, current message count: 2
Total prompt tokens: 28
Total completion tokens: 40
Duration: 0.70 seconds
TaskResult(messages=[TextMessage(source='user', models_usage=None, content='What
was the last line of the poem you wrote?', type='TextMessage'),
TextMessage(source='assistant_agent', models_usage=RequestUsage(prompt_tokens=28,
completion_tokens=40), content="I'm sorry, but I am unable to recall or access previous
interactions, including any specific poem I may have composed in our past conversations. If
you like, I can write a new poem for you.", type='TextMessage')], stop_reason='Maximum
number of messages 2 reached, current message count: 2')
Next, we load the state of the team and ask the same question. We see that the team is able
to accurately return the last line of the poem it wrote.
print(team_state)
import json