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

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 运维知识库

  • 可以问文本问题
  • 可以上传监控截图
  • 可以输入日志片段
  • 最后由大模型结合知识库,给出解释和建议。
相关推荐
云起SAAS1 小时前
空号号码状态检测抖音快手微信小程序看广告流量主开源
ai编程·1024程序员节·看广告变现轻·空号号码状态检测
神秘的土鸡5 小时前
从数据仓库到数据中台再到数据飞轮:我的数据技术成长之路
java·服务器·aigc·数据库架构·1024程序员节
墨风如雪7 小时前
Google Skills:AI时代的学习革命,你准备好了吗?
aigc
遥远_7 小时前
Spring Boot微服务健康检测:保障系统稳定性的关键实践
spring boot·微服务·1024程序员节·健康检测
苹果醋37 小时前
学习札记-Java8系列-1-Java8新特性简介&为什么要学习Java8
java·运维·spring boot·mysql·nginx
兜兜风d'8 小时前
RabbitMQ 发送方确认机制详解
spring boot·分布式·rabbitmq·java-rabbitmq·1024程序员节
shepherd1269 小时前
破局延时任务(上):为什么选择Spring Boot + DelayQueue来自研分布式延时队列组件?
java·spring boot·后端·1024程序员节
zl9798999 小时前
SpringBoot-Web开发之数据响应
java·spring boot·后端
也许是_9 小时前
Spring Boot 3.X推荐Micrometer Tracing 分布式链路追踪
spring boot·分布式·后端
没有bug.的程序员9 小时前
Spring Boot 起步:自动装配的魔法
java·开发语言·spring boot·后端·spring·1024程序员节