Skip to content

Tool ​

Agent 与大模型的一大区别在于能够使用外部工具拓展模型能力。在获取到每一步子任务的工作后,Agent 都会判断是否需要通过调用外部工具来完成该子任务,并在完成后获取该外部工具返回的信息提供给 LLM,进行下一步子任务的工作。

Function Calling 详解 ​

基本概念 ​

Function Call 是一种实现大型语言模型连接外部工具的机制。通过 API 调用 LLM 时,调用方可以描述函数,包括函数的功能描述、请求参数说明、响应参数说明,让 LLM 根据用户的输入,合适地选择调用哪个函数,同时理解用户的自然语言,并转换为调用函数的请求参数(通过 JSON 格式返回)。调用方使用 LLM 返回的函数名称和参数,调用函数并得到响应。最后,如果需要,把函数的响应传给 LLM,让 LLM 组织成自然语言回复用户。

Image

Function Calling 工作流程 ​

  1. 定义工具:使用JSON Schema格式描述可用的函数
  2. 用户输入:用户向LLM发送自然语言请求
  3. LLM决策:LLM分析用户意图,决定是否需要调用工具
  4. 生成调用:如果需要工具,LLM生成结构化的函数调用请求
  5. 执行函数:外部系统执行函数并返回结果
  6. 整合响应:LLM将函数结果整合到自然语言响应中

工具定义规范 ​

工具定义遵循JSON Schema规范,包含以下关键字段:

json
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "查询指定城市的天气信息",
    "parameters": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "description": "城市名称,如'北京'"
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "温度单位"
        }
      },
      "required": ["city"]
    }
  }
}

字段说明:

  • name:函数名称,LLM通过此名称调用函数
  • description:函数功能描述,帮助LLM理解何时使用此函数
  • parameters:参数定义,包括类型、描述、是否必填等
  • required:必填参数列表

多工具调用示例 ​

python
from openai import OpenAI

client = OpenAI()

# 定义多个工具
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"}
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_stock_price",
            "description": "获取股票价格",
            "parameters": {
                "type": "object",
                "properties": {
                    "symbol": {"type": "string", "description": "股票代码"}
                },
                "required": ["symbol"]
            }
        }
    }
]

# 调用LLM
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
    tools=tools
)

# 处理工具调用
if response.choices[0].message.tool_calls:
    tool_call = response.choices[0].message.tool_calls[0]
    # 执行工具并返回结果

工具注册机制 ​

工具注册架构 ​

工具注册是Agent系统的核心组件,负责管理所有可用工具的元数据和调用接口。

┌─────────────────────────────────────────────────────────┐
│                   工具注册中心                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │  工具元数据  │  │  调用接口   │  │  权限控制   │     │
│  │  (JSON Schema)│  │  (API/本地) │  │  (认证授权) │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└───────────────────────┬─────────────────────────────────┘
                        │
        ┌───────────────┼───────────────┐
        ↓               ↓               ↓
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│   天气工具    │ │   搜索工具    │ │   数据库工具  │
│   (API调用)   │ │   (本地执行)  │ │   (连接池)    │
└───────────────┘ └───────────────┘ └─────────────┘

工具注册实现 ​

python
from typing import Dict, Any, Callable
import json

class ToolRegistry:
    """工具注册中心,管理所有可用工具"""
    
    def __init__(self):
        self._tools: Dict[str, Dict[str, Any]] = {}
        self._handlers: Dict[str, Callable] = {}
    
    def register(self, name: str, description: str, 
                 parameters: dict, handler: Callable):
        """注册新工具"""
        self._tools[name] = {
            "name": name,
            "description": description,
            "parameters": parameters
        }
        self._handlers[name] = handler
    
    def get_tools_schema(self) -> list:
        """获取所有工具的JSON Schema"""
        return [
            {
                "type": "function",
                "function": tool
            }
            for tool in self._tools.values()
        ]
    
    def execute(self, name: str, arguments: dict) -> Any:
        """执行工具调用"""
        if name not in self._handlers:
            raise ValueError(f"Tool {name} not found")
        return self._handlers[name](**arguments)

# 使用示例
registry = ToolRegistry()

# 注册天气查询工具
def get_weather(city: str, unit: str = "celsius") -> dict:
    """获取城市天气信息"""
    # 实际实现中会调用天气API
    return {"city": city, "temperature": 25, "unit": unit}

registry.register(
    name="get_weather",
    description="查询指定城市的天气信息",
    parameters={
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "城市名称"},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
        },
        "required": ["city"]
    },
    handler=get_weather
)

动态工具发现 ​

动态工具发现允许Agent在运行时发现和加载新工具:

python
class DynamicToolDiscovery:
    """动态工具发现机制"""
    
    def __init__(self):
        self.discovered_tools = {}
    
    def discover_from_module(self, module_path: str):
        """从Python模块发现工具"""
        import importlib
        module = importlib.import_module(module_path)
        
        for attr_name in dir(module):
            attr = getattr(module, attr_name)
            if callable(attr) and hasattr(attr, 'tool_metadata'):
                self.discovered_tools[attr_name] = {
                    "handler": attr,
                    "metadata": attr.tool_metadata
                }
    
    def discover_from_api(self, api_spec_url: str):
        """从API规范发现工具(如OpenAPI/Swagger)"""
        import requests
        spec = requests.get(api_spec_url).json()
        
        for path, methods in spec.get("paths", {}).items():
            for method, details in methods.items():
                tool_name = f"{method}_{path.replace('/', '_')}"
                self.discovered_tools[tool_name] = {
                    "method": method.upper(),
                    "url": path,
                    "parameters": details.get("parameters", [])
                }

MRKL(Modular Reasoning, Knowledge and Language) ​

MRKL即模块化推理、知识和语言系统,是一种旨在改进现有大规模语言模型的自主代理的架构。MRKL系统旨在包含一系列"专家"模块,而LLM作为路由器,将查询引导至最合适的专家模块。这些专家模块既可以是大模型,也可以是符号的(例如数学计算器、货币转换器、天气API)。

MRKL 提供一个 Prompt 模板,其中包括:

  • 工具列表:每个工具的功能描述。

  • 主要指令:告诉 LLM "你可以思考后调用工具,拿到结果后继续思考"。

  • 对话或任务上下文:让 LLM 结合上下文做决策。

Image

Toolformer ​

训练了一个用于决定何时调用哪些API、传递什么参数以及如何最佳地将结果进行分析的大模型。这一过程通过微调的方法来训练大模型,仅需要每个API几个示例即可, 训练所用的数据集根据新增的 API 调用注释是否能够提高模型输出的质量而进行扩展。该工作集成了一系列工具,包括计算器、问答系统、搜索引擎、翻译系统和日历。

Image

HuggingGPT ​

HuggingGPT是由大型语言模型(LLM)驱动的,设计用来自主处理一系列复杂的人工智能任务。HuggingGPT融合了ChatGPT与HuggingFace。具体来说,LLM在这里扮演着大脑的角色,一方面根据用户请求拆解任务,另一方面依据任务描述选择适合的模型执行任务。通过执行这些模型并将结果整合到计划的任务中,HuggingGPT能自主完成复杂的用户请求。下图展示了从任务规划到模型选择,再到任务执行,最后是响应生成的完整流程:

  • 首先,HuggingGPT利用ChatGPT分析用户的请求以理解他们的意图,并将其分解为可能的解决方案。

  • 接下来,它会选择Hugging Face上托管的、最适合执行这些任务的专家模型。每个选定的模型被调用并执行,其结果将反馈给ChatGPT。

  • 最终,ChatGPT将所有模型的预测结果集成起来,为用户生成响应。

Image

HuggingGPT的这种工作方式不仅扩展了传统单一模式处理的能力,而且通过其智能的模型选择和任务执行机制,在跨领域任务中提供了高效、准确的解决方案。从本质上来说,HuggingGPT是一个使用ChatGPT作为任务规划器的框架,ChatGPT 可根据模型的描述选择 HuggingFace 平台中可用的模型,使其能够处理来自不同模态的输入,并根据执行结果总结响应结果。

MCP 协议详解 ​

MCP 概述 ​

MCP(Model Context Protocol,模型上下文协议)是Anthropic推出的标准化协议,旨在解决Agent开发中工具调用的碎片化问题。MCP通过统一的规范,让不同的工具提供商和模型提供商能够无缝对接。

MCP 核心概念 ​

  1. MCP Client(客户端):大模型运行环境,负责发起工具调用请求
  2. MCP Server(服务器):工具运行环境,提供具体的工具实现
  3. 传输层:支持stdio(本地通信)和HTTP+SSE(远程通信)两种模式

MCP 优势 ​

  • 复用性:一次开发,多处使用
  • 标准化:统一的接口规范
  • 生态丰富:社区贡献大量现成的MCP服务器
  • 安全性:标准化的认证和授权机制

MCP 服务器开发示例 ​

python
from mcp.server.fastmcp import FastMCP

# 创建MCP服务器实例
mcp = FastMCP("Demo")

# 注册工具
@mcp.tool()
def add(a: int, b: int) -> int:
    """两数相加"""
    return a + b

# 注册资源
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
    """获取个性化问候"""
    return f"Hello, {name}!"

# 注册提示模板
@mcp.prompt()
def review_code(code: str) -> str:
    """代码审查提示模板"""
    return f"请审查以下代码:\n{code}"

MCP 客户端开发 ​

python
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    # 配置服务器参数
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"],
        env=None
    )
    
    # 连接到MCP服务器
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化会话
            await session.initialize()
            
            # 列出可用工具
            tools = await session.list_tools()
            print("可用工具:", [tool.name for tool in tools.tools])
            
            # 调用工具
            result = await session.call_tool(
                "add",
                arguments={"a": 1, "b": 2}
            )
            print("结果:", result.content[0].text)

asyncio.run(main())

工具调用最佳实践 ​

1. 工具设计原则 ​

  • 单一职责:每个工具只做一件事
  • 清晰描述:工具描述要准确无歧义
  • 参数验证:严格验证输入参数
  • 错误处理:提供清晰的错误信息

2. 工具安全考虑 ​

  • 输入验证:防止注入攻击
  • 权限控制:限制工具访问范围
  • 日志记录:记录所有工具调用
  • 沙箱执行:高风险工具在沙箱中运行

3. 工具性能优化 ​

  • 缓存机制:缓存频繁调用的结果
  • 异步执行:长时间运行的工具使用异步
  • 并发控制:限制并发调用数量
  • 超时设置:设置合理的超时时间

总结 ​

工具调用是Agent系统的核心能力,从Function Calling到MCP协议,工具调用机制不断演进。掌握工具注册、调用和管理的最佳实践,是构建可靠Agent系统的关键。