Django+DeepSeek大模型知识图谱古诗词情感分析系统

发布时间:2026/10/16 4:56:31

Django+DeepSeek大模型知识图谱古诗词情感分析系统 1. 为什么这个毕设值得做选题价值与整体思路打开各种毕设选题列表一眼扫过去要么是清一色的电商系统、图书管理系统要么是连老师自己都讲不清楚的所谓“人工智能创新”。说实话每年到了毕设季我看到的作品里至少有一半是套着管理系统壳子改个数据库名字就交差的。所以当我看到“DjangoDeepSeek大模型知识图谱古诗词情感分析”这个组合的时候第一反应是这个选题的切入角度挺聪明。古诗词是中文自然语言处理领域里一个非常典型又非常有挑战的场景。文言文、意象、典故、互文这些东西和现代汉语的语法体系差异很大常规的文本分类模型或者情感词典在古诗上的表现往往一言难尽。但古诗词又是博大精深的从情感分析到推荐系统都有可以深挖的空间。这个题目把几个部分串联起来Django做后端框架和Web展示DeepSeek大模型负责语义理解和情感判断知识图谱用于构建诗人、作品、意象、朝代之间的关系网络推荐系统解决“读到哪首”的问题前端做可视化看板。这个组合的妙处在于每一层都有明确的职责不会出现“什么都能干但什么都干不深”的毛病。从毕设的角度来讲这个题目的覆盖面也足够撑起一篇有份量的论文。需求分析、系统设计、数据库建模、接口开发、算法选型、模型调用、前端可视化每一个环节都有东西可写。更重要的是它不是一个纯CRUD的“管理系统”也不是一个纯粹调模型API的“玩具Demo”而是有自己的业务逻辑和数据沉淀在里面。适合什么人做这个题目我个人的判断是Python基础过关、Django用过至少一个完整项目、对大模型API调用不陌生的同学做起来会比较顺。如果你只写过脚本、没碰过Web框架那前期补Django的成本会占用不少时间。但反过来如果做完这个题目你相当于一次性地把“后端开发算法应用数据工程可视化”全串了一遍这对找实习和简历描述都很有帮助。2. 系统架构与核心模块拆解2.1 整体架构从数据到展示的四层链路这个系统的核心链路可以概括为数据入库 → 图谱构建 → 模型分析 → 前端展示。数据先整理成结构化记录存进数据库同时按实体和关系抽象成知识图谱然后DeepSeek大模型对诗词文本做情感维度分析最后Django把分析结果和推荐结果通过接口送到ECharts渲染的可视化页面。分层的好处是每一层都可以独立修改和测试。比如情感分析模块想从规则方法换成大模型调用只需要改中间的服务层不用动前端的页面结构。同理推荐算法如果想从基于知识图谱路径扩展改为基于相似度计算也只是替换一个函数的问题。数据库层面我建议使用MySQL。虽然SQLite在Django里开箱即用但这个项目涉及知识图谱的实体关系存储SQLite对大批量查询的并发支持比较弱而且后面做可视化大屏展示时会有多个聚合查询同时打过来MySQL会稳很多。Django的ORM可以帮你屏蔽大部分SQL差异所以前期开发用MySQL也是可以的不用纠结。Redis在这个项目里不是必需品但如果你想让推荐接口响应更快可以加上。做法很简单用户请求推荐结果时先查缓存命中了直接返回没命中再走算法计算计算完写回缓存设置一个合理的过期时间。这个点不难但在论文里能作为系统性能优化的一节来写。2.2 Django项目结构设计项目结构上我建议按Django的标准习惯来组织但增加两个专门的应用一个负责数据处理和分析一个负责推荐引擎。这样分后面写文档和答辩时会好讲很多。poetry_project/ ├── manage.py ├── poetry_project/ # 项目配置目录 │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── poems/ # 古诗词基础数据应用 │ ├── models.py # 诗词、作者、意象等模型 │ ├── views.py # 页面视图 │ └── urls.py ├── analysis/ # 情感分析与知识图谱应用 │ ├── sentiment.py # DeepSeek情感分析服务 │ ├── graph.py # 知识图谱构建与查询 │ └── urls.py # 分析结果接口 ├── recommend/ # 推荐系统应用 │ ├── recommender.py # 推荐引擎核心逻辑 │ └── urls.py ├── templates/ # Django模板目录 ├── static/ # 静态资源(JS/CSS/图片) └── data/ # 爬取或整理的原始数据这样的结构让每个功能模块都有归属地修改一个模块不会牵连其他文件。特别提醒一点不要把情感分析逻辑写在视图函数里。视图只负责接收HTTP请求和返回HTTP响应业务逻辑应该放到独立的服务模块中。这不只是代码洁癖的问题而是后面你要写单元测试的时候就会发现独立服务类测起来远比测视图函数方便。2.3 核心数据表设计数据库是整个系统的地基。设计数据表的时候我有一条原则宁可多拆两张表也不要在字段里塞逗号分隔的字符串。下面是这个项目里最核心的几张表我按实际开发顺序列出来诗词表字段名类型说明idint主键titlevarchar题目dynastyvarchar朝代authorvarchar作者contenttext正文内容translationtext译文/注释appreciationtext赏析keywordsvarchar关键词标签created_atdatetime入库时间content字段用text而不是varchar因为古诗词不全都是绝句和律诗还有长诗和词牌几百上千字的作品很常见。translation和appreciation这两个字段可能暂时用不上但如果数据源里已经有这些信息建议一并存下来。后面做大模型情感分析的时候这些辅助文本可以作为上下文辅助判断对提升准确率很有帮助。诗词意象表字段名类型说明idint主键namevarchar意象名如“明月”categoryvarchar意象类别如“自然/天文”emotion_tagvarchar常见情感倾向如“思乡/孤寂”意象是古诗词情感表达的最小语义单元。“明月”是思乡怀人“杨柳”是送别留恋“落花”是时光易逝。这些意象和情感之间的关联是搭建知识图谱时最重要的边关系之一。正则匹配意象词表可以把每首诗关联到多个意象这是后面知识图谱可视化最出效果的一部分。情感分析结果表字段名类型说明idint主键poem_idint关联的诗词IDemotion_labelvarchar情感标签如思乡、豪迈、愁苦、恬淡等sentiment_scorefloat情感极性得分-1到1之间confidencefloat置信度analysis_detailtext模型返回的详细分析文本created_atdatetime分析时间把分析结果单独存一张表而不是在诗词表上加两列是为了保留多次分析的记录。后面你做模型效果对比的时候比如用规则方法和大模型各跑一遍就会发现独立结果表的好处了——可以横向比较不同方案的分析差异也能做统计分析看看不同朝代诗词的情感分布规律。3. 数据准备与知识图谱构建3.1 古诗词数据集获取与清洗跑这个项目的第一步是有数据。古诗词的公开数据集很多一般能找到几万到几十万首不等的语料。我建议选那种已经整理成JSON或者CSV格式的字段里包含朝代、作者、题目、正文的不要选纯文本打包的因为解析成本太高。拿到原始数据之后清洗工作非常关键。这一步我吃过大亏一上来就直接导入数据库结果前二十首就有三首的正文里带着出处注释还有的标题后面跟着“其二”“三首”之类的序号和题目本身混在一起。清洗的优先级是这样排的第一去掉空白字符和异常换行。很多语料里每句诗后面有个换行但多音字和繁体字保留情况不统一。我的做法是先把全文转成统一的简体再把句子按标点重新拼接。第二统一朝代和作者字段。同一个作者在不同数据集里的写法可能不一样比如有的写名有的写字有的写全名。务必做一次名称归一化否则后面知识图谱里同一个诗人会被拆成好几个节点。第三去重。同一首诗在不同语料来源里可能出现多次字段可能略有差异。我建议以“作者题目”作为业务唯一键配合正文做二次校验。第四格式过滤。每个朝代和每个作者的数据量都要统计一遍如果发现某个朝代突然缺了很多要回头检查清洗规则是否误删了。清洗完成之后我建议把最终数据导出成一个干净的JSON文件保留原始文件作为备份并写一份数据说明文档记录来源和清洗规则。这些在论文的数据处理章节都是要写的内容提前理清楚能给你省不少时间。3.2 知识图谱实体与关系建模知识图谱在这个项目里的定位是连接文本数据和语义信息。说白了它就是一个图结构节点表示实体边表示关系。这个项目涉及的实体类型主要有四类诗人、朝代、诗词、意象。诗人节点要存姓名、字号、朝代、简介、代表风格朝代节点可以只存朝代名和时间段诗词节点存题目、正文、情感标签意象节点存名称、类别、典型情感倾向。节点之间的关系用过几种比较常用的是这样几条诗人属于某个朝代、诗人写了某首诗、诗中含有某个意象、诗被归入某类情感。关系建模的过程中最容易遗漏的是“意象-情感”之间的隐式关系。比如“梧桐”通常和愁苦相关“大漠”通常和苍凉豪迈相关。这些关系不直接从哪首诗里读出来而是跨诗归纳出来的。这部分知识可以用大模型来辅助抽取。具体做法把某类意象相关的诗批量发给DeepSeek让它总结这些诗中该意象的情感倾向分布然后把结论整理成语义关系写进图谱。图数据库选型上我建议直接用Neo4j。Django项目自带的关系型数据库存不了图数据的高效多跳查询。Neo4j有开源的社区版Python客户端也很成熟。如果你的服务器内存比较紧张可以先用Neo4j的AuraDB免费云实例数据量在几万节点级别时完全够用。知识图谱在这个系统里发挥作用的地方主要有两个。一是可视化展示把所有诗人和他们作品的关系投射到前端界面上用户能直观地看到某个朝代的诗词分布和意象网络二是推荐系统的基础后面详细说。3.3 从结构化数据到图谱的构建流程图谱构建不是一蹴而就的我习惯分成三步来走。第一步将清洗后的JSON数据解析为实体对象这一步工作量最大要用ORM把每个实体写入Neo4j第二步建立实体之间的关系这些关系基于已有的关联字段诗属于哪个作者、作者属于哪个朝代等第三步用Django的管理命令定期重跑图谱构建因为数据源可能后续扩充让构建流程可重复执行。Neo4j的写入操作建议用MERGE语法而不是CREATE。CREATE每次执行都会创建新节点重复跑脚本就会产生重复节点。MERGE会先检查节点是否存在存在就更新属性不存在才创建。这个细节能省去你大量手动的数据清洗工作。# 构建知识图谱的核心代码片段 from neo4j import GraphDatabase class KnowledgeGraphBuilder: def __init__(self, uri, user, password): self.driver GraphDatabase.driver(uri, auth(user, password)) def add_poem(self, poem_info): with self.driver.session() as session: session.execute_write(self._create_poem_node, poem_info) staticmethod def _create_poem_node(tx, poem_info): query MERGE (p:Poem {id: $id}) SET p.title $title, p.content $content, p.dynasty $dynasty WITH p MERGE (a:Author {name: $author}) MERGE (a)-[:WROTE]-(p) WITH p, a MATCH (d:Dynasty {name: $dynasty}) MERGE (a)-[:BELONGS_TO]-(d) tx.run(query, **poem_info)批量导入的时候我建议分批提交比如每200条commit一次不然数据量过万之后写入速度会明显下降。而且Neo4j的事务对大批量单次提交的内存消耗较高分批处理能减少内存压力。构建完成后用简单的Cypher查询做数据校验比如查每个朝代的诗人数量、每类意象的诗词数量确认数据基本合理再进行可视化。4. 基于DeepSeek大模型的情感分析实现4.1 为什么古诗词情感分析要用大模型写代码之前先想清楚一个基本问题为什么选大模型来做情感分析而不是传统的情感词典方法传统情感词典方法的核心是维护一个带有情感极性分值的情感词表然后扫描句子、词频加权求和得出整体情感得分。这个方法在电商评论这种现代白话场景下效果尚可但遇到古诗词就会碰壁。“春风得意马蹄疾一日看尽长安花”这句从字面看全是正面词汇但结合诗人的生平处境这里面的“得意”带着强烈的反讽和自我安慰意味。词典方法完全捕捉不到这层语境。也有基于机器学习的方法比如用TF-IDF特征配合分类模型做训练。但问题是标注好的古诗词情感数据集非常稀少自己标注几千条的成本又太高小规模数据训练的模型泛化能力也不行。大模型方案走的是另一条路。DeepSeek这类模型在海量古诗文语料上做过预训练对文言文的语义理解、意象联想和典故把握都远超传统模型。我们不需要自己训练模型只需要设计好提示词把任务描述清楚、把输出格式限定好模型就能返回结构化的分析结果。准确率通常比词典方法高出不少。这里不是说要神化大模型而是它在这个场景下的确解决了传统方法解决不了的核心矛盾——上下文语义理解。4.2 提示词工程让大模型输出稳定的结构化结果大模型调用本身不难难点在于让输出结果稳定可用。古诗词情感分析需要的是结构化结果不是一段散文式的评论。我踩了几次坑之后总结出一套相对稳定的提示词方案。先说我犯过的错误最开始我直接把诗词文本扔给模型问“这首诗表达了什么情感”结果十个请求十种回答格式有的答一大段有的只回三个字有的还自己加分析段落。解析非常痛苦。后来我改用强约束格式的提示词把输出限定为JSON并规定必须包含的字段情况立刻好转。提示词的核心结构是这样的SENTIMENT_PROMPT 你是一位精通中国古典文学的专家。请对下面的古诗词进行情感分析。 诗词题目《{title}》 作者{author} 朝代{dynasty} 正文 {content} 请从以下情感标签中选择最合适的一个或多个 [思乡, 离别, 爱国, 忧民, 怀古, 豪迈, 愤懑, 愁苦, 孤寂, 恬淡, 喜悦, 爱情, 其他] 要求 1. 选择标签时考虑整首诗的创作背景和意境。 2. sentiment_score表示情感极性范围从-1极度消极到1极度积极。 3. confidence表示你对该判断的置信度范围0到1。 4. 请严格按照以下JSON格式返回不要添加任何其他内容 {{ emotion_label: 主要的情感标签, sentiment_score: 0.0, confidence: 0.0, analysis_detail: 简要说明判断依据不超过150字 }} 这个提示词的关键点有三个一是身份设定。让模型扮演“精通中国古典文学的专家”这个前缀看似不起眼实际上能显著提升输出质量。模型在角色设定下会更倾向于调用它自身存储的古典文学知识而不是泛泛地套用现代文本分类的逻辑。二是枚举标签要覆盖主流情感类别。如果标签集合太泛比如只有“积极”“消极”分析结果就失去了区分度。如果太细碎模型容易选错。我最终定的这些标签基本覆盖了古诗词里最常见的几类而且类目之间有区分度不会让模型在两个近义标签之间反复横跳。三是强制JSON输出。模型对“严格按照JSON格式返回”的理解能力很强加上提供了明确的字段示例输出稳定率能达到95%以上。剩下的少数情况多半是模型返回了JSON包裹在代码块里或者多了逗号导致解析失败。所以后端的解析容错也要写好对JSON解析失败的情况要做兜底处理不能因为一条数据格式不对就让整个批量任务崩掉。4.3 批量情感分析任务调度与结果落库做一个批量任务需要考虑两个问题调谁怎么存调谁就是选择DeepSeek的API还是本地部署。毕设场景下我推荐用API部署成本低直接用起来也方便。缺点是依赖网络以及有调用配额限制。我自己的习惯是批量处理之前先把要分析的全部诗词导成一个列表然后分批调用每批之间加一个较短的时间间隔避免触发限流。import json import time import requests from .models import Poem, SentimentResult class DeepSeekSentimentAnalyzer: def __init__(self, api_key, base_url): self.api_key api_key self.base_url base_url self.headers { Authorization: fBearer {self.api_key}, Content-Type: application/json } def analyze_poem(self, poem): prompt SENTIMENT_PROMPT.format( titlepoem.title, authorpoem.author, dynastypoem.dynasty, contentpoem.content ) payload { model: deepseek-chat, messages: [ {role: system, content: 你是一个严谨的古诗词分析工具必须按要求返回JSON。}, {role: user, content: prompt} ], temperature: 0.1, max_tokens: 500 } resp requests.post( f{self.base_url}/chat/completions, headersself.headers, jsonpayload ) resp.raise_for_status() content resp.json()[choices][0][message][content] return self._parse_json(content) def _parse_json(self, content): # 处理返回内容提取JSON部分 content content.strip() start content.find({) end content.rfind(}) 1 if start -1 or end 0: raise ValueError(No JSON found in model output) json_str content[start:end] # 移除可能导致解析失败的尾逗号 json_str json_str.replace(,}, }) return json.loads(json_str)批量任务执行时我建议加一个断点续传机制。就是每处理完一首诗就把结果写入SentimentResult表然后记录当前处理进度。如果中途网络断了或者API报错下次从断点继续跑就行不用从头再分析一遍。这个看似不起眼的细节在几千首诗词的数据量下能节省大量时间。温度参数可以调低。temperature0.1让模型输出更稳定、更少发散情感分析这类任务需要的是确定性不是创造性。如果要跑实验对比不同温度下的输出差异也可以做成参数配置但默认值建议保持低温度。4.4 情感分析结果的校验与可视化结果落库不代表结束还要做校验。我自己的校验方式很简单随机抽几十首诗人工阅读分析结果从“标签是否合理”和“分数是否偏颇”两个维度打分。如果失误率高就回去调整提示词里的标签定义甚至可以在提示词里加上几句示例诗句作为few-shot参考。校验之后是可视化。这里我用的方案是ECharts加Django接口。情感分布饼图展示各情感标签的占比情感极性分布柱状图展示分数区间朝代热力图展示不同朝代的情感变化趋势。ECharts对Django后端和前端的配合很友好只要求后端返回固定的JSON结构前端用fetch请求接口然后传给ECharts的setOption即可。fetch(/api/sentiment/distribution) .then(res res.json()) .then(data { const chart echarts.init(document.getElementById(sentimentChart)); chart.setOption({ tooltip: { trigger: item }, series: [{ type: pie, radius: 60%, data: data.labels.map((label, i) ({ name: label, value: data.values[i] })) }] }); });可视化的价值不只是给答辩老师看好看的图更重要的是帮助我们发现数据背后的规律。比如分析完之后我发现愁苦和思乡这两类在晚唐时期的占比显著高于初唐和盛唐这类结论在论文“实验分析”章节里非常加分。5. 推荐系统从知识图谱到大模型增强5.1 推荐系统的设计目标与方案对比推荐系统在这个项目里的任务是当用户点开一首诗的时候系统能给他推荐相关的诗。这里的“相关”不应当只停留在文本相似度还应该包含情感相似、时代相似、作者风格相似、意象相似等多个维度。如果只是按关键词匹配来推等于没做推荐直接做一个SQL查询就够了。对比几种方案基于内容的推荐把每首诗的特征向量化比如TF-IDF或者Embedding然后算余弦相似度。优点是实现简单缺点是会忽略跨时代的语义关联比如李白和苏轼之间的风格呼应。协同过滤推荐收集用户行为收藏、浏览等再算相似用户。这个方案在毕设数据量下比较困难因为用户行为数据太少。如果只是自己演示数据基本都是空的协同过滤算法没有发挥空间。基于知识图谱的推荐把实体关系转化成特征计算诗与诗之间的路径相似度。这个方案能解释推荐理由“因为你喜欢李白的诗而苏轼与李白同属豪放派”推荐结果有可解释性适合毕设展示。大模型增强的推荐把用户当前感兴趣的诗和一些候选诗的特征描述发给大模型让模型做排序或选品。优点是推荐结果更自然能体现大模型的“理解力”缺点是延迟较高而且逻辑可解释性弱如果回答不稳定不好控制。我的建议是主用知识图谱推荐然后用大模型做补充推荐理由生成。这样既有计算层面的确定性又有大模型的自然语言输出两类技术在系统里各司其职不会互相抢夺主导权。5.2 基于知识图谱的推荐逻辑基于知识图谱的推荐核心是计算图上的相关度。常用的指标包括最短路径长度、共同邻居个数、图谱上的随机游走概率。举个例子用户当前正在阅读李白的《静夜思》。图谱上存在这些路径《静夜思》和《月下独酌》共享“月亮”意象而且作者都是李白所以路径很短推荐优先级高《春江花月夜》与《静夜思》共享“月亮”和“思乡”情感路径包含意象节点和情感节点也是强相关《登高》在情感上都是愁苦但意象差异较大相关性中等到低。具体实现上可以先把每首诗的标签和意象作为路径特征构建所有诗之间的相似度矩阵然后取top Nclass GraphRecommender: def __init__(self, graph_driver): self.driver graph_driver def recommend(self, poem_id, top_k10): # 用Cypher查询与当前诗共享意象的诗 query MATCH (p:Poem {id: $poem_id})-[:CONTAINS]-(i:Image)-[:CONTAINS]-(other:Poem) WHERE NOT p other WITH other, COUNT(i) AS shared_images ORDER BY shared_images DESC RETURN other.id AS poem_id, shared_images LIMIT $top_k with self.driver.session() as session: result session.run(query, poem_idpoem_id, top_ktop_k) return [record[poem_id] for record in result]这段查询找的是共享同一个意象节点的其他诗然后按共享数降序排序。这个逻辑直观而且可以通过“共享意象”给用户解释推荐理由。实际使用的时候我一般会综合多个维度来打分共享意象的权重最高因为是语义层面的强关联同作者的作品占一定权重同朝代的作品权重较低情感标签相同的作品可以额外加分。把这些权重加权之后得到一个综合分数再取top N。权重参数可以做成可配置方便后面做效果调优。5.3 DeepSeek在推荐理由生成中的用法知识图谱的推荐结果给的是“推什么”但用户往往还想知道“为什么推这个”。这时候大模型就派上用场了。做法是这样从知识图谱推荐里取出当前诗和目标诗的公共特征共享意象、情感标签、作者、朝代把这些特征拼成一段描述文本发给DeepSeek让它生成一段30字左右的推荐理由。还是《静夜思》的例子RECOMMEND_REASON_PROMPT 用户在阅读古诗词《{current_title}》这首诗的情感标签是{current_emotion} 包含的意象有{current_images}。 系统根据以下共同点推荐了《{rec_title}》 共同意象{shared_images} 相似情感{shared_emotions} 其他关联{other_relations} 请用不超过50字的一段话从中国古典文学的欣赏角度说明推荐原因 要求自然、有文学气息不要生硬罗列标签。 这样生成的推荐理由不那么冷冰冰比如可能生成“同写明月一静一酌皆见李白对孤独的安放”这种输出比前端弹一个“推荐指数87分”要有说服力得多。而且这个环节能很好地体现大模型在系统里的价值——不是只有情感分析一处用到它推荐模块也有参与。5.4 推荐结果的接口设计与前端联动推荐接口设计上我走的是RESTful风格。GET /api/poems/{poem_id}/recommend返回结构{ code: 200, data: [ { poem_id: 20, title: 月下独酌·其一, author: 李白, dynasty: 唐, reason: 同写明月一静一酌皆见李白对孤独的安放。, score: 0.92 } ] }前端拿到数据之后在诗词详情页下方渲染一个“相关推荐”卡片列表。每张卡片显示推荐理由点击可以跳转到对应诗词详情。这一整套交互链路做完系统就从一个“情感分析看板”升级成了“有阅读推荐能力”的古诗词平台。5.5 推荐系统的评估与调优毕设里的推荐系统不用追求完美的线上指标但要能说明白“效果如何”。我的做法是构建一个小规模人工标注的测试集从语料库里随机抽100首诗每首标记3-5首人工认为相关的诗词然后用覆盖率和平均准确率评估推荐效果。覆盖率衡量的是推荐结果里有多少在人工标记者列表里命中。准确率衡量的是推荐结果中被用户认定为相关或标注为相关的比例。如果覆盖率太低说明推荐结果过于集中在少数热门诗词上可以考虑加入随机游走类的图算法比如PersonalRank增加推荐结果的多样性。如果准确率不够高就回去调整各维度权重或者扩充意象知识库。这里提醒一句调参要有记录每组参数跑出来的评估结果要存一张表方便后面在论文里展示调优过程和效果对比。6. 前端可视化与Django视图层实现6.1 前端技术选型与页面布局前端部分我用的是Django模板加原生JavaScript加ECharts的组合。没有引入前端框架因为页面数量不算多而且是典型的管理后台加展示型页面。Vue这类框架虽然也能用但需要在Django里额外配一套构建工具链对毕设来说增加了不少无形成本。页面布局我分成四个主要视图首页看板系统概况诗词总数、诗人总数、朝代分布、意象统计。情感分析页情感饼图、情感极性柱状图、朝代情感热力图、情感趋势折线图。知识图谱页诗人、朝代、意象的图谱关系网络支持节点点击高亮和关系展示。诗词详情页展示诗词全文、作者信息、情感分析结果以及推荐诗词列表。这套布局把系统的主要功能都挂在导航栏上评委打开系统就能看到各模块入口不用到处找。6.2 ECharts知识图谱可视化知识图谱页是视觉上最出彩的一块用的是ECharts的graph系列即关系图。节点按类型分组诗人节点、朝代节点、诗词节点、意象节点分别用不同的颜色和大小表示。连线表示关系比如“属于”“写作”“包含”。数据来源是Django接口返回的JSON节点列表和连线列表一起返回def knowledge_graph_api(request): data get_graph_data() return JsonResponse({ nodes: data[nodes], links: data[links], categories: [ {name: 诗人}, {name: 朝代}, {name: 诗词}, {name: 意象} ] })前端拿到数据之后直接配置ECharts的graph数据项即可。有一点要提醒节点数量如果超过几百个页面的交互流畅度会下降。所以展示时做筛选。默认只显示当前选中诗人的部分子图或者通过下拉框选择朝代来切换显示范围。这不光是技术优化也是交互设计上更合理的选择让用户不至于面对一团乱麻的关系网络。const chart echarts.init(document.getElementById(graphChart)); chart.setOption({ tooltip: {}, series: [{ type: graph, layout: force, force: { repulsion: 300, edgeLength: 100 }, categories: data.categories.map(c ({ name: c.name })), data: data.nodes.map(node ({ id: node.id, name: node.name, symbolSize: node.symbolSize, category: node.category })), links: data.links.map(link ({ source: link.source, target: link.target, label: { show: true, formatter: link.label } })), label: { show: true, position: right } }] });repulsion是节点间斥力值越大节点间距越大。edgeLength控制边的长度。这两个参数需要根据实际数据量手动调一下一般先跑一遍看看效果再微调。这个阶段不用花太多时间精修布局因为力导向图每次渲染都会震荡主要保证能看清关系结构就行。6.3 Django视图与接口的标准化写法视图层有一个容易犯的错误把业务逻辑全堆在views.py里。我在这个项目里采用的是“视图-服务-接口”三层写法。views.py只负责渲染页面或者转发请求具体的业务逻辑在对应的服务模块中完成接口统一返回JsonResponse或者在HTML页面上渲染模板。诗词列表页的视图以列表页加搜索分页为例def poem_list(request): keyword request.GET.get(keyword, ) dynasty request.GET.get(dynasty, ) poems Poem.objects.all() if keyword: poems poems.filter( Q(title__icontainskeyword) | Q(content__icontainskeyword) ) if dynasty: poems poems.filter(dynastydynasty) paginator Paginator(poems, 10) page paginator.get_page(request.GET.get(page)) return render(request, poems/poem_list.html, { page: page, keyword: keyword, dynasty: dynasty, })这种写法的好处是逻辑清晰、便于修改调试。搜索和筛选条件用Django ORM的过滤语法就能搞定不需要写原生SQL。需要注意一点Q查询对象做模糊搜索时数据量大之后可能比较慢但毕设数据量通常不会到几十万这个性能问题基本不用操心。6.4 看板页聚合统计接口看板首页要用到几个聚合接口总数统计、朝代分布、情感分布、意象热门Top10。这些数据都可以用Django ORM的聚合查询实现不需要额外存一张冗余统计表。from django.db.models import Count def dashboard_stats(request): poem_count Poem.objects.count() author_count Author.objects.count() dynasty_data list( Poem.objects .values(dynasty) .annotate(countCount(id)) .order_by(-count) ) emotion_data list( SentimentResult.objects .values(emotion_label) .annotate(countCount(id)) .order_by(-count) ) return JsonResponse({ poem_count: poem_count, author_count: author_count, dynasty_data: dynasty_data, emotion_data: emotion_data })不把统计结果缓存到数据库是有意为之的。数据更新频率不高实时聚合的开销完全可以接受而且避免了“统计表和业务表数据不一致”的经典问题。如果你的数据集到几万条这个查询也基本在一秒内能完成。7. 关键技术难点与问题排查实录7.1 大模型API调用中的限流与超时处理大模型API调用第一个拦路虎就是限流。我用DeepSeek的API跑批量分析时一开始用的是简单循环一首接一首发请求。前一百首还好跑到后面开始间歇性出现超时或者HTTP 429错误。后来总结出来的应对方案是批量任务加重试机制遇到超时或者429就退避等待再重试每次请求之间加固定间隔每500首做完强制休息一段时间。重试机制的实现要小心避免无限重试导致任务挂起。我写的重试逻辑是这样的import time def call_with_retry(fn, max_retries5, base_delay1): for attempt in range(max_retries): try: return fn() except requests.exceptions.Timeout: if attempt max_retries - 1: raise time.sleep(base_delay * (2 ** attempt)) except requests.exceptions.HTTPError as e: if e.response.status_code 429: retry_after int(e.response.headers.get(Retry-After, 5)) time.sleep(retry_after) else: raise指数退避是重试的常规策略第一次等1秒第二次等2秒第三次等4秒不会因为频繁重试把服务端的限流阈值顶得更厉害。7.2 Neo4j与Django的同步问题知识图谱的数据来源是关系型数据库里的诗词表。如果只做一次性的导入把数据写进Neo4j之后两者之间就没什么联动问题。但如果你想增加新诗词或者修改已有数据就得考虑同步策略。我采用的方案是“定时全量重建加增量补充”。全量重建的脚本可以手动触发适合数据源更新不频繁的场景。增量补充则是每次新增诗词写入MySQL之后同时调用图谱构建服务把新节点加进Neo4j。这个方案在毕设阶段够用了不用引入复杂的消息队列来做异步同步。Neo4j的连通性也值得先测试一下。本地跑Neo4j之后Django项目连接Neo4j的配置可以放到settings.py里最好用一个专门的连接模块来管理不要在多个地方裸写连接地址和账号密码。7.3 前端大图渲染的性能优化知识图谱页一开始我直接加载了全部几千个节点页面直接卡死了。后来做了三方面的优化。第一节点裁剪。默认只显示当前选中朝代的范围其他朝代的节点收进一个聚合节点里点击再展开。第二限制显示数量。每次最多渲染500个节点。超过这个数量就按权重取前500个并在界面上提示“已按权重显示前500个节点”。第三初始化时关闭动画等数据布局稳定后再开启。力导向图在动画状态下每次修改数据都会重新布局会带来明显的性能问题。经过这一轮优化之后页面交互流畅了很多。给一个量级参考500个节点加1200条边在普通笔记本上基本能保持在60帧左右的交互响应。7.4 模型输出格式解析失败的兜底方案大模型输出偶尔不稳定是常态。即使提示词里写了“必须返回JSON”还是会出现输出被markdown代码块包裹、或者末尾多了个逗号、甚至直接给了一段纯文本的情况。所以解析层必须健壮。我的兜底策略是四级先用正则提取JSON片段去掉JSON里多余的尾逗号如果JSON解析失败尝试用json.loads修复后再试还不行就标记为“解析失败”把原始输出存到日志表里方便人工事后排查。def safe_parse_model_output(content): # 提取第一个{到最后一个}之间的内容 start content.find({) end content.rfind(}) if start -1 or end -1: raise ValueError(No valid JSON found) json_str content[start:end 1] try: return json.loads(json_str) except json.JSONDecodeError: # 移除尾逗号 fixed json_str.replace(,}, }).replace(,], ]) return json.loads(fixed)这个函数不能保证百分百成功但能把成功率从80%拉到95%以上。剩下那5%的失败数据在批量任务里会被记录等整轮跑完了再人工处理。大模型输出是概率性的兜底不是为了消灭失败而是把失败控制在可管理的范围内。7.5 数据导入与编码问题古诗词数据里的编码问题也是坑。有的数据集是从网页上爬的繁体字和简体字混在一起还有全角半角标点不一致。统一转码时可能会出现生僻字丢失的情况。我建议在数据清洗阶段就把所有文本统一成Unicode使用UTF-8编码存取数据库连接参数也明确指定UTF-8避免在写入MySQL时出现乱码。还有一个容易踩的坑诗词正文里的中文引号、书名号、波浪线等特殊符号在导入JSON或者CSV时可能被转义搞坏。处理方式是统一使用ensure_asciiFalse写入并用标准库的json.dumps保证格式正确。8. 项目部署与答辩准备要点8.1 本地部署到服务器的基本流程到后期要展示项目不能只在自己电脑上跑。部署到服务器或者云主机能让演示更稳定也不受本机环境影响。部署流程大概是准备一台服务器环境安装Python、MySQL、Neo4j把项目代码传到服务器创建虚拟环境安装依赖迁移数据库启动Django服务。最省心的是用Django内置的开发服务器先跑通再用uWSGI加Nginx做正式部署。我的建议是如果答辩演示时间紧凑可以先用开发服务器跑启动方便不容易出问题。正式部署的配置可以写在论文里答辩时快速带过即可不用为了部署的复杂度牺牲演示稳定性。部署时有一个环境隔离的重灾区。DeepSeek的API Key、MySQL密码、Neo4j密码这些信息绝对不能硬编码在代码里。我用的是Django的python-decouple读取环境变量把敏感配置放到.env文件中并把.env加到.gitignore里。这一步既是安全习惯也是论文里的加分项。8.2 演示环境与演示脚本的准备答辩演示翻车率最高的环节是网络问题。如果现场网络不稳定DeepSeek API调用超时情感分析模块就卡在那里非常尴尬。我的建议是准备两张底牌。第一张底牌把情感分析结果提前缓存到数据库里。演示时从数据库读取结果展示不走在线API调用。批量子任务在前一天晚上全部跑完第二天展示时打开系统直接看到分析结果速度和稳定性都有保障。第二张底牌准备一个演示脚本。打开首页看板按序展示统计图进入知识图谱页选择某个朝代查看关系网络点开一首诗展示情感分析结果和推荐列表。每一步控制在30秒左右全程不超过五分钟。答辩的时候照着这个顺序走不容易遗漏功能点。8.3 论文写作中的重点章节规划论文结构上我的建议是把系统设计和实验分析这两章写厚实。系统设计一章把架构图、数据库ER图、知识图谱模型、接口设计完整呈现。实验分析一章不要只写“系统运行正常”要有数据、有对比。把情感分析准确率做一下量化评估比如随机抽样100首人工标注情感和DeepSeek输出做比对统计一致率。这个数据既有说服力答辩时也能回答“效果怎么样”的追问。推荐系统的评估同样要写。把知识图谱推荐和随机推荐、标签推荐做一下对比录一个准确率对比表格。有这个对比论文的含金量会高很多。我个人在做这个项目时的一点体会是把大模型接进系统并不难真正花时间的地方都在数据准备和结果校验上。古诗词本身的歧义性决定了情感分析不可能做到完全准确但只要你能说清楚系统的设计逻辑、实现路径、评估结果和局限性答辩老师是非常认可的。这种选题展示的不只是“我调了一个API”而是“我知道怎么把多个技术组件组织成一个完整功能系统”。想清楚这条线这个毕设的整体价值就立住了。
延伸阅读

更多相关文章

2026/10/16 4:51:30

TensorFlow Lite端侧推理实战:模型转换、量化与委托优化指南

1. 从训练到部署:为什么端侧推理是移动AI的最后一公里做过移动端AI功能的人都有一个共同感受:模型在服务器上跑得好好的,一放到手机上就各种问题。推理速度慢、内存占用高、包体积膨胀、不同芯片表现差异巨大——这些坑几乎每个团队都会踩一遍…

2026/10/16 4:51:30

TensorFlow Lite移动端推理集成实战:从模型转换到硬件加速

1. 移动端推理的工程化抉择把训练好的模型塞进手机里跑起来,这件事听起来简单,做起来坑多。我最早接触端侧推理是在一个图像分类的Demo上,当时模型在服务器上跑得好好的,一到手机上就各种问题:包体膨胀、推理耗时飙到几…

2026/10/16 4:51:30

AI日报系统设计:分层流水线架构与本地化实践

1. 项目概述:这不是一份新闻简报,而是一套可复用的AI信息流处理系统“AI 日报(2026年10月8日)”这个标题乍看像一份时效性极强的媒体简报,但作为从业十多年、亲手搭建过二十多个垂直领域信息聚合系统的博主&#xff0c…

2026/10/16 5:56:33

车载实时通信中间件设计:基于UDP的确定性BSP协议实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:56:33

Python函数式编程思维:从PTA第六章看模块化设计本质

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:56:33

Nginx反向代理Tomcat完全指南:从零配置到生产实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:56:33

深入解析blk-mq数据结构:多队列架构与性能调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:56:33

OpenCV卷积边缘处理:五种borderType原理、选型与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/16 0:01:11

Cephadm实战指南:Linux系统工程师的生产级Ceph集群部署与排障

简介:本资源是一份完整的Ceph分布式存储系统中文技术文档合集,面向Linux系统运维工程师、云平台架构师及存储方向开发者,帮助读者系统掌握Ceph集群的部署、配置与运维核心能力。文档覆盖对象存储(RGW)、块设备&#xf…

2026/10/16 0:01:11

ITK中SpatialObject几何体系解析:坐标系、变换与管道集成

1. 空间对象的定位:为什么 ITK 需要一套几何描述体系很多刚开始接触 ITK 的朋友,包括我自己,最初都是被它的图像滤波、配准、分割这些“正统”功能吸引进来的。等真正上手做点实际项目,比如把分割出来的器官边界做量化分析、给血管…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑