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.

chat_room.py 6.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import argparse
  2. import asyncio
  3. import json
  4. import logging
  5. import os
  6. import sys
  7. from agnext.application import SingleThreadedAgentRuntime
  8. from agnext.components import DefaultTopicId, RoutedAgent, message_handler
  9. from agnext.components._default_subscription import DefaultSubscription
  10. from agnext.components.memory import ChatMemory
  11. from agnext.components.models import ChatCompletionClient, SystemMessage
  12. from agnext.core import AgentId, AgentInstantiationContext, AgentProxy, AgentRuntime
  13. sys.path.append(os.path.abspath(os.path.dirname(__file__)))
  14. sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
  15. from agnext.core import MessageContext
  16. from common.memory import BufferedChatMemory
  17. from common.types import Message, TextMessage
  18. from common.utils import convert_messages_to_llm_messages, get_chat_completion_client_from_envs
  19. from utils import TextualChatApp, TextualUserAgent
  20. # Define a custom agent that can handle chat room messages.
  21. class ChatRoomAgent(RoutedAgent):
  22. def __init__(
  23. self,
  24. name: str,
  25. description: str,
  26. background_story: str,
  27. memory: ChatMemory[Message],
  28. model_client: ChatCompletionClient,
  29. ) -> None:
  30. super().__init__(description)
  31. system_prompt = f"""Your name is {name}.
  32. Your background story is:
  33. {background_story}
  34. Now you are in a chat room with other users.
  35. You can send messages to the chat room by typing your message below.
  36. You do not need to respond to every message.
  37. Use the following JSON format to provide your thought on the latest message and choose whether to respond:
  38. {{
  39. "thought": "Your thought on the message",
  40. "respond": <true/false>,
  41. "response": "Your response to the message or None if you choose not to respond."
  42. }}
  43. """
  44. self._system_messages = [SystemMessage(system_prompt)]
  45. self._memory = memory
  46. self._client = model_client
  47. @message_handler()
  48. async def on_chat_room_message(self, message: TextMessage, ctx: MessageContext) -> None:
  49. # Save the message to memory as structured JSON.
  50. from_message = TextMessage(
  51. content=json.dumps({"sender": message.source, "content": message.content}), source=message.source
  52. )
  53. await self._memory.add_message(from_message)
  54. # Get a response from the model.
  55. raw_response = await self._client.create(
  56. self._system_messages
  57. + convert_messages_to_llm_messages(await self._memory.get_messages(), self_name=self.metadata["type"]),
  58. json_output=True,
  59. )
  60. assert isinstance(raw_response.content, str)
  61. # Save the response to memory.
  62. await self._memory.add_message(TextMessage(source=self.metadata["type"], content=raw_response.content))
  63. # Parse the response.
  64. data = json.loads(raw_response.content)
  65. respond = data.get("respond")
  66. response = data.get("response")
  67. # Publish the response if needed.
  68. if respond is True or str(respond).lower().strip() == "true":
  69. await self.publish_message(
  70. TextMessage(source=self.metadata["type"], content=str(response)), topic_id=DefaultTopicId()
  71. )
  72. class ChatRoomUserAgent(TextualUserAgent):
  73. """An agent that is used to receive messages from the runtime."""
  74. @message_handler
  75. async def on_chat_room_message(self, message: TextMessage, ctx: MessageContext) -> None:
  76. await self._app.post_runtime_message(message)
  77. # Define a chat room with participants -- the runtime is the chat room.
  78. async def chat_room(runtime: AgentRuntime, app: TextualChatApp) -> None:
  79. await runtime.register(
  80. "User",
  81. lambda: ChatRoomUserAgent(
  82. description="The user in the chat room.",
  83. app=app,
  84. ),
  85. lambda: [DefaultSubscription()],
  86. )
  87. await runtime.register(
  88. "Alice",
  89. lambda: ChatRoomAgent(
  90. name=AgentInstantiationContext.current_agent_id().type,
  91. description="Alice in the chat room.",
  92. background_story="Alice is a software engineer who loves to code.",
  93. memory=BufferedChatMemory(buffer_size=10),
  94. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  95. ),
  96. lambda: [DefaultSubscription()],
  97. )
  98. alice = AgentProxy(AgentId("Alice", "default"), runtime)
  99. await runtime.register(
  100. "Bob",
  101. lambda: ChatRoomAgent(
  102. name=AgentInstantiationContext.current_agent_id().type,
  103. description="Bob in the chat room.",
  104. background_story="Bob is a data scientist who loves to analyze data.",
  105. memory=BufferedChatMemory(buffer_size=10),
  106. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  107. ),
  108. lambda: [DefaultSubscription()],
  109. )
  110. bob = AgentProxy(AgentId("Bob", "default"), runtime)
  111. await runtime.register(
  112. "Charlie",
  113. lambda: ChatRoomAgent(
  114. name=AgentInstantiationContext.current_agent_id().type,
  115. description="Charlie in the chat room.",
  116. background_story="Charlie is a designer who loves to create art.",
  117. memory=BufferedChatMemory(buffer_size=10),
  118. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  119. ),
  120. lambda: [DefaultSubscription()],
  121. )
  122. charlie = AgentProxy(AgentId("Charlie", "default"), runtime)
  123. app.welcoming_notice = f"""Welcome to the chat room demo with the following participants:
  124. 1. 👧 {alice.id.type}: {(await alice.metadata)['description']}
  125. 2. 👱🏼‍♂️ {bob.id.type}: {(await bob.metadata)['description']}
  126. 3. 👨🏾‍🦳 {charlie.id.type}: {(await charlie.metadata)['description']}
  127. Each participant decides on its own whether to respond to the latest message.
  128. You can greet the chat room by typing your first message below.
  129. """
  130. async def main() -> None:
  131. runtime = SingleThreadedAgentRuntime()
  132. app = TextualChatApp(runtime, user_name="You")
  133. await chat_room(runtime, app)
  134. runtime.start()
  135. await app.run_async()
  136. if __name__ == "__main__":
  137. parser = argparse.ArgumentParser(description="Chat room demo with self-driving AI agents.")
  138. parser.add_argument("--verbose", action="store_true", help="Enable verbose logging.")
  139. args = parser.parse_args()
  140. if args.verbose:
  141. logging.basicConfig(level=logging.WARNING)
  142. logging.getLogger("agnext").setLevel(logging.DEBUG)
  143. handler = logging.FileHandler("chat_room.log")
  144. logging.getLogger("agnext").addHandler(handler)
  145. asyncio.run(main())