1. 环境准备与项目初始化大家好我是老张一个在Java和AI领域摸爬滚打了十多年的老码农。最近几年大语言模型LLM火得一塌糊涂很多朋友尤其是咱们Java开发者看着Python那边玩得风生水起心里也痒痒总琢磨着怎么把这些智能能力搬到自己的SpringBoot项目里来。特别是医疗、金融这类对数据隐私和响应速度要求极高的领域一个本地化、可掌控的AI助手需求非常迫切。今天我就手把手带大家从一个干净的SpringBoot项目开始一步步构建一个专属于我们自己的“医疗AI小助手”。咱们不搞那些虚头巴脑的理论直接上代码踩坑的地方我会重点标出来。这个助手将具备几个核心能力既能调用本地部署的轻量模型比如用Ollama跑的DeepSeek也能无缝对接云上的大模型比如阿里百炼不仅能进行多轮对话记住聊天上下文还能通过“函数调用”去查询数据库、执行具体业务逻辑最后我们还会给它装上“知识库”通过RAG检索增强生成技术让它能基于我们提供的专业医疗文档来回答问题避免胡说八道。整个技术栈的核心是LangChain4j。你可以把它理解为Java版的LangChain它把和大模型交互那些繁琐的步骤——比如组装消息、管理记忆、调用工具——都封装成了简单易用的API让我们Java开发者也能优雅地集成AI能力。我们的目标就是用最熟悉的Java和SpringBoot打造一个智能、实用、可控的医疗问答引擎。首先打开你的IDE我习惯用IntelliJ IDEA我们来创建项目。这里我选择使用Spring Initializr来快速搭建。组GroupId和工件ArtifactId大家按自己习惯来比如com.example和medical-ai-assistant。打包方式选JarJava版本建议用17或以上这是目前的主流且稳定的选择。在依赖选择上我们初期只需要最基础的Spring Web为后续提供RESTful API做准备。Lombok减少样板代码让实体类更简洁。Spring Configuration Processor方便我们在application.yml里写配置时有提示。点击生成下载并导入到IDE中。项目结构生成后我们第一步不是急着写业务代码而是统一管理依赖版本。在pom.xml中我强烈建议使用dependencyManagement来锁定 LangChain4j 及其相关组件的版本避免未来依赖冲突的噩梦。这是我当前项目在用的版本相对稳定properties langchain4j.version0.31.0/langchain4j.version !-- 其他版本属性 -- /properties dependencyManagement dependencies dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-bom/artifactId version${langchain4j.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement这样做的好处是后续我们引入具体的LangChain4j模块时就不需要再指定版本号了Spring Boot的父POM和这里的BOM会帮我们协调好。项目初始化这块看似简单但打好基础至关重要尤其是依赖管理在后期引入向量数据库、各种模型连接器等组件时能让你少掉很多头发。2. 双模型接入本地与云端的灵活配置AI助手的“大脑”就是大语言模型。在实际项目中我们往往需要根据场景灵活选择对响应速度、数据隐私要求高的用本地模型对能力要求强、需要最新知识的用云端模型。我们的医疗助手需要兼顾两者所以这一章我们来搞定双模型接入。2.1 本地模型部署Ollama DeepSeek为什么选Ollama因为它太方便了。对于Java开发者来说我们不想去折腾复杂的Python环境和模型转换。Ollama一条命令就能把模型跑起来还提供了标准的OpenAI兼容的API接口LangChain4j可以直接调用。DeepSeek是一个优秀的开源模型在代码和推理能力上表现不错而且有较小的参数版本如1.5B、7B适合本地部署。第一步安装并启动模型。去Ollama官网下载安装对应操作系统的版本。安装完成后打开终端Windows是CMD或PowerShell运行一条命令ollama run deepseek-r1:1.5b这条命令会自动从镜像站拉取deepseek-r1:1.5b这个模型并运行。看到提示符就说明成功了。它默认会在11434端口启动一个API服务。你可以用curl简单测试一下curl http://localhost:11434/api/chat -d {model: deepseek-r1:1.5b, messages: [{role: user, content: 你好}]}。第二步在SpringBoot项目中集成。在pom.xml中添加Ollama的依赖dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-ollama/artifactId /dependency然后在application.yml中配置连接langchain4j: ollama: chat-model: base-url: http://localhost:11434 model-name: deepseek-r1:1.5b temperature: 0.7 # 控制创造性医疗问答建议调低如0.3 timeout: 60s这里的关键是base-url指向本地Ollama服务model-name必须和你在Ollama中拉取的模型名称完全一致。temperature参数很重要在医疗场景下我们追求答案的准确性和稳定性所以通常设置一个较低的值比如0.1到0.3减少模型“胡编乱造”的可能。第三步编写测试代码验证。创建一个测试类OllamaChatTest。这里我展示一种更“Spring”的方式使用Configuration来声明Bean而不是在测试里硬编码。Configuration public class OllamaConfig { Bean public ChatModel ollamaChatModel() { return OllamaChatModel.builder() .baseUrl(http://localhost:11434) .modelName(deepseek-r1:1.5b) .temperature(0.3) .build(); } }然后在测试中注入这个Bean进行调用。这样做的好处是配置集中管理后续切换模型或者增加参数都非常方便。跑一个简单的问答测试如果能看到模型返回的关于“感冒建议多喝水”这类答案说明本地模型通道就打通了。2.2 云端模型接入阿里百炼平台当我们需要更强大的模型能力或者希望获得更最新的医学知识时云端模型是更好的选择。国内我比较常用的是阿里云的百炼平台它提供了通义千问等多种模型稳定性和合规性都比较好。第一步获取API密钥。前往阿里云百炼平台开通服务并创建API-KEY。这个Key是调用服务的凭证务必妥善保管不要提交到代码仓库。我习惯把它放在环境变量或者Spring Boot的配置中心里。第二步添加依赖和配置。LangChain4j对百炼的支持在社区模块中需要添加额外依赖dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-community-dashscope-spring-boot-starter/artifactId /dependencyapplication.yml配置如下langchain4j: community: dashscope: chat-model: api-key: ${ALI_API_KEY:你的默认Key仅用于开发} model-name: qwen-plus # 可以选择 qwen-turbo, qwen-max 等 temperature: 0.2 top-p: 0.8注意api-key这里我使用了${ALI_API_KEY}这种占位符写法。最佳实践是在运行应用时通过环境变量ALI_API_KEY传入或者在生产环境的配置文件中单独配置避免密钥泄露。第三步测试与对比。同样我们可以声明一个AliyunChatModel的Bean。为了体验不同模型的效果我们可以同时配置通义千问和DeepSeek-V2如果百炼平台提供了。在测试方法中我们可以用同一个医学问题例如“请解释一下糖尿病患者的饮食管理原则”分别询问本地DeepSeek和云端千问对比它们的回答速度、详细程度和专业性。你会发现云端大模型在知识的广度和语言的流畅度上通常更有优势但本地模型的响应速度极快且完全无需担心数据外泄。在实际项目中我们可以设计一个简单的路由策略根据问题的复杂度或敏感度来决定调用哪个模型实现成本和效果的最优平衡。3. 核心服务抽象AIService的妙用前面我们直接操作ChatModel进行问答这就像直接使用JDBC操作数据库虽然灵活但繁琐。LangChain4j提供了一个更高级的抽象——AIService它类似于MyBatis的Mapper接口能让我们用定义接口的方式来完成复杂的AI交互极大地提升了开发效率。AIService到底是什么你可以把它想象成一个针对AI能力的“动态代理层”。我们只需要定义一个普通的Java接口然后通过LangChain4j的工具类就能自动获得这个接口的实现。这个实现体会帮我们处理所有底层细节把我们的Java方法参数转换成LLM能理解的Message列表调用模型再把模型的输出解析成我们方法定义的返回类型可以是String、自定义对象、甚至是List。它原生支持了聊天记忆、提示词模板、函数调用等高级功能。让我们来创建一个医疗问答专用的AIService。首先在pom.xml中确保已经引入了核心依赖langchain4j-core。然后我们创建一个接口MedicalQAServiceimport dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; public interface MedicalQAService { SystemMessage(你是一位专业的全科医生助手请用清晰、准确、通俗的语言回答用户的健康咨询问题。对于无法确定或涉及紧急情况的问题务必建议用户及时就医。) String chat(String userMessage); }看代码非常简洁。SystemMessage注解定义了AI的系统角色这相当于给模型一个“人设”和基础指令。UserMessage注解标记了哪个参数是用户的输入。这里我们只有一个String参数所以可以省略UserMessageLangChain4j默认会将唯一的String参数视为用户输入。如何获得这个接口的实现实例我们需要一个配置类来组装它。这里我展示一种与Spring深度集成的方式Configuration public class AIServiceConfig { Bean public MedicalQAService medicalQAService(ChatModel chatModel) { return AiServices.builder(MedicalQAService.class) .chatLanguageModel(chatModel) .build(); } }注意这里ChatModel是通过参数注入的。Spring会根据你的配置比如上一章定义的ollamaChatModel或aliyunChatModelBean自动注入进来。这意味着我们只需要更换ChatModel的Bean定义就能让整个MedicalQAService切换底层模型业务代码完全不用动测试一下效果。在Controller或者测试类中注入MedicalQAService并调用RestController public class TestController { Autowired private MedicalQAService medicalQAService; GetMapping(/ask) public String ask(RequestParam String question) { return medicalQAService.chat(question); } }访问/ask?question我有点头疼和发烧可能是什么原因你会得到一段以医生助手口吻给出的、包含建议就医的回复。这比直接调用ChatModel的generate方法返回的“原始”答案要规范得多因为SystemMessage已经在背后约束了模型的回答风格。理解其工作原理。当我们调用medicalQAService.chat(“头疼发烧”)时背后发生了LangChain4j的动态代理拦截了这个调用。它将SystemMessage的内容包装成SystemMessage对象。将我们的参数 “头疼发烧” 包装成UserMessage对象。将这两个Message组成一个列表发送给ChatModel。接收模型返回的AiMessage。将AiMessage的内容提取出来转换成String类型作为方法返回值。整个过程我们无需关心消息的格式转换只需关注业务接口设计。这才是符合Java开发者习惯的AI集成方式。4. 实现多轮对话与记忆管理一个合格的助手必须能记住对话历史否则每次问答都是全新的开始用户体验会非常糟糕。比如用户先问“糖尿病的症状有哪些”接着问“那该如何预防呢”模型需要理解“那”指的是糖尿病。这就是聊天记忆Chat Memory要解决的问题。LangChain4j提供了多种记忆存储方案。最简单的是TokenWindowChatMemory它在内存中维护一个最近对话的Token窗口。但内存记忆重启就消失了所以我们更需要持久化记忆比如存到数据库。这里我以MongoDB为例因为它存储JSON格式的对话记录非常自然。第一步引入依赖和配置。在pom.xml中添加MongoDB和LangChain4j对MongoDB记忆存储的支持dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-mongodb/artifactId /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-store-memory-mongodb/artifactId /dependency在application.yml中配置MongoDB连接信息。第二步创建支持记忆的AIService。记忆的核心是ChatMemory对象每个用户或每个会话需要独立的ChatMemory实例。我们通过MemoryId注解来标识。public interface MedicalChatService { SystemMessage(你是专业医生助手。) String chat(MemoryId String sessionId, UserMessage String userMessage); }注意chat方法多了一个sessionId参数并用MemoryId标注。这个sessionId可以来自前端传入的用户ID、会话ID或者设备唯一标识。它用于隔离不同用户的聊天记忆。第三步配置带有MongoDB记忆的Bean。这里的配置稍复杂但一劳永逸Configuration public class PersistentMemoryConfig { Bean public ChatMemoryProvider chatMemoryProvider(MongoTemplate mongoTemplate) { // 使用MongoDB作为记忆存储后端 ChatMemoryStore store new MongoDBChatMemoryStore(mongoTemplate); return memoryId - { // 为每个 memoryId 创建一个持久化记忆最多保留20轮对话 ChatMemory chatMemory MessageWindowChatMemory.builder() .id(memoryId) .maxMessages(20) .chatMemoryStore(store) .build(); return chatMemory; }; } Bean public MedicalChatService medicalChatService(ChatModel chatModel, ChatMemoryProvider chatMemoryProvider) { return AiServices.builder(MedicalChatService.class) .chatLanguageModel(chatModel) .chatMemoryProvider(chatMemoryProvider) // 注入记忆提供者 .build(); } }ChatMemoryProvider是一个工厂根据传入的memoryId返回对应的ChatMemory实例。MessageWindowChatMemory.builder().maxMessages(20)限定了每个会话最多记住最近的20条消息防止记忆无限膨胀。MongoDBChatMemoryStore则将记忆实体持久化到MongoDB中。第四步测试持久化效果。编写一个测试用同一个sessionId连续问两个问题String sessionId user_123; String answer1 service.chat(sessionId, 糖尿病典型症状是什么); String answer2 service.chat(sessionId, 针对这些症状日常监测需要注意什么);观察第二个回答模型应该能基于第一个问题中提到的“糖尿病症状”来进行补充回答。然后重启你的SpringBoot应用再次用sessionId “user_123”问一个问题例如“我们刚才聊了什么”。如果配置正确模型应该能回忆起重启前的部分对话内容这说明记忆已经成功持久化到MongoDB并在重启后加载了。到MongoDB中查看会发现以sessionId为键的文档里面以JSON格式存储着结构化的对话消息。这种设计使得对话状态的管理变得非常清晰和可靠。5. 提示词工程与模板化直接让模型自由发挥在专业领域很容易“跑偏”。提示词工程Prompt Engineering就是通过精心设计的指令引导模型输出更符合我们期望的结果。在LangChain4j的AIService中我们可以通过注解非常优雅地实现复杂的提示词模板。系统提示词是模型的“角色卡”。之前的例子我们用了SystemMessage这很重要。对于医疗助手我们可以写得更加详细和严格SystemMessage( 你是一位严谨、专业且富有同情心的AI全科医生助手。 你的核心职责是提供准确的健康信息科普和就医指导但不能替代执业医师的诊断。 请严格遵守以下规则 1. 基于公开、权威的医学知识进行回答对不确定的信息必须明确声明“我不确定”。 2. 对于症状描述可列举常见可能性但必须强调“如果持续或加重请及时就医”。 3. 绝对禁止提供任何具体的用药剂量、疗程建议。 4. 回答语言需简洁、清晰避免使用过度专业的术语必要时进行解释。 5. 如果用户描述的情况可能涉及急症如剧烈胸痛、严重外伤、意识不清必须立即、强烈建议呼叫急救或前往急诊。 )多行字符串Java 15的文本块特性让长提示词的编写变得非常方便。这份“角色卡”极大地约束了模型的行为使其输出更加安全、可控。用户提示词模板让交互更动态。很多时候用户的问题需要结合一些上下文信息。比如我们有一个系统用户输入症状我们还想把用户的年龄信息也传给模型。这时可以用UserMessage模板String chatWithContext(V(symptoms) String symptoms, V(age) int age);在方法实现上我们可以这样写UserMessage(一位{{age}}岁的用户咨询以下症状{{symptoms}}。请根据该年龄段常见情况进行分析。) String chatWithContext(V(symptoms) String symptoms, V(age) int age);调用service.chatWithContext(“反复胃痛饭后加重”, 45)LangChain4j会自动将参数值填充到模板的{{age}}和{{symptoms}}占位符中生成完整的用户消息。这使得提示词不再是硬编码的字符串而是可以动态组装的模板。从文件加载复杂提示词。当提示词非常长或者需要频繁修改时放在代码里就不合适了。SystemMessage和UserMessage都支持从类路径资源文件加载。SystemMessage(fromResource /prompts/system_role.txt) public interface MedicalQAService {}我们在src/main/resources/prompts/下创建system_role.txt文件把长长的系统提示词放进去。这样产品经理或医学专家可以直接修改文本文件来调整AI的“人设”无需开发人员重新编译代码。这是一种很好的关注点分离实践。一个综合性的例子结构化输出。在医疗场景我们可能希望模型不仅给出文本回答还能结构化地输出一些关键信息比如可能的疾病分类、紧急程度等。我们可以定义返回一个自定义的MedicalAdvice对象public class MedicalAdvice { private String summary; // 简要总结 private ListString possibleCauses; // 可能原因 private String urgency; // 紧急程度低/中/高 private String recommendation; // 行动建议 } public interface MedicalQAService { UserMessage(分析症状{{symptoms}}。请按以下JSON格式回复{summary: ..., possibleCauses: [...], urgency: ..., recommendation: ...}) MedicalAdvice analyzeSymptoms(V(symptoms) String symptoms); }这里我们在UserMessage中明确要求了JSON格式的输出。LangChain4j会利用模型的能力将返回的文本解析并映射到MedicalAdvice对象的字段中。虽然目前LangChain4j对复杂对象映射的支持还在完善中但对于这种结构清晰的场景配合适当的提示已经能取得很好的效果。这让我们能够以类型安全的方式获取模型的输出极大地便利了后续的业务逻辑处理。6. 函数调用让AI助手“动手”操作模型再聪明它也无法直接操作我们的数据库、调用外部API或执行计算。函数调用Function Calling在LangChain4j里也叫工具Tools就是为了解决这个问题。它让大语言模型具备了“动手能力”模型分析用户请求决定是否需要调用一个我们预先定义好的Java方法并生成调用这个方法的参数我们去执行然后把执行结果返回给模型由模型整合成最终回答告诉用户。让我们给医疗助手添加查询药品信息的功能。假设我们有一个简单的药品数据库用Spring Data JPA实现。第一步定义一个“工具”类。任何工具方法都必须放在一个被Tool注解标记的类中。Component // 注册为Spring Bean public class DrugInfoTool { PersistenceContext private EntityManager entityManager; Tool(根据药品名称查询其通用名、主要功效和常见副作用。) public String searchDrugInfo(P(药品名称可以是商品名或通用名) String drugName) { // 这里简化处理实际应使用JPA Repository进行模糊查询 String sql SELECT generic_name, main_effects, common_side_effects FROM drug WHERE name LIKE CONCAT(%, :name, %) LIMIT 1; ListObject[] results entityManager.createNativeQuery(sql) .setParameter(name, drugName) .getResultList(); if (results.isEmpty()) { return 未在药品库中找到关于\ drugName \的详细信息。; } Object[] row results.get(0); return String.format(药品信息通用名【%s】主要功效【%s】常见副作用【%s】。, row[0], row[1], row[2]); } }关键点Tool注解在类上标记这是一个工具类。注解的value属性是对工具的自然语言描述这个描述至关重要模型就是靠它来理解这个工具是干什么的、什么时候该用。searchDrugInfo方法就是具体的工具。P注解用于描述参数同样清晰的描述能帮助模型更好地生成参数值。第二步将工具装配到AIService中。我们需要修改之前的AIService配置将工具类注入进去。Configuration public class ToolEnabledAIServiceConfig { Bean public MedicalQAService medicalQAService(ChatModel chatModel, ChatMemoryProvider chatMemoryProvider, DrugInfoTool drugInfoTool) { // 注入工具Bean return AiServices.builder(MedicalQAService.class) .chatLanguageModel(chatModel) .chatMemoryProvider(chatMemoryProvider) .tools(drugInfoTool) // 注册工具 .build(); } }第三步测试函数调用。现在当我们向MedicalQAService提问“阿司匹林是治什么的有什么副作用吗”背后会发生一系列自动化的交互模型理解问题发现需要具体的药品信息。模型“看到”我们注册了一个searchDrugInfo工具描述是查询药品信息。模型决定调用这个工具并自动生成调用参数drugName为“阿司匹林”。LangChain4j框架拦截这个决定反射调用drugInfoTool.searchDrugInfo(“阿司匹林”)。我们的方法执行数据库查询返回结构化的药品信息文本。框架将这个结果作为新的上下文信息发送回给模型。模型整合药品信息和你原始的问题生成最终的回答“阿司匹林是一种非甾体抗炎药通用名是乙酰水杨酸。其主要功效是解热、镇痛、抗炎常用于缓解轻度疼痛和发热。常见副作用可能包括胃肠道不适、出血风险增加等。请注意用药需遵医嘱。”整个过程对用户是透明的他感觉到的就是一个智能的、能查询知识的助手。我们可以在工具类里定义很多方法比如searchClinicalGuidelines查询临床指南、calculateBMi计算身体质量指数、bookAppointment预约挂号等等。模型会根据对话上下文自主判断是否需要调用、调用哪个工具。这真正实现了让AI成为我们业务系统的“智能接口”而不仅仅是聊天机器人。7. 构建知识库RAG增强与向量数据库集成尽管大模型拥有海量知识但它存在“幻觉”编造信息和“知识陈旧”的问题。对于专业的医疗助手我们不能让它信口开河。检索增强生成RAG技术是解决这个问题的银弹。其核心思想是先将我们自己的专业知识如医学文献、药品说明书、医院指南转换成向量存入专门的数据库向量数据库当用户提问时先从我们的知识库中检索出最相关的文档片段然后将这些片段和问题一起交给大模型让它“基于给定的资料”来生成答案。这大大提高了答案的准确性和可信度。第一步准备知识文档并分割。假设我们有一份PDF格式的《高血压防治指南》。我们需要将它转换成文本并切割成适合模型处理的小片段。LangChain4j提供了多种文档加载器如Apache PDFBox和分割器。// 1. 加载文档 Document document DocumentLoader.loadDocument(Paths.get(hypertension_guide.pdf)); // 2. 使用递归分割器这是最通用和推荐的方式 DocumentSplitter splitter DocumentSplitters.recursive(300, 30); ListTextSegment segments splitter.split(document);DocumentSplitters.recursive(300, 30)创建了一个分割器它会尝试将文档递归地分割成最大300个token的片段并且相邻片段之间有30个token的重叠。重叠是为了避免一个完整的句子或概念被硬生生切到两个片段里丢失上下文。300这个值需要根据你选用模型的上下文窗口大小来调整要预留出问题和其他指令的空间。第二步将文本片段向量化并存储。文本本身模型无法直接用于检索需要转换成数学向量Embedding。我们需要一个“嵌入模型”Embedding Model来做这件事。同样我们可以用本地模型如Ollama的nomic-embed-text或云端服务如阿里百炼的text-embedding-v3。这里以百炼为例配置langchain4j: community: dashscope: embedding-model: api-key: ${ALI_API_KEY} model-name: text-embedding-v3然后我们需要一个向量数据库来存储这些向量。生产环境不建议用内存存储这里我们使用Pinecone它是一个流行的云端向量数据库有免费额度。Configuration public class VectorStoreConfig { Bean public EmbeddingModel embeddingModel() { // 使用配置文件中定义的阿里云嵌入模型 return new DashScopeEmbeddingModel(); } Bean public EmbeddingStoreTextSegment embeddingStore() { // 从环境变量读取Pinecone的API密钥和索引名 String apiKey System.getenv(PINECONE_API_KEY); String indexName medical-knowledge; return new PineconeEmbeddingStore(apiKey, indexName, your-project-name, us-east-1); } Bean public ContentRetriever contentRetriever(EmbeddingModel embeddingModel, EmbeddingStoreTextSegment embeddingStore) { // 创建检索器设置最小相似度得分 return EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(3) // 每次检索返回最相关的3个片段 .minScore(0.7) // 相似度得分阈值过滤掉不相关的结果 .build(); } }ContentRetriever是检索过程的核心抽象。当用户提问时它会用同样的EmbeddingModel将问题转换成向量。在EmbeddingStore中搜索与问题向量最相似的几个文档片段向量。只返回相似度得分如余弦相似度高于minScore的片段。第三步将知识库检索器集成到AI助手。最后一步就是在创建AIService时将这个ContentRetriever注入进去。Bean public MedicalQAService medicalQAService(ChatModel chatModel, ContentRetriever contentRetriever) { return AiServices.builder(MedicalQAService.class) .chatLanguageModel(chatModel) .contentRetriever(contentRetriever) // 注入检索器 .build(); }现在整个流程就闭环了。当用户问“高血压患者每日钠盐摄入量建议是多少”时ContentRetriever会从我们上传的《高血压防治指南》向量库中找到关于“钠盐摄入”的段落。这些段落会作为额外的上下文和用户的原始问题一起组装成新的提示词发送给大模型。模型基于我们提供的权威指南片段进行回答答案的准确性和专业性得到极大保障。你可以定期更新你的向量数据库比如加入最新的医学研究摘要或医院内部规章这样你的AI助手就能持续学习永不过时。通过结合函数调用和RAG我们的医疗AI助手既拥有了“动手”查询结构化数据数据库的能力又拥有了“阅读”非结构化知识文档库的能力成为一个真正专业、可信的智能体。