使用LangGraph4j/Spring AI构建智能问诊Agent:医疗问诊的工程化实践
引言:医疗问诊的智能化挑战
传统医疗问诊流程面临着严重的效率瓶颈。患者往往缺乏医学专业知识,无法系统性地描述病情,导致就诊前信息准备不充分,严重影响诊断的准确性和治疗的有效性。在医疗资源紧张和后疫情时代远程医疗需求激增的背景下,这种低效不仅增加了医生的工作负担,还可能延误病情的诊断和治疗。

通过AI Agent实现智能化问诊预采集,可以显著提升就诊效率,降低医疗成本,改善患者就医体验,成为医疗服务数字化转型的关键基础设施。本文面向AI工程化开发者、医疗信息化系统架构师和智能对话系统开发者,将提供一个完整的技术实现方案。
背景分析:AI技术在医疗领域的机遇
医疗行业正经历着前所未有的数字化转型压力。一方面,医疗资源分布不均和医生短缺问题日益严重;另一方面,患者对便捷、高效医疗服务的需求不断增长。大模型技术的成熟为医疗问诊的智能化提供了新的可能性。
当前医疗AI应用已经从简单的问答系统发展到复杂的多轮对话和诊断辅助。特别是在症状收集、初步分诊、健康管理等场景,AI系统已经展现出超越传统方法的效率和准确性。LangGraph4j的MultiAgent架构天然适配医疗问诊的复杂流程,能够很好地模拟真实医疗环境中的分诊、专科问诊、信息确认等环节。
技术选型:为什么选择LangGraph4j + Spring AI
经过深入的技术调研和对比分析,我们选择了LangGraph4j + Spring AI的技术方案。主要基于以下考虑:
需求分析
- 多Agent协作: 需要分诊Agent、科室Agent、症状采集Agent的协同工作
- 状态管理: 需要完整追踪患者的就诊流程和状态转移
- 工具集成: 需要集成医疗知识检索、数据存储、外部API等工具
- Java生态: 团队熟悉Java技术栈,需要与现有Spring Boot系统集成
方案对比分析
| 技术方案 | 开发效率 | 维护成本 | 性能表现 | 团队匹配度 |
|---|
| LangGraph4j + Spring AI | 高 | 低 | 优秀 | 完美匹配 |
| 自研MultiAgent框架 | 低 | 高 | 中等 | 需要投入 |
| Semantic Kernel | 中等 | 中等 | 良好 | 技术栈不匹配 |
技术决策依据
- LangGraph4j优势: 提供完整的MultiAgent编排能力,支持复杂的状态管理和工具调用
- Spring AI集成: 与Spring Boot生态完美集成,降低学习成本和维护复杂度
- 社区支持: 开源项目活跃,文档完善,社区案例丰富
- 扩展性: 支持自定义Agent和工具,便于后续功能扩展
核心架构实现
核心架构图
flowchart TDsubgraph 前端层UI[用户界面]MobileApp[移动应用]WebApp[Web应用]endsubgraph 应用层ConsultationService[医疗问诊服务]WorkflowEngine[工作流引擎]ResponseGenerator[响应生成器]endsubgraph 服务层TriageAgent[分诊Agent]DepartmentAgent[科室Agent]SymptomAgent[症状采集Agent]RAGService[RAG知识服务]KnowledgeBase[医疗知识库]DataProtection[数据保护服务]endsubgraph 基础设施层PostgreSQL[PostgreSQL数据库]Redis[Redis缓存]Milvus[Milvus向量数据库]LLM[大语言模型]Monitoring[监控系统]endUI --> ConsultationServiceMobileApp --> ConsultationServiceWebApp --> ConsultationServiceConsultationService --> WorkflowEngineConsultationService --> ResponseGeneratorWorkflowEngine --> TriageAgentWorkflowEngine --> DepartmentAgentWorkflowEngine --> SymptomAgentTriageAgent --> RAGServiceDepartmentAgent --> RAGServiceSymptomAgent --> RAGServiceRAGService --> KnowledgeBaseRAGService --> LLMConsultationService --> DataProtectionDataProtection --> PostgreSQLConsultationService --> PostgreSQLConsultationService --> RedisRAGService --> MilvusConsultationService --> MonitoringWorkflowEngine --> MonitoringRAGService --> Monitoring
分层式MultiAgent设计
基于LangGraph4j的MultiAgent架构,我们设计了一套完整的分层式问诊系统。整个系统通过图结构定义了三个核心Agent的协作关系:分诊Agent负责初步评估,科室Agent进行专科问诊,症状采集Agent负责信息结构化。
@Componentpublic class MedicalConsultationWorkflow {private final TriageAgent triageAgent;private final DepartmentAgent departmentAgent;private final SymptomCollectionAgent symptomCollectionAgent;private final ChatLanguageModel chatModel;public Graph buildConsultationGraph() {GraphBuilder builder = StateGraph.builder(MedicalState.class);builder.addNode("triage", this::triageNode);builder.addNode("department_consultation", this::departmentConsultationNode);builder.addNode("symptom_collection", this::symptomCollectionNode);builder.addNode("emergency_routing", this::emergencyRoutingNode);builder.addNode("final_advice", this::finalAdviceNode);builder.setEntryPoint("triage");builder.addConditionalEdges("triage", this::routeAfterTriage, Map.of("EMERGENCY", "emergency_routing","DEPARTMENT", "department_consultation", "GENERAL_ADVICE", "final_advice" ));builder.addEdge("department_consultation", "symptom_collection");builder.addEdge("symptom_collection", "final_advice");builder.setFinishPoint("final_advice");builder.setFinishPoint("emergency_routing");return builder.build();}private MedicalState triageNode(MedicalState state) {try {log.info("开始医疗分诊,患者主诉: {}", state.getChiefComplaint());TriageResult result = triageAgent.performTriage(state.getChiefComplaint(),state.getPatientInfo());state.setDepartment(result.getDepartment());state.setUrgencyLevel(result.getUrgencyLevel());state.setEmergencySymptoms(result.hasEmergencySymptoms());state.setTriageResult(result);state.setCurrentPhase(MedicalPhase.TRIAGE_COMPLETED);log.info("分诊完成: 科室={}, 紧急程度={}", result.getDepartment(), result.getUrgencyLevel());} catch (Exception e) {log.error("医疗分诊失败", e);state.setErrorMessage("分诊失败: " + e.getMessage());state.setDepartment("全科");state.setUrgencyLevel(3);}return state;}private String routeAfterTriage(MedicalState state) {if (state.hasEmergencySymptoms()) {return "EMERGENCY";}int urgencyLevel = state.getUrgencyLevel();String department = state.getDepartment();if (urgencyLevel <= 2 && !"全科".equals(department)) {return "DEPARTMENT";}if ("全科".equals(department) && urgencyLevel > 3) {return "GENERAL_ADVICE";}return "DEPARTMENT";}}@Datapublic class MedicalState {private String sessionId;private String chiefComplaint;private PatientInfo patientInfo;private LocalDateTime timestamp;private String department;private int urgencyLevel;private boolean emergencySymptoms;private TriageResult triageResult;private ConsultationResult consultationResult;private StructuredMedicalData structuredMedicalData;private EmergencyAdvice emergencyAdvice;private MedicalPhase currentPhase;private String errorMessage;private String finalResponse;private Map contextData = new HashMap<>();public enum MedicalPhase {INITIALIZED,TRIAGE_COMPLETED,DEPARTMENT_COMPLETED,SYMPTOMS_COLLECTED,EMERGENCY_HANDLED,COMPLETED}public void addContext(String key, Object value) {contextData.put(key, value);}public boolean hasEmergencySymptoms() {return emergencySymptoms;}}
核心实现要点:
- 图结构定义: 使用StateGraph.builder()创建有向无环图,明确定义Agent间的依赖关系
- 节点注册: 通过addNode()方法注册每个Agent的处理函数,确保职责清晰分离
- 路由控制: 使用addConditionalEdges()实现基于条件判断的智能路由,支持复杂的医疗决策逻辑
- 状态管理: 通过AgentState对象在节点间传递状态信息,确保问诊流程的连续性
- 错误处理: 每个节点都有独立的异常处理机制,保证系统稳定性
MultiAgent协作机制
@Servicepublic class MedicalConsultationService {private final Graph consultationGraph;private final MedicalStateRepository stateRepository;public CompletableFuture processConsultation(String sessionId, String chiefComplaint, PatientInfo patientInfo) {return CompletableFuture.supplyAsync(() -> {try {log.info("开始医疗问诊流程,会话ID: {}", sessionId);MedicalState initialState = initializeMedicalState(sessionId, chiefComplaint, patientInfo);MedicalState finalState = consultationGraph.invoke(initialState);stateRepository.saveState(finalState);return buildMedicalConsultationResult(finalState);} catch (Exception e) {log.error("医疗问诊流程执行失败: sessionId={}", sessionId, e);throw new MedicalConsultationException("问诊流程执行失败", e);}});}}
医疗知识库与RAG系统
在医疗问诊系统中,知识的准确性和实时性直接影响问诊质量。我们采用RAG(Retrieval-Augmented Generation)架构,结合向量检索和大模型生成,确保医疗建议的准确性和可靠性。
知识增强生成的医疗应用
@Componentpublic class MedicalRAGGenerator {private final ChatLanguageModel chatModel;private final MedicalKnowledgeRetriever retriever;public String generateMedicalResponse(String userQuery, MedicalContext context) {List relevantDocs = retriever.retrieveRelevantKnowledge(userQuery, context);String enhancedPrompt = buildEnhancedPrompt(userQuery, relevantDocs, context);String response = chatModel.generate(enhancedPrompt);return postProcessAndValidate(response, relevantDocs);}private String buildEnhancedPrompt(String query, List docs,MedicalContext context) {StringBuilder knowledgeSection = new StringBuilder();for (int i = 0; i < docs.size(); i++) {RetrievedDocument doc = docs.get(i);knowledgeSection.append(String.format("[知识%d] 来源: %sn内容: %snn", i+1, doc.getSource(), doc.getContent()));}return String.format("""你是一位专业的医疗AI助手,负责协助医生进行问诊。请基于提供的医疗知识,回答患者的问题或提供建议。患者信息:- 科室: %s- 症状类别: %s- 紧急程度: %s相关医疗知识:%s患者问题: %s请基于上述医疗知识回答患者问题。要求:1. 回答必须基于提供的医疗知识2. 如果知识不足,明确说明无法回答3. 避免提供具体的诊断和处方建议4. 如果涉及紧急情况,建议立即就医5. 回答要专业、准确、易于理解""", context.getDepartment(),context.getSymptomCategory(),context.getUrgencyLevel(),knowledgeSection.toString(),query);}}
混合存储架构
医疗问诊系统需要处理多种类型的数据:结构化的患者信息、半结构化的对话内容、向量化的医疗知识。我们采用混合存储策略,充分发挥不同存储组件的优势。
@Repositorypublic class ConsultationRepository {private final JdbcTemplate jdbcTemplate;private final RedisTemplate redisTemplate;public ConsultationRecord saveConsultation(ConsultationRecord record) {String sql = """INSERT INTO consultations (consultation_id, patient_id, department, urgency_level, status, chief_complaint, triage_result, structured_data) VALUES (?, ?, ?, ?, ?, ?, ?, ?)RETURNING id, created_at""";GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();jdbcTemplate.update(connection -> {PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);ps.setString(1, record.getConsultationId());ps.setString(2, record.getPatientId());ps.setString(3, record.getDepartment());ps.setInt(4, record.getUrgencyLevel());ps.setString(5, record.getStatus());ps.setString(6, record.getChiefComplaint());ps.setObject(7, record.getTriageResult());ps.setObject(8, record.getStructuredData());return ps;}, keyHolder);String cacheKey = "consultation:" + record.getConsultationId();redisTemplate.opsForValue().set(cacheKey, record, Duration.ofHours(24));return record;}}
用户交互设计
对话式与结构化的结合
在医疗问诊系统中,用户体验的平衡至关重要:既要保持AI对话的自然便捷,又要确保医疗信息的完整准确。我们设计了渐进式的交互模式,通过自然语言对话收集信息,最后通过结构化表单进行确认。
@Componentpublic class ConversationFlowController {private final ConversationStateManager stateManager;private final ResponseGenerator responseGenerator;private final InformationValidator validator;public ConversationResponse processUserInput(String sessionId, String userInput) {ConversationState currentState = stateManager.getState(sessionId);switch (currentState.getPhase()) {case INITIAL_GREETING:return handleInitialGreeting(sessionId, userInput);case SYMPTOM_COLLECTION:return handleSymptomCollection(sessionId, userInput, currentState);case DEEP_DIVE:return handleDeepDive(sessionId, userInput, currentState);case STRUCTURED_CONFIRMATION:return handleStructuredConfirmation(sessionId, userInput, currentState);default:return handleUnexpectedInput(sessionId, userInput);}}private ConversationResponse handleSymptomCollection(String sessionId, String userInput, ConversationState state) {UnderstandingResult understanding = conversationUnderstander.understand(userInput, state);List newSymptoms = symptomExtractor.extractSymptoms(understanding);ValidationResult validation = validator.validateSymptoms(newSymptoms);stateManager.updateSymptoms(sessionId, newSymptoms);if (validation.isComplete()) {return transitionToDeepDive(sessionId, state);} else {return generateFollowUpQuestions(sessionId, validation);}}}
智能响应生成
@Componentpublic class ResponseGenerator {private final ChatLanguageModel llm;private final ResponseTemplateManager templateManager;public String generateNaturalResponse(List questions) {String prompt = buildNaturalResponsePrompt(questions);return llm.generate(prompt);}public String generateEmpatheticResponse(String symptom, String severity) {String template = templateManager.getEmpathyTemplate(symptom, severity);Map variables = Map.of("symptom", symptom,"severity", getSeverityDescription(severity),"time", getCurrentTimeGreeting());return templateEngine.process(template, variables);}public String generateMedicalExplanation(String condition, String explanation) {String prompt = String.format("""作为医疗助手,请用通俗易懂的语言解释以下医疗状况:状况: %s医学解释: %s要求:1. 使用患者能理解的语言,避免专业术语2. 结构清晰,分段说明3. 包含生活建议和注意事项4. 如果需要就医,明确说明5. 控制在200字以内""", condition, explanation);return llm.generate(prompt);}}
性能优化与监控
响应时间优化
@Componentpublic class PerformanceOptimizedConsultationService {private final L1Cache l1Cache;private final L2Cache l2Cache;private final L3Cache l3Cache;public CompletableFuture processAsync(String sessionId, String userInput) {return CompletableFuture.supplyAsync(() -> preprocessInput(userInput)).thenComposeAsync(this::generateResponseAsync).thenApplyAsync(this::postprocessResponse).exceptionally(this::handleException).completeOnTimeout(generateTimeoutResponse(), 3000, TimeUnit.MILLISECONDS);}private CompletableFuture generateResponseAsync(String processedInput) {String cached = l1Cache.get(processedInput);if (cached != null) {return CompletableFuture.completedFuture(cached);}cached = l2Cache.get(processedInput);if (cached != null) {l1Cache.put(processedInput, cached);return CompletableFuture.completedFuture(cached);}return CompletableFuture.supplyAsync(() -> generateWithLLM(processedInput)).thenApply(response -> {l1Cache.put(processedInput, response);l2Cache.put(processedInput, response, Duration.ofMinutes(30));return response;});}}
监控与告警
@Componentpublic class MedicalConsultationMonitor {private final MeterRegistry meterRegistry;private final AlertManager alertManager;private final Timer responseTimeTimer;private final Counter requestCounter;private final Gauge activeConversationsGauge;public void recordResponseTime(String sessionId, long responseTime) {responseTimeTimer.record(responseTime, TimeUnit.MILLISECONDS);if (responseTime > 3000) {alertManager.sendAlert(AlertLevel.WARNING,"问诊响应时间过长",Map.of("sessionId", sessionId, "responseTime", responseTime));}}@Scheduled(fixedRate = 60000) public void checkSystemHealth() {double averageResponseTime = responseTimeTimer.mean(TimeUnit.MILLISECONDS);double errorRate = calculateErrorRate();int activeConversations = getActiveConversations();double healthScore = calculateHealthScore(averageResponseTime, errorRate, activeConversations);if (healthScore < 0.7) {alertManager.sendAlert(AlertLevel.WARNING,"系统健康状态下降",Map.of("healthScore", healthScore, "averageResponseTime", averageResponseTime, "errorRate", errorRate, "activeConversations", activeConversations));}}}
实践踩坑与解决方案
关键问题与解决方案
部署挑战:多服务集成的复杂性
在实际部署过程中,我们发现MultiAgent架构的复杂性带来了几个关键挑战:
- 服务发现和注册机制的配置复杂
- Agent间的网络通信延迟影响用户体验
- 分布式事务的数据一致性难以保证
- 监控和调试的复杂性增加
解决方案:
version: '3.8'services:medical-agent-app:image: medical-agent:latestports:- "8080:8080"environment:- SPRING_PROFILES_ACTIVE=docker- OPENAI_API_KEY=${OPENAI_API_KEY}depends_on:- postgres- redis- milvusnetworks:- medical-networkhealthcheck:test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]interval: 30stimeout: 10sretries: 3postgres:image: postgres:15environment:- POSTGRES_DB=medical_db- POSTGRES_USER=medical_user- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}volumes:- postgres_data:/var/lib/postgresql/data- ./init.sql:/docker-entrypoint-initdb.d/init.sqlnetworks:- medical-network
性能调优:响应时间与准确率的平衡
初始版本中,我们发现响应时间和准确率之间存在明显的权衡关系:
- 使用大模型时准确率高但响应慢(平均5-8秒)
- 使用小模型时响应快但准确率低(仅70%左右)
- RAG检索增加了额外的延迟(200-500ms)
优化策略:
@Componentpublic class AdaptiveModelSelector {private final Map models;private final ResponseTimeMonitor monitor;public ChatLanguageModel selectOptimalModel(QueryComplexity complexity) {switch (complexity) {case SIMPLE:return models.get(ModelSize.SMALL);case MEDIUM:return models.get(ModelSize.MEDIUM); case COMPLEX:return models.get(ModelSize.LARGE);default:return models.get(ModelSize.MEDIUM);}}public CompletableFuture generateWithAdaptiveModel(String prompt) {QueryComplexity complexity = analyzeComplexity(prompt);ChatLanguageModel model = selectOptimalModel(complexity);return CompletableFuture.supplyAsync(() -> model.generate(prompt)).orTimeout(getTimeoutForModel(model.getSize()), TimeUnit.MILLISECONDS);}}
合规处理:医疗数据隐私保护
医疗数据的隐私保护要求带来了技术和流程上的挑战:
- 数据必须进行端到端加密
- 需要详细的审计日志
- 数据保留和删除策略复杂
- 跨境数据传输限制
合规实现:
@Componentpublic class MedicalDataProtection {private final AESEncryptionService encryptionService;private final AuditLogService auditService;private final DataRetentionService retentionService;@EventListenerpublic void handleDataAccess(DataAccessEvent event) {auditService.logAccess(event.getUserId(),event.getDataType(),event.getAction(),event.getTimestamp(),event.getIpAddress());if (!hasPermission(event.getUserId(), event.getDataType())) {throw new UnauthorizedAccessException("无权限访问医疗数据");}}public String encryptSensitiveData(String data, String patientId) {try {String encrypted = encryptionService.encrypt(data);auditService.logEncryption(patientId, encrypted);return encrypted;} catch (Exception e) {auditService.logEncryptionFailure(patientId, e);throw new DataEncryptionException("数据加密失败", e);}}}
技术总结与边界
核心技术结论
通过使用LangGraph4j和Spring AI构建智能问诊Agent,我们成功实现了医疗问诊的自动化和智能化。这个方案的核心价值在于:
- MultiAgent架构的自然适配:LangGraph4j的图结构完美匹配医疗问诊的分诊-专科-采集流程
- RAG技术的知识增强:结合向量检索和大模型生成,确保医疗建议的准确性
- 混合存储的性能优化:PostgreSQL、Redis、Milvus的组合,充分发挥各自优势
- 渐进式交互的用户体验:对话式收集+结构化确认,平衡便捷性和准确性
适用技术场景
适用场景:
- 常见病、慢性病的初步问诊和信息收集
- 医院预约前的病情描述和症状梳理
- 远程医疗的预诊断和分诊服务
- 健康管理的症状追踪和风险提醒
不适用场景:
- 紧急医疗情况(心梗、中风、严重外伤等)
- 需要影像学检查的复杂疾病诊断
- 处方开具和药物治疗建议
- 精神科、急诊科等需要特殊处理的科室
技术限制条件
- 准确率限制:系统不能完全替代医生诊断,准确率约85-90%
- 性能约束:复杂查询的响应时间可能达到3-5秒
- 知识边界:依赖于训练数据和知识库的质量
- 合规要求:需要满足HIPAA等医疗数据保护法规
后续技术方向
- 多模态融合:结合语音、图像、文本的综合问诊
- 个性化模型:基于患者历史的个性化问诊策略
- 实时协作:与医生系统的实时对接和协作
- 预测分析:基于症状数据的疾病风险预测
这个技术方案为医疗AI应用提供了一个可落地的实现路径,既保证了技术的先进性,又充分考虑了工程实践的复杂性和医疗行业的特殊性。通过持续的技术迭代和优化,该方案有望在医疗数字化转型中发挥重要作用。