Loading

使用SpringAI框架对接Ollama本地模型、OpenAI模型

概述

本文档介绍如何在 Spring AI 框架中同时对接 Ollama 本地模型和 OpenAI 模型,实现双渠道向量数据管理。

Spring AI官方文档:https://www.spring-doc.cn/projects/spring-ai#overview

双渠道对接的优势

架构图

  1. 两种方式的向量数据互不干扰
  2. 可以同时使用两种方案进行对比测试
  3. 方便迁移和切换

项目依赖配置

POM 文件配置

<dependencyManagement>
    <dependencies>
        <!-- 导入 Spring AI 的 BOM -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- 使用 Spring AI 的依赖时,不需要写版本号 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
        <!-- 版本由 BOM 统一管理,无需指定 -->
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-pgvector-store</artifactId>
        <!-- 版本由 BOM 统一管理,无需指定 -->
    </dependency>
</dependencies>

YAML 配置文件

spring:
  ai:
    openai:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key:
      embedding-model: text-embedding-v1
      embedding:
        options:
          num-batch: 1536
    ollama:
      base-url:
      mode:
      embedding-model: nomic-embed-text
      embedding:
        options:
          num-batch: 768

Ollama 配置

package cn.bugstack.knowledge.config;

import org.springframework.ai.ollama.OllamaEmbeddingModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.pgvector.PgVectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class OllamaConfig {

    @Bean("ollamaSimpleVectorStore")
    public SimpleVectorStore vectorStore(OllamaApi ollamaApi) {
        OllamaEmbeddingModel embeddingModel = OllamaEmbeddingModel
                .builder()
                .ollamaApi(ollamaApi)
                .defaultOptions(OllamaOptions.builder().model("nomic-embed-text").build())
                .build();
        return SimpleVectorStore.builder(embeddingModel).build();
    }


    @Bean("ollamaPgVectorStore")
    public PgVectorStore pgVectorStore(OllamaApi ollamaApi, JdbcTemplate jdbcTemplate) {
        OllamaEmbeddingModel embeddingModel = OllamaEmbeddingModel
                .builder()
                .ollamaApi(ollamaApi)
                .defaultOptions(OllamaOptions.builder().model("nomic-embed-text").build())
                .build();
        return PgVectorStore.builder(jdbcTemplate, embeddingModel)
                .vectorTableName("vector_store_ollama_deepseek")
                .build();
    }

}

OpenAI 配置

package cn.bugstack.knowledge.config;

import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.pgvector.PgVectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class OpenAIConfig {

    @Value("${spring.ai.openai.base-url}")
    private String baseUrl;

    @Value("${spring.ai.openai.api-key}")
    private String apiKey;

    @Bean
    public TokenTextSplitter tokenTextSplitter() {
        return new TokenTextSplitter();
    }

    @Bean
    public OpenAiApi openAiApi() {
        return OpenAiApi.builder()
                .baseUrl(baseUrl)
                .apiKey(apiKey)
                .build();
    }

    /**
     * 创建基于内存的向量存储
     * 特点:
     * - 数据存储在应用内存中,重启后数据丢失
     * - 适合开发测试、小规模数据场景
     * - 无需外部数据库依赖,启动快速
     */
    @Bean("openAiSimpleVectorStore")
    public SimpleVectorStore vectorStore(OpenAiApi openAiApi) {
        // 使用 OpenAI API 创建嵌入模型,用于将文本转换为向量
        OpenAiEmbeddingModel embeddingModel = new OpenAiEmbeddingModel(openAiApi);
        // 构建简单的向量存储,向量数据保存在内存中
        return SimpleVectorStore.builder(embeddingModel).build();
    }

    /**
     * 创建基于 PostgreSQL 的向量存储
     * 特点:
     * - 数据持久化存储在 PostgreSQL 数据库中
     * - 支持 pgvector 扩展,提供高效的向量相似度搜索
     * - 适合生产环境、大规模数据场景
     * - 需要配置 PostgreSQL 数据库和 pgvector 扩展
     */
    @Bean("openAiPgVectorStore")
    public PgVectorStore pgVectorStore(OpenAiApi openAiApi, JdbcTemplate jdbcTemplate) {
        // 使用 OpenAI API 创建嵌入模型
        OpenAiEmbeddingModel embeddingModel = new OpenAiEmbeddingModel(openAiApi);
        // 构建 PostgreSQL 向量存储
        return PgVectorStore.builder(jdbcTemplate, embeddingModel)
                .vectorTableName("vector_store_openai")  // 指定数据库中存储向量的表名
                .build();
    }

}

向量数据库搭建

Docker 环境配置

数据库连接工具推荐使用 DBeaver 或 pgAdmin4。

# docker-compose-vector-db.yml
# 使用方法: docker-compose -f docker-compose-vector-db.yml up -d
version: '3'
services:
  # PostgreSQL + pgvector 向量数据库
  vector_db:
    image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/pgvector:v0.5.0
    container_name: vector_db
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=ai-rag-knowledge
      - PGPASSWORD=postgres
    volumes:
      # 初始化 SQL 脚本(首次启动时执行)
      - ./pgvector/sql/init.sql:/docker-entrypoint-initdb.d/init.sql
      # 数据持久化(重要!避免容器重启后数据丢失)
      - ./pgvector/data:/var/lib/postgresql/data
    logging:
      options:
        max-size: 10m
        max-file: "3"
    ports:
      - '15432:5432'
    healthcheck:
      test: "pg_isready -U postgres -d ai-rag-knowledge"
      interval: 2s
      timeout: 20s
      retries: 10
    networks:
      - vector-network

networks:
  vector-network:
    driver: bridge

数据库初始化脚本

CREATE EXTENSION IF NOT EXISTS vector;

-- 查询表;SELECT * FROM information_schema.tables

-- 删除旧的表(如果存在)
DROP TABLE IF EXISTS public.vector_store_openai;

-- 创建新的表,使用UUID作为主键
CREATE TABLE public.vector_store_openai (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    content TEXT NOT NULL,
    metadata JSONB,
    embedding VECTOR(1536)
);

-- 删除旧的表(如果存在)
DROP TABLE IF EXISTS public.vector_store_ollama_deepseek;

-- 创建新的表,使用UUID作为主键
CREATE TABLE public.vector_store_ollama_deepseek (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    content TEXT NOT NULL,
    metadata JSONB,
    embedding VECTOR(768)
);

模型服务对接

Ollama 模型部署

由于 Ollama 本地安装后请求较慢(受机器配置限制),推荐采用线上 GPU 服务器小时付费方案。

推荐平台:京东云 GPU 服务器

京东云GPU服务器

使用方法:通过控制台拉取向量模型

ollama pull nomic-embed-text

拉取向量模型

OpenAI 模型对接

推荐使用阿里云百炼平台(兼容 OpenAI 接口):

平台地址:阿里云百炼

阿里云百炼平台

注意事项:需注意向量的维度配置。

功能测试验证

OpenAI 接口测试

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class OpenAiTest {

    @Value("classpath:data/dog.png")
    private Resource imageResource;

    @Autowired
    private OpenAiChatModel openAiChatModel;


    @jakarta.annotation.Resource
    private OpenAiApi openAiApi;

    @Test
    public void test_call() {
        ChatResponse response = openAiChatModel.call(new Prompt(
                "1+1",
                OpenAiChatOptions.builder()
                        .model("qwen-turbo")
                        .build()));

        log.info("测试结果(call):{}", JSON.toJSONString(response));
    }

      @Test
    public void test_call_images() {
        // 构建请求信息
        UserMessage userMessage = new UserMessage("请描述这张图片的主要内容,并说明图中物品的可能用途。",
                new Media(MimeType.valueOf(MimeTypeUtils.IMAGE_PNG_VALUE),
                        imageResource));

        ChatResponse response = openAiChatModel.call(new Prompt(
                userMessage,
                OpenAiChatOptions.builder()
                        .model("qwen-vl-max")
                        .build()));

        log.info("测试结果(images):{}", JSON.toJSONString(response));
    }


}

OpenAI测试结果

Ollama 接口测试

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class OllamaTest {

    @Resource
    private OllamaChatModel ollamaChatModel;

    @Value("classpath:data/dog.png")
    private org.springframework.core.io.Resource imageResource;

    @jakarta.annotation.Resource(name = "ollamaSimpleVectorStore")
    private SimpleVectorStore simpleVectorStore;

    @jakarta.annotation.Resource(name = "ollamaPgVectorStore")
    private PgVectorStore pgVectorStore;

    @jakarta.annotation.Resource
    private TokenTextSplitter tokenTextSplitter;

    @Test
    public void test_model() {
        ChatOptions defaultOptions = ollamaChatModel.getDefaultOptions();
    }

    @Test
    public void test_call() {
        ChatResponse response = ollamaChatModel.call(new Prompt(
                "1+1",
                OllamaOptions.builder().model("deepseek-r1:1.5b").build()));

        log.info("测试结果(call):{}", JSON.toJSONString(response));
    }

    @Test
    public void test_call_images() {
        // 构建请求信息
        UserMessage userMessage = new UserMessage("请描述这张图片的主要内容,并说明图中物品的可能用途。",
                new Media(MimeType.valueOf(MimeTypeUtils.IMAGE_PNG_VALUE),
                        imageResource));

        ChatResponse response = ollamaChatModel.call(new Prompt(
                userMessage,
                OllamaOptions.builder()
                        .model("deepseek-r1:1.5b")
                        .build()));

        log.info("测试结果(images):{}", JSON.toJSONString(response));
    }

}

Ollama测试结果

posted @ 2026-04-22 21:52  炒焖煎糖板栗  阅读(98)  评论(0)    收藏  举报