当前位置: 首页 > news >正文

开发日志(十):RAG 的智能菜单助手设计

一、项目背景

在境外旅行、跨文化聚餐或者浏览外文菜单时,用户经常会遇到以下问题:

  1. 菜单使用外语书写,无法快速理解菜品名称和配料;
  2. 菜单排版复杂,传统 OCR 只能识别文字,难以还原菜品与价格之间的对应关系;
  3. 用户可能存在过敏原、忌口、素食、低糖等饮食限制;
  4. 即使完成菜单翻译,用户仍然不知道“哪些菜适合自己”;
  5. 普通聊天模型不了解当前菜单内容,容易推荐菜单中不存在的菜品。

因此,本项目并没有将功能停留在“图片翻译”阶段,而是设计了一套完整的智能菜单理解与问答系统。

用户上传菜单图片后,系统首先使用多模态大模型识别菜单内容,将图片转换为结构化菜品数据;随后把菜品信息写入向量数据库;当用户针对菜单进行提问时,系统结合菜单知识、用户偏好和大语言模型生成个性化回答。

整个系统形成了如下业务闭环:

菜单图片上传 ↓ 多模态模型识别 ↓ 结构化菜品 JSON ↓ 文本切分与向量化 ↓ Chroma 向量数据库 ↓ 用户问题与偏好 ↓ 相关菜品检索 ↓ 大语言模型生成回答 ↓ Flutter 页面展示

这并不是简单地给原有应用增加一个聊天框,而是将“多模态识别、知识入库、向量检索、个性化推荐和移动端交互”整合成一条完整的 AI 工程链路。


二、项目需求分析

1. 菜单图片识别

系统需要支持用户上传真实菜单图片,并从图片中提取:

  • 原始菜品名称;
  • 中文翻译名称;
  • 菜品描述;
  • 价格;
  • 菜品标签;
  • 可能包含的食材;
  • 辣度、素食等属性。

传统 OCR 通常只能返回零散文字,例如:

Spicy Chicken 12.99 Beef Noodles 15.50

但系统真正需要的是结构化结果:

{"name_original":"Spicy Chicken","name_zh":"香辣鸡肉","description":"Chicken cooked with chili and vegetables","price":"12.99","tags":["辣","鸡肉"]}

因此,识别过程不仅包含 OCR,还包含语义理解、字段归类和结构化抽取。

2. 菜单知识问答

识别结果展示完成后,用户还可能继续提出问题,例如:

  • 哪一道菜不辣?
  • 有适合素食者的菜吗?
  • 我对花生过敏,哪些菜不能点?
  • 推荐一道价格较低的主食。
  • 这几道菜中哪一道热量可能更低?
  • 给我推荐两道适合分享的菜。

如果直接将问题发送给普通大模型,模型并不知道当前菜单里有哪些菜,也可能编造菜单中不存在的内容。

因此,系统需要使用 RAG 技术,将当前菜单作为外部知识来源。

3. 用户个性化偏好

不同用户的饮食需求并不相同,系统需要考虑:

  • 过敏原;
  • 忌口食材;
  • 素食或纯素需求;
  • 清真饮食需求;
  • 辣度偏好;
  • 口味偏好;
  • 价格倾向。

用户提出“帮我推荐一道菜”时,系统不能只根据菜品名称回答,而应结合用户个人信息进行推荐。

4. 移动端完整交互

Flutter 客户端需要实现完整流程:

选择图片 → 上传图片 → 等待识别 → 展示菜单 → 发起问题 → 等待后端生成 → 展示回答

此外还需要处理网络异常、模型异常、空结果、鉴权失效等情况,避免页面白屏或者一直处于加载状态。


三、什么是 RAG

RAG 的全称是 Retrieval-Augmented Generation,即“检索增强生成”。

它的核心思想是:

在大模型回答问题之前,先从外部知识库中检索相关内容,再让模型根据检索结果生成答案。

一个基本的 RAG 流程可以表示为:

用户问题 ↓ 问题向量化 ↓ 向量数据库相似度检索 ↓ 获取相关知识片段 ↓ 知识片段与问题拼接 ↓ 大模型生成回答

例如,用户提问:

我不能吃辣,推荐一道主食。

系统会先从当前菜单知识库中检索与“不辣”“主食”“推荐”等语义相关的菜品,再将检索结果传给大模型。

最终发送给模型的内容可能类似:

用户偏好: - 不吃辣 - 偏好主食 菜单检索结果: 1. Mushroom Pasta,奶油蘑菇意面,价格 13.99,标签:不辣、主食 2. Spicy Beef Noodles,香辣牛肉面,价格 15.99,标签:辣、主食 用户问题: 我不能吃辣,推荐一道主食。 请只根据菜单检索结果回答。

模型便可以推荐奶油蘑菇意面,并说明不推荐香辣牛肉面的原因。


四、为什么不能只使用大模型

直接调用大模型虽然实现简单,但存在几个明显问题。

1. 大模型不了解当前菜单

当前菜单属于用户实时上传的数据,不在模型原有训练知识中。

2. 容易出现幻觉

如果让模型自由回答,它可能生成菜单中不存在的菜品、价格或配料。

3. 菜单内容会不断变化

每次上传的菜单都不同,无法通过固定提示词保存全部菜单信息。

4. 长菜单会占用大量上下文

直接把完整菜单放入 Prompt,会增加 Token 消耗,也可能降低模型对重点信息的关注度。

RAG 可以只检索与当前问题相关的若干菜品,从而减少上下文长度,提高回答相关性。


五、本项目采用的核心技术

1. Qwen 多模态大模型

多模态模型负责理解菜单图片。

与传统 OCR 相比,多模态模型不仅能够识别文字,还可以理解:

  • 菜名与价格之间的对应关系;
  • 菜品分类;
  • 描述和菜名之间的从属关系;
  • 菜品可能具有的标签;
  • 图片中的版面结构。

模型输出经过约束后转换为统一的 JSON 格式,为后续处理提供基础。

2. LangChain

LangChain 用于组织 RAG 处理流程,包括:

  • 将菜品 JSON 转换为 Document;
  • 调用 Embedding 模型生成向量;
  • 管理检索过程;
  • 构造问答 Prompt;
  • 调用聊天模型生成答案。

3. Embedding 向量模型

Embedding 模型负责将文本转换为向量。

语义相近的文本在向量空间中的距离通常也更近。例如:

“不辣的菜” “清淡菜品” “没有辣椒的食物”

虽然文字不完全相同,但经过向量化后具有较高的语义相似度。

4. Chroma 向量数据库

Chroma 用于存储菜品文本向量和菜品元数据。

系统可以根据用户问题执行相似度搜索,返回最相关的 top-k 个菜品文档。

5. FastAPI

后端使用 FastAPI 提供接口,主要负责:

  • 接收菜单图片;
  • 调用多模态模型;
  • 解析结构化结果;
  • 执行向量入库;
  • 接收用户问题;
  • 查询用户偏好;
  • 调用 RAG 问答服务;
  • 返回统一格式的数据。

6. Flutter

Flutter 客户端负责:

  • 图片选择和上传;
  • 菜单结果展示;
  • RAG 对话交互;
  • 用户偏好传递;
  • 收藏和购物车等原有业务;
  • 异常提示和加载状态管理。

六、系统的四层架构

第一层:菜单感知层

输入是非结构化菜单图片,输出是结构化菜品 JSON。

这一层的核心任务不是单纯识别文字,而是将图片内容转换为后端可以稳定处理的数据结构。

第二层:知识入库层

结构化菜品需要重新组织成适合语义检索的文本,例如:

菜品原名:Spicy Chicken 中文名称:香辣鸡肉 描述:鸡肉搭配辣椒和蔬菜烹制 价格:12.99 标签:辣、鸡肉、主菜

随后生成向量并写入 Chroma 数据库。

第三层:检索增强问答层

用户提问后,系统先检索相关菜品,再结合用户偏好构建 Prompt,最后调用聊天模型回答。

第四层:前后端业务闭环层

Flutter 与 FastAPI 完成接口联调,确保菜单识别结果能够自动入库,并能够在结果页中进行真实问答。


七、项目整体特点

本项目可以概括为一个:

多模态菜单识别 + 向量检索 + 个性化推荐 + 移动端交互闭环的复合式 RAG 系统。

它同时涉及:

  • 非结构化图片理解;
  • 结构化信息抽取;
  • 菜品知识表示;
  • 向量索引构建;
  • 检索增强生成;
  • 用户偏好融合;
  • 前后端接口编排;
  • 模型运行环境管理;
  • 异常处理与工程兜底。

相比普通的聊天问答功能,这类系统的难点不只在模型本身,更在于如何让每个环节真正连接起来。

下一篇文章将详细介绍该系统的具体开发过程,包括菜单处理接口、Document 构造、Chroma 入库、RAG 问答服务和 Flutter 结果页改造。

http://www.jsqmd.com/news/997416/

相关文章:

  • 三沙市黄金回收白银回收铂金回收彩金回收靠谱门店TOP排行榜及联系方式地址电话+诚信店铺推荐 - 大熊猫898989
  • 终极自托管游戏串流实战指南:5步搭建你的家庭游戏云平台
  • 鸿蒙原生开发——从零构建倒数日追踪器
  • 从设计到量产:手把手拆解芯片内存测试(MBIST)与修复(BISR)的全流程
  • 百度网盘直链解析工具:技术侦探带你破解下载速度之谜
  • 从S32K1到S32K3:手把手教你迁移汽车MCU项目(基于Arm Cortex-M7实战)
  • AC7840芯片UART+DMA循环接收工程(IAR/Keil双环境验证)
  • 为什么你的MOS管在干燥冬天更容易挂?从极间电容和输入电阻角度拆解静电积累
  • 网络安全干货:护网行动实战经验分享
  • 如何用LinkSwift快速获取九大网盘直链下载地址:告别限速烦恼
  • 三亚市黄金回收白银回收铂金回收彩金回收靠谱门店TOP排行榜及联系方式地址电话+诚信店铺推荐 - 大熊猫898989
  • 告别舞台灯光盲区:用STM32F0单片机手把手实现DMX512信号解码(附完整代码)
  • 3分钟掌握手机号定位技术:免费开源工具让地理位置查询变得简单
  • 鸿蒙原生应用实战(五):编译构建与性能优化 —— 从开发到上架
  • 从收音机到Wi-Fi:串联RLC电路如何成为无线通信的“频率守门员”?
  • 荆门市黄金回收白银回收铂金回收彩金回收靠谱门店TOP排行榜及联系方式地址电话+诚信店铺推荐 - 大熊猫898989
  • Qdrant源码与算法
  • 荆州市黄金回收白银回收铂金回收彩金回收靠谱门店TOP排行榜及联系方式地址电话+诚信店铺推荐 - 大熊猫898989
  • 生产级多维聚合四大铁律:从pandas groupby到银行风控实战
  • CMake 015:日志级别全解析
  • Barlow字体技术深度解析:从加州公路标识到数字设计的变量革命
  • 从‘天书’到蓝图:一文读懂Gerber文件里每个层(.gbr)到底在告诉工厂什么
  • XGP存档提取终极指南:3分钟释放你的游戏进度自由
  • 百度网盘直链解析技术深度解析:绕过限速实现高速下载的技术实现
  • X79双路主板Win10开机卡Logo?富士康/广达平台专用DLL修复包
  • 百度网盘资源工具终极指南:3分钟学会一键获取提取码的完整方法
  • PyTorch工程化起点:可复现、可扩展、可交付的训练模板
  • 景德镇市黄金回收白银回收铂金回收彩金回收靠谱门店TOP排行榜及联系方式地址电话+诚信店铺推荐 - 大熊猫898989
  • AutoCAD里能拖拽选中的自定义直线插件(ObjectARX C++源码工程)
  • 2026年济南中职学校大揭秘:究竟哪个教学质量更胜一筹?