SpringBoot+LangChain4j+Ollama实现Function Calling工具调用-仿智能客服示例

场景

SpringBoot+LangChain4j+Ollama实现本地大模型语言LLM的搭建、集成和示例流程:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/160340066

在上面的基础上,学习LangChain4j 工具调用(Function Calling)

一、工具调用(Function Calling)是什么?

工具调用允许大语言模型(LLM)在生成回复时,主动请求调用外部函数(工具)来获取实时数据或执行操作。

例如,用户问"北京天气怎么样?",模型会返回一个调用天气工具的函数请求,

由你的代码执行并返回结果,模型再基于结果生成最终回答。

核心价值:

扩展模型能力:

获取实时信息、操作数据库、调用第三方 API

提高准确性:

避免模型"幻觉",基于真实数据回答

实现自动化:

让 AI 能真正"做事"

二、环境准备与模型选择

2.1 必须条件

模型必须支持 Function Calling。Ollama 中支持工具调用的模型:

qwen2:7b(推荐,中文好)

llama3.1:8b

mistral:7b-v0.3

LangChain4j 版本:建议使用 1.0.0-beta4 或更高版本(API 更稳定)

Spring Boot 3.x + JDK 17

2.2 Ollama配置国内镜像源

前面通过魔搭社区下载的 GGUF 模型(如 modelscope.cn/Qwen/Qwen2.5-7B-Instruct-GGUF:latest)

通常不支持工具调用。请使用 Ollama 官方仓库中的模型名。

为此需要先配置Ollama的国内镜像源

到此目录下

创建 %USERPROFILE%\.ollama\config.json

比如

C:\Users\admin\.ollama

修改内容:

复制代码
​
{
  "registry": {
    "mirrors": {
      "registry.ollama.ai": "https://registry.ollama.ai"
    }
  }
}

这里是配置的阿里镜像源,也可配置其他镜像源。

重启ollama

2.3 下载支持工具的模型

复制代码
ollama pull qwen2:7b

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

三、Spring Boot 项目依赖与yml修改

pom修改:

复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-langchain4j-ollama-tool</artifactId>
    <version>1.0</version>

    <properties>
        <java.version>17</java.version>
        <langchain4j.version>1.0.0-beta4</langchain4j.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <!-- 核心:用于启用 @AiService 声明式 AI 服务 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
        </dependency>
        <!-- 具体模型:用于集成本地 Ollama 服务 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
        </dependency>
        <!-- LangChain4j 的流式返回类型 Flux<String> 需要额外的模块支持。需要添加依赖。 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
    </dependencies>

yml修改:

复制代码
​
langchain4j:
  ollama:
    chat-model:
      base-url: http://localhost:11434
      model-name: qwen2:7b
      temperature: 0.7
      timeout: PT120S           # 总超时时间 120 秒
      connect-timeout: PT10S    # 连接超时 10 秒
      read-timeout: PT120S      # 读取超时 120 秒
      log-requests: true
      log-responses: true

​

四、定义工具(@Tool 注解)

4.1 天气查询工具示例

复制代码
package com.badao.ai.tool;


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

import java.time.LocalDateTime;

@Component
public class WeatherTool {

    @Tool("获取指定城市当前的天气状况")
    public String getWeather(@P("城市名称,例如:Beijing") String city) {
        if ("Beijing".equalsIgnoreCase(city)) {
            return "北京现在是晴天,温度 25°C。";
        }
        return "无法获取 " + city + " 的天气信息。";
    }

    @Tool("获取当前时间")
    public String getCurrentDateTime(@P("你希望了解当前日期和时间") String placeholder) {
        return "当前时间是:" + LocalDateTime.now().toString();
    }
}

4.2 智能客服工具示例(订单、物流、优惠券)

复制代码
package com.badao.ai.tool;

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

import java.util.HashMap;
import java.util.Map;

@Component
public class CustomerServiceTools {

    // 模拟订单数据库
    private static final Map<String, String> ORDER_STATUS_DB = new HashMap<>();
    private static final Map<String, String> ORDER_LOGISTICS_DB = new HashMap<>();
    private static final Map<String, String> COUPON_DB = new HashMap<>();

    static {
        ORDER_STATUS_DB.put("123456", "已发货");
        ORDER_STATUS_DB.put("789012", "待支付");
        ORDER_STATUS_DB.put("345678", "已完成");

        ORDER_LOGISTICS_DB.put("123456", "申通快递:777888999,已揽件");
        ORDER_LOGISTICS_DB.put("345678", "顺丰快递:SF123456789,已签收");

        COUPON_DB.put("user_1", "满100减10元优惠券一张");
        COUPON_DB.put("user_2", "无门槛5元优惠券一张");
    }

    @Tool("查询订单状态。根据订单号获取当前状态(已发货、待支付、已完成等)")
    public String getOrderStatus(@P("订单号,例如 123456") String orderId) {
        String status = ORDER_STATUS_DB.getOrDefault(orderId, "未找到该订单");
        return "订单 " + orderId + " 当前状态:" + status;
    }

    @Tool("查询订单物流信息。根据订单号获取物流公司、单号及最新轨迹")
    public String getLogistics(@P("订单号,例如 123456") String orderId) {
        String logistics = ORDER_LOGISTICS_DB.getOrDefault(orderId, "暂无物流信息,可能订单尚未发货");
        return "订单 " + orderId + " 物流信息:" + logistics;
    }

    @Tool("查询用户可用的优惠券。根据用户ID返回优惠券信息")
    public String getCoupon(@P("用户ID,例如 user_1") String userId) {
        String coupon = COUPON_DB.getOrDefault(userId, "您当前没有可用的优惠券");
        return "用户 " + userId + " 的优惠券:" + coupon;
    }
}

五、创建 AI 服务接口与 Bean

5.1 定义 Assistant 接口

智能天气:

复制代码
package com.badao.ai.config;


import dev.langchain4j.service.SystemMessage;

public interface Assistant {
    @SystemMessage("你是一个有帮助的AI助手。你可以使用工具来回答用户关于天气和时间的问题。")
    String chat(String userMessage);
}

智能客服:

复制代码
package com.badao.ai.config;

import dev.langchain4j.service.SystemMessage;

public interface CustomerServiceAssistant {

    @SystemMessage("""
            你是一个智能客服助手,可以回答用户关于订单状态、物流信息、优惠券等问题。
            请使用提供的工具来获取准确信息。
            如果用户询问订单状态,请调用 getOrderStatus 工具。
            如果用户询问物流信息,请调用 getLogistics 工具。
            如果用户询问优惠券,请调用 getCoupon 工具。
            回答要简洁、友好,使用中文。
            """)
    String chat(String userMessage);
}

5.2 手动构建 AiServices Bean(推荐,避免自动扫描冲突)

复制代码
package com.badao.ai.config;

import com.badao.ai.tool.CustomerServiceTools;
import com.badao.ai.tool.WeatherTool;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AiConfig {

    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        // 为每个 memoryId 分配一个独立的 ChatMemory,保留最近 10 条消息
        return memoryId -> MessageWindowChatMemory.withMaxMessages(10);
    }

    @Bean(name = "assistant")
    public Assistant assistant(ChatModel chatModel, WeatherTool weatherService) {
        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .tools(weatherService)
                .build();
    }

    @Bean(name = "customAssistant")
    public CustomerServiceAssistant customerServiceAssistant(ChatModel chatModel, CustomerServiceTools customerServiceTools) {
        return AiServices.builder(CustomerServiceAssistant.class)
                .chatModel(chatModel)
                .tools(customerServiceTools)
                .build();
    }
}

注意:tools() 方法接收的是工具实例,而不是 Class。且 @SystemMessage 注解在接口方法上会被自动识别。

六、Controller 与测试

测试天气:

复制代码
package com.badao.ai.controller;


import com.badao.ai.config.Assistant;
import org.springframework.web.bind.annotation.*;

@RestController
public class ChatController {

    private final Assistant aiAssistant;

    // 构造函数注入
    public ChatController(Assistant aiAssistant) {
        this.aiAssistant = aiAssistant;
    }

    @GetMapping("/ai/assistant")
    public String askAssistant(@RequestParam String message) {
        return aiAssistant.chat(message);
    }
}

测试智能客服:

复制代码
package com.badao.ai.controller;

import com.badao.ai.config.CustomerServiceAssistant;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/customer")
public class CustomerServiceController {

    private final CustomerServiceAssistant assistant;

    public CustomerServiceController(CustomerServiceAssistant assistant) {
        this.assistant = assistant;
    }

    @GetMapping("/chat")
    public String chat(@RequestParam("message") String message) {
        return assistant.chat(message);
    }
}

七、测试结果

测试智能天气:

curl "http://localhost:885/ai/chat?message=北京天气怎么样?"

测试智能客服:

curl "http://localhost:885/ai/chat?message=我的订单123456现在什么状态?

八、常见问题

(1)模型不支持 tools

错误信息:

400 Bad Request: {"error":"... does not support tools"}

原因:使用的模型不支持 Function Calling。

解决:改用 qwen2:7b、llama3.1:8b 等官方模型。

(2)Bean 名称冲突

错误信息:

ConflictingBeanDefinitionException: Annotation-specified bean name 'assistant' conflicts with existing

原因:同时使用了 @AiService 注解和手动 @Bean 创建。

解决:二选一。推荐手动创建,并在接口上移除 @AiService。

(3)Cannot resolve method 'systemMessage' in 'AiServices'

原因:手动构建时没有 systemMessage() 方法。

解决:使用接口方法上的 @SystemMessage 注解,或使用 systemMessageProvider()。

九、总结

模型选择:优先使用 Ollama 官方支持 Function Calling 的模型(如 qwen2:7b)。

工具定义:每个 @Tool 方法都要有清晰的描述,参数用 @P 注解。

依赖管理:使用 BOM 统一版本,避免混用 beta 和 rc 版本。

Bean 注册:手动创建 AiServices Bean 比注解更可控,避免扫描冲突。

系统提示词:放在接口方法的 @SystemMessage 中,保持声明式风格。

超时设置:本地模型推理较慢,务必调高 read-timeout。

测试验证:先用 curl 直接调用 Ollama API 确认模型支持工具,再集成到 Spring。

相关推荐
2401_865382502 小时前
各省政务信息化项目验收材料清单汇总及差异分析
java·开发语言·数据库
Rust研习社2 小时前
深入浅出 Rust 泛型:从入门到实战
开发语言·后端·算法·rust
京师20万禁军教头2 小时前
31面向对象(中级)-方法重写/覆盖(override)
java
许彰午2 小时前
源码全开放,没人看——一个框架作者的真实经历
java·后端
YGY顾n凡2 小时前
我开源了一个项目:一句话创造一个AI世界!
前端·后端·aigc
SamDeepThinking2 小时前
写了十几年代码,聊聊什么样的人能做好Java开发
java·后端·程序员
凛_Lin~~2 小时前
安卓实现textview跑马灯效果
android·java
我母鸡啊3 小时前
软考架构师故事系列-数据库系统
后端·架构
开源盛世!!3 小时前
4.20-4.22
java·服务器·开发语言