当前位置: 首页 > news >正文

第二章:动态 Prompt 管理与多科室智能问答系统

Spring AI Alibaba 医疗系统集成完整教程


🎯 学习目标

通过本教程学习,你将掌握:

  1. Spring AI Alibaba 框架的核心使用方法
  2. 动态 Prompt 管理的完整实现(支持 Nacos 和本地两种模式)
  3. 多科室智能问答系统的架构设计与实现]

📖 项目简介

业务场景

本项目是一个基于 Spring AI Alibaba 的医疗信息系统(HIS)AI 集成演示,主要实现:

  • 🏥 多科室智能问答:内科、儿科、外科各自专业的 AI 咨询
  • 📋 动态 Prompt 管理:支持热更新,无需重启即可切换提示词模板
  • 🎯 双模式切换:Nacos 配置中心 + 本地文件模式任意切换

为什么需要动态 Prompt 管理?

在医院 AI 系统中,Prompt 是模型行为的核心驱动

  • 内科问答更关注病因分析与化验指标
  • 儿科问答需避免过度医学术语,用语更加亲切
  • 外科问答重点关注手术适应症和风险评估

如果每次修改 Prompt 都要重启服务,维护成本极高。因此,动态 Prompt 管理成为生产环境的必要能力。


🏗️ 技术架构

核心技术栈

Spring Boot 3.2.0
├── Spring AI (1.0.0-M1) - AI 集成框架
├── Spring Cloud Context - 动态刷新支持
├── Nacos Client (2.4.3) - 配置中心
├── Milvus (2.3.5) - 向量数据库
└── 通义千问 API - 底层大模型

系统架构图

┌─────────────────┐
│  前端/客户端     │
└────────┬────────┘│ HTTP Request▼
┌─────────────────────────────────────┐
│      Controller Layer                │
│  ├─ DepartmentController (多科室)    │
│  ├─ RagController (RAG 问答)         │
│  └─ HisAiController (通用接口)       │
└────────┬────────────────────────────┘│▼
┌─────────────────────────────────────┐
│       Service Layer                  │
│  ├─ DepartmentAiService              │
│  ├─ SimpleRagService                 │
│  └─ RagMedicalQaService              │
└────────┬────────────────────────────┘│┌────┴────┐▼         ▼
┌─────────┐ ┌──────────────┐
│ Prompt  │ │   ChatClient │
│ Loader  │ │  (Spring AI) │
└────┬────┘ └──────┬───────┘│             │▼             ▼
┌─────────┐  ┌──────────┐
│ Nacos   │  │ 通义千问  │
│ Config  │  │   API    │
└─────────┘  └──────────┘

🚀 环境准备

1. 基础环境

# Java 17+
java -version# Maven 3.6+
mvn -version# Nacos 2.x(可选)
# 下载:https://nacos.io/zh-cn/docs/quick-start.html

2. 获取通义千问 API Key

  1. 访问 阿里云灵积模型平台
  2. 注册并创建 API Key
  3. 设置环境变量:
export DASHSCOPE_API_KEY=sk-your-api-key-here

3. 启动 Nacos(可选)

# 单机模式启动
cd nacos/bin
sh startup.sh -m standalone# 访问控制台: http://localhost:8848/nacos
# 默认账号密码: nacos/nacos

📁 项目结构详解

spring-ai-his-demo/
├── src/main/java/com/etong/ai/his/
│   ├── HisAiApplication.java          # 主启动类
│   ├── config/                        # 配置层
│   │   ├── ChatConfig.java            # ChatClient 配置
│   │   ├── LocalPromptLoader.java     # 本地 Prompt 加载器
│   │   └── PromptLoader.java          # Nacos Prompt 加载器
│   ├── controller/                    # 控制器层
│   │   ├── DepartmentController.java  # 科室问答接口
│   │   └── RagController.java         # RAG 问答接口
│   ├── service/                       # 服务层
│   │   ├── DepartmentAiService.java   # 科室 AI 服务
│   │   └── rag/
│   │       ├── SimpleRagService.java  # 简化 RAG 服务
│   │       └── RagMedicalQaService.java # 医学 RAG 服务
│   └── model/                         # 数据模型
├── src/main/resources/
│   ├── application.yml                # 主配置文件
│   ├── bootstrap.yml                  # Bootstrap 配置
│   └── prompts/                       # Prompt 模板目录
│       ├── prompt_his_internist.yaml  # 内科模板
│       ├── prompt_his_pediatrics.yaml # 儿科模板
│       └── prompt_his_surgery.yaml    # 外科模板
└── pom.xml                            # Maven 配置

💻 核心功能实现

功能一:动态 Prompt 加载器

设计思路

系统支持两种 Prompt 加载模式:

  1. Nacos 模式:从配置中心动态加载,支持热更新
  2. 本地模式:从 classpath 加载,适合开发测试
PromptLoader 核心代码
@Slf4j
@Component
public class PromptLoader {@Value("${prompt.source:nacos}")private String promptSource;@Autowiredprivate ConfigService configService;private Map<String, String> promptCache = new ConcurrentHashMap<>();@PostConstructpublic void loadPrompts() {log.info("开始加载Prompt模板配置,来源:{}", promptSource);if ("nacos".equalsIgnoreCase(promptSource)) {loadPromptsFromNacos();} else {loadPromptsFromLocal();}}private void loadPromptFromNacos(String dataId) {try {// 1. 从 Nacos 获取配置String content = configService.getConfig(dataId, group, 5000);promptCache.put(dataId, content);// 2. 添加配置监听器,实现热更新configService.addListener(dataId, group, new Listener() {@Overridepublic void receiveConfigInfo(String newConfig) {promptCache.put(dataId, newConfig);log.info("✅ Prompt配置已更新: {}", dataId);}@Overridepublic Executor getExecutor() {return null;}});} catch (NacosException e) {log.error("从Nacos加载失败,回退到本地: {}", dataId);loadPromptFromLocal(dataId);}}public String getPrompt(String dataId) {return promptCache.getOrDefault(dataId, "你是一名专业医生,请基于医学常识回答问题。{{question}}");}
}

关键点说明:

  • ✅ 使用 ConcurrentHashMap 确保线程安全
  • ✅ Nacos 监听器实现配置热更新
  • ✅ 失败降级机制:Nacos 失败自动切换本地

功能二:多科室智能问答服务

DepartmentAiService 核心实现
@Slf4j
@Service
public class DepartmentAiService {@Autowiredprivate LocalPromptLoader promptLoader;@Autowiredprivate ChatClient chatClient;/*** 根据科室提供专业问答* @param department 科室名称:internist(内科), pediatrics(儿科), surgery(外科)* @param question 用户问题* @return AI 回答*/public String askByDepartment(String department, String question) {log.info("收到{}科室的咨询问题: {}", department, question);// 1. 获取科室专属 Prompt 模板String dataId = "prompt_his_" + department + ".yaml";String promptTemplate = promptLoader.getPrompt(dataId);// 2. 替换模板变量String prompt = promptTemplate.replace("{{question}}", question);// 3. 调用 Spring AI ChatClienttry {String response = chatClient.prompt().user(prompt).call().content();log.info("✅ AI回复成功,科室: {}", department);return response;} catch (Exception e) {log.error("❌ AI调用失败", e);return "抱歉,系统暂时无法处理您的问题,请稍后再试。";}}
}

核心优势:

  • 🎯 不同科室使用不同的专业 Prompt
  • 🔄 支持动态切换模板而不重启服务
  • 🛡️ 完善的异常处理和降级机制

功能三:Prompt 模板设计

内科 Prompt 模板
name: internist_qa
version: v1.0
description: 内科医生问答模板
content: |你是一名专业的内科医生,请基于医学常识和患者症状,给出可能的诊断建议。请注意:1. 回答要专业、准确2. 给出可能的病因分析3. 建议相应的检查项目4. 必要时建议就医当前问题:{{question}}请以专业内科医生的口吻回答:
儿科 Prompt 模板
name: pediatrics_qa
version: v1.0
description: 儿科医生问答模板
content: |你是一名儿科医生,请用简明易懂的语言回答家长的问题。请注意:1. 使用温和、亲切的语气2. 避免使用过度专业的医学术语3. 给出实用的家庭护理建议4. 明确指出需要立即就医的危险信号当前问题:{{question}}请以儿科医生的口吻回答:
外科 Prompt 模板
name: surgery_qa
version: v1.0
description: 外科医生问答模板
content: |你是一名外科医生,请基于外科专业知识回答患者的问题。请注意:1. 重点关注手术治疗的可能性2. 说明手术的适应症和禁忌症3. 解释术前准备和术后护理要点4. 评估手术风险和预后当前问题:{{question}}请以外科医生的口吻回答:

模板设计原则:

  • ✅ 明确角色定位
  • ✅ 列出关注重点
  • ✅ 统一使用 {{question}} 占位符
  • ✅ 符合专业规范

功能四:RESTful API 接口设计

DepartmentController 实现
@Slf4j
@RestController
@RequestMapping("/ai/department")
@RequiredArgsConstructor
public class DepartmentController {private final DepartmentAiService departmentAiService;/*** 科室问答接口(GET 方式)*/@GetMappingpublic ResponseEntity<String> ask(@RequestParam String department, @RequestParam String question) {log.info("收到科室咨询 - 科室: {}, 问题: {}", department, question);try {String response = departmentAiService.askByDepartment(department, question);return ResponseEntity.ok(response);} catch (Exception e) {log.error("处理失败", e);return ResponseEntity.internalServerError().body("系统处理失败: " + e.getMessage());}}/*** Prompt 热更新接口*/@PostMapping("/refresh")public ResponseEntity<String> refreshPrompt(@RequestParam String department) {log.info("收到Prompt刷新请求 - 科室: {}", department);try {String response = departmentAiService.refreshPrompt(department);return ResponseEntity.ok(response);} catch (Exception e) {return ResponseEntity.internalServerError().body("刷新失败: " + e.getMessage());}}/*** 健康检查接口*/@GetMapping("/health")public ResponseEntity<String> health() {return ResponseEntity.ok("Department AI Service is running");}
}

🧪 运行与测试

1. 启动应用

# 方式一:使用 Maven
mvn spring-boot:run# 方式二:打包后运行
mvn clean package
java -jar target/spring-ai-his-demo-1.0.0-SNAPSHOT.jar

2. 测试用例

测试一:内科咨询
curl "http://localhost:8080/ai/department?department=internist&question=持续低热三天怎么办"

预期回答:

您可能有轻度感染或内分泌问题。建议:
1. 进行血常规检查,了解白细胞情况
2. 检查甲状腺功能
3. 如果伴有咳嗽、乏力,建议胸部CT
4. 建议到内科门诊就诊,明确诊断注意:持续发热超过3天应及时就医。
测试二:儿科咨询
curl "http://localhost:8080/ai/department?department=pediatrics&question=孩子晚上咳嗽厉害怎么办"

预期回答:

家长您好,孩子夜间咳嗽可能是以下原因:护理建议:
• 保持室内湿度在50-60%
• 睡前可以用温水蒸气熏一下
• 抬高枕头,帮助呼吸顺畅
• 避免接触烟雾和刺激性气味⚠️ 需要立即就医的情况:
- 呼吸急促或困难
- 伴有高热(38.5°C以上)
- 咳嗽超过一周未缓解
- 出现喘息声音建议先观察1-2天,如症状加重请及时就医。
测试三:外科咨询
curl "http://localhost:8080/ai/department?department=surgery&question=阑尾炎一定要手术吗"

预期回答:

作为外科医生,我来为您解答:手术适应症:
1. 急性阑尾炎伴腹膜刺激征
2. 阑尾脓肿形成
3. 保守治疗无效的慢性阑尾炎非手术治疗情况:
- 早期单纯性阑尾炎可尝试抗生素治疗
- 阑尾周围脓肿可先引流后择期手术手术优势:
✓ 根治性治疗,避免复发
✓ 微创腹腔镜手术创伤小
✓ 术后恢复快,一般3-5天出院建议:最终治疗方案需面诊后由外科医生评估决定。
测试四:Prompt 热更新
# 刷新配置
curl -X POST "http://localhost:8080/ai/department/refresh?department=internist"# 响应
{"success": true,"message": "Prompt配置刷新成功: prompt_his_internist.yaml"
}

📊 配置说明

application.yml 核心配置

spring:ai:openai:api-key: ${DASHSCOPE_API_KEY}base-url: https://dashscope.aliyuncs.com/compatible-modechat:options:model: qwen-turbo        # 模型选择temperature: 0.7         # 创造性(0-1)max-tokens: 1000         # 最大输出长度# Prompt 配置来源
prompt:source: local  # nacos 或 local# Nacos 配置
spring:cloud:nacos:config:enabled: trueserver-addr: localhost:8848namespace: devgroup: DEFAULT_GROUPrefresh-enabled: true      # 启用动态刷新

配置参数说明

参数说明推荐值
temperature回答的随机性0.7(专业)/ 0.9(创意)
max-tokens回答最大长度1000-2000
prompt.sourcePrompt 来源local(开发)/ nacos(生产)
refresh-enabled是否热更新true

🎓 核心知识点总结

1. Spring AI Alibaba 核心概念

ChatClient
├── prompt() - 设置提示词
├── user() - 设置用户消息
├── system() - 设置系统消息
├── call() - 同步调用
└── stream() - 流式调用

2. Prompt 管理最佳实践

DO(推荐做法):

  • 使用 YAML 格式管理 Prompt 模板
  • 区分系统提示词和用户提示词
  • 为不同场景设计专属模板
  • 使用 Nacos 实现生产环境热更新

DON’T(避免做法):

  • 不要在代码中硬编码 Prompt
  • 不要在一个模板中处理所有场景
  • 不要忽略异常处理和降级机制

3. 医疗 AI 系统设计原则

  1. 专业性:使用专业医学术语,但要分场景调整
  2. 安全性:明确声明"建议就医",避免承担诊断责任
  3. 可追溯:记录每次问答的 Prompt 版本和模型响应
  4. 可监控:集成日志和指标监控
  5. 可降级:AI 服务不可用时的兜底方案

🚀 生产级扩展建议

1. 添加缓存层

@Cacheable(value = "aiResponses", key = "#department + '_' + #question")
public String askByDepartment(String department, String question) {// ... 原有逻辑
}

2. 添加限流保护

@RateLimiter(name = "aiService", fallbackMethod = "askFallback")
public String askByDepartment(String department, String question) {// ... 原有逻辑
}

3. 添加可观测性

@Timed(value = "ai.department.ask", description = "科室问答耗时")
@Counted(value = "ai.department.requests", description = "科室问答请求数")
public String askByDepartment(String department, String question) {// ... 原有逻辑
}

4. 添加审计日志

@Aspect
@Component
public class AuditAspect {@Around("@annotation(Auditable)")public Object audit(ProceedingJoinPoint jp) throws Throwable {// 记录请求Object result = jp.proceed();// 记录响应return result;}
}

🔍 常见问题排查

Q1: Nacos 连接失败

症状: 启动时报 NacosException: Unable to connect

解决:

# 检查 Nacos 是否启动
curl http://localhost:8848/nacos# 修改配置切换到本地模式
prompt:source: local

Q2: API Key 无效

症状: 请求返回 401 Unauthorized

解决:

# 检查环境变量
echo $DASHSCOPE_API_KEY# 重新设置
export DASHSCOPE_API_KEY=sk-your-new-key

Q3: Prompt 模板不生效

症状: 修改了 YAML 但回答没变化

解决:

# 手动刷新配置
curl -X POST "http://localhost:8080/ai/department/refresh?department=internist"# 或重启应用

✅ 复习要点

  • 理解 Spring AI ChatClient 的基本用法
  • 掌握 Prompt 模板的设计原则
  • 理解 Nacos 配置监听器的工作机制
  • 掌握多科室问答的实现思路
  • 了解医疗 AI 系统的安全考虑
  • 掌握配置热更新的实现方式

🎯 实践练习

练习一:新增妇产科问答

  1. 创建 prompt_his_obstetrics.yaml 模板
  2. 在 PromptLoader 中添加加载逻辑
  3. 测试妇产科专业问答效果

练习二:添加多轮对话支持

  1. 使用 Spring Session 存储会话历史
  2. 在 Prompt 中注入上下文信息
  3. 实现连续问答能力

练习三:集成 Prometheus 监控

  1. 添加 Micrometer 依赖
  2. 暴露 AI 服务的关键指标
  3. 使用 Grafana 可视化

📚 参考资料

  • Spring AI 官方文档
  • Spring AI Alibaba GitHub
  • 通义千问 API 文档
  • Nacos 官方文档

👨‍💻 关于作者

Mr.Krab 🦀

  • GitHub: 蟹老板仓库
  • Email: pqjrkwem@gmail.com

📄 许可证

本项目仅供学习和演示使用,请勿用于商业用途。


感谢阅读!如有问题欢迎提 Issue 或 PR!

http://www.dtcms.com/a/461044.html

相关文章:

  • 【项目实战 Day12】springboot + vue 苍穹外卖系统(Apache POI + 工作台模块 + Excel表格导出 完结)
  • 微信小程序-8-电影数据布局案例和对话框及本地存储的应用
  • SD:Ubuntu 系统 stable diffusion Web UI - 安装更多插件
  • 什么是负载均衡?
  • 前端框架学习指南:提升开发效率
  • Avast Cleanup安卓版(手机清理优化) 修改版
  • c++多线程(6)------ 条件变量
  • 旅游网站建设与网页设计大连做网站报价
  • 网站排名英文服装加工厂网站建设方案计划书
  • 专题:2025中国人工智能医疗健康研究报告(智慧医院+医保+器械+AI)深度洞察|附130+份报告PDF、8个数据仪表盘汇总下载
  • 【学习记录】vscode+ros2+cpp调试
  • 性价比高的热冷分离真空共晶炉企业
  • 嵌入式分享#41:RK3576改UART波特率【精简版】
  • pc端pdf预览
  • 网站建设衤金手指花总十五wordpress电商主题数据
  • 【STM32项目开源】基于STM32的智能天然气火灾监控
  • Git 补丁完整指南:创建、发送和应用补丁详解
  • python中f-string详解
  • C++网络编程(十三)epoll如何设置边缘模式
  • 一流的高端企业网站网站建设与维护是什么意思
  • 上海做响应式网站的公司做后台系统的网站
  • 服务端与客户端的简单链接
  • Ubuntu24.04系统安装图形化桌面并使用xrdp远程桌面
  • 无人机通信链路技术要点与难点
  • 计算机二级45天通关秘籍:高效备考策略与编程语言选择指南​
  • 测试基础01
  • 东莞住建局官网网站宁波网站建站
  • 网上的彩票网站是怎么做的网站建设基本问题
  • 微信小程序执行onPullDownRefresh 之后 下拉刷新后刷新标志不消失
  • 如何在网站后台备份数据库表台州智能模板建站