大家好,我是Java1234_小锋老师,分享一套锋哥原创的AI智能 医学肿瘤诊断、分割以及分析系统(U-Net+大模型+AI智能问答+PyTorch+LangChain+FastAPI+Vue3+Neo4j 知识图谱)。

项目介绍
肺癌等肺部肿瘤是严重威胁人类健康的重大疾病。低剂量胸部CT筛查能够发现早期病灶,但阅片工作量大、不同医师之间主观差异明显,单纯依赖人工经验难以同时兼顾效率与一致性。围绕“发现病灶、勾画范围、给出可解释分析”这一临床辅助需求,本文设计并实现了一套AI智能医学肿瘤诊断、分割以及分析系统。
系统采用前后端分离架构。前端基于Vue3、Vite、Element Plus、Pinia与ECharts构建管理端界面,完成登录、患者档案、影像管理、分割可视化、报告阅读、知识图谱展示和个人中心等交互。后端基于FastAPI提供REST接口,使用SQLAlchemy访问MySQL数据库db_ai_tumor,并以JWT完成身份认证与角色鉴权。智能层以PyTorch实现二维U-Net,对肺部CT切片进行肿瘤区域分割,输出掩膜、叠加图以及面积、体积、最大径、病灶数和平均置信度等量化指标。分析层将分割结果映射为检索关键词,调用Neo4j知识图谱获取疾病、征象、分期、检查与治疗等关联知识,再通过LangChain对接大模型生成结构化中文报告;当大模型超时或不可用时,系统自动降级为规则化报告,保证业务闭环。
数据库方面,系统设计用户、患者、医学影像、分割模型、训练日志、分割任务、分析报告、图谱实体、图谱关系和操作日志等数据表,完成概念结构、逻辑结构与物理结构设计。知识图谱同步存储于Neo4j,用于可视化展示与上下文检索。测试表明,系统能够完成分角色登录、档案与影像管理、U-Net分割、报告生成、智能问答和后台管理等主要功能,界面日期时间显示规范,表格列宽可随浏览器自适应。
本文工作的意义在于把医学影像分割、知识图谱推理与大模型文本生成组织成可落地的本科毕业设计系统,为肺部肿瘤辅助诊断提供可演示、可复现的工程方案。需要强调的是,系统输出仅供学习与辅助参考,不能替代执业医师的诊断与治疗决策。
源码下载
链接: https://pan.baidu.com/s/1PyPiTKlsBKgZRIt9JovF7Q?pwd=1234 提取码: 1234
系统展示 








核心代码
"""Neo4j 知识图谱服务。"""
from neo4j import GraphDatabase
from app.core.config import settings
class KgService:
"""封装 Neo4j 连接、检索与同步。"""
def __init__(self):
self.driver = None
def _get_driver(self):
"""懒加载 Neo4j 驱动。"""
if self.driver is None:
self.driver = GraphDatabase.driver(
settings.NEO4J_URI,
auth=(settings.NEO4J_USER, settings.NEO4J_PASSWORD),
)
return self.driver
def close(self):
"""关闭驱动。"""
if self.driver:
self.driver.close()
self.driver = None
def run(self, cypher: str, **params):
"""执行 Cypher 并返回记录列表。"""
driver = self._get_driver()
with driver.session() as session:
result = session.run(cypher, **params)
return [dict(r) for r in result]
def search_context(self, keywords: list) -> str:
"""按关键词检索相关实体与关系,拼成大模型上下文。"""
lines = []
seen = set()
for kw in keywords:
if not kw:
continue
try:
rows = self.run(
"""
MATCH (n)
WHERE n.name CONTAINS $kw
OPTIONAL MATCH (n)-[r]-(m)
RETURN n.name AS name, labels(n)[0] AS type, n.description AS desc,
type(r) AS rel, m.name AS other, labels(m)[0] AS other_type,
m.description AS other_desc
LIMIT 20
""",
kw=kw,
)
except Exception:
rows = []
for row in rows:
key = (row.get("name"), row.get("rel"), row.get("other"))
if key in seen:
continue
seen.add(key)
if row.get("rel") and row.get("other"):
lines.append(
f"{row.get('name')}({row.get('type')}) -[{row.get('rel')}]-> "
f"{row.get('other')}({row.get('other_type')}): {row.get('other_desc') or ''}"
)
elif row.get("name"):
lines.append(f"{row.get('name')}({row.get('type')}): {row.get('desc') or ''}")
return "\\n".join(lines[:40])
def graph_data(self) -> dict:
"""返回前端可视化所需的节点与边。"""
try:
nodes_raw = self.run("MATCH (n) RETURN n.name AS name, labels(n)[0] AS type, n.description AS desc")
rels_raw = self.run(
"""
MATCH (a)-[r]->(b)
RETURN a.name AS source, b.name AS target, type(r) AS rel
"""
)
except Exception:
return {"nodes": [], "links": []}
nodes = [{"id": n["name"], "name": n["name"], "category": n["type"] or "Entity", "desc": n.get("desc")} for n in nodes_raw]
links = [{"source": r["source"], "target": r["target"], "name": r["rel"]} for r in rels_raw]
return {"nodes": nodes, "links": links}
def upsert_entity(self, name: str, entity_type: str, description: str = ""):
"""创建或更新节点。"""
label = entity_type if entity_type else "Entity"
self.run(
f"MERGE (n:{label} {{name: $name}}) SET n.description = $desc",
name=name,
desc=description or "",
)
def upsert_relation(self, source: str, target: str, rel_type: str, source_type: str, target_type: str):
"""创建关系。"""
rel = rel_type or "RELATED"
self.run(
f"MATCH (a:{source_type} {{name: $s}}), (b:{target_type} {{name: $t}}) "
f"MERGE (a)-[r:{rel}]->(b)",
s=source,
t=target,
)
def delete_entity(self, name: str):
"""删除节点及其关系。"""
self.run("MATCH (n {name: $name}) DETACH DELETE n", name=name)
kg_service = KgService()
<template>
<div class="page-card full-table">
<div class="toolbar">
<el-input v-model="query.keyword" placeholder="用户名 / 内容" clearable style="width:220px" />
<el-button type="primary" @click="load">查询</el-button>
</div>
<el-table :data="list" stripe border style="width:100%">
<el-table-column prop="id" label="编号" min-width="70" />
<el-table-column prop="username" label="用户" min-width="100" />
<el-table-column prop="module" label="模块" min-width="110" />
<el-table-column prop="action" label="动作" min-width="90" />
<el-table-column prop="content" label="内容" min-width="240" show-overflow-tooltip />
<el-table-column prop="ip" label="IP" min-width="120" />
<el-table-column prop="create_time" label="时间" min-width="170" />
</el-table>
<div style="margin-top:14px;text-align:right">
<el-pagination background layout="total, prev, pager, next" :total="total" v-model:current-page="query.page" :page-size="query.size" @current-change="load" />
</div>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { logsApi } from '../../api'
const list = ref([])
const total = ref(0)
const query = reactive({ page: 1, size: 10, keyword: '' })
async function load() {
const res = await logsApi(query)
list.value = res.data.list
total.value = res.data.total
}
onMounted(load)
</script>



