今年毕设季,我连续被好几个同学问同一个题目:基于 LLM 的微信小程序 AI 医疗问诊平台,技术栈还特别统一——RAG + Spring AI + Neo4j 知识图谱 + SpringBoot + Vue3。有人问我这题能不能做,有人问我 RAG 和知识图谱到底怎么配合,还有人直接卡在第一步:Spring AI 到底怎么调通。
先说结论:这个项目能做,但它的难点不在“接入大模型”,而在“让医疗问答变得可靠”。你给用户一个聊天框很容易,但用户问“头痛、发烧、咽喉痛是什么病”时,模型不能靠感觉答,它得能从知识库里找到依据,能给出科室建议,能规避“自己判断病情”的风险。能做到这一层,你才真正理解了 RAG 和知识图谱为什么要同时出现。
这篇文章我会按照自己设计这个项目时的思路,把整体架构、知识库构建、RAG 与 Neo4j 的协作机制、小程序端注意事项、常见坑点和答辩准备路径完整拆一遍。文章里的代码片段是通用实现结构,不是某个完整开源项目的搬运,落地时要结合你的 Spring Boot 和 Spring AI 版本调整。
1. 这个项目的核心难点不在 AI,而在医疗问答的可靠性
1.1 为什么普通聊天机器人方案不适合医疗场景
如果只是做一个“能聊天”的问诊助手,方案太简单了:把大模型 API 接进来,小程序端发消息,后端转发,返回流式文本。半天就能跑通。但这只能算 demo,不能算医疗问诊平台。
医疗场景有三层要求是普通聊天机器人扛不住的:
第一,回答必须有依据。用户问“高血压日常要注意什么”,模型如果凭训练数据答,可能抓到过时信息,甚至把养生偏方当医学建议。RAG 的逻辑是先从医学知识库里检索出相关内容,再让模型基于这些内容生成答案。模型不自由发挥,回答有出处,这是医疗平台的底线。
第二,实体关系必须清晰。医疗知识不是孤立的文档,而是大量实体之间的网络关系:疾病对应哪些症状,需要做哪些检查,常用药物是什么,应该挂哪个科室。这些关系用向量检索很吃力,用知识图谱却很自然。
第三,风险边界必须可控。问诊平台不是医生,不能给确定性诊断。系统必须能识别自己不适合回答的问题,并引导用户去医院就诊。这类“拒答”和“安全提示”策略,普通 chat 接口不会主动给你实现。
所以这个题目的真正价值,不是让模型学会看病,而是设计一套“先检索、再组织、最后生成”的知识处理流程。这也是答辩时最值得展开的地方。
1.2 这个项目里 RAG 和知识图谱分别扮演什么角色
很多人把 RAG 和知识图谱搞成二选一,其实在这个系统里它们负责的是两种完全不同的能力。
RAG 处理的是非结构化知识。比如你好不容易找到的医学教材、临床指南、疾病科普文档,这些是长篇文字,适合切块后做向量化,用语义相似度做召回。用户问“饭后血糖多少算正常”,RAG 能把相关段落找出来。
Neo4j 知识图谱处理的是结构化知识。症状、疾病、检查、药物、科室是节点,它们之间的“属于”“建议检查”“常用药物”“就诊科室”是关系。用户问“持续干咳伴胸痛应该挂什么科”,系统先在图谱里定位症状节点,沿着关系找到可能疾病,再映射到科室。这个过程是精确的、可解释的,不是语义模糊的向量匹配。
理想配合方式是:RAG 负责提供大段解释性知识,知识图谱负责提供精确的实体追溯和路径解释;两者结果一起交给 LLM 去组织成用户能读懂的答案。
一句话主判断:这个项目拼的不是模型聪明不聪明,而是知识组织得好不好、检索策略设计得对不对。
2. 总体架构:一条数据流把六个技术栈串起来
2.1 先看懂请求从微信小程序到模型的完整链路
整个系统可以看作一条生产线:
- 用户在微信小程序里输入问题。
- 小程序把问题通过 HTTP 请求发给 Spring Boot 后端。
- Spring Boot 使用 Spring AI 的 ChatClient 与 LLM 建立对话,但在此之前,先做增强检索。
- RAG 模块把用户问题转为向量,从向量数据库里检索相关医疗文档片段。
- Neo4j 查询模块把问题里的症状实体提取出来,在图谱中查询关联疾病、科室、检查项目。
- 把向量检索结果 + 图谱查询结果 + 用户问题拼装成 Prompt,发送给 LLM。
- LLM 生成答案,后端以流式方式返回给小程序,逐字展示。
这套链路里,Spring AI 的核心价值不是“调用模型”这一个动作,而是把模型调用、Prompt 模板、向量检索、结构化数据查询统一编排在一起。如果你只是在代码里硬编码 HTTP 调用大模型 API,那 Spring AI 就没有体现出意义。
2.2 Spring AI 在这套架构里到底负责什么
先别急着写代码,你要把 Spring AI 的角色理解成“AI 应用开发框架”,不是“大模型客户端”。
它主要负责三件事:
- 统一的模型接入层:封装 OpenAI、通义千问、DeepSeek 等模型,通过一个 ChatModel 接口调用,切换模型时不用改业务代码。
- Prompt 与上下文管理:支持模板化 Prompt、Message 组装、历史会话记录,不用自己拼字符串。
- RAG 组件:提供向量存储抽象、文档读取器、切分器、检索增强链等基础能力。
你说的 Spring AI 2.0,需要确认一下具体版本状态。按当前公开情况,Spring AI 1.0 已经发布,2.0 处于迭代期。如果标题写的是 2.0,建议先查一下对应里程碑版本的 Maven 坐标和 API 差异,不要照抄 1.0 教程,避免版本不兼容。
2.3 三层架构的职责边界
可以按三层来拆:
| 层次 | 技术组件 | 主要职责 |
|---|---|---|
| 前端展示层 | 微信小程序、Vue3 管理后台 | 问诊对话、历史记录、健康档案展示、后台知识库管理 |
| 后端服务层 | Spring Boot、Spring AI | 用户接口、会话管理、RAG 编排、图谱查询、模型调用、权限控制 |
| 数据与知识层 | MySQL、Neo4j、向量数据库 | 用户数据、对话记录、知识图谱、医疗文档向量 |
SpringBoot 4 和 Vue3 都代表了技术栈的“新”,但你要注意:SpringBoot 4 刚发布不久,生态适配还不算完全稳定,尤其是一些第三方 starter 可能还没有跟上。选型没有问题,但毕设演示阶段要有备选方案,至少保证 Spring AI 在目标 Spring Boot 版本上能正常初始化。这个问题放到第 5 部分细说。
3. 医疗知识库构建:这是项目质量的上限所在
3.1 数据从哪来,怎么清洗
这个项目避不开一个问题:医疗数据怎么获取。我用合法、稳妥的路径给你排序。
首选是公开的医疗指南和科普文档。比如国家卫健委发布的临床路径、诊疗指南,三甲医院官网的科普文章,权威医学媒体公开的疾病百科内容。这些内容可以人工整理成文本,数量不需要多,500 到 1000 条高质量条目就足够支撑毕设演示。
其次是结构化医学公开数据集。部分高校和科研机构开放了中文医学实体识别、关系抽取数据集,可以用来构建图谱。但使用前要确认授权协议,只用于学习研究。
不建议直接爬取商业问诊平台的内容,有版权和合规风险。毕设答辩也不会因为数据来源“全”而给你加分,反而会因为来源不透明被追问。
清洗时要注意:去除页眉页脚、广告、联系方式;统一医学术语表达;保留来源链接和更新时间;把专有名词(如“高血压”)统一成标准说法。数据质量直接决定后续向量检索的效果。
3.2 RAG 切块策略:不是切得越短越好
RAG 的效果,一大半取决于切块策略。医疗文档尤其讲究上下文完整性。
我一般建议这样处理:
- 先按章节标题切大块,保证同一个主题的内容不被打散。
- 再按段落切块,每一块控制在 300 到 800 个中文字符之间。
- 切块时保留文档标题、来源、章节路径作为元数据。
- 对包含症状列表、检查指标、药物用量的段落,单独切出来,不要混在长段落里。
为什么不建议切得太短?比如切成 100 字,检索时可能召回语义相近但上下文断裂的碎片,模型拼不出完整逻辑。为什么不建议切得太长?因为向量检索的相似度计算会被大量无关信息稀释,而且超出模型上下文窗口时还得做二次截断。
注意:切块没有一劳永逸的参数。切完之后用 20 个真实医疗问题做一轮召回测试,看检索结果里前 5 条是否包含正确答案,再决定调大还是调小。
3.3 Neo4j 知识图谱怎么设计实体和关系
知识图谱不是把文档塞进 Neo4j 就完了,你需要先设计本体(Ontology)。对于医疗问诊场景,我建议先从这五类实体和五类关系起步:
| 实体类型 | 示例 | 关系类型 | 示例 |
|---|---|---|---|
| 疾病 | 高血压、2型糖尿病 | 疾病 -> 症状 | 高血压 -> 头晕 |
| 症状 | 头痛、心悸、多饮 | 疾病 -> 检查 | 高血压 -> 血压测量 |
| 检查 | 血常规、糖化血红蛋白 | 疾病 -> 药物 | 2型糖尿病 -> 二甲双胍 |
| 药物 | 硝苯地平、阿卡波糖 | 疾病 -> 科室 | 高血压 -> 心血管内科 |
| 科室 | 心血管内科、内分泌科 | 症状 -> 可能疾病 | 心悸 -> 心律失常 |
先建这么多就够支撑问诊的常见路径。Neo4j 的 Cypher 查询核心是:
// 根据症状查可能疾病和对应科室 MATCH (s:Symptom {name: '干咳'})-[r:POSSIBLE_DISEASE]->(d:Disease) MATCH (d)-[:DEPARTMENT]->(dept:Department) RETURN d.name, dept.name, r.weight关键在于,图谱里的关系必须有来源或权重。比如“干咳”可能是感冒症状,也可能是支气管炎症状。你在关系上设置一个 weight 字段,表示这种关联的常见程度或来源权威度,检索时要排序,不能让模型把所有可能性都当等概率输出。
3.4 文档型知识和结构化知识如何并存
最常见的错误是:把文档切块后向量化,又把这些块复制到 Neo4j 里,结果两边数据冗余,查询结果互相矛盾。
更合理的方式是:
- 文档型知识进向量数据库,用于生成答案时的参考内容。
- 结构化关系进 Neo4j,用于精确实体定位和路径解释。
- 两者通过“疾病名称”或“标准实体名”产生交集。
举个例子,用户问“高血压不能吃哪些食物”。RAG 可能检索到一段“高血压饮食建议”的文档,含有低盐、低脂、多果蔬等具体描述;图谱查询则找到“高血压”这个疾病节点,顺着“推荐饮食”和“避免饮食”关系返回一些结构化条目。两条路径的结果都可以塞进 Prompt,让模型综合回答。一种是内容补充,一种是逻辑约束,缺一不可。
4. 从 Demo 到可演示项目:四个关键技术实现点
4.1 Spring AI 的调用链怎么搭
在 Spring Boot 项目里引入 Spring AI 之后,通常会有这样一条调用链:
- 用户消息进入 Controller。
- Service 层先调用 RAG API 做向量检索。
- Service 层再调用 Neo4jService 做图谱查询。
- 把检索结果组装成 Prompt。
- 调用 ChatModel 流式生成回答。
- 通过 SSE 或 WebSocket 推送给小程序。
代码结构可以参考:
@Service public class ConsultationService { private final ChatModel chatModel; private final VectorStore vectorStore; private final Neo4jKnowledgeService knowledgeService; public Flux<String> ask(String userId, String question) { List<Document> docs = vectorStore.similaritySearch(question); GraphQueryResult graphResult = knowledgeService.queryBySymptom(question); Prompt prompt = buildMedicalPrompt(question, docs, graphResult); return chatModel.stream(prompt.getContents()); } }这只是显示调用顺序,具体 API 名称和参数在不同版本里有差异。引用方式以 Spring AI 官方文档和你引入的版本为准。
4.2 RAG 与图谱的检索策略:向量先召回,图谱再约束
这里有一个很值得写进论文的策略:不把所有知识一次性塞给模型,而是分阶段召回。
比如用户问题:“我最近经常头痛,有时候还感到恶心,应该挂什么科?”
第一阶段,从问题里抽取症状实体“头痛”“恶心”。可以用 LLM 做轻量实体抽取,也可以用规则匹配。毕设场景建议先用规则匹配 + 预置症状词表,简单可控;如果效果不够,再引入 LLM 抽取。
第二阶段,把症状实体带去 Neo4j 查询。找到关联疾病后,按 weight 排序,再映射到科室。
第三阶段,把疾病名和科室名作为查询词,去向量数据库检索相关科普文档,获得更完整的背景知识。
第四阶段,组合这三个结果,送入模型。
这个策略是“先精确定位,再补充扩展”。如果反过来,先做向量召回,模型可能被大段相似但不精确的文献带偏。
4.3 微信小程序端的技术选型和展示细节
项目标题里同时写“微信小程序”和“Vue3”,这里有一个常见混淆需要澄清:微信小程序原生开发用的是 WXML/WXSS/JS,不是 Vue3 语法。如果你写 Vue3,通常指的是用 uni-app 或 Taro 这类跨端框架来生成小程序,或者 Vue3 用于后台管理端。
我建议把定位拆清楚:
- 用户问诊端用微信小程序,不管是用原生还是 uni-app,都要做聊天式界面。
- 管理后台用 Vue3 + Element Plus,统一管理用户、对话日志、知识库文档、图谱数据。
小程序端最影响体验的不是 UI 动画,而是流式响应。你发一句话等 10 秒才看到完整回答,用户会觉得系统很慢。要优先实现字级或句级流式展示,让用户看到模型“正在生成”。这个功能后端用 SSE 或 WebSocket,小程序端用wx.request的 enableChunked 能力或 WebSocket 接收分片,逐段追加到消息气泡里。
建议用微信开发者工具的真机预览模式调试流式效果。开发工具模拟器和真机在网络行为上经常不一致,不要等到答辩前一天才发现。
4.4 会话上下文和用户意图识别按最小可用设计
第一次做这个项目,别急着把上下文管理做得很复杂。两个原则足够:
第一,只保留最近 5 到 8 轮对话。把历史摘要和当前问题一起拼到 Prompt 里。对话超过窗口就丢弃最早的消息,避免 Token 过长。
第二,区分问诊意图和非问诊意图。比如用户发“你好”“谢谢”“你是谁”,不需要走 RAG 和知识图谱,直接由模型回答即可。可以通过关键词或轻量分类识别。如果用户问“吃什么能治癌症”这类高风险问题,要触发安全回复模板,提示“不能替代医生诊断,请及时就医”。
这些边界规则看着不复杂,但它们是医疗问诊平台区别于普通聊天机器人的关键所在。
5. 最容易翻车的四个场景和排查路径
5.1 检索结果为空或相关性差
现象:用户问题很明确,但 RAG 返回的文档片段完全不相关,或者 Neo4j 查询结果为空。
排查顺序:
- 先检查文本是否进入向量库。常见问题是已经执行了切块和向量化,但没有调用 store 保存,或者向量数据库服务没启动。
- 检查问题与文档片段的语言一致性。用户用口语,文档用书面语,可能导致语义相似度偏低。
- 检查知识图谱里的实体名称和用户问题里的症状名是否一致。比如用户说“头晕”,图谱里只有“眩晕”,匹配不到。
- 检查 Neo4j 服务地址、账号密码、数据库名称是否对得上。开发环境最容易忽略 Neo4j 默认数据库是
neo4j,如果你新建了medical.db又没切换,查不到任何数据。
对于匹配不到的问题,最实用的解决办法是维护一个“同义词表”或“别名表”,把口语化表达映射到标准术语。比如“脑袋疼”映射到“头痛”。
5.2 模型幻觉:回答与检索内容不一致
现象:检索结果明明说“高血压患者每日盐摄入量应低于 5 克”,模型生成时却写成“可以不限盐”。
排查路径:
- 检查 Prompt 是否明确约束了“只能基于给定知识库内容回答”。Spring AI 的 System Prompt 可以写成固定模板,去掉模型自由发挥的空间。
- 检查是不是检索到的内容太多了,模型抓不住重点。适当减少 Top-K 返回数量,比如从 10 条降到 5 条。
- 检查模型温度参数。医疗场景建议把 temperature 调低到 0.2 以下,或直接设置为 0。温度越高,回答随机性越强,不适合医疗场景。
- 检查是否有多个矛盾来源同时被召回。比如一篇文档说“高血压标准是 140/90”,另一篇说“新版标准是 130/80”,模型可能综合出错误结论。这需要在数据清洗时统一标准,或在 Prompt 里要求“如果存在矛盾,提示信息需要进一步确认”。
5.3 版本不兼容:Spring AI、Spring Boot、Neo4j 驱动互相打架
这个项目最容易卡住的地方不是业务逻辑,而是启动阶段报错。
排查顺序:
- 先确认 Spring Boot 版本和 Spring AI 版本的兼容关系。Spring AI 官方文档通常会在 Release Notes 里标明支持的 Spring Boot 版本范围。
- Neo4j 驱动版本要和 Neo4j 服务端版本匹配。比如 Neo4j 4.x 对应旧驱动,Neo4j 5.x 需要更新驱动,这个不能混。
- 向量数据库组件选型要提前确认。Spring AI 提供了多种向量存储适配器,本地开发建议先用轻量级实现,比如基于内存的 SimpleVectorStore,避免一开始就引入复杂中间件。
- 如果使用 Docker 安装 Neo4j,要确认容器内部端口和宿主机端口的映射关系,连接失败常常是端口没映射对。
# 常见 Docker 启动 Neo4j 的命令结构 docker run -d \ --name neo4j-medical \ -p 7474:7474 -p 7687:7687 \ -e NEO4J_AUTH=neo4j/yourpassword \ neo4j:5.26不同 Neo4j 版本的镜像 tag 不一样,启动前用docker images和docker ps检查容器状态。
5.4 小程序端白屏、报错、无法访问接口
排查顺序:
- 先看微信开发者工具的 Network 面板,确认请求是否发出、响应码是多少。注意开发环境中关闭“不校验合法域名”前要确认你只是本地调试。
- 检查本地开发服务器是否监听在小程序可访问的地址上。不能只监听 127.0.0.1,真机调试时手机访问不到本机 localhost。
- 检查小程序端是不是用了未编译的 npm 包。小程序不支持直接 require 所有 Node.js 包,需要先构建 npm。
- 检查页面栈。微信小程序页面跳转层级过深时,用
wx.navigateTo会失效,需要改用wx.redirectTo或wx.reLaunch,这是关键词里出现pageindex和“分包异步化”时最常关联的问题。问诊对话页要考虑分包加载,避免主包体积超限。
6. 从毕设题目到能演示的项目,还需要走完哪几段路
6.1 第一优先级:先把最小闭环跑通
不要一上来就想着做完整平台。推荐顺序是:
- 后端先写一个最简单接口,直接调用 ChatModel 返回一句话。
- 小程序端展示这句话,确认网络通路正常。
- 引入 Spring AI 的向量能力,手动放入 20 篇医疗文档,做一次检索测试。
- 在 Neo4j 里建 50 个疾病节点和 200 条关系,手工写 Cypher 查询,确认能查到科室。
- 把检索结果拼进 Prompt,让模型说出答案和参考来源。
- 最后再做流式、历史记录、后台管理。
每完成一步都保留一个可演示的截图或录屏,写论文和答辩 PPT 时都需要这些过程材料。
6.2 第二优先级:在“玩具”和“毕设系统”之间补齐差距
演示时,很多人一眼就能看出项目是纯 demo 还是完整系统。差距通常体现在:
- 用户登录和会话隔离。至少要有 openId 绑定、会话表、历史记录表。
- 知识库管理页面。允许管理员上传文档进行切割、向量化,并查看状态。
- 对话日志。记录用户的每次提问、系统回答、检索命中的文档 ID。这个记录既是调试工具,也是论文里的测试数据来源。
- 错误兜底。模型调用失败时,返回固定提示,而不是空白或报错。
6.3 答辩时最可能被追问的几个问题
提前准备好这几个问题的答案,比背稿更有用:
- 为什么用 RAG 而不是微调模型?答:医疗知识更新快,RAG 可以随时更新知识库而无需重新训练模型;且 RAG 能附来源,可解释性更强。
- 为什么还需要知识图谱?答:很多问诊问题本质上是实体关系查询,图谱路径清晰可解释,能纠正纯语义检索的模糊性。
- 如果用户问的问题在知识库里查不到怎么办?答:先触发拒答模板,告知无法提供确定性建议,引导前往正规医疗渠道。
- 如何评估回答质量?答:建立了测试集,包含 50 个常见医疗问题,分别评估检索召回率和回答正确率。虽然不够完整,但能证明系统具备效果验证能力。
6.4 对毕设项目使用 LLM 的几条实在建议
第一,模型选择没必要追求最大最强。用国内大模型 API 或开源模型部署都能完成项目,优先选文档完善、接入方便的。毕设评分看的是系统设计、工程完整度和对问题理解的深度,不是模型推理分数。
第二,本地部署大模型不是必选项。如果你的机器不支持,就老老实实走 API 方案。需要展示“本地化”能力时,可以说明模型层被 Spring AI 抽象,后续可无缝切换到本地部署模型。
第三,所有外部服务和数据集,都要在论文里注明来源。既体现学术规范,也避免答辩评审追问来源时语焉不详。
收尾:先解决知识组织,再谈模型强弱
这个题目很容易让人误以为“AI 医疗问诊 = 接入大模型 + 聊天气泡”,但真正把系统做完后你会发现,大模型只是最后一步的生成器,前面还有知识清洗、切块、向量化、图谱建模、检索策略、安全兜底、流式链路。项目质量的高下,不取决于模型多聪明,而取决于你让模型站在多少真实、可靠、有结构的医学知识之上。
再提醒一句:开工时别急着调参数、写炫酷界面。先把一条最简单的问题问答链路完整打通,再逐步叠加 RAG 和图谱。链路畅通了,后面所有优化才有意义。