Java直接調(diào)用本地大模型文件實(shí)現(xiàn)對(duì)話機(jī)器人
demo背景
作為java程序員,我們?nèi)绻苯诱{(diào)用springai框架作為深入學(xué)習(xí)大模型的技術(shù)的話,會(huì)發(fā)現(xiàn)在進(jìn)行文生圖或者文本對(duì)話的時(shí)候,要么依賴本地的ollama,要么是直接對(duì)接官方開放的api接口。哪怕再底層一些,也是寫方法調(diào)用python或者c語(yǔ)言,最終實(shí)現(xiàn)調(diào)用第三方功能來完成大模型的調(diào)用。但是這種方式并不是非常簡(jiǎn)便的方法。對(duì)于java開發(fā)者而言,直接通過springboot項(xiàng)目+大模型文件來實(shí)現(xiàn)一個(gè)大模型項(xiàng)目是最簡(jiǎn)單并且可控性最強(qiáng)的。那么這種方式能否實(shí)現(xiàn)呢?
答案是肯定的,我們這里介紹其中一種方法:Jlama
本文通過一個(gè)demo例子,來完成整個(gè)實(shí)驗(yàn)的驗(yàn)證。
最終的項(xiàng)目結(jié)構(gòu)如下:
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
第一步:準(zhǔn)備環(huán)境
JDK 版本:Jlama 需要 JDK 21 及以上的版本。
開發(fā)工具:一個(gè)趁手的 IDE(如 IntelliJ IDEA 或 VS Code)應(yīng)該已經(jīng)準(zhǔn)備好了。
第二步:構(gòu)建項(xiàng)目骨架
我們用一個(gè)最小化的 Spring Boot 項(xiàng)目就能開始,??pom.xml?? 是關(guān)鍵配置。
<?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 啟動(dòng)器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Thymeleaf 啟動(dòng)器,用于渲染 HTML 頁(yè)面 -->
<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 核心庫(kù) -->
<dependency>
<groupId>com.github.tjake</groupId>
<artifactId>jlama-core</artifactId>
<version>${jlama.version}</version>
</dependency>
<!-- Jlama 原生庫(kù),會(huì)自動(dòng)適應(yīng)操作系統(tǒng) -->
<dependency>
<groupId>com.github.tjake</groupId>
<artifactId>jlama-native</artifactId>
<version>${jlama.version}</version>
<classifier>${os.detected.classifier}</classifier>
</dependency>
<!-- LangChain4j 核心庫(kù) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- LangChain4j Jlama 集成庫(kù) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-jlama</artifactId>
<version>${langchain4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven 編譯器插件,配置 JVM 參數(shù) -->
<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,用于檢測(cè)操作系統(tǒng) -->
<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>第三步:編寫后端服務(wù)
1. 配置模型加載 (??JlamaConfig.java??)
我們需要一個(gè)專門的配置類來加載模型,確保它在應(yīng)用啟動(dòng)時(shí)是單例的,這樣能最大程度地節(jié)省內(nèi)存。
// 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 會(huì)嘗試從 Hugging Face 下載
// 模型路徑可以是本地文件夾路徑,也可以是 Hugging Face 的模型名稱
File localModelPath = new Downloader("./models", modelPath).huggingFaceModel();
// 使用 LangChain4j 提供的 JlamaChatModel 來構(gòu)建模型實(shí)例
// 這里傳入模型路徑、工作數(shù)據(jù)類型和量化數(shù)據(jù)類型
// AbstractModel model = ModelSupport.loadModel(localModelPath, DType.F32, DType.I8);
// 使用 JlamaChatModel 簡(jiǎn)化配置
ChatLanguageModel model = JlamaChatModel.builder()
.modelName(modelPath) // 使用 modelPath 作為模型名稱
.temperature(0.7f) // 設(shè)置溫度參數(shù),控制隨機(jī)性
.build();
logger.info("模型加載完成。");
return model;
}
}2. 定義 API 請(qǐng)求格式 (??ChatRequest.java??)
定義一個(gè)簡(jiǎn)單的 DTO(數(shù)據(jù)傳輸對(duì)象)來處理前端發(fā)來的請(qǐng)求。
// 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. 實(shí)現(xiàn)核心業(yè)務(wù)邏輯 (??JlamaService.java??)
在 Service 層,我們將注入剛剛配置好的 ??ChatLanguageModel??,并編寫一個(gè)方法來調(diào)用它。
// 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;
// 構(gòu)造器注入 ChatLanguageModel
public JlamaService(ChatLanguageModel model) {
this.model = model;
}
public String generateResponse(String prompt) {
logger.info("收到請(qǐng)求: {}", prompt);
// 調(diào)用模型生成響應(yīng)
String response = model.generate(prompt);
logger.info("生成完成");
return response;
}
}4. 創(chuàng)建 REST 控制器 (??ChatController.java??)
最后,創(chuàng)建一個(gè) Controller 來暴露 API,處理前端的請(qǐng)求。
// 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;
}
}第四步:創(chuàng)建 Web 交互界面
創(chuàng)建 HTML 頁(yè)面 (??index.html???):在 ??src/main/resources/static?? 目錄下創(chuàng)建 ??index.html?? 文件。
編寫前端代碼 (??index.html???):編輯 ??index.html??,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天界面。
<!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">發(fā)送</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('網(wǎng)絡(luò)響應(yīng)失敗');
}
const data = await response.json();
appendMessage(data.response, 'assistant');
} catch (error) {
console.error('Error:', error);
appendMessage('抱歉,發(fā)生了錯(cuò)誤。請(qǐng)檢查后端服務(wù)是否正常運(yùn)行。', 'assistant');
} finally {
loading.style.display = 'none';
}
}
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>第五步:運(yùn)行與測(cè)試
- 編譯與構(gòu)建:打開終端,進(jìn)入項(xiàng)目根目錄,執(zhí)行 ?
?mvn clean compile??,讓 Maven 來下載所有依賴。 - 啟動(dòng)應(yīng)用:在 IDE 中右鍵運(yùn)行 ?
?SpringBootJlamaDemoApplication.java?? 的 ??main?? 方法。 - 訪問界面:打開瀏覽器,訪問 ?
?http://localhost:8080??,就可以看到聊天界面了。
高級(jí)配置:使用本地模型
如果你想使用下載好的本地模型,只需修改 ??src/main/resources/application.yml?? 文件即可。
# src/main/resources/application.yml
jlama:
model:
# 使用本地路徑
path: /absolute/path/to/your/deepseek-4b-model常見問題與解決
Q1: 模型加載失敗怎么辦?
A: 如果模型需要從 Hugging Face 下載,你可以手動(dòng)下載 ??.safetensors?? 文件和 ??config.json??,然后將它們放在項(xiàng)目的 ??./models?? 文件夾下。修改 ??application.yml?? 中的 ??path?? 為本地的模型文件夾路徑即可。
Q2: 為什么我的想法與代碼不一致?
A: 正如你提到的,本地加載其實(shí)指向的是 Hugging Face 模型名,這背后是 Jlama 自動(dòng)處理了下載與緩存。我們可以優(yōu)化 ??JlamaConfig.java??,讓模型真正“離線”。例如,使用 ??ModelSupport.loadModel?? 方式,或自定義模型加載路徑。
以上就是Java直接調(diào)用本地大模型文件實(shí)現(xiàn)對(duì)話機(jī)器人的詳細(xì)內(nèi)容,更多關(guān)于Java調(diào)用本地大模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Java使用WebFlux調(diào)用大模型實(shí)現(xiàn)智能對(duì)話
- 在Java中創(chuàng)建對(duì)話框的最佳實(shí)戰(zhàn)指南
- Java調(diào)用DeepSeek實(shí)現(xiàn)多輪對(duì)話功能
- JAVA調(diào)用Deepseek的api完成基本對(duì)話簡(jiǎn)單代碼示例
- Java使用百度AI接口實(shí)現(xiàn)智能機(jī)器人對(duì)話系統(tǒng)
- Java實(shí)現(xiàn)模擬機(jī)器人對(duì)話的示例代碼
- Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式
相關(guān)文章
使用System.exit()來優(yōu)雅地終止SpringBoot項(xiàng)目的代碼示例
System.exit() 方法是 Java 中用于退出程序的方法,它接受一個(gè)整數(shù)參數(shù),通常被用來指示程序的退出狀態(tài),本文給大家介紹了如何使用System.exit()來優(yōu)雅地終止SpringBoot項(xiàng)目,需要的朋友可以參考下2024-08-08
Java使用備忘錄模式實(shí)現(xiàn)過關(guān)類游戲功能詳解
這篇文章主要介紹了Java使用備忘錄模式實(shí)現(xiàn)過關(guān)類游戲功能,結(jié)合實(shí)例形式詳細(xì)分析了java備忘錄模式的概念、原理及其在過關(guān)類游戲中的具體應(yīng)用方法,需要的朋友可以參考下2018-04-04
SpringBoot實(shí)現(xiàn)前端驗(yàn)證碼圖片生成和校驗(yàn)
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)前端驗(yàn)證碼圖片生成和校驗(yàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Spring中Bean的創(chuàng)建流程詳細(xì)解讀
這篇文章主要介紹了Spring中Bean的創(chuàng)建流程詳細(xì)解讀,Spring 中創(chuàng)建 Bean ,是通過調(diào)用 GetBean 方法來觸發(fā)的,所以,我們會(huì)從這個(gè)方法開始,需要的朋友可以參考下2023-10-10
SpringBoot返回前端Long型丟失精度后兩位變成00的解決
在后端開發(fā)中,當(dāng)Long類型的ID超過19位時(shí),前端JavaScript可能會(huì)出現(xiàn)精度問題,導(dǎo)致最后兩位變成00,本文提出了三種解決方案:將ID轉(zhuǎn)換為字符串、使用@JsonSerialize注解和使用@JsonFormat注解,通過這些方法,可以確保ID在前后端傳輸過程中不會(huì)發(fā)生精度丟失2026-01-01
java時(shí)間 java.util.Calendar深入分析
這篇文章主要介紹了java時(shí)間 java.util.Calendar深入分析的相關(guān)資料,需要的朋友可以參考下2017-02-02
SpringBoot結(jié)合dev-tool實(shí)現(xiàn)IDEA項(xiàng)目熱部署的流程步驟
這篇文章主要給大家介紹了SpringBoot結(jié)合dev-tool實(shí)現(xiàn)IDEA項(xiàng)目熱部署的流程步驟,文章通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)有一定的幫助,需要的朋友可以參考下2023-10-10

