一、开发背景

在智能菜单助手项目开发过程中,前端和后端都已经完成了不少功能:

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 项目的复杂度不只是模型调用。

真正困难的是让:

前端
后端
数据库
向量库
大模型
用户系统

全部稳定协作。

最终,通过逐层排查和接口统一,项目从“单个功能能跑”推进到了“完整业务链路能跑”。

这也是智能菜单助手从实验功能走向完整应用的重要一步。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐