做一个「运维知识库 + 多模态检索问答」的案例

Ops 多模态 RAG(Retrieval-Augmented Generation) ,结合 Spring AI + Qdrant,可以用来做一个「运维知识库 + 多模态检索问答」的案例。下面我将简单实现一下这个案例:


🌐 场景设计

背景

  • 运维团队需要快速排查问题,但信息分散在 文档(markdown/PDF)、图片(监控截图)、日志(结构化/半结构化) 里。
  • 我们用 Spring AI (方便整合LLM)+ Qdrant (向量数据库)做一个 多模态 RAG ,支持输入:
    • 文本问题(自然语言问答)
    • 图片截图(例如:用户上传一张服务器 CPU 监控图,让系统结合已有知识库解释异常)
    • 日志片段(半结构化输入)

目标

  • 用户输入"CPU usage looks spiky" + 一张 CPU 曲线图,系统去 Qdrant 检索最相关的知识文档 & 历史故障案例,并由 LLM 给出解释 + 建议。

🔧 技术架构

  1. Spring AI
    • ChatClient 调用大模型(支持文本和多模态输入)
    • 集成 Qdrant 作为向量检索层
  2. Qdrant
    • 存储多模态 Embeddings(文本/图像/日志)
    • 使用 collection 区分数据类型(如 ops_text_docs, ops_images, ops_logs
  3. Embedding 模型
    • 文本:text-embedding-3-small
    • 图像:CLIP-based embedding(如 openai/clip-vit-base-patch32
    • 日志:同文本 embedding 处理

📂 Qdrant Schema 设计

json 复制代码
{
  "ops_text_docs": {
    "vector_size": 1536,
    "distance": "Cosine"
  },
  "ops_images": {
    "vector_size": 512,
    "distance": "Cosine"
  },
  "ops_logs": {
    "vector_size": 1536,
    "distance": "Cosine"
  }
}

💻 Spring AI 代码示例

1. 配置依赖(pom.xml

xml 复制代码
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
  <version>1.0.0-M1</version>
</dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-qdrant-spring-boot-starter</artifactId>
  <version>1.0.0-M1</version>
</dependency>

2. Qdrant 配置(application.yml

yaml 复制代码
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
    vectorstore:
      qdrant:
        host: localhost
        port: 6333

3. 向量存储服务

java 复制代码
@Service
public class KnowledgeIngestService {

    private final QdrantVectorStore qdrant;
    private final OpenAiEmbeddingClient embeddingClient;

    public KnowledgeIngestService(QdrantVectorStore qdrant,
                                  OpenAiEmbeddingClient embeddingClient) {
        this.qdrant = qdrant;
        this.embeddingClient = embeddingClient;
    }

    public void ingestTextDoc(String id, String content) {
        EmbeddingResponse embedding = embeddingClient.embed(content);
        qdrant.add(
            new Document(id, content, embedding.getVector())
        );
    }

    public void ingestLog(String id, String log) {
        EmbeddingResponse embedding = embeddingClient.embed(log);
        qdrant.add(
            new Document(id, log, embedding.getVector())
        );
    }

    public void ingestImage(String id, byte[] imageBytes) {
        float[] vector = MyClipEmbeddingModel.encodeImage(imageBytes);
        qdrant.add(
            new Document(id, "image", vector)
        );
    }
}

4. RAG 服务

java 复制代码
@Service
public class OpsRagService {

    private final QdrantVectorStore qdrant;
    private final ChatClient chatClient;

    public OpsRagService(QdrantVectorStore qdrant, ChatClient chatClient) {
        this.qdrant = qdrant;
        this.chatClient = chatClient;
    }

    public String answerQuestion(String question, byte[] optionalImage, String optionalLog) {
        // 1. 构造查询向量
        List<float[]> queryVectors = new ArrayList<>();
        queryVectors.add(embedding(question));
        if (optionalLog != null) queryVectors.add(embedding(optionalLog));
        if (optionalImage != null) queryVectors.add(MyClipEmbeddingModel.encodeImage(optionalImage));

        // 2. 检索知识库
        List<Document> retrievedDocs = new ArrayList<>();
        for (float[] qv : queryVectors) {
            retrievedDocs.addAll(qdrant.similaritySearch(qv, 3));
        }

        // 3. 拼接上下文
        String context = retrievedDocs.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n---\n"));

        // 4. 调用 LLM
        return chatClient.prompt()
                .system("You are an Ops assistant. Use the following context:\n" + context)
                .user(question)
                .call()
                .content();
    }

    private float[] embedding(String text) {
        return new OpenAiEmbeddingClient().embed(text).getVector();
    }
}

🚀 使用方式

java 复制代码
@RestController
@RequestMapping("/ops")
public class OpsController {

    private final OpsRagService ragService;

    public OpsController(OpsRagService ragService) {
        this.ragService = ragService;
    }

    @PostMapping("/ask")
    public String ask(@RequestParam String question,
                      @RequestParam(required=false) MultipartFile image,
                      @RequestParam(required=false) String log) throws IOException {
        return ragService.answerQuestion(
                question,
                image != null ? image.getBytes() : null,
                log
        );
    }
}

✅ 这样,你就有了一个 Spring AI + Qdrant 的多模态 RAG 运维知识库

  • 可以问文本问题
  • 可以上传监控截图
  • 可以输入日志片段
  • 最后由大模型结合知识库,给出解释和建议。
相关推荐
老华带你飞几秒前
小区服务|基于Java+vue的小区服务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·小区服务管理系统
龙茶清欢23 分钟前
具有实际开发参考意义的 MyBatis-Plus BaseEntity 基类示例
java·spring boot·spring cloud·mybatis
计算机学姐27 分钟前
基于微信小程序的扶贫助农系统【2026最新】
java·vue.js·spring boot·mysql·微信小程序·小程序·mybatis
Keegan小钢29 分钟前
AI+Web3实战营日志 #7 | 完成Core合约测试
web3·ai编程
C++chaofan34 分钟前
游标查询在对话历史场景下的独特优势
java·前端·javascript·数据库·spring boot
小蒜学长41 分钟前
springboot房地产销售管理系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
齐 飞1 小时前
Spring Cloud Alibaba快速入门-Sentinel熔断规则
spring boot·spring cloud·sentinel
邂逅星河浪漫1 小时前
【LangChain4j+Redis】会话记忆功能实现
java·spring boot·后端·阿里云·langchain4j·会话记忆
win4r1 小时前
🚀保姆级教程!GitHub Copilot CLI横空出世,支持MCP扩展+自动PR创建,让AI编程效率提升1000%,开发者必看!开发完整应用实战演示!
aigc·openai·github copilot
aloha_7891 小时前
新国都面试真题
jvm·spring boot·spring·面试·职场和发展