工具

背景介绍

工具调用根据实现方式不同分为两种类型:

两种不同实现方式
  • 基于 prompt 的工具调用: 开发人员在 prompt 中介绍工具,并从 LLM 响应中手动提取工具调用。

  • 基于 API 的工具调用:开发人员提供 JSON Schema 格式的工具描述,LLM API 将直接返回特定格

式的工具调用。

AgentScope 同时兼容两种方式,在本教程中,将展示如何使用 AgentScope 中内置的工具函数, 以及如何创建自定义工具。

import json

import agentscope
from agentscope.message import Msg
from agentscope.models import DashScopeChatWrapper

内置工具函数

AgentScope 提供了一个 ServiceToolkit 模块,支持以下功能:

  • 自动解析工具函数为 JSON Schema 格式

  • 提供一套默认的调用格式,

  • 参数检查和调用函数

在使用 ServiceToolkit 之前,我们可以先看一下 agentscope.service 模块中可用的工具。

from agentscope.service import get_help, ServiceResponse, ServiceExecStatus

get_help()

以上所有函数都是用 Python 实现的,可以通过调用 add 方法注册到 ServiceToolkit 中。 ServiceToolkit 将自动将工具函数解析为 JSON Schema 格式。

from agentscope.service import ServiceToolkit
from agentscope.service import bing_search, execute_shell_command

toolkit = ServiceToolkit()
toolkit.add(execute_shell_command)

注意,一些工具函数的参数(例如 api_key)应该由开发人员处理。 你可以直接在 add 方法中以关键字参数的形式传递这些参数,保留其他参数留给智能体填写。

toolkit.add(bing_search, api_key="xxx")

print("工具说明:")
print(toolkit.tools_instruction)
工具说明:
## Tool Functions:
The following tool functions are available in the format of
```
{index}. {function name}: {function description}
{argument1 name} ({argument type}): {argument description}
{argument2 name} ({argument type}): {argument description}
...
```

1. execute_shell_command: Executes a given shell command.
        command (string): The shell command to execute.
2. bing_search: Search question in Bing Search API and return the searching results
        num_results (number): The number of search results to return.
        question (string): The search query string.

内置的默认调用格式:

print(toolkit.tools_calling_format)
[{"name": "{function name}", "arguments": {"{argument1 name}": xxx, "{argument2 name}": xxx}}]

自动生成的工具函数 JSON Schema 格式说明:

print(json.dumps(toolkit.json_schemas, indent=2))
{
  "execute_shell_command": {
    "type": "function",
    "function": {
      "name": "execute_shell_command",
      "description": "Executes a given shell command.",
      "parameters": {
        "type": "object",
        "properties": {
          "command": {
            "type": "string",
            "description": "The shell command to execute."
          }
        },
        "required": [
          "command"
        ]
      }
    }
  },
  "bing_search": {
    "type": "function",
    "function": {
      "name": "bing_search",
      "description": "Search question in Bing Search API and return the searching results",
      "parameters": {
        "type": "object",
        "properties": {
          "num_results": {
            "type": "number",
            "description": "The number of search results to return.",
            "default": 10
          },
          "question": {
            "type": "string",
            "description": "The search query string."
          }
        },
        "required": [
          "question"
        ]
      }
    }
  }
}

基于 prompt 的工具调用

在基于 prompt 的工具调用中,开发人员需要: - 在 prompt 中介绍工具和调用格式 - 从 LLM 响应中手动提取工具调用

关于从字符串中提取工具调用,可以参考 结构化输出 章节中的解析器介绍。 `ServiceToolkit.parse_and_call_func`方法接受的工具调用格式如下:

from agentscope.message import ToolUseBlock

tool_call = ToolUseBlock(
    type="tool_use",
    id="xxx",
    name="bing_search",
    input={"query": "AgentScope"},
)

ServiceToolkit 注册工具函数后,可以将其集成到智能体中。

AgentScope 提供了 ReActAgent 智能体类来使用工具,只需要将 ServiceToolkit 对象传递给这个智能体。 有关该智能体的实现细节,请参阅 内置智能体

备注

ReActAgent 采用的是 prompt 本地拼接和解析的方式调用工具,而不是通过 模型 API 的 tools API 进行调用。关于 tools API 的使用,请参考 tools-api.

from agentscope.agents import ReActAgent

agentscope.init(
    model_configs={
        "config_name": "my-qwen-max",
        "model_type": "dashscope_chat",
        "model_name": "qwen-max",
    },
)

agent = ReActAgent(
    name="Friday",
    model_config_name="my-qwen-max",
    service_toolkit=toolkit,
    sys_prompt="你是一个名为 Friday 的助手。",
)

msg_task = Msg("user", "帮我计算一下 1615114134*4343434343", "user")

res = agent(msg_task)
system: Respond with specific tags as outlined below:
<thought>{what you thought}</thought>
<function>{the function name you want to call}</function>
<{argument name}>{argument value}</{argument name}>
<{argument name}>{argument value}</{argument name}>
...
Friday: <thought>用户需要我帮助他计算一个乘法问题。这个问题不需要使用到提供的工具函数,可以直接通过编程语言的计算功能来解决。</thought>
<function>execute_shell_command</function>
<command>echo "1615114134*4343434343" | bc</command>
system: 1. Execute function execute_shell_command
   [ARGUMENTS]:
       {"command": "echo \"1615114134*4343434343\" | bc"}
   [RESULT]: 7015142197480303962

system: Respond with specific tags as outlined below:
<thought>{what you thought}</thought>
<function>{the function name you want to call}</function>
<{argument name}>{argument value}</{argument name}>
<{argument name}>{argument value}</{argument name}>
...
Friday: <thought>已经得到了计算结果,现在可以将这个结果告诉用户。</thought>
<function>finish</function>
<response>1615114134乘以4343434343的结果是7015142197480303962。</response>
system: 1. Execute function finish
   [ARGUMENTS]:
       {"response": "1615114134乘以4343434343的结果是7015142197480303962。"}
   [RESULT]: 1615114134乘以4343434343的结果是7015142197480303962。

基于 API 的工具调用

基于 API 的工具调用中,开发者只需要提供 JSON Schema 格式的工具描述。但是不同的模型 API - 要求的 JSON Schema 格式不同 - 将工具调用,执行结果集成到prompt中的方式也不同

API-based tool calling

上图以 OpenAI 为例展示了基于 API 的工具调用如何工作。通过`agentscope.formatter`和 ModelResponse`的封装,我们隐藏了 API 的格式要求和转化细节,开发者只需要专注应用的开发。 开发者需要了解的是: - `ServiceToolkit 支持自动将函数解析为标准 JSON Schema 格式 - Formatter 模块支持将 JSON Schema 和消息列表转化成 API 需要的格式 - 不同模型 API 返回的工具调用会在 ModelResponse 中统一格式(ToolUseBlock)

小技巧

新的智能体类 ReActAgentV2 已上线,使用基于 API 的工具调用方法。 其使用方法与 ReActAgent 保持高度一致。

备注

目前 Formatter 模块只有 format_chat 函数支持工具调用,

format_multi_agent 函数的支持还在开发中。

备注

tools API目前还不支持流式返回,相关功能处于开发中。

我们以 DashScope API 为例展示如何使用 tools API.

from agentscope.formatters import DashScopeFormatter
from agentscope.message import TextBlock, ToolUseBlock, ToolResultBlock

model = DashScopeChatWrapper(
    config_name="_",
    model_name="qwen-max",
)

图中的 3 -> 4 步,将 JSON schemas 和 messages 转化成模型 API 需要的格式:

msgs = [
    Msg("user", "Help me to execute shell cmd 'whoami'", "user"),
]

formatted_msgs = DashScopeFormatter.format_chat(msgs)
formatted_schemas = DashScopeFormatter.format_tools_json_schemas(
    toolkit.json_schemas,
)
print(json.dumps(formatted_msgs, indent=4, ensure_ascii=False))
[
    {
        "role": "user",
        "content": [
            {
                "text": "Help me to execute shell cmd 'whoami'"
            }
        ]
    }
]

图中 5 -> 6 -> 7 步,获取模型响应:

response = model(formatted_msgs, tools=formatted_schemas)
print("tool_calls:", json.dumps(response.tool_calls, indent=4))
tool_calls: [
    {
        "type": "tool_use",
        "id": "call_67ad2e8345444e8a98ec64",
        "name": "execute_shell_command",
        "input": {
            "command": "whoami"
        }
    }
]

图中 8 步,创建一个包含工具调用的新消息:

# 创建带有工具调用的新消息
content = []
if response.text:
    content.append(TextBlock(type="text", text=response.text))
if response.tool_calls:
    content.extend(response.tool_calls)
msgs.append(Msg("assistant", content, "assistant", echo=True))

# 执行工具调用
msg_execution = toolkit.parse_and_call_func(
    response.tool_calls,
    tools_api_mode=True,  # 基于 API 的工具调用必须为 True
)
assistant: [
    {
        "type": "tool_use",
        "id": "call_67ad2e8345444e8a98ec64",
        "name": "execute_shell_command",
        "input": {
            "command": "whoami"
        }
    }
]

图中 9 步,将执行结果添加到消息列表中:

msgs.append(msg_execution)

在完成了一轮工具调用之后,让我们再次尝试将包含工具调用和结果的消息列表格式化:

formatted_msgs = DashScopeFormatter.format_chat(msgs)
print(json.dumps(formatted_msgs, indent=4, ensure_ascii=False))
[
    {
        "role": "user",
        "content": [
            {
                "text": "Help me to execute shell cmd 'whoami'"
            }
        ]
    },
    {
        "role": "assistant",
        "content": null,
        "tool_calls": [
            {
                "id": "call_67ad2e8345444e8a98ec64",
                "type": "function",
                "function": {
                    "name": "execute_shell_command",
                    "arguments": "{\"command\": \"whoami\"}"
                }
            }
        ]
    },
    {
        "role": "tool",
        "tool_call_id": "call_67ad2e8345444e8a98ec64",
        "content": "runner",
        "name": "execute_shell_command"
    }
]

目前,我们已经完成了基于 API 的工具调用全过程。在智能体内的具体实现可以参 考 agentscope.agents.ReActAgentV2

在 ServiceToolkit 中使用 MCP

AgentScope 支持集成 MCP (Model Context Protocol) 服务器,使模型和工具具有增强的功能。 您可以使用 add_mcp_servers 方法将 MCP 服务器添加到 ServiceToolkit 中,并在其中指定每个服务器的配置。 请注意,MCP 要求 Python 版本>=3.10。

configs = {
    "mcpServers": {
        "puppeteer": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-puppeteer"],
        },
    },
}

将 MCP 服务器配置添加到 ServiceToolkit toolkit.add_mcp_servers(server_configs=configs)

创建工具函数

自定义工具函数必须遵循以下规则:

  • 参数使用 typing 指定类型

  • 使用 Google 风格书写完整的 docstring

  • 函数返回值必须用 ServiceResponse 包装

def new_function(arg1: str, arg2: int) -> ServiceResponse:
    """简单介绍该函数。

    Args:
        arg1 (`str`):
            对 arg1 的简单描述
        arg2 (`int`):
            对 arg2 的简单描述
    """
    return ServiceResponse(
        status=ServiceExecStatus.SUCCESS,
        content="完成!",
    )

Total running time of the script: (0 minutes 11.151 seconds)

Gallery generated by Sphinx-Gallery