简介:毕业设计资源围绕大语言模型与多模态人工智能技术,构建健康管理与辅助诊疗系统,面向计算机、医学信息工程等专业学生,提供从需求分析、系统设计到论文撰写与答辩展示的完整参考。压缩包共247个文件,约93.2MB,核心包含论文电子版与汇报PPT,另有Vue.js前端页面、Flask后端服务、MySQL数据库脚本及RabbitMQ消息队列配置;大模型侧基于PyTorch与Transformers框架,集成Qwen2.5-3B-Instruct推理能力,覆盖数据存储、消息通信与智能问答等关键环节。包内46张jpg界面截图可用于对照系统运行效果,多份PDF与设计文档辅助理解架构思路,py源码与vue组件按模块组织,目录清晰便于按需检索。阅读论文可还原设计决策脉络,参照PPT可快速组织答辩内容,适合作开题、中期检查及答辩前参考;目前已有92人学习下载。
1. LLM+多模态人工智能的健康管理与辅助诊疗系统:毕业设计为什么选“多模态”而不只靠大模型
你拿着血常规报告去问通用大模型,它只能帮你读字面意思;你拍一张舌象照片问它,它倒是能说两句,可一旦把几十项检验指标和主诉文本放在一起,它就不知道先看哪个了。真正的健康管理与辅助诊疗系统,本质是一个“信息整合”命题,不是“接一个LLM”命题。这套毕业设计题目给出的答案是把文本问诊、语音描述、医学图像、检验指标四路输入汇到同一条推理链路,再交给LLM做综合判断,最后输出带风险等级的结构化建议。我拆完这套资源和配套论文、汇报PPT之后的直观感受是:多模态融合是它的技术难点,也是论文工作量和答辩演示里最拿得出手的地方。适合想做医疗AI方向、又不想把毕业设计做成“API调用员”的学生。
整套资源覆盖了从系统设计、数据库建模、多模态预处理到RAG检索增强、Prompt编排,再到论文成稿和汇报PPT的完整链路。你照着走完,能看清医疗场景下“数据入口→特征融合→模型推理→结果输出”的完整形态,也能知道哪些环节是真正的工作量,哪些环节是纯凑字数。
2. 系统架构与技术选型:从需求到调用链的落地映射
2.1 需求拆解:辅助诊疗系统到底在解决什么
辅助诊疗不是替代医生下结论,而是把医生接诊时看到的文字描述、语音补充、影像资料和检验数值汇总成一份“可讨论的结构化参考意见”。毕业设计层面,需求拆成下面几条链路才说得清楚:
- 用户描述症状,系统支持文字输入和语音输入两种方式;
- 用户上传体检报告或拍摄舌象、面相、皮肤照片,系统提取可计算的特征;
- 系统关联用户历史健康档案,按时间维度生成健康趋势;
- LLM依据多模态输入和检索到的医学知识,生成辅助诊断建议、生活干预建议和复查提醒;
- 所有结果结构化落库,支持医生或用户事后回看审计。
这套系统的主流程可以表述为“多模态输入采集 → 数据预处理 → 特征融合 → 知识检索 → LLM推理 → 结构化输出”。你在论文的“系统设计”章节里画这条调用链,比写一百行功能描述都直观。
这里要提醒你一个习惯问题:别在一开始就陷入“模型选多大”的纠结。毕业设计的核心评价点是“完整度”和“可解释性”。导师想看到的是你理解每个环节为什么存在,而不是你调了一个多牛的模型。
2.2 技术选型:为什么是RAG+多模态,而不是单一模型
先看一张我常用的对比表,这张表可以直接改写进论文的“技术选型分析”小节:
| 对比维度 | 纯LLM问答方案 | RAG+多模态融合方案 |
|---|---|---|
| 输入形态 | 仅文本 | 文本、语音、图像、表格指标 |
| 医学知识时效性 | 依赖模型训练数据,容易过时 | 知识库独立更新,可控性强 |
| 幻觉风险 | 高,医学场景不可接受 | 中低,检索结果可追溯 |
| 输出可解释性 | 低,无法定位依据 | 高,可展示知识来源 |
| 毕业设计工作量 | 偏少,答辩容易空洞 | 适中,每一章都有实体内容 |
| 部署成本 | 低 | 可控,向量库可本地部署 |
这里的选择逻辑很明确:医学是高风险领域,LLM的幻觉问题不是靠“微调”能解决的。微调在本科毕设里成本极高——需要标注数据、GPU资源和大量实验时间,而且微调之后依然无法解决时效性问题。RAG则把“模型能力”和“知识来源”解耦,你可以在不重训模型的情况下,把最新版药品说明书、检验指标参考区间塞进知识库。
技术栈方面,我的习惯是后端用 Python FastAPI,前端用 Vue3 或微信小程序二选一。数据库用 MySQL 存用户档案和诊断记录,向量库用 Milvus 或 Chroma 存知识库切片。大模型接口统一走一个 middleware 网关,便于切换不同服务商,避免答辩当天某一家 API 不可用导致整个演示凉掉。
2.3 数据库设计:健康档案、检查记录与知识库的三层结构
数据库是这套系统里最容易被忽视,但论文里最好写的一部分。我建议至少设计三组核心表:用户健康档案主表、每次咨询的检查记录表、辅助诊疗结论表。
-- 用户健康档案表:存储基础信息和历史病史 CREATE TABLE user_profile ( id INT PRIMARY KEY AUTO_INCREMENT, user_name VARCHAR(64) NOT NULL, age INT, gender TINYINT COMMENT '0-男 1-女 2-未知', height_cm DECIMAL(5,1), weight_kg DECIMAL(5,1), allergy_history TEXT COMMENT '过敏史,支持逗号分隔多个条目', chronic_disease TEXT COMMENT '慢性病史:高血压/糖尿病/其他', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 咨询检查记录表:一次咨询对应一条主记录和N条多模态附件 CREATE TABLE consult_record ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, consult_time DATETIME, symptom_text TEXT COMMENT '主诉文本,一段话描述症状', voice_transcript TEXT COMMENT '语音转写后的文本', image_paths JSON COMMENT '上传图像的文件路径列表', lab_indicator JSON COMMENT '检验指标的JSON结构化字段', status TINYINT DEFAULT 0 COMMENT '0-处理中 1-已完成 2-失败', FOREIGN KEY (user_id) REFERENCES user_profile(id) ); -- 辅助诊疗结论表:LLM输出落库,支持审计回溯 CREATE TABLE diagnosis_result ( id INT PRIMARY KEY AUTO_INCREMENT, consult_id INT NOT NULL, risk_level TINYINT COMMENT '1-低风险 2-中风险 3-高风险', summary TEXT COMMENT 'LLM生成的综合判断摘要', suggestions JSON COMMENT '结构化建议列表:用药提醒/生活方式/复查建议', references JSON COMMENT '知识库来源引用,用于可追溯', model_name VARCHAR(64) COMMENT '本次调用的模型标识', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (consult_id) REFERENCES consult_record(id) );image_paths用文件路径列表而不用二进制存库,是工程经验和论文双重考虑的结论:图片在数据库里只保留路径,实际文件走独立存储目录或 OSS,既方便清洗调试,也能避免数据库膨胀。lab_indicator用 JSON 是因为不同体检机构的项目名称千差万别,结构化字段在毕业论文里反而难以覆盖所有场景。diagnosis_result里单独留一个model_name字段,答辩时如果你想对比不同模型的效果,这条字段能帮你省很多事。
3. 多模态数据接入与预处理:文本、语音、图像、检验指标的四路融合
3.1 文本与语音:问诊信息的清洗与转写
文本是最基础的一路输入,但它的坑不在“接进去”,而在“怎么洗”。用户输入的主诉文本通常口语化严重,夹杂着错别字、网络用语和大量无用信息。我一般会做三层清洗:第一层正则去掉表情符号和重复标点;第二层做同义替换,比如把“有点难受”归一化成“不适”;第三层做症状关键词提取,抽取出部位、持续时间、疼痛性质等字段。
语音输入在毕设里常被做成“看起来有,实际不解释”的黑匣子,这其实是论文里可以浓墨重彩写的一块。语音转写我一般接 Whisper 或云厂商 ASR,但关键点在于转写后的文本不能直接进模型,要做置信度处理。
import re import json def normalize_symptom_text(raw_text: str) -> dict: """ 清洗主诉文本,提取结构化症状要素 返回JSON方便后续拼接Prompt """ # 去掉表情符号:匹配常见emoji范围,直接替换为空 emoji_pattern = re.compile( "[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF]", flags=re.UNICODE ) text = emoji_pattern.sub("", raw_text) # 去除多余空白和重复标点 text = re.sub(r"\s+", " ", text).strip() text = re.sub(r"[。!?!?]{2,}", "。", text) # 用简单规则抽取症状片段:按标点切分,过滤过短片段 segments = [seg for seg in re.split(r"[,,。;;]", text) if len(seg) >= 2] # 同义归一化映射表,按需求自行扩充 synonym_map = { "有点难受": "轻微不适", "特别疼": "剧烈疼痛", "老犯困": "嗜睡", "没力气": "乏力" } segments = [synonym_map.get(seg, seg) for seg in segments] return { "cleaned_text": " ".join(segments), "segment_count": len(segments), "segments": segments } # 调用示例:模拟用户输入 sample_input = "最近老犯困,没力气,偶尔还有点难受😞,食欲也一般般..." result = normalize_symptom_text(sample_input) print(json.dumps(result, ensure_ascii=False, indent=2))这段代码里有三个参数值得你在答辩时展开讲:emoji_pattern的unicode范围匹配,处理的是用户从手机粘贴文本时夹带的符号;re.sub(r"[。!?!?]{2,}", "。", text)把多个终止符压缩成一个,避免LLM把重复标点当作信息密度;segment_count过滤单字碎片,防止像“疼”这样的单字被单独作为一段输入。语音转写文本走的也是同一套清洗逻辑,之后可以和手动输入统一处理。
语音分支还有一个容易翻车的细节:转写结果的置信度。Whisper 这类工具对普通话的识别率在安静环境下还行,但用户一旦站在嘈杂环境里,转写文本里经常出现同音错字。我会在转写后加一个简单的“医学术语纠错表”,把“心慌”误写成“星荒”这类错误用映射表纠正回来。这个纠错表只有几十条,但足以让答辩时的语音演示效果稳一大截。
3.2 医学图像:从舌象照片到可计算的特征描述
图像输入放在这套系统里,最合适的入口是舌象和面色判断,这也是中医诊断里成熟度比较高的方向。我不建议直接让LLM“看”图片——目前的LLM视觉接口对医学图像的编码细节理解有限,而且你要在论文里写清楚特征提取算法,直接丢给大模型反而没什么可写。
我采用的是双路方案:第一路用OpenCV做传统特征提取,计算舌色、舌苔厚薄的HSV分布特征;第二路把关键特征文本化后拼进Prompt。这样做的好处是让LLM基于“描述”而非“原始像素”推理,可控性强得多。
import cv2 import numpy as np def extract_tongue_features(image_path: str) -> dict: """ 提取舌象HSV颜色特征 返回舌色倾向和舌苔厚薄的量化描述 """ img = cv2.imread(image_path) if img is None: return {"error": "image not found"} # 统一缩放到固定尺寸,减少不同设备拍照的尺度差异 img = cv2.resize(img, (224, 224), interpolation=cv2.INTER_AREA) # 转HSV色彩空间,医学图像常用HSV而非RGB做色相分析 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 划分舌体区域:简化版本用中心60%区域近似,实际可用分割模型 h, w, _ = hsv.shape roi = hsv[int(h*0.2):int(h*0.8), int(w*0.2):int(w*0.8)] h_channel = roi[:, :, 0].flatten() s_channel = roi[:, :, 1].flatten() v_channel = roi[:, :, 2].flatten() # 统计H通道均值:OpenCV中H范围0-179,红色约0-10和170-179 avg_hue = np.mean(h_channel) avg_sat = np.mean(s_channel) avg_val = np.mean(v_channel) # 按阈值规则给出文本描述,避免直接把数值丢给LLM if avg_hue < 10 or avg_hue > 165: tongue_color = "偏红" elif avg_hue < 25: tongue_color = "偏黄" else: tongue_color = "偏淡" if avg_sat > 100: coating = "舌苔较厚" elif avg_sat > 60: coating = "舌苔中等" else: coating = "舌苔较薄" return { "tongue_color": tongue_color, "coating": coating, "avg_hue": round(float(avg_hue), 2), "avg_sat": round(float(avg_sat), 2) }cv2.resize用INTER_AREA而不是默认的INTER_LINEAR,是因为插值算法直接关系到后续色彩统计的稳定性。ROI区域取中心60%,是为了减少嘴唇、牙齿和背景对色相统计的干扰。H通道的阈值判断标准不追求医学精确,但足以在论文里展示一套“规则可解释”的特征提取流程。你如果后续想改进,可以把ROI提取换成轻量级分割模型,这块写进“未来展望”里会让论文的延展性更好。
3.3 检验指标:规则校验与异常标记
检验指标是四路输入中“数值密集度”最高的一路,也是最容易出乱子的。用户上传一张体检报告照片,OCR识别出来的数字经常缺单位或错位,直接塞给LLM会产生离谱幻觉。我在这路输入上坚持“宁缺毋滥”的原则:先做规则校验,不合格的字段宁可丢弃也不能硬传给模型。
def validate_lab_indicators(indicators: dict) -> dict: """ 校验检验指标:单位归一化 + 范围合法性判断 + 异常标记 输入格式: {"项目": {"value": 数值, "unit": "单位"}} """ REFERENCE_RANGES = { "白细胞计数": (4.0, 10.0), # 单位: 10^9/L "血红蛋白": (120.0, 160.0), # 单位: g/L "空腹血糖": (3.9, 6.1), # 单位: mmol/L "甘油三酯": (0.4, 1.7) # 单位: mmol/L } UNITS = { "白细胞计数": "10^9/L", "血红蛋白": "g/L", "空腹血糖": "mmol/L", "甘油三酯": "mmol/L" } validated = {} for item, data in indicators.items(): if item not in REFERENCE_RANGES: continue value = float(data["value"]) unit = data.get("unit", "") # 物理合理性检查:数值不可能为负数或超出人类极限 if value <= 0 or value > 2000: print(f"[WARN] 非法数值: {item}={value}") continue low, high = REFERENCE_RANGES[item] # 异常标记:低于下限/高于上限/正常 if value < low: status = "low" elif value > high: status = "high" else: status = "normal" validated[item] = { "value": value, "unit": UNITS[item], "status": status, "reference": f"{low}-{high}" } return validated这个函数的核心价值体现在三处:REFERENCE_RANGES定义的是“参考范围”,论文里必须标注来源是临床指南或教材,不能自己编;物理合理性检查里的value > 2000是兜底过滤,防OCR识别错位;status字段设计成low/high/normal三态而非直接写“异常”,是把判断权留给LLM,系统只做数据标记。
异常标记之后的融合策略也很关键:不是所有维度都要喂给LLM。我的原则是“有异常才强调,正常值只做概要”。当检验指标超过10项时,如果全部拼接进Prompt,LLM的注意力会被正常值稀释。我会把异常项单独挑出,加上一句“以下指标超出参考范围”,再附带全部指标的JSON概要。
4. 核心诊疗引擎:Prompt编排、医学知识库与LLM的协作
4.1 从多模态输入到结构化Prompt:特征怎么喂给模型
这是整套系统技术含量最高的模块。LLM本身不理解“多模态”,它只理解文本。所以你的工程能力体现在:把四种输入形态统一转成文本特征,并用一种让LLM容易遵循的结构排列出来。
我习惯把Prompt分成四段:系统角色定义、当前用户状态、知识库检索结果(可选)、输出格式约束。下面是一个可替换的构造函数:
def build_medical_prompt( user_profile: dict, cleaned_symptom: str, tongue_features: dict, lab_indicators: dict, retrieved_knowledge: list ) -> str: """ 构造最终发送给LLM的Prompt retrieved_knowledge: RAG检索结果切片列表 """ # 第一段:角色定位——明确边界,禁止越权诊断 system_role = ( "你是一名健康管理助手,你的任务是基于用户提供的主诉、" "体征数据和检验指标,给出健康风险提示与就医建议。" "你不是执业医师,不能给出确诊结论。若信息不足," "必须主动询问或建议就医。" ) # 第二段:用户基础档案摘要 user_summary = ( f"用户年龄:{user_profile['age']}岁," f"性别:{'男' if user_profile['gender'] == 0 else '女'}," f"身高:{user_profile['height_cm']}cm," f"体重:{user_profile['weight_kg']}kg。" ) if user_profile.get('chronic_disease'): user_summary += f"既往病史:{user_profile['chronic_disease']}。" # 第三段:症状、舌象、检验指标 status_block = ( f"主诉症状:{cleaned_symptom}\n" f"舌象特征:{tongue_features.get('tongue_color', '未知')}," f"{tongue_features.get('coating', '未知')}\n" f"检验指标(关注异常项):\n" ) # 只把异常指标详细列出,正常项一笔带过 for item, detail in lab_indicators.items(): if detail["status"] != "normal": status_block += ( f"- {item}: {detail['value']} {detail['unit']} " f"(参考范围 {detail['reference']}) [异常:{detail['status']}]\n" ) # 第四段:RAG检索到的参考知识 knowledge_block = "" if retrieved_knowledge: knowledge_block = "以下是检索到的医学参考资料,请优先依据它们:\n" for i, doc in enumerate(retrieved_knowledge[:3], 1): knowledge_block += f"[{i}] {doc}\n" # 第五段:输出格式硬约束 output_constraint = ( "请按以下JSON格式输出,不要输出额外解释:\n" "{\"risk_level\": \"low|medium|high\", " "\"summary\": \"综合判断摘要\", " "\"suggestions\": [\"建议1\", \"建议2\"], " "\"need_doctor\": true}" ) prompt = "\n".join([ system_role, "用户档案:" + user_summary, "当前状态:" + status_block, knowledge_block, output_constraint ]) return prompt这段代码的设计意图是让LLM明确三件事:第一,它不能被当做人——system_role里的“不能给出确诊结论”既是产品合规要求,也是论文里“安全性设计”章节的素材;第二,它的注意力被引导到异常项而非全部数据——[异常:high]这种显式标记比单纯数值更能触发模型的敏感度;第三,它的输出被强制约束成JSON——这样后续才能自动化解析、落库到diagnosis_result表。
这里有一个经验性的细节:suggestions字段我故意用中文键而非英文字段,因为绝大多数中文医疗语料训练出的模型对中文JSON键名的一致性更好,如果你用riskLevel这种驼峰格式,偶尔会出现模型输出和你的解析代码不匹配的问题。
4.2 知识库检索与召回:让LLM依据指南而不是凭空编
RAG模块在毕设里的实现并不需要多高的复杂度,但需要把链路跑通。我在这个系统里用的检索链路是:医学文档 → 文本切片 → Embedding向量化 → 向量检索 → TopK结果拼进Prompt。
切片策略是RAG工程里最玄学的部分,但也是论文里最好写参数分析的部分。我的默认配置是:切片大小500字符,重叠50字符。太大,切片内容容易被无关信息稀释;太小,语义不完整。重叠50字符是为了避免恰好把一个完整概念切断在切片边界。
from typing import List def chunk_text(doc: str, chunk_size: int = 500, overlap: int = 50) -> List[str]: """ 文本切片函数:按段落优先、长度兜底的策略 """ paragraphs = doc.split("\n") chunks = [] current = "" for para in paragraphs: # 段落过长时强制按固定窗口切分 if len(para) > chunk_size: for i in range(0, len(para), chunk_size - overlap): chunks.append(para[i:i + chunk_size]) continue # 当前累积加段落仍小于切片上限,继续累积 if len(current) + len(para) + 1 <= chunk_size: current += para + "\n" else: # 先把当前切片截断到上限,然后保留重叠部分重新累积 chunks.append(current[:chunk_size]) tail = current[-overlap:] if overlap > 0 else "" current = tail + para + "\n" if current.strip(): chunks.append(current[:chunk_size]) return chunks这个切片函数的关键在于“段落优先”策略:先按换行符切段,只有段落长度超过阈值时才硬切。这样做比纯按字符长度切片的效果明显要好,因为医学文档的段落本身就承载了完整语义。
知识库的内容从哪里来?毕设场景里最方便的是把《内科学》常见病章节的电子版、药品说明书公开数据、以及体检报告常见指标解读整理成Markdown文档,按疾病类型分目录存放。这里有一个合规细节:论文里要写清楚知识来源,不要直接引用受版权保护的整本教材。我一般建议用公开指南摘要和科普级别内容,答辩时更安全。
4.3 输出后校验与LLM-as-judge:让演示结果可复现
把模型输出直接落库不是好习惯。我一般会在模型返回后加三道校验:第一道是JSON语法解析校验,很多模型偶尔会输出多余的前导文字或末尾句号,解析失败就要求模型重试一次;第二道是枚举字段校验,比如risk_level必须严格是low/medium/high之一,出现其他值就抛出不兼容错误;第三道是规则校验,比如用户所有指标正常且主诉为“无不适”时,risk_level不应为high。
这三道校验可以在论文里写成“输出鲁棒性设计”,是一块能体现工程素养的内容。每次从模型返回的结果都先过这三个关卡,过了才写入diagnosis_result表,不过就触发自动重试。
LLM-as-judge 是最近在AI应用层非常流行的质量验证方法,放在这里用性价比极高:拿一个额外的模型(可以是同一个模型的另一个实例,也可以是一个轻量模型)去给主模型的输出打分,判断是否有逻辑矛盾或遗漏关键项。这在答辩演示时是个很漂亮的加分项——你可以在PPT里放两列:主模型的输出、评判模型的打分。
import json def validate_and_judge(model_output: str) -> dict: """ 输出后处理:先解析JSON,再规则校验 """ # 清理模型输出中的多余字符,兼容常见非严格JSON cleaned = model_output.strip() if cleaned.startswith("```json"): cleaned = cleaned[7:-3].strip() if cleaned.endswith("```"): cleaned = cleaned[:-3].strip() try: result = json.loads(cleaned) except json.JSONDecodeError as e: return {"valid": False, "error": f"JSON解析失败: {e}"} # 枚举校验 if result.get("risk_level") not in ["low", "medium", "high"]: return {"valid": False, "error": f"非法风险等级: {result.get('risk_level')}"} if "suggestions" not in result or not isinstance(result["suggestions"], list): return {"valid": False, "error": "缺少suggestions列表"} if not isinstance(result.get("need_doctor"), bool): return {"valid": False, "error": "need_doctor必须为布尔值"} return {"valid": True, "result": result}这一步能过滤掉约5%~10%的不稳定输出。答辩前用固定测试集跑一遍,所有输出都无异常时,演示才不会有“现场翻车”的风险。
5. 毕业设计避坑与排查:从数据到答辩的五个高频翻车点
5.1 多模态接口超时,前端频繁报错
现象:图像上传后,前端等很久没有响应,最后直接超时;语音转写偶尔也卡住,用户以为系统崩了。
原因:这三个操作都是计算密集型的——图片特征提取、ASR转写、LLM推理——如果后端是同步阻塞逻辑,一个请求占住线程,其他请求全部排队。
解决:把耗时操作全部改为异步任务。后端用FastAPI的BackgroundTasks或独立的消息队列(Celery或简单的Redis队列),前端提交后先拿到一个任务ID,然后轮询或WebSocket推送结果。我的做法是更粗暴但更稳的方案:图片和语音预处理直接同步执行,控制在500ms以内;LLM推理设计为30秒超时,配合前端loading文案“AI医生正在分析,请稍候”,用户在这个场景下普遍有耐心等10秒以上。
5.2 同样的输入,两次给的结论不一样
现象:答辩前一天演示还很正常,第二天跑同一个测试用例,输出的建议内容变了,甚至风险等级都不同。
原因:大模型推理本身带有随机性,temperature参数没有设置为0,或者远程API在频繁请求时自动调整了采样参数。
解决:所有医疗场景的推理请求,temperature固定设为0或接近0的值,关闭随机采样;同时把随机种子seed固定。如果你调用的是云端API,要看服务商文档确认是否支持seed参数。还有一招后悔药:把每次的模型输出连同输入一起存库,答辩时如果评委要求看一致性,直接展示历史记录,比口头解释更有说服力。
5.3 RAG检索总召回不到相关内容,医疗回答出现幻觉
现象:知识库里明明有高血压管理章节,用户问“血压高怎么办”,检索结果却返回了“糖尿病饮食建议”,最后LLM给出的答案出现明显编造。
原因:最常见的根源是切片时把大标题和正文切断了,Embedding向量丢失了语义锚点。比如“高血压”这个标题落在切片A末尾,“患者饮食建议”落在切片B开头,切片B的向量就无法和查询建立关联。
解决:把文档切片策略从“按长度硬切”改成“按语义块切”。保留Markdown各级标题作为切片的元数据,在切片内容前面拼上标题路径。这个做法在向量检索里叫做metadata augmentation,用大白话说就是:让每个切片知道自己属于哪一章,检索时顺手过滤掉不属于该疾病章节的无关结果。
5.4 答辩演示时网络波动,整个系统瘫掉
现象:到了演示节点,访问大模型API的请求超时或返回限流错误,前端白屏,演示中断。
原因:毕业设计答辩现场用的往往是大楼公用WiFi,对境外或高并发API的限制很严格,而且现场多人同时用网,带宽极不稳定。
解决:准备两层降级方案。第一层是本地模型兜底——在答辩用的笔记本上部署一个小模型(比如几GB的量化模型),网络API失败时自动切换到本地推理,速度稍慢但能跑通全流程。第二层是“预录演示视频”法,把完整操作流程提前录制成高清视频放在本地,现场如果网络确实不行,直接播放视频并同步讲解。这两层方案我都试过,实际体验上本地模型兜底更自然,评委不会觉得你在逃避演示。
5.5 论文查重偏高,AI生成的痕迹明显
现象:论文提交查重后重复率超过30%,标注的AI痕迹检测为高风险。
原因:毕设系统相关的章节用了太多套话模板,比如“随着人工智能技术的飞速发展”这类开头在知网库里同质化严重,加上从LLM生成内容里直接摘录的段落未改写。
解决:论文叙事从“技术栈罗列”改成“问题驱动”。每一章开头先写“这里遇到了什么问题,现有方案为什么不够”,再写“我采用了什么方案,参数怎么定的”。比如第2章不要写“该系统采用FastAPI框架”,改写成“最初使用Flask搭建后端,但异步任务增多后出现阻塞现象,调研后改用FastAPI的异步支持”。叙述方式的变化会显著降低查重率,同时让论文看起来有真实决策过程。
6. 从系统到答辩:固定测试用例与汇报PPT的关键技巧
6.1 固定测试集:一致性验证与效果演示两用
系统开发完成后,我强烈建议你建一个固定测试集,至少包含10~20个病例场景。每个场景写清楚四路输入的标准值:主诉文本、语音转写文本、舌象描述特征、检验指标JSON、预期输出等级。
这个测试集有三个用途。第一,功能性回归测试——每次改完代码后跑一遍,确保没有把之前能跑通的场景改坏。第二,一致性验证——固定temperature=0后连续跑三次相同输入,检查输出是否一致。第三,答辩演示素材——挑其中3个最典型的病例做演示路径,一个低风险、一个中风险、一个高风险,覆盖全部输出形态。
我设计测试集时有一条血泪经验:不要只设计“典型症状”用例,也一定要混入“信息不足”的用例。比如只给一句“我最近睡不好”没有补充数据,系统应返回“需要更多症状细节,建议补充睡眠时长、是否伴有其他不适”而不是硬着头皮给建议。这种信息不足场景的处理方式,往往是答辩时评委最欣赏的部分,因为大多数人的毕设都做成了“有输入必输出”的机器人。
6.2 PPT叙事线:让评委在5分钟里看清你的工作量
汇报PPT是毕业设计的最终呈现载体,大多数人会犯同一个毛病:按系统模块一页一页平铺,上来讲数据库建了几张表,中间贴代码截图,最后放运行截图。这种PPT的信息密度很低,评委看不出你的思考。
我用的叙事线是“痛点→矛盾→方案→实证”四段式。开头第一页直接抛出一个场景:用户拿着一份体检报告和一张舌象照片,面对通用大模型,得到的回答是孤立的、割裂的,无法形成综合判断。第二页指出当前方案的局限——纯LLM有幻觉且不消化多模态输入,微调又成本过高。第三页亮出你的系统架构图,重点标注“多模态预处理+RAG+结构化输出”这条主线。之后每一页都在回答“这个模块解决了上一页的哪个矛盾”。
PPT里有一个细节技巧值得压轴使用:放一张“接口调用参数表”,列出不同temperature值和不同TopK检索数量下,同一测试用例的生成结果对比。这张表直观展现了你不只是把模型接进去,还做了参数级别的测试与调优。论文里同样的数据放在实验章节,简直是一鱼两吃。
这次做这个项目到最后,我已经记不清为 RAG 召回率低调了多少次切片参数,半夜盯着日志看“知识库检索为空”的报错看了多少遍。但有一段代码习惯我始终没丢:所有配置项——切片大小、重叠长度、temperature、seed、参考范围阈值——全部集中在一个config.yaml里,每次实验换参都强制记录一条实验日志。答辩时评委问我“你这些参数是拍脑袋定的吗”,我直接翻出实验日志表,横竖都是一张表,说服力远超口头解释。希望你做完这个项目之后,也把“留实验记录”的习惯带走,它比这一个毕设项目本身更值钱。希望帮到你。
本文还有配套的精品资源,点击获取