一、概述
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);
}
}