很多餐食类 App 的 AI 功能,最后都停在"给我推荐几道菜"。SoloChef 想做的更像一个独居生活规划助手:用户说出预算、忌口、营养目标和本周安排,系统不只返回菜名,还要继续推导出购物清单、采购分类、预算分配,最后把结果变成一份可以打卡、反馈、继续修改的周计划。
这篇文章想回答一个更底层的问题:当你手里只有大模型这个"会聊天的脑子"时,怎么把它变成一条真正能干活、能落地、还能持续变聪明的业务链路?答案就是 LangGraph 负责"编排流程",RAG 负责"给模型喂对的上下文",再外面套一层"结构化产出 + 确定性校验"的工程外壳。
1. 项目概述
1.1 项目定位
SoloChef — AI 独居膳食与采买规划师。它的核心不是"推荐菜谱",而是把"这周预算多少、不能吃什么、想吃得快一点"这种模糊的生活问题,翻译成可执行的业务结果:一份排好七天、带购物清单和预算分配的周计划。
这里有个关键认知差异:如果只是"推荐几道菜",一个会聊天的模型加几句 prompt 就能糊弄出来;但要做成"规划助手",模型必须和数据库里的用户画像、营养目标、历史反馈联动,产出还必须能被程序校验、存储、修改和打卡。这就不是 prompt 工程能解决的,需要一套完整的工程架构。
1.2 目标用户
独居自炊人群,按目标细分三类:增肌 / 减脂 / 健康维持,并兼顾性别差异(男女基础代谢不同,营养目标算法也不同)。
1.3 项目规模
前后端分离的单仓库:后端 FastAPI(约百个 Python 文件),前端 Vue 3 单页应用(10 个主页面);基础设施四件套(后面会讲);一条贯穿"生成 → 执行 → 反馈 → 再生成"的 AI 工作流。
2. 系统架构
2.1 整体架构
前端(Vue3/Vite) ──HTTP/SSE──> 后端(FastAPI) │ ┌────────────┬──────────┼────────────┬──────────┐ PostgreSQL Redis 7 Neo4j 5 Milvus 2.4 Celery (业务数据) (队列/ (用户画像/ (向量知识) (后台任务) 缓存/SSE) 关系知识) │ LangGraph 工作流 (检索→领域智能体→规划器→校验器)2.2 后端架构:LangGraph 到底解决什么问题
后端负责认证、业务数据、AI 工作流与基础设施适配。AI 部分由LangGraph编排——这是整篇文章的技术主轴,值得先讲透。
如果你没接触过 LangGraph,可以把它理解成一个"带记忆的流程图引擎"。传统的 chatbot 是一问一答,大模型接到问题直接回答;但 SoloChef 要的是一条多步骤、有状态、能回退、能恢复的流水线。LangGraph 用一张StateGraph(状态图)把"检索 → 领域智能体 → 规划器 → 校验器"这些节点串起来,每个节点都能读写一份共享的"工作记忆"(WorkflowState)。
为什么不直接写一串 Python 函数顺序调用?因为顺序调用会丢失几样东西:
- 可恢复性:流程跑到第 3 步崩了,能不能从第 3 步接着跑,而不是从头来?LangGraph 的checkpointer(检查点)把每一步的状态持久化到 PostgreSQL / Redis / 内存,崩了能续。
- 可观测性:每个节点花了多少时间、输入输出是什么、有没有报错,都能被记录成"轨迹",前端能画出来给用户看。
- 可控的循环:校验器发现计划不合规,可以"回退"让规划器重来,这种带环的有向图用普通线性代码很难优雅表达。
所以 LangGraph 在这里不是炫技,而是把"一个会出错的复杂 AI 流程"变成"可调试、可恢复、可审计"的工程对象。
2.3 前端架构
Vue 3 + TypeScript + Vite + Pinia + Element Plus。状态用 Pinia 管理,API 调用走 axios,图表用 ECharts。前端的角色不是"展示一段 AI 文本",而是把 AI 的产物做成可交互的产品(这一点在第 5 节展开)。
2.4 数据流:一条会自我喂养的闭环
用户画像 / 营养目标 / 历史反馈 → 注入工作流共享状态 → 双路 RAG 检索 → 多领域智能体产出结构化建议 → 规划器合成完整计划 → 校验器把关 → 落库为周计划 → 前端看板展示 → 用户打卡反馈 → 回流口味画像 → 下一轮生成更懂你。
注意最后那一步"回流"——这是它和普通推荐系统最大的区别:它会越用越懂你,而且懂你的方式不靠模型"自觉",靠的是结构化数据的累积。
3. 技术栈
3.1 前端技术栈
Vue 3.5、TypeScript 5.8、Vite 7、Pinia 3、Element Plus 2.14、vue-router 4、axios、ECharts、Vitest(单测)。
3.2 后端技术栈
Python + FastAPI、Pydantic(数据校验与结构化输出核心)、SQLAlchemy 2.x(异步AsyncSession+asyncpg驱动)、PyJWT(HS256 鉴权)、Uvicorn。
3.3 基础设施
| 组件 | 选型 | 用途 |
|---|---|---|
| 主数据库 | PostgreSQL 16 | 14 张业务表持久化(用户、计划、购物、反馈等) |
| 缓存 / 队列 / 实时 | Redis 7 | Celery 任务 broker、SSE 事件重放、checkpointer 缓存 |
| 图数据库 | Neo4j 5 Community | 用户画像、忌口、食材/菜谱之间的"关系"知识 |
| 向量数据库 | Milvus 2.4 | 文档知识的稠密+稀疏双路向量检索 |
3.4 AI 与第三方服务
- LangGraph:AI 工作流编排(StateGraph + checkpointer)。
- LangChain 的
ChatOpenAI接口:模型调用的兼容层。默认LLM_PROVIDER=demo(零密钥也能跑起来演示),配置真实密钥后接 DeepSeek 等任意 OpenAI 兼容模型。 - BGE-M3 / bge-reranker-v2-m3:Embedding(把文字变成向量)与二阶段精排模型。
- Qwen-VL(DashScope):多模态视觉模型,默认关闭。
- Tavily:可选的联网搜索工具。
4. 核心:AI 规划链路是怎么跑通的
这一节是全文重点。我会从一个"外行视角"讲清楚:当用户点下"生成周计划"时,系统内部到底经历了一遍什么。
4.0 先建立大图景
一次POST /api/v1/plans/generate-weekly请求进来后,系统不会直接把问题丢给大模型。它先做一件很重要的事:从数据库把"用户上下文"捞出来——忌口过敏、营养目标(折算成每天的热量/蛋白/碳水/脂肪)、最长备餐时间、可用厨具、历史打卡形成的口味偏好、增肌还是减脂。
这些上下文被放进前面说的"工作记忆"里,然后才启动那条 LangGraph 工作流。关键点在于:用户画像不是塞进一段 prompt 就完事,而是进入后续所有环节的共享状态——检索要用它过滤,智能体要用它约束,校验器要用它核对。这一步决定了后面所有环节都能"对齐用户真实情况"。
4.1 RAG 检索:为什么"只做向量相似度"不够
先给没接触过的人补一句:RAG(检索增强生成)就是"先去知识库里翻出相关内容,再把内容和问题一起喂给模型,让它基于事实回答",而不是让模型凭空编。对餐食规划来说,模型本身不知道"这个用户不吃辣"、“工作日晚餐要控制在 20 分钟内”,这些必须靠检索补进来。
SoloChef 的 RAG 不是"只做向量相似度搜索",而是拆成两条并行路径,由工作流用并发机制同时触发、各自设超时:
第一条:向量检索(Milvus)
文档(菜谱原则、营养知识)先被切成带重叠的小块(chunk),每块通过 Embedding 模型变成向量存进 Milvus。这里有个进阶点:用的BGE-M3模型能同时产出两种向量——
- 稠密向量:捕捉语义,"番茄炒蛋"和"鸡蛋料理"语义相近;
- 稀疏向量(lexical):类似传统关键词加权,"不吃辣"这种硬词权重大。
Milvus 用hybrid_search(混合检索)同时跑这两路,再用RRF(Reciprocal Rank Fusion,倒数排名融合)把两路结果按排名融成一个列表。召回之后还有一道二阶段精排(rerank):先用 Embedding 模型召回top_k × 3个候选,再用bge-reranker-v2-m3这种专攻"相关性排序"的模型精排回top_k。这就像先海选再面试,比一次到位更准。
第二条:图谱检索(Neo4j)
向量检索擅长"找相似内容",但表达不了"用户不吃辣"这种硬关系。于是 SoloChef 另开一条知识图谱路径:把用户偏好、忌口、食材、菜谱之间的关系存进Neo4j 图数据库(实体 + 关系)。查询时,系统先把自然语言"改写"成结构化的查询规格(关键词 / 实体类型 / 关系),再用图数据库查询语言Cypher去遍历"用户约束 → 食材 → 菜谱"的关系链。
为什么要按事实类型拆分两路?这是这个项目最值得记的一课:
- 偏好、忌口、过敏——是确定性关系,放图谱,查出来就是铁律;
- 菜谱、营养原则——是软性知识,放向量库,按相似度召回。
两路结果在上下文里被明确分开成"图谱硬关系"和"向量软知识",模型既能看到"用户不吃辣"这种硬约束,也能参考"工作日晚餐控 20 分钟内"这种文档建议。
工程兜底:任一路检索失败(比如图库没起来),另一条照常工作,整条计划流程不会被打崩;RRF / rerank 模型缺失时自动回退纯稠密检索。这种"降级不中断"的思想贯穿整个 AI 栈。
4.2 领域智能体:让专家各管一摊,而且"说人话以外的语言"
检索完成后,工作流会并行启动三个各管一摊的专家智能体:一个管餐食搭配(排除忌口、控制烹饪时间),一个管采购清单(食材标准化、同类合并、分类),一个管预算分配(上限、分类限额、预留金、预警)。
这里有两个对非技术读者也很重要的设计:
第一,它们的产出不是自然语言,而是结构化数据对象。
三个专家返回的不是"我觉得可以这样买"这种话,而是符合Pydantic / JSON Schema的规整数据(比如"购物项列表"“预算分配表”)。为什么这么关键?因为后面的规划器和校验器是程序,它们要直接读字段、做计算、存数据库——如果模型吐一段自由文本,程序就得去"猜"模型到底想表达什么,既慢又不可靠。让模型输出结构化数据,是把 AI 接进工程系统的前提。
第二,它们有"双层实现"。
默认情况下,这些专家不走大模型,而是走确定性规则引擎(比如预算预留 10%、分类限额之和严格等于周预算,用纯代码算)。只有开启开关后才调用大模型、并只允许它用"只读工具"去查知识库和用户资料。这个设计的现实考量很朴素:大模型贵、慢、偶尔抽风;能用确定性规则稳定解决的事,就别花那个钱。能力与成本按开关分层,是这类项目能落地的基本纪律。
另外,这些专家的"提示词"不是散落在代码各处,而是集中在一个版本化注册表里,对外提供接口可查"当前用了第几版、改了什么"。这就是"提示词即代码"——提示词也是要被审计、能回滚的资产。
4.3 主规划器:强制结构化输出
三个专家的建议加上 RAG 检索到的上下文,一起交给规划器合成完整计划。真实模型路径下,模型被强制绑定response_format={"type":"json_object"}——这是 OpenAI 兼容接口的标准能力,意思是"你必须返回合法 JSON,不许夹带 Markdown 代码块或多余解释"。
系统还对产出施加了硬性结构约束:
- 每周必须生成 7 天 × 早/午/晚 =21 个餐食槽位,一个不能少;
- 每个餐食必须有明确的类型标记;
- 金额、时长等字段必须符合规范。
为什么这么"霸道"?因为这份计划接下来要被存储、展示成看板、被人修改和打卡——它必须是程序能直接消费的数据,而不是一段文章。把"结构化"从"建议"升级为"强制",是 AI 应用能真正跑业务的底线。
4.4 校验器:模型负责提方案,确定性代码负责把关
这是整套系统里我个人最欣赏的设计。核心理念一句话:模型可以犯错,但规则不能放过它。
规划器产出的计划会交给校验器,由纯确定性代码逐项核对:忌口过敏有没有碰、七天是不是都覆盖了、有没有重复菜、营养目标达成没、预算和分类限额超没超。发现的问题被分成两级:
- hard conflict(硬冲突):碰了忌口、超了预算、营养严重不达标——必须解决;
- soft conflict(软冲突):某天菜重复了、某餐偏咸——尽量优化。
校验失败后,系统不是简单甩一句"生成失败",而是走三级自愈策略:
- 自动修正:换掉重复菜、补齐缺失日期、调整营养或高价菜——但有一条铁律:可以换菜,绝不能擅自放宽过敏、忌口、预算和营养目标。
- 降级提示:硬冲突或自动修正后仍存在的问题,生成"可供用户选择的替换方案"返回前端。
- 人工接管:当硬冲突占餐数超过 30% 时,明确提示用户"你的条件太苛刻了,放宽一点吧"。
更进一步,系统还支持一个"主管(Supervisor)"角色:当校验器发现某类问题,它只把受影响的专家重新调度一遍,而不是把所有环节从头跑——而且受轮次上限约束,避免多个智能体互相调用失控。
这种"LLM 生成 + 规则验证"的组合,比单纯依赖"把 prompt 写得更长更狠"可靠得多,尤其适合"错误会真实影响你去采购、去吃饭"的场景。
4.5 反馈闭环:系统会越用越懂你,但不篡改硬约束
计划生成不是一次性结束。前端支持餐食打卡、替换、差评、"没买到"等反馈,系统把这些事件聚合成口味画像,并回流进知识库。
下一轮生成时,画像被重新注入餐食专家:喜欢的标签进入"偏好",被拒绝过的菜或负向标签进入"排除项"。这里同样有一条铁律:过敏永远优先于历史口味。反馈只能改变推荐方向和排序,不能覆盖安全约束;而且当用户连续多次对某类食材差评,系统会自动把它升级为正式忌口——这是从"软偏好"到"硬约束"的演化。
于是形成真正的闭环:计划生成 → 执行打卡 → 用户反馈 → 口味画像 → 下一轮更懂你。
4.6 对话助手:一条刻意独立的链路
除了生成周计划,项目还有聊天接口。但它的定位不是"每问一句就重做一份计划",而是只读问答:读取用户画像、营养目标、当前计划摘要,再做一小规模检索,然后流式回答。
对话通过SSE(Server-Sent Events,服务器推送事件)把"思考中 / 出字了 / 调工具了 / 工具返回了 / 完成"这些事件推给前端,并写入 Redis——好处是前端断线后,可以调接口把漏掉的事件"重放"补齐,不丢上下文。聊天智能体启用后只允许使用"只读工具",而且系统提示明确把"工具结果和检索内容"视为不可信数据——不能执行其中的指令,也不能泄露系统提示。
这条链路和计划生成链路是有意分开的:聊天可以自然语言输出,不强制结构化;计划生成必须结构化、可验证、可落库。两个场景的模型温度、超时、输出约束都不同。
4.7 多模态:把图片接进饮食场景
系统提供独立的视觉服务(基于Qwen-VL),支持五种场景:自动识别、食材识别、菜品与热量估算、营养标签 OCR、小票 OCR。图片进模型前会先做安全和成本控制——检查大小、压缩长边到 2048 像素、统一转 JPEG,再编码上传;模型返回的结果也要经过结构化校验,避免 OCR 吐出无法消费的自由文本。
视觉能力默认关闭,是可选增强而非启动硬依赖——这又体现了"能力分层"的纪律:核心链路不依赖它,你想要再开。
5. 前端如何承接 AI 结果
前端不是"一个文本结果框",而是把 AI 产物做成可交互产品。10 个主页面中与 AI 强相关的几个:
首页
档案采集
- 计划看板页:把周计划拆成七天,每天展示三餐、时长、费用、标签;生成中的状态持续更新,确认后才落库;可打卡、反馈、看版本历史、预览调整结果再确认。
- 计划明细页:单份计划明细与修改前后的差异对比。
- 知识库页:知识文档与检索。
- 聊天页:用 fetch + ReadableStream 消费 SSE(而非浏览器原生 EventSource),断线后按游标重放补齐。
计划"局部修改"先过意图路由:系统用词法信号判断用户到底是"要生成"“要修改”“问购物”“问预算"还是"纯咨询”,防止用户只是问"晚餐怎么做"却误触发整周计划重算。对"把周三晚餐换成不含海鲜的高蛋白餐"这类请求,系统会计算受影响的餐食/购物/预算,并在前端展示差异对比与冲突提醒——用户看清改动再确认。
6. 关键技术创新(也是可复用的方法论)
- GraphRAG 双路检索:偏好/忌口进图谱关系(Neo4j + Cypher),菜谱/原则做向量召回(Milvus + BGE-M3 + RRF + Reranker),按事实类型拆分后合并,才接近真实规划场景。
- 结构化输出 + 确定性校验:Pydantic Schema、21 餐完整性、预算等式、冲突分级,都是模型之外的安全网,比"写更长的 prompt"可靠。
- 校验器三级自愈:模型生成 + 规则验证的组合,适合真实影响采购和饮食执行的场景。
- SSE 流式对话 + Redis 事件重放:断线不丢上下文,体验连贯。
- 全栈降级:图谱/向量任一路失败保留另一路;Reranker/BGE-M3 缺失回退纯稠密;真实 LLM 超时切 Demo 规划器;视觉/对话未配置返回明确"未启用"。
7. 开发中的挑战与解决方案
这些不是教科书里的理论,而是真实踩过的坑:
- 模型不稳定 / 没有 API 密钥:代码默认
LLM_PROVIDER=demo,演示模式保留完整工作流与校验,只把昂贵的外部模型调用替换为本地确定性实现;真实 LLM 超时按开关切回兜底方案。这样没密钥也能跑、也能演示、也能测流程。 - RAG 底座可能不可达:双路并发 + 各自超时,任一路失败保留另一路;RRF / rerank 缺失自动回退纯稠密检索,链路不中断。
- "模型自觉"不可信:再长的 prompt 也拦不住模型偶尔放松约束,所以用校验器三级自愈替代纯 prompt 约束——过敏/忌口/预算/营养目标绝不靠模型"自觉"。
- SSE 断线丢上下文:事件写入 Redis,前端断线后按游标重放补齐。
- 意图误触发整周计划:用意图路由做词法分类,区分生成/修改/购物/预算/咨询,防止一句闲聊误重算整周。
- 外部依赖成本与耦合:领域专家默认确定性规则、主管限轮次、视觉/联网搜索默认关闭——能力按开关严格分层,最小成本起步。
8. 项目成果
- 完整 AI 规划闭环:从一句自然语言,到 21 个餐食槽位 + 购物清单 + 预算分配 + 可修改版本,真正打通"自然语言 → 业务结果"。
- 可观测:每次工作流运行都会记录节点轨迹(名称、耗时、状态、输入输出摘要、错误),前端可展示完整 Agent Trace,后端提供接口可查。出问题时有迹可循,而不是"模型抽风了但我不知道哪步抽的"。
9. 收获与展望
9.1 项目开发收获
- AI 应嵌入业务流程,而不是一个孤立聊天框。
- 结构化输出 + 确定性校验,比"写更长的 prompt"可靠。
- RAG 要按事实类型拆分(图谱关系 vs 向量召回)。
- 默认能力与增强能力必须分层(Demo / 真实 LLM / BGE-M3 / Reranker / Supervisor / 联网搜索 / Qwen-VL / 分段规划都由开关控制)。
9.2 未来展望
真实领域专家的调用成本需继续评估(Supervisor / 联网搜索仍是可选能力);Reranker 与稀疏检索依赖额外本地模型权重;视觉识别需独立 VLM 配置;意图路由目前更多由前端弹窗承接,尚未全部接入 API。后续可做流式计划生成、更细的用户级图谱隔离、Agent 运行恢复、检索质量评测与成本监控——现有数据结构、路由和前端页面已预留扩展点,不必推翻架构。
总结
SoloChef 的核心价值,不是"模型能推荐多少道菜",而是把一个模糊的生活问题,转成一条可追踪、可校验、可执行、还能持续学习的 AI 工作流。
如果你也正打算用大模型做点"能落地"的东西,希望这篇复盘能帮你建立一个最朴素的判断框架:LangGraph 负责把流程编排成可恢复、可观测的图;RAG(尤其是 GraphRAG 双路)负责给模型喂对的上下文;结构化输出和确定性校验负责把 AI 从"会聊天的脑子"变成"能干活的系统"。这三者合起来,才是一个 AI 应用真正值得长期维护的部分。