简介:本资源是一套基于知识图谱技术实现的《三国演义》人物关系可视化与智能问答系统,专为计算机类专业本科生毕业设计、课程设计及科研入门者打造,覆盖人工智能、自动化、电子信息等方向,解决古典文学数据结构化建模与语义交互的实际问题。压缩包共605个文件,含20个核心Python脚本(构建图谱、抽取关系、训练问答模型)、16个JavaScript与8个HTML文件(前端可视化与交互界面)、22个CSS样式文件(含Bootstrap、Nifty、Font Awesome等主流框架支持)、490张人物/关系图示JPG及JSON、CSV等结构化数据文件,整体大小15.67MB,目录组织清晰,模块职责分明。已有70人学习下载,资源包含完整项目说明文档、可稳定运行的源码、详细部署指南及测试用例,小白可获远程技术支持,进阶者亦可在现有架构上扩展实体识别或对话逻辑。 毕设做知识图谱方向,我是举双手支持的。一方面它把《三国演义》这种经典文本变成了可检索、可推理的结构化数据,比单纯做个CRUD管理系统有含量得多;另一方面知识图谱涉及数据采集、实体关系抽取、图数据库、可视化、自然语言问答,链条长、技术面广,论文和答辩都不愁没东西讲。这篇就把我当时做“三国人物关系可视化及问答系统”的完整思路、踩坑记录和可复现方案一次性写清楚。
1. 项目概述与整体架构
1.1 这个项目到底做了什么
整个系统分成两大部分:第一部分是知识图谱的构建与存储,把《三国演义》里的人物、阵营、官职、事件、地盘等信息抽出来,整理成“实体—关系—实体”的三元组,存进图数据库Neo4j;第二部分是基于这套图谱做问答和可视化,用户在网页上输入“谁杀了关羽”“诸葛亮和司马懿是什么关系”这类自然语言问题,系统能理解提问意图、生成查询语句、把答案返回给用户,同时把人物关系网络用力引导布局图展示在浏览器里。
我最终整理出来的数据规模大概是这样:有名有姓的人物实体1100多个,加上地名、官职、战役、阵营等实体一共1600个左右;关系三元组6500多组,涉及“父子”“兄弟”“效力于”“交战于”“杀害”“继承”等18种关系类型。这个体量放在毕设里非常合适——太小显得工作量不足,太大又容易陷入清洗数据的泥潭。
1.2 系统架构选型思路
整体架构分三层:数据层、服务层、展示层。
- 数据层:Neo4j图数据库,负责存储三元组和提供Cypher查询。
- 服务层:Python Flask提供RESTful接口,负责把自然语言问题翻译成Cypher查询语句。
- 展示层:Vue3 + ECharts,负责渲染人物关系图、属性面板和搜索联想框。
有人可能会问,为什么不直接用Gephi或者Neo4j自带的Browser做可视化?因为毕设要呈现的是一个完整的“系统”,不是单个工具演示。Neo4j Browser虽然能展示图谱,但它的UI风格一眼就是数据库管理工具,交互方式也满足不了“按关系类型筛选、点选人物看详情”这种需求。自己用ECharts封装一套前端,界面是可控的,答辩时演示效果也更好看。
至于为什么选Flask而不是Django或FastAPI,我的考虑是:问答逻辑本身不复杂,一共就十几个接口,Flask轻量、上手快,文档又多,出了问题好查。FastAPI性能更好但也更现代,如果你熟悉异步编程并用Pydantic做数据校验,用FastAPI也完全可以,不影响整体设计。
2. 深度设计:人物关系图谱怎么建模
2.1 实体类型设计
知识图谱的核心是本体设计。本体设计得好不好,直接决定后面所有查询和问答能不能跑通。我当时把实体分成了六种。
- 人物:这是绝对核心,三国演义里出现过的所有角色都要收录。
- 阵营:魏、蜀、吴,再加上东汉朝廷、黄巾军、袁绍势力、吕布势力等。
- 地点:州、郡、县城池名,比如许昌、江陵、赤壁、街亭。
- 战役:官渡之战、赤壁之战、夷陵之战这些大型军事行动。
- 官职:丞相、大将军、司马、军师等,反映人物的政治和军事地位。
- 事件:桃园结义、三顾茅庐、草船借箭、白帝城托孤等。
这种多实体类型的设计比“只有人物”的作品要高一个档次,因为它可以支撑更多样的查询,比如“蜀汉阵营有哪些人物”“赤壁之战发生在哪里”“诸葛亮担任过哪些官职”。
2.2 关系类型设计
实体是骨架,关系是灵魂。我在关系设计上花了很多时间反复调整,最终确定了按语义维度划分的18种关系。
| 语义维度 | 关系类型 | 示例 |
|---|---|---|
| 亲属关系 | 父子、兄弟、夫妻、叔侄 | 曹操-曹植:父子;孙策-孙权:兄弟 |
| 效力关系 | 效力于、背叛于 | 诸葛亮-刘备:效力于;许攸-袁绍:背叛于 |
| 军事关系 | 交战于、杀害于、围困于 | 曹操-袁绍:交战于;马谡-张郃:杀害于 |
| 政治关系 | 拥立、废黜、册封 | 董卓-汉献帝:废黜 |
| 社交关系 | 结义、举荐、师从 | 刘备-关羽:结义;徐庶-诸葛亮:举荐 |
| 归属关系 | 所属阵营、统治区域 | 曹操-许昌:统治区域 |
这里我想特别解释一下为什么把“杀害于”单独拿出来。在三国的语境里,人物死亡原因往往和历史走向强相关,比如关羽被杀直接引发夷陵之战,这个关系对问答系统来说价值极高。很多初做知识图谱的同学会把“死因”做成人物实体的属性,但这样检索能力非常弱。做成关系之后,“谁杀了关羽”这种问句就能直接转化为MATCH (p:人物{name:'关羽'})<-[:杀害于]-(killer),整个查询链路极其顺畅。
2.3 为什么把“效力于”拆成动态关系
这里有一个设计上的细节值得说说。我一开始把“人物—阵营”直接建成(刘备)-[:属于]->(蜀汉)这样的静态关系,后来发现根本行不通,因为三国人物的阵营归属是会变的。张辽先跟吕布,后归曹操;黄忠先跟刘表,后归刘备;贾诩先后侍奉过董卓、李傕、张绣、曹操。如果只用一个静态的属于关系,查“张辽属于哪个阵营”的时候就会得到一堆互相矛盾的结果。
所以我后来把关系改成了带属性的动态关系:(张辽)-[:效力于 {起始年份:198, 结束年份:220}]->(曹操),这样既保留了“效力于”这个语义,又能通过属性标注时间段。查询“张辽最终属于哪个阵营”的时候,按结束年份倒序排序取最近一条就行。这种设计在答辩时是很好的加分点,因为它体现的不是“会调框架”,而是“理解了业务的复杂性”。
3. 数据准备与图谱构建:全网最脏的活
3.1 数据来源怎么选
知识图谱项目第一步永远是数据。我的数据来源有三条线。
第一是中文维基百科的人物词条,包括信息栏里的“姓名、字、号、生卒年、籍贯、官职、势力”等结构化字段,这是主体数据的来源。
第二是各小说文学网站整理的人物关系表。很多三国同人站、资源站有现成的人物关系清单,虽然格式不统一,但作为交叉验证的参考价值很高。
第三是一份公开的三国人物关系数据集,格式是CSV三元组,可以直接导入。网上这类资源不少,搜“三国人物关系图谱数据”或者GitHub上的Chinese-Knowledge-Graph项目就找得到。
这里给新手的忠告是:不要自己从零看原著标注数据,工作量太大且容易漏。正确策略是“以开源数据集为基础,用百科全书数据做补充,再人工校对关键人物”。我大概花了四天时间做数据清洗,最终把原始的三万多条噪声数据压缩到六千五百条有效三元组。
3.2 实体对齐与去重
实体对齐是知识图谱构建里最折磨人的环节。简单说就是“同一个实体在数据源里可能有许多不同的名称”,需要把它们合并成一个节点。
《三国演义》里这个问题尤其严重,因为人物有本名、字、号、别称,还有避讳改名。举几个我实际处理过的例子:
- 诸葛亮:字孔明、号卧龙,又被尊称武乡侯、诸葛丞相。
- 赵云:字子龙,常被称常山赵子龙。
- 关羽:字云长,别称关公、关云长、美髯公。
- 曹丕:字子桓,被汉献帝册封后称魏王。
我的处理方案是给每个人物节点建立一个别名列表属性,把所有身份映射到同一个实体上。具体做法是:先加载所有数据源,然后针对每个实体维护一个“标准名称→别名集合”的字典,利用规则匹配加少量人工校对完成对齐。对了,三国人物姓名和字基本都有对应规律可查,这方面中文百科的Tabel字段很有用。
3.3 Neo4j批量导入的踩坑记录
数据清洗完成后,下一步就是导入Neo4j。这里我强烈建议用LOAD CSV批量导入,而不是逐条用Python写Cypher插入。后者在几千条数据量级还能跑,上到几万条就会慢到怀疑人生。
首先把数据整理成CSV格式,人物节点和关系分开。导入前记得先创建唯一约束,这是很多教程漏掉的关键步骤:
CREATE CONSTRAINT ON (p:人物) ASSERT p.name IS UNIQUE; CREATE CONSTRAINT ON (c:阵营) ASSERT c.name IS UNIQUE; CREATE CONSTRAINT ON (l:地点) ASSERT l.name IS UNIQUE;然后写导入脚本。这里给一个简化版的人物导入示例:
LOAD CSV WITH HEADERS FROM 'file:///characters.csv' AS row MERGE (p:人物 {name: row.name}) SET p.alias = split(row.alias, '|'), p.birth = row.birth, p.death = row.death, p.office = row.office, p.description = row.description关系的导入要稍微复杂一些,因为需要两个MERGE先找到两端的节点,再MERGE关系:
LOAD CSV WITH HEADERS FROM 'file:///relations.csv' AS row MATCH (a:人物 {name: row.source}) MATCH (b:人物 {name: row.target}) MERGE (a)-[r:效力于 {startYear: toInteger(row.startYear), endYear: toInteger(row.endYear)}]->(b)这里的坑在于:如果source字段的值在人物表里不存在,MATCH就匹配不到,这一行会静默跳过。所以导入完成后一定要跑校验查询,统计有多少关系没有匹配上源节点和目标节点:
MATCH (a:人物)-[r]->(b:人物) RETURN count(r) AS totalRelations;还有一个隐藏问题是编码。Windows下导出的CSV常有BOM头和GBK编码问题,Neo4j导入时中文会乱码。我的解决方案是:所有CSV统一用UTF-8编码,并且在Python侧做清洗时预先去掉BOM头。这个坑非常隐蔽,但如果遇到乱码问题,第一个就要检查它。
4. 问答系统实现:从自然语言到Cypher
4.1 问答系统的三层结构
问答系统是整个项目里技术含量最高的部分。我的设计思路不是只做一个简单问答,而是做成三层结构:问题预处理层、意图识别层、查询生成层。
第一层预处理:把用户输入的问题做清洗和标准化,包括统一全半角标点、繁简转换(防止用户用繁体提问)、分词。
第二层意图识别:核心工作是判断“用户想查什么”,具体来说就是识别出问题里的实体和关系类型。比如“谁杀了关羽”这句话,要先识别出“关羽”这个人物实体,再看“杀了”这个动作对应哪种关系。意图识别我采用了“规则模板 + 词典匹配”的方式,并没有上复杂的机器学习模型,原因后面会讲。
第三层查询生成:把意图和实体映射成一条Cypher查询语句,发送给Neo4j执行,然后把结果格式化。
4.2 词典和规则模板怎么搭
先说实体识别部分。因为领域非常窄(只有三国相关实体),所以我直接用词典匹配。把所有人物名、别名、地名、官职名、战役名整理成一份大词典,用最大正向匹配算法在问题里找实体。最大正向匹配的思路是:从句子开头往右找尽可能长的词,如果这个长词在词典里,就切分出来;否则缩短长度继续找。这一步我用了jieba分词器的自定义词典功能,把实体词典直接加载进去,让分词结果尽可能贴合三国实体。
再来说意图识别。我把问题分成六类意图:
| 意图类型 | 问法示例 | 对应查询模式 |
|---|---|---|
| 人物关系 | 谁杀了关羽 | (关羽)<-[:杀害于]-(?) |
| 人物信息 | 诸葛亮的字是什么 | (诸葛亮).别名 |
| 阵营成员 | 蜀汉有哪些将领 | (?)-[:效力于]->(蜀汉) |
| 战役详情 | 赤壁之战双方的将领是谁 | (?)-[:参战]->(赤壁之战) |
| 事件详情 | 三顾茅庐是什么事件 | (三顾茅庐).description |
| 地点归属 | 许昌在谁的治下 | (?)-[:统治区域]->(许昌) |
每种意图对应若干组触发词。比如“关系类”意图的触发词有“谁杀了”“谁攻打”“谁的儿子”“谁的父亲”“某和某什么关系”;“阵营类”意图的触发词有“有哪些”“将领”“谋士”“成员”等,同时问题里必须出现阵营实体名。识别流程就是先抽取实体,再根据触发词匹配意图,最后将两者组合成查询语句。
4.3 一个典型的问答流程拆解
举个完整例子,用户输入“谁杀害了关羽”。
第一步做实体识别,从词典里匹配出“关羽”是人物实体。同时识别到“杀害”这个动作。
第二步做意图识别,“谁…杀害…”命中“人物关系-受害者”模式,确定查询方向是(关羽)<-[关系的发出方]-(答案)。
第三步构建Cypher查询:
MATCH (victim:人物 {name: '关羽'}) MATCH (killer:人物)-[r:杀害于]->(victim) RETURN killer.name AS 凶手, r.reason AS 原因第四步把查询结果格式化成自然语言答案:“杀害关羽的人是吕蒙。马忠抓住了关羽,孙权下令处决。”我在返回答案时加了一个reason属性字段存背景故事,让答案不只是干巴巴的名字,这一点在答辩演示时特别能展示系统的深度。
4.4 为什么不用实体消歧和机器学习
在这里说说一个很多人会踩的坑:一上手就打算用BERT或者什么深度学习模型做意图识别。我的建议是,在毕设阶段不要这么做。原因有三个:
第一,你没有足够多的标注数据。三国问答这个特定领域,网上几乎没有现成的训练集,你最多手动标几百条,这点语料微调一个BERT模型效果非常有限,反而容易过拟合。
第二是规则系统可控。评委问“这个意图怎么识别的”,你可以讲得清清楚楚;你告诉他“用BERT黑盒识别”,反而容易被追问到说不出细节。
第三是知识图谱问答对这个场景,规则模板的覆盖率已经足够。我是做了六类意图、四十多组模板,测试下来对系统自带的示例问题准确率在九成以上。真遇到规则覆盖不了的问题,我会返回一句“这个问题我还在学习,请换一种问法”,这本身就是合理的系统边界。
当然,如果你想展示自己懂机器学习,可以在系统里做一个可选的“NLP增强模块”,用一个TF-IDF加朴素贝叶斯的分类器来兜底识别意图类型,再补充词典提取实体。这种方式既展示了ML能力,又不会因为数据集太小而跑飞。
5. 可视化大屏:把人物关系画出来
5.1 数据可视化技术选型与对比
可视化部分是整个系统最有视觉冲击力的模块。我当时在ECharts和D3.js之间犹豫了很久,最终选择了ECharts的graph类型。
ECharts的优势在于配置简单、图例和提示框开箱即用、力引导布局的渲染性能足够支撑千级节点。D3.js要自己实现力模拟器、缩放拖拽、节点渲染逻辑,工作量会大很多。对毕设来说,ECharts是完全够用的。
但我要提醒一点:ECharts的graph类型默认渲染是Canvas,节点数在两三百时交互还很流畅,到了上千节点就会出现拖拽卡顿。我的方案是:默认只展示核心人物和热门关系(筛选出度入度总和前80的人物),全量图谱作为“展开全部”的可选功能。这种“局部优先、按需全量”的设计,在答辩演示现场特别实用,不会因为节点太多而卡死在评委面前。
5.2 全量关系图和局部子图什么时候用
我做了两套可视化视图。
全量视图展示的是整个关系网络,节点用不同的颜色代表不同阵营——蜀汉红色、曹魏蓝色、东吴绿色、其他势力灰色。节点大小和该人物的“关联度”成正比,关联度我定义为入度加出度的总和。这样一来,哪个角色在演义中的“社交网络”最复杂,一眼就能看出来。诸葛亮、曹操、刘备、孙权这几个节点的尺寸明显大于其他人,视觉冲击力很强。
局部视图是点击某个具体人物时,只展示与这个人直接相关的人物和关系。比如点击“曹操”,界面会弹出他周围的关系网——儿子有曹丕、曹植、曹彰,谋士有荀彧、荀攸、郭嘉,对手有刘备、孙权、袁绍,每个关系线旁边标注关系名称。这个交互方式对阅读理解《三国演义》的人际关系非常有帮助,问答系统也常常会跳转到这个视图。
5.3 大屏交互的用户体验优化
我在打磨可视化这个环节上花的时间不少,有几个交互细节是真正提升了使用体验的。
第一是关系类型筛选。图谱里十八种关系同时显示会让人觉得乱,我加了一个筛选面板,用户可以选择只看“亲属关系”或“军事关系”等类别,未选中的关系线会淡出。这样既能表达信息丰富度,又不会让用户被信息淹没。
第二是搜索定位。可视化页面顶部有一个搜索框,支持拼音首字母和模糊匹配。输入“zgl”,能联想出“诸葛亮”;输入“关”,能联想出“关羽、关平、关兴、关索”。这个功能用Neo4j的STARTS WITH加别名匹配就可以实现,但在答辩时非常惊艳。
第三是点击与悬停的联动效果。悬停某个节点时,它的相邻节点高亮、不相邻节点变灰;单击节点弹出右侧详情面板,展示人物的生平介绍、头像、出生去世年份、所属阵营、涉及战役列表。这些数据都存在Neo4j节点的属性里,前端通过API动态加载。
6. 后端接口与查询优化:让系统跑得稳
6.1 Flask接口设计
后端我设计了六个主要接口,全部基于Flask Blueprint做模块化:
/api/search:关键词联想搜索,前端搜索框调用。/api/entity/{name}:获取人物或实体的详情信息。/api/entity/{name}/relations:获取某个实体的相邻关系,用于局部视图。/api/graph/full:获取全量图谱数据。/api/qa/question:问答接口,接收用户问题返回答案。/api/categories:获取关系类型列表,用于可视化筛选面板。
每个接口返回JSON格式数据。前端用Axios调用,跨域问题通过Flask-Cors解决。这里插一句,前端开发模式如果和后端端口不同,一定要记得配置CORS,否则浏览器会拦截所有请求,典型表现为“接口能通但页面报错”。我在本地调试时被这个问题坑过一下午。
6.2 查询性能和索引优化
Neo4j在处理深度遍历关系时性能优势很明显,但前提是索引建得对。我在人名、地名、阵营名等关键属性上全部建了索引,复杂查询的响应时间基本控制在100毫秒以内。如果查询超过500毫秒,我会优化Cypher语句而不是加机器配置。
举一个典型的优化例子。查“某人的二度关系”(即朋友的朋友)时,初版Cypher是这样的:
MATCH (a:人物 {name: '曹操'})-[:效力于|:交战于|:亲属*1..2]->(b:人物) RETURN DISTINCT b.name LIMIT 50这个查询在数据量小的时候没问题,但全量查询时会因为关系路径爆炸而变慢。优化方案是拆成两步查询,先查一度关系收集中间实体ID,再根据ID集合查二度关系。这样查询计划的可控性更强,也能精确控制返回数量。
另外一个很重要的实践是在后端做Redis缓存。热门查询比如“谁杀了关羽”“曹操的儿子有哪些”这类问题,用户会反复问。我会在Flask层加一个简单的缓存装饰器:先查Redis,命中就直接返回,未命中再查Neo4j并将结果写入Redis,键值设为实体名加关系类型。这个优化在并发量不大时性能提升不明显,但面试答辩时讲出来,能体现你有生产环境的意识。
7. 常见问题与排查技巧实录
7.1 问题速查表
我把实际操作中遇到的典型问题整理成了一张速查表,每一条背后都是实打实的调试经历。
| 现象 | 原因 | 处理方式 |
|---|---|---|
| 中文乱码 | CSV编码非UTF-8或有BOM头 | 统一转UTF-8-sig,去除BOM |
| 某些关系死活查不到 | 导入时两端节点未匹配 | 导入后跑孤立关系校验 |
| 前端跨域报错 | 未配置Flask-Cors | 注册CORS插件并设置allow_origins |
| ECharts节点重叠严重 | 力引导布局参数未调整 | 调大repulsion和edgeLength参数 |
| 某问题返回空答案 | 意图模板没覆盖该问法 | 检查意图识别日志,补充模板 |
| 模糊搜索速度慢 | Neo4j属性未建索引 | 在常用属性上创建索引 |
| 人物名称不统一 | 实体对齐不彻底 | 扩展别名映射表 |
| 页面容器溢出 | 大屏适配未做 | 用rem和vw/vh做响应式 |
7.2 几个值得展开讲的坑
第一个坑是Neo4j的MATCH静默失败。刚做完导入时我信心满满跑问答,结果发现“诸葛亮效力的阵营是哪个”能答出来,“曹操的儿子是谁”却一直返回空。查了半小时才发现问题:曹操这个人物在图里有两个节点,一个来自人物表,一个来自关系表的源节点,名字相同但ID不同,关系全部挂在了另一个节点上。后来的解决方式是先删除重复节点,再用MERGE强制按name属性合并:MATCH (p:人物) WITH p.name AS name, collect(p) AS nodes WHERE size(nodes)>1 ...逐个处理重复。
第二个坑是ECharts的graph布局初始渲染位置。全量数据第一次加载时,几百个节点可能堆在画布左上角,看起来是一坨黑点。后来我通过设置layout: { type: 'force', initial: 'center' }以及手动指定一些核心节点的初始坐标,解决了首屏布局的问题。
第三个经验是Neo4j的日志要会看。系统跑着跑着突然查询超时,控制台又没有任何报错,第一反应应该是查看Neo4j的服务日志,比如debug.log和query.log。有时候是长查询占用了过多内存导致GC停顿,可以通过在Neo4j配置里限制事务内存dbms.memory.transaction.max_size来缓解。
7.3 日志、监控与调试工具
调试问答系统时,我加了一个记录所有问句和对应Cypher查询日志的功能。用户每次提问,后端会把原始问句、识别到的意图、实体、生成的Cypher、查询耗时、返回结果完整记录到本地日志文件。这个工具在开发期的价值太大了,因为你可以随时翻出之前的错误查询分析是哪一步出了问题。答辩演示时也可以现场展示日志,证明系统“确实在处理自然语言”,细节感拉满。
8. 从毕设到进阶:知识图谱还能往哪走
8.1 关系抽取的自动化升级
这次毕设的关系抽取大部分依靠开源数据和规则对齐,真正做到“从自然语言文本自动抽关系”的部分很少。如果你想把这个项目做成一个更完整的作品,或者作为找工作的项目经验,下一步的自然演进是用预训练语言模型做自动关系抽取。思路是这样的:把《三国演义》全文按章节切分,用命名实体识别技术找出所有人物名,再通过关系分类模型判断句子中两个人物的关系类型。这个方案对古汉语文本有一定挑战,但确实能展示你在NLP方向上的延伸能力。
8.2 从模板问答升级到RAG
问答系统目前依赖预设模板,覆盖面有限。如果你想把它升级成真正的开放域问答,可以走RAG(检索增强生成)路线。具体思路是:把知识图谱里的三元组转成自然语言描述,比如“诸葛亮-效力于-刘备”转成“诸葛亮曾效力于刘备”,存入向量数据库;用户提问时,先在向量库里做语义检索,找到相关的三元组片段,再扔给大语言模型生成答案。
这条技术路线现在也正好是热点。我自己搭过一套基于llama.cpp加Qwen2-7B的本地RAG问答系统,做法很成熟:用llama.cpp跑Qwen2量化模型,用fastapi包一层接口,文档进行embedding后用向量检索召回,再拼prompt让模型生成。如果你把三国知识图谱作为RAG的知识源,最后得到的是一个既能精准回答结构化问题、又能自由对话的复合型问答系统,这个方案放在简历上是很有分量的。
8.3 可视化大屏的扩展方向
现在的可视化停留在静态关系图上,进阶方向可以做时间轴动画。三国一百多年的历史,人物关系其实一直在变化——赵云前期跟公孙瓒,后来才跟随刘备。做一个时间轴滑动条,用户拖动年份,图上只显示该年份活跃的人物和关系,这种动态图谱在展示上会非常震撼,也契合“数据大屏”的概念。
另外一个不错的方向是接入地图。把地点实体映射到现代地图坐标上,做成“三国战役地图”,用户点击某个城市就能看到该地发生过的所有历史事件。这个功能可以基于ECharts的地图组件或Leaflet实现,后端只需增加一个地点坐标表。技术上不复杂,但作品的完整度和话题性都会上一个台阶。
写在最后的几点体会
做这个项目最大的体会是:知识图谱的难点从来不在调框架,而在数据质量。你的关系抽取得再漂亮,如果源头数据里有大量重复、矛盾、别名不统一的脏数据,后面所有查询问答的正确率都会大打折扣。所以奉劝准备做类似题目的同学,一定不要把时间都花在写前端特效上,要在数据清洗阶段投入足够的精力。
还有一点想提醒的是:毕设答辩时,评委最常问的问题不是“你用了什么技术”,而是“你为什么要这么设计”。这要求你对每一个选择都能讲出取舍依据——为什么用Neo4j而不是MySQL,为什么用规则而不是BERT,为什么关系要分成十八种而不是三种。把“为什么”想清楚,比把代码跑通更值钱。这个项目做完之后你再去复盘,会发现你已经不是刚开题时的那个只会写CRUD的自己了。
本文还有配套的精品资源,点击获取