Spring AI+Ollama+Milvus构建RAG应用

45 阅读3分钟

1.下载Ollama
下载地址(Download Ollama on Windows) 选择对应的统系统的版本。
2.进入Ollama官网,选择合适的模型Ollama Models(我这里以deepseek-r1:8b举例)

image.png 3.在cmd里面运行ollama run deepseek-r1:8b。会自动拉取模型。拉取成功后,可以输入“你好!”进行测试(deepseek-r1默认启用思考模式,后续可根据需要禁用)。 image.png 这样就拉取成功了。
4.下载Milvus,这里以docker compose举例。

wget https://github.com/milvus-io/milvus/releases/download/v2.6.6/milvus-standalone-docker-compose.yml -O docker-compose.yml 下载好后,进入到milvus- standalone下执行docker compose up -d。这样就启动起来了。
如何需要使用管控台的话,接着执行docker run -p 8000:3000 -e MILVUS_URL=localhost:19530 -d zilliz/attu:latest。成功之后,就能在浏览器进行输入 localhost:8000 访问管控台了。
5.配置依赖项

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.1.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
       
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <!--spring ai 整合ollama相关依赖--> 
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-ollama</artifactId>
        </dependency>
        
       <!--spring ai 整合milvus相关依赖-->
        <dependency>
           <groupId>org.springframework.ai</groupId>
           <artifactId>spring-ai-starter-vector-store-milvus</artifactId>
        </dependency>

    </dependencies>

6.我这里已经提前拉取了,嵌入模型embeddinggemma:300mdeepseek-r1:8b, 配置application.yml文件

spring:
  ai:
    ollama:
      #ollama的url
      base-url: http://localhost:11434
      #聊天模型
      chat:
        options:
          model: "deepseek-r1:8b"
      #嵌入模型
      embedding:
        options:
          model: "embeddinggemma:300m"

    #milvus相关配置
    vectorstore:
      milvus:
        client:
          host: "🤭" 
          port: 19530 #milvus端口
          username: "🤭"
          password: "🤭"
        databaseName: "default" #数据库名
        collectionName: "vector_store3" #集合名
        embeddingDimension: 768 #向量模型的嵌入维度(根据实际情况)
        initialize-schema: true #如果集合名不存在,就自动创建。
        indexType: IVF_FLAT #向量索引的类型
        metricType: COSINE #向量相似度搜索的度量方式,使用余弦相似度
@RestController
@RequestMapping("/api/v1")
public class ChatModelController {

    @Autowired
    private VectorStore vectorStore;

    @Autowired
    private ChatModel chatModel;

    @GetMapping(value = "/rag/chat")
    public Flux<String> chat1(@RequestParam String message){
        //构建相似度相关参数
        MilvusSearchRequest request = MilvusSearchRequest.milvusBuilder()
                .query(message)//文本信息
                //返回最相似的前5个结果
                //对于RAG建议3-10 太小可能遗漏,太大引入噪声增加token消耗
                .topK(5)
                //相似度阈值 只返回>=0.7的结果 由于用的是余弦相似度,建议这里填0.7
                .similarityThreshold(0.7)
                //IVF索引的搜索精度参数 数值越大 搜索越精确 但速度越慢
                    //nprobe=16  速度快,精度较低
                    //nprobe=64  平衡模式(常用)
                    //nprobe=128 精度高,速度较慢
                    //nprobe=256 高精度,慢速
                .searchParamsJson("{"nprobe":128}")
                .build();
        //执行相似度搜索。返回相似文档
        List<Document> similarityDocuments = vectorStore.similaritySearch(request);

        //获取返回的文本信息
        String context = similarityDocuments.stream()
                .map(Document::getText)
                .collect(Collectors.joining("\n\n"));

        //构建提示词
        String promptText = String.format("""
        你是一个专业的图书馆智能客服。请严格遵循以下规则:
        
        1.  核心原则:你的回答必须严格且仅依据下面提供的【参考信息】。如果参考信息中找不到明确答案,你必须说:“根据现有资料,我无法回答该问题。”
        2.  回答格式:请使用简洁、友好的口语化中文直接给出答案。
        3.  静态规则:图书馆共有5层,开放时间为每日8:00-22:004.  信息边界:如果用户的问题与图书馆服务完全无关,请礼貌地告知:“您的问题暂不在我的服务范围内。”
        
        现在,请基于以下【参考信息】回答用户问题:
        【参考信息】: %s
        
        【用户问题】: %s
        
        请开始你的回答:
        """, context, message);


        //将promptText传给deepseek-r1
        Flux<ChatResponse> response = chatModel.stream(
                new Prompt(
                        promptText,
                        OllamaChatOptions.builder()
                                .model("deepseek-r1:8b")
                                //禁用思考
                                .disableThinking()
                                .build()
                ));



        //流式输出(普通输出 让人感觉响应慢)
        Flux<String> map = response
                .flatMapIterable(ChatResponse::getResults)
                .map(generation -> generation.getOutput().getText());

        return map;
    }

    //文本转向量(这里只是粗略的转换)
    @GetMapping("/to/emb")
    public ApiResponse<Void> textTo(@RequestParam(value = "text") String message) {
        List<Document> documents = List.of(new Document(message));
        vectorStore.add(documents);
        return ApiResponse.success(null);
    }
 }

7.以上就是关于RAG的全部内容。由于个人经验有限,文中如有不足或错误之处,欢迎各位大佬指正交流!