一、目标阶段六的核心目标是在已有美食知识图谱和菜单识别功能的基础上进一步构建一个能够真实服务用户点餐和饮食推荐的 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 聊天更适合真实点餐场景因为它既保留了大模型的自然表达能力又通过知识图谱和后端边界控制降低了幻觉和错误推荐风险。