# AI 美食智能推荐与菜谱生成 Agent 平台 - 功能说明
# 项目演示
# 项目概述
本系统是一个以真实大模型驱动的美食智能推荐与菜谱生成 Agent 平台,采用 Spring Boot 3.2.10 + MyBatis + MySQL 后端与 Vue 3 + Vite + Element Plus + Pinia 前端的前后端分离架构,通过 JWT 完成登录态校验,共 14 张数据表,面向管理员与普通用户两类角色:普通用户用自然语言描述手边食材、口味、忌口与可用时长,由 Agent 自主调用工具链检索美食知识库、查询食材营养、生成并入库菜谱、计算营养与规划一周食谱;管理员负责食材与菜谱维护、菜谱审核、知识库解析入库与向量库维护、Agent 工具与模型通道配置、对话与调用日志观测。系统不引入 Spring AI / LangChain,使用 JDK 17 原生 HttpClient + fastjson2 手写轻量客户端对接阿里云百炼 (DashScope) OpenAI 兼容接口,流式输出、工具调用、向量检索与多模态识别全部为真实调用结果
# 项目概览
# 创新点
# 1. 真实大模型 + 真实工具调用的 ReAct Agent
- 不接第三方 Agent 框架:对话
qwen-plus、视觉qwen-vl-max、向量text-embedding-v3三个模型通道全部由 JDK 17HttpClient手写调用POST {base_url}/chat/completions与POST {base_url}/embeddings,接口协议完全对齐 OpenAI 兼容规范,每一步都可解释 - 6 个真实工具:
search_knowledge知识库检索、query_ingredient食材查询、search_recipe菜谱检索、save_recipe菜谱入库、calc_nutrition营养计算、plan_week_menu菜单规划,工具定义存agent_tool表并可在后台启用 / 禁用,执行逻辑在 JavaToolRegistry中按code分发 - 多轮 ReAct 循环:最多 5 轮 (
ai.max-tool-rounds),流式下按index累积delta.tool_calls分片并逐段拼接function.arguments,工具执行完以role=tool+tool_call_id回填后再发起下一轮,直到finish_reason=stop
# 2. 真实向量检索的美食知识库 (RAG)
- 切片与向量化:上传
.txt / .md / .pdf文档后异步解析,按段落聚合为约 500 字一片、相邻片段重叠 80 字,分批 (每批不超过 10 条,接口上限) 调用/embeddings得到 1024 维真实向量,以 JSON 形式存入knowledge_chunk.embedding - 内存余弦召回:查询向量与所有
status=已向量化的片段计算余弦相似度,按similarity_threshold(默认 0.35) 过滤后取 topK (默认 5),维度不一致的片段自动跳过并提示重建 - 召回结果可验证:后台「向量库管理 - 召回测试」输入自然语言问题即可查看命中片段、来源文档与相似度数值,直观证明是真实向量召回而不是关键词匹配
# 3. SSE 流式输出 + 思考过程 + 工具链可视化
- 打字机式 Markdown:
POST /chat/send以text/event-stream逐步推送,前端用fetch+ReadableStream+TextDecoder逐行解析 (不用EventSource,因为它无法携带 JWT 请求头),并增量渲染 Markdown - 9 类 SSE 事件:
start(会话 ID 与模型名)、reasoning(思考链增量)、tool_call(工具名与参数)、tool_result(耗时与结果摘要)、message(正文增量)、recipe(菜谱卡与营养)、usage(Token 与费用)、done(结束)、error(中文错误提示) - 可随时停止生成:前端通过
AbortController中断请求,后端捕获客户端断开后置中止标记并关闭上游连接,同时把已生成内容落库并标记status=失败
# 4. 多模态美食图片识别与菜谱反查
- 真实视觉模型:图片经
/file/upload上传后,后端读取本机文件转成data:image/xxx;base64,...提交给视觉模型,输出dishName/confidence/ingredients/cuisine/taste/tips结构化 JSON - 两级反查算法:先按
dishName与菜谱名称互相包含打分 (加 10 / 加 5),再按识别出的食材在recipe.ingredients中的命中数加分 (每项加 2),只取审核通过的菜谱并按匹配分降序返回前 6 条 - 不夸大识别能力:模型返回非 JSON 时回退为整段文本展示,
confidence为模型自评值,前端如实展示「识别置信度」
# 5. 营养计算的统一口径与可视化
- 统一计算口径:食材按「克数 / 100 × 每 100 克营养」累加,菜谱按 1 份营养值累加,三大营养素供能占比按蛋白质 4、碳水化合物 4、脂肪 9 (千卡/克) 计算,Java 侧用
BigDecimal保留 1 位小数避免浮点误差 - 营养缺失自动补算:
save_recipe入库时若模型未给出热量或热量为 0,后端用食材库数据按克数反算热量、蛋白质、脂肪与碳水化合物写入菜谱,并在返回信息中告知模型 - 多处复用同一套口径:前台「营养分析」页、菜谱详情营养饼图、AI 对话菜谱卡与食谱计划每日热量汇总均基于同一套营养字段,数值互相一致
# 项目亮点
# 一、 业务功能亮点
- 自然语言生成完整菜谱:用户只需描述「冰箱里只有番茄、鸡蛋,不吃辣,10 分钟能做什么菜」,Agent 自动完成知识库检索 → 食材营养查询 → 已有菜谱检索 → 生成并入库 → 营养计算,最终输出 Markdown 菜谱与菜谱卡
- 多轮对话持续调整:支持「少油一点」「改成烤箱版」「去掉香菜」这类追加要求,模型基于真实上下文重生成,菜谱卡与营养值同步变化
- 一周食谱一键规划:按天数 (默认 7 天,上限 30 天)、每日目标热量、口味与忌口自动配餐,三餐热量比约 3:4:3,逐日校验热量偏差 (超过 15% 会在返回信息中提示可手动调整),落库为
meal_plan+meal_plan_item - 菜谱审核闭环:AI 生成的菜谱以
source=AI生成、status=待审核入库,管理员审核通过后才进入公共菜谱列表、前台推荐与食谱计划配餐候选池
# 二、 技术实现亮点
- 技术栈版本明确:后端 Spring Boot 3.2.10 + MyBatis 3.0.4 + JDK 17 + MySQL Connector 8.0.31 + jjwt 0.9.1 + hutool 5.7.20 + PDFBox 2.0.29 + fastjson2 2.0.53;前端 Vue 3.4.29 + Vite 5.3.1 + Element Plus 2.8.4 + Pinia 2.2.2 + vue-router 4.4.3 + axios 1.7.5 + ECharts 5.5.1 + @kangc/v-md-editor 2.3.18
- 14 张数据表:
admin、user、ingredient、recipe、recipe_favorite、knowledge_doc、knowledge_chunk、ai_model_config、agent_tool、chat_session、chat_message、ai_call_log、meal_plan、meal_plan_item,其中ai_model_config与ai_call_log为 AI 支撑表 - 第三方集成全程可观测:每次真实调用 (对话 / 工具 / 向量 / 视觉) 都写入
ai_call_log,记录模型名、输入与输出 Token、费用估算、耗时、状态、错误信息与请求响应摘要 (超 2000 字截断存储),后台首页据此统计累计费用、调用成功率与平均响应耗时 - 密钥与数据权限双重保护:API Key 只保存在后端数据库或环境变量
DASHSCOPE_API_KEY,列表与详情接口一律返回脱敏值 (形如sk-****3f7a),前端留空提交表示不修改原 Key;数据可见范围在 Service 层控制,管理员不过滤,普通用户仅查询user_id = 当前用户ID,前端不传userId - 上游异常有兜底:429 限流与 5xx 错误自动重试 2 次 (退避 1s、2s),401 / 403 / 429 / 400 与 5xx 分别翻译成中文提示并保留上游错误原文写入
ai_call_log.error_msg;工具执行异常不抛出,转为错误 JSON 回填模型,避免整轮对话崩溃
# 三、 用户体验亮点
- 前台卡片式网格布局:菜谱大全、食材百科、我的收藏统一使用
el-row :gutter="20"+ 响应式el-col :xs="24" :sm="12" :md="8" :lg="6",卡片展示封面、名称、标签、用时热量与摘要,不使用表格 - 数据可视化到位:后台首页 4 个数字统计 (用户数 / 菜谱数 / 知识片段数 / 今日 Token 消耗) + 4 个 ECharts (近 7 天调用次数与 Token 消耗双轴折线、菜系分布饼图、菜谱难度分布柱状图、调用类型占比饼图) + 3 个附加卡片 (累计费用估算 / 调用成功率 / 平均响应耗时),个人数据概览页同样为 4 个统计 + 4 个图表 + 猜你喜欢
- 每一处 AI 输出都可追溯:
- 对话页展示模型名、思考过程折叠面板、工具调用卡片 (调用中 / 完成并附耗时、参数与结果摘要)、Markdown 正文、菜谱卡与四项营养、底部 Token 与费用
- 后台「对话日志」可查看会话列表 (用户 / 会话标题 / 消息数量 / Token 合计 / 创建时间) 与逐条消息明细,含思考链、工具调用链与关联菜谱
- 交互细节统一收敛:前台页面宽度 75% 居中、禁用渐变配色与卡片 hover 效果、列表中富文本步骤经去标签处理后展示且仅在详情页用
v-html渲染、所有列表连表展示用户名 / 菜谱名 / 文档名而不是裸 ID
# 管理员
# 平台总览
首页:查看用户数、菜谱数、知识片段数与今日 Token 消耗四项统计,以及近 7 天调用次数与 Token 消耗双轴折线、菜系分布、菜谱难度分布、调用类型占比四个 ECharts 图表,并读取累计费用估算、调用成功率、平均响应耗时三项 AI 汇总指标
# 账号管理
管理员管理:分页查询管理员,按用户名 / 手机号 / 状态搜索,新增、编辑、删除管理员,维护头像、昵称、电话、邮箱与启用状态,支持一键重置密码 用户管理:分页查询普通用户,按用户名 / 手机号 / 状态搜索,新增、编辑、删除用户,维护头像、昵称、电话、邮箱、口味偏好与忌口食材,支持一键重置密码
# 食材与菜谱
食材管理:食材 CRUD,覆盖食材名称、分类、图片、热量、蛋白质、脂肪、碳水化合物、保质期、储存方式、搭配禁忌、食用功效与简介,是食材百科、食材查询工具与营养计算的数据源
菜谱管理:菜谱 CRUD,覆盖菜系、分类、口味、难度、烹饪时长、份量、食材清单、富文本制作步骤、小贴士、封面与四项营养,连表展示来源与所属用户;仅 待审核 状态显示「审核」按钮,弹窗单选 审核通过 / 审核不通过 并填写审核回复
菜谱收藏:分页查询全部用户的收藏记录,按用户名与菜谱名称搜索,展示菜谱封面、口味、难度与收藏时间,支持单条删除
# 食谱计划
食谱计划:查看全部用户的食谱计划,按计划名称搜索,展示所属用户、起止日期、每日目标热量与备注,查看逐日三餐明细,支持编辑与删除 (删除计划同时删除其明细)
# AI 知识库
知识库文档:管理 RAG 原始文档,支持上传 .txt / .md / .pdf 文件、选择文档类型 (菜谱资料 / 食材百科 / 营养手册 / 菜系资料) 与填写简介,展示字符数、片段数量、向量模型、维度、上传人与失败原因;点击「解析入库」后状态按 待解析 → 解析中 → 已入库 (或 解析失败) 变化,页面自动轮询进度,另提供「重建向量」按钮
向量库管理:查看知识片段列表 (所属文档、片段序号、片段标题、内容折叠预览、Token 数、向量状态、维度、创建时间),支持删除片段,按文档范围执行「重建向量」覆盖 embedding,并可在「召回测试」中输入自然语言问题与召回条数,查看命中片段、来源文档与相似度
# Agent 配置
Agent 工具:查看 6 个 Function Calling 工具的名称、编码、说明与启用状态,按工具名称与状态搜索,弹窗查看完整参数结构 (JSON Schema),通过状态切换启用或禁用工具,被禁用的工具不会出现在请求体的 tools 参数中
模型通道配置:配置 BaseURL、API Key (脱敏回显,留空提交表示不修改)、对话模型、视觉模型、向量模型、向量维度、温度、max_tokens、检索条数 topK、相似度阈值、输入单价、输出单价、系统提示词、欢迎语与启用状态;点击「测试连接」会真实调用一次对话模型 (max_tokens=8) 与一次向量模型 (input 为「测试」),返回各自连通结果与耗时,并在 Key 未配置时给出中文提示而不报错
# 运行观测
对话日志:查看全部会话列表 (用户、会话标题、消息数量、Token 合计、创建时间),按会话标题搜索,进入「查看消息」可看该会话的逐条消息明细,包括思考过程、工具调用链与关联菜谱
调用日志:按调用类型 (对话 / 工具 / 向量 / 视觉)、状态 (成功 / 失败) 与模型名称搜索筛选,展示用户、输入 / 输出 / 总 Token、费用、耗时与错误信息,弹窗查看请求内容与响应内容摘要
# 普通用户
# 智能菜谱助手
智能菜谱助手:核心 AI 交互页,条件区可多选食材、选择口味、填写忌口食材、设置烹饪时长 (5 至 240 分钟,步长 5) 与期望难度;发送后以 SSE 增量渲染思考过程、工具调用卡片与 Markdown 菜谱正文,底部显示输入 / 输出 Token 与费用;支持多轮追加要求、AbortController 停止生成、上传美食图片做菜品识别,左侧展示历史会话列表并可新建对话
# 菜谱浏览
菜谱大全:卡片式网格浏览菜谱,支持按名称搜索、按口味与菜系筛选、按分类与难度单选,卡片展示封面、名称、难度标签、菜系 / 分类 / 口味标签、烹饪时长、热量、步骤摘要、来源与浏览量 菜谱详情:查看富文本制作步骤、食材清单、小贴士与营养分析饼图,记录浏览量,一键收藏或把该菜谱加入食谱计划 食材百科:卡片式网格浏览食材,按名称搜索、按分类筛选,卡片展示封面、分类、四项营养 (每 100 克)、保质期与食用功效,点击弹窗查看储存方式、搭配禁忌与简介
# 我的数据
我的收藏:卡片式网格展示本人收藏的菜谱,按名称与口味前端筛选,支持单卡取消收藏与勾选后批量取消收藏,点击卡片进入菜谱详情 我的食谱计划:新增与删除本人食谱计划,按日展示早餐、午餐、晚餐安排与每日总热量、每餐小计,支持「一键智能生成」按天数、目标热量、口味与忌口由 Agent 配餐并写入计划 营养分析:多选菜谱 (按 1 份) 并添加食材 (按克数),按真实营养字段累加出总热量、蛋白质、脂肪、碳水化合物与三大营养素供能占比饼图,并给出营养明细表 我的数据概览:查看我的对话数 / 我的收藏数 / 我生成的菜谱数 / 我的食谱计划数四项统计,以及我的收藏口味分布、我的收藏菜系分布、近 7 天我的对话次数、我的菜谱营养素供能占比四个图表,附猜你喜欢推荐
# 账号
个人信息:维护昵称、头像、手机号、邮箱、口味偏好与忌口食材 修改密码:校验原密码后设置新密码
# 功能权限对照表
| 功能模块 | 管理员 | 普通用户 |
|---|---|---|
| 首页 / 数据概览 | ✓ (全平台统计) | ✓(仅自己的) |
| 管理员管理 | ✓ | - |
| 用户管理 | ✓ | - |
| 食材管理 | ✓ | - |
| 食材百科 (浏览) | ✓(后台食材管理) | ✓ |
| 菜谱管理 (含审核) | ✓ | - |
| 菜谱大全 / 菜谱详情 | ✓(后台菜谱管理) | ✓(仅审核通过) |
| 菜谱收藏 | ✓(全部用户的收藏) | ✓(仅自己的) |
| 食谱计划 | ✓(全部用户的计划) | ✓(仅自己的) |
| 知识库文档 | ✓ | - |
| 向量库管理 / 召回测试 | ✓ | - |
| Agent 工具 | ✓ | - |
| 模型通道配置 / 测试连接 | ✓ | - |
| 对话日志 | ✓(全部会话) | - |
| 调用日志 | ✓ | - |
| 智能菜谱助手 (流式对话 / 图片识别) | - | ✓ |
| 营养分析 | - | ✓ |
| 个人信息 / 修改密码 | ✓ | ✓ |
说明:前台布局会检测登录身份,非 USER 会被跳转到 /admin,因此管理员的菜谱、食材、收藏与食谱计划浏览均在后台对应模块完成
# 数据状态说明
# 菜谱状态 (recipe.status)
待审核:新增或 AI 生成后的初始状态,此时不进入公共菜谱列表与前台推荐,后台列表显示「审核」按钮
审核通过:审核通过后在前台菜谱大全、菜谱详情、猜你喜欢、热门菜谱、图片识别反查与食谱计划配餐中可见
审核不通过:审核结果为不通过,审核回复写入 audit_remark 字段
说明:审核只允许在 待审核 状态下执行,且结果只能是 审核通过 或 审核不通过,其他取值会被拒绝
# 知识文档状态 (knowledge_doc.status)
待解析:文档新增后的默认状态,尚未抽取文本与向量化
解析中:已触发解析,页面轮询该状态;处于该状态时重复点击「解析入库」会被拒绝
已入库:文本抽取、清洗、切片与向量化全部成功,回写 char_count、chunk_count、embedding_model 与 dimensions 解析失败:任一步骤出错,失败原因写入 parse_error (如未抽取到文本、文档文件不存在、读取失败),已写入的片段保留可重试
# 知识片段向量状态 (knowledge_chunk.status)
未向量化:片段已创建但尚无向量,不参与召回 已向量化:已持有对应模型的真实向量,参与余弦相似度召回 向量失败:向量化未成功,可通过「重建向量」重新覆盖
# 通用状态
启用:账号、模型通道与 Agent 工具的正常可用状态;模型通道只有 启用 才会被取用,Agent 工具只有 启用 才会出现在请求体的 tools 参数中
禁用:账号被禁用后登录会被拒绝并提示「用户已禁用」;工具被禁用后模型无法调用该工具
# 调用与消息状态
成功:单次模型调用或工具执行成功
失败:单次调用失败,错误信息写入 ai_call_log.error_msg;对话消息在生成被中断或异常时同样标记为 失败,前端对应展示为「已停止生成」,已生成内容保留
# 默认账号
# 管理员
用户名: admin
密码: 123456
2
(昵称「管理员」,状态 启用,登录页身份选择「管理员」)
# 测试用户
用户名: user
密码: 123456
2
(昵称「普通用户」,口味偏好 清淡,忌口 香菜,状态 启用,登录页身份选择「用户」)
另有一条普通用户初始数据:用户名 xiaomei,密码 123456,昵称「美食小美」,口味偏好 香辣
# 核心业务流程
# 智能菜谱助手流式生成菜谱
1. 用户在条件区勾选食材 (如番茄、鸡蛋)、选择口味、填写忌口、设置烹饪时长与难度后发送消息
2. 后端先保存 user 消息,首条消息自动取前 20 字作为会话标题;组装 system 提示词 (含本次条件) + 最近 10 条历史消息 + 本次输入
3. 携带 agent_tool 表中启用工具生成的 tools 参数,调用 POST {base_url}/chat/completions (stream=true, stream_options.include_usage=true),最多循环 5 轮
4. 逐块解析增量:delta.reasoning_content 推送 reasoning 事件,delta.content 推送 message 事件,delta.tool_calls 按 index 累积
5. finish_reason=tool_calls 时依次推送 tool_call 事件、在 Java 侧执行工具、落库 assistant(tool_calls) 与 tool(tool_call_id) 消息、推送 tool_result 事件,随后回到循环顶部
6. save_recipe 执行成功时推送 recipe 事件 (菜谱 ID 与四项营养),前端渲染菜谱卡与营养饼图
7. finish_reason=stop 后保存 assistant 消息、累加会话消息数与 Token 合计、推送 usage 与 done 事件,前端刷新会话列表
2
3
4
5
6
7
# 知识库文档解析入库 (RAG 向量化)
1. 管理员在「知识库文档」上传 .txt / .md / .pdf 文件并选择文档类型,保存后文档状态为 待解析
2. 点击「解析入库」,接口立即返回,解析在单线程线程池中串行执行,避免并发打爆接口限流
3. 文档状态置「解析中」并清空旧片段与失败原因,页面按固定间隔轮询文档状态
4. 文本抽取:.txt / .md 按 UTF-8 直接读取,.pdf 用 PDFBox 按页抽取;未抽取到文本即判为解析失败并提示扫描件需先 OCR
5. 文本清洗:统一换行、全角空格转半角、去除行尾空白、压缩连续空行与行内空白
6. 切片:按段落聚合到约 500 字一片,相邻片段重叠 80 字,片段标题取段落首行前 50 字
7. 向量化:分批 (每批不超过 10 条) 调用 POST {base_url}/embeddings,把 usage 记入 ai_call_log,调用类型为 向量
8. 逐片段写入 knowledge_chunk (content、token_count、embedding JSON、embedding_model、dimensions、status=已向量化),回写文档字符数、片段数量与状态「已入库」
9. 任一步失败则状态置「解析失败」并记录上游错误原文,已写入的片段保留可重试
2
3
4
5
6
7
8
9
# 向量召回与召回测试
1. 输入自然语言问题 (如「番茄炒蛋火候」) 与召回条数
2. 调用 /embeddings 生成查询向量
3. 取出所有 已向量化 且 embedding 不为空的片段作为候选集,维度与查询向量不一致的片段跳过
4. 逐条计算余弦相似度,低于相似度阈值 (默认 0.35) 的丢弃,其余按相似度降序取 topK (默认 5)
5. 返回命中片段、所属文档与相似度数值,并把向量字段置空避免向前端传输约 10KB 的向量数据
6. 工具侧再把结果拼成「【知识片段 n|文档:xx|相似度 0.82】正文…」回填给模型作为生成依据
2
3
4
5
6
# 图片识别与菜谱反查
1. 用户在对话页上传美食图片 (页面提示建议 1MB 以内),图片经 /file/upload 存入 platform/uploads/ 并返回文件名
2. 后端读取本机文件转 Base64 Data URL (单张超过 4MB 直接拒绝),连同「识别这道菜品,输出 JSON」的指令提交给视觉模型 (max_tokens=512, temperature=0.2)
3. 解析模型返回的 JSON (自动去除代码块包裹),得到 dishName、confidence、ingredients、cuisine、taste、tips
4. 反查菜谱:只遍历 审核通过 的菜谱,按菜名互相包含加 10 / 加 5 分,再按识别出的食材在食材清单中的命中数每项加 2 分
5. 按匹配分降序返回前 6 条,前端展示识别结果卡片与匹配到的菜谱列表
6. 用户点「生成完整菜谱」进入正常 ReAct 流程;识别结果与图片一并记录到 chat_message,视觉调用写入 ai_call_log,调用类型为 视觉
2
3
4
5
6
# 一键生成一周食谱
1. 用户在「我的食谱计划」点击「一键智能生成」,填写天数 (默认 7,上限 30)、每日目标热量 (默认 1800 千卡)、口味与忌口
2. 后端调用 plan_week_menu 工具,筛选候选菜谱:仅 审核通过,且食材清单不含忌口食材
3. 口味匹配的菜谱优先作为菜谱池,口味不匹配时保留全部候选作为兜底;菜谱池为空时直接返回明确错误提示
4. 逐日三餐按 3:4:3 分配热量目标,优先采用模型给出的菜谱 ID 组合 (会校验是否在可用池内),缺失时按热量最接近原则挑选且当天不重复
5. 逐日统计实际热量与目标热量的偏差百分比,偏差超过 15% 的天数会在返回信息中提示可手动调整
6. 写入 meal_plan 与 meal_plan_item,前端展示逐日三餐安排与每日热量汇总
2
3
4
5
6
# 菜谱审核
1. AI 生成的菜谱由 save_recipe 工具写入 recipe 表,来源为 AI生成、user_id 为当前用户、状态为 待审核、浏览量为 0
2. 管理员在「菜谱管理」列表中看到 待审核 标签与该行专属的「审核」按钮
3. 点击「审核」弹窗,单选 审核通过 或 审核不通过 并填写审核回复
4. 后端校验该菜谱当前必须为 待审核 且提交结果只能是两种之一,写回 status 与 audit_remark
5. 审核通过后该菜谱进入前台菜谱大全、推荐与食谱计划候选池
2
3
4
5
# 技术说明
# 系统架构
- 前端:Vue 3.4.29 + Vite 5.3.1 + Element Plus 2.8.4 + Pinia 2.2.2 + vue-router 4.4.3 + axios 1.7.5 + ECharts 5.5.1 + @kangc/v-md-editor 2.3.18 (Markdown 渲染),含前台 9 个功能页面 (首页、我的数据概览、智能菜谱助手、菜谱大全、菜谱详情、食材百科、我的收藏、我的食谱计划、营养分析) 与后台 13 个管理页面
- 后端:Spring Boot 3.2.10 + JDK 17 + MyBatis (mybatis-spring-boot-starter 3.0.4) + MySQL Connector 8.0.31,统一
ResponseVO响应与PageVO分页,异常统一走GlobalExceptionHandler与CustomException - 数据库:MySQL 8.0+ (兼容 5.7 及以上),14 张表,字段间使用
xxx_id逻辑关联且不加物理外键与额外索引 - 认证:JWT (jjwt 0.9.1,HS256 签名),
LoginInterceptor拦截/**并放行/common/login、/common/register、/common/retrievePassword、/file/**,解析出的用户信息存入CurrentUserThreadLocal;数据可见范围在 Service 层按ADMIN/USER区分 - AI:阿里云百炼 (DashScope) OpenAI 兼容模式,接口地址
https://dashscope.aliyuncs.com/compatible-mode/v1,对话模型qwen-plus、视觉模型qwen-vl-max、向量模型text-embedding-v3(默认 1024 维);不引入 Spring AI / LangChain,使用 JDK 17 原生HttpClient+ fastjson2 手写客户端,PDFBox 2.0.29 负责 PDF 文本抽取
# 访问地址
- 后端API:http://localhost:1000
- 前端页面:http://localhost:5173(开发环境,Vite 默认端口;开发环境接口地址由
.env.development中的VITE_APP_API_URL指定为http://127.0.0.1:1000) - 上传文件访问:http://localhost:1000/file/{fileName}
# 数据库
- 数据库名:a_1_1_food_system(以
application.yaml与sql/sql.sql为准) - 字符集:utf8mb4
- 排序规则:utf8mb4_unicode_ci
- 总表数:14张表
# AI 关键参数 (可在后台「模型通道配置」调整)
- 工具调用轮数上限:5(
ai.max-tool-rounds) - 知识片段目标字数 / 重叠字数:500 / 80(
ai.chunk-size/ai.chunk-overlap) - 向量化批量:10(
ai.embedding-batch-size,接口单请求上限) - 超时设置:连接超时 10 秒、普通请求读超时 120 秒、流式请求读超时 300 秒
- 默认生成参数:温度 0.7、max_tokens 2048、检索条数 topK 5、相似度阈值 0.35
- 默认单价:输入 0.0008 元/千 Token、输出 0.002 元/千 Token,费用按「输入 Token / 1000 × 输入单价 + 输出 Token / 1000 × 输出单价」估算
- 上传限制:单文件 100MB、批量请求 1000MB;图片识别要求单张不超过 4MB
# 系统特色
# 手写 OpenAI 兼容客户端
- 组件/实现:
AiHttpClient基于 JDK 17HttpClient封装postJson()与postStream(),统一附加Authorization: Bearer {key}与Content-Type: application/json,429 与 5xx 自动重试 2 次 (退避 1s、2s) - 应用:对话、工具调用、视觉识别与向量化四类请求全部走同一客户端,连接超时与读超时可配置
- 功能:流式请求仅在尚未产出任何数据前允许重试,避免前端重复渲染;上游 401 / 403 / 429 / 400 与 5xx 分别翻译成中文提示并保留错误原文便于现场排障
# ReAct 工具编排
- 组件/实现:
ToolRegistry从agent_tool表读取启用工具生成tools参数,并通过 Spring 注入的ToolHandler列表按code建立索引 - 应用:6 个工具 (知识库检索、食材查询、菜谱检索、菜谱入库、营养计算、菜单规划) 由模型自主决定调用哪一个,后台可启用或禁用
- 功能:支持流式下
delta.tool_calls分片累积与arguments拼接,arguments解析失败时以原始字符串作为参数传入,工具异常转为错误 JSON 回填模型而不断链
# 会话与消息落库
- 组件/实现:
ChatOrchestrator负责 ReAct 主循环、SSE 事件推送、chat_session与chat_message落库以及会话 Token 累计 - 应用:会话列表展示消息数量与 Token 合计,消息明细保留
reasoning_content思考链、tool_code与tool_call_id工具调用信息、recipe_id关联菜谱 - 功能:历史上下文取最近 10 条 user / assistant 文本消息;
chat_message未持久化tool_calls结构,因此回放时只取文本消息,避免消息序列不合法导致接口报错
# 成本与调用可观测
- 组件/实现:
AiUsageRecorder统一写入ai_call_log,StatisticsServiceImpl聚合累计 Token、费用、平均耗时、成功率以及调用类型占比与近 7 天调用趋势 - 应用:后台首页的附加卡片与 ECharts 图表、调用日志页的类型与状态筛选及请求响应详情
- 功能:费用按配置单价估算并保留 4 位小数,请求与响应摘要超 2000 字自动截断存储,写日志失败不影响主流程
# 向量库可视化运维
- 组件/实现:
RagService提供解析入库、重建向量与内存余弦召回,KnowledgeChunkController暴露rebuild与search接口 - 应用:后台「向量库管理」可按文档范围重建向量,并按片段查看向量状态、维度与 Token 数
- 功能:「召回测试」返回命中片段、来源文档与相似度数值,并自动跳过维度不一致的片段,便于在切换向量模型或维度后定位历史数据问题
文档版本:v1.0 最后更新:2025-02-17 系统版本:1.0.0