Skip to content

点餐系统装上了「DeepSeek 大脑」

基于 Spring AI + PgVector 的 RAG 落地指南

大家好,我是 Sail

pgsql 版本的 AI 点餐系统 已经完成,代码也已经提交到仓库 🎉 目前支持:

  • 🧠 本地 DeepSeek 1.5B 模型(960 显卡也能跑)
  • ☁️ DeepSeek API 云端模式

前段时间我开源了 Sail-Food 扬帆点餐系统(基础版),收到了不少反馈:

  • 👍 界面丝滑、部署简单
  • 🎓 非常适合 毕设 / 商用 / 二次开发
  • 🏪 有同学建议:搞个多商户版本

👉 多商户后面会看时间考虑补一个 PostgreSQL 版本


为什么写这篇文章?

今天不聊“AI 概念”, 而是 完整复盘一次:Java 生态下,RAG(检索增强生成)是如何真正落地的

不是 Demo

不是 PPT 架构

✅ 是已经跑在真实业务里的 AI 点餐系统


💡 架构设计:为什么选 PostgreSQL,而不是 Milvus?

在设计 AI 功能时,我首先面临一个问题:

向量数据,存哪?

常见方案

text
MySQL(业务数据)
+ Milvus / Elasticsearch(向量)

我最终的选择

👉 PostgreSQL All-in-One

架构师视角的思考

  • ❌ 引入 Milvus = 新组件 + 新运维成本
  • ❌ 小项目 / 中小系统 性价比极低
  • ❌ 数据一致性、备份、迁移复杂

而 PostgreSQL:

  • ✅ 原生 pgvector
  • ✅ 事务 + 向量一体化
  • ✅ Java 生态支持成熟
  • ✅ 一个数据库,解决所有问题

结论:

👉 业务规模没到“必须分库”的程度,PostgreSQL 是 RAG 的最优解


🧠 最终系统架构图

mermaid
graph LR
User[用户提问] --> App[后端 SpringBoot]
App --> DeepSeek_1.5B[意图识别<br/>(本地 / 云端)]
DeepSeek_1.5B -- 是点餐 --> OrderService[下单逻辑]
DeepSeek_1.5B -- 是咨询 --> RAG[RAG 检索服务]
RAG -- 语义检索 --> PG[PostgreSQL<br/>(pgvector)]
PG -- 返回相似菜品 --> RAG
RAG -- 组装 Prompt --> DeepSeek_V3[DeepSeek V3 API]
DeepSeek_V3 -- 生成回答 --> User

🛠️ 核心代码实战

1️⃣ 依赖引入(Maven)

全面拥抱 Spring AI 官方生态 👇

xml
<!-- PGVector -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-pgvector-store</artifactId>
</dependency>

<!-- Ollama(本地模型) -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>

<!-- DeepSeek API(OpenAI 兼容) -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

2️⃣ 数据向量化(Embedding)

我们需要把 菜品信息转成向量 存入数据库。

⚠️ 关键技巧:不要只存菜名!

要拼装成一段 “人话描述”

java
@Async
@Override
public void syncAllProductsToVectorStore() {

    List<WyProducts> products =
        wyProductsMapper.selectWyProductsList(new WyProducts());

    // 清空旧向量
    jdbcTemplate.execute("DELETE FROM vector_store");

    List<Document> documents = new ArrayList<>();

    for (WyProducts p : products) {

        String aiDesc = String.format(
            "菜名:%s,价格:%s元,分类:%s,介绍:%s",
            p.getProductName(),
            p.getPrice(),
            p.getWyProductCategories().getCategoryName(),
            p.getDescription() == null ? "暂无详细介绍" : p.getDescription()
        );

        Map<String, Object> metadata = Map.of(
            "id", p.getProductId(),
            "price", p.getPrice()
        );

        documents.add(new Document(aiDesc, metadata));
    }

    // 写入向量库
    vectorStore.add(documents);

    log.info("AI 数据同步完成,共 {} 条", documents.size());
}

3️⃣ 智能问答实现(Intent + RAG)

核心思路:

  1. 先让 AI 判断:是不是在点餐
  2. 如果能解析出 订单 JSON → 直接下单
  3. 否则 → 走 RAG 问答流程
java
public OrderIntentDTO parseOrder(String userMessage) {

    PromptTemplate promptTemplate =
        new PromptTemplate(INTENT_PROMPT);

    Prompt prompt = promptTemplate.create(
        Map.of("user_input", userMessage)
    );

    String jsonResult = chatClient.prompt(prompt)
        .options(ChatOptionsBuilder.builder()
            .withTemperature(0.0)
            .build())
        .call()
        .content();

    try {
        jsonResult = jsonResult
            .replace("```json", "")
            .replace("```", "")
            .trim();

        OrderIntentDTO intent =
            JSON.parseObject(jsonResult, OrderIntentDTO.class);

        // 防止把“抽象词”当成菜名
        if ("ORDER".equals(intent.getType())
            && intent.getItems() != null) {

            boolean isInvalid = intent.getItems().stream()
                .anyMatch(item ->
                    INVALID_DISH_NAMES.contains(item.getName()));

            if (isInvalid) {
                intent.setType(IntentType.QA);
                intent.setItems(null);
            }
        }

        return intent;

    } catch (Exception e) {
        return null;
    }
}

📱 效果展示

场景一:AI 自动点餐

  • 用户自然语言描述
  • AI 自动识别菜品
  • 直接加入购物车

用户只需要点击「结算」即可完成下单


场景二:AI 咨询菜品

  • 「推荐点清淡的」
  • 「适合小孩吃的菜」
  • 「有没有辣的?」

👉 全部通过 RAG + PgVector 实时检索回答


🧵 总结

通过 PostgreSQL + Spring AI + DeepSeek

  • 🍽️ 点餐系统从「CRUD 工具」
  • 🚀 进化成「真正懂用户的 AI 助手」

这不仅是功能升级,更是:

Java 开发者迈向 AI 全栈的一次完整实践


🔮 下一步计划

  • ⏳ 快过年了,先歇一歇
  • 🔥 年前补一个 Redis + Lua 库存扣减
  • 🏪 后续考虑 多商户版本(PG 方案)

👇 源码获取

👉 Gitee: https://gitee.com/wyabsdai/sail-food


我是 Sail, 37 岁高级架构师。

不写代码就得去摆摊了 😂

👉 点个关注支持一下吧!