简介:这是一套面向计算机相关专业毕业设计与课程设计场景的知识图谱电影问答系统完整项目源码,基于Python与Neo4j构建,适合正在准备毕设、需要项目实战练习的学生参考使用。项目采用前后端分离结构,后端以Flask搭建问答服务,配合爬虫模块完成电影数据采集,前端为小程序页面,并附带知识图谱工作流示意图与说明文档,便于理解整体架构与数据流转。资源包共59个文件,包含11个py源码、10个csv数据集、8个json配置、7个png与5个svg图示、5个js及3个wxss等前端文件,另有md说明文档,压缩包约6.28MB,目录划分清晰。目前已有77人学习下载。读者可据此掌握Neo4j图数据库建模、问答逻辑实现与前后端联调思路,并参考文档快速跑通项目,完成毕设或课程大作业。
1. 从一份能跑通的 MovieKGQA 说起:知识图谱电影问答系统到底解决什么问题
很多计算机专业的同学做毕业设计时,最头疼的不是写不出代码,而是拼不出一套「能演示、能答辩、能讲清原理」的完整系统。这份 MovieKGQA 项目就是冲着这个痛点来的:后端 Flask 提供问答接口,前端微信小程序做交互界面,中间用 Neo4j 存电影知识图谱,爬虫脚本负责从公开数据源抓取电影、演员、导演、类型等实体和关系。整套东西拆开看每个模块都不复杂,但串起来就是一个完整的「知识图谱构建 + 智能问答系统」闭环。
它适合谁?正在做知识图谱方向毕业设计、需要一套可运行参考实现的学生;想练手 Python 爬虫 + Flask + Neo4j 全链路、但不知道从哪找完整案例的开发者;以及课程设计或期末大作业需要交一个「有技术含量、能演示」项目的同学。不适合谁?指望直接拿去做生产级问答系统的——它的问答逻辑以模板匹配和规则为主,不是端到端深度学习方案,边界要提前认清。
2. 拆开 MovieKGQA 的目录:backend、frontend、spider 各管什么
2.1 三个核心目录的职责划分
拿到压缩包解压后,根目录下能看到MovieKGQA-master,里面主要分三块:
backend:Flask 应用,负责接收前端传来的自然语言问题,解析意图,查询 Neo4j,返回结构化答案。frontend:微信小程序前端,包含pages、utils、app.js、app.json、app.wxss等标准小程序结构,用户在这里输入问题、看到答案。spider:爬虫脚本,负责从电影数据源抓取实体和关系数据,生成可导入 Neo4j 的 CSV 或 Cypher 语句。
另外根目录还有README.md、assets文件夹(存放MovieKGQA3.png、MovieKGQA_workflow_graph.png等流程图和截图)、package.json和package-lock.json(前端依赖)。project.private.config.json、project.config.json是小程序项目配置,用微信开发者工具打开时需要。
2.2 数据流:从爬虫到 Neo4j 再到问答
整个系统的数据流向是这样的:
spider抓取电影数据,输出实体(电影、演员、导演、类型)和关系(出演、执导、属于类型)。- 数据导入 Neo4j,形成图结构。常见做法是用
LOAD CSV或直接执行CREATE语句。 backend收到问题后,先做意图识别(比如「周星驰导演了哪些电影」对应「导演-电影」查询),再拼 Cypher 语句查 Neo4j。- 查询结果格式化成自然语言,通过 Flask 接口返回给小程序前端展示。
这个链路里,Neo4j 是核心存储,Flask 是调度中枢,小程序是展示层。理解了这个流向,后面配环境、导数据、调接口就不会迷路。
2.3 环境依赖与版本选择
项目基于 Python 3.x,主要依赖:
| 组件 | 作用 | 常见版本 |
|---|---|---|
| Python | 后端运行环境 | 3.8~3.10 |
| Flask | Web 框架 | 2.x |
| py2neo / neo4j-driver | Python 连 Neo4j | 与 Neo4j 版本匹配 |
| Neo4j | 图数据库 | 4.x 或 5.x 社区版 |
| 微信开发者工具 | 运行小程序前端 | 稳定版 |
提示:Neo4j 社区版免费,下载后需要配置 Java 环境(Neo4j 4.x 需要 JDK 11,5.x 需要 JDK 17)。这一步卡住的人最多,建议先确认
java -version输出正确再继续。
3. 把环境跑起来:Neo4j 安装、数据导入与 Flask 启动
3.1 Neo4j 安装与初始配置
Neo4j 社区版下载后解压,进入bin目录:
# Linux/Mac ./neo4j start # Windows neo4j.bat start启动后浏览器访问http://localhost:7474,默认用户名neo4j,密码neo4j,首次登录会强制改密码。改完密码后,在backend的配置文件中同步修改连接信息。
常见配置项在conf/neo4j.conf:
# 允许远程连接(默认只监听本地) dbms.default_listen_address=0.0.0.0 # 内存配置,根据机器调整 dbms.memory.heap.initial_size=512m dbms.memory.heap.max_size=1G dbms.memory.pagecache.size=512m参数说明:heap是 JVM 堆内存,pagecache是图数据缓存。毕设演示数据量不大,512M~1G 足够。如果启动报内存不足,先调小这两个值。
3.2 导入电影知识图谱数据
spider抓取的数据通常以 CSV 形式存在。导入 Neo4j 的常见做法:
// 导入电影节点 LOAD CSV WITH HEADERS FROM 'file:///movies.csv' AS row CREATE (:Movie {title: row.title, year: row.year, rating: row.rating}); // 导入演员节点 LOAD CSV WITH HEADERS FROM 'file:///actors.csv' AS row CREATE (:Actor {name: row.name}); // 建立出演关系 LOAD CSV WITH HEADERS FROM 'file:///acted_in.csv' AS row MATCH (a:Actor {name: row.actor}) MATCH (m:Movie {title: row.movie}) CREATE (a)-[:ACTED_IN]->(m);逻辑说明:LOAD CSV从 Neo4j 安装目录的import文件夹读取文件,所以 CSV 要放到那里。CREATE建节点,MATCH定位已有节点再建关系。如果重复导入会生成重复节点,建议先用MERGE代替CREATE:
MERGE (m:Movie {title: row.title}) SET m.year = row.year, m.rating = row.rating;MERGE是「存在则匹配,不存在则创建」,适合反复调试时用。
3.3 启动 Flask 后端并验证接口
进入backend目录,安装依赖后启动:
pip install flask py2neo python app.py默认跑在5000端口。用 curl 测一下:
curl -X POST http://localhost:5000/qa \ -H "Content-Type: application/json" \ -d '{"question": "周星驰导演了哪些电影"}'如果返回 JSON 格式的答案列表,说明后端和 Neo4j 已经打通。如果报连接错误,检查backend里 Neo4j 的 URI、用户名、密码是否和实际一致。URI 格式一般是bolt://localhost:7687。
3.4 小程序前端联调
用微信开发者工具打开frontend目录,修改app.js或请求工具里的后端地址,指向你本机的http://localhost:5000。开发者工具里可以勾选「不校验合法域名」,否则本地 HTTP 请求会被拦截。
前端页面通常有一个输入框和一个结果展示区。输入问题后,wx.request把问题 POST 到 Flask 接口,拿到答案后渲染。如果请求失败,先看开发者工具的 Network 面板,确认请求地址、方法、参数都对。
4. 问答逻辑怎么写的:意图识别与 Cypher 模板匹配
4.1 意图分类的常见实现方式
MovieKGQA 的问答核心在backend里。常见做法是维护一个意图模板表,用关键词匹配判断用户问的是哪类问题:
# 意图模板示例 INTENT_PATTERNS = { "director_movies": ["导演", "执导"], "actor_movies": ["出演", "主演"], "movie_actors": ["演员", "谁演"], "movie_genre": ["类型", "什么类型"], } def detect_intent(question): for intent, keywords in INTENT_PATTERNS.items(): if any(kw in question for kw in keywords): return intent return "unknown"逻辑说明:遍历每个意图的关键词列表,命中即返回。这种方式简单直接,适合毕设场景。参数方面,关键词列表可以根据实际数据扩充,比如加上「参演」「出演过」等变体。
4.2 从意图到 Cypher 的映射
识别出意图后,需要提取实体(比如电影名、演员名),再拼 Cypher:
def build_cypher(intent, entity): if intent == "director_movies": return f""" MATCH (d:Director {{name: '{entity}'}})-[:DIRECTED]->(m:Movie) RETURN m.title AS title """ elif intent == "actor_movies": return f""" MATCH (a:Actor {{name: '{entity}'}})-[:ACTED_IN]->(m:Movie) RETURN m.title AS title """ return None逻辑说明:根据意图选择不同的图查询模式。Director和Actor是节点标签,DIRECTED和ACTED_IN是关系类型,这些必须和导入数据时保持一致。实体提取可以用简单的字符串匹配,也可以接一个 NER 模型,毕设里前者够用。
注意:直接拼接字符串有 Cypher 注入风险,虽然毕设场景不涉及安全评审,但养成用参数化查询的习惯更好。py2neo 支持
graph.run(cypher, entity=entity)传参。
4.3 返回结果的格式化
查到的结果是节点属性列表,需要转成自然语言:
def format_answer(intent, results): if not results: return "没有找到相关信息" titles = [r["title"] for r in results] if intent == "director_movies": return f"该导演执导的电影有:{'、'.join(titles)}" return "、".join(titles)这一步决定了用户看到的答案是否自然。可以根据意图加不同的前缀,比如「主演电影」「所属类型」等。如果结果为空,给一个友好提示,别直接返回空字符串。
5. 避坑与排查:环境、数据、接口三类高频问题
5.1 Neo4j 启动报 Java 版本不匹配
现象:执行neo4j start后提示Unsupported Java version或直接闪退。
原因:Neo4j 4.x 需要 JDK 11,5.x 需要 JDK 17。机器上装了多个 Java 版本时,JAVA_HOME可能指向了错误的版本。
解决:确认java -version输出,修改JAVA_HOME指向正确版本。Windows 下还要检查系统环境变量顺序,Linux/Mac 下用export JAVA_HOME=...临时切换测试。
5.2 LOAD CSV 报找不到文件
现象:Cypher 执行LOAD CSV时提示Couldn't load the external resource。
原因:Neo4j 默认只从安装目录的import文件夹读文件,且neo4j.conf里dbms.directories.import可能被改过。
解决:把 CSV 放到import目录下,用相对路径file:///xxx.csv。如果还不行,检查conf里dbms.security.allow_csv_import_from_file_urls=true是否开启。
5.3 Flask 接口返回 500 但日志看不清
现象:前端请求后端,返回 500,但控制台只显示一行错误。
原因:Flask 默认不输出详细堆栈,或者异常被 try/except 吞掉了。
解决:启动时加debug=True,或者在异常处理里app.logger.exception(e)打印完整堆栈。常见错误是 Neo4j 连接失败、Cypher 语法错误、实体名带单引号导致拼接出错。
5.4 小程序请求被拦截
现象:开发者工具里请求一直失败,提示「不在以下 request 合法域名列表中」。
原因:小程序默认只允许 HTTPS 且域名需备案,本地 HTTP 请求被限制。
解决:开发者工具右上角「详情」→「本地设置」→ 勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」。这只在开发阶段有效,上线需配置正式域名。
5.5 数据导入后查询结果为空
现象:Neo4j 里能看到节点,但问答接口返回空。
原因:节点标签或关系类型和代码里写的不一致,比如导入时用了:Film,代码里查的是:Movie。
解决:在 Neo4j 浏览器里执行MATCH (n) RETURN labels(n), count(*)确认实际标签,再对照backend里的 Cypher 修改。关系类型同理,用MATCH ()-[r]->() RETURN type(r), count(*)查看。
6. 进阶技巧:把问答准确率从「能跑」拉到「能答辩」
6.1 用同义词表提升实体匹配率
原始的关键词匹配很脆弱,用户说「星爷」系统就认不出「周星驰」。加一层同义词映射:
SYNONYMS = { "星爷": "周星驰", "发哥": "周润发", "哥哥": "张国荣", } def normalize_entity(text): for alias, standard in SYNONYMS.items(): if alias in text: return text.replace(alias, standard) return text在实体提取前先跑一遍normalize_entity,能明显减少「查不到」的情况。同义词表可以手动维护几十条,答辩演示时覆盖常见问法就够了。
6.2 多跳查询的 Cypher 写法
答辩时老师常问「能不能查演员的导演合作过的其他演员」这类多跳问题。Cypher 的优势就在这里:
// 查某演员合作过的导演执导的其他电影 MATCH (a:Actor {name: '周星驰'})-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Director) MATCH (d)-[:DIRECTED]->(other:Movie) WHERE other.title <> m.title RETURN DISTINCT other.title AS title;逻辑说明:第一行找到演员出演的电影和对应导演,第二行找该导演执导的其他电影,WHERE排除原电影,DISTINCT去重。这种多跳查询是知识图谱相比关系型数据库的亮点,答辩时值得重点讲。
6.3 用 Neo4j 浏览器做可视化验证
在http://localhost:7474里直接执行查询,结果可以切换成图模式展示。节点和关系一目了然,截图放进论文或答辩 PPT 里比表格直观得多。建议提前跑几条典型查询,把图截图存好。
6.4 接口层加缓存减少重复查询
如果演示时反复问同一个问题,每次都查 Neo4j 没必要。加一个简单的字典缓存:
from functools import lru_cache @lru_cache(maxsize=128) def query_kg(question): # 原有查询逻辑 ...lru_cache按参数缓存返回值,适合问答这种「同样问题重复问」的场景。注意如果数据会变,需要手动清缓存或设过期时间。
6.5 答辩前必做的三件事
第一,把 Neo4j、Flask、小程序三端全部重启一遍,确认冷启动没问题。第二,准备 5~8 个典型问题,覆盖单跳、多跳、无结果三种情况,提前跑通。第三,把README.md里的启动步骤自己照着走一遍,别到答辩现场才发现文档和实际不一致。
我自己的习惯是:每次改完 Cypher 或意图模板,先在 Neo4j 浏览器里单独验证查询语句,确认结果对了再写进代码。这样能把「代码问题」和「数据问题」分开排查,省掉很多来回折腾。希望帮到你。
本文还有配套的精品资源,点击获取