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.2 kB

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