随着大语言模型(LLM)技术的飞速发展,医疗健康领域正迎来智能化变革的新机遇。本文将以 DeepSeek 大模型和 Spring AI 框架为核心,从零到一搭建一个具备多轮对话、症状预分析、用药提醒和健康知识问答的 AI 家庭医生 应用。文章将深入技术细节,提供完整可运行的代码,并分享生产级落地的关键考量。
DeepSeek 作为国产高性能开源大模型,在医疗常识问答、逻辑推理和中文语义理解上表现优异,且支持 128K 上下文窗口,非常适合承载复杂的健康咨询场景。其 API 兼容 OpenAI 格式,调用便捷,成本可控。
Spring AI 是 Spring 生态下的 AI 应用开发框架,它抽象了与大模型的交互细节,提供了统一的 ChatClient、PromptTemplate、StreamingResponse 等组件,并内置了向量存储(RAG)、函数调用(Function Calling)等高级特性,能极大降低 Java 开发者接入 LLM 的门槛。
二者结合,可以快速构建一个健壮、可扩展、云原生友好的智能医疗应用。
┌─────────────────────────────────────────────────────────┐
│ 前端(Web/小程序) │
└─────────────────┬───────────────────────────────────────┘
│ RESTful API / SSE
┌─────────────────▼───────────────────────────────────────┐
│ Spring Boot 3.x + Spring AI │
│ ┌───────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ 对话管理模块 │ │ 症状分析引擎 │ │ 用药提醒服务 │ │
│ └───────────────┘ └──────────────┘ └─────────────┘ │
│ ┌───────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ 上下文记忆仓储 │ │ RAG 知识库 │ │ 日志/监控 │ │
│ └───────────────┘ └──────────────┘ └─────────────┘ │
└─────────────────┬───────────────────────────────────────┘
│ DeepSeek API (HTTP/SSE)
┌─────────────────▼───────────────────────────────────────┐
│ DeepSeek 大模型(deepseek-chat) │
└─────────────────────────────────────────────────────────┘使用 Spring Initializr 或 IDEA 创建项目,选择 JDK 17+,依赖如下:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI 起步依赖(兼容 DeepSeek) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
<!-- 用于 SSE 流式输出 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 缓存和状态管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>在 application.yml 中配置 DeepSeek API 地址和密钥(注意 DeepSeek 兼容 OpenAI 接口):
spring:
ai:
openai:
base-url: https://api.deepseek.com/v1
api-key: ${DEEPSEEK_API_KEY} # 环境变量注入
chat:
options:
model: deepseek-chat
temperature: 0.7
max-tokens: 2048安全提示:切勿将 API Key 硬编码,建议使用环境变量或配置中心。
Spring AI 的 ChatClient 是核心交互对象,我们通过配置类创建具备函数调用和记忆能力的 Bean。
@Configuration
public class AiConfig {
@Bean
public ChatClient chatClient(OpenAiChatModel chatModel) {
return ChatClient.builder(chatModel)
// 系统提示词,定义角色为家庭医生
.systemPrompt("""
你是一位专业的AI家庭医生助手,拥有丰富的医学常识。请遵循以下原则:
1. 仅提供健康建议和科普信息,不代替线下诊断。
2. 对症状进行逻辑分析,给出可能的病因及建议就医科室。
3. 回答要温和、清晰,使用通俗语言。
4. 当用户描述紧急症状(如胸痛、大出血)时,立即提醒拨打120。
""")
// 注册自定义函数(用药提醒)
.function("setMedicineReminder", new MedicineReminderFunction())
.build();
}
}我们设计 HealthConsultationService,负责维护会话上下文(使用内存缓存,生产可替换为 Redis)并调用 ChatClient。
@Service
public class HealthConsultationService {
private final ChatClient chatClient;
private final Cache<String, List<Message>> sessionCache;
public HealthConsultationService(ChatClient chatClient) {
this.chatClient = chatClient;
this.sessionCache = CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES)
.maximumSize(1000)
.build();
}
/**
* 同步对话(非流式)
*/
public String chat(String sessionId, String userMessage) {
// 获取历史消息
List<Message> history = sessionCache.get(sessionId, ArrayList::new);
// 构建用户消息
UserMessage userMsg = new UserMessage(userMessage);
history.add(userMsg);
// 调用模型
ChatResponse response = chatClient.prompt()
.messages(history)
.call()
.chatResponse();
// 保存助手回复
AssistantMessage assistantMsg = response.getResult().getOutput();
history.add(assistantMsg);
sessionCache.put(sessionId, history);
return assistantMsg.getText();
}
/**
* 流式对话(SSE),返回 Flux<String>
*/
public Flux<String> streamChat(String sessionId, String userMessage) {
List<Message> history = sessionCache.get(sessionId, ArrayList::new);
UserMessage userMsg = new UserMessage(userMessage);
history.add(userMsg);
// 流式调用
Flux<ChatResponse> flux = chatClient.prompt()
.messages(history)
.stream()
.chatResponse();
// 收集完整响应后保存至历史
return flux.doOnComplete(() -> {
// 注意:流式响应无法直接获取完整消息,需另存拼接结果
// 可采用 StringBuilder 在 subscriber 中拼接,这里简化处理
}).map(response -> {
if (response.getResult() != null) {
return response.getResult().getOutput().getText();
}
return "";
});
}
}改进点:流式场景下,完整回复需要在外层拼接,这里为简化仅展示逐字输出。生产环境建议使用
Flux的reduce或collectList获取完整内容再存历史。
Spring AI 支持将 Java 方法注册为可被模型调用的工具。当用户说“我每天需要吃两次阿莫西林,每次一粒”时,模型可自动调用 setMedicineReminder 函数。
@Component
@Description("设置用药提醒,参数:药品名称、用法、间隔小时数")
public class MedicineReminderFunction implements Function<MedicineReminderFunction.Request, MedicineReminderFunction.Response> {
@Override
public Response apply(Request request) {
// 实际可存入数据库或发送通知
log.info("设置用药提醒:药品={}, 用法={}, 间隔={}小时",
request.medicineName, request.usage, request.intervalHours);
// 模拟生成提醒ID
return new Response("提醒已设置,ID: " + UUID.randomUUID().toString());
}
public record Request(String medicineName, String usage, Integer intervalHours) {}
public record Response(String result) {}
}在配置中通过 .function("setMedicineReminder", new MedicineReminderFunction()) 注册后,模型在需要时会自动生成 JSON 参数调用该方法。
对于常见病科普、药品说明书等静态知识,我们使用向量检索 + 文档加载器。这里以 Spring AI 的 VectorStore 和 DocumentReader 为基础(需引入 spring-ai-pgvector-store 或内存实现)。
@Service
public class HealthKnowledgeService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
public String askWithRag(String question) {
// 1. 向量检索相似文档
List<Document> relevantDocs = vectorStore.similaritySearch(question, 3);
String context = relevantDocs.stream()
.map(Document::getText)
.collect(Collectors.joining("\n---\n"));
// 2. 构建增强提示
String prompt = """
基于以下医学知识资料,回答用户问题。若资料不足,请根据通用医学常识回答。
资料:
%s
问题:%s
回答:""".formatted(context, question);
return chatClient.prompt(prompt).call().content();
}
}实际部署时,需预先将医疗指南、药品说明等文档切分、向量化并存入 PGVector 或 Elasticsearch。
@RestController
@RequestMapping("/api/health")
public class HealthController {
private final HealthConsultationService consultationService;
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestParam String sessionId, @RequestBody String message) {
return ResponseEntity.ok(consultationService.chat(sessionId, message));
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestParam String sessionId, @RequestParam String message) {
return consultationService.streamChat(sessionId, message)
.map(content -> ServerSentEvent.<String>builder()
.data(content)
.event("message")
.build());
}
}前端可通过 EventSource 或 fetch 流式接收。
为了在流式场景下正确保存对话历史,我们需要重构 streamChat 方法,使用 Flux 的 buffer 或自定义 Subscriber 拼接完整内容。
public Flux<String> streamChatWithHistory(String sessionId, String userMessage) {
List<Message> history = sessionCache.get(sessionId, ArrayList::new);
UserMessage userMsg = new UserMessage(userMessage);
history.add(userMsg);
Flux<ChatResponse> flux = chatClient.prompt()
.messages(history)
.stream()
.chatResponse();
// 用于拼接完整回复
StringBuilder fullReply = new StringBuilder();
return flux.map(response -> {
String chunk = response.getResult().getOutput().getText();
fullReply.append(chunk);
return chunk;
})
.doOnComplete(() -> {
// 将完整回复作为 AssistantMessage 存入历史
AssistantMessage assistantMsg = new AssistantMessage(fullReply.toString());
history.add(assistantMsg);
sessionCache.put(sessionId, history);
})
.doOnError(e -> log.error("流式对话异常", e));
}这样既保证了实时输出,又不丢失上下文。
医疗场景对稳定性要求极高,我们需处理以下异常:
示例:使用 @Retryable 处理 API 调用失败。
@Retryable(value = {AiApiException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public String chatWithRetry(String sessionId, String message) {
// ...
}同时,提供降级响应(如“系统繁忙,请稍后再试”)。
本应用可一键部署至腾讯云 TKE(Kubernetes)或轻量应用服务器。提供 Dockerfile:
FROM openjdk:17-jdk-slim
COPY target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]配置环境变量 DEEPSEEK_API_KEY,并可结合腾讯云 API 网关对外暴露,启用限流和鉴权。
本文完整展示了如何利用 DeepSeek 和 Spring AI 构建一个具备生产能力的 AI 家庭医生应用。我们实现了多轮对话、流式输出、函数调用、RAG 检索等关键技术,并充分考虑了异常处理和性能优化。
该应用可作为远程问诊、健康管理的智能底座,未来可扩展接入医学影像分析、电子病历结构化等更复杂功能。DeepSeek 的高性价比和 Spring AI 的标准化接口,使得 Java 开发者能够快速拥抱 LLM 时代,为医疗数字化注入新动能
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。