第一部分:Tool 概念与规范

1.1 什么是 Tool

Tool 是 LLM 可调用的具体功能模块。LLM 本身只能输出文本,无法主动执行操作(如查询数据库、调用 API、发送邮件等)。通过 Tool,LLM 可以“指挥”外部程序执行任务,再将结果整合成自然语言回复。

1.2 Tool 的三要素

要素 作用 是否必须
name 工具名,LLM 用来识别
description 工具描述,告诉 LLM 何时使用
parameters schema 参数定义(JSON Schema)
执行函数 真正干活的代码

1.3 Tool vs Function Call vs Agent

概念 角色 类比
Tool 可被调用的实际功能(定义) 工程师手里的扳手
Function Call LLM 输出“调用 Tool”的指令 工程师下达的指令
Agent 整合 LLM + Tools 的系统 自主完成项目的工程师

1.4 工具调用流程

用户提问 ──► LLM 思考 ──► 决定调用 Tool
                              │
                              ▼
                    生成结构化调用请求
                  (tool_name + arguments)
                              │
                              ▼
                    外部程序执行真实函数
                              │
                              ▼
                       返回执行结果
                              │
                              ▼
                  LLM 整合结果,输出自然语言回复

1.5 编写 Tool 的八条核心规范

✅ 规范 1:description 写清楚(最重要)

LLM 完全依赖 description 来判断何时调用。描述模糊会导致其不敢调用或错误调用。

# ❌ 反例
"天气工具"

# ✅ 正例
"查询指定城市的实时天气,包括温度、天气状况、风力等级。
 当用户问到天气、温度、是否下雨、出行建议时调用。"
✅ 规范 2:参数 description 也要写
# ✅ 正例
city: str  # description: 城市中文名,例如:北京、上海
limit: int = 5  # description: 返回结果数量,默认 5

LLM 通过参数描述来判断应填入什么值。

✅ 规范 3:必须返回字符串

复杂对象应使用 json.dumps 进行序列化:

return json.dumps({"orders": [...]}, ensure_ascii=False)
✅ 规范 4:异常不要抛,返回给 LLM

将异常信息以字符串形式返回,让 LLM 组织友好的回复,而不是直接抛出:

try:
    return call_api(city)
except Exception as e:
    return f"查询失败:{e}"
✅ 规范 5:敏感操作需二次确认
def delete_user(user_id: str, confirm: bool = False) -> str:
    if not confirm:
        return "操作已取消:需要 confirm=True 才能删除"
    user_service.delete(user_id)
    return f"用户 {user_id} 已删除"
✅ 规范 6:Tool 数量控制在 5~15 个

过多的 Tool 会导致 LLM 选择困难,过少则能力受限。复杂场景可采用分组 + 路由 Agent

✅ 规范 7:Tool 函数保持“无状态”

不要在 Tool 里读写全局变量,每次调用应只依赖输入参数,便于 LLM 进行推理。

✅ 规范 8:耗时操作加超时

外部 API 调用务必设置 timeout,避免 Agent 无限期等待:

resp = requests.get(url, timeout=5)

1.6 通用 JSON Schema 结构

无论使用何种语言,最终发送给 LLM 的都是符合 JSON Schema 规范的描述:

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "查询指定城市的天气",
    "parameters": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "description": "城市中文名"
        }
      },
      "required": ["city"]
    }
  }
}

支持的参数类型包括:stringnumberintegerbooleanarrayobjectenum


第二部分:Tool 规范详解与主流 Agent 框架内置 Tool 清单

在掌握如何编写自定义 Tool 之前,先了解 Tool 的协议规范——这决定了不同 LLM/框架之间能否互通。

2.0 Tool 协议规范

Tool 主要遵循两大标准:应用层协议(OpenAI Function Calling,事实上的工业标准)和 Schema 层标准(JSON Schema Draft 2020-12)。

2.0.1 OpenAI Function Calling 完整格式
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "查询指定城市的实时天气",
    "strict": true,
    "parameters": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "description": "城市中文名"
        },
        "unit": {
          "type": "string",
          "description": "温度单位",
          "enum": ["celsius", "fahrenheit"]
        }
      },
      "required": ["city"],
      "additionalProperties": false
    }
  }
}

LLM 返回的函数调用请求:

{
  "id": "call_abc123",
  "type": "function",
  "function": {
    "name": "get_weather",
    "arguments": "{\"city\": \"北京\", \"unit\": \"celsius\"}"
  }
}

工具执行结果回传:

{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "{\"temperature\": 25, \"condition\": \"sunny\"}"
}
2.0.2 JSON Schema 支持的类型
类型 用途 示例
string 字符串 "北京"
number 数字(浮点数) 25.5
integer 整数 100
boolean 布尔值 true / false
array 数组 ["a", "b"]
object 对象 {"key": "value"}
enum 枚举 ["a", "b", "c"]
null 空值 null
2.0.3 常用约束字段
{
  "type": "string",
  "description": "城市名称",
  "enum": ["北京", "上海", "广州"],
  "default": "北京",
  "minLength": 1,
  "maxLength": 50,
  "pattern": "^[\\u4e00-\\u9fa5]+$"
}
字段 适用类型 作用
description 所有 参数描述(LLM 可见
enum string/number 限定可选值
default 所有 默认值
minLength/maxLength string 长度限制
minimum/maximum number/integer 数值范围
pattern string 正则校验
format string 格式提示(emaildateuri
2.0.4 顶层必填字段
{
  "type": "object",
  "properties": { ... },
  "required": ["city"],              // 必填字段
  "additionalProperties": false       // 是否允许额外字段
}
2.0.5 各家 LLM 的 Tool 规范对比
厂商 协议 兼容性 特色
OpenAI Function Calling 事实上的标准 strict: true 严格模式
Anthropic Tool Use 自有格式 支持通过 input_schema 返回结构化数据
Google Gemini Function Calling 兼容 OpenAI 支持 codeExecution 等内置工具
阿里通义千问 DashScope Tool 完全兼容 OpenAI 无缝迁移
DeepSeek Function Call 兼容 OpenAI 推理能力较强

Anthropic Tool Use(差异点):格式稍有不同,无 type: "function" 包裹,parameters 改名为 input_schema,支持 cache_control 提示缓存标记。

{
  "name": "get_weather",
  "description": "查询天气",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {"type": "string", "description": "城市名"}
    },
    "required": ["city"]
  }
}
2.0.6 OpenAI 严格模式(strict: true

OpenAI 2024 年推出结构化输出(Structured Outputs),开启后强制要求 LLM 输出严格符合 schema:

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "查询天气",
    "strict": true,
    "parameters": {
      "type": "object",
      "properties": {
        "city": {"type": "string", "description": "城市名"},
        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
      },
      "required": ["city", "unit"],
      "additionalProperties": false
    }
  }
}

严格模式的要求:

  1. 所有字段必须列入 required(包括可选字段,可通过 default 指定默认值)
  2. 必须设 additionalProperties: false
  3. 部分高级组合(oneOfanyOfallOf)有使用限制
2.0.7 协议发展历程
2023.06  OpenAI 提出 Function Calling(业界开始有“标准”概念)
   │
2023.10  LangChain 推出 Tools,统一封装各 LLM 的 Tool 格式
   │
2024.05  Anthropic 推出 Tool Use,引入 input_schema 命名
   │
2024.08  OpenAI 推出 Structured Outputs(strict: true)
   │      大幅提升 Tool 调用可靠性
   │
2024.11  MCP(Model Context Protocol)发布
   │      由 Anthropic 主导,旨在统一工具/数据源协议
   │
2025     各大框架逐步兼容 MCP
2.0.8 新兴规范:MCP(Model Context Protocol)

2024 年底 Anthropic 推出 MCP,目标是统一 LLM 与外部工具/数据源的通信协议,类似“Agent Tool 的 USB-C 接口”。

核心思想:

  • 标准化:一次开发,所有兼容 MCP 的 LLM/Agent 都能用
  • 本地 + 远程:支持本地进程通信(stdio)和远程服务(HTTP/SSE)
  • 三角色架构:Host(Claude/Cursor 等)↔ Client ↔ Server(提供 Tool)

MCP Server 示例(Python):

from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio

app = Server("weather-server")

@app.list_tools()
async def list_tools():
    return [
        Tool(
            name="get_weather",
            description="查询城市天气",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名"}
                },
                "required": ["city"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_weather":
        city = arguments["city"]
        return [TextContent(type="text", text=f"{city} 晴天 25°C")]

MCP 生态:

  • 官方支持:Claude Desktop、Cursor、Cline
  • 社区:越来越多框架(Spring AI、LangChain)开始适配
  • 优势:避免每个 LLM/Agent 都重复实现工具接入
2.0.9 规范使用要点
规范层 标准 说明
应用层协议 OpenAI Function Calling 事实标准,所有主流 LLM 兼容
Schema 层 JSON Schema Draft 2020-12 参数描述、类型、约束的通用规范
严格性 strict: true OpenAI 结构化输出,可靠性最强
跨平台 MCP 新兴统一协议,未来方向

实际开发建议:

  1. 直接用 OpenAI 格式——兼容性最好,几乎所有 LLM 和框架都支持
  2. description 写清楚——规范之外的"软规范",但比硬规范更影响效果
  3. 开启 strict: true——能 100% 保证 LLM 输出的 JSON 合法
  4. 关注 MCP——未来 Agent Tool 的统一标准,值得提前了解

了解规范后,下面介绍主流 Agent 框架预置了哪些开箱即用的 Tool——大多数业务场景直接复用内置 Tool 即可,无需重复造轮子。

2.1 LangChain(Python / JS)

LangChain 是最成熟的 Agent 框架,其内置的 Tool 数量也最多。

2.1.1 搜索类
Tool 功能 典型用途
TavilySearchResults 调用 Tavily 搜索 API(专为 LLM 优化) 联网问答、RAG 增强
GoogleSerperRun 调用 Serper 的 Google 搜索 API 实时信息检索
SerpAPIWrapper 调用 SerpAPI(支持多搜索引擎) 通用搜索
DuckDuckGoSearchRun DuckDuckGo 免费搜索 无需 API Key 的搜索
WikipediaQueryRun 维基百科查询 知识问答
ArxivQueryRun Arxiv 论文搜索 学术研究
YouTubeSearchTool YouTube 视频搜索 视频内容检索
2.1.2 代码与文档类
Tool 功能 典型用途
PythonREPLTool 在沙箱中执行 Python 代码 计算、数据处理
ShellTool 执行 Shell 命令 文件操作、系统管理
FileManagementToolkit 文件读写、删除、列表 文档处理
ReadFileTool / WriteFileTool 读写文件 文档生成
ListDirectoryTool 列出目录 文件浏览
CopyFileTool / MoveFileTool 复制/移动文件 文件管理
DeleteFileTool 删除文件 清理操作
2.1.3 数据库与 API 类
Tool 功能 典型用途
SQLDatabaseToolkit 一整套 SQL 工具(查询、建表、描述表结构) 数据库问答
RequestsToolkit 发送 HTTP 请求 通用 API 调用
JsonToolkit 操作 JSON 数据 API 响应处理
OpenAPISpec 从 OpenAPI 规范自动生成 Tool 集 REST API 集成
ZapierToolkit 调用 Zapier 的上万种应用 连接 SaaS 服务
2.1.4 浏览器与抓取类
Tool 功能 典型用途
PlaywrightBrowserToolkit 浏览器自动化(点击、填表、截图等) Web 自动化
RequestsGetTool 发送 HTTP GET 请求 网页抓取
ExtractHyperlinksTool 从 HTML 中提取超链接 网页分析
ExtractTextTool 从 HTML 中提取文本内容 内容抽取
2.1.5 AI/ML 类
Tool 功能 典型用途
HumanInputRun 中途向用户提问 关键决策确认
MultionTool 浏览器自动化(AI 驱动) 复杂 Web 操作
WolframAlphaQueryRun 数学与科学计算 高级计算
2.1.6 快速使用示例
from langchain_community.tools import TavilySearchResults, WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain.prompts import ChatPromptTemplate

# 直接使用内置 Tool
search = TavilySearchResults(max_results=3)
wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())

# 添加自定义 Tool
@tool
def get_company_info(name: str) -> str:
    """查询公司基本信息。

    Args:
        name: 公司名称
    """
    # 业务逻辑
    return f"{name} 是一家科技公司"

tools = [search, wiki, get_company_info]

llm = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手。"),
    ("user", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_openai_tools_agent(llm, tools, prompt)
AgentExecutor(agent=agent, tools=tools, verbose=True).invoke({
    "input": "苹果公司的创始人是谁?最近有什么新闻?"
})

2.2 LlamaIndex

LlamaIndex 专注于 RAG(检索增强生成),内置丰富的“连接器”和“查询引擎”。

2.2.1 数据加载类
Tool 功能 典型用途
SimpleDirectoryReader 加载目录下所有文档 本地文档问答
NotionPageReader 读取 Notion 页面 Notion 数据接入
SlackReader 读取 Slack 消息 聊天数据
GmailReader 读取 Gmail 邮件 邮件数据
GoogleDocsReader 读取 Google Docs 文档 协作文档
PDFReader / DocxReader 读取 PDF/Word 文档 办公文档
WebPageReader 抓取网页 网页内容
2.2.2 查询工具类
Tool 功能 典型用途
QueryEngineTool 将任意 QueryEngine 封装为 Tool 多数据源问答
RetrieverTool 将 Retriever 封装为 Tool 检索增强
OnDemandLoaderTool 按需加载 + 检索 大规模数据
RouterQueryEngine 路由到不同数据源 多库联合问答
2.2.3 使用示例
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from llama_index.core.agent import ReActAgent
from llama_index.llms.openai import OpenAI

# 加载文档并建立索引
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()

# 包装为 Tool
doc_tool = QueryEngineTool(
    query_engine=query_engine,
    metadata=ToolMetadata(
        name="company_docs",
        description="查询公司内部文档,回答员工手册、政策等问题"
    )
)

agent = ReActAgent.from_tools([doc_tool], llm=OpenAI(model="gpt-4o"))
response = agent.chat("公司年假政策是什么?")

2.3 Spring AI / Spring AI Alibaba(Java)

Spring AI 提供官方 Tool 集成,Spring AI Alibaba(阿里云版)扩展了对阿里云生态的支持。

2.3.1 内置 Tool
Tool 功能 典型用途
WebSearchTool Web 搜索(基于 Brave Search) 联网问答
WebFluxHttpClient / RestClient HTTP 客户端 API 调用
FileSystemTools 文件读写 文档处理
CodeExecutorTool 代码执行 计算、脚本运行
VectorStoreTool 向量库检索 RAG
JiraTool / ConfluenceTool 集成 Atlassian 项目管理
SlackTool Slack 集成 通知发送
GitHubTool GitHub 操作 代码仓库管理
2.3.2 Spring AI Alibaba 扩展
Tool 功能 典型用途
阿里云百炼搜索 Tool 通义大模型 + 联网搜索 联网增强
阿里云 OSS Tool 对象存储操作 文件管理
阿里云 SLS Tool 日志服务查询 日志分析
钉钉 Tool 钉钉消息/通知 企业 IM
高德地图 Tool 地图查询、路径规划 LBS 服务
2.3.3 使用示例
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

@Component
public class WebSearchTool {

    private final HttpClient httpClient = HttpClient.newHttpClient();

    @Tool(description = "通过 Brave Search 联网搜索信息。" +
                       "当用户问到最新事件、新闻、实时数据时调用。")
    public String webSearch(
            @ToolParam(description = "搜索关键词") String query,
            @ToolParam(description = "返回结果数量,默认 5") int count) {

        try {
            String url = "https://api.search.brave.com/res/v1/web/search?q="
                       + query + "&count=" + count;
            HttpRequest req = HttpRequest.newBuilder()
                    .uri(java.net.URI.create(url))
                    .header("X-Subscription-Token", "BSA-xxx")
                    .GET()
                    .build();
            HttpResponse<String> resp = httpClient.send(req,
                    HttpResponse.BodyHandlers.ofString());
            return resp.body();
        } catch (Exception e) {
            return "搜索失败:" + e.getMessage();
        }
    }
}

2.4 Microsoft AutoGen(多 Agent 协作)

AutoGen 强项是多 Agent 协作,每个 Agent 都可以作为"工具人"。

Tool / Agent 功能 典型用途
CodingAgent 编写并执行代码 数据处理、自动化
WebSurferAgent 浏览器操作 网页交互
FileSurferAgent 文件操作 文档处理
MultimodalWebSurfer 多模态网页操作 视觉+文本网页

2.5 CrewAI(多 Agent 团队)

CrewAI 强调"角色化 Agent 团队协作",Tool 通过 @tool 装饰器或继承 BaseTool 添加。

内置 Tool 功能 典型用途
SerperDevTool Google 搜索 联网搜索
ScrapeWebsiteTool 网页抓取 内容提取
WebsiteSearchTool 网站站内搜索 站内检索
PDFSearchTool PDF 内容搜索 文档检索
DOCXSearchTool Word 内容搜索 文档检索
CSVSearchTool CSV 数据搜索 表格检索
DirectoryReadTool 目录读取 文件浏览
FileReadTool 文件读取 文件操作
CodeInterpreterTool 代码执行 计算
YoutubeVideoSearchTool YouTube 搜索 视频检索
EXASearchTool EXA 神经搜索 语义搜索
GithubSearchTool GitHub 搜索 代码搜索

2.6 选择建议

场景 推荐框架 理由
快速原型 / 个人项目 LangChain 生态最丰富,社区最活跃
RAG / 文档问答 LlamaIndex 数据连接器最多,索引能力最强
Java 企业项目 Spring AI Alibaba 与 Spring 生态完美融合
多 Agent 协作 AutoGen / CrewAI 原生支持多 Agent 编排
低代码 / 可视化 Dify / Coze 拖拽式搭建

2.7 实战建议

  1. 优先复用内置 Tool:90% 的常见需求(搜索、文件、HTTP、数据库)都有现成实现。
  2. 自定义 Tool 守住边界:只写业务专属的 Tool(订单系统、公司知识库等)。
  3. Tool 数量控制在 5~15 个:超过时用"分组 + 路由 Agent"。
  4. 持续评估 Tool 调用效果:通过日志分析 LLM 是否选错 Tool,针对性优化 description。

第三部分:Java 用例(Spring AI Alibaba)

3.1 环境准备

Maven 依赖(pom.xml):

<dependencies>
    <!-- Spring AI Alibaba 通义千问 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        <version>1.0.0</version>
    </dependency>

    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

application.yml:

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-plus

3.2 定义 Tool(注解式,推荐)

Spring AI 通过 @Tool 注解自动生成 JSON Schema,无需手写:

package com.example.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
public class WeatherTools {

    @Tool(description = "查询指定城市的实时天气情况,包括温度、天气状况、风力等级。" +
                       "当用户问到天气、温度、是否下雨、出行建议时调用。")
    public String getWeather(
            @ToolParam(description = "城市中文名,例如:北京、上海")
            String city) {
        return city + " 今天晴天,温度 25°C,东南风 3 级";
    }

    @Tool(description = "计算两个数字的和")
    public double add(
            @ToolParam(description = "第一个数字") double a,
            @ToolParam(description = "第二个数字") double b) {
        return a + b;
    }

    @Tool(description = "计算两个数字的乘积")
    public double multiply(
            @ToolParam(description = "第一个数字") double a,
            @ToolParam(description = "第二个数字") double b) {
        return a * b;
    }
}

注解对照:

注解 作用
@Tool(description="...") 工具描述
@ToolParam(description="...") 参数描述
@Component 注册为 Spring Bean

3.3 定义 Tool(编程式,更灵活)

import org.springframework.ai.tool.function.FunctionTool;
import java.util.function.Function;

public class CalculatorTools {

    public static FunctionTool addTool() {
        return FunctionTool.builder()
                .name("add")
                .description("计算两数之和")
                .inputType(AddRequest.class)
                .function((AddRequest req) -> req.a() + req.b())
                .build();
    }

    public record AddRequest(double a, double b) {}
}

3.4 用例 1:ChatClient 自动调用(推荐)

主类:

package com.example;

import com.example.tools.WeatherTools;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public ChatClient chatClient(ChatModel chatModel, WeatherTools weatherTools) {
        return ChatClient.builder(chatModel)
                .defaultTools(weatherTools)
                .build();
    }
}

Controller:

package com.example.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return chatClient.prompt(message).call().content();
    }
}

测试:

curl "http://localhost:8080/chat?message=北京天气怎么样?另外帮我算下 25*4"

Spring AI 会自动完成:

  1. @Tool 方法转成 JSON Schema 发给 LLM
  2. LLM 决定调用 getWeather(city="北京")multiply(a=25, b=4)
  3. Spring AI 执行这两个方法
  4. 把结果回传给 LLM
  5. LLM 组织自然语言返回给用户

3.5 用例 2:手动控制 Function Call

类似 Python 原生写法,使用底层 API:

import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ManualToolCaller {

    private final ChatModel chatModel;
    private final ToolCallingManager toolManager;

    public ManualToolCaller(ChatModel chatModel, ToolCallingManager toolManager) {
        this.chatModel = chatModel;
        this.toolManager = toolManager;
    }

    public String chat(String userInput) {
        ToolCallingChatOptions options = ToolCallingChatOptions.builder()
                .toolNames("getWeather", "multiply")
                .build();

        Prompt prompt = new Prompt(List.of(new UserMessage(userInput)), options);
        ChatResponse response = chatModel.call(prompt);
        AssistantMessage assistantMessage = response.getResult().getOutput();

        if (assistantMessage.getToolCalls() == null
                || assistantMessage.getToolCalls().isEmpty()) {
            return assistantMessage.getText();
        }

        List<Message> messages = new ArrayList<>(prompt.getInstructions());
        messages.add(assistantMessage);

        ToolExecutionResult toolResult = toolManager.executeTools(response);
        messages.addAll(toolResult.conversationHistory());

        ChatResponse finalResponse = chatModel.call(new Prompt(messages, options));
        return finalResponse.getResult().getOutput().getText();
    }
}

3.6 实战:调用外部 API

@Component
public class OrderTools {

    private final RestTemplate restTemplate = new RestTemplate();

    @Tool(description = "根据订单号查询订单详情,包括商品、价格、状态。" +
                       "当用户问到订单、购买、发货状态时调用。")
    public String getOrderInfo(
            @ToolParam(description = "订单号,例如:ORD20250801001")
            String orderId) {
        try {
            String url = "https://api.example.com/orders/" + orderId;
            return restTemplate.getForObject(url, String.class);
        } catch (Exception e) {
            return "{\"error\": \"订单不存在或查询失败\"}";
        }
    }

    @Tool(description = "根据订单号取消订单。仅在用户明确要求取消订单时调用。")
    public String cancelOrder(
            @ToolParam(description = "要取消的订单号") String orderId,
            @ToolParam(description = "取消原因") String reason) {
        try {
            String url = "https://api.example.com/orders/" + orderId
                       + "/cancel?reason=" + reason;
            restTemplate.postForObject(url, null, String.class);
            return "订单 " + orderId + " 已成功取消,原因:" + reason;
        } catch (Exception e) {
            return "取消失败:" + e.getMessage();
        }
    }
}

第四部分:Python 用例

4.1 环境准备

pip install openai langchain langchain-openai
import os
os.environ["OPENAI_API_KEY"] = "sk-xxx"

4.2 写法对比

方式 代码量 适用场景
原生 OpenAI 学习原理、底层控制
LangChain @tool 快速原型(推荐
Pydantic BaseTool 类型安全、复杂 Tool

4.3 用例 1:原生 OpenAI(手写 Function Call)

定义工具函数 + Schema:

import json
from openai import OpenAI

client = OpenAI(api_key="sk-xxx")

# 真实工具函数
def get_weather(city: str) -> str:
    return f"{city} 今天晴天,温度 25°C"

def add(a: float, b: float) -> float:
    return a + b

def multiply(a: float, b: float) -> float:
    return a * b

# 工具描述(OpenAI 格式)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的实时天气。当用户问到天气、温度、是否下雨时调用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市中文名,例如:北京、上海"}
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "add",
            "description": "计算两数之和",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {"type": "number", "description": "第一个数字"},
                    "b": {"type": "number", "description": "第二个数字"}
                },
                "required": ["a", "b"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "multiply",
            "description": "计算两数之积",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {"type": "number", "description": "第一个数字"},
                    "b": {"type": "number", "description": "第二个数字"}
                },
                "required": ["a", "b"]
            }
        }
    }
]

调用循环:

available_functions = {
    "get_weather": get_weather,
    "add": add,
    "multiply": multiply
}

def run_conversation(user_input: str) -> str:
    messages = [{"role": "user", "content": user_input}]

    while True:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )
        message = response.choices[0].message
        messages.append(message)

        if not message.tool_calls:
            return message.content

        for tool_call in message.tool_calls:
            name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)
            print(f"[Tool] {name}({args})")
            result = available_functions[name](**args)
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": str(result)
            })

print(run_conversation("北京天气怎么样?另外帮我算下 (3+5)*2"))

输出:

[Tool] get_weather({'city': '北京'})
[Tool] add({'a': 3.0, 'b': 5.0})
[Tool] multiply({'a': 8.0, 'b': 2.0})
北京今天晴天,温度 25°C。(3+5)*2 的结果是 16。

4.4 用例 2:LangChain @tool(推荐)

LangChain 会自动从函数签名 + docstring 生成 Schema:

from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain.prompts import ChatPromptTemplate

@tool
def get_weather(city: str) -> str:
    """查询指定城市的实时天气。当用户问到天气、温度、是否下雨时使用。"""
    return f"{city} 今天晴天,温度 25°C"

@tool
def calculate(expression: str) -> str:
    """计算数学表达式。例如:calculate(expression='2+3*4')"""
    try:
        return str(eval(expression))
    except Exception as e:
        return f"计算失败:{e}"

@tool
def search_database(query: str, limit: int = 5) -> str:
    """在数据库中搜索信息。

    Args:
        query: 搜索关键词
        limit: 返回结果数量,默认为 5
    """
    return f"找到 {limit} 条关于 '{query}' 的结果"

tools = [get_weather, calculate, search_database]

llm = ChatOpenAI(model="gpt-4o", api_key="sk-xxx")
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,可以使用工具回答问题。请用中文回复。"),
    ("user", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True
)

result = agent_executor.invoke({"input": "北京天气怎么样?顺便算下 2^10"})
print(result["output"])

verbose 输出:

> Entering new AgentExecutor chain...
Invoking: `get_weather` with `{'city': '北京'}`
北京 今天晴天,温度 25°C
Invoking: `calculate` with `{'expression': '2**10'}`
1024
北京今天晴天,温度 25°C。2^10 = 1024。
> Finished chain.

4.5 用例 3:Pydantic BaseTool(类型安全)

from langchain.tools import BaseTool
from pydantic import BaseModel, Field
from typing import Type

class WeatherInput(BaseModel):
    city: str = Field(description="城市中文名,例如:北京、上海")
    unit: str = Field(default="celsius", description="温度单位:celsius 或 fahrenheit")

class WeatherTool(BaseTool):
    name = "get_weather"
    description = "查询指定城市的实时天气"
    args_schema: Type[BaseModel] = WeatherInput

    def _run(self, city: str, unit: str = "celsius") -> str:
        temp = 25
        if unit == "fahrenheit":
            temp = temp * 9 / 5 + 32
        return f"{city} 当前温度:{temp}°{'C' if unit == 'celsius' else 'F'}"

    async def _arun(self, city: str, unit: str = "celsius") -> str:
        return self._run(city, unit)

4.6 实战:调用外部 API

import requests

@tool
def get_stock_price(symbol: str) -> str:
    """查询股票实时价格。当用户问到股票、股价、行情时使用。

    Args:
        symbol: 股票代码,例如:AAPL、TSLA、600519
    """
    try:
        resp = requests.get(
            f"https://api.example.com/stock/{symbol}",
            timeout=5
        )
        data = resp.json()
        return f"{symbol} 当前价格:{data['price']} 元,涨跌幅:{data['change']}%"
    except Exception as e:
        return f"查询失败:{e}"

@tool
def query_database(sql: str) -> str:
    """执行 SQL 查询数据库。仅支持 SELECT 语句。
    当用户需要数据分析、查询记录时使用。

    Args:
        sql: SQL 查询语句
    """
    if not sql.strip().lower().startswith("select"):
        return "错误:仅支持 SELECT 查询"
    try:
        result = db.execute(sql)
        return json.dumps(result, ensure_ascii=False)
    except Exception as e:
        return f"查询失败:{e}"

@tool
def delete_user(user_id: str, confirm: bool = False) -> str:
    """删除用户(危险操作)。仅在管理员明确要求时调用。

    Args:
        user_id: 要删除的用户ID
        confirm: 二次确认,必须为 True 才会真正执行
    """
    if not confirm:
        return "操作已取消:需要 confirm=True 才能删除用户"
    user_service.delete(user_id)
    return f"用户 {user_id} 已删除"

4.7 多轮对话 + 记忆

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)

agent_executor.invoke({"input": "北京天气怎么样?"})
agent_executor.invoke({"input": "那上海呢?"})   # 能记住上文

第五部分:Java vs Python 对比

特性 Python 原生 LangChain Python Spring AI Java
Schema 定义 手写 JSON docstring 自动生成 @Tool 注解自动生成
Tool 执行循环 手写 while Agent 自动处理 ChatClient 自动处理
多轮 Tool 调用 手动维护 messages 框架自动管理 框架自动管理
类型安全 弱(Pydantic 可加强) 强(编译期检查)
适用场景 快速原型、脚本 中小型项目 企业级 Spring 项目
学习曲线 中(需了解 Spring)

第五部分:总结

编写一个 Tool 的标准流程:

┌──────────────────────────────────────────────────┐
│ 1. 写函数 + 清晰的 description(最重要)         │
│ 2. 按 JSON Schema 描述参数                       │
│ 3. 注册到框架(@Tool / @tool / 手写 schema)      │
│ 4. 让 LLM 看到工具列表                           │
│ 5. 解析 LLM 的 tool_calls 请求                   │
│ 6. 执行真实函数 → 结果回传 LLM                   │
│ 7. 循环直到 LLM 给出最终答案                     │
└──────────────────────────────────────────────────┘

核心原则: Tool 的好坏 80% 取决于 description 写得是否清晰准确,这是 LLM 判断"何时调用、调什么参数"的唯一依据。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐