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

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

  • 可以问文本问题
  • 可以上传监控截图
  • 可以输入日志片段
  • 最后由大模型结合知识库,给出解释和建议。
相关推荐
墨风如雪2 小时前
你的AI分析师已上线:阿里巴巴“神助攻”开启数据洞察新纪元!
aigc
桂花饼3 小时前
谷歌 “Nano Banana“ 深度解析:AI 图像的未来是精准编辑,而非从零生成
人工智能·aigc·gpt-4o·gpt-5·claude 4.1·nano banana
zabr5 小时前
告别ai味,拥抱设计新生:聊聊如何用 Claude Code 创造设计师级别的UI
前端·aigc·ai编程
sheji34167 小时前
【开题答辩全过程】以 信达纸巾公司生产管理系统为例,包含答辩的问题和答案
spring boot
孟健8 小时前
出海产品支付难?我用AI半小时搞定兑换码系统,转化率暴增300%!
ai编程
泉城老铁10 小时前
Spring Boot项目开发中,JPA 和mybatisplus哪个更哇塞呢
java·spring boot·后端
编啊编程啊程10 小时前
响应式编程框架Reactor【5】
java·jvm·spring boot·spring cloud·java-ee·maven
pepedd86412 小时前
AI Coding 最佳实践-从零到一全栈项目编写
前端·aigc·trae
Jooolin12 小时前
【C++】C++中的 set
数据结构·c++·ai编程