开发日志(十):RAG 的智能菜单助手设计
一、项目背景在境外旅行、跨文化聚餐或者浏览外文菜单时用户经常会遇到以下问题菜单使用外语书写无法快速理解菜品名称和配料菜单排版复杂传统 OCR 只能识别文字难以还原菜品与价格之间的对应关系用户可能存在过敏原、忌口、素食、低糖等饮食限制即使完成菜单翻译用户仍然不知道“哪些菜适合自己”普通聊天模型不了解当前菜单内容容易推荐菜单中不存在的菜品。因此本项目并没有将功能停留在“图片翻译”阶段而是设计了一套完整的智能菜单理解与问答系统。用户上传菜单图片后系统首先使用多模态大模型识别菜单内容将图片转换为结构化菜品数据随后把菜品信息写入向量数据库当用户针对菜单进行提问时系统结合菜单知识、用户偏好和大语言模型生成个性化回答。整个系统形成了如下业务闭环菜单图片上传 ↓ 多模态模型识别 ↓ 结构化菜品 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 客户端需要实现完整流程选择图片 → 上传图片 → 等待识别 → 展示菜单 → 发起问题 → 等待后端生成 → 展示回答此外还需要处理网络异常、模型异常、空结果、鉴权失效等情况避免页面白屏或者一直处于加载状态。三、什么是 RAGRAG 的全称是 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. LangChainLangChain 用于组织 RAG 处理流程包括将菜品 JSON 转换为 Document调用 Embedding 模型生成向量管理检索过程构造问答 Prompt调用聊天模型生成答案。3. Embedding 向量模型Embedding 模型负责将文本转换为向量。语义相近的文本在向量空间中的距离通常也更近。例如“不辣的菜” “清淡菜品” “没有辣椒的食物”虽然文字不完全相同但经过向量化后具有较高的语义相似度。4. Chroma 向量数据库Chroma 用于存储菜品文本向量和菜品元数据。系统可以根据用户问题执行相似度搜索返回最相关的 top-k 个菜品文档。5. FastAPI后端使用 FastAPI 提供接口主要负责接收菜单图片调用多模态模型解析结构化结果执行向量入库接收用户问题查询用户偏好调用 RAG 问答服务返回统一格式的数据。6. FlutterFlutter 客户端负责图片选择和上传菜单结果展示RAG 对话交互用户偏好传递收藏和购物车等原有业务异常提示和加载状态管理。六、系统的四层架构第一层菜单感知层输入是非结构化菜单图片输出是结构化菜品 JSON。这一层的核心任务不是单纯识别文字而是将图片内容转换为后端可以稳定处理的数据结构。第二层知识入库层结构化菜品需要重新组织成适合语义检索的文本例如菜品原名Spicy Chicken 中文名称香辣鸡肉 描述鸡肉搭配辣椒和蔬菜烹制 价格12.99 标签辣、鸡肉、主菜随后生成向量并写入 Chroma 数据库。第三层检索增强问答层用户提问后系统先检索相关菜品再结合用户偏好构建 Prompt最后调用聊天模型回答。第四层前后端业务闭环层Flutter 与 FastAPI 完成接口联调确保菜单识别结果能够自动入库并能够在结果页中进行真实问答。七、项目整体特点本项目可以概括为一个多模态菜单识别 向量检索 个性化推荐 移动端交互闭环的复合式 RAG 系统。它同时涉及非结构化图片理解结构化信息抽取菜品知识表示向量索引构建检索增强生成用户偏好融合前后端接口编排模型运行环境管理异常处理与工程兜底。相比普通的聊天问答功能这类系统的难点不只在模型本身更在于如何让每个环节真正连接起来。下一篇文章将详细介绍该系统的具体开发过程包括菜单处理接口、Document 构造、Chroma 入库、RAG 问答服务和 Flutter 结果页改造。