使用 Ollama 本地模型与 Spring AI Alibaba

Spring AI RAG 体验项目中使用的是阿里云的模型,嵌入、推理和排序要调用阿里云的服务。本例尝试本地搭建ollama,跑一些开源的模型。

ollama号称大模型领域的docker。用来做验证或者跑一些经过蒸馏/量化的轻量模型、搭建问答机器人知识库,够用了。

整体步骤是:(1)安装ollama,相当于安装docker (2)ollama中拉取模型,相当于docker拉取镜像进行并运行 (3)验证ollama中的模型(4)写一个spring ai应用,该应用调用ollama中跑的模型。

本例代码 github.com/zhouruibest...

1. 安装ollama

ollama被设计用来单机运行的。可以用到Window上的显卡。

2. 拉取模型

windows powershell中

sh 复制代码
ollama pull deepseek-r1:8b
ollama pull nomic-embed-text:latest

3. 验证

ollama默认监听本地11434端口,spring ai应用就是访问这个地址。用curl测一下:

sh 复制代码
curl http://localhost:11434/api/generate -d '{
  "model": "deepseek-r1:8b",
  "prompt": "请介绍一下AI的发展历史"
}'


curl http://localhost:11434/api/embed -d '{
  "model": "nomic-embed-text:latest",
  "input": "这是一段需要转换为向量的文本"
}'

命令行中显示不友好。也可以用Open-WebUI(web 页面工具,镜像是 ghcr.io/open-webui/open-webui:main) ,在页面验证。 这里用了它的国内的镜像。推荐一个镜像同步网站 docker.aityp.com/。

bash 复制代码
docker run -d -p 3000:8080 \
  --add-host=host.docker.internal:host-gateway \
  -e OLLAMA_BASE_URL=http://host.docker.internal:11434 \  # 指定了ollama的服务地址
  swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/open-webui/open-webui:main

4. spring ai 应用

本例是在 Spring AI RAG 体验项目 的基础上改的。

配置

yaml 复制代码
spring:
  application:
    name: handbook
  autoconfigure:
    exclude:
      - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration
  ai:
    dashscope:
      api-key: ${MY_APP_API_KEY}        # 项目中还有 spring-ai-alibaba-starter启动器,方便切回去
    ollama:
      base-url: http://localhost:11434  # Ollama 服务地址,默认是这个端口号
      chat:
        model: deepseek-r1:8b           # 指定要使用的推理模型,这个3060Ti还能跑起来
      embedding:
        model: nomic-embed-text:latest  # 文本嵌入模型,维度768

initKnowledge: false # 是否初始化到向量数据库。只运行回次就行
milvus:
  host: mydevcvm
  port: 19530
  token: "root:Milvus"
  database: "default" # 使用默认的
  collection: "ollamarag1"
  initializeSchema: false # milvus中的database要先创建好。collection可以不用创建;这个字段第一次为true,以后运行为false

向量数据库

nomic-embed-text:latest 的向量维度是768而非 1536

为了方便改回去,项目还引入了 spring-ai-alibaba-starter启动器 和相关的配置。因此会有两个Chat Model和Embedding Model,所以要用 @Qualifier("ollamaEmbeddingModel")@Qualifier("ollamaChatModel")指定一下。

java 复制代码
// VectorStoreConfig.java
@Bean
public VectorStore vectorStore(MilvusServiceClient milvusClient, @Qualifier("ollamaEmbeddingModel") EmbeddingModel embeddingModel) {
    return MilvusVectorStore.builder(milvusClient, embeddingModel) //  嵌入模型实例,用于将文本转换为向量表示
            .collectionName(collection).databaseName(database).embeddingDimension(768) 
            .indexType(IndexType.IVF_FLAT) // 设置为IVF_FLAT(倒排文件Flat索引),是一种常用的近似最近邻搜索索引
            .metricType(MetricType.COSINE).batchingStrategy(new TokenCountBatchingStrategy()).initializeSchema(initializeSchema).build();
    }

嵌入并存储向量数据库

java 复制代码
// KnowledgeInitializer.java
@Component
public class KnowledgeInitializer implements ApplicationRunner {
    @Value("${initKnowledge}")
    private Boolean initKnowledge;

    @Value("classpath:/docs/handbook.pdf")
    private Resource springAiResource;

    @Autowired
    private VectorStore vectorStore;

    private final Logger logger = LoggerFactory.getLogger(KnowledgeInitializer.class);

    public void run(ApplicationArguments args) throws Exception {
        if (!initKnowledge) {
            logger.info("initKnowledge is false, skip init");
            return;
        }

        // 1. parse document
        DocumentReader reader = new PagePdfDocumentReader(springAiResource);
        List<Document> documents = reader.get();
        logger.info("{} documents loaded", documents.size());

        // 2. split trunks
        List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
        logger.info("{} documents split", splitDocuments.size());

        // 3. create embedding and store to vector store
        logger.info("create embedding and save to vector store");
        vectorStore.add(splitDocuments);

    }
}

rag service

java 复制代码
// AIRagService
public class AIRagService {
    // 引入 system prompt tmpl
    @Value("classpath:/docs/system-qa.st")
    private Resource systemResource;

    // 注入相关 bean 实例
    @Qualifier("ollamaChatModel")
    @Autowired
    private ChatModel ragChatModel;

    @Autowired
    private VectorStore vectorStore;

    private String getPromptTemplate(Resource systemResource) {
        try {
            return systemResource.getContentAsString(StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 文本过滤,增强向量检索精度    private static final String textField = "content";
    // ......
    public Flux<String> retrieve(String prompt) {
        // 加载 prompt tmpl
        String promptTemplate = getPromptTemplate(systemResource);
        // 启用混合搜索,包括嵌入和全文搜索
        SearchRequest searchRequest = SearchRequest.builder().topK(4).similarityThresholdAll().build();
        // build chatClient,发起大模型服务调用。
        return ChatClient.builder(ragChatModel).build().prompt().advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest, promptTemplate)).user(prompt).stream().content();
    }
}

访问验证

浏览器地址栏输入:http://localhost:8080/rag/?message=现在我们身边一些老年人感染后病情就很重,那是不是老年人感染后一定会走向重症和危重症

markdown 复制代码
这是一个非常关心的问题,但答案**并不简单,也不是绝对的"是"或"否"**。

老年人感染新冠病毒后,**发展为重症和危重症的风险确实比年轻健康人群要高得多**,但这**并不意味着所有**老年人感染后都会必然走向重症和危重症。感染后的严重程度受到多种因素的影响,主要包括:

1.  **年龄**:年龄越大,风险通常越高。高龄老人(尤其是80岁以上)风险最高。
2.  **基础健康状况**:**合并症**是最重要的风险因素。患有**心血管疾病、糖尿病、慢性肺病、免疫缺陷、癌症、慢性肾病、肥胖**等基础疾病,以及**免疫功能低下**的老年人,感染后发展为重症的风险显著增加。
3.  **疫苗接种情况**:完成疫苗(包括加强针)接种,特别是有效疫苗,可以显著降低重症和死亡风险。
4.  **感染的病毒毒株和变种**:不同毒株的致病力和传播性不同,感染不同毒株的风险和严重程度也会有所差异。
5.  **感染时的身体状况和免疫反应**:个体对病毒的反应不同,有些老人即使有基础病,但感染时状态较好,免疫系统反应适当,也可能相对较轻。
6.  **医疗支持**:及时的医疗干预、氧疗、抗病毒药物(如Paxlovid)和有效的支持治疗可以显著改善预后,避免走向危重症。

**总结来说:**

*   老年人是**感染后发展为重症的高危人群**,这是毋庸置疑的。
*   **但是,个体差异很大**。并非所有老年人都会经历同样的严重程度。很多老年人在感染后可能只是出现类似感冒或流感的症状。
*   **关键在于预防和早期干预**。通过接种疫苗、做好防护、管理基础疾病、以及在出现症状时及时就医,可以大大降低老年人走向重症和危重症的风险。

因此,与其说"老年人感染后一定会走向重症",不如说"**老年人感染新冠病毒后,有很高的风险发展为重症,特别是那些有基础疾病和免疫功能低下的老人**"。我们需要对老年人群给予特别的关注和保护。
相关推荐
程序员小假2 小时前
SQL 语句左连接右连接内连接如何使用,区别是什么?
java·后端
小坏讲微服务2 小时前
Spring Cloud Alibaba Gateway 集成 Redis 限流的完整配置
数据库·redis·分布式·后端·spring cloud·架构·gateway
方圆想当图灵3 小时前
Nacos 源码深度畅游:Nacos 配置同步详解(下)
分布式·后端·github
方圆想当图灵3 小时前
Nacos 源码深度畅游:Nacos 配置同步详解(上)
分布式·后端·github
小羊失眠啦.3 小时前
用 Rust 实现高性能并发下载器:从原理到实战
开发语言·后端·rust
Filotimo_4 小时前
SpringBoot3入门
java·spring boot·后端
一 乐5 小时前
校园墙|校园社区|基于Java+vue的校园墙小程序系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·小程序
golang学习记5 小时前
🍵 Go Queryx 入门指南:让数据库操作像喝奶茶一样丝滑!
后端
z_鑫5 小时前
Java线程池原理深度解析
java·开发语言·后端