硅谷小智

本文最后更新于:2025年4月29日 下午

硅谷小智(医疗版)

一、LangChain4j入门

1、简介

LangChain4j 的目标是简化将大模型(LLM - Large Language Model)集成到 Java 应用程序中的过程。

1.1、历史背景

2022年11月30日OpenAI发布了Chat GPT (GPT-3.5)
早在2022年10月,Harrison Chase发布了基于Python的LangChain。
随后同时包含了Python版和JavaScript (LangChain.js)版的LangChain也发布了。
2023年11月,Quarkus 发布了LangChain4j 的0.1版本,2025年2月发布了1.0-Beta1 版本,4月发布了1.0-Beta3版本
官网:LangChain4j

1.2、主要功能

与大型语言模型和向量数据库的便捷交互
通过统一的应用程序编程接口(API),可以轻松访问所有主要的商业和开源大型语言模型以及向量数据库,使你能够构建聊天机器人、智能助手等应用。
专为 Java 打造
借助Spring Boot集成,能够将大模型集成到 Java 应用程序中。大型语言模型与 Java 之间实现了双向集成:你可以从Java中调用大型语言模型,同时也允许大型语言模型反过来调用你的Java代码
智能代理、工具、检索增强生成(RAG)
为常见的大语言模型操作提供了广泛的工具,涵盖从底层的提示词模板创建、聊天记忆管理和输出解析,到智能代理和检索增强生成等高级模式。

1.3、应用示例

  1. 你想要实现一个自定义的由人工智能驱动的聊天机器人,它可以访问你的数据,并按照你期望的方式运行:
    • 客户支持聊天机器人,它可以:
      • 礼貌地回答客户问题
    • 处理/更改/取消订单
    • 教育助手,它可以:
      • 教授各种学科
    • 解释不清楚的部分
      • 评估用户的理解/知识水平
  2. 你想要处理大量的非结构化数据(文件、网页等),并从中提取结构化信息。例如:
    • 从客户评价和支持聊天记录中提取有效评价
    • 从竞争对手的网站上提取有趣的信息
    • 从求职者的简历中提取有效信息
  3. 你想要生成信息,例如:
    • 为你的每个客户量身定制的电子邮件
    • 为你的应用程序/网站生成内容:
      • 博客文章
      • 故事
  4. 你想要转换信息,例如:
    • 总结
    • 校对和改写
    • 翻译

2、创建SpringBoot项目

2.1、创建一个Maven项目

名称:java-ai-langchain4j

<groupId>com.wwj</groupId>
    <artifactId>java-ai-langchain4j</artifactId>
    <version>1.0-SNAPSHOT</version>

2.2、添加SpringBoot相关依赖

pom.xml 配置如下:

<properties>
       <maven.compiler.source>17</maven.compiler.source>
       <maven.compiler.target>17</maven.compiler.target>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <spring-boot.version>3.2.6</spring-boot.version>
       <knife4j.version>4.3.0</knife4j.version>
       <langchain4j.version>1.0.0-beta3</langchain4j.version>
       <mybatis-plus.version>3.5.11</mybatis-plus.version>
   </properties>
   <dependencies>
       <!-- web应用程序核心依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!-- 编写和运行测试用例 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
       <!-- 前后端分离中的后端接口测试工具 -->
       <dependency>
           <groupId>com.github.xiaoymin</groupId>
           <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
           <version>${knife4j.version}</version>
       </dependency>
   </dependencies>
   <dependencyManagement>
       <dependencies>
           <!--引入SpringBoot依赖管理清单-->
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-dependencies</artifactId>
               <version>${spring-boot.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>

2.3、创建配置文件

resourcese下创建配置文件 application.yml

# Web服务端口号
server:
  port: 8080

2.4、创建启动类

package com.wwj.java.ai.langchain4j;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class XiaozhiApp {
	public static void main(String[] args) {
		SpringApplication.run(XiaozhiApp.class, args);
	}
}

2.5、启动启动类

访问 http://localhost:8080/doc.html 查看程序能否成功运行

3、接入大模型

参考文档:https://docs.langchain4j.dev/get-started

3.1、LangChain4j 库结构

LangChain4j 具有模块化设计,包括:

  1. langchain4j-core 模块,它定义了核心抽象概念(如聊天语言模型和嵌入存储)及其 API。
  2. 主 langchain4j 模块,包含有用的工具,如文档加载器、聊天记忆实现,以及诸如人工智能服务等 高层功能。
  3. 大量的 langchain4j-{集成} 模块,每个模块都将各种大语言模型提供商和嵌入存储集成到 LangChain4j 中。你可以独立使用 langchain4j-{集成} 模块。如需更多功能,只需导入主 langchain4j 依赖项即可。

3.2、添加LangChain4j相关依赖

<properties>
    <langchain4j.version>1.0.0-beta3</langchain4j.version>
 </properties>
 <dependencies>
    <!-- 基于open-ai的langchain4j接口:ChatGPT、deepseek都是open-ai标准下的大模型 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai</artifactId>
    </dependency>
 </dependencies>
 <dependencyManagement>
    <dependencies>
        <!--引入langchain4j依赖管理清单-->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-bom</artifactId>
            <version>${langchain4j.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
 </dependencyManagement>

3.3、创建测试用例

接入任何一个大模型都需要先去申请 ApiKey

如果你暂时没有密钥,也可以使用LangChain4j 提供的演示密钥,这个密钥是免费的,有使用配额限制, 且仅限于 gpt-4o-mini 模型。

package com.wwj.java.ai.langchain4j;

import dev.langchain4j.model.openai.OpenAiChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class LLMTest {
	@Test
	public void testGPT() {
		OpenAiChatModel model = OpenAiChatModel.builder()
				.baseUrl("http://langchain4j.dev/demo/openai/v1")
				.apiKey("demo")
				.modelName("gpt-4o-mini")
				.build();
		String answer = model.chat("你好,你都会做些什么");
		System.out.println(answer);
	}
}

4、SpringBoot整合

参考文档:https://docs.langchain4j.dev/tutorials/spring-boot-integration

4.1、替换依赖

langchain4j-open-ai 替换成 langchain4j-open-ai-spring-boot-starter

<dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
</dependency>

4.2、在application.yml中配置模型参数

langchain4j:
  open-ai:
    chat-model:
      base-url: http://langchain4j.dev/demo/openai/v1
      api-key: demo
      model-name: gpt-4o-mini
      # 应用程序发送给大模型的请求日志与响应日志
      log-requests: true
      log-responses: true

4.3、创建测试用例

@Autowired
private OpenAiChatModel openAiChatModel;
	
@Test
public void testSpringBoot() {
    String answer = openAiChatModel.chat("我是谁?");
    System.out.println(answer);
}

二、接入其他大模型

1、都有哪些大模型

  • 大语言模型排行榜SuperCLUE中文大模型测评基准——评测榜单

    SuperCLUE 是由国内 CLUE 学术社区于 2023 年 5 月推出的中文通用大模型综合性评测基准。

  • 评测目的:全面评估中文大模型在语义理解、逻辑推理、代码生成等 10 项基础能力,以及涵盖数 学、物理、社科等 50 多学科的专业能力,旨在回答在通用大模型发展背景下,中文大模型的效果情 况,包括不同任务效果、与国际代表性模型的差距、与人类的效果对比等问题。

  • 特色优势:针对中文特性任务,如成语、诗歌、字形等设立专项评测,使评测更符合中文语言特 点。通过 3700 多道客观题和匿名对战机制,动态追踪国内外主流模型,如 GPT-4、文心一言、通义 千问等的表现差异,保证评测的客观性和时效性。

  • 行业影响:作为中文领域权威测评社区,其评测结果被学界和产业界广泛引用,例如商汤 “日日新 5.0” 和百度文心大模型均通过 SuperCLUE 验证技术突破,推动了中文 NLP 技术生态的迭代,为中文 大模型的发展和优化提供了重要的参考依据,促进了中文大模型技术的不断进步和应用。

  • LangChain4j支持接入的大模型Comparison Table of all supported Language Models | LangChain4j

2、接入DeepSeek

1.1、获取开发参数

1.2、配置开发参数

为了apikay的安全,建议将其配置在服务器的环境变量中。变量名自定义即可,例如 DEEP_SEEK_API_KEY

1.3、配置模型参数

DeepSeek API文档:https://api-docs.deepseek.com/zh-cn/

在 LangChain4j 中,DeepSeek 和 GPT 一样也使用了 OpenAI 的接口标准,因此也使用OpenAiChatModel进 行接入

#DeepSeek
 langchain4j.open-ai.chat-model.base-url=https://api.deepseek.com
 langchain4j.open-ai.chat-model.api-key=${DEEP_SEEK_API_KEY}
 #DeepSeek-V3
 langchain4j.open-ai.chat-model.model-name=deepseek-chat
 #DeepSeek-R1 推理模型
#langchain4j.open-ai.chat-model.model-name=deepseek-reasoner

1.4、测试

直接使用前面的测试用例即可

3、ollama本地部署

3.1、为什么要本地部署

Ollama 是一个本地部署大模型的工具。使用 Ollama 进行本地部署有以下多方面的原因:

  • 数据隐私与安全:对于金融、医疗、法律等涉及大量敏感数据的行业,数据安全至关重要。
  • 离线可用性:在网络不稳定或无法联网的环境中,本地部署的 Ollama 模型仍可正常运行。
  • 降低成本:云服务通常按使用量收费,长期使用下来费用较高。而 Ollama 本地部署,只需一次性投 入硬件成本,对于需要频繁使用大语言模型且对成本敏感的用户或企业来说,能有效节约成本。
  • 部署流程简单:只需通过简单的命令 “ollama run < 模型名>”,就可以自动下载并运行所需的模型。
  • 灵活扩展与定制:可对模型微调,以适配垂直领域需求。

3.2、在ollama上部署DeepSeek

官网https://ollama.com/

(1)下载并安装ollama: OllamaSetup.exe

(2)查看模型列表,选择要部署的模型,模型列表Ollama Search

(3)执行命令: ollama run deepseek-r1:1.5 运行大模型。如果是第一次运行则会先下载大模型

3.3、常用命令

Available Commands:
  serve       Start ollama
  create      Create a model from a Modelfile
  show        Show information for a model
  run         Run a model
  stop        Stop a running model
  pull        Pull a model from a registry
  push        Push a model to a registry
  list        List models
  ps          List running models
  cp          Copy a model
  rm          Remove a model
  help        Help about any command

3.4、引入依赖

参考文档:https://docs.langchain4j.dev/integrations/language-models/ollama#get-started

<!-- 接入ollama -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
</dependency>

3.5、配置模型参数

#ollama
 langchain4j.ollama.chat-model.base-url=http://localhost:11434
 langchain4j.ollama.chat-model.model-name=deepseek-r1:1.5b
 langchain4j.ollama.chat-model.log-requests=true
 langchain4j.ollama.chat-model.log-responses=true

3.6、创建测试用例

/**
 * ollama接入
*/
@Autowired
private OllamaChatModel ollamaChatModel;

@Test
public void testOllama() {
    String answer = ollamaChatModel.chat("你是谁?");
    System.out.println(answer);
}

4、接入阿里百炼平台

4.1、什么是阿里百炼

  • 阿里云百炼是 2023 年 10 月推出的。它集成了阿里的通义系列大模型和第三方大模型,涵盖文本、 图像、音视频等不同模态。
  • 功能优势:集成超百款大模型 API,模型选择丰富;5-10 分钟就能低代码快速构建智能体,应用构 建高效;提供全链路模型训练、评估工具及全套应用开发工具,模型服务多元;在线部署可按需扩 缩容,新用户有千万 token 免费送,业务落地成本低。
  • 支持接入的模型列表https://help.aliyun.com/zh/model-studio/models
  • 模型广场https://bailian.console.aliyun.com/?productCode=p_efm#/model-market

4.2、申请免费体验

(1)点击进入免费体验页面:百炼控制台

(2)登录选择API-Key,创建并复制自己的API-Key

4.3、添加依赖

LangChain4j参考文档:https://docs.langchain4j.dev/integrations/language-models/dashscope#plain-java

<dependencies>
<!-- 接入阿里云百炼平台 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>
</dependencies>
<dependencyManagement>
     <dependencies>
     <!--引入百炼依赖管理清单-->
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-community-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
    </dependencies>
</dependencyManagement>

4.4、配置模型参数

langchain4j:
  #阿里百练平台
  community:
    dashscope:
      chat-model:
        api-key: sk-46c3383686324d6298a1e6549bf815b2
        model-name: qwen-max

4.5、测试通义千问

@Autowired
private QwenChatModel qwenChatModel;

@Test
public void testQwen() {
    String answer = qwenChatModel.chat("你是谁?");
    System.out.println(answer);
}

4.6、测试通义万象

生成图片测试

@Test
public void testQwen() {
	WanxImageModel wanxImageModel = WanxImageModel.builder()
			.modelName("wanx2.1-t2i-turbo")
			.apiKey("sk-xxxxxxxxxxx")
			.build();
	Response<Image> response = wanxImageModel.generate("奇幻森林精灵:在一片弥漫着轻柔薄雾的古老森林深处,阳光透过茂密枝叶洒下金色光斑。一位身材娇小、长着透明薄翼的精灵少女站在一朵硕大的蘑菇上。她有着海藻般的绿色长发,发间点缀着蓝色的小花,皮肤泛着珍珠般的微光。身上穿着由翠绿树叶和白色藤蔓编织而成的连衣裙,手中捧着一颗散发着柔和光芒的水晶球,周围环绕着五彩斑斓的蝴蝶,脚下是铺满苔藓的地面,蘑菇和蕨类植物丛生,营造出神秘而梦幻的氛围。");
	URI url = response.content().url();
	System.out.println(url);
}

三、人工智能服务 AIService

1、什么是AIService

AIService使用面向接口和动态代理的方式完成程序的编写,更灵活的实现高级功能。

1.1、链 Chain(旧版)

链的概念源自 Python 中的 LangChain。其理念是针对每个常见的用例都设置一条链,比如聊天机器人、 检索增强生成(RAG)等。链将多个底层组件组合起来,并协调它们之间的交互。链存在的主要问题是不 灵活,我们不进行深入的研究。

1.2、人工智能服务 AIService

在LangChain4j中我们使用AIService完成复杂操作。底层组件将由AIService进行组装。

AIService可处理最常见的操作:

  • 为大语言模型格式化输入内容
  • 解析大语言模型的输出结果

它们还支持更高级的功能:

  • 聊天记忆 Chat memory
  • 工具 Tools
  • 检索增强生成 RAG

2、创建AIService

2.1、引入依赖

<!--langchain4j高级功能-->
 <dependency>
     <groupId>dev.langchain4j</groupId>
     <artifactId>langchain4j-spring-boot-starter</artifactId>
 </dependency>

2.2、创建接口

package com.wwj.java.ai.langchain4j.assistant;

public interface Assistant {
	String chat(String userMessage);
}

2.3、测试用例

@SpringBootTest
public class AIServiceTest {
	@Autowired
	private QwenChatModel qwenChatModel;
	
	@Test
	public void testChat() {
		Assistant assistant = AiServices.create(Assistant.class, qwenChatModel);
		String answer = assistant.chat("你是谁?");
		System.out.println(answer);
	}
}

2.4、@AiService

也可以在 Assistant 接口上添加 @AiService 注解

import dev.langchain4j.service.spring.AiService;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

//因为我们在配置文件中同时配置了多个大语言模型,所以需要在这里明确指定(EXPLICIT)模型的beanName
@AiService(wiringMode = EXPLICIT, chatModel = "qwenChatModel")
public interface Assistant {
	String chat(String userMessage);
}

测试用例中,我们可以直接注入Assistant对象

@Autowired
private Assistant assistant;
@Test
public void testAssistant() {
    String answer = assistant.chat("你是谁?");
    System.out.println(answer);
}

2.5、工作原理

AiServices会组装Assistant接口以及其他组件,并使用反射机制创建一个实现Assistant接口的代理对象。 这个代理对象会处理输入和输出的所有转换工作。在这个例子中,chat方法的输入是一个字符串,但是大 模型需要一个 **UserMessage **对象。所以,代理对象将这个字符串转换为 UserMessage ,并调用聊天语 言模型。chat方法的输出类型也是字符串,但是大模型返回的是 AiMessage 对象,代理对象会将其转换 为字符串。 简单理解就是:代理对象的作用是输入转换和输出转换

简单理解就是:代理对象的作用是输入转换和输出转换

四、聊天记忆 Chat memory

1、测试对话是否有记忆

@SpringBootTest
public class ChatMemoryTest {
	
	@Autowired
	private Assistant assistant;
	@Test
	public void testChatMemory() {
		// 创建一个会话
		String answer1 = assistant.chat("我是坏坏");
		System.out.println(answer1);
		String answer2 = assistant.chat("我是谁?");
		System.out.println(answer2);
	}
}

answer1:你好,坏坏!有什么我可以帮助你的吗?或者你想聊些什么呢?

answer2:您好!您是使用这个平台的用户,但我无法直接知道您的具体身份信息,因为我重视并保护用户的隐私。如果您想分享更多信息关于您自己,我很乐意进一步交流!或者,如果您有其他想要了解的问题或需要帮助的地方,请告诉我。

很显然,目前的接入方式,大模型是没有记忆的。

2、聊天记忆的简单实现

可以使用下面的方式实现对话记忆。

@Autowired
private QwenChatModel qwenChatModel;

@Test
public void testChatMemory2() {
    // 第一轮对话
    UserMessage userMessage1 = UserMessage.userMessage("我是坏坏");
    ChatResponse chatResponse1 = qwenChatModel.chat(userMessage1);
    AiMessage aiMessage1 = chatResponse1.aiMessage();
    System.out.println(aiMessage1.text());

    // 第二轮对话
    UserMessage userMessage2 = UserMessage.userMessage("你知道我是谁吗?");
    ChatResponse chatResponse2 = qwenChatModel.chat(Arrays.asList(userMessage1, aiMessage1, userMessage2));
    AiMessage aiMessage2 = chatResponse2.aiMessage();
    System.out.println(aiMessage2.text());
}

第一轮:你好,坏坏!有什么我可以帮助你的吗?或者你想聊些什么呢?

第二轮:你好!根据你提供的信息“我是坏坏”,我了解到你的昵称或名字是“坏坏”。不过,除非你之前和我有过交流并且提供了更多的个人信息,否则我无法知道更多关于你的具体信息。如果你愿意分享更多信息,我会很高兴更好地了解你并提供帮助。有什么特别的事情想要聊聊吗?

3、使用ChatMemory实现聊天记忆

使用AIService可以封装多轮对话的复杂性,使聊天记忆功能的实现变得简单

@Test
public void testChatMemory3() {
    //创建chatMemory
    MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
    //创建AIService
    Assistant assistant = AiServices
        .builder(Assistant.class)
        .chatLanguageModel(qwenChatModel)
        .chatMemory(chatMemory)
        .build();
    //调用service的接口
    String answer1 = assistant.chat("我是环环");
    System.out.println(answer1);
    String answer2 = assistant.chat("我是谁");
    System.out.println(answer2);
}

4、使用AIService实现聊天记忆

4.1、创建记忆对话智能体

当AIService由多个组件(大模型,聊天记忆,等)组成的时候,我们就可以称他为智能体

我们先创建一个具有记忆功能的智能体接口:MemoryChatAssistant

package com.wwj.java.ai.langchain4j.assistant;

import dev.langchain4j.service.spring.AiService;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
		wiringMode = EXPLICIT,
		chatModel = "qwenChatModel",
		chatMemory = "chatMemory"
)
public interface MemoryChatAssistant {
	String chat(String message);
}

4.2、配置ChatMemory

在智能体MemoryChatAssistant接口中的chatMemory进行配置,作为Bean注入

package com.wwj.java.ai.langchain4j.config;

import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MemoryChatAssistantConfig {
	
	@Bean
	public ChatMemory chatMemory() {
		return MessageWindowChatMemory.withMaxMessages(10);
	}
}

4.3、测试

@Autowired
private MemoryChatAssistant memoryChatAssistant;
@Test
public void testChatMemory4() {
    //调用service的接口
    String answer1 = memoryChatAssistant.chat("我是环环");
    System.out.println(answer1);
    String answer2 = memoryChatAssistant.chat("我是谁");
    System.out.println(answer2);
}

5、隔离聊天记忆

为每个用户的新聊天或者不同的用户区分聊天记忆

5.1、创建记忆隔离对话智能体

package com.wwj.java.ai.langchain4j.assistant;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
    wiringMode = EXPLICIT,
    chatModel = "qwenChatModel",
    chatMemoryProvider = "chatMemoryProvider"
)
public interface SeparateChatAssistant {
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

5.2、配置ChatMemoryProvider

package com.wwj.java.ai.langchain4j.config;

import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatMemoryProviderConfig {
	@Bean
	public ChatMemoryProvider chatMemoryProvider() {
		return memoryId -> MessageWindowChatMemory.builder().id(memoryId).maxMessages(10).build();
	}
}

5.3、测试对话助手

用两个不同的memoryId测试聊天记忆的隔离效果

@Autowired
private SeparateChatAssistant separateChatAssistant;
@Test
public void testChatMemory5() {
    String answer1 = separateChatAssistant.chat(1,"我是环环");
    System.out.println(answer1);
    String answer2 = separateChatAssistant.chat(1,"我是谁");
    System.out.println(answer2);
    String answer3 = separateChatAssistant.chat(2,"我是谁");
    System.out.println(answer3);
}

结果如下,只有Id为 1 与 2记忆分开

answer1:你好,环环!很高兴见到你。有什么我可以帮助你的吗?如果你有任何问题或需要讨论的话题,随时告诉我。

answer2:你刚才告诉我你是环环。如果你是用这个名字来指代自己,那么你就是环环。如果这是一个昵称或者有其他含义,请告诉我更多信息,这样我可以更好地帮助你。

answer3:您是使用这个平台的用户,但我无法直接知道您的具体身份信息,因为我重视并保护用户的隐私。如果您想告诉我更多关于您的信息,我很乐意了解!或者,如果您有任何问题或需要帮助的地方,请随时告诉我。

五、持久化聊天记忆 Persistence

默认情况下,聊天记忆存储在内存中。如果需要持久化存储,可以实现一个自定义的聊天记忆存储类, 以便将聊天消息存储在你选择的任何持久化存储介质中。

1、存储介质的选择

大模型中聊天记忆的存储选择哪种数据库,需要综合考虑数据特点、应用场景和性能要求等因素,以下 是一些常见的选择及其特点:

  • MySQL
    • 特点:关系型数据库。支持事务处理,确保数据的一致性和完整性,适用于结构化数据的存储 和查询。
    • 适用场景:如果聊天记忆数据结构较为规整,例如包含固定的字段如对话 ID、用户 ID、时间 戳、消息内容等,且需要进行复杂的查询和统计分析,如按用户统计对话次数、按时间范围查 询特定对话等,MySQL 是不错的选择。
  • Redis
    • 特点:内存数据库,读写速度极高。它适用于存储热点数据,并且支持多种数据结构,如字符 串、哈希表、列表等,方便对不同类型的聊天记忆数据进行处理。
    • 适用场景:对于实时性要求极高的聊天应用,如在线客服系统或即时通讯工具,Redis 可以快 速存储和获取最新的聊天记录,以提供流畅的聊天体验。
  • MongoDB
    • 特点:文档型数据库,数据以 JSON - like 的文档形式存储,具有高度的灵活性和可扩展性。它 不需要预先定义严格的表结构,适合存储半结构化或非结构化的数据。
    • 适用场景:当聊天记忆中包含多样化的信息,如文本消息、图片、语音等多媒体数据,或者消 息格式可能会频繁变化时,MongoDB 能很好地适应这种灵活性。例如,一些社交应用中用户可 能会发送各种格式的消息,使用 MongoDB 可以方便地存储和管理这些不同类型的数据。
  • Cassandra
    • 特点:是一种分布式的 NoSQL 数据库,具有高可扩展性和高可用性,能够处理大规模的分布 式数据存储和读写请求。适合存储海量的、时间序列相关的数据。
    • 适用场景:对于大型的聊天应用,尤其是用户量众多、聊天数据量巨大且需要分布式存储和处 理的场景,Cassandra 能够有效地应对高并发的读写操作。例如,一些面向全球用户的社交媒 体平台,其聊天数据需要在多个节点上进行分布式存储和管理,Cassandra 可以提供强大的支 持。

2、MongoDB

2.1、简介

MongoDB 是一个基于文档的 NoSQL 数据库,由 MongoDB Inc. 开发。

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数 据库的数据库管理系统的统称。

MongoDB 的设计理念是为了应对大数据量、高性能和灵活性需求。

MongoDB使用集合(Collections)来组织文档(Documents),每个文档都是由键值对组成的。

  • 数据库(Database):存储数据的容器,类似于关系型数据库中的数据库。
  • 集合(Collection):数据库中的一个集合,类似于关系型数据库中的表。
  • 文档(Document):集合中的一个数据记录,类似于关系型数据库中的行(row),以 BSON 格式 存储。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成,文档类似于 JSON 对象,字段值 可以包含其他文档,数组及文档数组:

MongoDB数据存储

2.2、安装MongoDB

服务器: mongodb-windows-x86_64-8.0.6-signed.msihttps://www.mongodb.com/try/download/community

命令行客户端 : mongosh-2.5.0-win32-x64.ziphttps://www.mongodb.com/try/download/shell

图形客户端: mongodb-compass-1.39.3-win32-x64.exehttps://www.mongodb.com/try/download/compass

2.3、使用mongosh

启动 MongoDB Shell:

在命令行中输入 mongosh 命令,启动 MongoDB Shell,如果 MongoDB 服务器运行在本地默认端口 (27017),则可以直接连接

mongosh

连接到 MongoDB 服务器: 如果 MongoDB 服务器运行在非默认端口或者远程服务器上,可以使用以下命令连接:

mongosh --host <hostname>:<port>

其中 <hostname> 是 MongoDB 服务器的主机名或 IP 地址,<port>是 MongoDB 服务器的端口号。

执行基本操作:

连接成功后,可以执行各种 MongoDB 数据库操作。例如:

  • 查看当前数据库:db
  • 显示数据库列表:show dbs
  • 切换到指定数据库:use <database_name>
  • 执行查询操作: db.<collection_name>.find()
  • 插入文档: db.<collection_name>.insertOne({ ... })
  • 更新文档:db.<collection_name>.updateOne({ ... })
  • 删除文档:db.<collection_name>.deleteOne({ ... })
  • 退出 MongoDB Shell:quit() 或者 exit

CRUD

# 插入文档
db.mycollection.insertOne({ name: "Alice", age: 30 })
 # 查询文档
db.mycollection.find()
 # 更新文档
db.mycollection.updateOne({ name: "Alice" }, { $set: { age: 31 } })
 # 删除文档
db.mycollection.deleteOne({ name: "Alice" })
 # 退出 MongoDB Shell
 test>quit()

2.4、使用mongodb-compass

可以通过mongodb-compass可视化工具直接操作mongoDB的集合。

2.5、整合SpringBoot

引入MongoDB依赖:

<!-- Spring Boot Starter Data MongoDB -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

添加远程连接配置:

#MongoDB连接配置
spring:
  data:
    mongodb:
            uri: mongodb://localhost:27017/chat_memory_db

如果新增了用户与密码使用如下:

spring:
  data:
    mongodb:
      uri: mongodb://mongo:123456@10.11.12.13:27017/chat_memory_db?authSource=admin

2.6、CRUD测试

创建实体类:映射MongoDB中的文档(相当与MySQL的表)

package com.wwj.java.ai.langchain4j.bean;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chat_messages")
public class ChatMessages {
	//唯一标识,映射到 MongoDB 文档的 _id 字段
	@Id
	private ObjectId messageId;
	//private Long messageId;
	
	private String content; //存储当前聊天记录列表的json字符串
}

创建测试类:

package com.wwj.java.ai.langchain4j;

import com.wwj.java.ai.langchain4j.bean.ChatMessages;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

@SpringBootTest
public class MongoCrudTest {
    @Autowired
    private MongoTemplate mongoTemplate;
    /**
	 * 插入文档
	 */
    //    @Test
    //    public void testInsert() {
    //        mongoTemplate.insert(new ChatMessages(1L, "聊天记录"));
    //    }

    /**
	 * 插入文档
	 */
    @Test
    public void testInsert2() {
        ChatMessages chatMessages = new ChatMessages();
        chatMessages.setContent("聊天记录列表");
        mongoTemplate.insert(chatMessages);
    }

    /**
	 * 根据id查询文档
	 */
    @Test
    public void testFindById() {
        ChatMessages chatMessages = mongoTemplate.findById("680e4cfc53eeba4ef57db43f",
                                                           ChatMessages.class);
        System.out.println(chatMessages);
    }

    /**
	 * 修改文档
	 */
    @Test
    public void testUpdate() {
        Criteria criteria = Criteria.where("_id").is("680e4cfc53eeba4ef57db43f");
        Query query = new Query(criteria);
        Update update = new Update();
        update.set("content", "新的聊天记录列表");
        // 修改或新增
        mongoTemplate.upsert(query, update, ChatMessages.class);
    }

    /**
	 * 删除文档
	 */
    @Test
    public void testDelete() {
        Criteria criteria = Criteria.where("_id").is("680e4cfc53eeba4ef57db43f");
        Query query = new Query(criteria);
        mongoTemplate.remove(query, ChatMessages.class);
    }
}

3、持久化聊天

3.1、优化实体类

package com.wwj.java.ai.langchain4j.bean;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chat_messages")
public class ChatMessages {
	//唯一标识,映射到 MongoDB 文档的 _id 字段
	@Id
	private ObjectId id;
	
	private int messageId;
	
	//存储当前聊天记录列表的json字符串
	private String content;
}

3.2、创建持久化类

创建一个类实现ChatMemoryStore接口

package com.wwj.java.ai.langchain4j.store;

import com.wwj.java.ai.langchain4j.bean.ChatMessages;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.LinkedList;
import java.util.List;

@Component
public class MongoChatMemoryStore implements ChatMemoryStore {
	@Autowired
	private MongoTemplate mongoTemplate;
	
	@Override
	public List<ChatMessage> getMessages(Object memoryId) {
		Criteria criteria = Criteria.where("memoryId").is(memoryId);
		Query query = new Query(criteria);
		ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class);
		if (chatMessages == null) return new LinkedList<>();
		return ChatMessageDeserializer.messagesFromJson(chatMessages.getContent());
	}
	
	@Override
	public void updateMessages(Object memoryId, List<ChatMessage> list) {
		Criteria criteria = Criteria.where("memoryId").is(memoryId);
		Query query = new Query(criteria);
		Update update = new Update();
		update.set("content", ChatMessageSerializer.messagesToJson(list));
		// 修改或新增
		mongoTemplate.upsert(query, update, ChatMessages.class);
	
	}
	
	@Override
	public void deleteMessages(Object memoryId) {
		Criteria criteria = Criteria.where("memoryId").is(memoryId);
		Query query = new Query(criteria);
		mongoTemplate.remove(query, ChatMessages.class);
	}
}

SeparateChatAssistantConfig中,添加MongoChatMemoryStore对象的配置

package com.wwj.java.ai.langchain4j.config;

import com.wwj.java.ai.langchain4j.store.MongoChatMemoryStore;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatMemoryProviderConfig {
	
	// 注入持久化对象
	@Autowired
	private MongoChatMemoryStore mongoChatMemoryStore;
	
	@Bean
	public ChatMemoryProvider chatMemoryProvider() {
		return memoryId -> MessageWindowChatMemory
				.builder()
				.id(memoryId)
				.maxMessages(10)
//				.chatMemoryStore(new InMemoryChatMemoryStore())
				.chatMemoryStore(mongoChatMemoryStore)
				.build();
	}
}

4、测试

发现MongoDB中已经存储了会话记录

[
  {
    "_id": {"$oid": "680f2346b055f44aa7fbcd22"},
    "content": "[{\"contents\":[{\"text\":\"我是张三\",\"type\":\"TEXT\"}],\"type\":\"USER\"},{\"text\":\"你好,张三!很高兴见到你。有什么我可以帮助你的吗?\",\"type\":\"AI\"},{\"contents\":[{\"text\":\"我是谁?\",\"type\":\"TEXT\"}],\"type\":\"USER\"},{\"text\":\"你刚才告诉我你是张三。如果你是在问其他身份相关的信息,或者有其他问题需要帮助,请告诉我更多的细节哦!\",\"type\":\"AI\"}]",
    "memoryId": 1
  },
  {
    "_id": {"$oid": "680f234cb055f44aa7fbcd2f"},
    "content": "[{\"contents\":[{\"text\":\"我是谁?\",\"type\":\"TEXT\"}],\"type\":\"USER\"},{\"text\":\"您好!在这里,我看到的是一个使用此平台提问的用户。不过,您的具体身份信息我并不知晓,因为我被设计为尊重每一位用户的隐私。如果您是在寻找自我介绍的帮助,或者有其他相关问题想要讨论,请告诉我更多细节,我会尽力提供帮助!\",\"type\":\"AI\"}]",
    "memoryId": 2
  }
]

六、提示词 Prompt

1、系统提示词

@SystemMessage 设定角色,塑造AI助手的专业身份,明确助手的能力范围

1.1、配置@SystemMessage

在SeparateChatAssistant类的chat方法上添加@SystemMessage注解

@SystemMessage("你是我的好朋友,请用东北话回答问题。")//系统消息提示词
String chat(@MemoryId int memoryId, @UserMessage String userMessage);

@SystemMessage 的内容将在后台转换为 SystemMessage 对象,并与UserMessage一起发送给大语言模型(LLM)。

SystemMessaged的内容只会发送给大模型一次。

如果你修改了SystemMessage的内容,新的SystemMessage会被发送给大模型,之前的聊天记忆会失效。

1.2、测试

package com.wwj.java.ai.langchain4j;

import com.wwj.java.ai.langchain4j.assistant.SeparateChatAssistant;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class PromptTest {
    @Autowired
    private SeparateChatAssistant separateChatAssistant;

    @Test
    public void testSystemMessage() {
        String answer = separateChatAssistant.chat(3, "今天几号");
        System.out.println(answer);
    }
}

哎呀,你这问我我还真不知道,你得自个儿瞅一眼手机或者日历啥的。我这儿也没法实时给你报时啊!哈哈。

如果要显示今天的日期,我们需要在提示词中添加当前日期的占位符

 @SystemMessage("你是我的好朋友,请用东北话回答问题。今天是{{current_date}}")//系统消息提示词
String chat(@MemoryId int memoryId, @UserMessage String userMessage);

哎呀,今儿个是2025年4月28号,你这记性咋跟金鱼似的呢!

1.3、从资源中加载提示模板

@SystemMessage 注解还可以从资源中加载提示模板:

@SystemMessage(fromResource = "my-prompt-template.txt")
 String chat(@MemoryId int memoryId, @UserMessage String userMessage);

my-prompt-template.txt (文件放在 resources中)

你是我的好朋友,请用东北话回答问题,回答问题的时候适当添加表情符号。

表示当前日期

你是我的好朋友,请用上海话回答问题,回答问题的时候适当添加表情符号。

今天是 。

2、用户提示词模板

@UserMessage:获取用户输入提示词;用户提示词会在每次对话时携带

2.1、配置@UserMessage

MemoryChatAssistantchat 方法中添加注解

// {{it}}表示这里唯一的参数的占位符
@UserMessage("你是我的好朋友,请用上海话回答问题,并且添加一些表情符号。 {{it}}")
String chat(String message);

2.2、测试

@Autowired
private MemoryChatAssistant memoryChatAssistant;
@Test
public void testUserMessage() {
    String answer = memoryChatAssistant.chat("我是环环");
    System.out.println(answer);
}

嗨,环环!侬好呀~ 🌟 有啥事儿要问我伐?我一直拉海啊,准备帮侬~ 😊

3、指定参数名称

3.1、配置@V

@V 明确指定传递的参数名称

@UserMessage("你是我的好朋友,请用上海话回答问题,并且添加一些表情符号。{{message}}")
 String chat(@V("message") String userMessage);

3.2、多个参数的情况

如果有两个或两个以上的参数,我们必须要用@V,在 SeparateChatAssistant 中定义方法 chat2

@UserMessage("你是我的好朋友,请用粤语回答问题。{{message}}")
String chat2(@MemoryId int memoryId, @V("message") String userMessage);

测试: @UserMessage 中的内容每次都会被和用户问题组织在一起发送给大模型

@Test
public void testV() {
    String answer1 = separateChatAssistant.chat2(10, "我是环环");
    System.out.println(answer1);
    String answer2 = separateChatAssistant.chat2(10, "我是谁");
    System.out.println(answer2);
}

3.3、@SystemMessage和@V

也可以将 @SystemMessage@V 结合使用

SeparateChatAssistant 中添加方法chat3

@SystemMessage(fromResource = "my-prompt-template3.txt")
String chat3(
    @MemoryId int memoryId, 
    @UserMessage String userMessage, 
    @V("username") String username, 
    @V("age") int age
);

创建提示词模板my-prompt-template3.txt,添加占位符

你是我的好朋友,我是,我的年龄是,请用东北话回答问题,回答问题的时候适当添加表情 符号。

今天是 。

测试:

@Test
public void testUserInfo() {
    String answer = separateChatAssistant.chat3(1, "我是谁,我多大了", "翠花", 18);
    System.out.println(answer);
}

七、项目实战-创建硅谷小智

这部分我们实现硅谷小智的基本聊天功能,包含聊天记忆、聊天记忆持久化、提示词

1、创建硅谷小智

创建XiaozhiAgent

package com.wwj.java.ai.langchain4j.assistant;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
    wiringMode = EXPLICIT,
    chatModel = "qwenChatModel",
    chatMemoryProvider = "chatMemoryProviderXiaozhi")
public interface XiaozhiAgent {
    @SystemMessage(fromResource = "zhaozhi-prompt-template.txt")
    String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}

2、提示词模板

zhaozhi-prompt-template.txt

你的名字是“硅谷小智”,你是一家名为“北京协和医院”的智能客服。
你是一个训练有素的医疗顾问和医疗伴诊助手。
你态度友好、礼貌且言辞简洁。

1、请仅在用户发起第一次会话时,和用户打个招呼,并介绍你是谁。

2、作为一个训练有素的医疗顾问:
请基于当前临床实践和研究,针对患者提出的特定健康问题,提供详细、准确且实用的医疗建议。请同时考虑可能的病因、诊断流程、治疗方案以及预防措施,并给出在不同情境下的应对策略。对于药物治疗,请特别指明适用的药品名称、剂量和疗程。如果需要进一步的检查或就医,也请明确指示。

3、作为医疗伴诊助手,你可以回答用户就医流程中的相关问题,主要包含以下功能:
AI分导诊:根据患者的病情和就医需求,智能推荐最合适的科室。
AI挂号助手:实现智能查询是否有挂号号源服务;实现智能预约挂号服务;实现智能取消挂号服务。

4、你必须遵守的规则如下:
在获取挂号预约详情或取消挂号预约之前,你必须确保自己知晓用户的姓名(必选)、身份证号(必选)、预约科室(必选)、预约日期(必选,格式举例:2025-04-14)、预约时间(必选,格式:上午 或 下午)、预约医生(可选)。
当被问到其他领域的咨询时,要表示歉意并说明你无法在这方面提供帮助。

5、请在回答的结果中适当包含一些轻松可爱的图标和表情。

6、今天是 {{current_date}}。

3、配置小智助手

配置持久化和记忆隔离

package com.wwj.java.ai.langchain4j.config;

import com.wwj.java.ai.langchain4j.store.MongoChatMemoryStore;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class chatMemoryProviderXiaozhiConfig {

    @Autowired
    private MongoChatMemoryStore mongoChatMemoryStore;

    @Bean
    public ChatMemoryProvider chatMemoryProviderXiaozhi() {
        return memoryId ->
            MessageWindowChatMemory.builder()
            .id(memoryId)
            .maxMessages(20)
            .chatMemoryStore(mongoChatMemoryStore)
            .build();

    }
}

4、封装对话对象

package com.wwj.java.ai.langchain4j.bean;

import lombok.Data;

@Data
public class ChatForm {
    private Long memoryId;//对话id
    private String message;//用户问题
}

5、添加Controller方法

package com.wwj.java.ai.langchain4j.controller;

import com.wwj.java.ai.langchain4j.assistant.XiaozhiAgent;
import com.wwj.java.ai.langchain4j.bean.ChatForm;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "硅谷小智")
@RestController
@RequestMapping("/xiaozhi")
public class XiaozhiController {

    @Autowired
    private XiaozhiAgent xiaozhiAgent;

    @Operation(summary = "对话")
    @PostMapping("/chat")
    public String chat(@RequestBody ChatForm chatForm) {
        return xiaozhiAgent.chat(chatForm.getMemoryId(), chatForm.getMessage());
    }

}

6、待优化

信息查询:提示词中还应该提供医院信息(如位置信息,营业时间等)、科室信息(都有哪些科室)、医生信息(都有哪些医生)

业务实现:预约、取消预约、查询是否预约等

信息查询可以使用RAG检索增强生成

业务实现需要通过 Function Calling 函数调用

八、 Function Calling 函数调用

Function Calling 函数调用 也叫 Tools 工具

1、入门案例

例如,大语言模型本身并不擅长数学运算。如果应用场景中偶尔会涉及到数学计算,我们可以为他提供 一个 “数学工具”。当我们提出问题时,大语言模型会判断是否使用某个工具。

1.1、创建工具类

用 @Tool 注解的方法:

  • 既可以是静态的,也可以是非静态的;
  • 可以具有任何可见性(公有、私有等)。

硅谷小智
https://junyyds.top/2025/04/22/硅谷小智/
作者
Phils
发布于
2025年4月22日
更新于
2025年4月29日
许可协议