You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mixture_of_agents.py 6.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. """
  2. This example demonstrates the mixture of agents implemented using pub/sub.
  3. Mixture of agents: https://github.com/togethercomputer/moa
  4. The example consists of two types of agents: reference agents and an aggregator agent.
  5. The aggregator agent distributes tasks to reference agents and aggregates the results.
  6. The reference agents handle each task independently and return the results to the aggregator agent.
  7. """
  8. import asyncio
  9. import os
  10. import sys
  11. import uuid
  12. from dataclasses import dataclass
  13. from typing import Dict, List
  14. from agnext.application import SingleThreadedAgentRuntime
  15. from agnext.components import DefaultTopicId, RoutedAgent, message_handler
  16. from agnext.components._type_subscription import TypeSubscription
  17. from agnext.components.models import ChatCompletionClient, SystemMessage, UserMessage
  18. from agnext.core import MessageContext
  19. sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
  20. from agnext.core import TopicId
  21. from common.utils import get_chat_completion_client_from_envs
  22. @dataclass
  23. class ReferenceAgentTask:
  24. session_id: str
  25. task: str
  26. @dataclass
  27. class ReferenceAgentTaskResult:
  28. session_id: str
  29. result: str
  30. @dataclass
  31. class AggregatorTask:
  32. task: str
  33. @dataclass
  34. class AggregatorTaskResult:
  35. result: str
  36. class ReferenceAgent(RoutedAgent):
  37. """The reference agent that handles each task independently."""
  38. def __init__(
  39. self,
  40. description: str,
  41. system_messages: List[SystemMessage],
  42. model_client: ChatCompletionClient,
  43. ) -> None:
  44. super().__init__(description)
  45. self._system_messages = system_messages
  46. self._model_client = model_client
  47. @message_handler
  48. async def handle_task(self, message: ReferenceAgentTask, ctx: MessageContext) -> None:
  49. """Handle a task message. This method sends the task to the model and publishes the result."""
  50. task_message = UserMessage(content=message.task, source=self.metadata["type"])
  51. response = await self._model_client.create(self._system_messages + [task_message])
  52. assert isinstance(response.content, str)
  53. task_result = ReferenceAgentTaskResult(session_id=message.session_id, result=response.content)
  54. await self.publish_message(task_result, topic_id=DefaultTopicId())
  55. class AggregatorAgent(RoutedAgent):
  56. """The aggregator agent that distribute tasks to reference agents and aggregates the results."""
  57. def __init__(
  58. self,
  59. description: str,
  60. system_messages: List[SystemMessage],
  61. model_client: ChatCompletionClient,
  62. num_references: int,
  63. ) -> None:
  64. super().__init__(description)
  65. self._system_messages = system_messages
  66. self._model_client = model_client
  67. self._num_references = num_references
  68. self._session_results: Dict[str, List[ReferenceAgentTaskResult]] = {}
  69. @message_handler
  70. async def handle_task(self, message: AggregatorTask, ctx: MessageContext) -> None:
  71. """Handle a task message. This method publishes the task to the reference agents."""
  72. session_id = str(uuid.uuid4())
  73. ref_task = ReferenceAgentTask(session_id=session_id, task=message.task)
  74. await self.publish_message(ref_task, topic_id=DefaultTopicId())
  75. @message_handler
  76. async def handle_result(self, message: ReferenceAgentTaskResult, ctx: MessageContext) -> None:
  77. """Handle a task result message. Once all results are received, this method
  78. aggregates the results and publishes the final result."""
  79. self._session_results.setdefault(message.session_id, []).append(message)
  80. if len(self._session_results[message.session_id]) == self._num_references:
  81. result = "\n\n".join([r.result for r in self._session_results[message.session_id]])
  82. response = await self._model_client.create(
  83. self._system_messages + [UserMessage(content=result, source=self.metadata["type"])]
  84. )
  85. assert isinstance(response.content, str)
  86. task_result = AggregatorTaskResult(result=response.content)
  87. await self.publish_message(task_result, topic_id=DefaultTopicId())
  88. self._session_results.pop(message.session_id)
  89. print(f"Aggregator result: {response.content}")
  90. async def main() -> None:
  91. runtime = SingleThreadedAgentRuntime()
  92. # TODO: use different models for each agent.
  93. await runtime.register(
  94. "ReferenceAgent1",
  95. lambda: ReferenceAgent(
  96. description="Reference Agent 1",
  97. system_messages=[SystemMessage("You are a helpful assistant that can answer questions.")],
  98. model_client=get_chat_completion_client_from_envs(model="gpt-4o-mini", temperature=0.1),
  99. ),
  100. )
  101. await runtime.add_subscription(TypeSubscription("default", "ReferenceAgent1"))
  102. await runtime.register(
  103. "ReferenceAgent2",
  104. lambda: ReferenceAgent(
  105. description="Reference Agent 2",
  106. system_messages=[SystemMessage("You are a helpful assistant that can answer questions.")],
  107. model_client=get_chat_completion_client_from_envs(model="gpt-4o-mini", temperature=0.5),
  108. ),
  109. )
  110. await runtime.add_subscription(TypeSubscription("default", "ReferenceAgent2"))
  111. await runtime.register(
  112. "ReferenceAgent3",
  113. lambda: ReferenceAgent(
  114. description="Reference Agent 3",
  115. system_messages=[SystemMessage("You are a helpful assistant that can answer questions.")],
  116. model_client=get_chat_completion_client_from_envs(model="gpt-4o-mini", temperature=1.0),
  117. ),
  118. )
  119. await runtime.add_subscription(TypeSubscription("default", "ReferenceAgent3"))
  120. await runtime.register(
  121. "AggregatorAgent",
  122. lambda: AggregatorAgent(
  123. description="Aggregator Agent",
  124. system_messages=[
  125. SystemMessage(
  126. "...synthesize these responses into a single, high-quality response... Responses from models:"
  127. )
  128. ],
  129. model_client=get_chat_completion_client_from_envs(model="gpt-4o-mini"),
  130. num_references=3,
  131. ),
  132. )
  133. await runtime.add_subscription(TypeSubscription("default", "AggregatorAgent"))
  134. runtime.start()
  135. await runtime.publish_message(
  136. AggregatorTask(task="What are something fun to do in SF?"), topic_id=TopicId("default", "default")
  137. )
  138. # Keep processing messages.
  139. await runtime.stop_when_idle()
  140. if __name__ == "__main__":
  141. import logging
  142. logging.basicConfig(level=logging.WARNING)
  143. logging.getLogger("agnext").setLevel(logging.DEBUG)
  144. asyncio.run(main())