Spring AI ChatClient

Spring AI中的ChatClient是一个提供流畅API(Fluent API)的客户端,它主要用于与各种AI模型进行通信。ChatClient 提供了与 AI 模型通信的 Fluent API,它支持同步和反应式(Reactive)编程模型。与 ChatModel、Message、ChatMemory 等原子 API 相比,使用 ChatClient 可以将与 LLM 及其他组件交互的复杂性隐藏在背后,因为基于 LLM 的应用程序通常要多个组件协同工作(例如,提示词模板、聊天记忆、LLM Model、输出解析器、RAG 组件:嵌入模型和存储),并且通常涉及多个交互,因此协调它们会让编码变得繁琐。当然使用 ChatModel 等原子 API 可以为应用程序带来更多的灵活性,成本就是您需要编写大量样板代码。

一、核心功能与特点

  1. 与AI模型通信:
    • ChatClient能够与各种支持HTTP请求交互的AI模型进行通信,如GPT系列模型、BERT模型等。
    • 它通过发送HTTP请求到AI模型的端点,并解析响应来实现与AI模型的通信。
  2. 流畅API设计:
    • ChatClient采用了Fluent API的设计模式,通过方法链的方式简化了与AI模型通信的过程。
    • 开发者可以通过链式调用的方式设置请求参数、发起请求,并获取响应结果。
    • 这种设计方式提高了代码的可读性,减少了样板代码的量。
  3. 支持同步和异步处理:
    • ChatClient支持同步和反应式(Reactive)编程模型。
    • 同步处理模式下,开发者可以直接获取AI模型的响应结果。
    • 异步处理模式下,开发者可以流式地接收AI模型的响应结果,提高系统的并发处理能力和响应速度。
  4. 丰富的响应格式化选项:
    • ChatClient提供了多种方法来格式化AI模型的响应结果。
    • 开发者可以根据需要选择返回字符串、实体对象或流式响应等不同类型的输出格式。
  5. 自定义提示与配置:
    • ChatClient允许开发者通过Prompt对象来自定义与AI模型的交互过程。
    • 开发者可以设置不同的提示语和参数来引导AI模型的回复方向和内容。
    • ChatClient还支持在运行时通过Prompt的配置项覆盖初始化的配置项,以实现更灵活的配置管理。

二、应用场景

ChatClient可以应用于多种业务场景,包括但不限于:

  1. 客户服务:
    • ChatClient可以用于构建智能客服系统,通过集成先进的AI模型(如ChatGPT),自动回答用户的问题,提供24/7不间断的服务。
    • 这不仅可以提高客户满意度,还能降低企业的人力成本。
  2. 教育培训:
    C - hatClient可以用于构建智能辅导系统,通过集成各种知识图谱和AI模型,根据学生的学习情况和兴趣爱好提供个性化的学习建议和辅导。
    • 这不仅可以提高学生的学习效率,还能激发他们的学习兴趣。
  3. 娱乐游戏:
    • ChatClient可以用于构建智能NPC(非玩家角色),通过集成先进的对话系统和情感计算模型,与玩家进行更加自然和有趣的互动。
    • 这不仅可以提高游戏的沉浸感和趣味性,还能增加玩家的粘性和活跃度。

三、使用方式

  1. 引入依赖:
    • 如果使用的是Maven项目,可以在pom.xml文件中添加Spring AI的依赖来引入ChatClient。
  2. 创建ChatClient实例:
    • 可以使用Spring Boot的自动配置功能来创建ChatClient实例,也可以通过编程方式来创建。
      自动配置方式下,只需在类中注入ChatClient的Bean即可。
    • 编程方式下,需要手动创建ChatClient.Builder实例,并通过它来构建ChatClient。
  3. 设置请求参数并发起请求:
    • 使用ChatClient的prompt()方法来设置请求参数,如用户输入、系统提示等。
    • 调用call()方法向AI模型发送请求,并获取响应结果。
    • 可以根据需要使用chatResponse()、entity()等方法来格式化响应结果。
  4. 处理响应结果:
    • 根据需要处理AI模型的响应结果,如将其映射为实体类、进行流式处理等。

四、源代码解读

ChatClient接口

public interface ChatClient {
	..... 创建ChatClient方法。
	create
	.....
	..... 创建Builder方法
	builder
	.....
	.....用于设置聊天请求的规范,如提示信息等。
	ChatClientRequestSpec prompt();

	ChatClientRequestSpec prompt(String content);

	ChatClientRequestSpec prompt(Prompt prompt);
	.....
	.....用于创建新的 Builder  其设置将复制自当前客户端的默认
	Builder mutate();
	.....配置用户提示
	interface PromptUserSpec {......}
	.....
	.....配置系统提示
	interface PromptSystemSpec {......}
	.....
	interface AdvisorSpec {......}
	......回调的响应规格
	interface CallResponseSpec {......}
	......
	......流式响应的规格
	interface StreamResponseSpec {......}
	......提示响应响应的规格
	interface CallPromptResponseSpec {......}
	......
	......流式提示响应响应的规格	
	interface StreamPromptResponseSpec {......}
	......	
	......聊天客户端的请求规格
	interface ChatClientRequestSpec {......}
	......
	......提供了一种构建 ChatClient 对象的方式,允许设置默认的顾问、选项、用户和系统提示等。
	interface Builder {

		Builder defaultAdvisors(Advisor... advisor);

		Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);

		Builder defaultAdvisors(List<Advisor> advisors);

		Builder defaultOptions(ChatOptions chatOptions);

		Builder defaultUser(String text);

		Builder defaultUser(Resource text, Charset charset);

		Builder defaultUser(Resource text);

		Builder defaultUser(Consumer<PromptUserSpec> userSpecConsumer);

		Builder defaultSystem(String text);

		Builder defaultSystem(Resource text, Charset charset);

		Builder defaultSystem(Resource text);

		Builder defaultSystem(Consumer<PromptSystemSpec> systemSpecConsumer);

		/**
		 * @deprecated use {@link #defaultFunctions(FunctionCallback...)} instead.
		 */
		@Deprecated
		<I, O> Builder defaultFunction(String name, String description, java.util.function.Function<I, O> function);

		/**
		 * @deprecated use {@link #defaultFunctions(FunctionCallback...)} instead.
		 */
		@Deprecated
		<I, O> Builder defaultFunction(String name, String description,
				java.util.function.BiFunction<I, ToolContext, O> function);

		Builder defaultFunctions(String... functionNames);

		Builder defaultFunctions(FunctionCallback... functionCallbacks);

		Builder defaultToolContext(Map<String, Object> toolContext);

		Builder clone();

		ChatClient build();

	}

}

ChatClient 实现类 DefaultChatClient类

创建

使用 ChatClient.Builder 对象创建 ChatClient 实例,您可以自动注入由Spring Boot 自动配置创建的默认 ChatClient.Builder 实例,您也可以通过编程方式自行创建一个 ChatClient.Builder 实例并用它来得到 ChatClient 实例。

  1. 使用自动配置的 ChatClient.Builder
    在快速开始示例中,就是使用的 Spring Boot 自动装配默认生成的 ChatClient.Builder 的 bean,把它注入到您自己的类中。这里是根据用户提问并从模型得到文本回答的简单例子:
    @RestController
    public class ChatController {

      private final ChatClient chatClient;

      public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
      }

      @GetMapping("/chat")
      public String chat(String input) {
        return this.chatClient.prompt()
            .user(input)
            .call()
            .content();
      }
    }

在这个示例中,首先设置了用户消息的内容,call 方法向 AI 模型发送请求,content 方法以字符串形式返回 AI 模型的响应。

  1. 以编程方式创建 ChatClient
    可以通过设置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder bean 的自动配置,如果需要多个聊天模型一起使用,这会很有用,然后以编程方式创建 ChatClient.Builder,这样可以为每个聊天模型创建一个实例 ChatModel:
    ChatModel myChatModel = ... // usually autowired

    ChatClient.Builder builder = ChatClient.builder(myChatModel);

    // or create a ChatClient with the default builder settings:

    ChatClient chatClient = ChatClient.create(myChatModel);

ChatClient 响应

  1. 返回 ChatResponse
    AI 模型的响应是一种由ChatResponse类型定义的丰富结构。它包含响应生成相关的元数据,同时它还可以包含多个子响应(称为Generation),每个子响应都有自己的元数据。元数据包括用于创建响应的令牌(token)数量信息(在英文中,每个令牌大约为一个单词的 3/4)。
    ChatResponse
public class ChatResponse implements ModelResponse<Generation> {

    private final ChatResponseMetadata chatResponseMetadata;
	private final List<Generation> generations;

	@Override
	public ChatResponseMetadata getMetadata() {...}

    @Override
	public List<Generation> getResults() {...}

    // other methods omitted
}

Generation

public class Generation implements ModelResult<AssistantMessage> {

	private final AssistantMessage assistantMessage;
	private ChatGenerationMetadata chatGenerationMetadata;

	@Override
	public AssistantMessage getOutput() {...}

	@Override
	public ChatGenerationMetadata getMetadata() {...}

    // other methods omitted
}

下面的代码段显示了通过调用 chatResponse() 返回 ChatResponse 的示例,相比于调用 content() 方法,这里在调用 call() 方法之后调用 chatResponse()。

    ChatResponse chatResponse = chatClient.prompt()
        .user("Tell me a joke")
        .call()
        .chatResponse();
  1. 返回实体类(Entity)
    Spring AI 框架可以自动替我们完成从 String 到实体类的转换,调用entity() 方法可完成响应数据转换。
  ActorFilms aFilms = chatClient.prompt()
        .user("Generate the filmography for a random actor.")
        .call()
        .entity(ActorFilms.class);

entity 还有一种带有参数的重载方法 entity(ParameterizedTypeReference<T> type),可让您指定如泛型 List 等类型:

    List<ActorFilms> aFilms = chatClient.prompt()
        .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
        .call()
        .entity(new ParameterizedTypeReference<List<ActorFilms>>() {
        });
  1. 流式响应
    stream 方法是一种异步的、持续的获得模型响应的方式:
    Flux<String> oput = chatClient.prompt()
        .user("Tell me a joke")
        .stream()
        .content();

还可以使用 Flux<ChatResponse> chatResponse() 方法获得 ChatResponse 响应数据流。

call 返回值
ChatClient.call() 方法支持几种不同类型的响应格式。

  • String content():返回响应的字符串内容
  • ChatResponse chatResponse():返回ChatResponse包含多个代以及有关响应的元数据的对象,例如,使用了多少个令牌来创建响应。
  • entity 返回 Java 类型
    • entity(ParameterizedTypeReference type):用于返回实体类型的集合。
    • entity(Class type): 用于返回特定的实体类型。
    • entity(StructuredOutputConverter structuredOutputConverter): 用于指定一个实例 StructuredOutputConverter,将 String 转换为实体类型。

stream() 返回值
还可以调用该stream方法而call不是,在指定stream方法后ChatClient,响应类型有几个选项:

  • Flux<String> content():返回由AI模型生成的字符串的Flux。
  • Flux<ChatResponse> chatResponse():返回对象的 Flux ChatResponse,其中包含有关响应的附加元数据。

示例代码:

由ChatClient.Builder创建

@RestController
public class MyController {

    private final ChatClient chatClient;

    // 通过构造函数注入ChatClient的Bean
    public MyController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/ai")
    public String generateResponse(String userInput) {
        // 设置请求参数并发起请求
        ChatResponse chatResponse = chatClient.prompt()
                .user(userInput) // 设置用户输入
                .call() // 向AI模型发送请求
                .chatResponse(); // 获取包含元数据的ChatResponse对象

        // 处理响应结果(这里只是简单地返回响应内容)
        return chatResponse.getContent();
    }
}

ChatClient 默认值

使用 ChatClient.Builder.build() 快速创建了一个 ChatClient 实例,开发者还可以通过修改 ChatClient.Builder 定制 ChatClient 实例。

注意,创建 ChatClient 时指定的配置将作为与模型交互时的默认参数,这样可以避免每次调用都重复设置。

  1. 示例:设置默认 System Message
    为 ChatClient 设置了一个默认的 system message(以海盗风格回答所有问题),这样,当 ChatClient 与模型交互时都会自动携带这条 system message,用户只需要指定 user message 即可。
       @Configuration
    class Config {

        @Bean
        ChatClient chatClient(ChatClient.Builder builder) {
            return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
                    .build();
        }

    }

    @RestController
    class AIController {
      private final ChatClient chatClient
      AIController(ChatClient chatClient) {
        this.chatClient = chatClient;
      }
      @GetMapping("/ai")
      Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message, String voice) {
        return Map.of(
            "completion",
            chatClient.prompt()
                .system(sp -> sp.param("voice", voice))
                .user(message)
                .call()
                .content());
      }
    }

启动示例,通过 curl 测试效果:

> curl http localhost:8080/ai?voice=='Robert DeNiro'
    {
        "completion": "You talkin' to me? Okay, here's a joke for ya: Why couldn't the bicycle stand up by itself? Because it was two tired! Classic, right?"
    }
  1. 其他默认设置
    • defaultOptions(ChatOptions chatOptions):传入 ChatOptions 类中定义的可移植选项或特定于模型实现的如 DashScopeChatOptions 选项。有关特定于模型的ChatOptions实现的更多信息,请参阅 JavaDocs。

    • defaultFunction(String name, String description, java.util.function.Function<I, O> function):name 用于在用户文本中引用该函数,description解释该函数的用途并帮助 AI 模型选择正确的函数以获得准确的响应,参数 function 是模型将在必要时执行的 Java 函数实例。

    • defaultFunctions(String… functionNames):应用程序上下文中定义的 java.util.Function 的 bean 名称。

    • defaultUser(String text)、defaultUser(Resource text)、defaultUser(Consumer userSpecConsumer) 这些方法允许您定义用户消息输入,Consumer<UserSpec>允许您使用 lambda 指定用户消息输入和任何默认参数。

    • defaultAdvisors(RequestResponseAdvisor… advisor):Advisors 允许修改用于创建 Prompt 的数据,QuestionAnswerAdvisor 实现通过在 Prompt 中附加与用户文本相关的上下文信息来实现 Retrieval Augmented Generation 模式。

    • defaultAdvisors(Consumer advisorSpecConsumer):此方法允许您定义一个 Consumer 并使用 AdvisorSpec 配置多个 Advisor,Advisor 可以修改用于创建 Prompt 的最终数据,Consumer<AdvisorSpec> 允许您指定 lambda 来添加 Advisor 例如 QuestionAnswerAdvisor。

Advisors

使用上下文数据附加或扩充 Prompt,最终使用扩充后的 Prompt 与模型交互。
扩充 Prompt 的上下文数据可以是不同类型的,常见类型包括:
- 自己的数据:这是 AI 模型尚未训练过的数据,如特定领域知识、产品文档等,即使模型已经看到过类似的数据,附加的上下文数据也会优先生成响应。
- 对话历史记录:聊天模型的 API 是无状态的,如果告诉 AI 模型您的姓名,它不会在后续交互中记住它,每次请求都必须发送对话历史记录,以确保在生成响应时考虑到先前的交互。

检索增强生成(RAG)

向量数据库存储的是 AI 模型不知道的数据,当用户问题被发送到 AI 模型时,会在向量数据库中查询与用户问题相关的文档。
来自向量数据库的响应被附加到用户消息 Prompt 中,为 AI 模型生成响应提供上下文。
假设已将数据加载到中 VectorStore,则可以通过向 ChatClient 提供 QuestionAnswerAdvisor 实例来执行检索增强生成 (RAG ) 。

    ChatResponse response = ChatClient.builder(chatModel)
            .build().prompt()
            .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
            .user(userText)
            .call()
            .chatResponse();

在此示例,SearchRequest.defaults() 将对 Vector 向量数据库中的所有文档执行相似性搜索。为了限制要搜索的文档类型。

记忆

ChatMemory 接口表示聊天对话历史记录存储,提供向对话添加消息、从对话中检索消息以及清除对话历史记录的方法。

目前提供两种实现方式 InMemoryChatMemoryCassandraChatMemory,分别为聊天对话历史记录提供内存存储time-to-live 类型的持久存储。
创建一个包含 time-to-live 配置的 CassandraChatMemory

CassandraChatMemory.create(CassandraChatMemoryConfig.builder().withTimeToLive(Duration.ofDays(1)).build());

以下 Advisor 实现使用 ChatMemory 接口来使用对话历史记录来增强(advice)Prompt,这些 advisor 实现在如何将对话历史记录添加到 Prompt 的细节上有所不同。

  • MessageChatMemoryAdvisor:内存被检索并作为消息集合添加到提示中
  • PromptChatMemoryAdvisor:检索内存并将其添加到提示的系统文本中。
  • VectorStoreChatMemoryAdvisor :构造函数VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize)允许指定要从中检索聊天历史记录的 VectorStore、唯一的对话 ID、要检索的聊天历史记录的大小(以令牌大小为单位)。

下面的 @Service 提供了一个使用多个 Advisor 的示例实现:

    import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
    import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;

    @Service
    public class CustomerSupportAssistant {

        private final ChatClient chatClient;

        public CustomerSupportAssistant(ChatClient.Builder builder, VectorStore vectorStore, ChatMemory chatMemory) {

        this.chatClient = builder
                .defaultSystem("""
                        You are a customer chat support agent of an airline named "Funnair".", Respond in a friendly,
                        helpful, and joyful manner.
                        If there is a charge for the change, you MUST ask the user to consent before proceeding.
                        """)
                .defaultAdvisors(
                        new PromptChatMemoryAdvisor(chatMemory),
                        // new MessageChatMemoryAdvisor(chatMemory), // CHAT MEMORY
                        new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()),
                        new LoggingAdvisor()) // RAG
                .defaultFunctions("getBookingDetails", "changeBooking", "cancelBooking") // FUNCTION CALLING
                .build();
    }

    public Flux<String> chat(String chatId, String userMessageContent) {

        return this.chatClient.prompt()
                .user(userMessageContent)
                .advisors(a -> a
                        .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
                .stream().content();
        }
    }

日志记录

SimpleLoggerAdvisor 是一个用于记录 ChatClient 的 request 和 response 数据 Advisor,这对于调试和监控 AI 交互非常有用。

要启用日志记录,请在创建 ChatClient 时将 SimpleLoggerAdvisor 添加到 Advisor 链中。建议将其添加到链的末尾:

    ChatResponse response = ChatClient.create(chatModel).prompt()
            .advisors(new SimpleLoggerAdvisor())
            .user("Tell me a joke")
            .call()
            .chatResponse();

要查看日志,请将 Advisor 包的日志记录级别设置为 DEBUG:

org.springframework.ai.chat.client.advisor=DEBUG

将其添加到 application.properties 或 application.yaml 文件中。

可以使用以下构造函数自定义如何使用 SimpleLoggerAdvisor 记录来自 AdvisedRequest 和 ChatResponse 的数据:

    SimpleLoggerAdvisor(
        Function<AdvisedRequest, String> requestToString,
        Function<ChatResponse, String> responseToString
    )

使用示例:

    javaCopySimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
        request -> "Custom request: " + request.userText,
        response -> "Custom response: " + response.getResult()
    );

参考项目

紫汐AI

综上所述,Spring AI的ChatClient是一个功能强大且灵活的客户端工具,它能够帮助开发者轻松地与各种AI模型进行通信,并实现丰富的交互功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VsCode对Arduino的开发配置

ps&#xff1a;我的情况是在对esp32进行编译、烧录时&#xff0c;找不到按钮&#xff0c;无法识别Arduino文件&#xff0c;适合已经有ini文件的情况。 1.在vscode中安装拓展 2.打开设置&#xff0c;点击右上角&#xff0c;转到settings.json文件 3.复制以下代码并保存 {"…

python学opencv|读取图像(三十二)使用cv2.getPerspectiveTransform()函数制作透视图-变形的喵喵

【1】引言 前序已经对图像展开了平移、旋转缩放和倾斜拉伸技巧探索&#xff0c;相关链接为&#xff1a; python学opencv|读取图像&#xff08;二十八&#xff09;使用cv2.warpAffine&#xff08;&#xff09;函数平移图像-CSDN博客 python学opencv|读取图像&#xff08;二十…

AWS云计算概览(自用留存,整理中)

目录 一、云概念概览 &#xff08;1&#xff09;云计算简介 &#xff08;2&#xff09;云计算6大优势 &#xff08;3&#xff09;web服务 &#xff08;4&#xff09;AWS云采用框架&#xff08;AWS CAF&#xff09; 二、云经济学 & 账单 &#xff08;1&#xff09;定…

Unity TextMesh Pro入门

概述 TextMesh Pro是Unity提供的一组工具&#xff0c;用于创建2D和3D文本。与Unity的UI文本和Text Mesh系统相比&#xff0c;TextMesh Pro提供了更好的文本格式控制和布局管理功能。 本文介绍了TMP_Text组件和Tmp字体资产(如何创建字体资产和如何解决缺字问题),还有一些高级功…

数据结构与算法之链表: LeetCode 19. 删除链表的倒数第 N 个结点 (Ts版)

删除链表的倒数第 N 个结点 https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示…

【STM32-学习笔记-2-】外部中断

文章目录 外部中断Ⅰ、EXIT函数Ⅱ、EXTI_InitTypeDef结构体参数①、EXTI_Line②、EXTI_LineCmd③、EXTI_Mode④、EXTI_Trigger Ⅲ、NVIC函数Ⅳ、NVIC_InitTypeDef结构体参数①、NVIC_IRQChannel②、NVIC_IRQChannelCmd③、NVIC_IRQChannelPreemptionPriority④、NVIC_IRQChanne…

利用 awk 定制化处理大量数据的计算

问题 有上万行&#xff08;甚至更多&#xff09;不断递增的浮点数&#xff08;每行一个&#xff09;&#xff0c;怎么将它们每四个一组计算每组第四个和第一个之间的差值&#xff0c;并打印输出计算结果&#xff1f; 例如文件 data 有以下数据&#xff1a; 2.699350 2.69935…

llama.cpp 模型可视化工具 GGUF Visualizer

llama.cpp 模型可视化工具 GGUF Visualizer 1. GGUF Visualizer for VS Code (gguf-viz)1.1. Features1.2. Extension Settings References GGUF Visualizer https://marketplace.visualstudio.com/items?itemNameAgainstEntropy.gguf-viz 1. GGUF Visualizer for VS Code (g…

10,STL——list类

一&#xff0c;list类的介绍和使用 1&#xff0c;了解list类 1. &#xff09;list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. &#xff09;list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关…

Guilite字库工具

目录 前言 使用方法 离线字库解析 工具链接 前言 最近通过Qt写了一个Guilite字库工具&#xff0c;相比原始工具&#xff0c;主要有以下几个优点&#xff1a; &#xff08;1&#xff09;支持同时生成多套字库 &#xff08;2&#xff09;支持离线字库生成 &#xff08;3&a…

【C++】深入解析pop_back()方法及其应用

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;什么是 pop_back()&#xff1f;定义与功能使用场景 &#x1f4af;深入解析代码示例基础示例分析示例代码分析 空字符串上的 pop_back() 调用错误示例错误原因分析 &#x1…

Java Web开发基础:HTML的深度解析与应用

文章目录 前言&#x1f30d;一.B/S 软件开发架构简述&#x1f30d;二.HTML 介绍❄️2.1 官方文档❄️2.2 网页的组成❄️2.3 HTML 是什么❄️2.4html基本结构 &#x1f30d;三.HTML标签1.html 的标签/元素-说明2. html 标签注意事项和细节3.font 字体标签4.标题标签5.超链接标签…

第三十六章 Spring之假如让你来写MVC——拦截器篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…

IDEA中创建maven项目

1. IDEA中创建maven项目 在IDEA中创建Maven项目&#xff0c;前提是已经安装配置好Maven环境。如还未配置安装Maven的&#xff0c;请先下载安装。如何下载安装&#xff0c;可参考我另外篇文章&#xff1a;maven的下载与安装教程本篇教程是以创建基于servlet的JavaWeb项目为例子&…

MACPA:fMRI连接性分析的新工具

摘要 不同脑区的共同激活为它们之间的功能交互或连接提供了一个有价值的衡量指标。元分析连接模型(MACM)是一种经过充分验证的研究某一特定区域共激活模式的方法&#xff0c;该方法对基于任务的功能磁共振成像(task-fMRI)数据进行种子点(seed-based)元分析。虽然MACM是一种强大…

React中createRoot函数原理解读——Element对象与Fiber对象、FiberRootNode与HostRootNode

【2024最新版】React18 核心源码分析教程&#xff08;全61集&#xff09; Element对象与Fiber对象 在 React 中&#xff0c;Element 对象 和 Fiber 对象 是核心概念&#xff0c;用于实现 React 的高效渲染和更新机制。以下是它们的详细解读&#xff1a; 1. Element 对象 定…

【C】初阶数据结构1 -- 时间复杂度与空间复杂度

目录 1 数据结构 2 算法 3 复杂度 1&#xff09; 时间复杂度 2&#xff09; 空间复杂度 4 提升算法能力的两点建议 1&#xff09; 画图 2&#xff09; 多实践&#xff0c;多上手写代码 重点一 数据结构的定义 1 数据结构 数据结构是计算机存储、组织数据的…

TypeScript Jest 单元测试 搭建

NPM TypeScript 项目搭建 创建目录 mkdir mockprojectcd mockproject初始化NPM项目 npm init -y安装TypeScript npm i -D typescript使用VSCode 打开项目 创建TS配置文件tsconfig.json {"compilerOptions": {"target": "es5","module&…

一.项目课题 <基于TCP的文件传输协议实现>

客户端代码 需要cJSON.c文件和cJSON.h文件 在这里插入代码片#include "myheadth.h" #include "myfun.h"#define TIME 10 int sockfd; void heartbeat(int signum) {cJSON* root cJSON_CreateObject();cJSON_AddStringToObject(root,"request"…

C#调用OpenCvSharp实现图像的膨胀和腐蚀

图像膨胀和腐蚀操作属于图像处理中常用的形态学操作&#xff0c;其原理都是采用特定小矩形&#xff08;核矩形&#xff09;&#xff0c;将其中心位置与图像中的每个像素对齐后&#xff0c;对重合位置的像素执行特定处理后&#xff0c;将处理结果保存到中心位置对应的像素处&…