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 交互界面
  1. 创建 HTML 页面 ( index.html​) :在 ​src/main/resources/static​ 目录下创建 ​index.html​ 文件。

  2. 编写前端代码 ( 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>

    🤖 DeepSeek 本地助手

    基于 Jlama 和 DeepSeek 4B 模型

    你好!我是 DeepSeek 本地助手,有什么我可以帮你的吗?
    思考中...
    <button id="send-btn">发送</button>
    复制代码
     <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>

▶️ 第五步:运行与测试
  1. 编译与构建 :打开终端,进入项目根目录,执行 ​mvn clean compile​,让 Maven 来下载所有依赖。
  2. 启动应用 :在 IDE 中右键运行 ​SpringBootJlamaDemoApplication.java​​main​ 方法。
  3. 访问界面 :打开浏览器,访问 ​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​ 方式,或自定义模型加载路径。

好了,项目到这里就搭建完毕了。如果在运行中遇到任何问题,随时告诉我,我们一起解决~

相关推荐
笨蛋不要掉眼泪4 小时前
Java并发编程:深入理解ThreadLocal
java·开发语言·jvm·并发
番茄去哪了4 小时前
JVM虚拟机(中)
java·开发语言·jvm
SimonKing4 小时前
从惊艳到踩坑:AI结对编程的真实复盘
java·后端·程序员
海兰4 小时前
【第56篇】Graph Example —— MCP-Node 模块
java·人工智能·spring boot·spring ai
程序猿乐锅4 小时前
【Tilas|第十篇】万字讲解SpringAOP知识点
java·开发语言·idea·tlias
zhougl9964 小时前
Maven build配置 补
java·maven
Seven974 小时前
dubbo服务调用源码
java
长谷深风1114 小时前
多线程并发实战:从原理到应用【个人八股】
java·并发编程·线程安全·java多线程·synchronized·锁升级