尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

融合知识图谱与生成式AI的智能食谱推荐系统构建

融合知识图谱与生成式AI的智能食谱推荐系统构建 简介这是一个基于知识图谱和生成式AI的智能食谱推荐系统完整工程面向正在做毕业设计的计算机专业学生也适合需要项目实战练习的入门者作为课程设计、期末大作业使用。项目采用前后端分离结构前端以TypeScript/React技术栈呈现包含19个tsx页面组件、9个less样式文件和类型定义负责菜品展示、交互与推荐结果呈现后端包含Python入口、YAML配置和依赖锁定信息另有shell发布脚本用于本地运行、调试与部署。整个压缩包共42个文件仅681KB目录层次清晰解压后即可对照学习。源码经过严格调试评审分为98分内容经助教老师审定难度适中能够帮助读者理解知识图谱在食谱推荐中的应用以及生成式AI能力的接入方式。目前已有403人学习下载适合作为毕业设计选题方案、系统搭建参考和功能复现素材。1. 从“猜你喜欢”到“告诉你为什么并教会你做”推荐系统最常见的尴尬是推给用户一道“理论上相似”的菜用户却完全没听过、不知道食材去哪买、更别说怎么做。基于知识图谱和生成式AI的智能食谱推荐系统解法是把推荐从二维的“用户-物品”相似度升级为“食材、菜系、营养、做法、口味”组成的知识网络再让生成式AI为推荐结果补上人类能读懂的“理由”和“步骤”。换句话说它不只是告诉你“推荐什么”还回答“为什么推荐”和“你会不会做”。这个项目非常适合Python毕业设计——它不依赖大规模算力mainblock是Neo4j图数据库加一套推荐算法再配一个生成式AI接口就能串起来更难得的是它天然分成数据、算法、应用三层工作量便于分段推进答辩时故事线也好讲。本文按我通常的做法从知识图谱建模、推荐计算、生成式AI接入到毕业设计工程化落地拆成一条可以照做的路径。前置知识需要Python和基本SQL/数据库概念推荐系统理论有了解更好没有也能跟着代码走通。2. 知识图谱建模与食材数据的预处理方案2.1 为什么是知识图谱而不是关系型数据库传统食谱系统用三张表——菜谱表、食材表、关联表——也能工作但遇到“我想吃清淡的、含虾、30分钟内能做完的川菜”这类组合条件时SQL关联查询会越写越复杂还要处理“番茄炒蛋”和“西红柿炒鸡蛋”这种同义食材问题。知识图谱把万物建模为“实体-关系-实体”查询模式天然是图路径跨关系推理食材→营养→菜系→做法只需一次遍历不需要多次JOIN。知识图谱对推荐系统的另一个关键贡献是可解释性。协同过滤告诉你“与你相似的人喜欢这道菜”但说不出为什么知识图谱可以返回一条路径用户偏好“高蛋白”→ 虾仁的蛋白质属性 → 虾仁属于海鲜 → 海鲜常出现于粤菜 → 推荐白灼虾。这条路径本身就是推荐理由后面接生成式AI时素材就来自这里。2.2 实体、关系与属性设计智能食谱推荐系统的知识图谱建议从四种核心实体和五类边关系开始别贪多毕设规模控制在一千道菜、三百种食材就能跑通全链路。实体类型实体属性建议示例Dish菜谱name, cuisine, cooking_time, difficulty, steps, flavor_profile麻婆豆腐 / 川菜 / 20分钟 / 入门 / 麻辣Ingredient食材name, category, season, shelf_life豆腐 / 豆制品 / 全年 / 冷藏3天Nutrient营养标签name, unit, health_benefit蛋白质 / g / 肌肉修复UserPreference用户偏好user_id, taste, allergy, diet_styleu_1024 / 偏辣 / 花生过敏 / 高蛋白五类边关系关系边一般用CQL创建核心语句是:CREATE (d:Dish {name: 麻婆豆腐, cuisine: 川菜, cooking_time: 20}) CREATE (i:Ingredient {name: 豆腐, category: 豆制品}) CREATE (n:Nutrient {name: 蛋白质, unit: g}) CREATE (d)-[:CONTAINS {amount_g: 300}]-(i) CREATE (i)-[:HAS_NUTRIENT]-(n) CREATE (u:UserPreference {user_id: u_1024, taste: 辣}) CREATE (u)-[:LIKES {weight: 0.8}]-(d)这段代码创建了菜谱、食材、营养标签、用户偏好四个节点以及“菜谱包含食材”“食材具有营养”“用户喜欢菜谱”三种关系。CONTAINS关系上的amount_g属性会在后续做营养过滤时派上用场比如“蛋白质含量低于30g的不要推荐”。节点融合是构建图谱时第一个坑。同一食材在不同菜谱里叫法不一常见做法是import re def normalize_ingredient(raw): text raw.strip() text re.sub(r\d(克|g|克/kg)?$, , text) text text.replace(西红柿, 番茄) return text我一般还会准备一个手工映射表synonym_map.json覆盖“土豆/马铃薯”“青椒/柿子椒”这类高频同义食材。清洗规则不必追求百分百毕业设计能覆盖top 80的食材就足够支撑推荐效果了。2.3 向量化与嵌入存储知识图谱构建好之后推荐计算还需要一个向量化步骤。两个常用选择TransE / DistMult 等图嵌入把每个实体映射成低维向量保留图结构语义。自建同现矩阵加 Word2Vec把“菜谱-食材”视为文档-词用Word2Vec学习食材向量实现成本低效果也不差。毕设场景我更推荐后者数据量小、训练快还能复用gensim的成熟接口from gensim.models import Word2Vec # corpus_by_dish: 每道菜的食材列表 dish_ingredient_corpus [ [豆腐, 牛肉, 豆瓣酱], [番茄, 鸡蛋], [虾仁, 西兰花, 蒜], ] model Word2Vec(sentencesdish_ingredient_corpus, vector_size64, window3, min_count1, epochs50) # 验证语义相似度 similar_ingredients model.wv.most_similar(豆腐, topn5) print(similar_ingredients)向量存哪两个选择直接压成.npy文件供内存调用或者写进Neo4j节点属性。毕设建议选前者省去图数据库扩容操作答辩时可以表达“大数据量场景可替换为向量数据库”。3. 融合知识图谱的智能食谱推荐算法设计3.1 推荐主流程分层与热启动策略一张图把推荐主流程拆清楚冷启动阶段用户没有任何历史行为先从知识图谱做面向前端的基础推荐包含大众口味菜、当季食材推荐、低难度菜。内容侧保证质量实现方式是查图谱MATCH (d:Dish) WHERE d.difficulty 入门 AND d.cooking_time 30 RETURN d.name AS name, d.cuisine AS cuisine ORDER BY d.name LIMIT 10热启动阶段用户有了评分、点击、搜索、收藏等行为进入偏好画像 → 图谱路径召回 → 排序 → 重排 → 生成式AI理由五步流水线。这条路径在毕业设计里足够撑起推荐系统的完整论域。3.2 基于图路径的偏好画像构建用户偏好不只在显式评分里。基于点击行为就能建轻量画像。这里给出一个可运行的画像构建脚本核心片段def build_user_profile(user_id, clicks): profile {ingredients: [], cuisines: [], tastes: []} for dish, weight in clicks.items(): # 从Neo4j查询这道菜的食材、菜系、口味 cypher_query MATCH (d:Dish {name: $dish_name}) OPTIONAL MATCH (d)-[:CONTAINS]-(i:Ingredient) OPTIONAL MATCH (d)-[:HAS_CUISINE]-(c:Cuisine) RETURN collect(i.name) AS ingredients, c.name AS cuisine # 累加权重到profile profile[ingredients].extend( [(ing, weight * 0.8) for ing in row[ingredients]] ) return profile这里的要点是不是简单记“看过麻婆豆腐”而是把它拆成“牛肉、豆腐、川菜、麻辣、下饭菜”等标签每个标签携带权重。点击权重默认1.0收藏权重1.5完整吃教程步骤则加权到2.0。这样向量化后即使用户下一秒的兴趣漂移也能实时反映出来。3.3 DeepWalk / Node2Vec 排序与 EE 策略偏好画像出来了推荐排序有两种进阶组合方案A规则路径加分。基于知识图谱的个性化路径查询直接当作推荐结果召回MATCH (u:UserPreference {user_id: u_1024})-[:LIKES]-(d1:Dish) MATCH (d1)-[:CONTAINS]-(i:Ingredient)-[:CONTAINS]-(d2:Dish) WHERE d2 d1 RETURN d2.name AS recommended, count(*) AS score ORDER BY score DESC LIMIT 10这条查询做了基于共同食材的推荐但它只是“相同食材”维度推荐结果会陷入“吃过麻婆豆腐就只推豆腐菜”的尴尬。方案B图嵌入向量加相似度排序。把用户画像的向量和菜谱向量做点积或余弦相似度再结合随机游走生成的Node2Vec特征得到综合排序分from node2vec import Node2Vec from gensim.models import KeyedVectors # 用node2vec在Neo4j导出的边列表上学习图嵌入 node2vec Node2Vec(edges_path, dimensions128, walk_length20, num_walks10, workers4) model node2vec.fit(window10, min_count1, epochs30) model.wv.save_word2vec_format(kg_embeddings.vec)这里walk_length20表示每次随机游走20步num_walks10表示每个节点做10次游走。两者决定邻居探索深度太大让远亲节点都算近邻太小则只覆盖直接相连节点。对于一千到两千节点的图谱20的步长和10的游走次数足够。嵌入维度128对应多分类任务足够图规模扩大后再考虑256。排序分数计算公式我一般用final_score alpha * content_score beta * graph_path_score gamma * user_item_sim参数配置参考参数推荐初始值调节方向alpha0.4内容质量冷启动时加大beta0.4用户历史丰富后加大gamma0.2有评分矩阵时可加大至0.3walk_length20冷启动初期降低数据多可提高top_k 候选池50最终展示取8~12条EE策略探索-利用平衡建议用“随机ε”实现有10%的概率从候选池里随机抽一道菜不按分数排序。这么做的好处是防反馈闭环避免只推荐相似口味让用户腻味同时能攒到负反馈数据用于下一轮训练。毕业设计里在排序结果上做这个随机覆盖层代码工作量不大答辩讲清楚原理就能拿分。3.4 从候选到结果重排与去重图谱推荐的常见问题是一堆菜高度重复。我一般会先按菜系或食材品类做MMR最大边际相关重排。核心逻辑是每选一道菜不仅要看它的推荐分数还要惩罚与已选菜重叠度过高的候选。伪代码如下def mmr_rerank(candidates, already_selected, lambda_0.7, top_k8): result [] while len(result) top_k and candidates: best_score -float(inf) best_item None for item in candidates: relevance item.score # 重复度: 与已选菜共享食材的比例 overlap compute_ingredient_overlap(item, already_selected) mmr_score lambda_ * relevance - (1 - lambda_) * overlap if mmr_score best_score: best_score mmr_score best_item item result.append(best_item) already_selected.append(best_item) candidates.remove(best_item) return resultlambda_0.7偏重相关性lambda_0.3偏重多样性。建议冷启动时用0.50.6给新用户多展示不同菜系老用户保持0.7。这一层的算法要从“用户-菜谱”二元关系里挖掘重复推同食材菜会让用户觉得“系统没别的东西了”。MMR重排环节就是对多样化推荐的直接兑现。4. 生成式AI接入推荐理由与动态菜谱生成4.1 生成式AI在食谱推荐系统里扮演什么角色知识图谱擅长查关系不擅长写文案。生成式AI要干的活有两件把图谱路径翻译成自然语言推荐理由“因为你偏好高蛋白、低脂且多次浏览海鲜类菜谱虾仁西兰花和您的饮食方向匹配度较高。”动态生成定制食谱细节用户说“最近在减脂能不能少油做这道菜”生成式AI基于原菜谱改写生成替代食材和烹饪建议。这两个输出都直接面向用户是系统展现“智能感”的关键入口。生成式AI部分完成度的高低往往决定毕业设计打分天花板。4.2 接入生成式AI的最小可运行方案2025年这个时间点主流接入方式已经是OpenAI兼容接口走统一调用。以国产大模型为例结构化输入尤为关键。我的常见做法是import os import json from openai import OpenAI client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL) ) def generate_recommendation_reason(dish_info, user_profile, graph_path): prompt f 你是资深美食推荐官。请用不超过80字写一段推荐理由。 菜名: {dish_info[name]} 菜系: {dish_info[cuisine]} 主要食材: {, .join(dish_info[ingredients])} 用户偏好: {json.dumps(user_profile, ensure_asciiFalse)} 图谱路径依据: {graph_path} 要求: 说清楚“为什么推荐”引用图谱路径中的偏好不写空话不用emoji。 resp client.chat.completions.create( modelyour-model-name, messages[{role: user, content: prompt}], temperature0.7, max_tokens150, ) return resp.choices[0].message.content这段代码的关键参数说明temperature0.7让文案有变化但不过于发散max_tokens150限制推荐词控制在合理长度graph_path是前一步图谱查询出来的路径文本例如“李四偏好高蛋白 → 虾仁蛋白质含量19g/100g → 虾仁→鳕鱼同属海鲜 → 推荐鳕鱼西兰花”。没有它生成式AI就只能依赖菜品名瞎编推荐理由的可信度会断崖式下降。4.3 生成式AI与知识图谱的两种结合模式第一种叫“图谱引导生成”用户请求 → 查询知识图谱得到图谱路径 → 把路径作为prompt上下文 → 调用大模型生成自然语言第二种叫“生成内容图谱回写”用户请求 → 大模型输出新菜谱 → 解析结构化字段 → 写回Neo4j图谱作为新的节点毕设建议重点做第一种第二种作为扩展。理由第一种是推荐系统核心能力闭环不需要面对生成菜谱质量不稳定的问题第二种涉及新菜谱节点、关系边、以及食材标准化映射做不好会污染图谱评分反而扣分。4.4 生成式AI的稳定性和成本控制两条必须提前处理的坑一是非结构化输出导致前端渲染崩。大模型偶尔会输出Markdown表格、代码块或换行混乱的文本。解决的常用做法是让模型输出JSON结构再用Pydantic做约束解析from pydantic import BaseModel class RecipeAdvice(BaseModel): reason: str substitute_ingredients: list[str] cooking_tips: list[str] # 在prompt中要求: 直接输出JSON不要Markdown代码块二是调用失败降级。毕设现场演示时最怕大模型API挂了或者网络不稳。降级策略是必须的保留一份本地规则模板def fallback_reason(dish_info, user_profile): return (f为您推荐{dish_info[cuisine]}风味的{dish_info[name]} f主要食材包含{ 、.join(dish_info[ingredients][:3]) } f符合您当前偏好的{高蛋白 if user_profile.get(taste) 清淡 else 重口味}方向。)最后再补充一次本地写入提示语优化如果接入模型支持system prompt在system层写明“你是DietGPT一个基于知识图谱的膳食推荐文案助手”输出质量会明显稳定。5. 毕业设计的工程落地从源码到可演示系统5.1 技术栈选择与目录结构毕业设计源码要能当场跑起来技术选型千万别贪新。推荐组合后端 FastAPI Neo4j Python3.11前端 Vue3 Element Plus嵌入服务用 gensim node2vecAPI层装openai库兼容大模型。如果不想写前端FastAPI直接带一个 /docs Swagger界面也能演示接口。目录组织方式参考recipe-recommendation-system/ ├── app/ │ ├── api/ # 后端路由 │ ├── core/ # 配置与依赖注入 │ ├── db/ # Neo4j连接与数据初始化 │ ├── models/ # Pydantic数据模型 │ ├── services/ │ │ ├── graph_service.py # 图谱查询 │ │ ├── rec_engine.py # 推荐引擎召回/排序 │ │ ├── rerank.py # MMR重排 │ │ └── llm_service.py # 生成式AI接入 │ └── data/ # 菜谱csv / 图谱dump ├── notebooks/ # 数据处理与模型训练实验 └── scripts/ ├── build_graph.py # 从csv构建知识图谱 └── train_embeddings.py # 训练图嵌入/食材向量每个文件职责建议在代码文件头写清楚docstring毕业设计源码查重和答辩讲解都省事。5.2 知识图谱构建脚本的完整示例构建Python知识图谱到Neo4j是最占工作量的一步。用一个纯Python脚本搞定数据管道import csv from neo4j import GraphDatabase driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, password)) def load_dish(csv_path): with driver.session() as session: with open(csv_path, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: # 用MERGE防重复导入 session.run( MERGE (d:Dish {id: $id}) SET d.name $name, d.cuisine $cuisine, d.cooking_time toInteger($cooking_time), d.difficulty $difficulty , **row)MERGE是这里的关键它做“有则更新无则创建”避免重复跑脚本时产生脏数据。另外注意csv的编码用utf-8-sig很多Windows导出的csv带BOM头不解码会出现第一个列名带“\ufeff”字符的问题。数据量适中时每批用事务提交。推荐在脚本里加一个batch_size200的控制项Neo4j单事务执行大量MERGE会慢到怀疑人生。5.3 演示环节的稳定性和答辩技巧毕设演示最怕冷场和卡壳。教授最常见的问题是“你如何证明推荐有效”所以离线评估得准备。在源数据中留出20%的用户行为作为测试集用 recall10 和 hit_rate10 作为指标def recall_at_k(recommended, actual, k10): rec_top_k set(recommended[:k]) if not actual: return 0.0 return len(rec_top_k actual) / len(actual)只跑一个baseline不充分更好的是对比两个方案协同过滤只用UserCF vs 知识图谱生成式AI的组合。预期结果知识图谱方案在包含图谱路径信息的新菜上命中率更高冷启动场景优势明显。答辩PPT里放一张柱状对比图胜过讲10页理论。另一个高频答辩问题“生成式AI在推荐里到底解决了什么问题”回答要点传统推荐系统只能给“推荐了什么”生成式AI给“为什么推荐”和“怎么做”两者是互补关系不是替代关系。知识图谱给生成式AI提供事实基础生成式AI把事实翻译成用户可感知的语言——这个回答框架可应对大部分追问。5.4 一个必须做的性能优化最后一章落到具体技巧给Neo4j查询加查询缓存。毕业设计现场演示用户反复点击查询如果每次都走CQL响应时间是200~500ms但演示时连续操作10次Neo4j的IO开销会让体验变差。用Python一个functools装饰器就能解决from functools import lru_cache lru_cache(maxsize256) def graph_recommend(user_id_key: str, top_k: int): 带缓存的图谱推荐user_id_key格式: user_id|top_k with driver.session() as session: result session.run(RECOMMEND_CYPHER, user_iduser_id_key.split(|)[0]) return list(result)注意lru_cache的key必须是hashable所以参数拼成字符串传入。用户行为一更新需要手动刷新缓存调用graph_recommend.cache_clear()或者在写入行为动作后重新构造key。这个细节写进文档、演示时提一嘴“我做了缓存设计”是低成本的加分点。数据规模再往上走时才需要引入Redis或内存缓存层——毕业设计阶段一个函数级缓存已经足够后面这句话留给答辩时展示你对扩展性的认识。本文还有配套的精品资源点击获取
返回列表