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.

coding_direct.py 5.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. """
  2. This example implements a tool-enabled agent that uses tools to perform tasks.
  3. 1. The tool use agent receives a user message, and makes an inference using a model.
  4. If the response is a list of function calls, the tool use agent executes the tools by
  5. sending tool execution task to a tool executor agent.
  6. 2. The tool executor agent executes the tools and sends the results back to the
  7. tool use agent, who makes an inference using the model again.
  8. 3. The agents keep executing the tools until the inference response is not a
  9. list of function calls.
  10. 4. The tool use agent returns the final response to the user.
  11. """
  12. import asyncio
  13. import os
  14. import sys
  15. from dataclasses import dataclass
  16. from typing import List
  17. from agnext.application import SingleThreadedAgentRuntime
  18. from agnext.components import FunctionCall, RoutedAgent, message_handler
  19. from agnext.components.code_executor import LocalCommandLineCodeExecutor
  20. from agnext.components.models import (
  21. AssistantMessage,
  22. ChatCompletionClient,
  23. FunctionExecutionResult,
  24. FunctionExecutionResultMessage,
  25. LLMMessage,
  26. SystemMessage,
  27. UserMessage,
  28. )
  29. from agnext.components.tool_agent import ToolAgent, ToolException
  30. from agnext.components.tools import PythonCodeExecutionTool, Tool, ToolSchema
  31. from agnext.core import AgentId, AgentInstantiationContext
  32. sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
  33. from agnext.core import MessageContext
  34. from common.utils import get_chat_completion_client_from_envs
  35. @dataclass
  36. class Message:
  37. content: str
  38. class ToolUseAgent(RoutedAgent):
  39. """An agent that uses tools to perform tasks. It executes the tools
  40. by itself by sending the tool execution task to itself."""
  41. def __init__(
  42. self,
  43. description: str,
  44. system_messages: List[SystemMessage],
  45. model_client: ChatCompletionClient,
  46. tool_schema: List[ToolSchema],
  47. tool_agent: AgentId,
  48. ) -> None:
  49. super().__init__(description)
  50. self._model_client = model_client
  51. self._system_messages = system_messages
  52. self._tool_schema = tool_schema
  53. self._tool_agent = tool_agent
  54. @message_handler
  55. async def handle_user_message(self, message: Message, ctx: MessageContext) -> Message:
  56. """Handle a user message, execute the model and tools, and returns the response."""
  57. session: List[LLMMessage] = []
  58. session.append(UserMessage(content=message.content, source="User"))
  59. response = await self._model_client.create(self._system_messages + session, tools=self._tool_schema)
  60. session.append(AssistantMessage(content=response.content, source=self.metadata["type"]))
  61. # Keep executing the tools until the response is not a list of function calls.
  62. while isinstance(response.content, list) and all(isinstance(item, FunctionCall) for item in response.content):
  63. results: List[FunctionExecutionResult | BaseException] = await asyncio.gather(
  64. *[
  65. self.send_message(call, self._tool_agent, cancellation_token=ctx.cancellation_token)
  66. for call in response.content
  67. ],
  68. return_exceptions=True,
  69. )
  70. # Combine the results into a single response and handle exceptions.
  71. function_results: List[FunctionExecutionResult] = []
  72. for result in results:
  73. if isinstance(result, FunctionExecutionResult):
  74. function_results.append(result)
  75. elif isinstance(result, ToolException):
  76. function_results.append(FunctionExecutionResult(content=f"Error: {result}", call_id=result.call_id))
  77. elif isinstance(result, BaseException):
  78. raise result
  79. session.append(FunctionExecutionResultMessage(content=function_results))
  80. # Execute the model again with the new response.
  81. response = await self._model_client.create(self._system_messages + session, tools=self._tool_schema)
  82. session.append(AssistantMessage(content=response.content, source=self.metadata["type"]))
  83. assert isinstance(response.content, str)
  84. return Message(content=response.content)
  85. async def main() -> None:
  86. # Create the runtime.
  87. runtime = SingleThreadedAgentRuntime()
  88. # Define the tools.
  89. tools: List[Tool] = [
  90. # A tool that executes Python code.
  91. PythonCodeExecutionTool(
  92. LocalCommandLineCodeExecutor(),
  93. )
  94. ]
  95. # Register agents.
  96. await runtime.register(
  97. "tool_executor_agent",
  98. lambda: ToolAgent(
  99. description="Tool Executor Agent",
  100. tools=tools,
  101. ),
  102. )
  103. await runtime.register(
  104. "tool_enabled_agent",
  105. lambda: ToolUseAgent(
  106. description="Tool Use Agent",
  107. system_messages=[SystemMessage("You are a helpful AI Assistant. Use your tools to solve problems.")],
  108. model_client=get_chat_completion_client_from_envs(model="gpt-4o-mini"),
  109. tool_schema=[tool.schema for tool in tools],
  110. tool_agent=AgentId("tool_executor_agent", AgentInstantiationContext.current_agent_id().key),
  111. ),
  112. )
  113. runtime.start()
  114. # Send a task to the tool user.
  115. response = await runtime.send_message(
  116. Message("Run the following Python code: print('Hello, World!')"), AgentId("tool_enabled_agent", "default")
  117. )
  118. print(response.content)
  119. # Run the runtime until the task is completed.
  120. await runtime.stop()
  121. if __name__ == "__main__":
  122. import logging
  123. logging.basicConfig(level=logging.WARNING)
  124. logging.getLogger("agnext").setLevel(logging.DEBUG)
  125. asyncio.run(main())