开发日志(十五):智能菜单助手前后端联调踩坑记录
一、开发背景
在智能菜单助手项目开发过程中,前端和后端都已经完成了不少功能:
Flutter 页面搭建
FastAPI 接口开发
MySQL 数据持久化
Qwen 多模态菜单识别
RAG 问答服务
个人中心
收藏管理
偏好设置
但是功能分别完成,并不代表系统可以真正跑通。
实际联调时,我遇到了很多典型问题:
接口地址不一致
字段名称不匹配
token 没有正确携带
后端返回成功但前端解析失败
页面能显示菜品但 RAG 检索为空
依赖安装成功但运行时报错
这些问题看起来零散,但本质上都属于全栈项目中的链路一致性问题。
二、问题一:接口地址切换不彻底
1. 问题现象
后端已经新增了菜单处理接口:
/api/v1/menu/process
但是 Flutter 某些页面仍然调用旧接口:
/upload
结果表现为:
图片上传似乎成功
但没有进入新的菜单识别逻辑
也没有触发向量入库
RAG 问答无法使用
2. 原因分析
项目迭代过程中,旧接口和新接口曾经同时存在。
部分页面已经切换到新接口,但一些 service 文件、测试页面或重新上传逻辑仍然保留旧地址。
这导致系统表面能运行,但实际调用链路并不完整。
3. 解决方案
我统一检查了所有 Flutter 网络请求代码,将菜单上传相关接口统一为:
/api/v1/menu/process
同时确认:
请求方法是 POST
上传字段名与后端一致
返回结构与前端解析模型一致
这个问题说明,接口升级不能只改一个地方,而要全局检查调用入口。
三、问题二:字段名不一致导致解析失败
1. 问题现象
后端返回了 HTTP 200,说明请求成功。
但 Flutter 端仍然报错,页面无法正常显示菜单结果。
常见错误包括:
FormatException
type 'Null' is not a subtype of type 'String'
type 'String' is not a subtype of type 'List'
2. 原因分析
多模态模型返回的数据本身存在一定不确定性。
例如 tags 字段有时是数组:
"tags": ["不辣", "主食"]
有时可能被处理成字符串:
"tags": "不辣, 主食"
价格字段有时是数字,有时是字符串:
"price": 12.99
或者:
"price": "$12.99"
如果前端模型类写得过于严格,就很容易因为某个字段格式异常导致整个页面崩溃。
3. 解决方案
我在后端增加了字段标准化处理,尽量保证返回结构稳定。
同时在 Flutter 端也增加了容错解析:
字符串统一 toString()
数组字段先判断类型
字段为空时使用默认值
中文响应使用 UTF-8 解码
这样即使某个字段为空,也不会影响整个页面显示。
四、问题三:页面有数据,但 RAG 问答没结果
1. 问题现象
这是开发 RAG 时最典型的问题之一。
Flutter 结果页可以正常显示识别出的菜品,但用户提问时,后端返回:
没有找到相关菜单内容
2. 原因分析
这个问题说明菜单展示链路是通的,但 RAG 知识库链路没有通。
可能原因包括:
识别结果没有写入 Chroma
Document 构造为空
Embedding 模型没有成功调用
Chroma 路径不一致
入库时 menu_id 和检索时 menu_id 不一致
检索过滤条件错误
也就是说,页面看到菜品并不代表向量库中有菜品。
3. 解决方案
我在后端增加了分阶段日志:
识别菜品数量
构造 Document 数量
向量入库开始
向量入库完成
当前 menu_id
检索结果数量
通过日志逐层定位后,保证菜单识别成功后立即执行向量入库。
完整链路应该是:
识别成功
→ JSON 标准化
→ Document 构造
→ Embedding
→ Chroma 入库
→ 检索成功
→ LLM 生成回答
五、问题四:token 鉴权遗漏
1. 问题现象
部分接口在 Postman 中测试正常,但 Flutter 中调用失败。
后端返回:
401 Unauthorized
或者:
用户未登录
2. 原因分析
这是因为某些请求没有携带登录 token。
个人中心、偏好设置、收藏管理、RAG 问答等功能都需要知道当前用户是谁。
如果前端没有在请求头中携带:
Authorization: Bearer token
后端就无法获取用户身份。
3. 解决方案
我将网络请求进行封装,统一添加 token。
这样各个页面不需要重复写 header,也可以减少遗漏。
同时在 token 失效时,前端应该跳转到登录页,而不是让页面一直加载。
六、问题五:虚拟环境依赖不一致
1. 问题现象
已经安装了依赖,但运行项目时仍然报错:
ModuleNotFoundError
尤其是在安装 Chroma、LangChain 相关依赖时比较常见。
2. 原因分析
Windows 环境下可能存在多个 Python 解释器。
例如:
全局 Python
项目 venv
VS Code 选择的解释器
PowerShell 当前解释器
如果依赖安装到了一个环境,而服务运行在另一个环境,就会出现“安装了但找不到”的问题。
3. 解决方案
统一使用:
python -m pip install xxx
而不是直接使用:
pip install xxx
并通过以下命令确认解释器:
python -c "import sys; print(sys.executable)"
python -m pip show chromadb
最终保证安装依赖和启动服务使用的是同一个虚拟环境。
七、问题六:前端状态没有及时刷新
1. 问题现象
用户在收藏页面取消收藏后,返回个人中心时收藏数量没有变化。
或者用户在菜单页收藏菜品后,进入个人中心统计仍然是旧数据。
2. 原因分析
这是典型的前端状态同步问题。
收藏操作已经成功写入后端,但当前页面仍然显示旧的本地状态。
3. 解决方案
我在页面返回时重新请求数据。
例如:
进入收藏页
→ 用户修改收藏
→ 返回个人中心
→ 重新加载收藏数量
这种方式虽然简单,但能保证数据准确。
后续如果项目规模继续扩大,可以进一步使用统一状态管理方案。
八、联调过程中的经验总结
通过这次联调,我总结出一个排查顺序:
1. 先看接口地址是否正确
2. 再看请求方法和字段名
3. 检查 token 是否携带
4. 检查后端日志是否进入目标函数
5. 检查数据是否成功写入数据库或向量库
6. 检查返回结构是否符合前端解析
7. 检查页面状态是否及时刷新
不要只盯着最终报错。
全栈项目的问题往往不是单点错误,而是多个模块之间的数据没有对齐。
九、我的主要工作
这一阶段我主要完成了:
1. 统一 Flutter 上传接口;
2. 修复前后端字段不一致问题;
3. 增加菜单识别结果容错解析;
4. 联调 RAG 入库与检索链路;
5. 修复 token 鉴权遗漏;
6. 处理虚拟环境和依赖问题;
7. 优化收藏状态同步;
8. 增加错误提示和加载状态;
9. 提高页面健壮性。
这些工作虽然不像新功能一样直观,但对项目最终能否稳定运行非常关键。
十、技术难度体现
这个阶段的难点不在于单个页面或单个接口,而在于系统链路长。
一次菜单问答需要经过:
Flutter 页面
→ HTTP 请求
→ FastAPI 路由
→ 用户鉴权
→ 菜单数据
→ 向量库
→ Embedding
→ LLM
→ JSON 返回
→ Flutter 展示
任何一层数据格式不一致,最终都会导致功能失败。
因此,联调阶段真正考验的是工程整合能力。
十一、总结
这次联调让我认识到,AI 项目的复杂度不只是模型调用。
真正困难的是让:
前端
后端
数据库
向量库
大模型
用户系统
全部稳定协作。
最终,通过逐层排查和接口统一,项目从“单个功能能跑”推进到了“完整业务链路能跑”。
这也是智能菜单助手从实验功能走向完整应用的重要一步。
更多推荐

所有评论(0)