<dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
@SystemMessage(""" You are "MediBot", an AI medical assistant for Peking Union Medical College Hospital. Your responsibilities: 1. Provide general medical information and guidance 2. Help patients understand symptoms and treatment options 3. Assist with appointment scheduling when asked 4. Answer questions about hospital departments and doctors Rules: - Always be polite and professional - Include appropriate medical disclaimers - Never provide definitive diagnoses - Recommend seeing a doctor for serious concerns """) String chat(String userMessage); }
系统消息在开始时发送一次,用于设定 AI 的行为上下文。
5. 添加聊天记忆
5.1 为什么记忆很重要
没有记忆,每条消息都是独立的:
1 2 3 4 5
User: My name is John AI: Nice to meet you, John!
User: What's my name? AI: I don't know your name. ← Problem!
@SystemMessage("You are a helpful medical assistant.") String chat( @MemoryId Long conversationId, // Unique per conversation @UserMessage String message ); }
User: Book an appointment with Dr. Wang tomorrow at 2pm
LLM → Detects "book appointment" intent → Calls bookAppointment(doctor="Dr. Wang", date="2025-06-12", time="14:00") ← Returns "Appointment confirmed" → Responds: "Your appointment with Dr. Wang is confirmed for tomorrow at 2pm."
6.2 创建工具
使用 @Tool 注解方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
@Component publicclassCalculatorTools {
@Tool(name = "sum", value = "Add two numbers") publicdoublesum( @P(value = "First number")double a, @P(value = "Second number")double b) { return a + b; }
@Tool(name = "checkAvailability", value = "Check if a doctor has available slots") publicbooleancheckAvailability( @P(value = "Department name") String department, @P(value = "Date in YYYY-MM-DD format") String date, @P(value = "Time slot: morning or afternoon") String time, @P(value = "Doctor name (optional)", required = false) String doctorName) {
return appointmentService.hasAvailability(department, date, time, doctorName); }
@Tool(name = "bookAppointment", value = "Book a medical appointment. Confirm details with user first.") public String bookAppointment( @P(value = "Patient name") String patientName, @P(value = "Patient ID card number") String idCard, @P(value = "Department") String department, @P(value = "Date in YYYY-MM-DD") String date, @P(value = "Time: morning or afternoon") String time, @P(value = "Doctor name (optional)", required = false) String doctorName) {
You are "MediBot", an AI assistant for Peking Union Medical College Hospital.
Your capabilities: 1. Medical consultation - provide general health information 2. Department guidance - help patients find the right department 3. Doctor information - answer questions about our doctors 4. Appointment management - book and cancel appointments
Rules: - Always verify patient identity (name + ID card) before appointments - Confirm appointment details before booking - Use the knowledge base for hospital-specific information - Be professional yet friendly - Add appropriate emoji to make responses warm
@RestController @RequestMapping("/api/medical") @Tag(name = "Medical AI Assistant") publicclassMedicalController {
@Autowired private MedicalAssistant assistant;
@PostMapping("/chat") @Operation(summary = "Chat with medical assistant") public String chat(@RequestBody ChatRequest request) { return assistant.chat(request.getConversationId(), request.getMessage()); } }
@Data publicclassChatRequest { private Long conversationId; // Unique per user session private String message; }
9. 最佳实践
系统设计
实践
重要性
多用户使用 @MemoryId
防止用户之间的对话串扰
持久化聊天记忆
服务器重启不丢失上下文
工具描述
清晰的描述帮助 LLM 正确选择
文档分块
更小的分块提高检索精度
最低分数阈值
过滤低相关性结果
安全
1 2
# Never commit API keys langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
性能
1 2 3 4 5 6 7 8
// Limit chat memory to control token usage MessageWindowChatMemory.withMaxMessages(10)
// Set minScore to filter irrelevant documents EmbeddingStoreContentRetriever.builder() .minScore(0.8) .maxResults(3) .build()
常见陷阱
陷阱
解决方案
LLM 不使用工具
改进工具描述
工具参数错误
使用 @P 注解并配以清晰的描述
超出范围的回复
优化系统消息
响应缓慢
使用流式输出实现实时反馈
10. 常见问题
Q:能否在不改代码的情况下从 OpenAI 切换到 DeepSeek?
A:可以。只需修改配置:
1 2 3 4 5 6 7
# From OpenAI langchain4j.open-ai.chat-model.base-url=https://api.openai.com/v1 langchain4j.open-ai.chat-model.api-key=${OPENAI_KEY} # To DeepSeek langchain4j.open-ai.chat-model.base-url=https://api.deepseek.com langchain4j.open-ai.chat-model.api-key=${DEEPSEEK_KEY}
Q:如何处理敏感的医疗数据?
A:
通过 Ollama 使用本地 LLM 处理敏感数据
在发送到外部 API 之前实施数据脱敏
加密存储聊天记录
遵循 HIPAA/GDPR 合规要求
Q:ChatMemory 和 ChatMemoryProvider 有什么区别?
A:
ChatMemory:单个共享的记忆实例
ChatMemoryProvider:工厂模式,为每个 memoryId 创建隔离的记忆
Q:如何更新知识库?
A:只需添加新文档并重新导入:
1 2 3 4 5
// New document DocumentnewDoc= FileSystemDocumentLoader.loadDocument("new-policy.md");
// Add to existing store EmbeddingStoreIngestor.ingest(newDoc, embeddingStore);
Q:LLM 能在一次对话中调用多个工具吗?
A:可以。LLM 可以链式调用工具:
1 2 3 4 5
User: "Book Dr. Wang and check if Dr. Li is available next week"
LLM → Calls bookAppointment(...) → Calls checkAvailability(...) → Responds with both results