autogen icon indicating copy to clipboard operation
autogen copied to clipboard

AgentChat component serialization

Open victordibia opened this issue 1 year ago • 0 comments

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

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

victordibia avatar Dec 01 '24 05:12 victordibia