
简介这份资源是面向计算机相关专业学生与开发者的高分毕业设计项目包主题为基于Python、知识图谱Neo4j与生成式AI的智能食谱推荐系统适合用作毕设、课程设计、作业或项目立项演示也便于基础较好的学习者在此基础上二次开发。压缩包共43个文件约682KB以tsx与less为主配合ts、json、yaml等配置与样式文件另有Python脚本、shell部署脚本及图片素材前端页面、组件、布局与数据配置模块划分清晰便于按目录快速定位与理解整体架构。项目已通过mac与Windows 10/11运行测试并获导师认可、答辩评审95分目前已有294人学习关注。读者可获得完整源码、详细文档与全部数据资料结合知识图谱构建与生成式AI推荐逻辑快速掌握从数据建模到推荐落地的实现思路也可直接用于毕设答辩或课程作业提交。1. 从一份 95 分毕设拆起Python 知识图谱加生成式 AI 的食谱推荐系统能跑出什么食谱推荐这个题目十个毕设里能撞见三四个但大多数停在协同过滤调包、拿 MovieLens 换个壳就交差的水平。这份资源不一样的地方在于它把「知识图谱」和「生成式 AI」两条线真正接进了推荐链路Neo4j 里存的是食材、菜系、口味、烹饪方式、营养标签之间的实体关系Python 后端负责图查询和推荐打分前端用 React 把结果渲染成可交互的食谱卡片生成式 AI 那层则负责把结构化查询结果转成自然语言的推荐理由和替代食材建议。整套东西跑在 Mac 和 Windows 10/11 上都验证过答辩评审 95 分说明功能完整度和文档质量都过了导师那关。适合谁如果你正在找一份能直接改、能讲清楚技术选型、又不会在环境配置上卡三天的毕设或课设底稿这份源码包值得拆开看。它不教你 Python 语法但把「知识图谱怎么建、Neo4j 怎么查、推荐逻辑怎么和生成式 AI 拼起来」这条链路走通了。下面按我实际复现的顺序从环境到数据到推荐逻辑再到几个我踩过的坑一层层拆。2. 环境与依赖Neo4j 社区版加 Python 虚拟环境怎么配才不翻车2.1 为什么选 Neo4j 社区版而不是内存图数据库食谱推荐的核心查询是「给定用户偏好找出满足多个食材约束、且烹饪方式匹配的菜谱」这种多跳关系查询用 SQL 写会变成一堆 JOIN用 Neo4j 的 Cypher 则直观得多。社区版免费、支持 Cypher、有桌面端和 Server 两种模式对毕设场景完全够用。常见做法是本地装 Neo4j Desktop建一个本地数据库实例记下 bolt 端口默认 7687和初始密码。注意Neo4j 4.x 和 5.x 的 Cypher 语法有差异尤其是CALL {}子查询和索引创建语句。这份源码包里的查询脚本按 4.x 写的话在 5.x 上跑会报语法错误先确认版本再导入。2.2 Python 侧依赖安装与虚拟环境后端是 Python依赖集中在main.py同级目录的 requirements 里源码包内通常有。我一般会先建虚拟环境再装避免污染系统 Python。# 创建虚拟环境Python 3.8 均可推荐 3.9/3.10 python -m venv venv # Windows 激活 venv\Scripts\activate # Mac/Linux 激活 source venv/bin/activate # 安装依赖neo4j 驱动版本要和数据库版本对齐 pip install neo4j4.4.0 flask flask-cors openai python-dotenv这里neo4j驱动版本很关键驱动 5.x 连 4.x 数据库会握手失败报Neo.ClientError.Security.Unauthorized或协议不匹配。源码包里如果没锁版本按数据库版本反推驱动版本。flask和flask-cors负责把推荐接口暴露成 HTTP 服务前端 React 通过 fetch 调用。openai库用于生成式 AI 那层实际调用时把 API Key 放在.env里不要硬编码进main.py。2.3 前端 React 环境与.umirc.ts配置前端目录是food-react-master用的是 UmiJS 框架配置文件.umirc.ts里定义了路由、代理和构建选项。装依赖用 pnpm源码包里有pnpm-lock.yaml没有 pnpm 的话先npm i -g pnpm。cd food-react-master pnpm install pnpm devpnpm dev启动后默认在 8000 端口。如果后端 Flask 跑在 5000需要在.umirc.ts的proxy字段里把/api转发到http://127.0.0.1:5000否则前端请求会 404。这个代理配置是新手最容易漏的一步漏了之后页面能打开但数据全是空的。3. 知识图谱建模食材、菜系、口味实体怎么落进 Neo4j3.1 实体与关系的设计思路食谱知识图谱的节点类型通常包括Recipe菜谱、Ingredient食材、Cuisine菜系、Flavor口味、CookingMethod烹饪方式、Nutrition营养标签。关系类型包括CONTAINS菜谱包含食材、BELONGS_TO菜谱属于菜系、HAS_FLAVOR菜谱具有口味、COOKED_BY菜谱用某种烹饪方式、HAS_NUTRITION菜谱有营养信息。这种建模的好处是推荐时可以沿着「用户喜欢的口味 → 具有该口味的菜谱 → 这些菜谱的食材 → 用户冰箱里有的食材」这条路径做多跳匹配而不是简单打标签。常见做法是先用 CSV 或 JSON 整理原始数据再用 Cypher 的LOAD CSV或 Python 脚本批量写入。3.2 用 Python 批量导入节点和关系源码包里通常有数据导入脚本核心逻辑是用neo4j驱动的 session 执行 Cypher。下面是我复现时用的简化版导入逻辑from neo4j import GraphDatabase import json driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, your_password)) def create_recipe(tx, recipe): # MERGE 避免重复创建SET 更新属性 tx.run( MERGE (r:Recipe {name: $name}) SET r.difficulty $difficulty, r.time $time WITH r UNWIND $ingredients AS ing_name MERGE (i:Ingredient {name: ing_name}) MERGE (r)-[:CONTAINS]-(i) , namerecipe[name], difficultyrecipe[difficulty], timerecipe[time], ingredientsrecipe[ingredients]) with driver.session() as session: with open(recipes.json, r, encodingutf-8) as f: recipes json.load(f) for r in recipes: session.execute_write(create_recipe, r)MERGE而不是CREATE是关键CREATE每次执行都会新建节点重复跑脚本会造出一堆同名食材节点图谱直接废掉。UNWIND把食材列表展开成多行每行和菜谱节点建一条CONTAINS关系。execute_write是驱动 4.x 的写法5.x 里改成session.execute_write仍然可用但事务函数签名略有不同。3.3 验证图谱是否建对导入完成后跑一条 Cypher 确认节点和关系数量MATCH (n) RETURN labels(n) AS label, count(n) AS cnt ORDER BY cnt DESC; MATCH ()-[r]-() RETURN type(r) AS rel, count(r) AS cnt ORDER BY cnt DESC;如果Ingredient节点数量远大于原始数据里的食材种类数说明MERGE没生效或者数据里有空格、大小写不一致。我一般会在导入前对食材名做strip().lower()归一化否则「番茄」和「番茄 」会被当成两个节点。4. 推荐逻辑与生成式 AI 接入从 Cypher 查询到自然语言推荐理由4.1 基于图谱的候选菜谱召回推荐的第一步是召回。给定用户偏好比如喜欢的口味、忌口食材、可用食材用 Cypher 从图谱里捞出候选菜谱。下面这条查询是「找出所有不含忌口食材、且口味匹配的菜谱」MATCH (r:Recipe)-[:HAS_FLAVOR]-(f:Flavor) WHERE f.name IN $preferred_flavors AND NOT EXISTS { MATCH (r)-[:CONTAINS]-(i:Ingredient) WHERE i.name IN $disliked_ingredients } RETURN r.name AS recipe, r.difficulty AS difficulty LIMIT 20NOT EXISTS子查询用来排除忌口比先查再过滤高效。LIMIT 20控制候选集大小避免后续生成式 AI 调用时上下文过长。参数$preferred_flavors和$disliked_ingredients从用户画像里来用户画像可以存在 Neo4j 里也可以前端传过来。4.2 用生成式 AI 生成推荐理由和替代建议召回之后把候选菜谱的结构化信息拼成 prompt交给生成式 AI 生成自然语言推荐。常见做法是让模型输出 JSON包含推荐理由、替代食材、注意事项三个字段方便前端解析。import openai, os, json openai.api_key os.getenv(OPENAI_API_KEY) def generate_recommendation(recipe_info, user_prefs): prompt f你是一个食谱推荐助手。根据以下菜谱信息和用户偏好生成推荐理由。 菜谱{recipe_info[name]} 食材{, .join(recipe_info[ingredients])} 口味{, .join(recipe_info[flavors])} 用户偏好{user_prefs} 请输出 JSON包含 reason推荐理由、substitute替代食材建议、note注意事项。 resp openai.ChatCompletion.create( modelgpt-3.5-turbo, messages[{role: user, content: prompt}], temperature0.7 ) return json.loads(resp.choices[0].message.content)temperature0.7让输出有一定多样性但不至于跑偏。json.loads之前最好加一层 try-except模型偶尔会返回带 markdown 代码块的 JSON直接解析会炸。我一般会先strip(json).strip()再解析。4.3 推荐打分与排序候选菜谱召回后需要排序。源码包里通常用加权打分口味匹配度占 0.4食材可用度占 0.3烹饪难度占 0.2生成式 AI 给出的推荐置信度占 0.1。权重可以根据场景调比如给新手推荐时把难度权重调高。def score_recipe(recipe, user_prefs): flavor_score len(set(recipe[flavors]) set(user_prefs[flavors])) / max(len(user_prefs[flavors]), 1) ingredient_score len(set(recipe[ingredients]) set(user_prefs[available])) / max(len(recipe[ingredients]), 1) difficulty_score 1 - recipe[difficulty] / 5 # 难度 1-5越低越好 return 0.4 * flavor_score 0.3 * ingredient_score 0.2 * difficulty_score 0.1 * recipe.get(ai_confidence, 0.5)这个打分函数是纯 Python不依赖外部服务方便调试。ai_confidence可以从生成式 AI 的输出里解析也可以固定给 0.5 先跑通链路。5. 避坑与排查复现这套系统时最容易翻车的五个地方5.1 Neo4j 连接报ServiceUnavailable现象Python 脚本一跑就抛neo4j.exceptions.ServiceUnavailable提示无法连接 bolt 端口。原因Neo4j 服务没启动或者防火墙拦了 7687 端口或者连接地址写成了http://而不是bolt://。解决先确认 Neo4j Desktop 里数据库实例是 Running 状态再用telnet localhost 7687测端口通不通。连接字符串必须是bolt://localhost:7687不是http://。5.2 前端页面能打开但数据为空现象pnpm dev启动后页面正常渲染但食谱列表、推荐结果全是空白。原因.umirc.ts里的 proxy 没配或者配了但目标端口和后端实际端口不一致。解决检查.umirc.ts的proxy字段确认/api转发到了 Flask 实际监听的端口。Flask 默认 5000但如果main.py里写了app.run(port5001)proxy 也要跟着改。5.3 生成式 AI 接口超时或返回乱码现象推荐接口偶尔 500日志里显示openai.error.Timeout或返回内容不是合法 JSON。原因网络波动导致 API 超时或者模型返回了带 markdown 包裹的 JSON。解决给 API 调用加timeout30和重试逻辑解析前先清理 markdown 标记。如果用的是国内可访问的生成式 AI 服务确认 base_url 配置正确。5.4 图谱导入后查询结果重复现象同一条 Cypher 查询返回多行相同菜谱或者食材节点数量异常多。原因导入时用了CREATE而不是MERGE或者食材名没有归一化。解决导入脚本里所有节点创建都用MERGE食材名统一strip().lower()。已经导入脏数据的话跑MATCH (n) DETACH DELETE n清空重来。5.5 虚拟环境依赖版本冲突现象pip install时报ResolutionImpossible或者装完后import neo4j报错。原因neo4j驱动版本和数据库版本不匹配或者 Flask 和 Werkzeug 版本冲突。解决先确认 Neo4j 数据库版本再装对应驱动。Flask 2.x 配 Werkzeug 2.xFlask 3.x 配 Werkzeug 3.x。实在不行就pip install neo4j4.4.0 flask2.3.0 werkzeug2.3.0锁死版本。6. 进阶技巧把推荐结果做成可解释的图谱路径可视化跑通基础链路之后最有价值的进阶方向是把推荐理由从「一段文字」变成「一条可追溯的图谱路径」。用户看到的不只是「推荐这道菜因为口味匹配」而是能看到「你的偏好 → 川菜 → 麻婆豆腐 → 含豆腐 → 你冰箱里有豆腐」这条完整路径。实现方式是在 Cypher 查询里用MATCH path ...返回路径前端用图谱可视化库渲染。MATCH path (u:User {id: $user_id})-[:PREFERS]-(f:Flavor)-[:HAS_FLAVOR]-(r:Recipe)-[:CONTAINS]-(i:Ingredient) WHERE i.name IN $available_ingredients RETURN path, r.name AS recipe LIMIT 5这条查询返回的是路径对象前端可以用neo4j-driver的path解析或者直接拿节点和关系列表。我一般会把路径转成{nodes: [...], links: [...]}的格式丢给 ECharts 的 graph 系列或者 D3 渲染。这样推荐结果就有了「黑匣子」被打开的效果答辩时演示这一块导师基本会追问实现细节说明讲到位了。另一个技巧是给生成式 AI 的 prompt 里加 few-shot 示例。比如给两个「输入菜谱信息 → 输出推荐 JSON」的样例模型输出的格式稳定性会明显提升。我试过不加示例时 JSON 解析失败率大概 15%加了两个示例后降到 3% 以内。这个改动很小但省掉了大量调试解析逻辑的时间。从那以后我每次接生成式 AI 的输出都强制先跑一遍 JSON schema 校验不通过就重试绝不直接把模型输出丢给前端。希望帮到你。本文还有配套的精品资源点击获取