0% found this document useful (0 votes)
48 views

Autogen Framework Guide

The document outlines the core concepts of AutoGen, focusing on agents, agent runtime, and communication mechanisms for multi-agent applications. It details how to implement agents using the RoutedAgent class, manage their lifecycles through the agent runtime, and communicate via messages, including direct messaging and broadcasting. Additionally, it provides examples of agent implementation, message handling, and registration processes within the SingleThreadedAgentRuntime environment.

Uploaded by

FranMorón10
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
48 views

Autogen Framework Guide

The document outlines the core concepts of AutoGen, focusing on agents, agent runtime, and communication mechanisms for multi-agent applications. It details how to implement agents using the RoutedAgent class, manage their lifecycles through the agent runtime, and communicate via messages, including direct messaging and broadcasting. Additionally, it provides examples of agent implementation, message handling, and registration processes within the SingleThreadedAgentRuntime environment.

Uploaded by

FranMorón10
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 18

Agent and Agent Runtime

In this and the following section, we focus on the core concepts of AutoGen: agents, agent
runtime, messages, and communication – the foundational building blocks for an multi-agent
applications.

Note

The Core API is designed to be unopinionated and flexible. So at times, you may find it
challenging. Continue if you are building an interactive, scalable and distributed multi-agent
system and want full control of all workflows. If you just want to get something running
quickly, you may take a look at the AgentChat API.

An agent in AutoGen is an entity defined by the base interface Agent. It has a unique
identifier of the type AgentId, a metadata dictionary of the type AgentMetadata.

In most cases, you can subclass your agents from higher level class RoutedAgent which
enables you to route messages to corresponding message handler specified with
message_handler() decorator and proper type hint for the message variable. An agent
runtime is the execution environment for agents in AutoGen.

Similar to the runtime environment of a programming language, an agent runtime provides


the necessary infrastructure to facilitate communication between agents, manage agent
lifecycles, enforce security boundaries, and support monitoring and debugging.

For local development, developers can use SingleThreadedAgentRuntime, which can be


embedded in a Python application.

Note

Agents are not directly instantiated and managed by application code. Instead, they are
created by the runtime when needed and managed by the runtime.

If you are already familiar with AgentChat, it is important to note that AgentChat’s agents
such as AssistantAgent are created by application and thus not directly managed by the
runtime. To use an AgentChat agent in Core, you need to create a wrapper Core agent that
delegates messages to the AgentChat agent and let the runtime manage the wrapper agent.

Implementing an Agent
To implement an agent, the developer must subclass the RoutedAgent class and implement
a message handler method for each message type the agent is expected to handle using
the message_handler() decorator. For example, the following agent handles a simple
message type MyMessageType and prints the message it receives:

from dataclasses import dataclass

from autogen_core import AgentId, MessageContext, RoutedAgent, message_handler


@dataclass
class MyMessageType:
content: str

class MyAgent(RoutedAgent):
def __init__(self) -> None:
super().__init__("MyAgent")

@message_handler
async def handle_my_message_type(self, message: MyMessageType, ctx:
MessageContext) -> None:
print(f"{self.id.type} received message: {message.content}")
This agent only handles MyMessageType and messages will be delivered to
handle_my_message_type method. Developers can have multiple message handlers for
different message types by using message_handler() decorator and setting the type hint for
the message variable in the handler function. You can also leverage python typing union for
the message variable in one message handler function if it better suits agent’s logic. See the
next section on message and communication.

Using an AgentChat Agent


If you have an AgentChat agent and want to use it in the Core API, you can create a
wrapper RoutedAgent that delegates messages to the AgentChat agent. The following
example shows how to create a wrapper agent for the AssistantAgent in AgentChat.

from autogen_agentchat.agents import AssistantAgent


from autogen_agentchat.messages import TextMessage
from autogen_ext.models.openai import OpenAIChatCompletionClient

class MyAssistant(RoutedAgent):
def __init__(self, name: str) -> None:
super().__init__(name)
model_client = OpenAIChatCompletionClient(model="gpt-4o")
self._delegate = AssistantAgent(name, model_client=model_client)

@message_handler
async def handle_my_message_type(self, message: MyMessageType, ctx:
MessageContext) -> None:
print(f"{self.id.type} received message: {message.content}")
response = await self._delegate.on_messages(
[TextMessage(content=message.content, source="user")], ctx.cancellation_token
)
print(f"{self.id.type} responded: {response.chat_message.content}")
For how to use model client, see the Model Client section.

Since the Core API is unopinionated, you are not required to use the AgentChat API to use
the Core API. You can implement your own agents or use another agent framework.
Registering Agent Type
To make agents available to the runtime, developers can use the register() class method of
the BaseAgent class. The process of registration associates an agent type, which is uniquely
identified by a string, and a factory function that creates an instance of the agent type of the
given class. The factory function is used to allow automatic creation of agent instances when
they are needed.

Agent type (AgentType) is not the same as the agent class. In this example, the agent type
is AgentType("my_agent") or AgentType("my_assistant") and the agent class is the Python
class MyAgent or MyAssistantAgent. The factory function is expected to return an instance
of the agent class on which the register() class method is invoked. Read Agent Identity and
Lifecycles to learn more about agent type and identity.

Note

Different agent types can be registered with factory functions that return the same agent
class. For example, in the factory functions, variations of the constructor parameters can be
used to create different instances of the same agent class.

To register our agent types with the SingleThreadedAgentRuntime, the following code can
be used:

from autogen_core import SingleThreadedAgentRuntime

runtime = SingleThreadedAgentRuntime()
await MyAgent.register(runtime, "my_agent", lambda: MyAgent())
await MyAssistant.register(runtime, "my_assistant", lambda: MyAssistant("my_assistant"))
AgentType(type='my_assistant')
Once an agent type is registered, we can send a direct message to an agent instance using
an AgentId. The runtime will create the instance the first time it delivers a message to this
instance.

runtime.start() # Start processing messages in the background.


await runtime.send_message(MyMessageType("Hello, World!"), AgentId("my_agent",
"default"))
await runtime.send_message(MyMessageType("Hello, World!"), AgentId("my_assistant",
"default"))
await runtime.stop() # Stop processing messages in the background.
my_agent received message: Hello, World!
my_assistant received message: Hello, World!
my_assistant responded: Hello! How can I assist you today?
Note

Because the runtime manages the lifecycle of agents, an AgentId is only used to
communicate with the agent or retrieve its metadata (e.g., description).

Running the Single-Threaded Agent Runtime


The above code snippet uses start() to start a background task to process and deliver
messages to recepients’ message handlers. This is a feature of the local embedded runtime
SingleThreadedAgentRuntime.

To stop the background task immediately, use the stop() method:

runtime.start()
# ... Send messages, publish messages, etc.
await runtime.stop() # This will return immediately but will not cancel
# any in-progress message handling.
You can resume the background task by calling start() again.

For batch scenarios such as running benchmarks for evaluating agents, you may want to
wait for the background task to stop automatically when there are no unprocessed
messages and no agent is handling messages – the batch may considered complete. You
can achieve this by using the stop_when_idle() method:

runtime.start()
# ... Send messages, publish messages, etc.
await runtime.stop_when_idle() # This will block until the runtime is idle.
To close the runtime and release resources, use the close() method:

await runtime.close()
Other runtime implementations will have their own ways of running the runtime.
Message and Communication
An agent in AutoGen core can react to, send, and publish messages, and messages are the
only means through which agents can communicate with each other.

Messages
Messages are serializable objects, they can be defined using:

A subclass of Pydantic’s pydantic.BaseModel, or

A dataclass

For example:

from dataclasses import dataclass

@dataclass
class TextMessage:
content: str
source: str

@dataclass
class ImageMessage:
url: str
source: str
Note

Messages are purely data, and should not contain any logic.

Message Handlers
When an agent receives a message the runtime will invoke the agent’s message handler
(on_message()) which should implement the agents message handling logic. If this message
cannot be handled by the agent, the agent should raise a CantHandleException.

The base class BaseAgent provides no message handling logic and implementing the
on_message() method directly is not recommended unless for the advanced use cases.

Developers should start with implementing the RoutedAgent base class which provides built-
in message routing capability.

Routing Messages by Type


The RoutedAgent base class provides a mechanism for associating message types with
message handlers with the message_handler() decorator, so developers do not need to
implement the on_message() method.

For example, the following type-routed agent responds to TextMessage and ImageMessage
using different message handlers:
from autogen_core import AgentId, MessageContext, RoutedAgent,
SingleThreadedAgentRuntime, message_handler

class MyAgent(RoutedAgent):
@message_handler
async def on_text_message(self, message: TextMessage, ctx: MessageContext) -> None:
print(f"Hello, {message.source}, you said {message.content}!")

@message_handler
async def on_image_message(self, message: ImageMessage, ctx: MessageContext) ->
None:
print(f"Hello, {message.source}, you sent me {message.url}!")
Create the agent runtime and register the agent type (see Agent and Agent Runtime):

runtime = SingleThreadedAgentRuntime()
await MyAgent.register(runtime, "my_agent", lambda: MyAgent("My Agent"))
AgentType(type='my_agent')
Test this agent with TextMessage and ImageMessage.

runtime.start()
agent_id = AgentId("my_agent", "default")
await runtime.send_message(TextMessage(content="Hello, World!", source="User"),
agent_id)
await runtime.send_message(ImageMessage(url="https://example.com/image.jpg",
source="User"), agent_id)
await runtime.stop_when_idle()
Hello, User, you said Hello, World!!
Hello, User, you sent me https://example.com/image.jpg!
The runtime automatically creates an instance of MyAgent with the agent ID
AgentId("my_agent", "default") when delivering the first message.

Routing Messages of the Same Type


In some scenarios, it is useful to route messages of the same type to different handlers. For
examples, messages from different sender agents should be handled differently. You can
use the match parameter of the message_handler() decorator.

The match parameter associates handlers for the same message type to a specific message
– it is secondary to the message type routing. It accepts a callable that takes the message
and MessageContext as arguments, and returns a boolean indicating whether the message
should be handled by the decorated handler. The callable is checked in the alphabetical
order of the handlers.

Here is an example of an agent that routes messages based on the sender agent using the
match parameter:

class RoutedBySenderAgent(RoutedAgent):
@message_handler(match=lambda msg, ctx: msg.source.startswith("user1")) # type:
ignore
async def on_user1_message(self, message: TextMessage, ctx: MessageContext) ->
None:
print(f"Hello from user 1 handler, {message.source}, you said {message.content}!")

@message_handler(match=lambda msg, ctx: msg.source.startswith("user2")) # type:


ignore
async def on_user2_message(self, message: TextMessage, ctx: MessageContext) ->
None:
print(f"Hello from user 2 handler, {message.source}, you said {message.content}!")

@message_handler(match=lambda msg, ctx: msg.source.startswith("user2")) # type:


ignore
async def on_image_message(self, message: ImageMessage, ctx: MessageContext) ->
None:
print(f"Hello, {message.source}, you sent me {message.url}!")
The above agent uses the source field of the message to determine the sender agent. You
can also use the sender field of MessageContext to determine the sender agent using the
agent ID if available.

Let’s test this agent with messages with different source values:

runtime = SingleThreadedAgentRuntime()
await RoutedBySenderAgent.register(runtime, "my_agent", lambda:
RoutedBySenderAgent("Routed by sender agent"))
runtime.start()
agent_id = AgentId("my_agent", "default")
await runtime.send_message(TextMessage(content="Hello, World!", source="user1-test"),
agent_id)
await runtime.send_message(TextMessage(content="Hello, World!", source="user2-test"),
agent_id)
await runtime.send_message(ImageMessage(url="https://example.com/image.jpg",
source="user1-test"), agent_id)
await runtime.send_message(ImageMessage(url="https://example.com/image.jpg",
source="user2-test"), agent_id)
await runtime.stop_when_idle()
Hello from user 1 handler, user1-test, you said Hello, World!!
Hello from user 2 handler, user2-test, you said Hello, World!!
Hello, user2-test, you sent me https://example.com/image.jpg!
In the above example, the first ImageMessage is not handled because the source field of the
message does not match the handler’s match condition.

Direct Messaging
There are two types of communication in AutoGen core:

Direct Messaging: sends a direct message to another agent.


Broadcast: publishes a message to a topic.

Let’s first look at direct messaging. To send a direct message to another agent, within a
message handler use the autogen_core.BaseAgent.send_message() method, from the
runtime use the autogen_core.AgentRuntime.send_message() method. Awaiting calls to
these methods will return the return value of the receiving agent’s message handler. When
the receiving agent’s handler returns None, None will be returned.

Note

If the invoked agent raises an exception while the sender is awaiting, the exception will be
propagated back to the sender.

Request/Response
Direct messaging can be used for request/response scenarios, where the sender expects a
response from the receiver. The receiver can respond to the message by returning a value
from its message handler. You can think of this as a function call between agents.

For example, consider the following agents:

from dataclasses import dataclass

from autogen_core import MessageContext, RoutedAgent, SingleThreadedAgentRuntime,


message_handler

@dataclass
class Message:
content: str

class InnerAgent(RoutedAgent):
@message_handler
async def on_my_message(self, message: Message, ctx: MessageContext) -> Message:
return Message(content=f"Hello from inner, {message.content}")

class OuterAgent(RoutedAgent):
def __init__(self, description: str, inner_agent_type: str):
super().__init__(description)
self.inner_agent_id = AgentId(inner_agent_type, self.id.key)

@message_handler
async def on_my_message(self, message: Message, ctx: MessageContext) -> None:
print(f"Received message: {message.content}")
# Send a direct message to the inner agent and receves a response.
response = await self.send_message(Message(f"Hello from outer, {message.content}"),
self.inner_agent_id)
print(f"Received inner response: {response.content}")
Upone receving a message, the OuterAgent sends a direct message to the InnerAgent and
receives a message in response.

We can test these agents by sending a Message to the OuterAgent.

runtime = SingleThreadedAgentRuntime()
await InnerAgent.register(runtime, "inner_agent", lambda: InnerAgent("InnerAgent"))
await OuterAgent.register(runtime, "outer_agent", lambda: OuterAgent("OuterAgent",
"inner_agent"))
runtime.start()
outer_agent_id = AgentId("outer_agent", "default")
await runtime.send_message(Message(content="Hello, World!"), outer_agent_id)
await runtime.stop_when_idle()
Received message: Hello, World!
Received inner response: Hello from inner, Hello from outer, Hello, World!
Both outputs are produced by the OuterAgent’s message handler, however the second
output is based on the response from the InnerAgent.

Generally speaking, direct messaging is appropriate for scenarios when the sender and
recipient are tightly coupled – they are created together and the sender is linked to a specific
instance of the recipient. For example, an agent executes tool calls by sending direct
messages to an instance of ToolAgent, and uses the responses to form an action-
observation loop.

Broadcast
Broadcast is effectively the publish/subscribe model with topic and subscription. Read Topic
and Subscription to learn the core concepts.

The key difference between direct messaging and broadcast is that broadcast cannot be
used for request/response scenarios. When an agent publishes a message it is one way
only, it cannot receive a response from any other agent, even if a receiving agent’s handler
returns a value.

Note

If a response is given to a published message, it will be thrown away.

Note

If an agent publishes a message type for which it is subscribed it will not receive the
message it published. This is to prevent infinite loops.

Subscribe and Publish to Topics


Type-based subscription maps messages published to topics of a given topic type to agents
of a given agent type. To make an agent that subsclasses RoutedAgent subscribe to a topic
of a given topic type, you can use the type_subscription() class decorator.
The following example shows a ReceiverAgent class that subscribes to topics of "default"
topic type using the type_subscription() decorator. and prints the received messages.

from autogen_core import RoutedAgent, message_handler, type_subscription

@type_subscription(topic_type="default")
class ReceivingAgent(RoutedAgent):
@message_handler
async def on_my_message(self, message: Message, ctx: MessageContext) -> None:
print(f"Received a message: {message.content}")
To publish a message from an agent’s handler, use the publish_message() method and
specify a TopicId. This call must still be awaited to allow the runtime to schedule delivery of
the message to all subscribers, but it will always return None. If an agent raises an exception
while handling a published message, this will be logged but will not be propagated back to
the publishing agent.

The following example shows a BroadcastingAgent that publishes a message to a topic


upon receiving a message.

from autogen_core import TopicId

class BroadcastingAgent(RoutedAgent):
@message_handler
async def on_my_message(self, message: Message, ctx: MessageContext) -> None:
await self.publish_message(
Message("Publishing a message from broadcasting agent!"),
topic_id=TopicId(type="default", source=self.id.key),
)
BroadcastingAgent publishes message to a topic with type "default" and source assigned to
the agent instance’s agent key.

Subscriptions are registered with the agent runtime, either as part of agent type’s registration
or through a separate API method. Here is how we register TypeSubscription for the
receiving agent with the type_subscription() decorator, and for the broadcasting agent
without the decorator.

from autogen_core import TypeSubscription

runtime = SingleThreadedAgentRuntime()

# Option 1: with type_subscription decorator


# The type_subscription class decorator automatically adds a TypeSubscription to
# the runtime when the agent is registered.
await ReceivingAgent.register(runtime, "receiving_agent", lambda:
ReceivingAgent("Receiving Agent"))
# Option 2: with TypeSubscription
await BroadcastingAgent.register(runtime, "broadcasting_agent", lambda:
BroadcastingAgent("Broadcasting Agent"))
await runtime.add_subscription(TypeSubscription(topic_type="default",
agent_type="broadcasting_agent"))

# Start the runtime and publish a message.


runtime.start()
await runtime.publish_message(
Message("Hello, World! From the runtime!"), topic_id=TopicId(type="default",
source="default")
)
await runtime.stop_when_idle()
Received a message: Hello, World! From the runtime!
Received a message: Publishing a message from broadcasting agent!
As shown in the above example, you can also publish directly to a topic through the
runtime’s publish_message() method without the need to create an agent instance.

From the output, you can see two messages were received by the receiving agent: one was
published through the runtime, and the other was published by the broadcasting agent.

Default Topic and Subscriptions


In the above example, we used TopicId and TypeSubscription to specify the topic and
subscriptions respectively. This is the appropriate way for many scenarios. However, when
there is a single scope of publishing, that is, all agents publish and subscribe to all
broadcasted messages, we can use the convenience classes DefaultTopicId and
default_subscription() to simplify our code.

DefaultTopicId is for creating a topic that uses "default" as the default value for the topic type
and the publishing agent’s key as the default value for the topic source.
default_subscription() is for creating a type subscription that subscribes to the default topic.
We can simplify BroadcastingAgent by using DefaultTopicId and default_subscription().

from autogen_core import DefaultTopicId, default_subscription

@default_subscription
class BroadcastingAgentDefaultTopic(RoutedAgent):
@message_handler
async def on_my_message(self, message: Message, ctx: MessageContext) -> None:
# Publish a message to all agents in the same namespace.
await self.publish_message(
Message("Publishing a message from broadcasting agent!"),
topic_id=DefaultTopicId(),
)
When the runtime calls register() to register the agent type, it creates a TypeSubscription
whose topic type uses "default" as the default value and agent type uses the same agent
type that is being registered in the same context.
runtime = SingleThreadedAgentRuntime()
await BroadcastingAgentDefaultTopic.register(
runtime, "broadcasting_agent", lambda: BroadcastingAgentDefaultTopic("Broadcasting
Agent")
)
await ReceivingAgent.register(runtime, "receiving_agent", lambda:
ReceivingAgent("Receiving Agent"))
runtime.start()
await runtime.publish_message(Message("Hello, World! From the runtime!"),
topic_id=DefaultTopicId())
await runtime.stop_when_idle()
Received a message: Hello, World! From the runtime!
Received a message: Publishing a message from broadcasting agent!
Note

If your scenario allows all agents to publish and subscribe to all broadcasted messages, use
DefaultTopicId and default_subscription() to decorate your agent classes.
Logging
AutoGen uses Python’s built-in logging module.

There are two kinds of logging:

Trace logging: This is used for debugging and is human readable messages to indicate what
is going on. This is intended for a developer to understand what is happening in the code.
The content and format of these logs should not be depended on by other systems.

Name: TRACE_LOGGER_NAME.

Structured logging: This logger emits structured events that can be consumed by other
systems. The content and format of these logs can be depended on by other systems.

Name: EVENT_LOGGER_NAME.

See the module autogen_core.logging to see the available events.

ROOT_LOGGER_NAME can be used to enable or disable all logs.

Enabling logging output


To enable trace logging, you can use the following code:

import logging

from autogen_core import TRACE_LOGGER_NAME

logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(TRACE_LOGGER_NAME)
logger.setLevel(logging.DEBUG)
Structured logging
Structured logging allows you to write handling logic that deals with the actual events
including all fields rather than just a formatted string.

For example, if you had defined this custom event and were emitting it. Then you could write
the following handler to receive it.

import logging
from dataclasses import dataclass

@dataclass
class MyEvent:
timestamp: str
message: str

class MyHandler(logging.Handler):
def __init__(self) -> None:
super().__init__()
def emit(self, record: logging.LogRecord) -> None:
try:
# Use the StructuredMessage if the message is an instance of it
if isinstance(record.msg, MyEvent):
print(f"Timestamp: {record.msg.timestamp}, Message: {record.msg.message}")
except Exception:
self.handleError(record)
And this is how you could use it:

logger = logging.getLogger(EVENT_LOGGER_NAME)
logger.setLevel(logging.INFO)
my_handler = MyHandler()
logger.handlers = [my_handler]
Emitting logs
These two names are the root loggers for these types. Code that emits logs should use a
child logger of these loggers. For example, if you are writing a module my_module and you
want to emit trace logs, you should use the logger named:

import logging

from autogen_core import TRACE_LOGGER_NAME


logger = logging.getLogger(f"{TRACE_LOGGER_NAME}.my_module")
Emitting structured logs
If your event is a dataclass, then it could be emitted in code like this:

import logging
from dataclasses import dataclass
from autogen_core import EVENT_LOGGER_NAME

@dataclass
class MyEvent:
timestamp: str
message: str

logger = logging.getLogger(EVENT_LOGGER_NAME + ".my_module")


logger.info(MyEvent("timestamp", "message"))
Open Telemetry
AutoGen has native support for open telemetry. This allows you to collect telemetry data
from your application and send it to a telemetry backend of your choosing.

These are the components that are currently instrumented:

Runtime (Single Threaded Agent Runtime, Worker Agent Runtime)

Instrumenting your application


To instrument your application, you will need an sdk and an exporter. You may already have
these if your application is already instrumented with open telemetry.

Clean instrumentation
If you do not have open telemetry set up in your application, you can follow these steps to
instrument your application.

pip install opentelemetry-sdk


Depending on your open telemetry collector, you can use grpc or http to export your
telemetry.

# Pick one of the following

pip install opentelemetry-exporter-otlp-proto-http


pip install opentelemetry-exporter-otlp-proto-grpc
Next, we need to get a tracer provider:

from opentelemetry import trace


from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

def configure_oltp_tracing(endpoint: str = None) -> trace.TracerProvider:


# Configure Tracing
tracer_provider = TracerProvider(resource=Resource({"service.name": "my-service"}))
processor = BatchSpanProcessor(OTLPSpanExporter())
tracer_provider.add_span_processor(processor)
trace.set_tracer_provider(tracer_provider)

return tracer_provider
Now you can send the trace_provider when creating your runtime:

# for single threaded runtime


single_threaded_runtime = SingleThreadedAgentRuntime(tracer_provider=tracer_provider)
# or for worker runtime
worker_runtime = GrpcWorkerAgentRuntime(tracer_provider=tracer_provider)
And that’s it! Your application is now instrumented with open telemetry. You can now view
your telemetry data in your telemetry backend.
Existing instrumentation
If you have open telemetry already set up in your application, you can pass the tracer
provider to the runtime when creating it:

from opentelemetry import trace

# Get the tracer provider from your application


tracer_provider = trace.get_tracer_provider()

# for single threaded runtime


single_threaded_runtime = SingleThreadedAgentRuntime(tracer_provider=tracer_provider)
# or for worker runtime
worker_runtime = GrpcWorkerAgentRuntime(tracer_provider=tracer_provider)
Component config
AutoGen components are able to be declaratively configured in a generic fashion. This is to
support configuration based experiences, such as AutoGen studio, but it is also useful for
many other scenarios.

The system that provides this is called “component configuration”. In AutoGen, a component
is simply something that can be created from a config object and itself can be dumped to a
config object. In this way, you can define a component in code and then get the config object
from it.

This system is generic and allows for components defined outside of AutoGen itself (such as
extensions) to be configured in the same way.

How does this differ from state?


This is a very important point to clarify. When we talk about serializing an object, we must
include all data that makes that object itself. Including things like message history etc. When
deserializing from serialized state, you must get back the exact same object. This is not the
case with component configuration.

Component configuration should be thought of as the blueprint for an object, and can be
stamped out many times to create many instances of the same configured object.

Usage
If you have a component in Python and want to get the config for it, simply call
dump_component() on it. The resulting object can be passed back into load_component() to
get the component back.

Loading a component from a config


To load a component from a config object, you can use the load_component() method. This
method will take a config object and return a component object. It is best to call this method
on the interface you want. For example to load a model client:

from autogen_core.models import ChatCompletionClient

config = {
"provider": "openai_chat_completion_client",
"config": {"model": "gpt-4o"},
}

client = ChatCompletionClient.load_component(config)
Creating a component class
To add component functionality to a given class:

Add a call to Component() in the class inheritance list.

Implment the _to_config() and _from_config() methods

For example:
from autogen_core import Component, ComponentBase
from pydantic import BaseModel

class Config(BaseModel):
value: str

class MyComponent(ComponentBase[Config], Component[Config]):


component_type = "custom"
component_config_schema = Config

def __init__(self, value: str):


self.value = value

def _to_config(self) -> Config:


return Config(value=self.value)

@classmethod
def _from_config(cls, config: Config) -> "MyComponent":
return cls(value=config.value)
Secrets
If a field of a config object is a secret value, it should be marked using SecretStr, this will
ensure that the value will not be dumped to the config object.

For example:

from pydantic import BaseModel, SecretStr

class ClientConfig(BaseModel):
endpoint: str
api_key: SecretStr

You might also like