news 2026/6/8 19:00:10

基于Django与Neo4j搭建的中药知识图谱问答系统(含源码+部署指南)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Django与Neo4j搭建的中药知识图谱问答系统(含源码+部署指南)

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

简介:一个即装即用的中医药领域问答系统,后端用Django开发,数据层采用Neo4j图数据库存储中药、方剂、证候、药材等实体及它们之间的关联关系。支持自然语言提问,通过语义解析和图谱路径匹配生成答案。前端包含用户注册登录、首页导航、关键词检索、问答交互界面、单条知识详情页、个性化推荐列表和文献浏览页面;后端实现用户权限管理、图查询API、问句意图识别、关系路径推理及后台内容维护功能。项目结构清晰:models.py定义图映射逻辑,views.py封装核心业务,urls.py路由完整,settings.py配置就绪,附带详细README.md部署说明,所有HTML模板齐全,本地运行只需安装requirements.txt所列依赖即可启动调试。适合高校学生用于毕业设计、课程实践或中医药信息化入门项目,无需二次开发就能跑通全流程。

1. 项目概述:为什么中药问答需要知识图谱,而不是传统搜索?

你有没有试过在某个中医药网站上搜“黄芪能治气虚吗”,结果跳出二十篇标题带“黄芪”的科普文,但没有一篇直接回答“能”或“不能”,更别说解释“为什么能”——它和“气虚”之间到底存在怎样的药性-病机-功效逻辑链?这正是当前大多数中医药信息化系统的真实困境:信息堆砌有余,语义理解不足;关键词匹配热闹,因果推理缺席。而我这次做的这个系统,不是又一个静态网页集合,它是一套真正能“思考关系”的中药问答引擎——当你输入“哪几味药常配伍治疗脾虚泄泻”,后端不会去全文检索“脾虚泄泻”四个字,而是瞬间在图谱里定位到【脾虚泄泻】这个证候节点,逆向追踪它的上游病因(如脾气虚弱)、下游表现(如大便溏薄),再顺向查找与之存在【主治】【配伍】【增效】关系的药材节点(党参、白术、茯苓、甘草……),最后按临床常用度加权排序返回答案。整个过程像中医师在脑中调取经验图谱一样自然。

这套系统用 Django 搭建 Web 框架层,不是因为它“最流行”,而是因为它的 MTV(Model-Template-View)结构天然适配知识图谱项目的分层逻辑:models.py 不再只是 ORM 映射关系型表,而是定义实体类(Herb、Formula、Syndrome、Symptom)及其在 Neo4j 中的标签(Label)与关系类型(Relationship Type);views.py 里封装的不是 CRUD,而是问句解析器(基于规则+轻量级词典)、路径查询构造器(Cypher 动态生成)、答案聚合器(多跳路径去重与置信度打分);templates 目录下的每一个 HTML 文件,都对应着一个明确的知识消费场景——login.html 是权限入口,qs.html 是问答主战场,single.html 则是单点深度穿透的“知识显微镜”。它不追求炫酷前端动画,但每个页面的 DOM 结构都为语义化数据暴露做了准备,比如在 show.html 的药材详情页里,<div>MATCH (h:Herb {name: $herb_name})-[r:TREATS]->(s:Syndrome) RETURN s.name AS target, r.evidence_level AS level, r.source AS source

其中$herb_name是从第一步提取的实体,TREATS是关系类型,evidence_level是存储在关系上的证据等级(1-5 星,来自《中药药理学》文献评分)。sidefunctions.py里的build_cypher_query(intent, params)函数,就是负责把意图和参数组装成可执行的 Cypher 字符串。这里有个易错点:Neo4j 的参数化查询必须用$param形式,不能拼接字符串,否则有注入风险。我最初犯过这个错误,直接f"MATCH (h:Herb {{name: '{herb}'}})",结果用户输入"黄芪' OR '1'='1"就能绕过限制——后来全部重构为session.run(cypher, {"herb_name": herb})

提示:词典文件必须用 UTF-8-BOM 编码保存,否则 Django 读取时会出现乱码。Windows 记事本默认保存为 ANSI,务必用 VS Code 或 Notepad++ 重新编码。

3.2 图谱查询:Cypher 的艺术——如何写出高效、可读、可维护的查询语句

Neo4j 的威力,90% 在于 Cypher 查询的质量。本项目views.py中的query_knowledge_graph()函数,是整个系统的“心脏起搏器”。它不直接执行原始 Cypher,而是调用sidefunctions.pyexecute_cypher_with_retry(cypher, params, max_retries=3),内置了连接池异常重试和超时熔断(session.read_transaction超时设为 5 秒)。下面以一个典型查询为例,拆解其设计逻辑:

需求:“请列出所有含有黄芪且主治气虚的方剂”

Cypher 实现:

MATCH (h:Herb {name: $herb_name}) MATCH (f:Formula)-[r:CONTAINS]->(h) MATCH (f)-[r2:TREATS]->(s:Syndrome {name: $syndrome_name}) WITH f, COUNT(DISTINCT r) AS herb_count, COUNT(DISTINCT r2) AS syndrome_count WHERE herb_count > 0 AND syndrome_count > 0 RETURN f.name AS formula_name, f.properties AS formula_properties, COLLECT(DISTINCT s.name) AS treated_syndromes, AVG(r2.evidence_level) AS avg_evidence ORDER BY avg_evidence DESC, herb_count DESC LIMIT 10

这段查询的精妙之处在于WITH 子句的管道式处理。它没有用AND把所有条件堆在MATCH里(那样会导致笛卡尔积爆炸),而是分三步精准定位:先找黄芪节点,再找包含它的方剂,再找这些方剂主治的证候。WITH将中间结果f(方剂节点)传递给下一步,并计算两个关键指标:herb_count(该方剂含黄芪的次数,用于区分“君药”和“佐使药”)、syndrome_count(该方剂主治目标证候的数量,用于排除误匹配)。最后WHERE筛选确保两者都大于 0,COLLECT聚合所有相关证候,AVG计算证据等级均值——这比简单返回r2.evidence_level更科学,因为一个方剂可能通过多条路径(不同文献)支持同一主治结论。

实操中最大的坑是索引缺失导致全表扫描。Neo4j 默认不为属性建索引,MATCH (h:Herb {name: '黄芪'})如果没索引,就要遍历全部药材节点。解决方案是在 Neo4j Browser 中执行:

CREATE INDEX herb_name_index ON :Herb(name); CREATE INDEX syndrome_name_index ON :Syndrome(name); CREATE INDEX formula_name_index ON :Formula(name);

这三行命令必须在首次导入数据前执行,否则百万级数据下查询会从 20ms 拖慢到 2 秒以上。我在本地测试时,就因忘记建索引,导致首页轮播图加载延迟,后来用PROFILE命令分析执行计划,一眼就看到NodeByLabelScan占用 95% 时间,立刻补上索引,性能立竿见影。

3.3 前端渲染:Django 模板的深度定制,让知识“活”起来

Django 模板远不止是变量替换。show.html这个药材详情页,是知识呈现的典范。它接收context = {'herb': herb_node, 'relations': related_data},其中related_data是一个字典,键为关系类型('TREATS','CONTAINS_IN','CONTRAINDICATED_WITH'),值为对应的节点列表。模板代码如下:

<h1>{{ herb.name }} <small class="text-muted">{{ herb.pinyin }}</small></h1> <div class="row"> <div class="col-md-8"> <h4>【性味归经】</h4> <p>{{ herb.property_xingwei|default:"暂无数据" }}</p> <h4>【功效主治】</h4> <ul> {% for syndrome in relations.TREATS %} <li> <a href="{% url 'single' node_type='Syndrome' node_id=syndrome.id %}"> {{ syndrome.name }} </a> <span class="badge bg-success">{{ syndrome.evidence_level }}星</span> <small>{{ syndrome.source|truncatechars:20 }}</small> </li> {% endfor %} </ul> </div> <div class="col-md-4"> <h4>【配伍禁忌】</h4> {% if relations.CONTRAINDICATED_WITH %} {% for herb2 in relations.CONTRAINDICATED_WITH %} <div class="alert alert-danger"> <strong>⚠️ 禁忌:</strong> <a href="{% url 'single' node_type='Herb' node_id=herb2.id %}">{{ herb2.name }}</a> <br><small>{{ herb2.contraindication_reason|default:"详见《中药学》第X版" }}</small> </div> {% endfor %} {% else %} <p class="text-success">暂无已知配伍禁忌</p> {% endif %} </div> </div>

这个模板的亮点在于语义化链接与上下文感知。每个证候名称都包裹在<a>标签中,href使用 Django 的{% url %}模板标签动态生成,参数node_typenode_id确保点击后跳转到对应类型的详情页(single.html会根据node_type渲染不同模板片段)。更重要的是,{{ syndrome.evidence_level }}星这个显示,直接复用了图谱中关系节点的属性,让用户一眼判断答案的可靠性。而contraindication_reason字段,则是为未来扩展预留的——目前为空,但字段已存在,教研室老师随时可在后台补充“十八反十九畏”的详细依据。

注意:single.html必须实现类型路由分发。它通过request.GET.get('node_type')获取类型,然后用{% if node_type == 'Herb' %}等分支加载不同_detail_herb.html_detail_syndrome.html片段。这样一套模板就能服务所有实体类型,避免重复代码。

4. 部署全流程与避坑指南:从零开始跑通本地环境的实操手册

4.1 环境准备:Python、Neo4j、Django 的版本协同陷阱

部署的第一步,不是写代码,而是精确锁定版本。本项目requirements.txt明确指定:

django==4.2.7 neomodel==5.2.0 neo4j==5.14.0

为什么是这三个版本?因为neomodel是连接 Django 与 Neo4j 的关键桥梁,它的版本必须与 Neo4j 服务器和 Django 严格兼容。neomodel 5.2.0是首个完全支持 Neo4j 5.x 的稳定版,而django 4.2.7是 4.2.x 系列的最后一个安全更新版,修复了csrf_token在某些代理环境下失效的漏洞。如果你贸然升级到django 5.0neomodelStructuredNode类会因 Django 内部 API 变更而报AttributeError: 'NoneType' object has no attribute 'db'错误——这是我踩过的最深的坑,调试了整整两天才定位到版本冲突。

安装步骤必须严格按顺序:
1.安装 Neo4j Desktop(推荐,比社区版更易管理)。启动新项目,选择 Neo4j DBMS 5.14.0,创建数据库medicinal_db,设置用户名neo4j,密码medicinal123(此密码必须与settings.pyNEO4J_PASSWORD一致)。
2.创建 Python 虚拟环境python -m venv venv && source venv/bin/activate(Mac/Linux)或venv\Scripts\activate.bat(Windows)。
3.安装依赖pip install -r requirements.txt。注意:neomodel会自动安装neo4j驱动,无需单独pip install neo4j
4.配置 Django:打开settings.py,确认以下关键项:
python NEO4J_BOLT_URL = 'bolt://localhost:7687' NEO4J_USERNAME = 'neo4j' NEO4J_PASSWORD = 'medicinal123' # 其他设置保持默认

提示:Windows 用户若遇到ImportError: DLL load failed,大概率是neo4j驱动与 Python 版本不兼容。解决方案是卸载neo4j,改用pip install neo4j==5.14.0指定版本,再重装neomodel

4.2 数据初始化:从空图谱到可问答知识库的三步走

图谱不是凭空生成的,它需要结构化数据注入。本项目提供data/initial_data.json(示例数据)和manage.py的自定义命令load_medical_data。执行流程如下:

第一步:运行迁移(Migration)
python manage.py makemigrations && python manage.py migrate
这会在 Neo4j 中创建节点标签(Herb,Syndrome)和关系类型(TREATS,CONTAINS)的元数据,但不创建任何实例数据。

第二步:加载初始数据
python manage.py load_medical_data --file data/initial_data.json
这个命令在management/commands/load_medical_data.py中实现,核心逻辑是:

for item in json_data: if item['type'] == 'Herb': herb = Herb(name=item['name'], pinyin=item['pinyin']).save() # 为每个药材创建索引,避免后续查询慢 herb.__class__.nodes.get_or_create({'name': herb.name})

注意:get_or_create是 neomodel 的原子操作,它先尝试get,失败则create,确保数据幂等性。如果你重复执行此命令,不会产生重复节点。

第三步:验证数据完整性
启动 Django 开发服务器:python manage.py runserver,访问http://127.0.0.1:8000/admin/,用超级用户登录(python manage.py createsuperuser创建),在后台查看HerbSyndrome模型列表,确认数据已加载。此时,你可以直接在 Neo4j Browser 中执行MATCH (n) RETURN count(n)查看总节点数,应与initial_data.json中的条目数一致。

常见问题:load_medical_data报错Connection refused。原因通常是 Neo4j 服务未启动,或NEO4J_BOLT_URL地址错误(如写成bolt://127.0.0.1:7687而非localhost)。解决方案:在终端执行curl -v http://localhost:7474,若返回 Neo4j 的欢迎页,说明服务正常;否则检查 Neo4j Desktop 是否点击了“Start”按钮。

4.3 启动与调试:如何快速定位前端空白、后端 500、图谱无响应三大故障

故障一:前端页面空白,Network 面板显示 404
检查urls.py是否正确包含应用路由:

urlpatterns = [ path('admin/', admin.site.urls), path('', include('core.urls')), # 必须有这一行! ]

core.urls是项目主路由文件,它应该包含:

urlpatterns = [ path('', views.index, name='index'), path('qs/', views.question_answer, name='question_answer'), path('single/<str:node_type>/<int:node_id>/', views.single_detail, name='single'), ]

如果漏掉include('core.urls'),所有非/admin/的请求都会 404。

故障二:问答页面提交后返回 500 Internal Server Error
打开 Django 的DEBUG=True模式(settings.py中),错误页面会显示详细 traceback。最常见的原因是 Cypher 查询语法错误。例如,把MATCH (h:Herb {name: $name})写成MATCH (h:Herb {name: name})(少了$),或者参数名nameexecute_cypher调用时传入的{"herb_name": ...}不一致。解决方案:在views.pyquestion_answer视图中,添加日志:

import logging logger = logging.getLogger(__name__) # ... logger.debug(f"Cypher: {cypher}, Params: {params}")

然后在终端运行python manage.py runserver --noreload,观察日志输出,精准定位拼写错误。

故障三:问答返回空结果,但 Neo4j Browser 中手动查询有数据
这几乎 100% 是参数传递错误。检查execute_cypher函数中session.run(cypher, params)params字典键名,是否与 Cypher 中的$key完全一致(大小写敏感!)。例如,Cypher 里写$herb_name,但传入{"HerbName": "黄芪"},就会匹配失败。另一个可能是 Neo4j 的事务隔离级别:Django 默认开启事务,如果load_medical_data命令未提交事务,runserver的查询就看不到数据。解决方案:在load_medical_data.py的循环末尾,添加transaction.commit()(neomodel 通常自动处理,但显式调用更保险)。

5. 实战经验与扩展建议:一个毕业设计项目如何做出专业感

5.1 我踩过的五个真实坑,帮你省下至少 40 小时调试时间

  1. Neo4j 密码特殊字符引发的连接失败:我最初设的密码是medic!nal@2024,包含!@。结果NEO4J_BOLT_URL解析时,@被误认为 URL 用户名/密码分隔符,导致连接地址被截断。解决方案:对密码进行 URL 编码,medic%21nal%402024,或干脆改用不含特殊字符的密码。

  2. Django 模板中|safe过滤器的安全隐患{{ answer|safe }}会直接渲染 HTML,如果answer来自用户输入(如评论),就有 XSS 风险。本项目中answer是图谱查询结果,绝对可信,所以可用。但如果你要扩展用户投稿功能,必须改用|escape或自定义过滤器清理 HTML 标签。

  3. neomodelsave()方法不触发post_save信号:Django 的post_save信号在neomodel节点上无效,因为neomodel不继承 Django 的Model类。如果你想在药材创建后自动发送邮件通知,必须在Herb.save()后手动调用send_notification(herb),不能依赖信号。

  4. requirements.txtneomodel的版本锁死neomodel==5.2.0必须写死,不能写neomodel>=5.2.0。因为5.3.0版本引入了对pydantic的强依赖,而pydantic与 Django 的asgiref库存在 asyncio 兼容性问题,会导致runserver启动失败。

  5. static文件未收集导致 CSS/JS 404:开发时DEBUG=True,Django 自动提供static文件。但部署到生产环境(DEBUG=False)时,必须运行python manage.py collectstatic,将所有应用的static文件合并到STATIC_ROOT目录。否则login.html的样式会全部丢失。settings.pySTATICFILES_DIRS必须包含BASE_DIR / "static",且STATIC_ROOT要指向一个独立目录(如BASE_DIR / "staticfiles")。

5.2 从课程设计到科研原型:三个低成本高价值的升级方向

  1. 引入轻量级实体链接(Entity Linking):当前解析器依赖精确匹配词典,用户输“丹参酮”就找不到“丹参”。升级方案:在parse_question中加入jieba分词 + 编辑距离(Levenshtein)模糊匹配。例如,对分词结果["丹参酮"],在herb_dict.txt中搜索编辑距离 ≤2 的词条,返回["丹参"]。代码只需 10 行:from Levenshtein import distance; candidates = [h for h in herb_list if distance(h, word) <= 2]

  2. 增加图谱可视化面板:利用 Neo4j Bloom 工具(免费版足够教学使用),为single.html添加一个“关系图谱”Tab。在views.py中新增get_subgraph_data(node_id, node_type)视图,返回 JSON 格式的节点和关系数组,前端用vis.js渲染。这能让用户直观看到“黄芪”连接了多少证候、多少方剂,极大提升系统专业感。

  3. 构建问答日志分析后台:在models.py中新增QuestionLog模型,记录每次问答的question_text,intent,cypher_used,response_time,is_answered。然后在admin.py中注册,用 Django Admin 的图表插件(如django-chartjs)生成“高频提问TOP10”、“平均响应时间趋势”报表。这不仅是课程设计的加分项,更是未来申请科研项目的宝贵数据资产。

这个项目的价值,从来不在技术有多前沿,而在于它是否真实解决了中医药知识传播中的一个具体痛点。当你看到一位老中医在qs.html的输入框里敲下“更年期综合征用什么中成药”,系统瞬间返回“坤宝丸、更年安、静心口服液”,并附上每款药的【适用证型】和【禁忌人群】,那一刻,技术就完成了它的使命——它不再是代码,而是跨越代际的知识桥梁。

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

简介:一个即装即用的中医药领域问答系统,后端用Django开发,数据层采用Neo4j图数据库存储中药、方剂、证候、药材等实体及它们之间的关联关系。支持自然语言提问,通过语义解析和图谱路径匹配生成答案。前端包含用户注册登录、首页导航、关键词检索、问答交互界面、单条知识详情页、个性化推荐列表和文献浏览页面;后端实现用户权限管理、图查询API、问句意图识别、关系路径推理及后台内容维护功能。项目结构清晰:models.py定义图映射逻辑,views.py封装核心业务,urls.py路由完整,settings.py配置就绪,附带详细README.md部署说明,所有HTML模板齐全,本地运行只需安装requirements.txt所列依赖即可启动调试。适合高校学生用于毕业设计、课程实践或中医药信息化入门项目,无需二次开发就能跑通全流程。


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

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

鸣潮自动化脚本完全指南:智能后台挂机解放游戏时间

鸣潮自动化脚本完全指南&#xff1a;智能后台挂机解放游戏时间 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves 还在为鸣潮游戏中繁…

作者头像 李华
网站建设 2026/6/8 18:53:25

终极指南:如何在旧款Mac上免费升级最新macOS系统

终极指南&#xff1a;如何在旧款Mac上免费升级最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher是一个革命性的开源工具…

作者头像 李华
网站建设 2026/6/8 18:53:13

VideoFusion视频批量处理完整指南:如何轻松实现智能视频合并与优化

VideoFusion视频批量处理完整指南&#xff1a;如何轻松实现智能视频合并与优化 【免费下载链接】VideoFusion 一站式短视频拼接软件 无依赖,点击即用,自动去黑边,自动帧同步,自动调整分辨率,批量变更视频为横屏/竖屏 https://271374667.github.io/VideoFusion/ 项目地址: htt…

作者头像 李华
网站建设 2026/6/8 18:51:47

TEKLauncher:告别方舟MOD管理噩梦,3步打造完美游戏体验

TEKLauncher&#xff1a;告别方舟MOD管理噩梦&#xff0c;3步打造完美游戏体验 【免费下载链接】TEKLauncher Launcher for ARK: Survival Evolved 项目地址: https://gitcode.com/gh_mirrors/te/TEKLauncher 你是否曾经花费数小时只为让方舟的MOD正常运作&#xff1f;是…

作者头像 李华