Swarm Orchestration
With AG2, you can initiate a Swarm Chat similar to OpenAI’s Swarm. This orchestration offers two main features:
- Headoffs: Agents can transfer control to another agent via function calls, enabling smooth transitions within workflows.
- Context Variables: Agents can dynamically update shared variables through function calls, maintaining context and adaptability throughout the process.
Instead of sending a task to a single LLM agent, you can assign it to a swarm of agents. Each agent in the swarm can decide whether to hand off the task to another agent. The chat terminates when the last active agent’s response is a plain string (i.e., it doesn’t suggest a tool call or handoff).
Components
We now introduce the main components that need to be used to create a swarm chat.
Create a SwarmAgent
All the agents passed to the swarm chat should be instances of
SwarmAgent
. SwarmAgent
is very similar to AssistantAgent
, but it
has some additional features to support function registration and
handoffs. When creating a SwarmAgent
, you can pass in a list of
functions. These functions will be converted to schemas to be passed to
the LLMs, and you don’t need to worry about registering the functions
for execution. You can also pass back a SwarmResult
class, where you
can return a value, the next agent to call, and update context variables
at the same time.
Notes for creating the function calls
- For input arguments, you must define the type of the argument,
otherwise, the registration will fail (e.g.
arg_name: str
). - If your function requires access or modification of the context
variables, you must pass in
context_variables: dict
as one argument. This argument will not be visible to the LLM (removed when registering the function schema). But when called, the global context variables will be passed in by the swarm chat. - The docstring of the function will be used as the prompt. So make sure to write a clear description.
- The function name will be used as the tool name.
Registering Handoffs to agents
While you can create a function to decide what next agent to call, we
provide a quick way to register the handoff using ON_CONDITION
. We
will craft this transition function and add it to the LLM config
directly.
Registering Handoffs to a nested chat
In addition to transferring to an agent, you can also trigger a nested
chat by doing a handoff and using ON_CONDITION
. This is a useful way
to perform sub-tasks without that work becoming part of the broader
swarm’s messages.
Configuring the nested chat is similar to establishing a nested chat for an agent.
Nested chats are a set of sequential chats and these are defined like so:
New to nested chats within swarms is the ability to carryover some context from the swarm chat into the nested chat. This is done by adding a carryover configuration. If you’re not using carryover, then no messages from the swarm chat will be brought into the nested chat.
The carryover is applicable only to the first chat in the nested chats and works together with that nested chat’s “message” value, if any.
The summary_method
can be (with messages referring to the swarm chat’s
messages):
"all"
- messages will be converted to a new-line concatenated string, e.g.[first nested chat message]\nContext: \n[swarm message 1]\n[swarm message 2]\n...
"last_msg"
- the latest message will be added, e.g.[first nested chat message]\nContext: \n[swarm's latest message]
"reflection_with_llm"
- utilises an LLM to interpret the messages and its resulting response will be added, e.g.[first nested chat message]\nContext: \n[llm response]
Callable
- a function that returns the full message (this will not concatenate with the first nested chat’s message, it will replace it entirely).
The signature of the summary_method
callable is:
def my_method(agent: ConversableAgent, messages: List[Dict[str, Any]], summary_args: Dict) -> str:
Both the “reflection_with_llm” and Callable will be able to utilise the
summary_args
if they are included.
With your configuration available, you can add it to the first chat in the nested chat:
Finally, we add the nested chat as a handoff in the same way as we do to an agent:
See the documentation on registering a nested
chat
for further information on the parameters
reply_func_from_nested_chats
, use_async
, and config
.
Once a nested chat is complete, the resulting output from the last chat in the nested chats will be returned as the agent that triggered the nested chat’s response.
AFTER_WORK
When the active agent’s response doesn’t suggest a tool call or handoff,
the chat will terminate by default. However, you can register an
AFTER_WORK
handoff to control what to do next. You can register these
AFTER_WORK
handoffs at the agent level and also the swarm level
(through the after_work
parameter on initiate_swarm_chat
). The agent
level takes precedence over the swarm level.
The AFTER_WORK takes a single parameter and this can be an agent, an
agent’s name, an AfterWorkOption
, or a callable function.
The AfterWorkOption
options are: - TERMINATE
: Terminate the chat
STAY
: Stay at the current agentREVERT_TO_USER
: Revert to the user agent. Only if a user agent is passed in when initializing. (See below for more details)
The callable function signature is:
def my_after_work_func(last_speaker: SwarmAgent, messages: List[Dict[str, Any]], groupchat: GroupChat) -> Union[AfterWorkOption, SwarmAgent, str]:
Note: there should only be one AFTER_WORK
, if your requirement is more
complex, use the callable function parameter.
Here are examples of registering AFTER_WORKS
Initialize SwarmChat with initiate_swarm_chat
After a set of swarm agents are created, you can initiate a swarm chat
by calling initiate_swarm_chat
.
How we handle messages:
- Case 1: If you pass in one single message
- If there is a name in that message, we will assume this message is from that agent. (It will be error if that name doesn’t match any agent you passed in.)
- If there is no name, 1. User agent passed in: we assume this message is from the user agent. 2. No user agent passed in: we will create a temporary user agent just to start the chat.
- Case 2: We will use the Resume
GroupChat
feature to resume the chat. The
name
fields in these messages must be one of the names of the agents you passed in, otherwise, it will be an error.
Q&As
How are context variables updated?
In a swarm, the context variables are shared amongst Swarm agents. As
context variables are available at the agent level, you can use the
context variable getters/setters on the agent to view and change the
shared context variables. If you’re working with a function that returns
a SwarmResult
you should update the passed in context variables and
return it in the SwarmResult
, this will ensure the shared context is
updated.
What is the difference between ON_CONDITION and AFTER_WORK?
When registering an ON_CONDITION handoff, we are creating a function schema to be passed to the LLM. The LLM will decide whether to call this function.
When registering an AFTER_WORK handoff, we are defining the fallback mechanism when no tool calls are suggested. This is a higher level of control from the swarm chat level.
When to pass in a user agent?
If your application requires interactions with the user, you can pass in a user agent to the groupchat, so that don’t need to write an outer loop to accept user inputs and call swarm.
Demonstration
Create Swarm Agents
Start Chat
Demo with User Agent
We pass in a user agent to the swarm chat to accept user inputs. With
agent_6
, we register an AFTER_WORK
handoff to revert to the user
agent when no tool calls are suggested.