大模型:DeepSeek、阿里云百炼、ollama
下面以DeepSeek为例,进行学习
一、快速使用
1.添加依赖
在pom.xml添加
<dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-deepseek</artifactId> </dependency> </dependencies>2.配置API key和模型
在application.properties添加
spring.ai.deepseek.api-key = ${DEEP_SEEK_KEY} spring.ai.deepseek.chat.options.model = deepseek-chat3.测试
(1)同步阻塞式输出
@Test public void testDeepseek(@Autowired DeepSeekChatModel deepSeekChatModel){ String content = deepSeekChatModel.call("你好你是谁"); System.out.println(content); }(2)流式输出
@Test public void testDeepseekStream(@Autowired DeepSeekChatModel deepSeekChatModel){ Flux<String> stream = deepSeekChatModel.stream("你好你是谁"); stream.toIterable().forEach(System.out::println); }二、options配置选项
(1)temperature
范围:0-2,值越大,输出内容相似度越低-越热情;反之相似度越高-越冷淡
(2)maxTokens
限制生成的最大token数
(3)stop
截断你不想输出的内容
@Test public void testChatOptions(@Autowired DeepSeekChatModel deepSeekChatModel{ DeepSeekChatOptions options = DeepSeekChatOptions.builder() .model("deepseek-chat") // 指定大模型 //.maxTokens(5) // 限制字数 .stop(Arrays.asList(",")) // 从逗号截断 .temperature(1.9).build(); Prompt prompt = new Prompt("请写一句诗描述清晨。", options); ChatResponse res = deepSeekChatModel.call(prompt); System.out.println(res.getResult().getOutput().getText()); }三、深度思考
1.修改application.properties
spring.ai.deepseek.api-key = ${DEEP_SEEK_KEY} spring.ai.deepseek.chat.options.model = deepseek-reasoner2.测试
(1)同步阻塞式输出
@Test public void testDeepseekReasoning(@Autowired DeepSeekChatModel deepSeekChatModel{ Prompt prompt = new Prompt("你好你是谁"); ChatResponse res = deepSeekChatModel.call(prompt); DeepSeekAssistantMessage assistantMessage = (DeepSeekAssistantMessage)res.getResult().getOutput(); System.out.println(assistantMessage.getReasoningContent); // 思考内容 System.out.println(assistantMessage.getText()); // 最终回答内容 }(2)流式输出
@Test public void testDeepseekStream(@Autowired DeepSeekChatModel deepSeekChatModel{ Prompt prompt = new Prompt("你好你是谁"); Flux<ChatResponse> stream = deepSeekChatModel.stream(prompt); stream.toIterable().forEach(chatResponse -> { DeepSeekAssistantMessage assistantMessage = (DeepSeekAssistantMessage)chatResponse.getResult().getOutput(); System.out.println(assistantMessage.getReasoningContent()); // 思考内容 }); stream.toIterable().forEach(chatResponse -> { DeepSeekAssistantMessage assistantMessage = (DeepSeekAssistantMessage)chatResponse.getResult().getOutput(); System.out.println(assistantMessage.getText()); // 最终回答内容 }); }插入一个概念
多模态:将图片、语音、视频传给大模型,让大模型识别后生成文本返回
四、ChatClient
由于我们对接大模型使用的ChatModel是不同的,而ChatClient适用所用的大模型,更加便捷
(1)只有一个大模型
@Test public void testChatClient(@Autowired ChatClient.Builder chatClientBuilder){ ChatClient chatClient = chatClientBuilder.build(); String content = chatClient.prompt() .user("你好") .call() .content(); System.out.println(content); }(2)有多个大模型
@Test public void testChatClient(@Autowired DashScopeChatModel dashScopeChatModel){ // 使用哪个模型就传入哪个模型 ChatClient chatClient = ChatClient.builder(dashScopeChatModel).build(); String content = chatClient.prompt() .user("你好") .call() .content(); System.out.println(content); }五、提示词
1.系统提示词
为ChatClient设置提示词,只要用到这个chatCilent的对话都会用到这个提示词
@Test public void testChatClient(@Autowired ChatClient.Builder chatClientBuilder){ ChatClient chatClient = chatClientBuilder .defaultSystem(""" #角色说明 你是一名专业法律顾问AI.. ## 回复格式 1.问题分析 2.相关依据 3.梳理和建议 **特别注意:** -不承担律师责任。 -不生成涉敏、虚假内容。 """) .build(); String content = chatClient.prompt() .user("你好") .call() .content(); System.out.println(content); }为当前对话设置提示词
@Test public void testChatClient(@Autowired ChatClient.Builder chatClientBuilder){ ChatClient chatClient = chatClientBuilder .build(); String content = chatClient.prompt() .system(""" #角色说明 你是一名专业法律顾问AI.. ## 回复格式 1.问题分析 2.相关依据 3.梳理和建议 **特别注意:** -不承担律师责任。 -不生成涉敏、虚假内容。 """) .user("你好") .call() .content(); System.out.println(content); }2.动态传参
@Test public void testChatClient(@Autowired ChatClient.Builder chatClientBuilder){ ChatClient chatClient = chatClientBuilder .defaultSystem(""" #角色说明 你是一名专业法律顾问AI.. ## 回复格式 1.问题分析 2.相关依据 3.梳理和建议 **特别注意:** -不承担律师责任。 -不生成涉敏、虚假内容。 当前服务的用户: 姓名:{name},年龄:{age} """) .build(); String content = chatClient.prompt() .system(p -> p.param("name","张三").param("age","22")) .user("你好") .call() .content(); System.out.println(content); }3.提示词模版
(1)新建prompt.st,将提示词写入
(2)导入
@Test public void testChatClient(@Autowired ChatClient.Builder chatClientBuilder,@Value("classpath:/files/prompt.st") Resource systemResource){ ChatClient chatClient = chatClientBuilder .defaultSystem(systemResource) .build(); String content = chatClient.prompt() .system(p -> p.param("name","张三").param("age","22")) .user("你好") .call() .content(); System.out.println(content); }4.公式
「角色设定」+「具体任务(技能)」+「限制条件(约束)」+「示例参考」
六、Advisor
1.日志拦截器
ChatClient chatClient = chatClientBuilder .defaultAdvisors(new SimpleLoggerAdvisor()) .build();直接执行是没有日志的,需要打开日志级别,才会显示。在application.properties添加
logging.level.org.springframework.ai.chat.client.advisor = DEBUG2.敏感词拦截器
当用户输入“李四”时,不会调用大模型,直接抛错,所以相应速度会很快
ChatClient chatClient = chatClientBuilder .defaultAdvisors(new SimpleLoggerAdvisor(), new SafeGuardAdvisor(List.of("李四"))) .build();3.自定义拦截器
public class ReReadingAdvisor implements BaseAdvisor { private static final String DEFAULT_USER_TEXT_ADVISE = """ {re2_input_query} Read the question again: {re2_input_query} """; @Override public ChatClientRequest before(ChatClientRequest chatClientRequest,AdvisorChain advisorChain){ // 用户提示词 String contents = chatClientRequest.prompt().getContents(); // 替换占位符中的内容 String re2InputQuery = PromptTemplate.builder() .template(DEFAULT_USER_TEXT_ADVISE) .render(Map.of("re2_input_query", contents)); // 新建一个ChatClientRequest返回 ChatClientRequest clientRequest = chatClientRequest.mutate() .prompt(Prompt.builder().content(re2InputQuery).build()) .build(); return clientRequest; } }ChatClient chatClient = chatClientBuilder .defaultAdvisors(new SimpleLoggerAdvisor(), new ReReadingAdvisor()) .build();七、会话记忆
LLM是无状态的,不会保留先前的交互信息
通过defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemoryD.build())存储会话
1.添加依赖
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-autoconfigure-model-chat-memory</artifactId> </dependency>2.测试
@Test public void testMemoryAdvisor(@Autowired ChatClient.Builder builder, @Autowired ChatMemory chatMemory) { ChatClient chatClient = builder .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemoryD.build()) .build(); String content = chatclient.prompt() .user("我是张三") .call() .content(); System.out.println(content); System.out.println("–----------------"); content = chatClient.prompt() .user("我叫什么?") .call() .content(); System.out.println(content); }3.记忆最大存储数量
默认的消息窗口大小为20条消息
如果第11条会话进来,那么就会从第2条会话开始展示,确保最大只展示10条(先进先出原则)
MessageWindowChatMemory memory = MessageWindowChatMemory.builder() .maxMessages(10) .build();4.多用户记忆隔离
如果没有配置记忆隔离,那么所有用户都会共享这一份聊天记录
通过advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION, "1"))进行配置
@Test public void testMemoryAdvisor(@Autowired ChatClient.Builder builder, @Autowired ChatMemory chatMemory) { ChatClient chatClient = builder .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemoryD.build()) .build(); String content = chatclient.prompt() .user("我是张三") .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION, "1")) .call() .content(); System.out.println(content); System.out.println("–----------------"); content = chatClient.prompt() .user("我叫什么?") .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION, "1")) .call() .content(); System.out.println(content); System.out.println("–----------------"); content = chatClient.prompt() .user("我叫什么?") .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION, "2")) .call() .content(); }八、数据库存储对话记忆
Spring AI 通过方言抽象支持多种关系型数据库。开箱即支持以下数据库:
PostgreSQL
MySQL / MariaDB
SQL Server
HSQLDB
Oracle数据库
1.数据库存储
1.导入依赖
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mysql驱动--> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>2.添加配置
spring.ai.chat.memory.repository.jdbc.initialize-schema=always <!--如果要覆盖架构脚本的位置--> spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql3.连接数据库
spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/springai?characterEncoding=utf8&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver2.Redis存储
1.导入依赖
<properties> <jedis.version>5.2.0</jedis.version> </properties> <!--基于redis记忆存储--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId> </dependency>2.连接数据库
ai: memory: redis: host: localhost port: 6379 timeout: 5000 password: 123456九、多层记忆架构
当记忆越多的时候,说明越聪明,同时带来的问题是token上限。为解决此问题,使用多层记忆架构,具体如下:
近期记忆:保留在上下文窗口中的最近几轮对话,每轮对话完成后立即存储(可通过
ChatMemory);
中期记忆:通过RAG检索的相关历史对话(每轮对话完成后,异步将对话内容转换为向量并存入
向量数据库) ------>向量数据库进行相似性检索,然后提取与对话相关的内容出来
长期记忆:关键信息的固化总结
方式一:定时批处理
- 通过定时任务(如每天或每周)对积累的对话进行总结和提炼
- 提取关键信息、用户偏好、重要事实等
- 批处理方式降低计算成本,适合大规模处理
方式二:关键点实时处理
- 在对话中识别出关键信息点时立即提取并存储
- 例如,当用户明确表达偏好、提供个人信息或设置持久性指令时
- 采用"写入触发器"机制,在特定条件下自动更新长期记忆
十、Tool
定义:用于提供大模型不具备的信息和能力
1.使用
(1)提供了什么工具@Tool
(2)使用工具需要什么参数@ToolParam
@Tool(description ="获取指定位置天气,根据位置自动推算经纬度") public String getAirQuality(@ToolParam(description ="纬度") double latitude, @ToolParam(description ="经度") double longitude) { return "天晴"; }