
简介面向计算机专业毕业设计与课程设计场景这套基于PythonDjango开发的主观题自动阅卷系统围绕在线考试业务实现了用户管理、试题管理、试卷管理、在线考试与成绩管理五大模块并支持对主观题作答结果进行自动判定。资源包共316个文件大小约8.92MB除28个Python业务代码外还包含Django模板HTML、CSS/JavaScript前端资源、SQL数据库脚本以及大量GIF操作演示和录像说明便于对照界面与后台逻辑逐项理解。压缩包内按项目目录组织静态文件与业务代码分层清楚可直接运行后继续扩展题型或评分规则配套说明对功能模块和部署要点有一定梳理适合需要完整可运行项目源码、准备答辩演示或快速上手Django项目开发的读者参考。目前已有233人学习浏览整体内容对初学者较为友好。1. 主观题自动阅卷系统毕业设计选它到底在做什么期末改 200 份简答题老师平均每份要看 2 分钟碰上字迹潦草、表述绕圈的5 分钟都批不完。主观题自动阅卷系统要解决的就是这个场景学生提交一段文字答案系统按预设的评分规则自动打分把人工从重复劳动里解放出来。这类课题在计算机相关专业的毕业设计里一直热度很高核心是 Python 处理文本、Django 搭后台、算法模型给答案评分一套下来等于把 NLP 基础、Web 开发和工程化能力全练了一遍。适合想拿一个完整项目证明自己动手能力的同学也适合需要一套课设方案的开发者参考。它不追求把语义理解做到极致而是用“关键词命中 文本相似度 人工复核兜底”的组合把评分链路打通。下面从算法选型、Django 实现、工程跑通和常见坑位逐个讲。2. 评分算法选型关键词权重、N-gram 相似度与向量相似度的取舍2.1 关键词权重评分把阅卷先变成可配置的规则主观题阅卷的第一步不是教机器“理解”文字而是先确定评分规则。市面上大量毕业设计用的都是关键词权重法教师为每道题配置几个必备关键词每个关键词带分值学生答案里命中哪些关键词就累加哪些分。用 Python 实现这一层很简单关键是规则怎么组织成数据而不是把分数写死在代码里。我一般会把题干和评分规则拆成两个表题干存题目内容评分规则表存关键词、同义词组、分值权重。这样可以做到“换一道题不用改代码只在后台加规则”。# scoring/rules.py import re def split_keywords(rule_text: str, sep: str |) - list: 把规则文本切成关键词列表默认用 | 分隔 return [k.strip() for k in rule_text.split(sep) if k.strip()] def count_hits(answer_text: str, keywords: list) - dict: 统计每个关键词在答案中出现的次数 hits {} for kw in keywords: # 用 re.escape 防止关键词里带特殊字符导致正则报错 pattern re.compile(re.escape(kw)) hits[kw] len(pattern.findall(answer_text)) return hits def score_by_keywords(answer_text: str, rules: list) - float: 按关键词权重累加得分 rules: [{keyword: TCP, weight: 2}, ...] total 0.0 for rule in rules: if rule[keyword] in answer_text: total rule[weight] return total这段代码的逻辑很直白先按分隔符把规则文本切成关键词列表再用正则统计每个关键词在答案里出现的次数最后按权重累加。re.escape这一步经常被漏掉题目答案里出现TCP这种带点的关键词或C这种带号的不转义轻则匹配不到重则直接抛异常。参数上需要注意三点一是关键词权重不要设成 1最好按“核心概念 2 分、辅助描述 1 分”这种梯度来否则学生只答出开头一句就能拿全分二是同义词必须先归一化常见做法是建一个同义词映射表评分前先把答案里的词替换成标准词三是count_hits只做演示实际项目里要结合分词结果做更精确的匹配不能只靠子串。2.2 N-gram 与编辑距离对付“意思对但字不同”的答卷关键词命中最大的问题是“学生写对了意思但用词不同”比如参考答案写“握手协议”学生写“三次握手过程”关键词一个字都没中。这时候需要相似度评分兜底。N-gram 的基本思路是把两段话分别切成连续的 n 个字的片段然后看有多少片段是重叠的。中文场景下我偏好 Bigram2-gram因为单字太碎、3-gram 太稀疏2-gram 在短文判分上的区分度最好。编辑距离Levenshtein则衡量把一个字符串变成另一个需要的最少编辑次数适合参考答案和真实答案都很短的场景。# scoring/similarity.py from difflib import SequenceMatcher def bigram_set(text: str) - set: 把字符串切成 2-gram 集合用于后续相似度计算 text text.replace( , ) if len(text) 2: return {text} return {text[i:i2] for i in range(len(text) - 1)} def jaccard_similarity(ans_a: str, ans_b: str) - float: Jaccard 相似度交集大小除以并集大小防除零 set_a bigram_set(ans_a) set_b bigram_set(ans_b) union set_a | set_b if not union: return 0.0 return len(set_a set_b) / len(union) def diff_similarity(ans_a: str, ans_b: str) - float: 基于 difflib 的序列匹配相似度适合短文本 return SequenceMatcher(None, ans_a, ans_b).ratio()Jaccard 相似度算的是两段文字“长得像不像”difflib.SequenceMatcher.ratio()算的是连续公共子序列覆盖度。实际使用时我一般把两个分数加权合并final_sim 0.6 * jaccard 0.4 * diff两个指标侧重点不同合并后对“乱序表述”和“部分省略”都有响应。这里有个关键参数相似度阈值。阈值设 0.7松一点学生换个说法也能拿分设 0.9严一点几乎只有原话才能拿分。我的经验是不用全局阈值按题型调名词解释题参考答案短0.75 合适简答题答案长0.65 左右合适。不同题型建一个threshold字段存到题型表里不要写死在代码里。2.3 Word2Vec 向量相似度该不该上复现成本决定上限每个做主观题阅卷的人都会想到 Word2Vec、BERT 这类向量模型。确实用预训练模型把答案编码成向量再算余弦相似度效果会比 N-gram 好很多尤其是学生用完全不同的词表达同一个意思的时候。但作为毕业设计或小型课设我不建议一上来就上 BERT 类模型。原因有三点一是模型文件大部署环境内存吃紧二是推理速度慢在线判分一两百份答案还能忍几百份并发就吃力三是答辩演示时不确定因素多模型加载失败、编码维度不一致任何一个问题都会在现场翻车。更稳妥的路线是先用关键词 N-gram 搭一套能演示的完整链路然后在“进阶功能”里预留 Word2Vec 接口作为加分项展示。如果确实要加词向量我建议用gensim加载一个轻量中文词向量把分词后的答案映射成向量再算余弦相似度# scoring/vector_score.py from sklearn.metrics.pairwise import cosine_similarity import numpy as np def answer_vector(tokens: list, wv_model) - np.ndarray: 把答案分词结果加权平均成单个向量 wv_model: gensim.models.KeyedVectors 实例 词向量不存在时直接丢弃该词不报错 vectors [] for token in tokens: if token in wv_model.key_to_index: vectors.append(wv_model[token]) if not vectors: return np.zeros(wv_model.vector_size) return np.mean(vectors, axis0) def vec_similarity(vec_a: np.ndarray, vec_b: np.ndarray) - float: 余弦相似度注意零向量直接返回 0 norm_a np.linalg.norm(vec_a) norm_b np.linalg.norm(vec_b) if norm_a 0 or norm_b 0: return 0.0 return cosine_similarity([vec_a], [vec_b])[0][0]这里的核心参数是vector_size不同词向量模型的维度不一致有的 200 维有的 300 维初始化零向量时必须从模型读取维度而不能写死。还有一点词向量表里查不到的词人名、生僻专业术语直接丢弃这是常规做法千万别报错中断判分。3. 用 Django 组织判分系统数据模型、判分接口与审批闭环3.1 数据模型设计题目表、答卷表、评分规则表怎么建算法再好也得落到工程里。用 Django 组织这套系统数据模型是地基。我见过不少毕业论文把判分逻辑写在视图函数里、临时从字典取规则演示没问题但老师一问“如果题目有 500 道规则怎么维护”就答不上来。正确的做法是规则进数据库通过 Django 管理后台维护。# grading/models.py from django.db import models class ExamPaper(models.Model): 试卷表一张试卷挂多道题 title models.CharField(max_length200) created_at models.DateTimeField(auto_now_addTrue) class Question(models.Model): 题目表一道题对应一个题型、一个参考答案、一组规则 paper models.ForeignKey(ExamPaper, on_deletemodels.CASCADE, related_namequestions) stem models.TextField(verbose_name题干) reference_answer models.TextField(verbose_name参考答案) question_type models.CharField(max_length20, choices[ (choice, 选择题), (short, 简答题), (essay, 论述题), (fill, 填空题), ], defaultshort) full_score models.FloatField(default10.0) threshold models.FloatField(default0.65, verbose_name相似度阈值) class ScoreRule(models.Model): 评分规则表一道题有多条关键词规则 question models.ForeignKey(Question, on_deletemodels.CASCADE, related_namerules) keyword models.CharField(max_length100, verbose_name关键词) synonym_group models.CharField(max_length200, blankTrue, verbose_name同义词组) weight models.FloatField(default1.0, verbose_name权重) is_required models.BooleanField(defaultFalse, verbose_name是否必含) class StudentAnswer(models.Model): 答卷表记录每个学生的答案和判分状态 question models.ForeignKey(Question, on_deletemodels.CASCADE, related_nameanswers) student_no models.CharField(max_length50) content models.TextField(verbose_name学生答案) auto_score models.FloatField(nullTrue, blankTrue) final_score models.FloatField(nullTrue, blankTrue) status models.CharField(max_length20, defaultpending, choices[ (pending, 待批改), (scored, 已自动评分), (reviewed, 已人工复核), ]) created_at models.DateTimeField(auto_now_addTrue)这几张表的关系是试卷一对多题目题目一对多评分规则题目一对多学生答案。设计时最容易被忽略的是final_score和status两个字段。自动评分只是初判系统需要允许老师人工改分并把状态从scored推进到reviewed这样才是一个完整的业务闭环。另外一个实用点是synonym_group字段存TCP|三次握手|连接建立这类同义词组评分前做归一化替换。3.2 判分接口实现事务、防重入与返回值约定数据模型定好后核心就是判分接口。这里有几个工程坑同一份答案不能因为刷新页面被重复判分多道题判分不能因为一道题报错导致前面全白做接口返回值要能明确区分“成功、部分成功、失败”。我一般把“单题判分”和“批量判分”拆开。单题判分负责核心逻辑批量判分循环调用单题接口并做事务处理# grading/services.py from django.db import transaction from .models import StudentAnswer, ScoreRule def apply_synonyms(content: str, rule: ScoreRule) - str: 把答案命中的同义词组统一替换成组内第一个标准词 if not rule.synonym_group: return content synonyms [s.strip() for s in rule.synonym_group.split(|) if s.strip()] if not synonyms: return content # 组内第一个词视为标准词其余视为变体 for variant in synonyms[1:]: content content.replace(variant, synonyms[0]) return content transaction.atomic def grade_single_answer(answer: StudentAnswer) - float: 判分单份答案并写回数据库事务保证字段一致性 # 防重入已评分的默认直接返回除非 forceTrue if answer.status ! pending: return answer.auto_score rules ScoreRule.objects.filter(questionanswer.question) base_content answer.content total 0.0 # 第一步关键词权重分 for rule in rules: normalized apply_synonyms(base_content, rule) if rule.keyword in normalized: total rule.weight # 必含关键词没命中直接判定 0 分 # 这里只记录逻辑具体策略由业务方决定 # 第二步N-gram 相似度兜底分 # 从外部传入相似度函数便于测试时替换 sim_score compute_similarity( base_content, answer.question.reference_answer ) full_score answer.question.full_score sim_part full_score * max(0.0, sim_score - 0.5) / (1.0 - 0.5) # 权重分上限限制为满分 60%相似度分上限 40% total min(total, full_score * 0.6) min(sim_part, full_score * 0.4) answer.auto_score round(total, 1) answer.status scored answer.save(update_fields[auto_score, status]) return answer.auto_scoretransaction.atomic保证判分过程里任何一步出错数据库不会留下半更新的状态。update_fields限定了只更新这两个字段避免把created_at等字段误刷。这里最有争议的是“权重分 60%、相似度分 40%”这个上限设计并不是所有题目都适合。名词解释这类题关键词命中就该拿大部分分论述题则相反得分点分散关键词权重不宜过高。因此我实际做的时候会把这个比例也挪到题型表里作为配置项而不是写死。判分接口的返回值约定为{code: 0, score: 8.5}这种统一结构前端拿到code ! 0就提示失败不要返回 200 之外的花式状态码。3.3 管理后台与人工复核的兜底逻辑自动阅卷系统的价值在于提升效率而不是完全取代人工。Django 自带的后台是天然的复核工作台但默认的ModelAdmin直接暴露所有字段会有风险复核老师不小心改了status或者把状态改了却忘记填分。# grading/admin.py from django.contrib import admin from .models import Question, ScoreRule, StudentAnswer admin.register(StudentAnswer) class StudentAnswerAdmin(admin.ModelAdmin): 答卷管理按试题筛选重点展示需复核的记录 list_display (student_no, question, auto_score, final_score, status) list_filter (status, question, student_no) search_fields (student_no, content) readonly_fields (auto_score,) actions [mark_as_reviewed] admin.action(description标记为已人工复核) def mark_as_reviewed(self, request, queryset): updated queryset.update(statusreviewed, final_score1.0) self.message_user(request, f已复核 {updated} 条记录)这里演示的是动作按钮复核老师在列表页勾选几条记录点“标记为已人工复核”批量把status改为reviewed。readonly_fields里的auto_score是防止误改自动评分人工分应该写在final_score里。实际项目里人工复核面的核心不在漂亮界面而在“高亮展示哪些需要复核”。我的做法是给StudentAnswer加一个confidence字段自动评分后计算“关键词命中率与相似度分数的离差”离差大的自动标成“低置信度”后台列表页按置信度升序排列老师只需要集中精力看低置信度记录。这部分逻辑放到第六章展开。4. 把源码工程跑通从环境准备到一次完整阅卷的最小链路4.1 环境准备与依赖安装拿到一个 Django 项目源码包第一件事不是直接python manage.py runserver而是先看目录里有没有requirements.txt、manage.py和README或说明文档。这三样东西在不在决定你后面要踩多少坑。没有requirements.txt的工程依赖版本全靠猜前几天装环境的时间比写代码还长玄学问题一堆。创建一个干净的虚拟环境是必须的我习惯在项目根目录执行# 创建虚拟环境并激活Python 版本建议 3.8 及以上 python3 -m venv venv source venv/bin/activate # 逐个安装核心依赖分步执行便于定位失败项 pip install Django4.2.* pip install pandas pip install python-dotenv pip install jiebaDjango 版本建议选 4.2 LTS毕业设计若选 5.0部分第三方库的兼容性还没跟上你会为了换版本白白加班。jieba用于中文分词python-dotenv用于管理数据库账号这类敏感配置。分步安装的好处是万一某个包编译失败你能精准知道是它的问题而不是一条长命令里糊成一团。如果你的机器上还没装 Python那就先去官网下安装包勾选“Add Python to PATH”这条路没有任何捷径。Linux 服务器上则注意用python3而不是python命令很多新手的翻车现场都是在crontab或systemd里写死了python结果调用的根本不是同一个解释器。4.2 初始化数据库并创建 Django App环境就绪后开始初始化工程。如果你是下载的源码包通常自带manage.py和主应用目录只需完成数据库迁移和超级用户创建。如果是从零新建常见做法是# 创建一个名为 grading 的 App承接阅卷核心业务 python manage.py startapp grading # 初始化数据库表结构 python manage.py makemigrations python manage.py migrate # 创建后台管理员账号 python manage.py createsuperuserstartapp grading之后必须在settings.py的INSTALLED_APPS里加上grading我见过有人漏了这步然后makemigrations一直提示“No changes detected”折腾半小时。migrate除了建业务表还会建 Django 内置的auth和session表这些是登录后台和判断用户状态的基础。如果源码包里自带data.sqlite3或类似的文件说明作者打包了演示数据可以直接跳过migrate里的业务表创建优先保证能跑起来。但如果要真正理解系统建议删掉演示库重新迁移不会迁移说明你根本还没读懂这个工程。4.3 导入测试数据并执行第一次批改数据库跑通后最快验证系统的方式是通过 Django Shell 手动录入一道题和几条答案直接调用判分服务。Shell 环境比写接口调试快很多也能让你看清异常到底发生在算法层还是 ORM 层# 在项目根目录执行 python manage.py shell逐行录入演示数据 from grading.models import ExamPaper, Question, StudentAnswer, ScoreRule paper ExamPaper.objects.create(title计算机网络期末试卷) q Question.objects.create( paperpaper, stem简述 TCP 三次握手的过程, reference_answer客户端发送 SYN服务器回复 SYNACK客户端再发 ACK连接建立, full_score10.0, threshold0.65 ) # 配置两条关键词规则核心词权重 2常见扩展词权重 1 ScoreRule.objects.create(questionq, keywordSYN, weight2, is_requiredTrue) ScoreRule.objects.create(questionq, keywordACK, weight1, is_requiredFalse) # 模拟学生提交答案 ans StudentAnswer.objects.create( questionq, student_no20240001, content客户端发 SYN服务端回 SYN 和 ACK客户端再确认 ACK连接就建立了 ) # 调用判分函数 from grading.services import grade_single_answer score grade_single_answer(ans) print(f自动评分: {score}状态: {ans.status}) print(f关键词优化后文本: {apply_synonyms(ans.content, ScoreRule.objects.first())})运行后得到自动评分: 8.0状态: scored说明整条链路已经通了。这里能验证两件事第一是关键词规则是否命中SYN和ACK都出现了权重分拿到 3 分第二是相似度兜底是否生效学生答案和参考答案高度相似sim_part接近满分 4 分但因为权重分被限制在 6 分上限所以总分 8.0 而不是 9.0 或 10.0。如果你发现相似度分值低得离谱先检查是否对答案做了停用词过滤。中文里“的、了、是、在”在参考答案和学生答案里反复出现SequenceMatcher会让不参与评分的虚词主导相似度导致分数虚高或失真。停用词表虽然是烦人的“体力活”但对主观题阅卷的分数质量影响极大。4.4 录像演示里常见的批改展示顺序做毕业设计演示录像时不要一上来就展示酷炫的可视化评卷老师想看的是“一次完整的数据流”。常见的展示顺序是后台添加试卷和题目 → 配置关键词规则 → 提交学生答案模拟几条正常、错漏、同义改写 → 点击自动批改 → 展示得分和状态 → 进入人工复核修正分数 → 展示最终成绩单。整个流程对应源码包里的“录像演示”部分你照着这个顺序录一段 5 分钟的实操视频答辩时基本不会被问倒。5. 自动阅卷绕不开的五个坑现象、原因与解决5.1 关键词判分全部落空得分为 0现象学生答案明明写对了关键词自动评分却打了 0 分。我排查过的一个项目学生写的是“三次握手”规则里配的是“三步握手”怎么看都对但程序没判中。原因规则里配置的关键词和答案里的词汇不是同一个字面形式中文分词后词条被切开关键词含全角/半角或不可见空格。这类问题在答卷文本从 Excel 导入后尤其常见字符串里藏着肉眼不可见的空格或换行符。解决评分前对答案和关键词都做一次统一清洗把全角转半角、去除空白字符和零宽字符再执行匹配。可以先打印repr(answer_text[:50])看原始字符串里到底有什么再决定清洗逻辑写多重。另外关键词匹配不能只用in判断配合keyword in normalized时要把apply_synonyms的归一化结果作为判断对象。5.2 Django ORM 查询后删对象报错或删错数据现象写定期清理测试答卷的脚本时用.filter(questionq)查出一批记录遍历时删除报RuntimeError: QuerySet already dehydrated或者删了半天发现记录没少几条。原因这是 Django ORM 的经典坑。在遍历QuerySet的同时删除对象会改变游标位置导致部分行被跳过或者误将queryset.delete()写在循环体里第一次迭代就把全表数据删光了后面全部白跑。解决不要边遍历边删。一次性把id收集成列表再调用一次批量删除# 一次性取出要删除的记录 ID再统一执行删除 answer_ids list( StudentAnswer.objects .filter(questionq, statuspending) .values_list(id, flatTrue) ) StudentAnswer.objects.filter(id__inanswer_ids).delete()这里values_list(id, flatTrue)拿到的是id列表id__in过滤后直接调用delete()整套操作只触达数据库一次不会因遍历影响游标。如果你需要在删除前针对每条记日志那就先遍历收集日志再统一删除别把删除混在循环里。5.3 相似度分数对“复制粘贴参考答案”和“原创描述”没有区分度现象把参考答案原封不动提交得分接近满分用完全自己的话复述得分点相似度只有 0.4得分很低。自动阅卷系统变成“背诵检测器”这显然不合理。原因N-gram 相似度和编辑距离本质上都是字面重叠度量换个说法立刻失效。这个问题不是 bug而是算法本身的边界。解决不要只用一种相似度算法。叠加 Jaccard 和SequenceMatcher两个指标再对关键词命中做加权。更进一步的方案是引入同义词替换事先在规则表里维护synonym_group评分前先把“客户端”替换成“client”、“连接建立”替换成“连接成功”这种变体让字面相似度有机会命中。这样的系统才能容忍“换词但意思不变”的答案。5.4 SQLite 在并发批改时频繁报 “database is locked”现象部署到服务器后多个老师同时触发批改后台时不时报OperationalError: database is locked有的答卷评分报错重试又生成重复记录。原因Django 默认用项目里的db.sqlite3它适合开发调试。并发写入时SQLite 对整个数据库加写锁多个写请求同时到达就会锁冲突。有人为了省事加了重试结果重复提交导致同一份答卷被记了两条。解决短期方案是把数据库切换成 MySQL 或 PostgreSQLsettings 里配置数据库连接信息即可业务代码一行不用改。如果毕业设计只允许用 SQLite 交付那就给在线判分接口加一个串行机制比如用 Redis 分布式锁或者简单的文件锁保证同时只有一个批改任务在写库。答辩时主动说清 SQLite 的适用边界比被老师问到时支支吾吾强得多。5.5 修改评分规则后历史分数不一致现象老师修改了一道题的关键词权重之前的判分记录保留旧分数新建答案用新规则同一道题两种分数并存前后对不上。原因auto_score在判分时被直接写死规则变更不会自动触发重判。解决在规则表里加一个version字段每次变更生成新版本号判分服务启动时读取当前版本号记录到StudentAnswer表的rule_version字段。方案落地后如果老师改了规则批量重判时只需要筛选rule_version ! current_version的记录。如果嫌版本表复杂至少要在修改规则的页面上放一个“重判该题所有答卷”的按钮手动触发一次全量重算别把不一致问题丢给用户自己去想。6. 进阶技巧用置信度把自动批改变成半自动批改自动阅卷做得再好总有些学生答案处在“似对非对”的灰色地带。与其让算法硬给分不如让系统学会说“我不确定”把不确定的答卷交给人工。实现思路是给每个判分结果算一个置信度用“关键词命中率和相似度分数的离差”来衡量。如果相似度很高但关键词命中不足说明学生可能换了一种表达方式算法未必把握得住如果关键词全命中但相似度极低可能是在堆关键词凑答案这两种情况都要降置信度。置信度计算的推荐公式是confidence 1 - (kw_score_ratio - sim_score_ratio)**2其中kw_score_ratio是关键词实际得分占权重满分比例sim_score_ratio是相似度得分占 40% 评分上限的比例。两者的差值越大置信度越低。判分服务算出置信度后把低于 0.6 的记录标成“低置信度”在管理后台单独过滤展示。# grading/services.py 增加置信度计算逻辑 def compute_confidence(kw_score: float, kw_full: float, sim_score: float, sim_full: float) - float: 置信度 1 - (关键词得分比例 - 相似度得分比例)^2 两者越接近置信度越高极端偏科置信度降低 ratio_kw kw_score / kw_full if kw_full else 0 ratio_sim sim_score / sim_full if sim_full else 0 confidence 1.0 - (ratio_kw - ratio_sim) ** 2 return round(min(max(confidence, 0.0), 1.0), 3)把置信度低于 0.6 的答卷批量导出一个待复核列表在后台列表页新增一个low_confidence筛选器老师打开就是一份按置信度升序排列的待人工复核队列改完状态自动更新为reviewed。这个功能让我在三次答辩里都被老师单独提出来问因为它回答了一个核心问题自动阅卷系统不是替代老师而是帮老师把注意力放在最需要人的地方。还有一个非常实用的做法把相似度矩阵可视化在后台展示“参考答案 × 学生答卷”的相似度热力图。用matplotlib生成热力图帮助老师快速看出哪些答卷与参考答案高度雷同、哪些明显偏题。这一手虽然是加分项但实现成本不高可视化效果好放在系统“数据统计”页面即可。回到最初的问题这个系统值不值得做我认为值得但要做对定位它不是追求 100% 评分准确率的科研项目而是把“规则可配置、流程可追溯、人工可介入”三件事做完整的工程实践。我最后交的版本里关键词和相似度两种算法加起来不到三百行 PythonDjango 后端加模板约两千行但整个系统逻辑闭环、数据稳定、答辩演示流程通畅。希望你做的时候别执着在调高那 1% 的算法准确率上先保证低置信度答卷能被筛出来、人工复核流程能走通、判分结果经得起追问这套系统就已经立住了。希望帮到你。本文还有配套的精品资源点击获取