LangChain4j学习笔记(三):函数调用(Function Calling)

LangChain4j学习笔记(三):函数调用(Function Calling)

知识分子没文化
2026-01-14 / 0 评论 / 1,126 阅读 / 6,225 字数 / 正在检测是否收录...

目录

LangChain官网:LangChain4j,官方英文文档:Get Started | LangChain4j,API 文档:Overview (LangChain4j)

非官方中文文档:快速开始 | LangChain4j 中文文档

官方示例代码仓库:langchain4j/langchain4j-examples

LangChain4j版本:1.14.0-beta24

一、定义

函数调用(Function Calling),亦称为工具调用(Tool Use),是指大语言模型在生成回复时,输出一个结构化的函数调用指令(包含函数名和参数),由外部程序负责执行该函数并将执行结果返回给模型,从而使模型能够获取实时数据、执行计算或触发外部操作的一种能力扩展机制。市面上的模型对函数调用的支持度不一,使用前需确认大模型是否支持该机制。

大模型本身无法直接执行代码,它只能生成文本,而函数调用是实现 Agent 能力的一个重要机制,让模型能执行一些它本身无法完成或者容易出错的任务,比如数学计算、操作数据库、调用外部API等。

二、执行流程

一次函数调用的完整流程如下:

  1. 用户发送问题 → AI Service 将消息 + 工具规格发送给大模型
  2. 大模型判断需要调用工具,返回 AiMessage(包含 ToolExecutionRequest:函数名 + 参数 JSON)
  3. 框架自动执行对应的 Java 方法,得到结果
  4. 框架将结果包装为 ToolExecutionResultMessage,连同之前的所有消息再次发送给大模型
  5. 大模型根据工具结果生成最终自然语言回复

特别是第 2 步和第 4 步,说明了 AiMessage 不只是"模型回复的消息",它还可能包含工具调用请求;也解释了为什么 ToolExecutionResultMessage 是一个独立的消息类型。

举个具体例子,假设用户问"100的平方根是多少",整个过程的数据流转如下:

第 1 步 → 发送给大模型:
  [SystemMessage] "你是一个能力强大的AI助手"
  [UserMessage] "100的平方根是多少?"
  + 工具规格:[ToolSpecification { name: "平方根运算", description: "...", parameters: {"num": double} }]

第 2 步 ← 大模型返回:
  AiMessage { toolExecutionRequests: [ToolExecutionRequest { name: "平方根运算", arguments: "{\"num\":100}" }] }

第 3 步 → 框架执行 CalculatorTools.squareRoot(100.0),返回 10.0

第 4 步 → 再次发送给大模型(需要带上之前的所有消息):
  [SystemMessage] "你是一个能力强大的AI助手"
  [UserMessage] "100的平方根是多少?"
  [AiMessage] ToolExecutionRequest { name: "平方根运算", arguments: "{\"num\":100}" }
  [ToolExecutionResultMessage] "10.0"

第 5 步 ← 大模型最终返回:
  AiMessage { text: "100的平方根是10.0" }

第 4 步再次发送给大模型时,之前的所有消息都要带上(包括用户消息、包含工具调用请求的 AiMessage),这样大模型才能理解上下文。这些消息也会被 ChatMemory 管理,在多轮对话中自动维护。

三、@Tool

使用 @Tool 标注一个 Java 方法,将该 Java 方法暴露为大模型可调用的工具,大模型通过分析用户问题和工具描述自主决策是否调用以及如何传参。

3.1、参数

@Tool 注解有几个参数:

  • name(工具名称)
    自定义工具的唯一标识符。如果在注解中未显式指定该参数,框架默认会使用被注解的 Java 方法名作为工具名。在大模型决定调用工具时,会严格匹配此名称来识别具体的工具。
  • value(工具描述)
    工具的文本描述。用于向大模型阐述该工具的功能、用途以及预期的使用场景。这是大模型进行语义理解并判断“在何种上下文条件下应该调用该工具”的核心依据,描述的清晰度直接决定了模型调用的准确率
  • returnBehavior(返回行为)
    控制工具执行完毕后的结果流向与处理策略。该参数接收一个 ReturnBehavior 枚举值,主要有以下三种行为模式:
    • TO_LLM(默认值):工具的执行结果会被作为上下文信息再次发送回大模型。大模型会基于该结果进行进一步的推理、总结或组织自然语言,最终生成回复给用户。
    • IMMEDIATE:工具的执行结果会跳过大模型的后续处理,直接作为最终响应返回给调用方(用户)。这种模式通常适用于那些不需要大模型再次加工的查询类操作,比如直接查库返回的 JSON 结果。
    • IMMEDIATE_IF_LAST:仅当该工具是本轮最后一个被调用的工具时,结果直接返回给用户;如果本轮还有其他工具待调用,则行为同 TO_LLM,结果仍发回模型继续推理。

关于 @Tool 工具方法的返回值和异常处理:

  • 返回值:可以是 String、任何对象(会被序列化返回给模型)或是 void(无返回时模型不会收到额外信息)
  • 异常处理:工具方法内抛出异常会导致工具调用失败,框架会捕获并将异常信息发送给大模型,让大模型决定如何处理,

3.2、基本使用

定义一个由 IoC 容器管理的工具类 CalculatorTools,用 @Tool 标注里面的工具方法:

@Component
public class CalculatorTools {

    @Tool(name = "平方根运算", value = "传入一个参数进行平方根运算,返回结果")
    double squareRoot(@P(value = "底数", required = true) double num) {
        System.out.println("调用平方根运算,计算" + num + "的平方根");
        return Math.sqrt(num);
    }

    @Tool("计算两个数的加减乘除")
    double calculate(@P("第一个数") double a, @P("运算符,仅支持+、-、*、/ ") String operator, @P("第二个数") double b) {
        return switch (operator) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("不支持的运算符:" + operator);
        };
    }

}

当用户问"3加5等于多少"时,大模型会根据工具描述判断调用 calculate 而非 squareRoot;当用户问"144的平方根"时则反过来。工具描述写得好不好,直接决定模型选得准不准

除了给工具方法添加描述,还可以使用@P 注解为工具方法的参数添加描述,该注解有两个参数:

  • value:参数描述
  • required:是否必填,默认为 true

@Toolname 未指定时,默认取方法名(如 calculate);当 value 是单个字符串时,value = 可以省略,如 @Tool("计算两个数的加减乘除") 等价于 @Tool(value = "计算两个数的加减乘除")

定义好工具类后,需要在 AI Service 中注入,有两种方式:

方式一:@AiService 注解声明

@AiService(wiringMode = EXPLICIT
           , chatModel = "qwenChatModel"
           // 也可用 Class 类型: tools = CalculatorTools.class 
           , tools = "calculatorTools"
)
public interface Assistant {

    @SystemMessage("你是一个能力强大的AI助手")
    String chat(String userQuestion);

}

如果要引入多个工具类,必须使用数组 @AiService(tools = {"calculatorTools", "xxxTools"}),而不是 @AiService(tools = "calculatorTools, xxxTools") 这样的字符串。

方式二:AiServices.builder() 编程式构建

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(qwenChatModel)
        .tools(new CalculatorTools())           // 可以传入多个工具类实例
        .maxSequentialToolsInvocations(10)      // 最大连续工具调用轮数,防止死循环
        .build();

maxSequentialToolsInvocations 限制了一轮对话中大模型最多连续调用工具的轮数。当工具之间的调用形成循环依赖(如工具A的结果触发工具B,工具B的结果又触发工具A),或大模型反复调用工具却无法得出结论时,该参数可以打破循环,避免无限递归。

3.3、@ToolMemoryId

在多用户场景下,@Tool 方法通常需要知道当前请求来自哪个用户/会话,才能执行与用户身份相关的操作(如查询该用户的订单、操作该用户的数据)。LangChain4j 提供了 @ToolMemoryId 注解,将 AI Service 方法上的 @MemoryId 值自动注入到 @Tool 方法的参数中。

@Component
public class OrderTools {

    @Autowired
    private OrderService orderService;

    @Tool("查询当前用户的待处理订单列表")
    String listMyOrders(@ToolMemoryId String memoryId) {
        // memoryId 就是 AI Service 方法上 @MemoryId 传入的值
        // 例如 "user-123",据此查询该用户的订单
        List<Order> orders = orderService.getPendingOrders(memoryId);
        return orders.toString();
    }

    @Tool("取消指定订单,仅能取消当前用户自己的订单")
    String cancelOrder(@ToolMemoryId String memoryId, @P("订单编号") String orderId) {
        boolean success = orderService.cancelOrder(memoryId, orderId);
        return success ? "取消成功" : "取消失败,订单不存在或不属于当前用户";
    }

}

对应的 AI Service:

@AiService(wiringMode = EXPLICIT, chatModel = "qwenChatModel", tools = "orderTools")

public interface Assistant {
    @SystemMessage("你是一个订单管理助手")
    String chat(@MemoryId String memoryId, String userMessage);
}

当用户调用 assistant.chat("user-123", "帮我查一下我的待处理订单") 时:

  1. 框架将 "user-123" 作为 @MemoryId 标识会话
  2. 大模型决定调用 listMyOrders 工具
  3. 框架自动将 "user-123" 注入到 @ToolMemoryId 标注的 memoryId 参数中
  4. 工具方法使用该值查询对应用户的订单

要点:

  • @ToolMemoryId 标注的参数不会出现在发送给大模型的工具规格(ToolSpecification)中,大模型看不到也不需要填充这个参数,它由框架在调用工具时自动注入
  • @ToolMemoryId 参数的类型应与 AI Service 方法上 @MemoryId 参数的类型一致(通常为 String
  • 当不使用 ChatMemory(即 AI Service 方法没有 @MemoryId 参数)时,@ToolMemoryId 的值为 null

3.4、@Tool 的局限

@Tool 注解为实现 Agent 能力提供了支持,但同时也存在一个局限,即不管本次会话是否会调用预留的工具,每次请求都会将工具类中的所有工具都发送给大模型。这会导致:

  • 一方面,工具数量多时 Token 消耗较大,并且当所有工具都暴露给大模型时,大模型更容易产生幻觉,从而选错工具、填错参数;

  • 另一方面,很多场景下需要按用户权限、按上下文条件暴露不同的工具,此时 @Tool 的全量静态注册无法满足这类需求。

因此有了 ToolProvider 来解决这些问题。

四、ToolProvider

ToolProvider 是 LangChain4j 提供用以实现 agent 能力的函数式接口,相比于全量静态@Tool 注解(服务启动时加载所有工具,每次请求全量打包发给大模型),ToolProvider动态的,在每一次对话请求发生时实时触发,根据当前请求的上下文按需装配工具,动态返回一组大模型可以调用的工具列表。

它主要带来以下几点优化:

  • 节约 Token:避免将成百上千个工具同时塞给大模型导致产生多余的 Token 消耗。
  • 权限隔离:能够根据当前会话的 chatMemoryId()(用户ID/会话ID),动态决定该用户是否可使用某项工具。
  • 出现幻觉:只给大模型提供当前场景最相关的工具,降低大模型选错工具、填错参数的概率。

4.1、定义与使用

定义一个 BookingToolProvider,根据用户消息中的关键词决定提供哪些工具:

@Component
public class BookingToolProvider implements ToolProvider {

    @Autowired
    private BookingService bookingService;

    @Override
    public ToolProviderResult provideTools(ToolProviderRequest request) {

        String userMessage = request.userMessage().singleText();
        // 不包含"预订"关键词,不提供任何工具
        if (!userMessage.contains("预订")) {
            return null;
        }

        // 1. 定义工具规格:告诉模型"有这个工具可用"
        ToolSpecification querySpec = ToolSpecification.builder()
                .name("get_booking_details")
                .description("根据预订编号查询预订详情")
                .parameters(JsonObjectSchema.builder()
                        .addStringProperty("bookingNumber", "预订编号,格式如 B-12345")
                        .required("bookingNumber")
                        .build())
                .build();

        // 2. 定义工具执行器:模型决定调用时,执行具体业务逻辑
        ToolExecutor queryExecutor = (toolExecutionRequest, memoryId) -> {
            try {
                Map<String, Object> args = new ObjectMapper().readValue(
                        toolExecutionRequest.arguments(), new TypeReference<Map<String, Object>>() {});
                String bookingNumber = args.get("bookingNumber").toString();
                return bookingService.getBooking(bookingNumber).toString();
            } catch (Exception e) {
                return "查询预订信息失败:" + e.getMessage();
            }
        };

        // 3. 将工具规格和执行器配对打包返回
        return ToolProviderResult.builder()
                .add(querySpec, queryExecutor)
                .build();
    }

}

通过 AiServices.builder() 注册 ToolProvider:

@Configuration
public class AiServiceConfig {

    @Bean
    public Assistant assistant(ChatLanguageModel chatModel, BookingToolProvider bookingToolProvider) {
        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .toolProvider(bookingToolProvider)
                .build();
    }

}

@ToolToolProvider 可以同时使用,框架会合并两者的工具列表。通常将通用、无条件的工具用 @Tool 静态注册,需要按条件动态提供的工具用 ToolProvider 管理。

4.2、说明

  • ToolProviderRequest:请求上下文对象,包含 userMessage()(当前用户消息)、chatMemoryId()(会话标识)和 invocationParameters()(调用参数)。通过这些上下文信息判断本次请求应该提供哪些工具。返回 null 表示不提供任何工具,模型将以纯对话模式响应。

  • ToolSpecification:工具规格,告诉模型"有哪些工具可以调用",相当于 @Tool 注解中 name + value + @P 参数的组合体。包含工具名称(name)、描述(description)和参数结构(parameters)。参数结构使用 JsonObjectSchema 定义,这是发送给大模型的 Schema 信息,模型据此判断何时调用以及如何传参。

    JsonObjectSchema 常用的属性定义方法:

    方法 对应 JSON Schema 类型 说明
    addStringProperty(name, description) string 字符串类型,如名称、编号
    addIntegerProperty(name, description) integer 整数类型,如数量、年龄
    addNumberProperty(name, description) number 数值类型(含小数),如金额、温度
    addBooleanProperty(name, description) boolean 布尔类型,如是/否
    addEnumProperty(name, enumSchema) enum 枚举类型,限定可选值
    addObjectProperty(name, objectSchema) object 嵌套对象类型
    addArrayProperty(name, arraySchema) array 数组类型
    required(name1, name2, ...) required 声明必填参数

    其中 addEnumProperty 的用法:

    JsonObjectSchema.builder()
            .addStringProperty("city", "城市名称")
            .addEnumProperty("unit", EnumSchema.builder()
                    .enumValues("celsius", "fahrenheit")
                    .description("温度单位")
                    .build())
            .required("city")
            .build()
  • ToolExecutor:工具执行器,是真正执行业务逻辑的地方,相当于 @Tool 标注的方法体本身。当模型决定调用某个工具时,框架会根据工具名称匹配到对应的 ToolExecutor 并执行。toolExecutionRequest.arguments() 返回模型传入的参数 JSON 字符串,需自行反序列化;memoryId 是当前会话标识,可用它获取用户身份等上下文信息。返回值为 String 类型,会作为 ToolExecutionResultMessage 发回给模型。

  • ToolProviderResult:最终返回结果,将 ToolSpecificationToolExecutor 配对打包。支持链式添加多个工具:.add(spec1, executor1).add(spec2, executor2).build()

ToolProvider 也支持配置返回行为,通过三参数重载 add(spec, executor, ReturnBehavior) 指定:

ToolProviderResult.builder()
        .add(bookingSpec, bookingExecutor, ReturnBehavior.IMMEDIATE)          // 结果直接返回给调用方
        .add(closeSpec, closeExecutor, ReturnBehavior.IMMEDIATE_IF_LAST)       // 仅当该工具是最后一个调用时直接返回
        .add(weatherSpec, weatherExecutor)                                      // ReturnBehavior.TO_LLM(默认),结果发回模型继续推理
        .build();

这与 @Tool 注解上的 returnBehavior 参数功能一致。

五、ToolChoice

ToolChoice 控制大模型在函数调用时的工具选择策略——是自主决策、强制调用,还是完全禁用。默认情况下,大模型会根据用户问题自行判断是否需要调用工具,但某些业务场景需要更精确的控制。

LangChain4j 中 ToolChoice 支持以下几种模式:

模式 说明 适用场景
AUTO auto 默认值,模型自主决定是否调用工具以及调用哪个工具 绝大多数场景的默认选择,模型根据问题自主决策
REQUIRED required 模型必须调用至少一个工具,不允许直接生成纯文本回复 适用于纯工具调用型 Agent,确保模型不会跳过工具直接编造答案。例如在一个只做数据查询的系统中,不能让模型在查不到数据时自己编一个结果
自定义工具名 {"type": "function", "function": {"name": "xxx"}} 模型必须调用指定的工具 适用于流程编排场景,开发者事先知道某一步必须调用特定工具,不需要模型"思考"
不指定 不传 tool_choice AUTO

ToolChoice 的实际效果取决于大模型是否支持。OpenAI 系列模型(及兼容接口)完整支持,部分模型可能不支持 REQUIRED 或指定工具名模式,使用前需确认模型文档。

5.1、在模型级别配置

在构建模型时通过 toolChoice() 设置默认策略:

OpenAiChatModel chatModel = OpenAiChatModel.builder()
        .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
        .apiKey(System.getenv("ApiKey_AliBian"))
        .modelName("qwen-plus")
        .toolChoice(ToolChoice.REQUIRED)    // 每次请求都必须调用工具
        .build();

5.2、在请求级别配置

模型级别的 toolChoice 是全局默认值,如果在某次特定请求中需要使用不同的策略,可以使用 ChatRequest 对象额外指定:

ChatRequest request = ChatRequest.builder()
        .messages(UserMessage.from("帮我查一下北京明天的天气"))
        .toolChoice(ToolChoice.REQUIRED)    // 本次请求强制调用工具
        .build();
ChatResponse response = chatModel.chat(request);

5.3、指定调用特定工具

通过传入 ToolChoice 还可以指定模型必须调用某个具体的工具:

// 强制模型调用名为 "get_weather" 的工具
ToolChoice mustCallWeather = ToolChoice.from("get_weather");
ChatRequest request = ChatRequest.builder()
        .messages(UserMessage.from("今天天气怎么样"))
        .toolChoice(mustCallWeather)
        .build();
ChatResponse response = chatModel.chat(request);

六、并行工具调用(Parallel Tool Execution)

并行工具调用是指大模型在一次回复中同时返回多个 ToolExecutionRequest,表示需要同时调用多个互不依赖的工具。例如用户问"北京和上海今天天气怎么样",模型可以一次性返回两个天气查询请求,而不是先查北京再查上海。

LangChain4j 默认为按顺序处理模型返回的多个工具调用请求:即框架依次执行所有请求的工具方法,收集全部结果后再一起发回给大模型,但也支持配置开启并行调用

6.1、模型配置

对于兼容 OpenAI 协议的模型,可以通过 parallelToolCalls 控制模型是否允许一次返回多个工具调用:

OpenAiChatModel chatModel = OpenAiChatModel.builder()
        .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
        .apiKey(System.getenv("ApiKey_AliBian"))
        .modelName("qwen-plus")
        .parallelToolCalls(true)   // 允许模型并行返回多个工具调用(默认 true)
        .build();

parallelToolCalls 设为 false 时,模型在一次回复中最多返回一个工具调用请求,确保严格串行执行。

6.2、执行行为说明

当模型一次返回多个 ToolExecutionRequest 时,LangChain4j 的处理逻辑:

模型返回:[ToolExecutionRequest(name="get_weather", args={"city":"北京"}),
           ToolExecutionRequest(name="get_weather", args={"city":"上海"})]

     ↓ LangChain4j 依次执行(当前为串行)

执行 get_weather("北京") → "北京:晴,25°C"

执行 get_weather("上海") → "上海:多云,28°C"

     ↓ 将所有执行结果一并返回给模型

[ToolExecutionResultMessage("北京:晴,25°C"), ToolExecutionResultMessage("上海:多云,28°C")]

     ↓ 模型基于所有工具结果生成最终回复

"北京今天晴,25°C;上海多云,28°C。"

需要注意:

  • 当前 LangChain4j 对多个工具调用是串行执行的,即按模型返回的顺序依次调用。如果工具之间相互独立且执行耗时较长,可以考虑自行用 CompletableFuture 并行化
  • 工具之间有依赖时慎用:如果工具 B 依赖工具 A 的执行结果,并行调用会导致逻辑错误。可以在系统提示词中约束 "每次只调用一个工具",或设置 parallelToolCalls(false)
  • 并行工具调用的支持度取决于模型,部分模型在一次回复中仅返回单个工具调用请求

七、工具调用的超时与重试

工具调用涉及两次网络请求:一次是调用大模型,一次是执行本地工具方法。两次都可能因网络波动、服务不可用等原因失败,为了程序的健壮性考虑,需要分别设置超时和重试策略。

7.1、模型调用超时

在构建模型时通过 timeout() 设置 HTTP 请求超时时间:

OpenAiChatModel chatModel = OpenAiChatModel.builder()
        .baseUrl("https://api.deepseek.com")
        .apiKey(System.getenv("Key_Deepseek"))
        .modelName("deepseek-v4-flash")
        .timeout(Duration.ofSeconds(120))     // HTTP 请求超时时间,默认 60 秒
        .build();

当大模型 API 响应时间超过设定值时,会抛出 TimeoutException

对于流式模型同样支持:

OpenAiStreamingChatModel streamingModel = OpenAiStreamingChatModel.builder()
        .baseUrl("https://api.deepseek.com")
        .apiKey(System.getenv("Key_Deepseek"))
        .modelName("deepseek-v4-flash")
        .timeout(Duration.ofSeconds(120))
        .build();

7.2、模型调用重试

LangChain4j 提供了重试能力,通过 maxRetries() 配置模型调用失败后的最大重试次数:

OpenAiChatModel chatModel = OpenAiChatModel.builder()
        .baseUrl("https://api.deepseek.com")
        .apiKey(System.getenv("Key_Deepseek"))
        .modelName("deepseek-v4-flash")
        .timeout(Duration.ofSeconds(60))
        .maxRetries(3)    // 请求失败后最多重试 3 次,默认 0 不重试
        .build();

重试的触发条件包括:网络超时、HTTP 5xx 错误、速率限制(429)等。LangChain4j 内部会在重试之间加入指数退避(exponential backoff),避免连续重试加剧服务端压力。

7.3、工具执行超时

@Tool 方法执行的是本地业务逻辑(数据库查询、外部 API 调用等),可能耗时较长。LangChain4j 在 AiServices.builder() 中提供了 toolExecutionTimeout 配置:

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(chatModel)
        .tools(new OrderTools())
        .toolExecutionTimeout(Duration.ofSeconds(30))  // 单个工具方法最大执行时间
        .maxSequentialToolsInvocations(10)
        .build();

当某个工具方法的执行时间超过设定值时,框架会中断执行并将超时信息返回给大模型,大模型会据此生成合适的回复(如"查询超时,请稍后再试")。

7.4、工具方法内的重试

工具方法自身的业务重试需要在方法内部自行实现,框架不负责工具方法级别的重试。例如调用外部支付 API 时,可以通过 Spring Retry 或手动循环实现:

@Tool("查询订单支付状态")
String queryPaymentStatus(@P("订单编号") String orderId) {

    int maxAttempts = 3;
    for (int i = 0; i < maxAttempts; i++) {
        try {
            return paymentClient.queryStatus(orderId);
        } catch (Exception e) {
            if (i == maxAttempts - 1) {
                // 最后一次仍失败,返回友好提示给模型,而不是把异常抛出去
                return "支付状态查询服务暂时不可用,请告知用户稍后再试";
            }
            // 指数退避
            try { Thread.sleep((long) Math.pow(2, i) * 500); } catch (InterruptedException ignored) {}
        }
    }
    return "支付状态查询服务暂时不可用,请告知用户稍后再试";
}

八、注意事项

在使用函数调用时也有部分问题需要留意:

异步/流式调用工具的冲突

当开启了流式传输(Streaming)并同时触发了 Function Calling 时,流式连接会暂时处于等待状态(Block),直到本地 Java 工具方法执行完毕、结果返回给大模型、大模型二次推理后,才会继续流式吐出最终文本。

异常处理

如果 @Tool 方法内部抛出了未经捕获的异常(例如数据库超时、空指针),LangChain4j 默认会把错误堆栈信息当作字符串返回给大模型。大模型会尝试"理解"这个报错,并用自然语言告诉用户:"对不起,系统报了 NullPointerException..."

本着家丑不可外扬的原则肯定不能让用户知道我们犯了这么低级的错误,因此不能让工具方法抛出未捕获的异常到框架层,就要在在工具方法内做好重试和异常兜底,比如在 @Tool 方法内使用 try-catch,返回对大模型友好的错误提示字符串(如 "当前查询服务不可用,请告知用户稍后再试"

工具描述的质量

大模型选择工具和填参完全依赖工具描述和参数描述的语义,描述模糊会导致模型犹豫不决或选错工具。建议在描述中包含三个要素:工具做什么、何时使用、参数格式。例如 “传入一个参数进行平方根运算,返回” 就不如 “对给定的非负数计算平方根,当用户询问某数的平方根或根号运算时使用,参数 num 必须为非负数” 清晰。

多次工具调用

大模型在一轮回复中可能同时请求调用多个工具(例如用户问"北京和上海的天气",模型可能一次返回两个 ToolExecutionRequest)。LangChain4j 会依次执行所有请求的工具,将全部结果一起发回模型。如果需要限制模型单次调用的工具数量,可在系统提示词中加以约束(如 "每次最多调用一个工具")。

参考资料:

官方英文文档:Get Started | LangChain4j,API 文档:Overview (LangChain4j)

非官方中文文档:快速开始 | LangChain4j 中文文档

2

评论 (0)

取消