Java直接调用本地大模型文件实现对话机器人
demo背景
作为java程序员,我们如果直接调用springai框架作为深入学习大模型的技术的话,会发现在进行文生图或者文本对话的时候,要么依赖本地的ollama,要么是直接对接官方开放的api接口。哪怕再底层一些,也是写方法调用python或者c语言,最终实现调用第三方功能来完成大模型的调用。但是这种方式并不是非常简便的方法。对于java开发者而言,直接通过springboot项目+大模型文件来实现一个大模型项目是最简单并且可控性最强的。那么这种方式能否实现呢?
答案是肯定的,我们这里介绍其中一种方法:Jlama
本文通过一个demo例子,来完成整个实验的验证。
最终的项目结构如下:
springboot-jlama-demo/
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── jlama
│ │ │ ├── SpringBootJlamaDemoApplication.java
│ │ │ ├── config
│ │ │ │ └── JlamaConfig.java
│ │ │ ├── controller
│ │ │ │ └── ChatController.java
│ │ │ ├── dto
│ │ │ │ └── ChatRequest.java
│ │ │ └── service
│ │ │ └── JlamaService.java
│ │ └── resources
│ │ ├── application.yml
│ │ └── static
│ │ └── index.html
│ └── test
│ └── java
│ └── com
│ └── example
│ └── jlama
│ └── SpringBootJlamaDemoApplicationTests.java
第一步:准备环境
JDK 版本:Jlama 需要 JDK 21 及以上的版本。
开发工具:一个趁手的 IDE(如 IntelliJ IDEA 或 VS Code)应该已经准备好了。
第二步:构建项目骨架
我们用一个最小化的 Spring Boot 项目就能开始,pom.xml 是关键配置。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-jlama-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-jlama-demo</name>
<description>Demo project for Spring Boot + Jlama with DeepSeek 4B</description>
<properties>
<java.version>21</java.version>
<jlama.version>0.8.4</jlama.version>
<langchain4j.version>1.0.0-beta1</langchain4j.version>
</properties>
<dependencies>
<!-- Spring Boot Web 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Thymeleaf 启动器,用于渲染 HTML 页面 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Boot DevTools,用于热重载 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Jlama 核心库 -->
<dependency>
<groupId>com.github.tjake</groupId>
<artifactId>jlama-core</artifactId>
<version>${jlama.version}</version>
</dependency>
<!-- Jlama 原生库,会自动适应操作系统 -->
<dependency>
<groupId>com.github.tjake</groupId>
<artifactId>jlama-native</artifactId>
<version>${jlama.version}</version>
<classifier>${os.detected.classifier}</classifier>
</dependency>
<!-- LangChain4j 核心库 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- LangChain4j Jlama 集成库 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-jlama</artifactId>
<version>${langchain4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven 编译器插件,配置 JVM 参数 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
<compilerArgs>
<arg>--add-modules=jdk.incubator.vector</arg>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</plugin>
<!-- Spring Boot Maven 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- os-maven-plugin,用于检测操作系统 -->
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>detect</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>第三步:编写后端服务
1. 配置模型加载 (JlamaConfig.java)
我们需要一个专门的配置类来加载模型,确保它在应用启动时是单例的,这样能最大程度地节省内存。
// src/main/java/com/example/jlama/config/JlamaConfig.java
package com.example.jlama.config;
import com.github.tjake.jlama.model.AbstractModel;
import com.github.tjake.jlama.model.ModelSupport;
import com.github.tjake.jlama.safetensors.DType;
import com.github.tjake.jlama.util.Downloader;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.jlama.JlamaChatModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
import java.io.IOException;
@Configuration
public class JlamaConfig {
private static final Logger logger = LoggerFactory.getLogger(JlamaConfig.class);
// 从 application.yml 读取模型路径和名称
@Value("${jlama.model.path}")
private String modelPath;
@Bean
public ChatLanguageModel chatLanguageModel() throws IOException {
logger.info("正在加载模型: {}", modelPath);
// 如果本地模型不存在,Jlama 会尝试从 Hugging Face 下载
// 模型路径可以是本地文件夹路径,也可以是 Hugging Face 的模型名称
File localModelPath = new Downloader("./models", modelPath).huggingFaceModel();
// 使用 LangChain4j 提供的 JlamaChatModel 来构建模型实例
// 这里传入模型路径、工作数据类型和量化数据类型
// AbstractModel model = ModelSupport.loadModel(localModelPath, DType.F32, DType.I8);
// 使用 JlamaChatModel 简化配置
ChatLanguageModel model = JlamaChatModel.builder()
.modelName(modelPath) // 使用 modelPath 作为模型名称
.temperature(0.7f) // 设置温度参数,控制随机性
.build();
logger.info("模型加载完成。");
return model;
}
}2. 定义 API 请求格式 (ChatRequest.java)
定义一个简单的 DTO(数据传输对象)来处理前端发来的请求。
// src/main/java/com/example/jlama/dto/ChatRequest.java
package com.example.jlama.dto;
public class ChatRequest {
private String prompt;
// Getters and Setters
public String getPrompt() {
return prompt;
}
public void setPrompt(String prompt) {
this.prompt = prompt;
}
}3. 实现核心业务逻辑 (JlamaService.java)
在 Service 层,我们将注入刚刚配置好的 ChatLanguageModel,并编写一个方法来调用它。
// src/main/java/com/example/jlama/service/JlamaService.java
package com.example.jlama.service;
import dev.langchain4j.model.chat.ChatLanguageModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class JlamaService {
private static final Logger logger = LoggerFactory.getLogger(JlamaService.class);
private final ChatLanguageModel model;
// 构造器注入 ChatLanguageModel
public JlamaService(ChatLanguageModel model) {
this.model = model;
}
public String generateResponse(String prompt) {
logger.info("收到请求: {}", prompt);
// 调用模型生成响应
String response = model.generate(prompt);
logger.info("生成完成");
return response;
}
}4. 创建 REST 控制器 (ChatController.java)
最后,创建一个 Controller 来暴露 API,处理前端的请求。
// src/main/java/com/example/jlama/controller/ChatController.java
package com.example.jlama.controller;
import com.example.jlama.dto.ChatRequest;
import com.example.jlama.service.JlamaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.HashMap;
@RestController
@RequestMapping("/api")
public class ChatController {
@Autowired
private JlamaService jlamaService;
@PostMapping("/chat")
public Map<String, String> chat(@RequestBody ChatRequest request) {
String response = jlamaService.generateResponse(request.getPrompt());
Map<String, String> result = new HashMap<>();
result.put("response", response);
return result;
}
}第四步:创建 Web 交互界面
创建 HTML 页面 (index.html):在 src/main/resources/static 目录下创建 index.html 文件。
编写前端代码 (index.html):编辑 index.html,实现一个简单的聊天界面。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>DeepSeek 本地助手</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: #1a1a2e;
color: white;
padding: 20px;
text-align: center;
}
.chat-box {
height: 400px;
overflow-y: auto;
padding: 20px;
background: #f9f9f9;
}
.message {
margin-bottom: 15px;
padding: 10px 15px;
border-radius: 10px;
max-width: 80%;
word-wrap: break-word;
}
.user {
background: #007bff;
color: white;
margin-left: auto;
text-align: right;
}
.assistant {
background: #e9ecef;
color: #333;
margin-right: auto;
}
.input-area {
display: flex;
padding: 20px;
background: white;
border-top: 1px solid #ddd;
}
.input-area input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
.input-area button {
margin-left: 10px;
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.input-area button:hover {
background: #0056b3;
}
.loading {
text-align: center;
color: #666;
padding: 10px;
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🤖 DeepSeek 本地助手</h1>
<p>基于 Jlama 和 DeepSeek 4B 模型</p>
</div>
<div id="chat-box" class="chat-box">
<div class="message assistant">
你好!我是 DeepSeek 本地助手,有什么我可以帮你的吗?
</div>
</div>
<div id="loading" class="loading">思考中...</div>
<div class="input-area">
<input type="text" id="user-input" placeholder="输入你的问题..." />
<button id="send-btn">发送</button>
</div>
</div>
<script>
const chatBox = document.getElementById('chat-box');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const loading = document.getElementById('loading');
function appendMessage(text, sender) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
messageDiv.textContent = text;
chatBox.appendChild(messageDiv);
chatBox.scrollTop = chatBox.scrollHeight;
}
async function sendMessage() {
const prompt = userInput.value.trim();
if (!prompt) return;
appendMessage(prompt, 'user');
userInput.value = '';
loading.style.display = 'block';
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt: prompt }),
});
if (!response.ok) {
throw new Error('网络响应失败');
}
const data = await response.json();
appendMessage(data.response, 'assistant');
} catch (error) {
console.error('Error:', error);
appendMessage('抱歉,发生了错误。请检查后端服务是否正常运行。', 'assistant');
} finally {
loading.style.display = 'none';
}
}
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>第五步:运行与测试
- 编译与构建:打开终端,进入项目根目录,执行
mvn clean compile,让 Maven 来下载所有依赖。 - 启动应用:在 IDE 中右键运行
SpringBootJlamaDemoApplication.java 的 main 方法。 - 访问界面:打开浏览器,访问
http://localhost:8080,就可以看到聊天界面了。
高级配置:使用本地模型
如果你想使用下载好的本地模型,只需修改 src/main/resources/application.yml 文件即可。
# src/main/resources/application.yml
jlama:
model:
# 使用本地路径
path: /absolute/path/to/your/deepseek-4b-model常见问题与解决
Q1: 模型加载失败怎么办?
A: 如果模型需要从 Hugging Face 下载,你可以手动下载 .safetensors 文件和 config.json,然后将它们放在项目的 ./models 文件夹下。修改 application.yml 中的 path 为本地的模型文件夹路径即可。
Q2: 为什么我的想法与代码不一致?
A: 正如你提到的,本地加载其实指向的是 Hugging Face 模型名,这背后是 Jlama 自动处理了下载与缓存。我们可以优化 JlamaConfig.java,让模型真正“离线”。例如,使用 ModelSupport.loadModel 方式,或自定义模型加载路径。
以上就是Java直接调用本地大模型文件实现对话机器人的详细内容,更多关于Java调用本地大模型的资料请关注脚本之家其它相关文章!
相关文章
解读Eureka的TimedSupervisorTask类(自动调节间隔的周期性任务)
在Eureka客户端中,尽管ScheduledExecutorService的schedule方法创建的是一次性任务,但通过在任务执行完毕后再次调用schedule方法实现了周期性执行,这种设计既考虑到了任务超时导致的间隔时间调整,又通过CAS实现了多线程同步,展现了简洁而巧妙的设计思想2024-11-11
IDEA项目使用SpringBoot+MyBatis-Plus的方法
这篇文章主要介绍了IDEA项目使用SpringBoot+MyBatis-Plus的方法,本文分步骤通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-10-10


最新评论