news 2026/9/10 12:52:19

Python论文格式检查系统:从python-docx解析到LLM辅助的全流程实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python论文格式检查系统:从python-docx解析到LLM辅助的全流程实现

简介:这套基于Python的论文格式检查与处理源码,主要面向需要批量规范学术稿件格式的研究人员、期刊编辑以及有文本处理需求的Python开发者,可自动识别并修正标题层级、摘要、章节编号、图表标注、引用和参考文献等常见格式问题,显著提升论文审校效率。资源共含47个文件,核心为9个Python源码文件,负责主控、批注、文档收集与处理等逻辑;配套23份docx样例文档用于测试或参考,6个XML配置文件定义检测规则,另有4个pyc编译文件、Git忽略配置、IntelliJ IDEA项目配置、Markdown说明、YAML环境配置及PNG示意图片等,整体压缩包约29MB,目录结构清晰。已有329人学习下载。通过阅读源码中的主控脚本、批注模块与文档处理逻辑,可快速掌握基于python-docx和正则表达式的论文格式自动化检查与格式校正思路,也能借助XML规则文件扩展自定义检测项,适合作为论文格式检查场景和文本处理工程的重要参考。

1. 论文格式检查系统的模块划分与运行入口

论文查重之前先被格式打回,几乎是每个研究生都经历过的环节。这个基于 Python 的论文格式检查与处理系统,解决的正是「提交前最后一公里」的问题。源码包里 47 个文件,真正干活的是 9 个 Python 源文件:main.py负责入口调度,check_paper.py做规则判定,annotate.py把问题写回 Word 文档,process.py组织批处理管线,db_collect.py汇总检查数据,llm.py提供语义层面的辅助判断。文件的命名规律也透露了工作流:uploads/下是原始上传的论文,processed/下是带时间戳的处理结果,modified_document.docx是批注后的产物。这套系统适合两类人:一是需要用 Python 批量处理 Word 文档的工程师,想看看 python-docx 在真实项目里怎么组织;二是被论文格式反复折磨的研究生,想找一个能自动检查标题层级、字体字号、参考文献编号的工具。下面按模块拆开讲。

2. check_paper.py 的规则判定与 python-docx 文档解析

2.1 段落、样式与字体:python-docx 的文档对象模型

check_paper.py是整个系统的判定核心。它的第一步是把.docx文件读入内存,这一步用的是 python-docx 库的Document类。很多人第一次接触 python-docx 时容易踩的坑是:以为doc.paragraphs返回的是纯文本数组,实际上每个Paragraph对象都携带样式名、对齐方式、字体属性等完整信息。

from docx import Document from docx.shared import Pt doc = Document('uploads/20240409140850975838_41805054.docx') for para in doc.paragraphs: style = para.style.name # 段落样式名,如 'Heading 1'、'Normal' text = para.text.strip() if not text: continue # 逐段提取字体信息(注意处理 run 级别覆盖段落默认样式的情况) font_names = set() font_sizes = set() for run in para.runs: font_names.add(run.font.name) if run.font.size: font_sizes.add(run.font.size.pt) print(f"样式: {style} | 文本: {text[:30]} | 字体: {font_names} | 字号: {font_sizes}")

python-docx 的文档模型分三层:Document包含Paragraph列表,Paragraph包含run列表,run是真正存储文本和字符级格式的最小单位。段落级格式(如行距、缩进)挂在paragraph_format上,字符级格式(字体、字号、加粗)挂在每个run上。上面代码里用set收集字体名和字号,是因为同一段落内不同run可能携带不同格式,比如中文用宋体、英文用 Times New Roman,这种情况在论文里最常见。

2.2 规则引擎的设计:从「人找问题」到「规则找问题」

拿到段落和格式信息后,check_paper.py要做的是把「论文格式要求」翻译成可执行的判定条件。我拆过的格式检查项目里,规则通常分四类:样式规则(标题是否用了 Heading 样式而不是手动加粗)、字体规则(中文宋体、英文 Times New Roman、字号是否达小四)、结构规则(标题层级是否跳级,图表编号是否连续)、文本规则(中英文之间是否缺空格,全角半角标点是否混用)。

import re STYLE_RULES = { "heading": { "pattern": r"^(Heading\s*\d|标题\s*\d|\d+\.\d+\s)", # 匹配Word内置标题样式 "message": "正文段落疑似使用标题样式" } } FONT_RULES = { "cn_font": "宋体", "en_font": "Times New Roman", "min_size_pt": 12, # 小四 } def check_font(para, errors): for run in para.runs: # 中文字符使用 run.font.name 取不到时,需检查 rPr 下的 eastAsia 属性 rPr = run._element.rPr east_asia = None if rPr is not None: rFonts = rPr.find('{http://schemas.openxmlformats.org' '/wordprocessingml/2006/main}rFonts') if rFonts is not None: east_asia = rFonts.get('{http://schemas.openxmlformats.org' '/wordprocessingml/2006/main}eastAsia') if east_asia and east_asia != FONT_RULES['cn_font']: errors.append(f"中文字体应为{FONT_RULES['cn_font']},实际为{east_asia}")

中文字体的获取有个老坑需要注意:run.font.name只能拿到西文字体,中文正文字体存在rPrw:eastAsia属性里。上面的代码通过直接操作 XML 元素绕开了 python-docx 的封装,这在处理国内高校论文模板时几乎是标配操作。

2.3 正则表达式在文本语义检查中的落地

正则检查是check_paper.py里最实用也最容易失控的部分。合理的做法是建立一张「规则表」,每条规则携带正则模式、问题等级(error/warning)和修复建议,检查引擎统一遍历。

import re TEXT_RULES = [ { "id": "T001", "name": "中英文之间缺少空格", "pattern": r"[\u4e00-\u9fff][A-Za-z]|[A-Za-z][\u4e00-\u9fff]", "level": "warning", "suggestion": "中英文之间建议插入一个空格" }, { "id": "T002", "name": "全角括号混用", "pattern": r"[()]", "level": "warning", "suggestion": "半角上下文中的括号应使用()" }, { "id": "T003", "name": "参考文献编号格式", "pattern": r"\[\d+\][^(\[\d+\])]", "level": "error", "suggestion": "参考文献引用格式应为[1]" } ] text_rules = [(rule["name"], re.compile(rule["pattern"]), rule["level"]) for rule in TEXT_RULES] def check_text(text, errors): for name, pattern, level in text_rules: matches = pattern.findall(text) if matches: errors.append(f"[{level}] {name}: 命中{len(matches)}处")

这里有个设计取舍值得展开:正则模式直接写在源码里方便调试,但规则多了以后(超过 30 条)维护成本会指数上升。我在实际项目里会把规则外置到 JSON 或 XML(这个项目里就是 XML 配置文件),检查引擎只做一件事——读配置、跑正则、收集结果。正则的匹配优先级也很关键:先跑结构性的格式检查(样式、字体),再跑文本层面的语义检查,否则一个全角括号的 warning 会刷屏,把真正严重的标题样式错误淹没掉。

3. annotate.py:把检查结果写回 Word 的批注与高亮实现

3.1 两种批注实现路径:API 操作与底层 XML 注入

检查结果不能只输出到终端,要落到文档里让作者看得见位置、看得懂问题。annotate.py承担的就是这个「可视化反馈」职责。python-docx 官方库没有直接提供批注的 API,实际项目里我见过两种做法:Windows 环境下用win32com.client调用 Word 的Comments.Add方法,跨平台环境则直接操作 docx 包内的 XML。

from docx import Document from docx.oxml import OxmlElement from docx.oxml.ns import qn def add_comment(paragraph, comment_text, comment_id=0): """在指定段落后追加一条批注(跨平台方案)""" # 1. 创建批注范围起始标记 range_start = OxmlElement('w:commentRangeStart') range_start.set(qn('w:id'), str(comment_id)) paragraph._p.insert(0, range_start) # 2. 在段落末尾插入批注引用标记 range_end = OxmlElement('w:commentRangeEnd') range_end.set(qn('w:id'), str(comment_id)) paragraph._p.append(range_end) # 3. 创建批注引用符号 comment_ref = OxmlElement('w:r') comment_ref.append(OxmlElement('w:commentReference')) # 设置 run 的 id 属性 rPr = OxmlElement('w:rPr') rStyle = OxmlElement('w:rStyle') rStyle.set(qn('w:val'), 'CommentReference') rPr.append(rStyle) comment_ref.insert(0, rPr) paragraph._p.append(comment_ref) return comment_id + 1

python-docx没有暴露批注对象,但.docx本质是一个 zip 包,里面word/document.xml定义了正文,word/comments.xml定义了批注内容。上面的代码往段落 XML 里插入了commentRangeStartcommentReference标记,这相当于在文档正文里「占了个位」。另一个配套步骤是把批注文本写入comments.xml部分,否则 Word 会认为批注引用是悬空的。用win32com会简单得多,直接comment = doc.Comments.Add(range, text),但绑定 Windows 和 Office 环境,换成 Linux 服务器就跑不起来。

3.2 高亮标记:实现对问题文本的精准定位

批注适合「段落级」的意见,比如「这段的标题样式不对」。但对于「第 3 个字符是全角括号」这类问题,批注粒度太粗,高亮标记更合适。实现方案是在问题文本对应的run上设置w:highlight属性。

from docx.oxml import OxmlElement from docx.oxml.ns import qn def highlight_run(run, color='yellow'): """给单个 run 添加高亮标记""" rPr = run._element.get_or_add_rPr() # 清除已有的高亮属性 for old in rPr.findall(qn('w:highlight')): rPr.remove(old) highlight = OxmlElement('w:highlight') highlight.set(qn('w:val'), color) rPr.append(highlight) def highlight_text_in_paragraph(para, keyword, color='yellow'): """在段落中查找指定关键词,命中部分设置高亮""" for run in para.runs: if keyword in run.text: start = run.text.find(keyword) before = run.text[:start] target = run.text[start:start + len(keyword)] after = run.text[start + len(keyword):] run.text = before new_run_highlighted = run._element.makeelement( qn('w:r'), {}) new_run_highlighted.text = target para._p.insert(list(para._p).index(run._element) + 1, new_run_highlighted)

上面代码的逻辑是:找到包含问题关键词的run,用find()定位偏移量,把原run拆成三段,中间段插入到新的run元素并加高亮。这样 Word 里打开就能直观看到「哪里标黄、哪里有问题」。需要注意makeelement创建的新元素需要手动处理rPr属性,否则新run会丢失原字体格式,我在实际测试中就遇到过高亮后字体从宋体变成默认 Calibri 的问题,解决办法是把原runrPr克隆一份到新元素上。

3.3 批注与高亮合并:生成可审阅的处理文档

annotate.py最终把检查结果合并成两类输出:一类是轻量级的高亮标记文档,用于快速浏览问题分布;另一类是完整版批注文档,每条批注包含问题类型、问题等级、修复建议三要素。

import json def generate_marked_document(origin_path, output_path, issues): doc = Document(origin_path) for para in doc.paragraphs: para_text = para.text.strip() if not para_text: continue matched_issues = [iss for iss in issues if iss['position'] in para_text or iss['paragraph_index'] == para._p.getparent().index(para._p)] for issue in matched_issues: if issue['level'] == 'error': # 严重问题用红色高亮 + 批注 for run in para.runs: if issue['keyword'] in run.text: highlight_run(run, color='red') add_comment(para, f"[{issue['id']}] {issue['name']}: {issue['suggestion']}") elif issue['level'] == 'warning': # 警告问题只用黄色高亮 for run in para.runs: if issue['keyword'] in run.text: highlight_run(run, color='yellow') doc.save(output_path) print(f"已生成批注文档: {output_path}")

issues数据结构里我习惯附带paragraph_index字段,annotate.py遍历到对应段落后按问题等级分流:error 级的问题用红色高亮加批注双重提醒,warning 级只做黄色高亮,避免批注过多导致文档臃肿。这种分级策略在生产环境里很实用。

4. process.py 批处理管线与 db_collect.py 的调度组织

4.1 时间戳文件命名:上传目录与处理目录的协同

项目里uploads/processed/目录的文件名格式值得单独拿出来讲。20240409140850975838_41805054.docx这类命名,前一段是时间戳加随机数,后一段是学号或工号,这种命名在文档处理系统里承担两个功能:按时间排序、按用户隔离。process.py的核心工作就是把uploads/里的原始文件挨个送进检查管线,输出到processed/时加上processed_前缀和新的时间戳。

import os import glob import shutil from datetime import datetime UPLOAD_DIR = 'uploads' PROCESSED_DIR = 'processed' ALLOWED_EXT = ('.docx',) def iter_pending_files(upload_dir=UPLOAD_DIR): """扫描上传目录,返回待处理的 docx 文件列表""" pending = [] for filepath in glob.glob(os.path.join(upload_dir, '*.docx')): # 跳过已经处理过的文件(防止重复处理) if 'processed_' in os.path.basename(filepath): continue # 检查文件是否还在写入中(简单通过文件大小变化判断) size1 = os.path.getsize(filepath) import time time.sleep(0.2) size2 = os.path.getsize(filepath) if size1 == size2: pending.append(filepath) return pending def process_single_document(filepath): """单文档处理流程:检查 + 批注 + 归档""" from check_paper import run_check from annotate import generate_marked_document filename = os.path.basename(filepath) student_id = filename.split('_')[-1].replace('.docx', '') # 1. 格式检查 issues = run_check(filepath) # 2. 生成带批注的文档 timestamp = datetime.now().strftime('%Y%m%d%H%M%S%f') output_name = f"processed_{timestamp}_{filename}" output_path = os.path.join(PROCESSED_DIR, output_name) generate_marked_document(filepath, output_path, issues) # 3. 返回结果摘要给数据收集模块 return { 'student_id': student_id, 'source_file': filename, 'output_file': output_name, 'issue_count': len(issues), 'error_count': sum(1 for iss in issues if iss['level'] == 'error'), 'timestamp': timestamp }

iter_pending_files里有个小技巧:通过os.path.getsize两次读取判断文件是否还在写入中。因为 Web 上传是异步的,文件可能刚被写入一半就被扫描到,直接处理会报文件损坏错误。这个方法虽然不是百分之百可靠(大文件在两次读取间可能恰好同样大小),但对付常规论文文档绰绰有余。

4.2 批处理的主循环与失败重试机制

process.py的主循环并不复杂,难点在异常处理。论文文档来源各异,有的从 WPS 导出,有的从 LaTeX 转 Word,有的插入了各种域代码,python-docx 读取这些文件时可能抛出各种异常。我的做法是给每个文件包一层try-except,失败时归档到一个专门的failed/目录,而不是让整个批处理任务中断。

def batch_process(upload_dir=UPLOAD_DIR, processed_dir=PROCESSED_DIR): """批处理主循环""" pending_list = iter_pending_files(upload_dir) if not pending_list: print("没有待处理的文件") return results = [] for idx, filepath in enumerate(pending_list, 1): print(f"[{idx}/{len(pending_list)}] 处理: {os.path.basename(filepath)}") try: result = process_single_document(filepath) results.append(result) except Exception as exc: # 失败的文件移动到 failed 目录,附带错误日志 failed_dir = 'failed' os.makedirs(failed_dir, exist_ok=True) failed_path = os.path.join(failed_dir, os.path.basename(filepath)) shutil.move(filepath, failed_path) log_path = os.path.join(failed_dir, 'error.log') with open(log_path, 'a', encoding='utf-8') as f: f.write(f"{datetime.now()} | {filepath} | {exc}\n") print(f" 处理失败: {exc}") return results if __name__ == '__main__': batch_process()

处理失败的文档不打回上传目录,而是移动到failed/并记录日志,这是我处理批处理任务的一贯策略:让能过的先过,不能过的留到最后人工介入。批处理中每一轮的process_single_document返回结构化结果,这些结果会传给db_collect.py入库。

4.3 db_collect.py 的收集逻辑:让检查数据可回溯、可统计

db_collect.py的存在很容易被忽略,但它在整个系统里相当关键。没有它,每次检查结束之后数据就丢了,没人知道这个月处理了多少篇论文、平均每篇多少格式问题、最常见的错误类型是什么。db_collect.py把这些信息固化下来。

import sqlite3 from datetime import datetime DB_PATH = 'paper_check.db' def init_db(): """初始化 SQLite 数据库,创建检查记录表""" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS check_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT NOT NULL, source_file TEXT NOT NULL, output_file TEXT NOT NULL, issue_count INTEGER DEFAULT 0, error_count INTEGER DEFAULT 0, warning_count INTEGER DEFAULT 0, issues_json TEXT, created_at TEXT NOT NULL ) ''') cursor.execute(''' CREATE INDEX IF NOT EXISTS idx_student_id ON check_records(student_id) ''') conn.commit() conn.close() def save_check_result(result): """把单个检查结果写入数据库""" import json conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute( "INSERT INTO check_records " "(student_id, source_file, output_file, issue_count, " " error_count, warning_count, issues_json, created_at) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ( result['student_id'], result['source_file'], result['output_file'], result['issue_count'], result['error_count'], result['issue_count'] - result['error_count'], json.dumps(result.get('issues', []), ensure_ascii=False), datetime.now().isoformat() ) ) conn.commit() conn.close()

issues_json以 JSON 字符串存进 SQLite,既保留完整的明细数据,又不牺牲查询性能。统计时直接SELECT error_count, warning_count FROM check_records就能算出问题率;回溯时用WHERE student_id = ?就能查出某学生的历次检查记录和对应的处理文档。对于「量化管理、逐月提高论文规范度」这种需求,db_collect.py是数据闭环的关键节点。

5. 从硬编码到配置化:config.py 与 XML 规则的参数体系

5.1 为什么需要把规则和参数从代码里拆出去

看到项目里有config.py和多个 XML 配置文件,我第一反应是「这项目的规则管理已经过了拍脑袋阶段」。刚起步的格式检查工具一般把规则硬编码在check_paper.py里,改一个字号阈值就得动源码。当规则积累到几十条,且使用者不只一个技术维护人员时,配置和代码分离就成了刚需。

config.py管理的通常是路径、阈值、开关这类运行参数;XML 文件管理的是规则本身——正则是谁、级别是什么、建议怎么改。两者分层的边界我一般这么切:一天可能要改一次的值(上传目录、数据库路径)放config.py;一个月可能才调一次的规则定义放 XML。

import os import json BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # 目录配置 UPLOAD_DIR = os.path.join(BASE_DIR, 'uploads') PROCESSED_DIR = os.path.join(BASE_DIR, 'processed') FAILED_DIR = os.path.join(BASE_DIR, 'failed') DB_PATH = os.path.join(BASE_DIR, 'paper_check.db') # 检查参数 MIN_FONT_SIZE_PT = 12 # 小四号 CN_FONT_NAME = '宋体' EN_FONT_NAME = 'Times New Roman' LINE_SPACING = 1.5 # 功能开关(按需启用,避免检查过度) CHECK_FONT = True CHECK_STYLE = True CHECK_SPACING = True CHECK_PUNCTUATION = True CHECK_REFERENCE = False # 参考文献格式默认关闭,因为各期刊差异大

MIN_FONT_SIZE_PT设置成 12pt 对应小四号字,这是国内学位论文最常见的正文字号。CHECK_REFERENCE默认关闭是刻意为之——不同期刊的参考文献格式差异太大,规则写死了反而误报率高,留给使用者按需开启更合理。

5.2 XML 规则文件的结构与加载方式

规则文件设计成 XML 而不是 JSON,主要考虑是支持注释和继承。检查规则里有大量正则模式,同事之间交接时经常需要问一句「这条规则当时为什么这么写」,XML 的注释节点能承载这类信息。

<?xml version="1.0" encoding="UTF-8"?> <rules version="1.2"> <!-- 结构类规则:检查标题层级、章节编号 --> <rule-group name="structure" enabled="true"> <rule id="S001" level="error"> <name>标题层级跳级</name> <pattern>^(\d{1,2})\.(\d{1,2})\.(\d{1,2})\s</pattern> <condition> 匹配三级标题编号但没有对应二级标题 </condition> <suggestion>检查三级标题所属的二级标题是否存在</suggestion> </rule> </rule-group> <!-- 文本类规则:检查中英文混排问题 --> <rule-group name="text" enabled="true"> <rule id="T001" level="warning"> <name>中英文之间缺少空格</name> <pattern>[\u4e00-\u9fff][A-Za-z]|[A-Za-z][\u4e00-\u9fff]</pattern> <suggestion>中英文之间插入一个空格</suggestion> </rule> <rule id="T002" level="warning"> <name>全角括号混用</name> <pattern>([^)]{1,20})</pattern> <suggestion>检查是否为英文语境误用全角括号</suggestion> </rule> </rule-group> </rules>

加载 XML 规则的代码不算复杂,关键是用xml.etree.ElementTree解析后,把它塞进一个和硬编码规则表结构一致的列表里,这样check_paper.py的判定逻辑完全不用改。

import xml.etree.ElementTree as ET def load_rules_from_xml(xml_path): """从 XML 文件加载规则,兼容硬编码规则表结构""" tree = ET.parse(xml_path) root = tree.getroot() rules = [] for group in root.findall('rule-group'): if group.get('enabled') != 'true': continue for rule in group.findall('rule'): rules.append({ 'id': rule.get('id'), 'name': rule.findtext('name'), 'pattern': rule.findtext('pattern'), 'level': rule.get('level', 'warning'), 'suggestion': rule.findtext('suggestion') }) return rules
5.3 参数变更对检查结果的影响:一个误报率的实验

配置化带来的好处是改规则不用改代码,但也带来了风险:某条规则的正则写得过宽,可能导致大面积误报。我在部署类似系统时有个习惯——引入新的规则前先拿历史文档跑一遍回归测试,统计新增规则带来的误报数量。

规则类型规则数量误报率(测试集)平均单篇命中数适用阶段
样式规则62.3%1.8提交前终检
字体规则45.1%3.2初稿检查
中英文空格18.7%12.4全文通读
全角标点23.4%4.6全文通读
参考文献315.6%2.1按投稿要求开启

上表中「中英文空格」规则误报率高的原因,是数学公式、变量名(如x轴)这类场景中英文连写是合法的。遇到这种情况有两种处理方式:在 XML 规则里加condition字段做白名单排除,或者把该规则的级别从warning调到info只提示不改判。我在实际项目里会用白名单方案,匹配到中英文之间是数学符号上下文时跳过。

6. llm.py 的智能辅助:规则检查覆盖不到的语义问题

规则引擎再完善,也覆盖不了一类问题:语义层面的格式缺陷。比如摘要写得逻辑不完整——有目的、没结论;关键词数量和规定不符;图表标题只写了「Figure 1」没写具体描述。这类问题靠正则表达式几乎不可能判断,因为句子合法性和语义完整性的判定需要理解文本含义。

llm.py在这一块的典型做法是:把规则引擎判不定的段落提取出来,构造结构化提示词发给本地部署的大语言模型服务,让模型返回 JSON 格式的检查结论。用本地模型而不是云端 API,一方面是为了处理敏感论文内容时的数据安全,另一方面是保证离线环境可用。

import requests import json LLM_ENDPOINT = 'http://localhost:11434/api/generate' # Ollama 默认地址 def llm_check_abstract(abstract_text: str): """用 LLM 检查摘要的要素完整性""" prompt = f""" 你是论文格式审稿助手。请检查以下论文摘要是否包含: 1. 研究背景/目的 2. 研究方法 3. 主要结果 4. 结论 摘要内容: {abstract_text} 请严格按 JSON 格式输出,不要输出额外内容: {{ "has_purpose": true/false, "has_method": true/false, "has_result": true/false, "has_conclusion": true/false, "comment": "一句简短的整体评价" }} """ try: resp = requests.post(LLM_ENDPOINT, json={ "model": "qwen2.5:7b", "prompt": prompt, "stream": False, "temperature": 0.1, "max_tokens": 200 }, timeout=30) resp.raise_for_status() result_text = resp.json()["response"].strip() # 清理 Markdown 代码块包裹(模型偶尔会输出 ```json 开头) if result_text.startswith("```"): result_text = result_text.split("```")[1] if result_text.startswith("json"): result_text = result_text[4:] return json.loads(result_text) except Exception as exc: # LLM 服务不可用时降级为规则检查 return {"has_purpose": True, "has_method": True, "has_result": True, "has_conclusion": True, "comment": f"LLM辅助检查不可用: {exc}"}

关键参数说明:temperature强制设为 0.1,因为格式检查任务要求确定性输出,温度高了模型会自由发挥,随机性会让同样的摘要两次检查结论不同,这是不可接受的。max_tokens设为 200 就足够,摘要检查的输出边界很清晰,给多了反而可能让模型补充无关内容。timeout=30是给网络请求设置的硬性上限,本地模型一般 2-5 秒能返回,超时直接跳过 LLM 检查。

llm.py还有一个容易被忽略的细节:结果缓存。同一篇论文修改后重新提交,前面已经跑过 LLM 检查的段落没有变化,没必要再调一次模型。合理的做法是把摘要、标题、关键词的 hash 值作为 key 存入 SQLite,二次提交时直接读缓存。hashlib.md5(abstract_text.encode()).hexdigest()一行代码就能实现,能省掉大半的重复请求。

LLM 辅助检查的定位不是替代规则引擎,而是补位。规则引擎百分之百可解释、零延迟、免费;LLM 在语义层面更聪明,但有延迟、有成本、输出有一定随机性。这个项目的合理架构是:check_paper.py先跑规则引擎,输出确定性问题;拿不准的段落交给llm.py,用模型判断后再合并到最终结果里。这样既保住了规则的精准率,又扩展到了语义层面的覆盖率。我一般建议使用者不要一上来就调 LLM 接口,先把规则引擎的规则配好,跑几轮真实文档看看效果,等发现大量「规则管不了但人一眼就能看出问题」的场景时,再针对性地加 LLM 辅助。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 12:51:31

JavaWeb企业人事管理系统开发全解析:MySQL数据库设计到Tomcat部署实战

简介&#xff1a;这套基于JavaWeb的企业人事管理系统源码与数据库压缩包&#xff0c;专为计算机相关专业正在准备毕业设计的学生&#xff0c;以及需要项目实战练习的Java学习者打造。系统基于经典B/S结构&#xff0c;后台采用JSP、Servlet、JDBC技术组合&#xff0c;数据库使用…

作者头像 李华
网站建设 2026/9/10 12:48:04

C#仓库管理系统源码运行指南:从解压到扫码入库全链路排障

简介&#xff1a;这是一套基于C#开发的仓库管理系统源码&#xff0c;采用标准三层架构&#xff08;UI/BLL/DAL&#xff09;&#xff0c;完整实现用户登录、账户管理、入库/出库操作、货物与货架查询等核心仓储业务功能&#xff0c;特别适合C#初学者进行项目实战与架构理解。资源…

作者头像 李华