一、目标
阶段六的核心目标,是在已有美食知识图谱和菜单识别功能的基础上,进一步构建一个能够真实服务用户点餐和饮食推荐的 AI 问答模块。
一开始,这个模块并不是单纯为了“接入大模型聊天”。如果只是把用户问题直接发给大模型,虽然可以得到自然语言回答,但很难保证回答是否结合了用户偏好、是否遵守过敏限制、是否基于当前菜单、是否会胡乱推荐不存在的菜品。因此,阶段六的重点不是简单调用 LLM,而是构建一条可控、可追踪、可约束的 AI 菜单问答链路。
最终目标可以概括为:
用户问题 → 场景识别 → 用户偏好和菜单上下文 → FoodKG / 向量检索提供证据 → LLM 生成自然回答 → 后端进行安全边界校验 → 前端展示结构化回答也就是说,AI 不只是“会聊天”,而是要成为一个能结合菜单、食材、过敏原、用户偏好和知识图谱证据的菜单助理。
二、双入口模式:区分菜单咨询和日常推荐
阶段六首先解决的是问答场景混杂的问题。之前用户问“推荐几个不辣的”时,系统可能既参考当前菜单,又参考购物车、收藏、历史菜单和知识库,导致回答范围不稳定。
因此,我将 AI 问答入口拆分为两种模式:
menu_consultation:基于当前菜单问我 daily_recommendation:日常饮食推荐菜单咨询模式下,系统必须严格限定在当前识别菜单范围内回答。例如用户问“这个菜单里有 pasta 推荐吗?”,如果当前菜单没有意面类菜品,系统应该明确回答“当前菜单没有看到明确的 pasta / 意面类菜品”,而不能擅自推荐菜单外的番茄意面或蘑菇意面。
日常推荐模式下,系统则不受当前菜单限制,可以结合 FoodKG、向量检索结果和大模型常识,给出更开放的饮食建议。例如用户问“I want pasta”,系统可以推荐番茄意面、蒜香橄榄油意面、蔬菜意面等常见选择。
这个双入口设计解决了一个关键问题:同样是“推荐”,在不同场景下回答边界完全不同。菜单咨询强调“不要越界”,日常推荐强调“自然、丰富、合理”。
三、接入 FoodKG 和向量检索
阶段六中,AI 问答模块开始真正接入 FoodKG 和向量检索。系统不再只依靠本地固定知识库,而是能够从构建好的菜品知识库和中文菜谱向量库中召回相关候选。
FoodKG / vector 检索提供的信息包括:
菜品名称 菜品描述 食材 过敏原 潜在风险因子 烹饪方式 来源信息例如用户问“不辣的家常菜”,系统可以召回鱼丸萝卜汤、鲫鱼白萝卜丝汤等候选;用户问“I want pasta”,系统可以召回家常意面酱、番茄意面相关菜品,也可能召回一些 spicy/chili 相关的冲突候选。
在这个阶段,我逐渐发现一个重要问题:RAG 检索结果不能直接等于最终推荐结果。向量检索擅长语义相似,但不擅长严格处理“不含、不要、无、过敏、忌口”等逻辑约束。例如“无花生甜点”可能召回“花生酱曲奇”,因为它们在“花生、甜点、烘焙”语义空间上很接近。
因此,阶段六后半段的重点逐渐从“让 RAG 多查一点”转向“让 RAG 查到的结果被正确使用”。
四、LLM Query Expansion:让模糊问题变成可检索问题
用户的问题往往很短,例如:
推荐几个不辣的家常菜 想吃清淡一点的鸡肉菜 想吃不含鸡蛋和花生的甜味烘焙 I want pasta这些问题如果直接用于向量检索,效果并不稳定。因此,我加入了 LLM Query Expansion,让大模型先把用户问题扩展成多个更适合检索的查询词。
例如“不辣的家常菜”可以扩展为:
清蒸鱼 家常做法 不辣 蒜蓉西兰花 做法 无蛋 土豆炖牛肉 清淡口味 香菇炒青菜 低辣度 无花生 冬瓜排骨汤 家常 粤菜风格这样做的好处是,系统不再只依赖用户原始问题中的几个词,而是能够从多个角度检索相关菜品。
但扩展本身也带来了新问题:LLM 有时会扩出不合适的查询,比如用户没有说想吃辣,却扩出了“spicy pasta”“medium heat”“chili”等词。因此,后续又引入了 query sanitization。
五、Query Sanitization:清理冲突检索词
Query Sanitization 的目标是对扩展后的 query 做安全清理,避免检索方向被错误带偏。
比如用户有鸡蛋和花生过敏时,以下 query 应该保留:
pasta without egg or peanuts egg-free dessert peanut-free cookies vegan pasta avoiding egg and peanuts safe for peanut allergy因为它们表达的是“避开风险”。
而以下 query 应该删除或降权:
egg pasta peanut butter cookies spicy tomato pasta pasta with chili flakes medium heat pasta因为它们要么和过敏冲突,要么和“不辣”需求冲突。
这一阶段修复了一个很关键的问题:原来系统会误删without egg / without peanuts / avoiding egg / safe for peanut allergy这类安全排除 query,反而保留 spicy/chili 相关 query,导致 “I want pasta” 被检索到辣味意面。修复后,英文 query 和过敏约束的检索效果明显更合理。
六、Evidence Pack:把检索结果分成 strong / conflict / weak
阶段六最重要的架构改进之一,是引入 retrieved evidence pack。
我不再让 FoodKG/vector 检索结果直接进入最终推荐,而是先把候选分成三类:
strong_candidates:强相关且相对安全的候选 conflict_candidates:与用户过敏、忌口、不辣需求等冲突的候选 weak_context:只能作为背景参考的弱相关内容例如用户问“不含鸡蛋和花生的甜味烘焙”,如果检索到了“花生酱曲奇”“鸡蛋小饼干”,这些不能因为语义相似就被推荐,而应该进入 conflict_candidates。
这个设计解决了 RAG 脏召回的问题。系统允许检索结果不完美,但不允许冲突结果直接污染最终回答。
阶段六后期形成的设计原则是:
RAG 负责召回,不保证干净; Evidence Pack 负责分桶; LLM 负责最终推荐; Validator 负责硬边界。七、LLM-led Recommendation:让 LLM 做最终推荐判断
早期系统还有很多固定模板,例如“不辣推荐”会自动生成“清蒸或白灼类、炖汤或清汤类、清炒类”等方向。这种做法虽然安全,但回答很死板,也不像真正的美食推荐。
后来我将推荐链路改为 LLM-led recommendation。FoodKG 和向量检索只提供证据和风险线索,最终推荐由 LLM 结合用户需求进行判断。
在 daily_recommendation 模式下,系统允许 LLM 在 FoodKG 候选不足时补充常见合理菜品。例如:
用户问:推荐几个不辣的家常菜 系统可以结合 FoodKG 候选“鱼丸萝卜汤”,再补充蒜蓉炒青菜、冬瓜排骨汤、清蒸鱼等常见菜。但是,系统仍然保留硬边界:
不能推荐 conflict candidates 不能把过敏冲突说成安全 不能声称 KG 外建议来自 FoodKG / 图谱 / 数据库 / 检索 菜单咨询模式不能推荐当前菜单外菜品这使得回答既自然,又不会失控。
八、菜单咨询模式的边界控制
菜单咨询是阶段六中特别重要的场景。用户在当前菜单里问问题时,系统必须只基于当前菜单回答。
例如:
这个菜单里推荐几个不辣的系统会从当前菜单中推荐金枪鱼 poke 碗、三文鱼 poke 碗、素食豆腐 poke 碗等不辣选项,并提醒确认酱汁是否含鸡蛋或花生。
而当用户问:
这个菜单里有 pasta 推荐吗?如果当前菜单没有意面类菜品,系统会明确说明没有,而不是推荐菜单外的番茄意面、蘑菇意面或隐藏菜单。
这个边界控制保证了菜单咨询模式的可信度。
九、阶段六的最终成果
阶段六最终完成的不是一个普通聊天框,而是一条比较完整的 AI 菜单问答主链路:
Flutter 双入口 → FastAPI /chat/message → 用户偏好、过敏、忌口、菜单上下文 → chat_mode / answer_scope 判断 → LLM Query Expansion → Query Sanitization / Rewrite → FoodKG / Vector Retrieval → Evidence Pack 分桶 → Structured Facts → Controlled LLM Answer → Validator Hard Boundary → Post-process Clean → 前端结构化展示最终系统具备了以下能力:
能区分“当前菜单咨询”和“日常饮食推荐”。
能结合用户过敏、忌口和辣度偏好。
能使用 FoodKG / 向量检索作为证据来源。
能识别并过滤冲突候选。
能允许 LLM 在安全边界内自然推荐。
能避免 KG 脏数据直接污染最终答案。
能输出更自然、更像菜单助理的回答。
能通过 trace 字段追踪回答路径和检索过程。
十、修改价值
这个阶段的核心价值在于:系统不是简单地把问题丢给大模型,而是将大模型、知识图谱、向量检索和用户个性化信息结合起来,形成了一个可控问答系统。
可以概括为:
大模型负责理解和表达; FoodKG 负责提供食材、菜品、过敏原和风险线索; 向量检索负责扩展召回范围; 后端规则负责守住安全和范围边界; 前端负责清晰展示用户可理解的结果。这种设计比普通 LLM 聊天更适合真实点餐场景,因为它既保留了大模型的自然表达能力,又通过知识图谱和后端边界控制降低了幻觉和错误推荐风险。