Spring AI与大模型Ollama如何集成整合?

Python 是进入人工智能领域(认为是机器学习或生成人工智能AIGC)的主要武器,Java 和 Spring 仍引领着大多数开发社区, Java 开发人员如何与 LLM大模型 一起工作?这就是 Spring AI 弥补差距的地方。

Spring AI在 Spring 应用程序和可以在本地运行的 LLM 之间创建集成,这为所有 Java 开发人员提供了开发人工智能应用程序的能力。

在本文中,我们将了解如何使用 Ollama 在本地运行大模型,我们将考虑四个方面。

  1. 本地安装并运行 Ollama 和 LLM
  2. 创建SpringAI项目并详细查看项目配置。
  3. 访问本地嵌入。
  4. 访问本地聊天模型。

安装并运行 Ollama
导航至https://ollama.com/download下载适用于特定操作系统的 ollama。

下载 ollama 后,打开终端并输入以下命令以提取 LLM 并在本地运行它们。
ollama run llama2 # pulls llama2 LLM model
ollama run mistral # pulls and runs mistral LLM

创建 Spring AI 项目
如果你使用任何集成开发环境(我使用的是 IntelliJ)或基于 Web 的 Spring Intializr,那么你可以看到现在可以直接从 Spring Intializr 添加 AI 依赖项。在本例中,我们将添加 Ollama。在执行此步骤之前,请按照步骤 1 确保已安装 Ollama 并在本地运行。

让我们深入研究一下 application.yaml,看看与 ollama 有关的不同项目配置。

spring:
  ai:
    ollama:
      embedding:
        model: "llama2"
        options:
          temperature: 0.5
          top-p: 0.2
          top-k: 2
          num-g-p-u: 1 enable metal gpu on MAC
      base-url:
"http://localhost:11434"


有关嵌入和聊天模型的更多配置可分别在这里 here 和这里here 找到。请注意一点,Spring 默认配置可与 Mistral LLM 配合使用,但我们已明确用 llama2 替代。

实体记录
首先,我们将创建两个记录:Payload 和 Response,名称更加直观。

package org.vaslabs.springai2.records;

public record Payload(String strings) {
}
package org.vaslabs.springai2.records;

import java.util.List;

public record Response(List<Double> data) {
}

服务类
现在让我们创建一个服务类,它将帮助我们为给定的输入字符串生成嵌入结果。

import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.embedding.EmbeddingRequest;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.stereotype.Service;
import org.vaslabs.springai2.records.Payload;

import java.util.List;
import java.util.Map;

@Service
public class OllamaEmbeddingsGenerator {
    private final EmbeddingClient embeddingClient;

    public OllamaEmbeddingsGenerator(EmbeddingClient embeddingClient) {
        this.embeddingClient = embeddingClient;
    }

    public EmbeddingResponse getEmbeddingsWithModelOverride(Payload payload) {
        return this.embeddingClient.call(new EmbeddingRequest(
                List.of(payload.strings()),
                OllamaOptions.create().withModel("mistral")));
    }

    public Map<String, EmbeddingResponse> getEmbeddingsWithDefaultModel(Payload payload) {
        EmbeddingResponse embeddingResponse = this.embeddingClient.embedForResponse(
                List.of(payload.strings()));
        return Map.of(
"embedding", embeddingResponse);
    }
}

上述 OllamaEmbeddingsGenerator 类定义在 Spring 应用程序的 org.vaslabs.springai2.service 包中,是生成文本嵌入的服务组件。它利用 Spring AI 框架的嵌入式客户端(EmbeddingClient)将文本数据转换为嵌入式,即在自然语言处理任务中非常有用的向量表示。

该服务配备了两个主要方法:getEmbeddingsWithModelOverride 和 getEmbeddingsWithDefaultModel。

  • 前一种方法允许指定用于嵌入生成的模型(例如 "mistral"),从而可以根据请求自定义行为。
  • 后一种方法则使用客户端的默认模型设置生成嵌入。这两种方法的操作方式都是获取一个 Payload 对象(该对象封装了要嵌入的字符串),并以不同的格式返回嵌入结果--在 getEmbeddingsWithModelOverride 的情况下直接返回 EmbeddingResponse,而在 getEmbeddingsWithDefaultModel 的情况下返回一个包含 EmbeddingResponse 的映射。

这种设计封装了与嵌入式生成技术交互的复杂性,并为应用程序中的各种嵌入式生成需求提供了灵活的应用程序接口。

让我们再了解一项有助于创建聊天一代的服务。

import org.springframework.ai.ollama.OllamaChatClient;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class OllamaChatGenerator {

    private final OllamaChatClient ollamaChatClient;

    public OllamaChatGenerator(OllamaChatClient ollamaChatClient) {
        this.ollamaChatClient = ollamaChatClient;
    }

    public Map generate(String message) {
        return Map.of("generation", this.ollamaChatClient.call(message));
    }
}

  • 上述 Java 类 OllamaChatGenerator 定义在 org.vaslabs.springai2.service 包中,设计为 Spring 服务,通过利用 Spring AI 框架中的 OllamaChatClient 来促进聊天交互。
  • 该服务使用 @Service 注解进行 Spring 组件扫描,封装了一种简单而有效的方法来生成聊天响应。
  • 通过构造函数实例化后,它会接收一个 OllamaChatClient 对象,从而建立起与底层聊天模型或服务通信的依赖关系。
  • 该服务的主要功能通过 generate 方法公开,该方法接受字符串消息作为输入,并返回一个包含以 "generation "为关键字的聊天生成结果的映射。

这种设置抽象化了与人工智能驱动的聊天功能进行交互的复杂性,为 Spring 应用程序的其他组件提供了一种直接的方法,可根据用户输入生成动态的对话式响应。

控制器
现在是为上述两个服务编写控制器的时候了,它们可以分别处理终端用户的请求。这些控制器将帮助我们选择用户所需的功能。

import org.springframework.ai.embedding.Embedding;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.vaslabs.springai2.records.Payload;
import org.vaslabs.springai2.records.Response;
import org.vaslabs.springai2.service.OllamaEmbeddingsGenerator;

import java.util.Map;

@RestController
@RequestMapping("/api/v1")
public class OllamaEmbeddingController {

    private final OllamaEmbeddingsGenerator ollamaEmbeddingsGenerator;

    public OllamaEmbeddingController(OllamaEmbeddingsGenerator ollamaEmbeddingsGenerator) {
        this.ollamaEmbeddingsGenerator = ollamaEmbeddingsGenerator;
    }
    @PostMapping(value =
"/embedding1", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Response> fetchEmbeddings1(@RequestBody Payload payload){
        Embedding embedding = this.ollamaEmbeddingsGenerator.getEmbeddingsWithModelOverride(payload).getResult();
        return ResponseEntity.ok(new Response(embedding.getOutput()));
    }

    @PostMapping(value =
"/embedding2", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, EmbeddingResponse>> fetchEmbeddings2(@RequestBody Payload payload){
        Map<String, EmbeddingResponse> embedding = this.ollamaEmbeddingsGenerator.getEmbeddingsWithDefaultModel(payload);
        return ResponseEntity.ok(embedding);
    }
}

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.vaslabs.springai2.records.Payload;
import org.vaslabs.springai2.service.OllamaChatGenerator;

import java.util.Map;

@RestController
@RequestMapping("/api/v1")
public class OllamaChatController {

    private final OllamaChatGenerator ollamaChatGenerator;

    public OllamaChatController(OllamaChatGenerator ollamaChatGenerator) {
        this.ollamaChatGenerator = ollamaChatGenerator;
    }

    @PostMapping(value =
"/chat", produces = MediaType.APPLICATION_JSON_VALUE)
    public Map chat(@RequestBody Payload payload){
        return this.ollamaChatGenerator.generate(payload.strings());
    }
}

如果上述代码和配置都没有问题,那么我们就可以运行该项目了(需要注意的是,请确保在本地运行 ollama)。

查看 OllamaEmbeddingController 的路由 /embedding1 和 /embedding2 非常有趣,第一个路由通过在服务中以编程方式覆盖模型的配置来使用 Mistral 模型,而后一个路由则使用 application.yaml 中的默认配置。

结论
总之,将 Ollama 与 Spring AI 结合使用,是将先进的大型语言模型 (LLM) 直接集成到应用程序中的令人信服的解决方案,可提供嵌入和文本生成功能。借助 Ollama 在本地运行 LLM 的能力,开发人员可以对人工智能驱动的功能获得前所未有的控制力和灵活性,确保数据隐私并减少延迟。

Spring AI 通过 OllamaEmbeddingClient 和 OllamaChatClient 对 Ollama 的支持简化了集成过程,在 Spring 生态系统中实现了无缝嵌入生成和对话式人工智能功能。这种组合不仅使最先进的人工智能技术的使用更加平民化,而且还增强了应用程序的可扩展性和响应能力,使其成为希望集成智能、上下文感知交互和数据处理功能的开发人员的理想选择。