LangChain4j 工具调用

LangChain4j 工具调用

薛定谔的汪 Lv5

LangChain4j 工具调用——让 AI 从”聊天”到”行动”

一、什么是工具调用?

1.1 从”聊天”到”行动”的进化

传统的 AI 对话是这样的:

  • 用户问:”今天北京天气怎么样?”
  • AI 答:”很抱歉,我无法获取实时天气信息。”

为什么?因为大模型本身只是一个”知识库”,他的训练数据是有截止时间的,它不知道此时此刻的温度。

工具调用(Function Calling / Tool Calling)解决了这个问题:当 AI 遇到自己不会的问题时,它会在响应中表达”我想调用某个工具”的意图,而不是直接输出文字。

开发者拿到这个意图后,执行真正的工具(查天气、发邮件、算数学),再把结果返回给 AI,AI 基于结果生成最终回答。

.2 工作原理:一次完整的工具调用流程

用户:”今天北京天气怎么样?”

【第1轮请求】UserMessage + 工具定义(getWeather)

【第1轮响应】AI说:我要调用 getWeather(“北京”)

【开发者执行】调用第三方天气平台获取北京天气

【第2轮请求】UserMessage + AiMessage(工具请求) + ToolExecutionResultMessage(结果)

【第2轮响应】AI说:”北京天气是 25摄氏度,晴朗”

整个过程完全自动,开发者只需要:

  1. @Tool 注解定义工具方法
  2. 在创建 AI Service 时注册这些工具

1.3 两个抽象级别

LangChain4j 提供了两个层次的工具 API:

级别 API 适用场景
低级别 ChatLanguageModel + ToolSpecification 需要完全控制请求/响应流程
高级别(推荐) AiServices + @Tool 注解 日常开发,自动处理工具执行和结果回传

二、核心概念:@Tool 注解详解

2.1 最简示例

java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;

@Component // 关键:必须让 Spring 管理,这样 AiService 才能找到
public class MyTools {

@Tool("执行加法运算,计算两个数的和") // 清晰的描述有助于 AI 判断何时调用
public double add(double a, double b) {
System.out.println("调用工具:加法运算 " + a + " + " + b);
return a + b;
}

@Tool("计算一个数字的平方根")
public double squareRoot(double x) {
System.out.println("调用工具:平方根运算 " + x);
if (x < 0) {
throw new IllegalArgumentException("不能对负数开平方");
}
return Math.sqrt(x);
}

// 模拟天气查询工具,参数可以用 @P 注解添加描述(可选)
@Tool("根据城市名称查询当前的天气状况")
public String getWeather(@P("城市名称,例如:北京、上海") String city) {
System.out.println("调用工具:查询天气,城市=" + city);
// 这里是模拟逻辑,实际可以调用第三方 API
if ("北京".equals(city)) {
return "北京当前天气:晴朗,25°C";
} else if ("上海".equals(city)) {
return "上海当前天气:多云,28°C";
}
return "未能查询到城市 '" + city + "' 的天气信息";
}
}

然后把工具注册到 AI Service:

1
2
3
4
5
6
@AiService(tools = "myTools")
public interface ChatAssistant {

@SystemMessage("你是一位有用的助手")
String chat(@UserMessage String userMessage);
}

2.2 @Tool 注解参数

根据官方文档,@Tool 注解提供以下配置项:

参数 说明 默认值
value / description 工具描述,AI 根据这个判断何时调用 空字符串
name 工具名称,不提供则使用方法名 方法名
returnBehavior 返回值处理方式, TO_LLM: 将自己的工具给的结果再请求LLM,LLM会再对结果“润色”, IMMEDIATE: 直接返回结果,不再二次请求 LLM TO_LLM
searchBehavior 工具是否可被搜索 SEARCHABLE

searchBehavior 是 LangChain4j 中 @Tool 注解的一个属性,用于控制工具在工具搜索策略 (ToolSearchStrategy) 下的可见性和发现方式。

它是一个枚举类型,包含两个值:

枚举值 说明 适用场景
SEARCHABLE (默认值) 工具不会在对话开始时就直接暴露给LLM,而是需要在需要时通过“工具搜索”的过程被LLM发现后才会变得可见。 当你的AI服务中配置了大量工具时,为避免占用LLM的上下文窗口(Token),让LLM按需动态发现相关工具,节省资源。
ALWAYS_VISIBLE 工具始终对LLM可见,无论是否配置了工具搜索策略。 对于核心、高频使用的工具(如获取用户信息的getUserProfile),确保LLM总能直接调用,无需额外搜索步骤。

核心概念解析:为什么需要 searchBehavior

要理解 searchBehavior 的作用,需要先了解 ToolSearchStrategy(工具搜索策略)。

  • 默认行为:如果不配置任何 ToolSearchStrategy,AI服务会将所有通过 .tools() 注册的工具一次性全部发送给LLM。当工具数量很少时,这没有问题。
  • 问题与优化:但如果你有几十个甚至上百个工具,每次都把所有工具的元数据(名称、描述、参数结构)发给LLM,会消耗大量Token,降低响应速度,甚至可能让LLM在众多工具中“挑花眼”,影响决策准确性。
  • ToolSearchStrategy 的价值:它允许你配置一种策略,让LLM先通过一两个“搜索工具”来按需查找真正相关的工具,然后再进行调用。这大大提高了效率。

实践对比

假设你为AI助手配置了20个工具,其中包括“搜索订单”和“查询天气”,并启用了 ToolSearchStrategy

  1. 如果 searchBehavior = SEARCHABLE (默认)
    • 初始对话:LLM只知道“工具搜索”这一两个特殊工具,不知道具体的20个工具。
    • 用户问:“帮我查一下最近的订单。”
    • LLM 行动:LLM会调用“工具搜索”工具,并提供“查询订单”作为关键词。
    • 框架行动ToolSearchStrategy 的执行器会从20个可搜索工具中找到“搜索订单”这个工具。
    • 后续调用:找到后,这个“搜索订单”工具才会被加载到上下文中,LLM接着用它完成最终查询。
  2. 如果 searchBehavior = ALWAYS_VISIBLE
    • 无论是否配置了 ToolSearchStrategy,“查询天气”这个工具在对话一开始就会和“工具搜索”工具一起暴露给LLM。
    • 用户问任何问题,LLM都能直接看到并尝试调用它。

2.3 参数定义:@P 注解

为了帮助 AI 正确填充参数,应该为每个参数提供描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class WeatherService {

@Tool("获取指定城市的天气预报")
public String getWeather(
@P("城市名称,如:北京、上海") String city,
@P("温度单位,可选 CELSIUS 或 FAHRENHEIT") TemperatureUnit unit
) {
// 查询天气...
return "北京今天晴,25°C";
}
}

// 枚举类型自动被 AI 识别
enum TemperatureUnit {
CELSIUS, FAHRENHEIT
}

参数必需性:默认所有参数都是必需的。可以通过 @P(required = false) 标记可选参数

2.4 多个工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.assistant.service;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface SmartAssistant {

@SystemMessage("""
你是一个智能助理,可以帮助用户完成各种任务。
你有以下能力:
1. 查询天气 - 当用户询问天气时使用 getWeather 工具
2. 数学计算 - 当用户需要计算时使用 calculate 工具
3. 联网搜索 - 当用户询问实时信息或新闻时使用 search 工具
4. 发送邮件 - 当用户要求发送邮件时使用 sendEmail 工具

请根据用户的问题,智能选择合适的工具。
如果用户要求发送邮件,请先确认收件人和内容。
""")
String chat(@UserMessage String userMessage);
}

配置工具:

可以把多个工具写到一个类里,如上述代码所示:

1
@AiService(tools = "myTools")

也可以将工具分别写到多个类里:

1
@AiService(tools = {"getWeather","calculate"...})

四、高级特性

4.1 并行工具调用

某些模型(如 OpenAI)支持一次响应中调用多个工具。LangChain4j 默认串行执行工具,但可以开启并发:

1
2
3
4
5
SmartAssistant assistant = AiServices.builder(SmartAssistant.class)
.chatLanguageModel(model)
.tools(weatherTool, calculatorTool)
.executeToolsConcurrently(true) // 开启并发执行
.build();

4.2 工具搜索策略(减少 Token 消耗)

当工具有很多时(比如 50+),每次都把所有工具定义发给 AI 会消耗大量 Token。LangChain4j 支持工具搜索策略:

java

1
// 使用 @Tool(searchBehavior = SearchBehavior.SEARCHABLE) 标记

4.3 工具执行前后通知

1
2
3
4
5
6
7
8
9
10
11
12
13
SmartAssistant assistant = AiServices.builder(SmartAssistant.class)
.chatLanguageModel(model)
.tools(weatherTool)
.beforeToolExecution((toolName, arguments) -> {
System.out.println("准备执行工具: " + toolName);
System.out.println("参数: " + arguments);
return null; // 返回非 null 会覆盖原执行
})
.afterToolExecution((toolName, arguments, result) -> {
System.out.println("工具执行完成: " + toolName);
System.out.println("结果: " + result);
})
.build();

4.4 MCP 集成:接入海量现成工具

MCP(Model Context Protocol) 是 Anthropic 推出的开放协议,允许 AI 应用通过标准化接口调用外部工具服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1. 创建 MCP 客户端连接工具服务器
McpTransport transport = new StdioMcpTransport.Builder()
.command(List.of("/usr/bin/npx", "-y", "@modelcontextprotocol/server-filesystem"))
.build();

McpClient mcpClient = new DefaultMcpClient.Builder()
.transport(transport)
.build();

// 2. 创建 MCP 工具提供者
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();

// 3. 注入到 AI Service
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider) // 使用 MCP 提供者
.build();

通过 MCP,你可以接入 GitHub、Slack、数据库等官方和社区提供的工具服务器,无需自己实现。

五、最佳实践与注意事项

5.1 工具描述要清晰

AI 靠提示词描述判断何时调用工具,描述越清晰,调用越准确:

1
2
3
4
5
//  不好
@Tool("处理数据")

// 好
@Tool("将 JSON 格式的用户数据保存到数据库,适用于用户注册或信息更新场景")

5.2 参数命名用英文

AI 训练数据中英文字段名更常见,中文参数名可能导致识别失败。

5.3 控制工具数量

一次请求发送太多工具定义会消耗大量 Token。建议:

  • 普通场景:5-10 个工具
  • 多工具场景:使用工具搜索策略

5.4 并发调用注意

AI Service 对同一个 @MemoryId 不支持并发调用,可能导致 ChatMemory 损坏。需要在高并发场景做好同步控制。

六、总结

需求 解决方案
基础工具调用 @Tool + AiServices.tools()
多个工具组合 注册多个工具对象,AI 自主选择
需要立即返回(不发回 LLM) @Tool(returnBehavior = IMMEDIATE)
减少 Token 消耗 工具搜索策略 / MCP 按需加载
接入社区工具 MCP 协议集成
工具执行监控 beforeToolExecution / afterToolExecution 钩子
  • Title: LangChain4j 工具调用
  • Author: 薛定谔的汪
  • Created at : 2025-04-30 18:01:54
  • Updated at : 2026-04-08 15:28:00
  • Link: https://www.zhengyk.cn/2025/04/30/ai/langchain4j_04_tools/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments