工具使用

工具使用(Tool Use)是AI智能体从"能说"进化到"能做"的关键能力。通过工具使用,Agent不再局限于文本生成,而是能够调用外部API、执行代码、查询数据库、操作文件系统,从而在真实世界中产生实际影响。工具使用能力直接决定了Agent的实用价值。

一、函数调用(Function Calling)

函数调用是现代LLM提供的核心能力之一。它允许模型在推理过程中决定调用预定义的函数,并生成符合函数签名的参数。这种机制让LLM能够与外部系统交互,而不需要修改模型本身。

函数调用的工作流程:

  • 工具定义:开发者定义可用工具的函数签名(名称、参数、描述)。
  • 模型推理:LLM在推理时分析用户需求,决定是否调用工具以及调用哪个工具。
  • 参数生成:模型生成符合JSON Schema的工具调用参数。
  • 函数执行:系统根据模型输出调用实际函数,获取返回结果。
  • 结果整合:将函数返回结果传回模型,模型基于结果生成最终回答。
# OpenAI 风格的函数调用示例
tools = [
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "在互联网上搜索信息。当需要获取最新数据或验证事实时使用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "搜索查询词,应该简洁明确"
                    },
                    "max_results": {
                        "type": "integer",
                        "description": "最大返回结果数,默认5",
                        "default": 5
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "execute_code",
            "description": "执行Python代码并返回结果。用于数据计算、文件处理等。",
            "parameters": {
                "type": "object",
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "要执行的Python代码"
                    },
                    "timeout": {
                        "type": "integer",
                        "description": "执行超时时间(秒),默认30",
                        "default": 30
                    }
                },
                "required": ["code"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "query_database",
            "description": "查询数据库获取结构化数据。仅支持SELECT语句。",
            "parameters": {
                "type": "object",
                "properties": {
                    "sql": {
                        "type": "string",
                        "description": "SQL查询语句(仅支持SELECT)"
                    },
                    "database": {
                        "type": "string",
                        "description": "目标数据库名称",
                        "enum": ["users", "orders", "products"]
                    }
                },
                "required": ["sql", "database"]
            }
        }
    }
]

# Agent调用函数的处理流程
def process_with_tools(user_message, conversation_history):
    response = llm.chat(
        messages=conversation_history + [{"role": "user", "content": user_message}],
        tools=tools,
        tool_choice="auto"  # 让模型自行决定是否调用工具
    )

    # 检查模型是否决定调用工具
    if response.tool_calls:
        for tool_call in response.tool_calls:
            # 执行工具
            result = execute_tool(
                tool_call.function.name,
                json.loads(tool_call.function.arguments)
            )
            # 将结果返回给模型
            conversation_history.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })

        # 模型基于工具结果生成最终回答
        final_response = llm.chat(
            messages=conversation_history,
            tools=tools
        )
        return final_response.content
    else:
        return response.content

二、API 集成

工具使用的核心是将外部API封装为Agent可调用的工具。良好的API集成需要考虑接口设计、错误处理、认证授权等多个方面。

常见的API集成模式:

  • RESTful API:通过HTTP请求调用远程服务,如搜索引擎、天气API、翻译服务等。
  • 数据库查询:直接查询SQL/NoSQL数据库获取结构化数据。
  • 代码执行:在沙箱环境中执行代码,用于数据分析和计算。
  • 文件系统操作:读写文件、创建目录、处理文档等。
  • 系统命令:执行Shell命令进行系统管理(需要严格的安全控制)。
class ToolRegistry:
    """工具注册表:管理Agent可用的所有工具"""

    def __init__(self):
        self.tools = {}
        self.categories = {}

    def register(self, tool):
        """注册一个工具"""
        self.tools[tool.name] = tool
        category = tool.category or "general"
        if category not in self.categories:
            self.categories[category] = []
        self.categories[category].append(tool.name)

    def get_schemas(self):
        """获取所有工具的JSON Schema描述"""
        return [tool.to_schema() for tool in self.tools.values()]

    def execute(self, tool_name, arguments):
        """执行工具调用"""
        if tool_name not in self.tools:
            raise ToolNotFoundError(f"工具 '{tool_name}' 不存在")

        tool = self.tools[tool_name]

        # 参数验证
        validated_args = tool.validate_arguments(arguments)

        # 权限检查
        if not tool.check_permissions():
            raise PermissionError(f"无权执行工具 '{tool_name}'")

        # 执行工具
        try:
            result = tool.run(**validated_args)
            return ToolResult(success=True, data=result)
        except Exception as e:
            return ToolResult(success=False, error=str(e))

# 具体的API集成示例
class WebSearchTool(BaseTool):
    """网页搜索工具"""
    name = "web_search"
    description = "在互联网上搜索信息,获取最新数据和事实"
    category = "information"

    def __init__(self, api_key):
        self.api_key = api_key
        self.client = SearchClient(api_key)

    def to_schema(self):
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "搜索关键词"
                        },
                        "num_results": {
                            "type": "integer",
                            "description": "返回结果数量(1-10)",
                            "default": 5
                        }
                    },
                    "required": ["query"]
                }
            }
        }

    def run(self, query, num_results=5):
        results = self.client.search(query, limit=num_results)
        return [
            {
                "title": r.title,
                "snippet": r.snippet,
                "url": r.url
            }
            for r in results
        ]

三、工具描述设计

工具描述的质量直接影响Agent选择和使用工具的准确性。一个好的工具描述应该让LLM清楚地理解:这个工具能做什么、什么时候应该使用、参数如何填写。

工具描述的最佳实践:

  • 清晰的功能描述:用一句话说明工具的核心功能,避免模糊表述。
  • 使用场景说明:明确说明在什么情况下应该使用这个工具。
  • 参数详细描述:每个参数都要有类型、描述、约束说明。
  • 限制和边界:说明工具的能力边界和已知限制。
  • 返回值说明:描述工具返回的数据格式和含义。

工具描述的"黄金法则"

工具描述本质上是写给LLM看的"使用手册"。想象你是一个向同事介绍工具的人:你需要说清楚它能做什么、什么时候该用它、怎么用。好的工具描述能显著提高Agent的工具选择准确率,减少不必要的试错调用。在OpenClaw中,建议使用中文编写工具描述,因为中文描述对中文任务场景更精准。

四、工具选择策略

当Agent拥有大量工具时,如何从中选择最合适的工具是一个关键挑战。工具选择策略包括:

  • 直接选择:LLM直接从工具列表中选择一个工具。适合工具数量较少(少于20个)的场景。
  • 两阶段选择:先用轻量模型或规则筛选候选工具,再由LLM从候选中选择。适合工具数量较多的场景。
  • 语义检索:将工具描述编码为向量,根据用户查询语义检索最相关的工具。
  • 层级选择:先选择工具类别,再从类别中选择具体工具。
class SmartToolSelector:
    """智能工具选择器"""

    def __init__(self, all_tools, embedding_model):
        self.all_tools = all_tools
        self.embedding_model = embedding_model
        # 预计算工具描述的向量
        self.tool_embeddings = {
            tool.name: embedding_model.encode(
                f"{tool.name}: {tool.description}"
            )
            for tool in all_tools
        }

    def select_tools(self, query, top_k=5):
        """基于语义相似度选择候选工具"""
        query_embedding = self.embedding_model.encode(query)

        # 计算与每个工具的相似度
        similarities = {}
        for tool_name, tool_emb in self.tool_embeddings.items():
            sim = cosine_similarity(query_embedding, tool_emb)
            similarities[tool_name] = sim

        # 返回最相似的top_k个工具
        sorted_tools = sorted(
            similarities.items(),
            key=lambda x: x[1],
            reverse=True
        )
        return [name for name, score in sorted_tools[:top_k]]

    def select_with_llm(self, query, context):
        """两阶段选择:先检索候选,再由LLM选择"""
        # 阶段1:语义检索候选工具
        candidates = self.select_tools(query, top_k=8)
        candidate_tools = [
            self.all_tools[n] for n in candidates
        ]

        # 阶段2:LLM从候选中选择
        prompt = f"""用户需求:{query}
当前上下文:{context}

可用工具:
{self._format_tools(candidate_tools)}

请从以上工具中选择1-3个最适合的工具来完成用户需求。
返回工具名称列表。"""

        selected = llm.generate(prompt)
        return self._parse_tool_selection(selected)

# 工具组合使用示例
class ToolChain:
    """工具链:按顺序调用多个工具"""

    def __init__(self, steps):
        self.steps = steps  # [(tool_name, arg_mapping), ...]

    def execute(self, initial_input):
        context = {"input": initial_input}

        for tool_name, arg_mapping in self.steps:
            # 从上下文中提取参数
            args = {
                param: context.get(source, source)
                for param, source in arg_mapping.items()
            }

            # 执行工具
            result = tool_registry.execute(tool_name, args)

            # 将结果存入上下文
            context[f"{tool_name}_result"] = result.data

        return context

五、工具安全与权限管理

工具使用带来了强大的能力,但也引入了安全风险。Agent可能调用危险的工具、传入恶意参数、或泄露敏感数据。因此,工具安全是Agent系统设计中不可忽视的环节。

  • 最小权限原则:Agent只应拥有完成任务所需的最少工具权限。
  • 参数验证:所有工具参数在使用前必须经过严格验证。
  • 沙箱执行:代码执行类工具应在隔离的沙箱环境中运行。
  • 操作审计:记录所有工具调用的详细日志,支持事后审计。
  • 速率限制:限制工具调用频率,防止滥用。

工具使用的演进方向

工具使用能力正在快速发展。从最初的固定函数调用,到动态工具发现、工具创建,再到Agent间工具共享,工具使用的范式不断演进。MCP(Model Context Protocol)等标准协议的出现,正在建立统一的工具集成规范。在OpenClaw中,工具系统支持热插拔,Agent可以在运行时动态加载新工具,极大提升了系统的灵活性。

六、工具使用的性能优化

在实际部署中,工具使用的性能优化至关重要:

  • 并行调用:当多个工具调用之间没有依赖关系时,可以并行执行以节省时间。
  • 结果缓存:对相同参数的工具调用结果进行缓存,避免重复执行。
  • 超时控制:为每个工具调用设置合理的超时时间,防止长时间阻塞。
  • 降级策略:当工具调用失败时,Agent应能使用替代方案继续执行。
  • 批量操作:将多个小操作合并为批量调用,减少网络开销。

工具使用是Agent连接数字世界的桥梁。通过精心设计的工具描述、智能的工具选择策略和严格的安全管理,我们可以让Agent安全高效地利用各种外部工具,真正发挥AI智能体的实用价值。这也是OpenClaw框架的核心竞争力之一——丰富的工具生态和灵活的工具集成机制。