☰
自然语言处理智能医疗诊断系统:从CSV数据表到NLP应用实战
2026/10/7 15:56:16 网站建设 项目流程

简介:这份基于自然语言处理的智能医疗诊断系统项目资料,面向计算机相关专业学生、教师及从业者,可用于NLP课程设计、毕业设计或项目初期演示。资源围绕疾病、症状、药物与并发症等医疗数据构建知识库,并通过Python后端与Vue前端实现自动问诊与诊断展示,方便理解NLP在医疗场景中的落地方式。压缩包共62个文件,大小56.59MB,含18个CSV医疗数据表、7个Python脚本、9个Vue组件与8个JS文件,以及JSON、Markdown等配置与说明文档;CSV覆盖疾病、症状、药物、预防等维度,Python脚本负责数据处理与API服务,Vue/JS支撑前端交互,结构清晰便于按模块查阅和二次开发。项目代码经测试运行成功并获导师指导认可,答辩评审分95分,可用于课程设计、毕业设计或作为NLP系统进阶样例。目前已有49人浏览学习,适合想深入理解智能诊断系统实现流程的学习者参考使用。

1. 基于自然语言处理的智能医疗诊断系统:数据网络比模型更值得先看

大多数人看到一个“自然语言处理+智能医疗诊断”的项目名,第一反应是“里面是不是挂了个BERT或者GPT”,打算下载回来先看模型结构。但这份资源拆开之后,最值得先看的其实是data目录下那二十多个CSV文件,以及围绕它们写的load_data.py、query.py。整个系统的核心思路不是深挖语言模型,而是把症状、疾病、药品建成了关系网络,用NLP做输入文本的切分与归一化,再靠图式查询把诊断结果和用药建议组织出来。这个定位决定了它适合谁:要做毕业设计或课程设计的计算机、人工智能、电子信息相关专业学生,想快速搭一个能演示、能答辩的垂直领域NLP应用的人,这份资源的结构比大多数“先跑个BERT再微调”的模板要实在得多。如果你只是想学算法,它不一定合口味;如果你想在下周之前把系统跑起来、把文档补全,它会帮你省很多事。

2. 从CSV看数据设计:症状、疾病、药品三类实体怎么串成网络

这套系统拿到手里,第一个要适应的不是代码风格,而是它的数据组织方式。绝大多数NLP课设项目喜欢把文本扔给模型,但这份资源走的是“实体-关系”的老路:先定义症状、疾病、药品三类核心实体,再用关联表把它们的对应关系固化下来。这样做的好处非常明显——不需要大量训练数据,不需要GPU,靠CSV查询就能给出可解释的诊断结果。你在答辩时被问到“为什么这么设计”,答案就藏在这些表里。

2.1 先盘一盘data目录下这堆CSV到底谁是谁

解压之后进data目录,第一眼会有点懵,disease_related_symptom.csv、symptom_related_disease.csv、drug_approval.csv、symptom_prevent.csv……一堆名字长得差不多。我拿到手先做了个分类,建议你也这么做,不然后面改数据时容易改错表。

角色代表文件作用
实体主表disease.csv、symptom.csv、drug.csv存放每种实体的唯一id和基础信息
名称映射表disease_name.csv、symptom_name.csv、drug_name.csv提供标准名称与别名映射
症状-疾病关联symptom_related_disease.csv、disease_related_symptom.csv双向索引,是诊断匹配的主干
属性补充表disease_overview.csv、disease_reason.csv、disease_prevention.csv、disease_complication.csv、disease_treatment.csv围绕疾病id挂接的说明字段
药品使用表drug_usage.csv、drug_approval.csv用药方式、批准文号等信息
纯列表disease_list.txt、symptom_list.txt、drug_list.txt原始实体清单,供脚本归并使用

这三类实体之间靠id关联,不是靠名字。比如symptom.csv里“发热”是S001,symptom_related_disease.csv里存的就是S001和D012的配对。这种设计保证了同一症状不管写“发热”还是“发烧”,只要名称映射表里能归并,都能命中同一批id。你如果想把数据扩展到自己的科室,也只需要沿着这个结构补表,不需要改查询逻辑。

2.2 load_data.py:把十几张CSV一次性读进内存

项目里load_data.py承担的就是“把data目录全部载入”的职责。真实代码里可能用pandas也可能用csv模块,我一般是按可读性优先来写,核心设计是:主表读成字典,关联表读成列表。

import csv from pathlib import Path DATA_DIR = Path("data") def load_all_data(data_dir: Path = DATA_DIR): tables = { "disease": {}, # id -> 疾病行 "symptom": {}, # id -> 症状行 "drug": {}, # id -> 药品行 "symptom_related_disease": [], "disease_related_symptom": [], "disease_complication": [], "drug_usage": [], } # 主表读成字典,后续按id查O(1) for key, fname in [("disease", "disease.csv"), ("symptom", "symptom.csv"), ("drug", "drug.csv")]: with open(data_dir / fname, encoding="utf-8-sig") as f: for row in csv.DictReader(f): tables[key][row["id"]] = row # 关系表按行追加保存,查询时逐行过滤 for key, fname in [("symptom_related_disease", "symptom_related_disease.csv"), ("disease_related_symptom", "disease_related_symptom.csv"), ("disease_complication", "disease_complication.csv"), ("drug_usage", "drug_usage.csv")]: with open(data_dir / fname, encoding="utf-8-sig") as f: for row in csv.DictReader(f): tables[key].append(row) return tables

逻辑说明:主表用id做键存字典,后面按id查疾病详情不需要遍历;关联表保存原始行,虽然查询时是线性扫描,但这个体量下速度完全够用。读取时用utf-8-sig而不是utf-8,是因为这些表大多用Excel打开另存过,Excel会自动加BOM头,用utf-8读会把第一个字段名读出“\ufeff”,后面匹配必出问题。这是一个典型的隐蔽坑,等你跑到第5章会再遇上。

参数说明:如果你拿到的load_data.py里用了pandas,那它的DataFrame组织方式可能不一样,但职责相同。你修改时只要保证返回的tables对象里能按表名取数就行。我习惯把DATA_DIR写成一个参数,是为了后面merge.py合并新数据时能传不同目录,不用改代码。

2.3 itemize.py:从原始文本抽实体,做同义词归一化

data目录里还有三个txt:disease_list.txt、symptom_list.txt、drug_list.txt。这就是原始词表,itemize.py的作用就是把这些txt里的词条和CSV里的标准名称做归并,生成别名映射。别小看这一步,它是整个NLP环节里最出效果的部分。

def build_alias_map(raw_list_path: str, standard_name_path: str) -> dict: alias_map = {} # 原始词表:每行一个可能出现的口语化写法 with open(raw_list_path, encoding="utf-8") as f: raw_names = [line.strip() for line in f if line.strip()] # 标准名称表:读入所有正式id与名称 with open(standard_name_path, encoding="utf-8-sig") as f: standard_rows = list(csv.DictReader(f)) # 把原始词逐条对到标准名上 for raw in raw_names: for row in standard_rows: if raw == row["name"] or raw in row.get("alias", ""): alias_map[raw] = row["id"] break return alias_map

逻辑说明:这个函数的核心价值在于,同一个症状可能出现在symptom_list.txt里写成“心慌”,而symptom_name.csv里的正式名叫“心悸”,如果没有这一层映射,后面query.py按名称匹配就直接漏掉了。所以在实际项目里,这个alias_map会在加载完成后传给query模块,所有入口文本先归一化,再进关联表。我见过不少同学跳过这一步,结果前端输“心慌”查不到任何结果,还以为是数据问题,其实就卡在词表没归并。

参数说明:raw_list_path是原始口语词表,standard_name_path是标准名称表。如果你的项目词表更大,可以考虑在load_data.py里就把alias合并成全局dict,避免query时临时去遍历。这个脚本还有一个作用,就是帮你查数据质量——如果某个raw词对不上任何标准名,说明词表里多了脏数据,得回去清理txt。

3. 诊断引擎的工作方式:从query.py理解“症状→疾病→对策”链路

数据设计看明白了,接下来要看这系统怎么把一句话变成诊断结果。很多同学以为query.py里会有一段“高深的NLP代码”,实际上它做的主要是三件事:从用户输入里切出症状词,用症状词映射到symptom_id,再拿id去关联表里找疾病并排序。整个过程像一个可解释的规则引擎,而不是黑匣子。这一章我们把它拆开讲清楚,顺便把main.py的API参数也过一遍。

3.1 核心匹配逻辑:先召回再排序,别一上来就搞句向量

用户在前端输入“头痛 发热 咳嗽”,后端拿到text后第一件事不是分词,而是直接做包含匹配。为什么?因为这份资源里的症状名词表是有限的,直接用名称包含判断比jieba分词更稳,分词容易把“咳嗽”切碎,反而匹配不上。

def extract_symptom_ids(text: str, symptom_index: dict, alias_map: dict) -> list: matched = [] for sym_id, sym in symptom_index.items(): names = {sym["name"], alias_map.get(sym["name"], "")} for n in names: if n and n in text: matched.append(sym_id) break return matched

逻辑说明:这段代码遍历整个症状表,看哪个症状名称出现在输入文本里。symptom_index是load_data阶段构建好的“症状id -> 症状记录”字典,alias_map负责把口语词并入。返回的matched列表就是召回集合,可能包含多个症状。这种方式的优点是稳定、可调试,你可以在日志里明确看到“到底命中了哪些症状”,答辩时很好讲;缺点是输入里一旦出现错别字就查不到,所以后面可以在alias_map里补常见错别词。

真正决定诊断质量的是排序函数。项目里常见做法是计算“症状覆盖率”和“症状数量”两个指标:覆盖率=该疾病相关症状中被命中的数量/该疾病全部相关症状数量,数量=命中的绝对个数。比如流行性感冒在relation表里有8个相关症状,用户命中了3个,覆盖率37.5%;普通感冒有4个相关症状,用户命中3个,覆盖率75%。虽然绝对数量相同,但后者置信度明显更高,应该排在前面。有的实现会再加一个权重系数,给“发热、咳嗽”这类核心症状更高的分,这个可以在query.py里调整。

3.2 关联查询:并发症、用药、预防信息怎么组装

召回疾病之后,不能只给一个病名就结束。这套系统的价值在于把“诊断报告”做完整:每个疾病要带上概述、病因、并发症、预防、治疗和用药。它们分散在不同表里,查询时就按disease_id去join。

def build_disease_report(disease_id: str, tables: dict) -> dict: report = dict(tables["disease"].get(disease_id, {})) # 概述与病因 for attr_key, table_name in [ ("overview", "disease_overview"), ("reason", "disease_reason"), ("prevention", "disease_prevention"), ("treatment", "disease_treatment"), ]: rows = [r for r in tables[table_name] if r["disease_id"] == disease_id] report[attr_key] = rows[0]["content"] if rows else "" # 并发症 comp_rows = [r for r in tables["disease_complication"] if r["disease_id"] == disease_id] report["complications"] = [r["complication_name"] for r in comp_rows] # 用药 drug_rows = [r for r in tables["drug_usage"] if r["disease_id"] == disease_id] report["drugs"] = drug_rows return report

逻辑说明:这里report是一个字典,把分散在六张表里的信息全部聚合到一个对象里。注意并发症和用药用的是列表,因为一个疾病可能对应多条记录;属性字段比如overview取的是第一条,因为设计上每个疾病只有一条概述。实际项目中你可能还要查disease_related_symptom把相关症状列表填进report,让前端展示“你可能出现的其他症状”,这一步对用户体验提升很明显。

参数说明:disease_id是关联的桥梁,所有表里都有这一列,养成“拿id说话”的习惯,不要拿疾病名去匹配,因为不同表里的疾病名可能带空格、带别名。drug_rows保留了drug_usage.csv里的原始行,里面既有药品id又有用法说明,直接序列化成JSON给前端就行。这里有个性能细节:在3800行的表里做列表推导式是毫秒级的,所以不用提前建索引;如果你把数据量扩到几十万行,再考虑按disease_id建dict分组。

3.3 main.py的API与参数:请求体、返回结构、阈值设置

后端api/main.py是FastAPI写的,对外暴露一个diagnose接口。用FastAPI而不是Flask的原因很简单:自动生成OpenAPI文档,前端调试和答辩演示都方便。核心实现大概长这样:

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI(title="智能医疗诊断") class QueryBody(BaseModel): text: str # 用户输入的症状描述 top_k: int = 5 # 返回前几个候选 min_score: float = 0.3 # 最低覆盖率阈值 @app.post("/api/diagnose") def diagnose(body: QueryBody): matched = extract_symptom_ids(body.text, symptom_index, alias_map) result = query_engine.rank_diseases(matched, top_k=body.top_k, min_score=body.min_score) return {"matched_symptoms": matched, "diseases": result}

逻辑说明:QueryBody定义了请求体结构,text是必传的,top_k和min_score都有默认值。rank_diseases内部先按覆盖率过滤低于min_score的疾病,再按得分降序取前top_k个。注意min_score不要设太高,否则普通感冒这种只有三四个相关症状的病很容易被过滤掉;建议演示时用0.3,答辩时再解释“阈值是启发式调出来的”。

请求参数里最值得调的是top_k。如果设为3,返回结果集中在高置信度疾病,适合做“疑似诊断”;如果设为10,覆盖面大,适合做“鉴别诊断”展示。返回结构中matched_symptoms建议保留,前端可以把它当关键词高亮,用户能理解“系统到底听懂了我哪几个词”,这个细节在答辩演示时很加分。如果main.py里没有设置CORS中间件,先别急,第4章联调时一起处理。

4. 把前后端跑起来:从test_main.http到vite联调

很多下载这份资源的人卡在第一步:后端说能跑,前端说能跑,但就是连不上。这一章直接讲联调路径。我建议的顺序是:先装依赖起后端,用test_main.http直接调接口,确认后端没问题,再起前端,最后处理跨域。千万别上来就两个终端一起开,出问题你分不清是哪一端挂了。

4.1 后端启动:用uvicorn跑FastAPI

后端在api目录下,先创建虚拟环境,再装依赖,最后用uvicorn启动。

cd api python -m venv venv # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate pip install -r requirements.txt uvicorn main:app --host 0.0.0.0 --port 8000

参数说明:--host 0.0.0.0表示监听所有网卡,这样同一个局域网里的手机也能访问,答辩时如果用手机演示比较方便;如果只在本地调试,改成127.0.0.1即可。--port 8000是后端服务端口,要和前端proxy配置保持一致。启动成功后终端会显示“Uvicorn running on http://0.0.0.0:8000”,这时先别急着开前端,先看下一节。

这里有一个很常见的坑:requirements.txt里如果有pandas、pydantic、fastapi这些包,直接用pip install有时会因为Python版本过高或过低报错。我一般建议用Python 3.9或者3.10来跑,千万不要用3.12硬刚,很多旧项目在3.12下装pydantic会很痛苦。

4.2 test_main.http怎么用:VSCode REST Client直接发请求

项目里api/test_main.http是VSCode REST Client插件的测试文件,不需要Swagger UI也可以快速验证接口。在VSCode里安装REST Client插件,打开这个文件,文件里每条请求上方会出现“Send Request”按钮,点一下就能发请求。

### 诊断测试 POST http://localhost:8000/api/diagnose Content-Type: application/json { "text": "头痛 发热 咳嗽 乏力", "top_k": 3, "min_score": 0.2 }

响应会显示在右侧面板。我一般看三样东西:HTTP状态码是不是200、返回的diseases列表长度是不是top_k以内、列表第一个疾病的score有没有超过min_score。如果返回500,多半是query.py里访问了不存在的表字段,回第3章检查report聚合逻辑;如果返回200但diseases为空,把min_score往下调到0.1再试,还不行就检查extract_symptom_ids是不是一个症状都没匹配上。

test_main.http这个文件的价值在于,它把常用的调试请求固化下来,不用每次打开Swagger点参数。你可以往里面追加更多用例,比如“只输一个症状”“输入不存在的症状”“输入空字符串”,这些边界用例在答辩时都能体现你测试过。我用这个文件还发现过一个数据问题:有的症状名带前后空格,匹配失败,这就是第5章要说的坑之一。

4.3 前端vite+tailwind:配置proxy把跨域挡住

前端是Vue3+Vite+Tailwind的项目,根目录有vite.config.js、package.json、tailwind.config.js。启动前先npm install,然后npm run dev。但在这之前,务必确认vite.config.js里配了proxy,否则浏览器直接请求后端接口会被CORS拦截。

import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true } } } })

参数说明:port是前端开发服务器端口,默认5173,如果被占用会自动往上加。proxy是关键,它把Vite开发服务器收到的“/api”开头的请求转发到后端8000端口。changeOrigin设为true表示让后端认为请求来自localhost:8000,避免跨域检查出问题。这样前端代码里fetch('/api/diagnose', ...)就不用写完整后端地址。

启动命令:

npm install npm run dev

如果node_modules已经存在,直接npm run dev即可。启动后浏览器访问http://localhost:5173,在输入框里敲“头痛 发热”,前端应该能调通后端并展示诊断结果。如果前端页面能打开但请求报404,先检查proxy有没有生效,最简单的方法是在浏览器DevTools的Network面板里看请求URL,如果显示的是localhost:5173/api/diagnose且状态码非200,就是proxy转发路径不对;如果显示localhost:8000/api/diagnose,说明前端绕过了proxy直连,检查代码里是不是写了硬编码地址。

5. 避坑与排查:这套项目最容易翻车的五个现场问题

下载资源是一回事,跑起来是另一回事。我拆这份项目时踩了不少坑,有些是数据问题,有些是环境问题,都是新手最容易卡住的地方。老规矩,每条按“现象→原因→解决”写清楚,你遇到同款直接按方抓药。

5.1 症状名读出来乱码,匹配全部失败

现象:后端日志里打印的匹配结果为0,或者前端界面显示的症状名称变成“鍙戠儹”之类的乱码;检查CSV文件时发现用记事本打开是正常的,但程序读出来不对。

原因:这些CSV文件要么是UTF-8编码,要么被Excel编辑后保存成了带BOM的UTF-8。如果load_data.py里用encoding="utf-8"读取带BOM的文件,第一列列名会带“\ufeff”,导致整个DictReader的解析结果字段名错位。另一个可能是数据文件本身是GBK编码,用UTF-8读必然乱码。

解决:先打开文件确认编码,Visual Studio Code右下角能看到当前编码。如果文件是GBK,把open的编码参数改成encoding="gbk";如果是带BOM的UTF-8,统一改成encoding="utf-8-sig"。我拿到这类资源的第一件事,就是写一段小脚本把所有csv的编码统一成UTF-8,省得后面反复踩。

5.2 输入“头痛 发热”能查到,输入“头痛、发热”就查不到

现象:用户输入用逗号、顿号、空格分隔的症状词,只有空格分隔时能命中,换成中文逗号或者顿号返回为空。

原因:extract_symptom_ids做的是子串包含匹配,理论上“头痛、发热”里也包含“头痛”和“发热”,按理说应该命中。但如果你的输入先做了分词或者清洗,把“头痛、发热”按逗号切成了“头痛”和“发热”,那没问题;如果用的是jieba分词,“头痛、发热”可能会被切成“头痛”和“发热”,问题不大。真正出问题的是query.py里用了“整句精确匹配”,只判断text in symptom_name,然后遍历symptom_list去equals比较。

解决:把包含匹配的逻辑统一改成“任何症状名出现在text中就算命中”,不要用equals。同时在前端或后端做一个简单的标点归一化,把中文逗号、顿号、分号统一替换成空格,再交给后端。

5.3 前端能打开,请求/api/diagnose返回CORS或者404

现象:前端页面正常,点击诊断后Network面板显示请求失败,报错信息是“Access to XMLHttpRequest has been blocked by CORS policy”或者404 Not Found。

原因:CORS是跨域问题,如果前端直接请求http://localhost:8000而不是走Vite代理,浏览器会拦截。404则通常是proxy没生效,或者后端没启动,或者端口不一致。

解决:先确认后端是否在8000端口运行,用浏览器直接访问http://localhost:8000/docs看Swagger页面能否打开。然后检查vite.config.js里的proxy配置,重启npm run dev(改配置必须重启)。如果后端部署在服务器上,需要在前端代码里把请求地址改成服务器域名,同时在main.py里加CORS中间件允许跨域。

5.4 “心慌”和“心悸”是两个症状,查“心慌”返回结果不如“心悸”多

现象:用户在输入框里输入“心慌”,返回的疾病列表不完整;同样症状换成“心悸”,结果明显不一样。对比发现alias_map没有生效。

原因:症状词表里标准名是“心悸”,口语词“心慌”只在symptom_list.txt里出现,但itemize.py生成的alias_map没被query.py正确使用;或者merge.py合并词表后没有重新生成别名映射。

解决:在query.py里检查extract_symptom_ids是否同时遍历了标准名和别名。如果alias_map没自动生成,手动在alias_map里补上:{"心慌": "心悸", "发烧": "发热", "拉肚子": "腹泻"}。这类词在医疗场景里特别多,我建议你把能想到的口语词都维护进去,直接影响用户体感。

5.5 pip install -r requirements.txt装完,uvicorn启动直接报错

现象:依赖装完后,运行uvicorn main:app --host 0.0.0.0 --port 8000,报ModuleNotFoundError或者ImportError,常见的是找不到pydantic或typing_extensions,也可能是pydantic版本与FastAPI不兼容。

原因:requirements.txt里如果写的是旧版本pydantic 1.x,而pip在Python 3.10以上环境里装了pydantic 2.x,FastAPI的接口定义会产生兼容性错误;或者是缺少某个间接依赖,比如python-multipart。

解决:先看报错模块名去单独补装,比如pip install python-multipart。如果卡在pydantic版本上,最简单的办法是升级FastAPI:pip install --upgrade fastapi。不要手动改项目里的pydantic模型代码,旧版本代码在pydantic 2下面跑起来成本很高。如果是Python 3.12环境,建议直接换3.10再建一次虚拟环境,这条血泪经验能帮你省掉半小时。

6. 把它改造成自己的项目:扩展知识库和自定义接口

资源能跑通只是第一步,答辩要拿高分,得让评委看到你在这个项目里加了自己的东西。这章给你两条最实用的进阶路径:一是扩展知识库,把自己的数据并进去;二是加自定义接口,体现你对架构的理解。最后附一个验证清单。

6.1 把merge.py和category.py用起来

项目里merge.py和category.py就是为扩展准备的。merge.py的作用是把多个CSV按同一个实体id横向合并,比如你从医学网站爬了一批“疾病别称”,可以merge到disease_name.csv里;category.py则负责给实体打分类标签,比如按科室或者按症状严重程度划分。如果你的目标是扩充数据量,只需要按原表结构准备新CSV,调用merge.py生成一个新的合并文件,然后替换data目录下的原文件。

# merge.py 的常见用法示意 # python merge.py --base disease.csv --extra new_disease.csv --key id import pandas as pd def merge_csv(base_path: str, extra_path: str, key: str) -> pd.DataFrame: base = pd.read_csv(base_path, encoding="utf-8-sig") extra = pd.read_csv(extra_path, encoding="utf-8-sig") merged = pd.merge(base, extra, on=key, how="left") return merged

逻辑说明:新版数据里“并发症”字段原本是空的,你补充了new_disease.csv里每个疾病的并发症列表,通过merge把两边的信息合到一起。注意merge时要确认key列名称完全一致,否则会出现大量NaN;合并顺序用how="left"保证原表所有疾病都保留,新增的数据只做补充。合并完记得重新跑一遍load_data.py,确认新数据没有被编码问题破坏。

6.2 加一个“按科室过滤”接口

我一般会再扩展一个接口,让前端可以按科室筛选诊断结果。这个功能改动小,但答辩时能讲出“业务价值”。在main.py里加一个可选参数department,在rank_diseases返回结果后再按科室过滤。

@app.post("/api/diagnose") def diagnose(body: QueryBody): matched = extract_symptom_ids(body.text, symptom_index, alias_map) result = query_engine.rank_diseases(matched, top_k=body.top_k, min_score=body.min_score) if body.department: result = [d for d in result if d.get("department") == body.department] return {"matched_symptoms": matched, "diseases": result}

这里的关键是在QueryBody里新增department字段,并且在疾病数据里提前维护好科室信息。如果没有科室字段,也可以改用“过滤掉某种药品”之类的条件,逻辑一样。这种做法说明你理解了数据模型,而不是只会调接口。

6.3 答辩前验证清单

最后给你一张我跑这类项目必过的检查清单,按顺序执行,不要跳步。

检查项预期结果
输入“头痛 发热 咳嗽”返回3个以上疾病候选,第一个是呼吸道相关疾病
输入“”或只有空格返回400或明确提示“请输入症状”
输入“心慌”能匹配到“心悸”相关疾病
前端请求/api/diagnose状态码200,无CORS报错
给merge后的数据重新load新数据能出现在诊断报告中

从那以后我每次拿到这类NL P课程设计,第一件事就是先把test_main.http跑一遍,边界用例全过一遍才敢动代码。诊断系统不同于普通博客项目,数据的一个小错位可能让结果看起来“煞有介事”却完全不靠谱,这种黑匣子式的翻车往往要到答辩现场才暴露。希望这份拆解能帮你少踩几个坑,把时间花在真正能加分的地方——扩展自己的数据、讲清楚设计思路,而不是跟编码和依赖纠缠。希望帮到你。

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

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

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

立即咨询