SpringAI入门及浅实践,实战 Spring‎ AI 调用大模型、提示词工程、对话记忆、Adv‎isor 的使用

上一次写AI学习笔记已经好久之前了,温习温习,这一章讲讲关于Spring‎ AI 调用大模型、对话记忆、Adv‎isor、结构化输出、自定义对话记忆‍、Prompt 模板的相关知识点。

快速跳转到你感兴趣的地方

  • 一、提示词工程(Prompt)
    • 1. 基本概念 (提示词、提示词工程)
    • 2. 分类 (基于角色、功能)
    • 3. Prompt 优化技巧
  • 二、对话记忆(多轮对话实现)
    • 1. 使用 Chat Client 实现基础对话功能
    • 2. Advisor 顾问的使用
  • 三、SpringAI会话记忆、Advisor实战
    • 1. 使用 InMemoryChatMemory:内存存储 实现对话记忆
    • 2. 自定义Advisor的使用
    • 3. 使用自定义 Advisor 实现自定义日志 Advisor
  • 四、结语(会不断补充,可以收藏一下)

一、提示词工程(Prompt)

1. 基本概念 (提示词、提示词工程)

提示词就是用户输入给AI的指令,但是写好提示词,是一件十分不容易的事情。准确的提示词能够让AI清楚的明白你所要达到的目的,从而生成满意的内容。
提示词工程就是通过优化输入给AI模型的指令或问题(即“提示词”),以提升模型输出的准确性和相关性。其核心在于理解模型的行为模式,并通过结构化语言引导模型生成更符合预期的结果。

2. 分类 (基于角色、功能)

按照角色来分类可以分为如下三类:

  • 用户 Prompt
    是用户直接向 AI 提出的具体需求或问题,例如请求写诗、解答问题或生成代码。其核心是传递用户的具体意图,要求 AI 执行明确的任务。
  • 系统 Prompt
    是开发者或平台设定的隐藏指令,用于定义 AI 的角色、行为边界和回答风格。它不直接面向用户,但决定了 AI 如何响应用户 Prompt。例如,设定 AI 为“专业诗人”或“情感顾问”会直接影响生成内容的专业性、语气和结构。
  • 助手 Prompt
    在多轮对话中,助手 Prompt(即 AI 的历史回复)会成为后续交互的上下文。预设的助手消息能引导对话方向,例如在客服场景中提前声明服务范围。这种机制增强了对话连贯性,但也需注意避免因历史信息导致回答偏差。

按照功能来分可以分为:

  • 指令型提؜示词: 明确告诉 AI‎ 模型需要执行的任务,‍通常以命令式语句开头。
将上述中文文本翻译为英文
  • 对话型؜提示词: 模拟‎自然对话,以问答形式‍与 AI 模型交互。
你认为AI会在取代人类吗?
  • 创意型؜提示词 引导 AI 模型‎进行创意内容生成,如‍故事、诗歌、广告文案等。
写一首描写爱情的诗歌,赞美矢志不渝伟大的爱情
  • 角色扮؜演提示词 ‎让 AI 扮演特定‍角色或人物进行回答。
你是一位使用Java的程序员,使用java编写一个贪吃蛇游戏
  • 少样本؜学习提示词 提供一些示例‎,引导 AI 理解所‍需的输出格式和风格。
将以下句子改写为正式商务语言:
示例1:
原句:这个想法不错。
改写:该提案展现了相当的潜力和创新性。示例2:
原句:我们明天见。
改写:期待明日与您会面,继续我们的商务讨论。现在请改写:这个价格太高了。

3. Prompt 优化技巧

网上和 Pro؜mpt 优化相关的资源非常丰富,几乎各大主流 AI ‎大模型和 AI 开发框架官‎方文档都有相关的介绍。Spring AI 提示工程指南,智谱 AI Prompt 设计指南等等
这里讲讲我学习整理后的一些心得

这是springAI的提示词编写教程中的重点,其实重点在三个方面:
在开发提示时,重要的是要集成几个关键组件,以确保清晰度和有效性:

  • 指令:向AI提供清晰而直接的指令,类似于你与人沟通的方式。这种清晰度对于帮助AI“理解”预期内容至关重要。
  • 外部环境:在必要时,为人工智能的反应提供相关的背景信息或具体指导。这种“外部环境”构成了提示,并帮助AI掌握整个场景。
  • 用户输入:这是最直接的部分——用户的直接请求或问题

只要说明白这三点,就算是一个合适的提示词,但是是远远不够的,这里我用AI整理出了优化的重点,感兴趣的可以看一下。


清晰具体地定义需求,避免模糊指令,确保模型准确理解任务目标明确指定输出格式,如JSON或特定结构,便于后续处理或解析将复杂任务拆解为多个简单子任务,逐步递进完成,提高准确率使用分隔符区分不同输入部分,避免混淆,如"""文本内容"""在需要推理的问题中要求展示思维链,分步骤呈现推导过程通过少样本示例引导模型模仿特定风格或行为模式控制输出长度,设定字数限制或篇幅要求,确保简洁性对长文本采用分段归纳再整合的方法,突破上下文限制在技术性任务中先让模型自主生成答案,再进行评估验证必要时隐藏详细推理过程,直接输出最终结果或结构化数据利用外部工具增强能力,如API查询实时数据或访问知识库在长对话中定期总结关键信息,保持上下文连贯性角色扮演方式可提升专业领域回答的准确性为系统提示设定明确的行为模式和任务规范### 优化Prompt的技巧清晰具体地定义需求,避免模糊指令,确保模型准确理解任务目标明确指定输出格式,如JSON或特定结构,便于后续处理或解析将复杂任务拆解为多个简单子任务,逐步递进完成,提高准确率使用分隔符区分不同输入部分,避免混淆,如"""文本内容"""在需要推理的问题中要求展示思维链,分步骤呈现推导过程通过少样本示例引导模型模仿特定风格或行为模式控制输出长度,设定字数限制或篇幅要求,确保简洁性对长文本采用分段归纳再整合的方法,突破上下文限制在技术性任务中先让模型自主生成答案,再进行评估验证必要时隐藏详细推理过程,直接输出最终结果或结构化数据利用外部工具增强能力,如API查询实时数据或访问知识库在长对话中定期总结关键信息,保持上下文连贯性角色扮演方式可提升专业领域回答的准确性为系统提示设定明确的行为模式和任务规范### 优化Prompt的技巧清晰具体地定义需求,避免模糊指令,确保模型准确理解任务目标明确指定输出格式,如JSON或特定结构,便于后续处理或解析将复杂任务拆解为多个简单子任务,逐步递进完成,提高准确率使用分隔符区分不同输入部分,避免混淆,如"""文本内容"""在需要推理的问题中要求展示思维链,分步骤呈现推导过程通过少样本示例引导模型模仿特定风格或行为模式控制输出长度,设定字数限制或篇幅要求,确保简洁性对长文本采用分段归纳再整合的方法,突破上下文限制在技术性任务中先让模型自主生成答案,再进行评估验证必要时隐藏详细推理过程,直接输出最终结果或结构化数据利用外部工具增强能力,如API查询实时数据或访问知识库在长对话中定期总结关键信息,保持上下文连贯性角色扮演方式可提升专业领域回答的准确性为系统提示设定明确的行为模式和任务规范

二、对话记忆(多轮对话实现)

1. 使用 Chat Client 实现基础对话功能

参考 Spring AI 的官方文档,了解到 Spring AI 提供了 ChatClient API 来和 AI 大模型交互。

ChatClient 是使用 ChatClient.Builder 对象创建的。您可以为任何 ChatModel Spring Boot 自动配置获取一个自动配置的 ChatClient.Builder 实例,也可以通过编程方式创建一个

这是官方提供的通过构造器注入的事例:

// 方式1:使用构造器注入
@RestController
class MyController {private final ChatClient chatClient;public MyController(ChatClient.Builder chatClientBuilder) {this.chatClient = chatClientBuilder.build();}@GetMapping("/ai")String generation(String userInput) {return this.chatClient.prompt().user(userInput).call().content();}
}

使用建造者模式

// 方式2:使用建造者模式
ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("you are a helpful AI").build();

ChatC؜lient 支持多种响应格式,比如返‎回 ChatRes‎ponse 对象、‍返回实体对象、流式返回:

// ChatClient支持多种响应格式
// 1. 返回 ChatResponse 对象(包含元数据如 token 使用量)
ChatResponse chatResponse = chatClient.prompt().user("Tell me a joke").call().chatResponse();// 2. 返回实体对象(自动将 AI 输出映射为 Java 对象)
// 2.1 返回单个实体
record ActorFilms(String actor, List<String> movies) {}
ActorFilms actorFilms = chatClient.prompt().user("Generate the filmography for a random actor.").call().entity(ActorFilms.class);// 2.2 返回泛型集合
List<ActorFilms> multipleActors = chatClient.prompt().user("Generate filmography for Tom Hanks and Bill Murray.").call().entity(new ParameterizedTypeReference<List<ActorFilms>>() {});// 3. 流式返回(适用于打字机效果)
Flux<String> streamResponse = chatClient.prompt().user("Tell me a story").stream().content();// 也可以流式返回ChatResponse
Flux<ChatResponse> streamWithMetadata = chatClient.prompt().user("Tell me a story").stream().chatResponse();

可以给 C؜hatClient 设置默认参数,比如系‎统提示词,还可以在对‎话时动态更改系统提示‍词的变量,类似模板的概念:

// 定义默认系统提示词
ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}").build();// 对话时动态更改系统提示词的变量
chatClient.prompt().system(sp -> sp.param("voice", voice)).user(message).call().content());

2. Advisor 顾问的使用

Advisor在Spring AI中扮演着类似AOP(面向切面编程)中顾问的角色,用于在 AI 模型调用前后添加逻辑以增强 AI 的能力。它允许开发者在不修改核心代码的情况下,对AI请求进行预处理或后处理,例如日志记录、对话记忆、RAG等。

  • 前置增强:调用 AI 前改写一下 Prompt 提示词、检查一下提示词是否安全
  • 后置增强:调用 AI 后记录一下日志、处理一下返回的结果

比如对话记忆顾问 Me‎ssageChatMemoryAdv‎isor 可以帮助我们实现多轮对话能‍力,省去了自己维护对话列表的麻烦。

var chatClient = ChatClient.builder(chatModel).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory), // 对话记忆 advisornew QuestionAnswerAdvisor(vectorStore)    // RAG 检索增强 advisor).build();String response = this.chatClient.prompt()// 对话时动态设定拦截器参数,比如指定对话记忆的 id 和长度.advisors(advisor -> advisor.param("chat_memory_conversation_id", "678").param("chat_memory_response_size", 100)).user(userText).call().content();

Advisors 的原理图如下:
在这里插入图片描述
在这里插入图片描述
实际开发中,往往我们会用到多个拦截器,组合在一起相当于一条拦截器链条(责任链模式的设计思想)。每个拦截器是有顺序的,通过 getOrder() 方法获取到顺序,得到的值越低,越优先执行。

想要实现对话记忆功能,可以使用 Spring‎ AI 的 ChatMe‎moryAdvisor,‍它主要有几种内置的实现方式:

  • MessageChatMemoryAdvisor:从记忆中检索历史对话,并将其作为消息集合添加到提示词中
  • PromptChatMemoryAdvisor:从记忆中检索历史对话,并将其添加到提示词的系统文本中
  • VectorStoreChatMemoryAdvisor:可以用向量数据库来存储检索历史对话

Messag؜eChatMemoryAdvisor 和 Pro‎mptChatMemor‎yAdvisor 用法类‍似,但是略有一些区别:

  1. Messag؜eChatMemoryAdvisor 将对话历史作为一系列独‎立的消息添加到提示中,保留原始‎对话的完整结构,包括每条消息的‍角色标识(用户、助手、系统)
[{"role": "user", "content": "你好"},{"role": "assistant", "content": "你好!有什么我能帮助你的吗?"},{"role": "user", "content": "讲个笑话"}
]
  1. PromptC؜hatMemoryAdvisor 将对话历史添加到提示词的系统文本部‎分,因此可能会失去原始的消息边界。‎                 ‍
以下是之前的对话历史:
用户: 你好
助手: 你好!有什么我能帮助你的吗?
用户: 讲个笑话
现在请继续回答用户的问题。

一般情况下,更建议使用 MessageChatMemoryAdvisor 更符合大多数现代 LLM 的对话模型设计,能更好地保持上下文连贯性。

使用 Chat Memory构造ChatMemoryAdvisor
上述 ChatMemoryAdvisor 都依赖 Chat Memory 进行构造,Chat Memory 负责历史对话的存储,定义了保存消息、查询消息、清空消息历史的方法。
在这里插入图片描述
Sprin؜g AI 内置了几种 Chat Me‎mory,可以将对‎话保存到不同的数据‍源中,比如:

  • InMemoryChatMemory:内存存储
  • CassandraChatMemory:在 Cassandra 中带有过期时间的持久化存储
  • Neo4jChatMemory:在 Neo4j 中没有过期时间限制的持久化存储
  • JdbcChatMemory:在 JDBC 中没有过期时间限制的持久化存储
  • 自定义数据源存储

三、SpringAI会话记忆、Advisor实战

1. 使用 InMemoryChatMemory:内存存储 实现对话记忆

  1. 首先初始化 ChatC؜lient 对象。使用 Spring 的构造器注入方式来注入阿里大模型dashscopeChatMod‎el 对象,并使用该对象来初始化 ChatCli‎ent。初始化时指定默认的系统 Prompt 和基于内存‍的对话记忆 Advisor。代码如下:
@Component
@Slf4j
public class App {private final ChatClient chatClient;private static final String SYSTEM_PROMPT = "your system prompt";public LoveApp(ChatModel dashscopeChatModel) {// 初始化基于内存的对话记忆ChatMemory chatMemory = new InMemoryChatMemory();chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)).build();}
}
  1. 编写对话方法,调用 chatClient 对象,传入用户 Pr‎ompt,并且给 advi‎sor 指定对话 id 和对话‍记忆大小。代码如下:
public String doChat(String message, String chatId) {ChatResponse response = chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).call().chatResponse();String content = response.getResult().getOutput().getText();log.info("content: {}", content);return content;
}
  1. 编写单元测试,测试多轮对话:
@SpringBootTest
class AppTest {@Resourceprivate App app;@Testvoid testChat() {String chatId = UUID.randomUUID().toString();// 第一轮String message = "你好,我是wangdong.";String answer = app.doChat(message, chatId);Assertions.assertNotNull(answer);// 第二轮message = "我想学习springAI!!!";answer = app.doChat(message, chatId);Assertions.assertNotNull(answer);// 第三轮message = "你记得我叫啥吗?";answer = app.doChat(message, chatId);Assertions.assertNotNull(answer);}
}

第一轮:
在这里插入图片描述
第二轮:
在这里插入图片描述
第三轮:
在这里插入图片描述
这样我们就完成了基于内存的会话记忆,但是这样还是有弊端,一旦程序重启,我们的会话还是会丢失,还需要别的方法来优化会话记忆

2. 自定义Advisor的使用

自定义Adcisor 和 Spring AOP 比较相似,我们可以通过编写拦截‎器或切面对请求和响应进行处理‍,比如记录请求响应日志、鉴权等。

Spring ؜AI 的 Advisor 就可以理解为拦截器,可以对‎调用 AI 的请求进行增强‎,比如调用 AI 前鉴权、‍调用 AI 后记录日志。

官方已经提供了一些 Advisor,但可能无法满足我们实际的业务需求,这时我们可以使用官方提供的 自定义 Advisor 功能。按照下列步骤操作即可。

  1. 自定义的Advisor要实现这两个接口CallAroundAdvisor StreamAroundAdvisor
public class CustomAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {//实现方法...
}

在这里插入图片描述

  1. 实现核心方法

对于非流式处理 (CallAroundAdvisor),实现 aroundCall 方法:

@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {// 1. 处理请求(前置处理)AdvisedRequest modifiedRequest = processRequest(advisedRequest);// 2. 调用链中的下一个AdvisorAdvisedResponse response = chain.nextAroundCall(modifiedRequest);// 3. 处理响应(后置处理)return processResponse(response);
}

对于流式处理 (StreamAroundAdvisor),实现 aroundStream 方法:

@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {// 1. 处理请求AdvisedRequest modifiedRequest = processRequest(advisedRequest);// 2. 调用链中的下一个Advisor并处理流式响应return chain.nextAroundStream(modifiedRequest).map(response -> processResponse(response));
}
  1. 设置执行顺序
    通过实现getOrder()方法指定 Advisor 在链中的执行顺序。值越小优先级越高,越先执行:
@Override
public int getOrder() {// 值越小优先级越高,越先执行return 100; 
}
  1. 提供唯一名称
    为每个 Advisor 提供一个唯一标识符:
@Override
public String getName() {return "鱼皮自定义的 Advisor";
}

3. 使用自定义 Advisor 实现自定义日志 Advisor

虽然 Spring ؜AI 已经内置了 SimpleLoggerAdvisor 日志拦截器,但是以 D‎ebug 级别输出日志,而默认 Spri‎ng Boot 项目的日志级别是 Inf‍o,所以看不到打印的日志信息。
虽然上述方؜式可行,但如果为了更灵活地打印指定的‎日志,建议自己实现‎一个日志 Adv‍isor。
我们可以同时参考 官方文档 和内置的 SimpleLoggerAdvisor 源码,结合 2 者并略做修改,开发一个更精简的、可自定义级别的日志记录器。默认打印 info 级别日志、并且只输出单次用户提示词和 AI 回复的文本。

/*** 自定义日志 Advisor* 打印 info 级别日志、只输出单次用户提示词和 AI 回复的文本*/
@Slf4j
public class MyLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {@Overridepublic String getName() {return this.getClass().getSimpleName();}@Overridepublic int getOrder() {return 0;}private AdvisedRequest before(AdvisedRequest request) {log.info("AI Request: {}", request.userText());return request;}private void observeAfter(AdvisedResponse advisedResponse) {log.info("AI Response: {}", advisedResponse.response().getResult().getOutput().getText());}public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {advisedRequest = this.before(advisedRequest);AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);this.observeAfter(advisedResponse);return advisedResponse;}public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {advisedRequest = this.before(advisedRequest);Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);return (new MessageAggregator()).aggregateAdvisedResponse(advisedResponses, this::observeAfter);}
}

上述代码中值得关注的是 arou؜ndStream 方法的返回,通过 MessageAggregator 工具类将 Flux 响应聚合成单个 AdvisedR‎esponse。这对于日志记录或其他需要观察整个响应而非流中各‎个独立项的处理非常有用。注意,不能在 MessageAggre‍gator 中修改响应,因为它是一个只读操作。

在 App 中应用自定义的日志 Advisor:

chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory),// 自定义日志 Advisor,可按需开启new MyLoggerAdvisor(),).build();

四、结语(会不断补充,可以收藏一下)

本章介绍了 Spring‎ AI 调用大模型、提示词工程、对话记忆、Adv‎isor 的基本使用,对于对话记忆的自定义和jdbc存储,也会在这段时间加入,我会在学习中不断整理笔记,巩固自己学到的东西。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90789.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对抗攻击-知识点

文章目录自然图像往往靠近机器学习分类器学习到的决策边界&#xff08;decision boundaries&#xff09;。正交方向--改变某一个不影响其它的特征降采样&#xff08;Feature Downsampling&#xff09;通过黑盒攻击的持续挑战&#xff0c;我们才能构建真正安全可靠的智能系统DCT…

7.26 作业

一、实验要求及其拓扑图&#xff1a; 本次实验拓扑图&#xff1a; 二、实验IP地址划分&#xff1a; 1. 公网地址&#xff08;R5 作为 ISP&#xff0c;使用公网地址&#xff09;&#xff1a; R1 与 R5 之间接口&#xff1a;15.1.1.0/24&#xff0c;R1 侧为 15.1.1…

Kafka运维实战 14 - kafka消费者组消费进度(Lag)深入理解【实战】

目录什么是消费者 Lag举例说明&#xff1a;Lag 的意义&#xff1a;Lag 监控和查询kafka-consumer-groups基本语法常用命令示例1. 查看单个消费者组的详细信息&#xff08;最常用&#xff09;2. 列出所有消费者组&#xff08;只显示名称&#xff09;3. 列出所有消费者组&#xf…

设计模式(十三)结构型:代理模式详解

设计模式&#xff08;十三&#xff09;结构型&#xff1a;代理模式详解代理模式&#xff08;Proxy Pattern&#xff09;是 GoF 23 种设计模式中的结构型模式之一&#xff0c;其核心价值在于为其他对象提供一种间接访问的机制&#xff0c;以控制对原始对象的访问。它通过引入一个…

24点数学游戏(穷举法求解表达式)

摘要本毕业设计旨在利用MATLAB技术实现一个24点数学游戏&#xff0c;采用穷举法求解所有可能的表达式组合。通过全排列数字、枚举运算符及括号位置&#xff0c;结合递归回溯算法&#xff0c;系统能够高效地搜索所有可能的运算路径&#xff0c;并验证结果是否为24。实验结果表明…

【web应用】如何进行前后端调试Debug? + 前端JavaScript调试Debug?

文章目录一、前后端&#xff1a;后端以Debug模式运行后端项目&#xff0c;打断点二、前后端&#xff1a;前端项目在浏览器中调试三、单独前端&#xff1a;前端JavaScript调试1、控制台输出2、网页调试器中添加断点3、debugger关键字一、前后端&#xff1a;后端以Debug模式运行后…

FreeCAD开发楼梯参数化三维模型和钢格栅

根据楼梯标准图集开发各种楼梯。上行左转&#xff0c;上行右转&#xff0c;对应的栏杆也是配套2种。楼梯总成钢格栅标准里的跨度和承载 扁钢尺寸&#xff0c;轻松切换和修改参数。格栅综合本来格栅上横杆是冷轧扭钢筋&#xff0c;先绘制一个圆柱&#xff0c;再做一个内切正方形…

【AcWing 836题解】合并集合

AcWing 836. 合并集合 【题目描述】 在查看解析之前&#xff0c;先给自己一点时间思考哦&#xff01; 【题解】 并查集是一种用于处理集合合并与查询问题的数据结构&#xff0c;通常支持以下两种操作&#xff1a; Find&#xff1a;查询一个元素所在的集合。 Union&#xff1a…

MySQL锁机制与MVCC原理剖析

在MySQL中&#xff0c;我们使用到了它的各种类锁&#xff1b;按照它的维度&#xff0c;有各种锁 从数据库的操作粒度有&#xff0c;表锁&#xff0c;行锁。从数据库的操作的类型&#xff0c;有读锁和写锁。性能上有乐观锁和悲观锁。 在上一篇文章中的事务隔离级别&#xff0c;需…

C++学习(线程相关)

目录 一、线程库thread 1.使用外部函数 2. 使用类的函数 3. 添加参数 二、线程库 mutex 1.使用lock()方法 2.try_lock()方法 三、线程库lock_guard 四、线程库unique_lock 1.adopt_lock 2.defer_lock() 五、线程库call_once 六、线程库promise & future 七、c…

EPOLLONESHOT 深度解析:Linux epoll 的单次触发机制

EPOLLONESHOT 深度解析&#xff1a;Linux epoll 的单次触发机制 EPOLLONESHOT 是 Linux epoll 接口中的高级事件标志&#xff0c;用于实现精确的事件单次触发控制。以下是其全面技术解析&#xff1a; 核心设计理念 #mermaid-svg-Xg5sCLdddqmKsvKG {font-family:"trebuchet…

深入解析MongoDB分片原理与运维实践指南

深入解析MongoDB分片原理与运维实践指南 技术背景与应用场景 随着互联网业务的高速发展&#xff0c;单节点MongoDB实例在数据量和访问并发上都面临瓶颈。为了解决数据存储容量受限和读写性能下降的问题&#xff0c;MongoDB官方提供了分片&#xff08;Sharding&#xff09;方案&…

基于Django的天气数据可视化分析预测系统

【86-Django】基于Django的天气数据可视化分析预测系统&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介 二、项目界面展示 三、项目视频展示 四、技术架构 五、核心功能模块 六、部署教程一、项目简介 随着全球气候变化和极端天气事件的频发&am…

怎么放大单片机输出电流

单片机作为电子系统的控制核心&#xff0c;其 I/O 口输出电流通常较小&#xff08;一般在 10-20mA 左右&#xff09;&#xff0c;难以直接驱动继电器、电机、大功率 LED 等需要较大工作电流的外设。因此&#xff0c;在实际应用中需通过特定电路放大单片机输出电流&#xff0c;实…

站长百科类网站pbootcms模板(自适应手机端)+利于SEO优化(下载)

站长百科类网站pbootcms模板(自适应手机端)利于SEO优化 模板介绍&#xff1a; PbootCMS内核开发的模板&#xff0c;该模板属于新闻资讯、新闻博客类企业使用&#xff01; 页面简洁简单&#xff0c;容易管理&#xff0c;附带测试数据&#xff01; 模板特点&#xff1a; 1、手工书…

【Golang】Go语言函数

Go语言函数 文章目录Go语言函数Go函数特点一、函数的基本格式定义二、匿名函数三、自执行函数四、闭包函数五、延迟调用Go函数特点 无需声明原型支持不定 变参支持多返回值支持匿名函数和闭包函数也是一种类型&#xff0c;一个函数可以赋值给变量不支持嵌套&#xff0c;一个包…

JAVA算法练习题day2

双指针4.移动零二刷昨天的题&#xff0c;学习了新的数据结构StringBuilder。专为频繁字符串拼接设计的可变字符串类。(https://blog.csdn.net/m0_73941339/article/details/145651287)二刷完昨天的题目&#xff0c;做到这题脑子已经转不动了。做双指针&#xff0c;一般双指针初…

LLM2Rec-新国立-KDD2025-微调LLM获得蕴含协同信息的embedding

文章目录1. 背景与问题任务背景动机LLM2Rec 两大步骤2. 方法2.1 Collaborative Supervised Fine-tuning&#xff08;CSFT&#xff09;2.2 Item-level Embedding Modeling2.2.1 从单向注意力 → 双向注意力&#xff08;Bidirectional attention&#xff09;2.2.2 商品级别的对比…

前端学习9:JavaScript--对象与原型

前言&#xff1a;适合有基础的同学入门尝试 / 复习回忆。对象基础&#xff1a;1.创建用户对象const user {// 属性&#xff08;键值对&#xff09;name: "小岛",age: 20,isAdmin: false, }2.方法&#xff08;函数属性&#xff09;sayHello() {console.log(你好&…

网络:应用层

网络&#xff1a;应用层 我们要知道&#xff0c;所有的问题解决都是在应用层。:happy: 协议是一种约定&#xff0c;也就是双方约定好的结构化的数据。但是在读写数据时我们都是按字符串的方式来发送接受的&#xff0c;那么我们应该如和传输结构化的数据呢&#xff1f;应用层协…