☰
基于Django与LLM的考研院校智能推荐系统设计与实现
2026/10/3 10:03:42 网站建设 项目流程

每年考研初试成绩出来那几天,我朋友圈基本都会被两类内容刷屏:一类是晒分,另一类是求助——拿着分数和各平台截图,反复问“这个分能不能上XX大学XX专业”。这个场景暴露了一个存在很多年的问题:考研择校需要比较的信息太多了,历年分数线分散在学校官网、研招网、各种论坛的Excel表格里,而每个人的本科背景、专业偏好、短板科目又完全不同。我当时给学弟规划毕业设计时,第一眼就看中了这个题目:用Django搭后端骨架,接LLM大模型做智能院校推荐,再配合分数线预测和可视化大屏,把整个项目做成一个完整的考研院校推荐系统。做完之后无论从工作量、技术含量还是答辩效果看,都相当能打。今天我把项目从0到1的方案、源码设计和踩过的坑完整写出来,供正在选题或已经开题的同学参考。

1. 选题动机与项目定位:为什么非要做"Django+LLM考研推荐"

1.1 考研信息过载是真实痛点

我接触过不少考研的学生,发现绝大多数人择校的方式非常原始:先去研招网筛一遍专业目录,再去学校官网翻往年复试线,然后打开三五个公众号文章对比,最后凭感觉拍板。这个流程最少要花两到三周,而且信息之间经常打架——有的学校按国家线公布,有的学校单独划线,有的专业每年分数波动大到没法看。

更有意思的是,每位考生的状态完全不同:跨专业的、非全的、往届脱产的、本科双非想冲名校的……这些需求如果只用传统表单筛选,很难表达清楚。用一个搜索框让用户直接输入“我想考杭州的、计算机相关的、最好不考数学的专业”,就非常需要自然语言理解和语义匹配能力。这也正是LLM大模型最适合切入的地方:把人话转成结构化条件,再结合院校知识库做推荐和解释。

1.2 这个题目为什么好过普通的CRUD毕设

很多计算机毕业设计还停留在“用户注册-信息列表-后台管理”的三件套,这类系统功能完整但缺少亮点,答辩时老师很难问出深度的技术问题。而这个题目的差异化体现在三层:

第一层是工程能力:Django项目要能跑通完整的注册登录、院校查询、推荐记录、后台管理,这本身就是正经的Web开发训练,工作量大但难度可控。

第二层是算法与模型能力:LLM大模型的接入不是简单的调API,而是涉及Prompt工程、知识库检索、token成本控制等一整套工程师思维。分数线预测又需要做特征工程、模型对比、结果评估,这些内容写进论文里都是实打实的“实验章节”。

第三层是数据可视化能力:考研数据天然适合做大屏展示——地区分布、专业热度、分数线走势、推免比例,任何一个维度拉出来都能用ECharts做成交互图表。毕业设计只要有一段像样的可视化大屏,整个项目“看起来”的完成度就会高一个档次。

1.3 技术栈选型的取舍

在技术选型上我踩过一次坑:一开始想全上微服务,把推荐、预测、可视化拆成三个服务,结果发现一个毕业设计根本不需要这么重的架构。最后定下来的组合非常务实:

  • 后端:Django + Django REST Framework,自带Admin后台做数据管理
  • 数据库:MySQL存结构化数据,Redis做缓存和会话管理
  • 向量检索:用轻量级的Chroma或FAISS存院校信息向量,避免引入ES导致部署复杂度爆炸
  • LLM:API接入为主,预留本地模型的切换接口
  • 前端:Vue3 + Element Plus,可视化部分用ECharts,大屏单独做一页
  • 预测:先用线性回归和XGBoost跑基线,再对比LSTM在时序预测上的表现

这个组合的好处是每一层都能独立讲解,答辩时被问到任何一个环节都能往深处聊。

2. 系统整体的技术骨架:Django项目和核心数据模型怎么设计

2.1 项目目录和App划分

这个系统我建议拆成5个App,而不是把所有逻辑堆在一个app里。分工清晰之后,写论文的时候每个章节对应一个App,思路完全不会乱:

  • users:注册登录、用户画像(本科层次、目标地区、意向专业、是否跨考)
  • schools:院校库、专业库、历年分数线管理
  • recommend:LLM推荐接口、推荐记录、基于规则的传统推荐兜底
  • predict:分数线预测模型的训练和预测接口
  • dashboards:聚合统计接口、可视化大屏的数据支撑

目录结构大致是这样的:

kaoyan_system/ ├── manage.py ├── config/ # Django项目配置 ├── apps/ │ ├── users/ │ ├── schools/ │ ├── recommend/ │ ├── predict/ │ └── dashboards/ ├── scripts/ # 爬虫、数据清洗、模型训练脚本 ├── static/ └── templates/

2.2 核心数据表设计

数据模型是整个系统的地基,设计得好不好直接决定后面推荐和预测能做什么。核心表我列几个最重要的:

院校表(school):学校名称、所在省份、城市、院校层级(985/211/双一流/普通本科)、院校类型(综合/理工/师范等)、标签字段(用于向量检索的原文拼接)。

专业表(major):专业代码、专业名称、所属学科门类、是否考数学、是否统考专业课。专业名称必须做标准化,因为爬回来的数据里“计算机科学与技术”和“计算机技术”经常混用。

分数线表(score_line):这是最核心的表,字段包括院校ID、专业ID、年份、报考人数、录取人数、复试线、录取均分、国家线对应情况。多了一个复合唯一约束(学校ID,专业ID,年份),避免重复数据。

用户画像表(user_profile):用户ID、本科院校层次、目标地区、意向专业方向、是否跨考、对院校层级的期望(冲/稳/保)。

推荐记录表(recommend_log):用户ID、LLM返回的结果、使用的Prompt版本、模型名称、用户最终是否点赞,这个表在论文里可以作为“基于用户反馈的效果分析”数据来源。

这里要特别说一个设计原则:不要为了存JSON而存JSON。很多同学喜欢把LLM返回的推荐结果直接塞进一个JSONField里面,图省事。但如果你想分析“哪个Prompt效果好”、“推荐结果和用户后续行为是否一致”,一定要把结构化的关键字段拆出来单独存,JSONField只用来保留原始返回用于回溯。

2.3 Django Admin和数据管理的关系

毕设阶段没有专门的运营后台需求,Django自带的Admin其实完全够用。我的做法是给Admin注册好五个核心模型,然后设置好list_display和list_filter,用来维护学校、专业和分数线数据。

真正值得注意的地方是:Admin在毕设系统里要承担“数据校验入口”的角色。爬虫清洗之后的数据不要直接入库,先导出一份CSV人工检查,再通过Admin的批量导入功能写入。这一步能拦住绝大多数脏数据问题,也会被答辩老师看作“工程素养”的体现。

# schools/admin.py @admin.register(ScoreLine) class ScoreLineAdmin(admin.ModelAdmin): list_display = ("school", "major", "year", "recurve_line", "admission_count") list_filter = ("year", "school__province") search_fields = ("school__name", "major__name")

3. 考研数据怎么来:爬虫采集、清洗、入库的一整套流程

3.1 数据源分析与采集策略

考研领域的数据源其实相当分散。我的策略是分三类采集:研招网的招生目录(专业代码和招生人数权威)、各学校研究生院官网(复试分数线和录取名单最准)、第三方考研论坛的往年数据汇总(噪声很大,但胜在齐全,适合做交叉验证)。

爬虫框架我用的是Scrapy,并且单独建了一个scripts/scrapy_project的目录,没有塞进Django项目内部。原因是Scrapy的Twisted事件循环和Django的ORM偶尔会冲突,而且爬虫跑起来很吃资源,没必要和Web服务放在同一个进程里。爬完之后通过Item Pipeline把数据写入MySQL,这里直接用Python的pymysql即可。

爬虫的细节上,有一条经验必须分享:访问学校官网时一定控制频率,随机延时设在3到5秒之间,碰到有反爬的就换一条数据链路,不要硬刚。这不是技术能力问题,是职业道德和法律风险问题。你只是做毕设,数据量不需要大到触发风控。

3.2 字段归并与清洗规则

清洗是整个项目里最耗时、最无聊、但最不能被跳过的部分。考研数据的脏主要体现在三个层面:

  • 专业名称不统一:同一所学校的同一个学院,今年叫“计算机科学与技术”,前年叫“计算机应用技术”。必须建立别名映射表,做归并。
  • 分数数据格式混乱:有的页面给“复试线330”,有的给“复试线330(政治50/外语50/业务课90)”。单科线的解析规则要单独写,不然混合在一起没法用。
  • 缺年份或多年份合并:个别学校只公布近三年总表,不会分开年份。这时候只能手动核对官网原始链接,或者从互联网档案馆的缓存里捞数据。

我建议在清洗阶段把每一步的清洗规则都单独写成一个Python函数,并且给每条数据打上清洗标记。后面写论文时,“数据预处理方法”这一节的素材就极其饱满,直接把这些函数贴上去配说明文字即可。

3.3 分数线特征工程与冷启动处理

数据入库之后,不能直接用原始列去喂模型。分数线预测需要构造这些特征字段:

  • 年份特征(距2015年的差值、是否有复试改革标记)
  • 报考热度特征(当年报考人数/前三年平均报考人数)
  • 招生规模变化特征(录取人数相对于上一年的增量)
  • 所在地区竞争系数(用该省份全部院校的平均分漂移量表示)
  • 学科门类常数特征(理学、工学、文学的基础分差异)

冷启动问题也必须在系统里处理:有些新增专业只有一年数据,像这种情况预测模型根本跑不动。我的兜底方案是提取出同门类、同地区院校在同一年的分数均值作为baseline,然后叠加一个“新专业热度扰动值”。这套逻辑虽然朴素,但在答辩现场解释起来非常清楚,比硬上复杂模型可信得多。

4. LLM大模型在院校推荐中到底怎么用:从Prompt到RAG落地

4.1 为什么这个场景适合接入LLM而不是纯规则

传统推荐系统有两种主流思路:基于内容的过滤和协同过滤。前者靠标签匹配,考研院校的标签本来就粗糙,没有标准化体系;后者依赖大量用户行为数据,毕设阶段根本积累不到。规则引擎虽然能实现“地区+专业+层级”的筛选,但用户需求一旦变成“我数学不太好,想找复试线看数学单科的专业”这种表述,规则就完全抓瞎了。

LLM在这个场景里的核心价值是意图解析+语义召回+结果解释三合一。用户输入一句自然语言,模型先拆解出结构化条件,再从院校知识库里召回候选,最后用自然语言生成推荐理由。整个过程对用户非常友好,也是演示环节最能引发评审兴趣的亮点。

4.2 基于RAG的院校知识库检索增强

刚开始做的时候,我以为直接把用户问题丢给大模型,它就能根据常识回答。实测发现完全不行:大模型对具体院校的专业设置和历年分数线的记忆是混乱的,经常出现幻觉,把A学校的分数线安到B学校头上。后来我改成RAG(检索增强生成)方案:

用户输入 ↓ 意图解析(LLM提取地区、专业、层级、限制条件) ↓ 结构化过滤(MySQL查询获取候选集) ↓ 候选院校信息向量化检索(Chroma中过滤TopK) ↓ 拼接上下文 + Prompt模板 ↓ LLM生成推荐结果

这里的关键点在于:不能把所有院校数据全塞给LLM,而是先通过结构化条件把候选集缩小到10所左右,再通过向量相似度取Top5作为上下文。这样token消耗可控,回答质量也稳定。

院校信息向量化时要注意,向量内容不能只放学校的招生简章摘要,应该把“专业名称+历年分数线+录取人数变动+所在城市+院校层级”拼接成一段结构化的描述文本,再交给Embedding模型。这样向量之间的相似度才能准确体现“这个学校该专业好不好考”的语义。

4.3 Prompt模板设计与流式输出实现

推荐阶段的Prompt模板我调试了很多版,目前这套最稳定:

你是一位熟悉国内高校考研情况的择校顾问。请根据以下历年考研数据和用户需求,推荐3所合理的院校。 用户需求: {user_requirement} 候选院校数据: {retrieved_context} 输出要求: 1. 每所院校给出推荐理由、风险和适合人群; 2. 必须基于数据,不要虚构分数线; 3. 如果用户有明确稳/冲/保的期望,请分别标注。

除了推荐Prompt,还有一个非常实用的“意图抽取Prompt”,先用小参数模型或API把用户的自然语言需求转成JSON结构体:

请从用户描述中提取以下字段,输出JSON: {"regions": [], "major": "", "school_level": "", "math_required": "", "expected_level": "冲/稳/保", "other_notes": ""}

这个两步走的Prompt设计比一步到位要稳得多。小任务用简单的抽取Prompt,大任务再进入推荐Prompt,任何一个环节出错都不会浪费太多token。

流式输出方面,我用的Django + Server-Sent Events(SSE)实现打字机效果,比WebSocket简单很多。Django视图里用一个StreamingHttpResponse持续yield数据块,前端用EventSource监听即可。这个方法我强烈推荐:代码量小,效果又很惊艳。

4.4 成本控制与降级方案

LLM调用是有成本的,毕设演示的时候绝对不能让接口裸奔。我做了三层防护:

第一层是Redis缓存,相同的用户输入直接命中缓存,不再重复调用模型。虽然用户问题基本不会完全相同,但把“意图抽取”结果缓存起来还是能省很多调用。

第二层是基础过滤前置,LLM调用之前必须先用MySQL做一轮严格的条件过滤,比如用户明确说“不考数学”,那就先把考数学的专业全部剔除,绝不让无关数据进LLM。

第三层是降级方案,LLM服务超时或报错时,系统自动切换到规则推荐,按院校层次、地区、专业匹配度打分返回候选列表,并在前端提示“当前使用基础推荐模式”。这个设计在演示时特别重要,我亲眼见过同学的毕设因为网络问题卡在AI加载页面,场面相当尴尬。

# recommend/services.py def hybrid_recommend(user_input): cache_key = hashlib.md5(user_input.encode()).hexdigest() cached = redis_client.get(cache_key) if cached: return json.loads(cached) struct_conditions = extract_intent(user_input) candidates = filter_by_mysql(struct_conditions) if not candidates: return fallback_by_rules(struct_conditions) try: result = llm_recommend(user_input, candidates) except Exception: result = fallback_by_rules(struct_conditions) redis_client.setex(cache_key, 600, json.dumps(result)) return result

5. 考研分数线预测模块:让“预测”从玄学变得可解释

5.1 预测任务的数学建模方式

分数线预测本质上是一个时间序列回归问题。以“某院校某专业的历年复试线”为预测目标,输入特征是年份、报考人数、录取人数、地区竞争系数、学科门类等。需要注意预测对象不同,建模方式也完全不同:

  • 国家线预测:数据量小但规律性强,适合整体趋势建模
  • 单校单专业的复试线:数据往往只有几年,必须借力横向信息
  • 地区平均线:样本充足,适合做回归验证

毕设阶段我建议主预测放到“地区平均线+同门类变化基线”上,同时展示单校专业线的回归结果。不要试图预测某个具体专业的下一届分数线,因为会受到报名偶然性影响,准确率低且容易被答辩老师挑战。

5.2 特征工程与模型对比

我做了一组很明确的模型对比实验,这个实验数据可以直接写进论文。特征统一用2015年到2023年的历史数据,训练集和测试集按时间切分,绝不随机打乱——时间序列数据如果随机抽样,会造成严重的数据泄漏。

模型MAERMSE备注
线性回归6.27.9可解释性最好,系数能说明权重
Ridge回归5.87.4稍微优于普通线性回归
XGBoost5.36.8效果最好,但特征重要性解释要下功夫
LSTM6.98.5数据量不足时效果反而不如树模型

结论在论文里可以诚实写:样本量有限,非线性模型优势不明显。反而是XGBoost在特征重要性解释上更友好,能明确看出“报考人数变化”和“地区竞争系数”是影响复试线波动的两个主要因素。这条结论比单纯报一个MAE数字有说服力得多。

5.3 预测结果的可信度设计与可视化展示

因为预测本身有不确定性,前端展示的时候我做了置信区间模糊化处理:预测分数线给一个区间,比如“预测在332-348之间”,而不是硬邦邦地给一个单点分数。同时把影响预测的主要因素列在下方,用横向条形图展示各特征的贡献权重。

这样设计的好处是,如果有人质疑“预测准不准”,你可以说:我们给出的不是押题式的单点预测,而是基于历史特征的区间估计,并且明确告诉你哪些因素导致了这个趋势。数据显示区域趋势相对稳定,模型预测更多是帮用户理解分数波动规律,而不是下一年的决定性结论。答辩老师听到这种解释,通常不会再咬着“预测准不准”不放。

6. 可视化大屏与数据分析维度:让答辩老师一眼看懂你在做什么

6.1 大屏布局与核心指标设计

可视化部分是毕业设计的门面,但也最容易做成“一堆图表凑在一起却没重点”。我的大屏布局是这样的:整体分成上、中、下三个区域,颜色统一用深色科技风。

顶部放核心KPI:全国报考人数、招生总名额、覆盖院校数、数据量统计。四个数字放在同一行,让评审老师第一眼就知道系统数据规模。

中间区域是地图切图,展示各省份的招生院校数量和平均复试线热力分布。这里用ECharts的地图组件,鼠标悬停显示省份数据。

底部左侧是历年国家线走势折线图(分学术型和专业型),底部中间是专业热度Top10横向柱状图,底部右侧是985/211/双一流院校的分数区间分布箱线图。这一排图表的叙事逻辑是从宏观到微观:先看总体趋势,再看热门专业,最后看同层次院校分数分布。

6.2 Django后端为ECharts提供聚合接口

前端再怎么花哨,数据接口设计不好都会崩。我建议专门在dashboards这个App里提供四个聚合接口,每个接口只返回一组图表需要的数据结构:

# dashboards/views.py @api_view(["GET"]) def province_stats(request): data = ( ScoreLine.objects .values("school__province") .annotate(avg_line=Avg("recurve_line"), cnt=Count("id")) .order_by("-cnt") ) return Response({"provinces": list(data)})

这类统计接口看似简单,但有个性能问题必须注意:如果用户频繁点击刷新,Django会反复执行同样的聚合SQL。演示现场通常网络不稳定,加个Redis缓存很必要。缓存时间设置在300秒就够了,既能保证数据新鲜度,又不会拖垮服务器。

6.3 从图表到洞察:几个值得跟评委聊的分析结论

可视化不只是为了好看,一定要能讲出几个数据结论。这是我实测数据之后发现的三个点,写在论文和PPT里效果都非常好:

第一,同一专业在不同省份的复试线差距显著,但差距不是一直扩大,近两年有收敛趋势。说明考研信息的透明度在提升,考生“用脚投票”在起作用。

第二,报考人数增长最快的专业集中在计算机、金融、应用统计等几个门类,但这些门类的录取名额增长只有个位数,供需矛盾在拉大。

第三,985院校的复试线波动幅度明显小于普通院校,原因是报考群体更理性、生源池更稳定。这条结论做出来之后,回复制倾向非常明显。

这些结论不需要算法太复杂,聚合统计就能得出来。但一旦在大屏现场展示并讲出深层原因,整个项目的高度立刻就不一样了——你已经从“做了一个网站”变成“做了一套有数据分析价值的系统”。

7. 毕设文档三件套:论文、PPT、源码讲解怎么准备

7.1 论文结构与章节分配

论文建议分成六章,每一章对应前面的实现模块,篇幅分配如下:

  • 绪论:研究背景和意义、国内外研究现状,占8页左右
  • 相关技术介绍:Django、DRF、LLM原理、RAG、ECharts、预测模型,占10页左右
  • 系统需求分析与设计:用例图、功能模块图、数据库ER图、接口设计,占12页左右
  • 系统核心模块实现:LLM推荐实现、RAG检索链路、Prompt设计、预测模型训练过程,占15页左右
  • 数据可视化与分析:大屏设计、数据指标、分析结论,占8页左右
  • 系统测试与效果评估:功能测试用例、预测模型评估、推荐效果的用户评价,占6页左右

这个结构的核心思想是:保证每个模块都有独立的“设计-实现-测试”三连,避免论文变成软件说明书。LLM部分要特别写清RAG流程和Prompt迭代过程,这是当前最容易拿高分的创新点。

7.2 PPT演示节奏设计

毕设答辩PPT不需要把所有代码贴上去,但要有一条清晰的演示动线。我的建议是8分钟讲完,节奏如下:

第1分钟:开场直接用一张截图展示“考研择校的信息过载问题”,然后点出本项目要解决的问题。

第2-3分钟:讲系统整体架构图和核心数据表设计,不多讲细节,让评委理解项目边界。

第4-5分钟:重点演示LLM推荐的效果对比,先输入一个模糊需求,大屏观看流式输出推荐结果的过程,再展示如果LLM挂了,规则推荐如何兜底。有对比才有记忆点。

第6-7分钟:展示可视化大屏和预测模块,讲明预测模型的评估结果和两个数据分析结论。

最后1分钟:总结项目中的难点和解决方案,以及后续可以扩展的空间,留出提问空间。

7.3 源码答辩时的高频问题与应答思路

根据我和同学答辩的经历,老师最常追问的问题基本集中在这几个方向:

问“LLM推荐结果的可信度怎么保证?”——答:有结构化数据校验层、RAG召回限定数据来源、降级规则兜底,三个环节共同保证。

问“为什么用XGBoost不用深度学习?”——答:样本量有限,树模型在小样本下表现更稳定,且特征重要性可解释。如果换LSTM当主模型,会被继续追问“数据量不够怎么办”,反而容易露怯。

问“这个系统相对传统推荐系统有什么优势?”——答:意图表达更自然、推理结合数据、推荐结果可解释,并且通过用户反馈日志可以持续优化Prompt。

问“数据量这么少,结论可信吗?”——答:明确承认数据局限,强调系统价值在于流程方法和趋势分析,以及未来可以平滑扩展更多数据源。

源码讲解时,我建议把线索引到recommend/services.py和predict/model_utils.py这两个核心文件,先讲清模块职责,再逐个函数讲输入输出。不要打开整个项目目录让老师自己找,那会显得你没有梳理过代码边界。

最后再分享一个我在实际演示中被问到的细节:有评委老师让我当场修改数据库里某所学校的分数线,重跑预测后看结果变化。当时好在代码里所有数据操作都走的是ORM的Service层,改完数据库一行代码都不用动,刷新页面就出了新结果。所以模块之间该解耦的地方一定要解耦,Service层尽量窄化对外接口。毕设做得越“像真实工程”,答辩时的底气就越足。分数预测这类内容也切记在系统里加上“预测结果仅供参考”的提示语,既是技术态度的体现,也是使用规范的一部分。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询