autogen
autogen copied to clipboard
AgentChat component serialization
What
Currently components in AgentChat dont have a built-in serialization story. This issue is meant to help discuss and architect a setup where all components can be easily serialized to some declarative format and runtime object instantiated.
model_client = OpenAIClient(...)
model_client_spec = model_client.to_dict()
agent_a = AssistantAgent(...)
agent_a_spec = agent_a.to_dict() # agent_a.to_yaml() ...
# should be possible to instantiate a new instance of the component from spec
new_agent_a = AssistantAgent.from_dict(agent_a_spec)
team_a = RoundRobinGroupChat(participants=[agent_b], mode)
team_a_spec = team_a.to_dict()
team_a_spec_yaml = team_a.to_yaml('configs/team_a.yaml')
termination = Termination(...)
termination_spec =
How
- Each top level concept (for lack of a better word) in AgentChat should be treated like a “component” (AGS has the idea of a component factory) - Model, Tool , Agent, Team , Memory , ….
- Each component should implement some serialization methods
-
.to_dict method that specifies how it should be serialized to some declarative specification. .. returns a dict that can be json serialized.
- Also implements a to_json and and to_yaml that uses to_dict underneath
- .from_dict method that can take the declarative spec above and instantiate a new version of the class
-
.to_dict method that specifies how it should be serialized to some declarative specification. .. returns a dict that can be json serialized.
Rough sketch:
class RoundRobinGroupChat(BaseGroupChat):
def to_dict(self) -> Dict[str, Any]:
"""Convert team to dictionary spec."""
return {
"component_type": "team",
"team_type": "RoundRobinGroupChat",
"participants": [p.to_dict() for p in self._participants],
"termination_condition": self._termination_condition.to_dict() if self._termination_condition else None,
"max_turns": self._max_turns
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'RoundRobinGroupChat':
"""Create team from dictionary spec."""
# Convert nested components
participants = [ChatAgent.from_dict(p) for p in data["participants"]]
termination = (TerminationCondition.from_dict(data["termination_condition"])
if data.get("termination_condition") else None)
return cls(
participants=participants,
termination_condition=termination,
max_turns=data.get("max_turns")
)
Benefits
- Native serialization of component specs
- Consistency across all applications using the component spec
- Responsibility for correctly instantiating and serializing a component is solely held by the developer of the component. As it rightfully should be (apps like AGS should do the bare minimum )
- The process for creating component specifications is not both consistent and simplified for all applications.
- The developer experience is greatly improved - easy switching between declarative and python code (a common ask).
- Create your agentchat team in python code, prototype,
- Team.dict() .. save to team.yaml or team.json for distribution/deployment/debugging in AGS (with a single command)
How to run team_a in AutoGen studio?
autogenstudio ui --config-dir=configs
Note that this is only focused on serialization - how to return a runtime instance of a component from a declaritive spec and how to obtain that declarative spec from an instance. Now the ability to load/save runtime state is a separate task tracked in #4100 .
Notes
- Should ChatAgent enforce a protocol for the new methods? to_dict etc
- Do we get any benefits from basing components on pydantic classes wrt to serialization?
- Any general conventions we want to support for all ? AGS component factory and example specs could be helpful here.
Related to #3624, #4388 Thoughts welcome @husseinmozannar @ekzhu @afourney @gagb @jackgerrits