.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "tutorial/task_tool.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_tutorial_task_tool.py: .. _tool: Tool ========================= To ensure accurate and reliable tool parsing, AgentScope fully embraces the use of tools API with the following features: - Support **automatic** tool parsing from Python functions with their docstrings - Support both **synchronous and asynchronous** tool functions - Support **streaming** tool responses (either synchronous or asynchronous generators) - Support **dynamic extension** to the tool JSON Schema - Support **interrupting** the tool execution with proper signal handling - Support **autonomous tool management** by agents All above features are implemented by the ``Toolkit`` class in AgentScope, which is responsible for managing tool functions and their execution. .. tip:: The support of MCP (Model Context Protocol) refers to the :ref:`mcp` section. .. GENERATED FROM PYTHON SOURCE LINES 21-33 .. code-block:: Python import asyncio import inspect import json from typing import Any, AsyncGenerator from pydantic import BaseModel, Field import agentscope from agentscope.message import TextBlock, ToolUseBlock from agentscope.tool import ToolResponse, Toolkit, execute_python_code .. GENERATED FROM PYTHON SOURCE LINES 34-42 Tool Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In AgentScope, a tool function is a Python function that - returns a ``ToolResponse`` object or a generator that yields ``ToolResponse`` objects - has a docstring that describes the tool's functionality and parameters A template of a tool function is as follows: .. GENERATED FROM PYTHON SOURCE LINES 42-55 .. code-block:: Python def tool_function(a: int, b: str) -> ToolResponse: """{function description} Args: a (int): {description of the first parameter} b (str): {description of the second parameter} """ .. GENERATED FROM PYTHON SOURCE LINES 56-60 .. tip:: Instance method and class method can also be used as tool functions, and the ``self`` and ``cls`` parameters will be ignored. AgentScope provides several built-in tool functions under the ``agentscope.tool`` module, such as ``execute_python_code``, ``execute_shell_command`` and text file write/read functions. .. GENERATED FROM PYTHON SOURCE LINES 60-66 .. code-block:: Python print("Built-in Tool Functions:") for _ in agentscope.tool.__all__: if _ not in ["Toolkit", "ToolResponse"]: print(_) .. rst-class:: sphx-glr-script-out .. code-block:: none Built-in Tool Functions: execute_python_code execute_shell_command view_text_file write_text_file insert_text_file dashscope_text_to_image dashscope_text_to_audio dashscope_image_to_text openai_text_to_image openai_text_to_audio openai_edit_image openai_create_image_variation openai_image_to_text openai_audio_to_text .. GENERATED FROM PYTHON SOURCE LINES 67-75 Toolkit ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``Toolkit`` class is designed to manage tool functions, extracting their JSON Schema from docstrings and providing a unified interface for tool execution. Basic Usage ------------------------------ The basic functionality of the ``Toolkit`` class is to register tool functions and execute them. .. GENERATED FROM PYTHON SOURCE LINES 75-101 .. code-block:: Python # Prepare a custom tool function async def my_search(query: str, api_key: str) -> ToolResponse: """A simple example tool function. Args: query (str): The search query. api_key (str): The API key for authentication. """ return ToolResponse( content=[ TextBlock( type="text", text=f"Searching for '{query}' with API key '{api_key}'", ), ], ) # Register the tool function in a toolkit toolkit = Toolkit() toolkit.register_tool_function(my_search) .. GENERATED FROM PYTHON SOURCE LINES 102-104 When registering a tool function, you can get its JSON Schema by calling the ``get_json_schemas`` method. .. GENERATED FROM PYTHON SOURCE LINES 104-108 .. code-block:: Python print("Tool JSON Schemas:") print(json.dumps(toolkit.get_json_schemas(), indent=4, ensure_ascii=False)) .. rst-class:: sphx-glr-script-out .. code-block:: none Tool JSON Schemas: [ { "type": "function", "function": { "name": "my_search", "parameters": { "properties": { "query": { "description": "The search query.", "type": "string" }, "api_key": { "description": "The API key for authentication.", "type": "string" } }, "required": [ "query", "api_key" ], "type": "object" }, "description": "A simple example tool function." } } ] .. GENERATED FROM PYTHON SOURCE LINES 109-111 ``Toolkit`` also allows developers to preset the arguments for tool functions, especially useful for API keys or other sensitive information. .. GENERATED FROM PYTHON SOURCE LINES 111-121 .. code-block:: Python # Clear the toolkit first toolkit.clear() # Register tool function with preset keyword arguments toolkit.register_tool_function(my_search, preset_kwargs={"api_key": "xxx"}) print("Tool JSON Schemas with Preset Arguments:") print(json.dumps(toolkit.get_json_schemas(), indent=4, ensure_ascii=False)) .. rst-class:: sphx-glr-script-out .. code-block:: none Tool JSON Schemas with Preset Arguments: [ { "type": "function", "function": { "name": "my_search", "parameters": { "properties": { "query": { "description": "The search query.", "type": "string" } }, "required": [ "query" ], "type": "object" }, "description": "A simple example tool function." } } ] .. GENERATED FROM PYTHON SOURCE LINES 122-124 In ``Toolkit``, the ``call_tool_function`` method takes a tool use block as input and executes the corresponding tool function, returning **a unified asynchronous generator** that yields ``ToolResponse`` objects. .. GENERATED FROM PYTHON SOURCE LINES 124-145 .. code-block:: Python async def example_tool_execution() -> None: """Example of executing a tool call.""" res = await toolkit.call_tool_function( ToolUseBlock( type="tool_use", id="123", name="my_search", input={"query": "AgentScope"}, ), ) # Only one tool response is expected in this case print("Tool Response:") async for tool_response in res: print(tool_response) asyncio.run(example_tool_execution()) .. rst-class:: sphx-glr-script-out .. code-block:: none Tool Response: ToolResponse(content=[{'type': 'text', 'text': "Searching for 'AgentScope' with API key 'xxx'"}], metadata=None, stream=False, is_last=True, is_interrupted=False, id='2025-08-15 10:15:20.903_285869') .. GENERATED FROM PYTHON SOURCE LINES 146-158 Extending JSON Schema Dynamically -------------------------------------- Toolkit allows to extend the JSON schemas of tool functions dynamically by calling the ``set_extended_model`` method. Such feature allows to add more parameters to the tool function without modifying its original definition. .. tip:: Related scenarios include dynamic :ref:`structured-output` and CoT (Chain of Thought) reasoning .. note:: The function to be extended should accept variable keyword arguments (``**kwargs``), so that the additional fields can be passed to it. Taking the CoT reasoning as an example, we can extend all tool functions with a ``thinking`` field, allowing the agent to summarize the current state and then decide what to do next. .. GENERATED FROM PYTHON SOURCE LINES 158-188 .. code-block:: Python # Example tool function def tool_function(**kwargs: Any) -> ToolResponse: """A tool function""" return ToolResponse( content=[ TextBlock( type="text", text=f"Received parameters: {kwargs}", ), ], ) # Add a thinking field so that the agent could think before giving the other parameters. class ThinkingModel(BaseModel): """A Pydantic model for additional fields.""" thinking: str = Field( description="Summarize the current state and decide what to do next.", ) # Register toolkit.set_extended_model("my_search", ThinkingModel) print("The extended JSON Schema:") print(json.dumps(toolkit.get_json_schemas(), indent=4, ensure_ascii=False)) .. rst-class:: sphx-glr-script-out .. code-block:: none The extended JSON Schema: [ { "type": "function", "function": { "name": "my_search", "parameters": { "properties": { "query": { "description": "The search query.", "type": "string" }, "thinking": { "description": "Summarize the current state and decide what to do next.", "type": "string" } }, "required": [ "query", "thinking" ], "type": "object" }, "description": "A simple example tool function." } } ] .. GENERATED FROM PYTHON SOURCE LINES 189-203 Interrupting Tool Execution ------------------------------ The ``Toolkit`` class supports **execution interruption** of **async tool functions** and provides a comprehensive **agent-oriented post-processing mechanism**. Such interruption is implemented based on the asyncio cancellation mechanism, and the post-processing varies depending on the return type of tool function. .. note:: For synchronous tool functions, their execution cannot be interrupted by asyncio cancellation. So the interruption is handled within the agent rather than the toolkit. Refer to the :ref:`agent` section for more information. Specifically, if the tool function returns a ``ToolResponse`` object, a predefined ``ToolResponse`` object with an interrupted message will be yielded. So that the agent can observe the interruption and handle it accordingly. Besides, a flag ``is_interrupted`` will be set to ``True`` in the response, and the external caller can decide whether to throw the ``CancelledError`` exception to the outer layer. An example of async tool function that can be interrupted is as follows: .. GENERATED FROM PYTHON SOURCE LINES 203-245 .. code-block:: Python async def non_streaming_function() -> ToolResponse: """A non-streaming tool function that can be interrupted.""" await asyncio.sleep(1) # Simulate a long-running task # Fake interruption for demonstration raise asyncio.CancelledError() # The following code won't be executed due to the cancellation return ToolResponse( content=[ TextBlock( type="text", text="Run successfully!", ), ], ) async def example_tool_interruption() -> None: """Example of tool interruption.""" toolkit = Toolkit() toolkit.register_tool_function(non_streaming_function) res = await toolkit.call_tool_function( ToolUseBlock( type="tool_use", id="123", name="non_streaming_function", input={}, ), ) async for tool_response in res: print("Tool Response:") print(tool_response) print("The interrupted flag:") print(tool_response.is_interrupted) asyncio.run(example_tool_interruption()) .. rst-class:: sphx-glr-script-out .. code-block:: none Tool Response: ToolResponse(content=[{'type': 'text', 'text': 'The tool call has been interrupted by the user.'}], metadata=None, stream=True, is_last=True, is_interrupted=True, id='2025-08-15 10:15:21.907_8b8289') The interrupted flag: True .. GENERATED FROM PYTHON SOURCE LINES 246-251 For streaming tool functions, which returns an asynchronous generator, the ``Toolkit`` will attach the interrupted message to the previous chunk of the response. By this way, the agent can observe what the tool has returned before the interruption. The example of interrupting a streaming tool function is as follows: .. GENERATED FROM PYTHON SOURCE LINES 251-303 .. code-block:: Python async def streaming_function() -> AsyncGenerator[ToolResponse, None]: """A streaming tool function that can be interrupted.""" # Simulate a chunk of response yield ToolResponse( content=[ TextBlock( type="text", text="1234", ), ], ) # Simulate interruption raise asyncio.CancelledError() # The following code won't be executed due to the cancellation yield ToolResponse( content=[ TextBlock( type="text", text="123456789", ), ], ) async def example_streaming_tool_interruption() -> None: """Example of streaming tool interruption.""" toolkit = Toolkit() toolkit.register_tool_function(streaming_function) res = await toolkit.call_tool_function( ToolUseBlock( type="tool_use", id="xxx", name="streaming_function", input={}, ), ) i = 0 async for tool_response in res: print(f"Chunk {i}:") print(tool_response) print("The interrupted flag: ", tool_response.is_interrupted, "\n") i += 1 asyncio.run(example_streaming_tool_interruption()) .. rst-class:: sphx-glr-script-out .. code-block:: none Chunk 0: ToolResponse(content=[{'type': 'text', 'text': '1234'}], metadata=None, stream=False, is_last=True, is_interrupted=False, id='2025-08-15 10:15:21.910_4312aa') The interrupted flag: False Chunk 1: ToolResponse(content=[{'type': 'text', 'text': '1234'}, {'type': 'text', 'text': 'The tool call has been interrupted by the user.'}], metadata=None, stream=False, is_last=True, is_interrupted=True, id='2025-08-15 10:15:21.910_4312aa') The interrupted flag: True .. GENERATED FROM PYTHON SOURCE LINES 304-323 Automatic Tool Management ------------------------------------- .. image:: https://img.alicdn.com/imgextra/i3/O1CN013cvRpO27MfesMsTeh_!!6000000007783-2-tps-840-521.png :width: 100% :align: center :alt: Automatic Tool Management The ``Toolkit`` class supports **automatic tool management** by introducing the concept of **tool group**, as well as a **meta tool function** named ``reset_equipped_tools``. The tool group is a set of related tool functions, e.g. browser-use tools, map services tools, etc., which will be managed together. Only the tools in the activated groups will be visible to agents, i.e. accessible by the ``toolkit.get_json_schemas()`` method. Note there is a special group called ``basic``, which is always activated and the tools registered without specifying the group name will be added to this group by default. .. tip:: The ``basic`` group ensures that the basic usage of tools won't be affected by the group features if you don't need them. Now we try to create a tool group named ``browser_use``, which contains some web browsing tools. .. GENERATED FROM PYTHON SOURCE LINES 323-364 .. code-block:: Python def navigate(url: str) -> ToolResponse: """Navigate to a web page. Args: url (str): The URL of the web page to navigate to. """ pass def click_element(element_id: str) -> ToolResponse: """Click an element on the web page. Args: element_id (str): The ID of the element to click. """ pass toolkit = Toolkit() # Create a tool group named browser_use toolkit.create_tool_group( group_name="browser_use", description="The tool functions for web browsing.", active=False, # The notes when using these tools notes="""1. Use ``navigate`` to open a web page. 2. When requiring user authentication, ask the user for the credentials 3. ...""", ) toolkit.register_tool_function(navigate, group_name="browser_use") toolkit.register_tool_function(click_element, group_name="browser_use") # We can also register some basic tools toolkit.register_tool_function(execute_python_code) .. GENERATED FROM PYTHON SOURCE LINES 365-366 If we check the tools JSON schema, we can only see the ``execute_python_code`` tool, because the ``browser_use`` group is not activated yet: .. GENERATED FROM PYTHON SOURCE LINES 366-370 .. code-block:: Python print("Tool JSON Schemas with Group:") print(json.dumps(toolkit.get_json_schemas(), indent=4, ensure_ascii=False)) .. rst-class:: sphx-glr-script-out .. code-block:: none Tool JSON Schemas with Group: [ { "type": "function", "function": { "name": "execute_python_code", "parameters": { "properties": { "code": { "description": "The Python code to be executed.", "type": "string" }, "timeout": { "default": 300, "description": "The maximum time (in seconds) allowed for the code to run.", "type": "number" } }, "required": [ "code" ], "type": "object" }, "description": "Execute the given python code in a temp file and capture the return\n\ncode, standard output and error. Note you must `print` the output to get\nthe result, and the tmp file will be removed right after the execution." } } ] .. GENERATED FROM PYTHON SOURCE LINES 371-372 Use the ``update_tool_groups`` method to activate or deactivate tool groups: .. GENERATED FROM PYTHON SOURCE LINES 372-378 .. code-block:: Python toolkit.update_tool_groups(group_names=["browser_use"], active=True) print("Tool JSON Schemas with Group:") print(json.dumps(toolkit.get_json_schemas(), indent=4, ensure_ascii=False)) .. rst-class:: sphx-glr-script-out .. code-block:: none Tool JSON Schemas with Group: [ { "type": "function", "function": { "name": "navigate", "parameters": { "properties": { "url": { "description": "The URL of the web page to navigate to.", "type": "string" } }, "required": [ "url" ], "type": "object" }, "description": "Navigate to a web page." } }, { "type": "function", "function": { "name": "click_element", "parameters": { "properties": { "element_id": { "description": "The ID of the element to click.", "type": "string" } }, "required": [ "element_id" ], "type": "object" }, "description": "Click an element on the web page." } }, { "type": "function", "function": { "name": "execute_python_code", "parameters": { "properties": { "code": { "description": "The Python code to be executed.", "type": "string" }, "timeout": { "default": 300, "description": "The maximum time (in seconds) allowed for the code to run.", "type": "number" } }, "required": [ "code" ], "type": "object" }, "description": "Execute the given python code in a temp file and capture the return\n\ncode, standard output and error. Note you must `print` the output to get\nthe result, and the tmp file will be removed right after the execution." } } ] .. GENERATED FROM PYTHON SOURCE LINES 379-383 Additionally, ``Toolkit`` provides a meta tool function named ``reset_equipped_tools``, taking the current group names as the argument to indicate which groups to activate: .. note:: In ``ReActAgent`` class, you can enable the meta tool function by setting ``enable_meta_tool=True`` in the constructor. .. GENERATED FROM PYTHON SOURCE LINES 383-401 .. code-block:: Python # Register the meta tool function toolkit.register_tool_function(toolkit.reset_equipped_tools) reset_equipped = next( tool for tool in toolkit.get_json_schemas() if tool["function"]["name"] == "reset_equipped_tools" ) print("JSON schema of the ``reset_equipped_tools`` function:") print( json.dumps( reset_equipped, indent=4, ensure_ascii=False, ), ) .. rst-class:: sphx-glr-script-out .. code-block:: none JSON schema of the ``reset_equipped_tools`` function: { "type": "function", "function": { "name": "reset_equipped_tools", "parameters": { "properties": { "browser_use": { "default": false, "description": "The tool functions for web browsing.", "type": "boolean" } }, "type": "object" }, "description": "Choose appropriate tools to equip yourself with, so that you can\n\nfinish your task. Each argument in this function represents a group\nof related tools, and the value indicates whether to activate the\ngroup or not. Besides, the tool response of this function will\ncontain the precaution notes for using them, which you\n**MUST pay attention to and follow**. You can also reuse this function\nto check the notes of the tool groups.\n\nNote this function will `reset` the tools, so that the original tools\nwill be removed first." } } .. GENERATED FROM PYTHON SOURCE LINES 402-405 When agent calls the ``reset_equipped_tools`` function, the corresponding tool groups will be activated, and the tool response will contain the notes of the activated tool groups. .. GENERATED FROM PYTHON SOURCE LINES 405-428 .. code-block:: Python async def mock_agent_reset_tools() -> None: """Mock agent to reset tool groups.""" # Call the meta tool function res = await toolkit.call_tool_function( ToolUseBlock( type="tool_use", id="154", name="reset_equipped_tools", input={ "browser_user": True, }, ), ) async for tool_response in res: print("Text content in tool Response:") print(tool_response) asyncio.run(mock_agent_reset_tools()) .. rst-class:: sphx-glr-script-out .. code-block:: none Text content in tool Response: ToolResponse(content=[{'type': 'text', 'text': "Active tool groups successfully: ['browser_user']. You MUST follow these notes to use the tools:\n## About browser_use Tools\n1. Use ``navigate`` to open a web page.\n2. When requiring user authentication, ask the user for the credentials\n3. ..."}], metadata=None, stream=False, is_last=True, is_interrupted=False, id='2025-08-15 10:15:21.918_9a65a9') .. GENERATED FROM PYTHON SOURCE LINES 429-433 The toolkit also provides a method to gather the notes of the activated tool groups, and you can assemble it into your agent's system prompt. .. tip:: The automatic tool management feature is already implemented in the ``ReActAgent`` class, refer to the :ref:`agent` section for more details. .. GENERATED FROM PYTHON SOURCE LINES 433-446 .. code-block:: Python # Create one more tool group toolkit.create_tool_group( group_name="map_service", description="The google map service tools.", active=True, notes="""1. Use ``get_location`` to get the location of a place. 2. ...""", ) print("The gathered notes of the activated tool groups:") print(toolkit.get_activated_notes()) .. rst-class:: sphx-glr-script-out .. code-block:: none The gathered notes of the activated tool groups: ## About browser_use Tools 1. Use ``navigate`` to open a web page. 2. When requiring user authentication, ask the user for the credentials 3. ... ## About map_service Tools 1. Use ``get_location`` to get the location of a place. 2. ... .. GENERATED FROM PYTHON SOURCE LINES 447-453 Further Reading --------------------- - :ref:`agent` - :ref:`state` - :ref:`mcp` .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.022 seconds) .. _sphx_glr_download_tutorial_task_tool.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: task_tool.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: task_tool.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: task_tool.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_