# AI 美食智能推荐与菜谱生成 Agent 平台 - 功能说明

本文作者:程序员瑞哥 (opens new window)

获取网址:https://javadh.com (opens new window)

# 项目演示

# 项目概述

本系统是一个以真实大模型驱动的美食智能推荐与菜谱生成 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 17 HttpClient 手写调用 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 表并可在后台启用 / 禁用,执行逻辑在 Java ToolRegistry 中按 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 对话菜谱卡与食谱计划每日热量汇总均基于同一套营养字段,数值互相一致

# 项目亮点

# 一、 业务功能亮点

  1. 自然语言生成完整菜谱:用户只需描述「冰箱里只有番茄、鸡蛋,不吃辣,10 分钟能做什么菜」,Agent 自动完成知识库检索 → 食材营养查询 → 已有菜谱检索 → 生成并入库 → 营养计算,最终输出 Markdown 菜谱与菜谱卡
  2. 多轮对话持续调整:支持「少油一点」「改成烤箱版」「去掉香菜」这类追加要求,模型基于真实上下文重生成,菜谱卡与营养值同步变化
  3. 一周食谱一键规划:按天数 (默认 7 天,上限 30 天)、每日目标热量、口味与忌口自动配餐,三餐热量比约 3:4:3,逐日校验热量偏差 (超过 15% 会在返回信息中提示可手动调整),落库为 meal_plan + meal_plan_item
  4. 菜谱审核闭环:AI 生成的菜谱以 source=AI生成、status=待审核 入库,管理员审核通过后才进入公共菜谱列表、前台推荐与食谱计划配餐候选池

# 二、 技术实现亮点

  1. 技术栈版本明确:后端 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
  2. 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 支撑表
  3. 第三方集成全程可观测:每次真实调用 (对话 / 工具 / 向量 / 视觉) 都写入 ai_call_log,记录模型名、输入与输出 Token、费用估算、耗时、状态、错误信息与请求响应摘要 (超 2000 字截断存储),后台首页据此统计累计费用、调用成功率与平均响应耗时
  4. 密钥与数据权限双重保护:API Key 只保存在后端数据库或环境变量 DASHSCOPE_API_KEY,列表与详情接口一律返回脱敏值 (形如 sk-****3f7a),前端留空提交表示不修改原 Key;数据可见范围在 Service 层控制,管理员不过滤,普通用户仅查询 user_id = 当前用户ID,前端不传 userId
  5. 上游异常有兜底:429 限流与 5xx 错误自动重试 2 次 (退避 1s、2s),401 / 403 / 429 / 400 与 5xx 分别翻译成中文提示并保留上游错误原文写入 ai_call_log.error_msg;工具执行异常不抛出,转为错误 JSON 回填模型,避免整轮对话崩溃

# 三、 用户体验亮点

  1. 前台卡片式网格布局:菜谱大全、食材百科、我的收藏统一使用 el-row :gutter="20" + 响应式 el-col :xs="24" :sm="12" :md="8" :lg="6",卡片展示封面、名称、标签、用时热量与摘要,不使用表格
  2. 数据可视化到位:后台首页 4 个数字统计 (用户数 / 菜谱数 / 知识片段数 / 今日 Token 消耗) + 4 个 ECharts (近 7 天调用次数与 Token 消耗双轴折线、菜系分布饼图、菜谱难度分布柱状图、调用类型占比饼图) + 3 个附加卡片 (累计费用估算 / 调用成功率 / 平均响应耗时),个人数据概览页同样为 4 个统计 + 4 个图表 + 猜你喜欢
  3. 每一处 AI 输出都可追溯:
    • 对话页展示模型名、思考过程折叠面板、工具调用卡片 (调用中 / 完成并附耗时、参数与结果摘要)、Markdown 正文、菜谱卡与四项营养、底部 Token 与费用
    • 后台「对话日志」可查看会话列表 (用户 / 会话标题 / 消息数量 / Token 合计 / 创建时间) 与逐条消息明细,含思考链、工具调用链与关联菜谱
  4. 交互细节统一收敛:前台页面宽度 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
1
2

(昵称「管理员」,状态 启用,登录页身份选择「管理员」)

# 测试用户

用户名: user
密码: 123456
1
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 事件,前端刷新会话列表
1
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. 任一步失败则状态置「解析失败」并记录上游错误原文,已写入的片段保留可重试
1
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】正文…」回填给模型作为生成依据
1
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,调用类型为 视觉
1
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,前端展示逐日三餐安排与每日热量汇总
1
2
3
4
5
6

# 菜谱审核

1. AI 生成的菜谱由 save_recipe 工具写入 recipe 表,来源为 AI生成、user_id 为当前用户、状态为 待审核、浏览量为 0
2. 管理员在「菜谱管理」列表中看到 待审核 标签与该行专属的「审核」按钮
3. 点击「审核」弹窗,单选 审核通过 或 审核不通过 并填写审核回复
4. 后端校验该菜谱当前必须为 待审核 且提交结果只能是两种之一,写回 status 与 audit_remark
5. 审核通过后该菜谱进入前台菜谱大全、推荐与食谱计划候选池
1
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 17 HttpClient 封装 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

Java导航网   |