SpringBoot3集成Langchain4j使用Ollama

一、概述

langchain4j英文官网:https://docs.langchain4j.dev/

langchain4j中文官网:https://docs.langchain4j.info/

经过测试,在使用Spring AI 1.x中,很多参数不支持传递,Spring AI 2.x中虽然重构了,但是对于SpringBoot版本兼容性不是很好(必须使用 Spring Boot 4.x)

经测试,在使用 OllamaStreamingChatModel 流响应时,必须增加Content-Type: application/json,否则会出现中文乱码导致的无响应问题

二、引入依赖

全局版本管理

java 复制代码
<dependencyManagement>
        <dependencies>
            <!-- 全局管理 langchain4j,统一版本号 -->
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>1.11.7</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- 修复 WebFlux 安全漏洞 -->
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-codec-dns</artifactId>
                <version>4.2.13.Final</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

使用到的依赖

java 复制代码
        <!-- WebFlux 响应式 Web 框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <!-- 声明式服务 -->
        <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>

        <!-- 流响应 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
        </dependency>

三、配置YAML

java 复制代码
server:
  servlet:
    encoding:
      charset: UTF-8
      enabled: true
      force: true

# LangChain4j配置
langchain4j:
  ollama:
    base-url: http://192.168.2.17:12434
    model-name: qwen2.5:7b
    temperature: 0.2
    timeout: 60s
    log-requests: true
    log-responses: true

四、配置Config

java 复制代码
package org.panda.config;

import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaStreamingChatModel;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Ollama配置类
 *
 * @author wangqingpan
 */
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "langchain4j.ollama")
public class OllamaConfig {

    /**
     * 模型服务地址
     */
    private String baseUrl;

    /**
     * 模型名称
     */
    private String modelName;

    /**
     * 请求超时时间
     */
    private Duration timeout;

    /**
     * 模型温度
     */
    private Double temperature;

    /**
     * 是否记录请求
     */
    private Boolean logRequests;

    /**
     * 是否记录响应
     */
    private Boolean logResponses;

    /**
     * 聊天记忆,保留十条对话
     */
    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(10)
                .build();
    }

    /**
     * 创建Ollama模型实例
     */
    @Bean("ollamaChatModels")
    public OllamaChatModel ollamaChatModels() {
        Map<String, String> headersMap = new HashMap<>();
        headersMap.put("enable_thinking", "false");
        return OllamaChatModel.builder()
                .baseUrl(baseUrl)
                .modelName(modelName)
                .temperature(temperature)
                .timeout(timeout)
                .logRequests(logRequests)
                .logResponses(logResponses)
                .customHeaders(headersMap)
                .build();
    }

    /**
     * 创建Ollama流式模型实例
     */
    @Bean("ollamaStreamingChatModels")
    public OllamaStreamingChatModel ollamaStreamingChatModels() {
        Map<String, String> headersMap = new HashMap<>();
        headersMap.put("enable_thinking", "false");
        // 添加Content-Type头(不加会导致中文乱发,造成无响应的情况)
        headersMap.put("Content-Type", "application/json");
        return OllamaStreamingChatModel.builder()
                .baseUrl(baseUrl)
                .modelName(modelName)
                .temperature(temperature)
                .timeout(timeout)
                .logRequests(logRequests)
                .logResponses(logResponses)
                .customHeaders(headersMap)
                .build();
     }
}

五、配置工具

java 复制代码
package org.panda.tools;

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

/**
 * Ollama工具类
 *
 * @author wangqingpan
 */
@Component
public class OllamaTool {

    @Tool("查询地区的指标数据")
    public String whoName(@P("地区") String area,
                          @P("指标") String metric) {

        return area + "的" + metric + "数据是"+ "100";
    }

}

六、配置prompt

在resources下新建prompt文件夹,在prompt文件夹下新建who.txt

java 复制代码
你是一个物联网智能助手,负责帮助用户查询环境数据,你的名字叫垃圾。

可用工具:
- whoName(area, metric): 查询指定地区的指标数据

任务规则:
1. 从用户问题中提取地区(area)和指标(metric)
2. metric 归一化:必须将感官词映射为标准物理量(如:热/度数->温度,闷/湿->湿度,下雨/降水->雨量,亮/黑->光照强度)
3. area 提取最小精确地名:提取国家、城市、区域或室内房间名
4. 使用 whoName 工具查询数据并返回结果
5. 如果问题与物联网环境感知无关,直接回复"该问题不在我的服务范围内"

示例:
- "北京现在热吗" -> 调用 whoName("北京", "温度") -> 返回工具的查询结果
- "北京丰台现在热吗" -> 调用 whoName("丰台", "温度") -> 返回工具的查询结果
- "美国那边下雨了吗" -> 调用 whoName("美国", "雨量") -> 返回工具的查询结果
- "你是谁" -> 回复"该问题不在我的服务范围内"
- "讲个笑话" -> 回复"该问题不在我的服务范围内"

重要:不要输出JSON格式,而是直接调用工具并返回工具的查询结果。
如果问你叫什么你可以回复"我叫垃圾,是一个物联网智能助手"

七、配置Agent服务类

java 复制代码
package org.panda.service;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;

/**
 * Ollama智能助手接口
 * wiringMode 指定装配模式
 *      AiServiceWiringMode.AUTOMATIC 默认自动模式、
 *      AiServiceWiringMode.EXPLICIT 手动模式
 * chatModel 聊天模型Bean的名称
 * chatMemoryProvider 聊天记忆Bean的名称
 * tools 可用于当前AI模型的工具Bean名称数组
 * @author wangqingpan
 */
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT,
        chatModel = "ollamaChatModels",
        chatMemoryProvider = "chatMemoryProvider",
        streamingChatModel = "ollamaStreamingChatModels",
        tools = {
                "ollamaTool"
        }
)
public interface OllamaAgent {

    /**
     * 聊天
     *
     * @param userMessage 用户消息
     * @return 响应
     */
    @SystemMessage(fromResource = "/prompt/who.txt")
    String chat(@UserMessage String userMessage);

    /**
     * 流式聊天
     *
     * @param userMessage 用户消息
     * @return 响应
     */
    @SystemMessage(fromResource = "/prompt/who.txt")
    Flux<String> chatStream(@UserMessage String userMessage);
}

八、测试

java 复制代码
package org.panda.controller;

import lombok.RequiredArgsConstructor;
import org.panda.service.OllamaAgent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * Ollama控制器
 *
 * @author wangqingpan
 */
@RestController
@RequestMapping("/api/ollama")
@RequiredArgsConstructor
public class OllamaController {

    private final OllamaAgent ollamaAgent;

    /**
     * 普通聊天接口
     *
     * @param message 用户消息
     * @return AI响应
     */
    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return ollamaAgent.chat(message);
    }

    /**
     * 流式聊天接口
     *
     * @param message 用户消息
     * @return AI响应
     */
    @GetMapping("/chat/stream")
    public Flux<String> chatStream(@RequestParam String message) {
        return ollamaAgent.chatStream(message);
    }
}
相关推荐
计算机安禾1 小时前
【c++面向对象编程】第26篇:对象的内存模型:成员变量与成员函数的存储分离
开发语言·c++·算法
郝学胜-神的一滴2 小时前
Qt 高级开发 005: Qt Creator与Visual Studio 项目双向转换
开发语言·c++·ide·qt·程序人生·visual studio
解决问题no解决代码问题2 小时前
JAVA GC
java·开发语言·jvm
之歆2 小时前
DAY_10 JavaScript 深度解析:原型链 · 引用类型 · 内置对象 · 数组方法全攻略(下)
开发语言·前端·javascript·ecmascript
risc1234562 小时前
python 的字符串前缀
开发语言·python
小程故事多_802 小时前
Agent Loop 核心突破,上下文压缩四大流派,重新定义窗口资源利用率
java·开发语言·人工智能
如竟没有火炬2 小时前
字符串相乘——int数组转字符串
开发语言·数据结构·python·算法·leetcode·深度优先
吃好睡好便好2 小时前
在Matlab中绘制三维等高线图
开发语言·python·学习·算法·matlab·信息可视化
天若有情6732 小时前
自制C++万能字符串流式库 formort.h|对标标准库endl,零拷贝链式拼接神器
开发语言·c++