mirror of
https://github.com/microsoft/autogen.git
synced 2026-04-20 03:02:16 -04:00
324 lines
16 KiB
Plaintext
324 lines
16 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Tools\n",
|
|
"\n",
|
|
"Tools are code that can be executed by an agent to perform actions. A tool\n",
|
|
"can be a simple function such as a calculator, or an API call to a third-party service\n",
|
|
"such as stock price lookup and weather forecast.\n",
|
|
"In the context of AI agents, tools are designed to be executed by agents in\n",
|
|
"response to model-generated function calls.\n",
|
|
"\n",
|
|
"AGNext provides the {py:mod}`agnext.components.tools` module with a suite of built-in\n",
|
|
"tools and utilities for creating and running custom tools."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Built-in Tools\n",
|
|
"\n",
|
|
"One of the built-in tools is the {py:class}`agnext.components.tools.PythonCodeExecutionTool`,\n",
|
|
"which allows agents to execute Python code snippets.\n",
|
|
"\n",
|
|
"Here is how you create the tool and use it."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from agnext.components.code_executor import LocalCommandLineCodeExecutor\n",
|
|
"from agnext.components.tools import PythonCodeExecutionTool\n",
|
|
"from agnext.core import CancellationToken\n",
|
|
"\n",
|
|
"# Create the tool.\n",
|
|
"code_executor = LocalCommandLineCodeExecutor()\n",
|
|
"code_execution_tool = PythonCodeExecutionTool(code_executor)\n",
|
|
"cancellation_token = CancellationToken()\n",
|
|
"\n",
|
|
"# Use the tool directly without an agent.\n",
|
|
"code = \"print('Hello, world!')\"\n",
|
|
"result = await code_execution_tool.run_json({\"code\": code}, cancellation_token)\n",
|
|
"print(code_execution_tool.return_value_as_string(result))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The {py:class}`~agnext.components.code_executor.LocalCommandLineCodeExecutor`\n",
|
|
"class is a built-in code executor that runs Python code snippets in a subprocess\n",
|
|
"in the local command line environment.\n",
|
|
"The {py:class}`~agnext.components.tools.PythonCodeExecutionTool` class wraps the code executor\n",
|
|
"and provides a simple interface to execute Python code snippets.\n",
|
|
"\n",
|
|
"Other built-in tools will be added in the future."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Custom Function Tools\n",
|
|
"\n",
|
|
"A tool can also be a simple Python function that performs a specific action.\n",
|
|
"To create a custom function tool, you just need to create a Python function\n",
|
|
"and use the {py:class}`agnext.components.tools.FunctionTool` class to wrap it.\n",
|
|
"\n",
|
|
"For example, a simple tool to obtain the stock price of a company might look like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"138.75280591295171\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import random\n",
|
|
"\n",
|
|
"from agnext.components.tools import FunctionTool\n",
|
|
"from agnext.core import CancellationToken\n",
|
|
"from typing_extensions import Annotated\n",
|
|
"\n",
|
|
"\n",
|
|
"async def get_stock_price(ticker: str, date: Annotated[str, \"Date in YYYY/MM/DD\"]) -> float:\n",
|
|
" # Returns a random stock price for demonstration purposes.\n",
|
|
" return random.uniform(10, 200)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a function tool.\n",
|
|
"stock_price_tool = FunctionTool(get_stock_price, description=\"Get the stock price.\")\n",
|
|
"\n",
|
|
"# Run the tool.\n",
|
|
"cancellation_token = CancellationToken()\n",
|
|
"result = await stock_price_tool.run_json({\"ticker\": \"AAPL\", \"date\": \"2021/01/01\"}, cancellation_token)\n",
|
|
"\n",
|
|
"# Print the result.\n",
|
|
"print(stock_price_tool.return_value_as_string(result))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Tool-Equipped Agent\n",
|
|
"\n",
|
|
"To use tools with an agent, you can use {py:class}`agnext.components.tool_agent.ToolAgent`,\n",
|
|
"by using it in a composition pattern.\n",
|
|
"Here is an example tool-use agent that uses {py:class}`~agnext.components.tool_agent.ToolAgent`\n",
|
|
"as an inner agent for executing tools."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import asyncio\n",
|
|
"from dataclasses import dataclass\n",
|
|
"from typing import List\n",
|
|
"\n",
|
|
"from agnext.application import SingleThreadedAgentRuntime\n",
|
|
"from agnext.components import FunctionCall, TypeRoutedAgent, message_handler\n",
|
|
"from agnext.components.models import (\n",
|
|
" AssistantMessage,\n",
|
|
" ChatCompletionClient,\n",
|
|
" FunctionExecutionResult,\n",
|
|
" FunctionExecutionResultMessage,\n",
|
|
" LLMMessage,\n",
|
|
" OpenAIChatCompletionClient,\n",
|
|
" SystemMessage,\n",
|
|
" UserMessage,\n",
|
|
")\n",
|
|
"from agnext.components.tool_agent import ToolAgent, ToolException\n",
|
|
"from agnext.components.tools import FunctionTool, Tool, ToolSchema\n",
|
|
"from agnext.core import AgentId, AgentInstantiationContext, MessageContext\n",
|
|
"\n",
|
|
"\n",
|
|
"@dataclass\n",
|
|
"class Message:\n",
|
|
" content: str\n",
|
|
"\n",
|
|
"\n",
|
|
"class ToolUseAgent(TypeRoutedAgent):\n",
|
|
" def __init__(self, model_client: ChatCompletionClient, tool_schema: List[ToolSchema], tool_agent: AgentId) -> None:\n",
|
|
" super().__init__(\"An agent with tools\")\n",
|
|
" self._system_messages: List[LLMMessage] = [SystemMessage(\"You are a helpful AI assistant.\")]\n",
|
|
" self._model_client = model_client\n",
|
|
" self._tool_schema = tool_schema\n",
|
|
" self._tool_agent = tool_agent\n",
|
|
"\n",
|
|
" @message_handler\n",
|
|
" async def handle_user_message(self, message: Message, ctx: MessageContext) -> Message:\n",
|
|
" # Create a session of messages.\n",
|
|
" session: List[LLMMessage] = [UserMessage(content=message.content, source=\"user\")]\n",
|
|
" # Get a response from the model.\n",
|
|
" response = await self._model_client.create(\n",
|
|
" self._system_messages + session, tools=self._tool_schema, cancellation_token=cancellation_token\n",
|
|
" )\n",
|
|
" # Add the response to the session.\n",
|
|
" session.append(AssistantMessage(content=response.content, source=\"assistant\"))\n",
|
|
"\n",
|
|
" # Keep iterating until the model stops generating tool calls.\n",
|
|
" while isinstance(response.content, list) and all(isinstance(item, FunctionCall) for item in response.content):\n",
|
|
" # Execute functions called by the model by sending messages to itself.\n",
|
|
" results: List[FunctionExecutionResult | BaseException] = await asyncio.gather(\n",
|
|
" *[self.send_message(call, self._tool_agent) for call in response.content],\n",
|
|
" return_exceptions=True,\n",
|
|
" )\n",
|
|
" # Combine the results into a single response and handle exceptions.\n",
|
|
" function_results: List[FunctionExecutionResult] = []\n",
|
|
" for result in results:\n",
|
|
" if isinstance(result, FunctionExecutionResult):\n",
|
|
" function_results.append(result)\n",
|
|
" elif isinstance(result, ToolException):\n",
|
|
" function_results.append(FunctionExecutionResult(content=f\"Error: {result}\", call_id=result.call_id))\n",
|
|
" elif isinstance(result, BaseException):\n",
|
|
" raise result # Unexpected exception.\n",
|
|
" session.append(FunctionExecutionResultMessage(content=function_results))\n",
|
|
" # Query the model again with the new response.\n",
|
|
" response = await self._model_client.create(\n",
|
|
" self._system_messages + session, tools=self._tool_schema, cancellation_token=cancellation_token\n",
|
|
" )\n",
|
|
" session.append(AssistantMessage(content=response.content, source=self.metadata[\"type\"]))\n",
|
|
"\n",
|
|
" # Return the final response.\n",
|
|
" assert isinstance(response.content, str)\n",
|
|
" return Message(content=response.content)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The `ToolUseAgent` class is a bit involved, however,\n",
|
|
"the core idea can be described using a simple control flow graph:\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"The `ToolUseAgent`'s `handle_user_message` handler handles messages from the user,\n",
|
|
"and determines whether the model has generated a tool call.\n",
|
|
"If the model has generated tool calls, then the handler sends a function call\n",
|
|
"message to the {py:class}`~agnext.components.tool_agent.ToolAgent` agent\n",
|
|
"to execute the tools,\n",
|
|
"and then queries the model again with the results of the tool calls.\n",
|
|
"This process continues until the model stops generating tool calls,\n",
|
|
"at which point the final response is returned to the user.\n",
|
|
"\n",
|
|
"By having the tool execution logic in a separate agent,\n",
|
|
"we expose the model-tool interactions to the agent runtime as messages, so the tool executions\n",
|
|
"can be observed externally and intercepted if necessary.\n",
|
|
"\n",
|
|
"To run the agent, we need to create a runtime and register the agent."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Create a runtime.\n",
|
|
"runtime = SingleThreadedAgentRuntime()\n",
|
|
"# Create the tools.\n",
|
|
"tools: List[Tool] = [FunctionTool(get_stock_price, description=\"Get the stock price.\")]\n",
|
|
"# Register the agents.\n",
|
|
"await runtime.register(\n",
|
|
" \"tool-executor-agent\",\n",
|
|
" lambda: ToolAgent(\n",
|
|
" description=\"Tool Executor Agent\",\n",
|
|
" tools=tools,\n",
|
|
" ),\n",
|
|
")\n",
|
|
"await runtime.register(\n",
|
|
" \"tool-use-agent\",\n",
|
|
" lambda: ToolUseAgent(\n",
|
|
" OpenAIChatCompletionClient(model=\"gpt-4o-mini\"),\n",
|
|
" tool_schema=[tool.schema for tool in tools],\n",
|
|
" tool_agent=AgentId(\"tool-executor-agent\", AgentInstantiationContext.current_agent_id().key),\n",
|
|
" ),\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"This example uses the {py:class}`agnext.components.models.OpenAIChatCompletionClient`,\n",
|
|
"for Azure OpenAI and other clients, see [Model Clients](./model-clients.ipynb).\n",
|
|
"Let's test the agent with a question about stock price."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"The stock price of NVDA on June 1, 2024, is approximately $49.28.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Start processing messages.\n",
|
|
"runtime.start()\n",
|
|
"# Send a direct message to the tool agent.\n",
|
|
"tool_use_agent = AgentId(\"tool-use-agent\", \"default\")\n",
|
|
"response = await runtime.send_message(Message(\"What is the stock price of NVDA on 2024/06/01?\"), tool_use_agent)\n",
|
|
"print(response.content)\n",
|
|
"# Stop processing messages.\n",
|
|
"await runtime.stop()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"See [samples](https://github.com/microsoft/agnext/tree/main/python/samples#tool-use-examples)\n",
|
|
"for more examples of using tools with agents, including how to use\n",
|
|
"broadcast communication model for tool execution, and how to intercept tool\n",
|
|
"execution for human-in-the-loop approval."
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "agnext",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.11.9"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |