第5章:在LangChain中如何使用AI Services

这篇文章详细介绍了 LangChain4j 中的 AI Services 概念,展示了如何通过高层次的抽象来简化与大语言模型(LLM)的交互。AI Services 的核心思想是隐藏底层复杂性,让开发者专注于业务逻辑,同时支持聊天记忆、工具调用和 RAG 等高级功能。通过示例和代码片段,文章展示了如何定义和使用 AI Services,以及如何将它们组合起来构建复杂的 LLM 驱动的应用程

AI Services | LangChain4j

引言

到目前为止,我们已经介绍了低层次的组件,例如 ChatLanguageModel、ChatMessage 和 ChatMemory 等。在这一层次上工作非常灵活,给你完全的自由,但这也迫使你编写大量的样板代码(boilerplate code)。由于基于 LLM 的应用程序通常不仅需要单个组件,而是多个组件协同工作(例如,提示词模板、聊天记忆、LLM、输出解析器、RAG 组件:嵌入模型和存储),并且通常涉及多次交互,因此协调它们变得更加繁琐。

解决方案

我们希望你专注于业务逻辑,而不是底层实现细节。因此,LangChain4j 提出了两个高层次的概念来帮助实现这一点:AI Services 和 Chains。

  1. Chains(已废弃)
    Chains 的概念源自 Python 的 LangChain(在引入 LCEL 之前)。其想法是为每个常见用例提供一个 Chain,例如聊天机器人、RAG 等。Chains 结合了多个低层次组件,并协调它们之间的交互。然而,它们的主要问题是,如果你需要自定义某些内容,它们会显得过于僵化。LangChain4j 目前只实现了两个 Chains(ConversationalChain 和 ConversationalRetrievalChain),并且目前不计划添加更多。
  2. AI Services
    我们提出了另一种解决方案,称为 AI Services,专为 Java 设计。其想法是将与 LLM 和其他组件交互的复杂性隐藏在一个简单的 API 后面。
    这种方法类似于 Spring Data JPA 或 Retrofit:你声明性地定义一个接口,指定所需的 API,而 LangChain4j 提供一个实现该接口的对象(代理)。你可以将 AI Service 视为应用程序服务层的一个组件,它提供 AI 服务,因此得名。

AI Services 处理最常见的操作:

  • 为 LLM 格式化输入。
  • 解析 LLM 的输出。
    它们还支持更高级的功能:
  • 聊天记忆(Chat Memory)。
  • 工具(Tools)。
  • RAG(Retrieval-Augmented Generation,检索增强生成)。

AI Services 可以用于构建支持来回交互的状态化聊天机器人,也可以用于自动化每个 LLM 调用都是独立的流程。

AI Service初探

最简单的 AI Service 示例

首先,我们定义一个接口,其中包含一个名为 chat 的方法,该方法接受一个 String 类型的输入并返回一个 String 类型的输出:

interface Assistant {String chat(String userMessage);
}

然后,我们创建低层次组件。这些组件将在 AI Service 的底层使用。在这个例子中,我们只需要 ChatLanguageModel:

ChatLanguageModel model = OpenAiChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).modelName(GPT_4_O_MINI).build();

最后,我们使用 AiServices 类创建 AI Service 的实例:

Assistant assistant = AiServices.create(Assistant.class, model);

注意:在 Quarkus 和 Spring Boot 应用程序中,自动配置会处理 Assistant 的创建。这意味着你不需要调用 AiServices.create(…),只需在需要的地方注入/自动装配 Assistant 即可。
现在我们可以使用 Assistant:

String answer = assistant.chat("Hello");
System.out.println(answer); // 输出:Hello, how can I help you?

工作原理

你将接口的 Class 和低层次组件提供给 AiServices,AiServices 会创建一个实现该接口的代理对象。目前,它使用反射实现,但我们也在考虑其他替代方案。这个代理对象处理所有输入和输出的转换。在这个例子中,输入是一个单独的 String,但我们使用的是接受 ChatMessage 作为输入的 ChatLanguageModel。因此,AiService 会自动将其转换为 UserMessage 并调用 ChatLanguageModel。由于 chat 方法的输出类型是 String,因此在从 chat 方法返回之前,ChatLanguageModel 返回的 AiMessage 将被转换为 String。

在 Quarkus 和 Spring Boot 应用程序中使用 AI Services

LangChain4j 提供了 Quarkus 扩展和 Spring Boot 启动器,极大地简化了在这些框架中使用 AI Services 的过程。

@SystemMessage

现在,我们来看一个更复杂的例子。我们将强制 LLM 使用俚语回答。这通常是通过在

SystemMessage 中提供指令来实现的:
interface Friend {@SystemMessage("You are a good friend of mine. Answer using slang.")String chat(String userMessage);
}
Friend friend = AiServices.create(Friend.class, model);
String answer = friend.chat("Hello"); // 输出:Hey! What's up?

在这个例子中,我们添加了 @SystemMessage 注解,并指定了我们想要使用的系统提示模板。这将在后台被转换为 SystemMessage,并与 UserMessage 一起发送给 LLM。
@SystemMessage 也可以从资源文件中加载提示模板:

@SystemMessage(fromResource = "my-prompt-template.txt")

系统消息提供者(System Message Provider)

系统消息也可以通过系统消息提供者动态定义:

Friend friend = AiServices.builder(Friend.class).chatLanguageModel(model).systemMessageProvider(chatMemoryId -> "You are a good friend of mine. Answer using slang.").build();

你可以根据聊天记忆 ID(用户或对话)提供不同的系统消息。

@UserMessage

假设我们使用的模型不支持系统消息,或者我们只想使用 UserMessage 来实现:

interface Friend {@UserMessage("You are a good friend of mine. Answer using slang. {{it}}")String chat(String userMessage);
}
Friend friend = AiServices.create(Friend.class, model);
String answer = friend.chat("Hello"); // 输出:Hey! What's shakin'?

我们将 @SystemMessage 替换为 @UserMessage,并指定了一个包含变量 it 的提示模板,该变量指向方法的唯一参数。
你也可以使用 @V 注解为提示模板变量指定自定义名称:

interface Friend {@UserMessage("You are a good friend of mine. Answer using slang. {{message}}")String chat(@V("message") String userMessage);
}

注意:在使用 LangChain4j 的 Quarkus 或 Spring Boot 应用程序中,@V 注解不是必需的。只有在 Java 编译时未启用 -parameters 选项时,才需要使用它。
@UserMessage 也可以从资源文件中加载提示模板:

@UserMessage(fromResource = "my-prompt-template.txt")

有效的 AI Service 方法示例

以下是一些有效的 AI Service 方法示例:

使用 UserMessage

String chat(String userMessage);String chat(@UserMessage String userMessage);String chat(@UserMessage String userMessage, @V("country") String country); // userMessage 包含 "{{country}}" 模板变量@UserMessage("What is the capital of Germany?")
String chat();@UserMessage("What is the capital of {{it}}?")
String chat(String country);@UserMessage("What is the capital of {{country}}?")
String chat(@V("country") String country);@UserMessage("What is the {{something}} of {{country}}?")
String chat(@V("something") String something, @V("country") String country);@UserMessage("What is the capital of {{country}}?")
String chat(String country); // 仅在 Quarkus 和 Spring Boot 应用程序中有效

结合 SystemMessage 和 UserMessage

@SystemMessage("Given a name of a country, answer with a name of its capital")
String chat(String userMessage);@SystemMessage("Given a name of a country, answer with a name of its capital")
String chat(@UserMessage String userMessage);@SystemMessage("Given a name of a country, {{answerInstructions}}")
String chat(@V("answerInstructions") String answerInstructions, @UserMessage String userMessage);@SystemMessage("Given a name of a country, answer with a name of its capital")
String chat(@UserMessage String userMessage, @V("country") String country); // userMessage 包含 "{{country}}" 模板变量@SystemMessage("Given a name of a country, answer with a name of its capital")
@UserMessage("Germany")
String chat();@SystemMessage("Given a name of a country, {{answerInstructions}}")
@UserMessage("Germany")
String chat(@V("answerInstructions") String answerInstructions);@SystemMessage("Given a name of a country, answer with a name of its capital")
@UserMessage("Germany")
String chat();@SystemMessage("Given a name of a country, {{answerInstructions}}")
@UserMessage("Germany")
String chat(@V("answerInstructions") String answerInstructions);@SystemMessage("Given a name of a country, answer with a name of its capital")
@UserMessage("{{country}}")
String chat(@V("country") String country);@SystemMessage("Given a name of a country, {{answerInstructions}}")
@UserMessage("{{country}}")
String chat(@V("answerInstructions") String answerInstructions, @V("country") String country);

多模态(Multimodality)

目前,AI Services 不支持多模态功能,需要使用基础的套件和 API 实现。

结构化输出(Structured Outputs)

如果你希望从 LLM 中获取结构化输出,可以将 AI Service 方法的返回类型从 String 改为其他类型。目前,AI Services 支持以下返回类型:

  • String
  • AiMessage
  • 任意自定义 POJO(Plain Old Java Object)
  • 任意 Enum 或 List 或 Set(用于对文本进行分类,例如情感分析、用户意图等)
  • boolean/Boolean(用于获取“是”或“否”的回答)
  • byte/short/int/BigInteger/long/float/double/BigDecimal
  • Date/LocalDate/LocalTime/LocalDateTime
  • List/Set(用于以项目符号列表的形式返回答案)
  • Map<K, V>
  • Result(如果需要访问 TokenUsage、FinishReason、来源(RAG 中检索到的内容)和执行的工具,除了 T,T 可以是上述任意类型。例如:Result、Result)

除非返回类型是 String、AiMessage 或 Map<K, V>,AI Service 会自动在 UserMessage 的末尾附加指示 LLM 应该如何响应的指令。在方法返回之前,AI Service 会将 LLM 的输出解析为所需的类型。
你可以通过启用日志记录来观察附加的指令。

注意:某些 LLM 提供商(例如 OpenAI 和 Google Gemini)允许为期望的输出指定 JSON 模式。如果此功能被支持且启用,自由格式的文本指令不会被附加到 UserMessage 的末尾。在这种情况下,将从你的 POJO 自动生成 JSON 模式并传递给 LLM,从而确保 LLM 遵循该 JSON 模式。

现在,让我们来看一些示例。

1. 返回类型为 boolean 的示例

interface SentimentAnalyzer {@UserMessage("Does {{it}} have a positive sentiment?")boolean isPositive(String text);
}SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, model);boolean positive = sentimentAnalyzer.isPositive("It's wonderful!");
// 输出:true

2. 返回类型为 Enum 的示例

enum Priority {@Description("Critical issues such as payment gateway failures or security breaches.")CRITICAL,@Description("High-priority issues like major feature malfunctions or widespread outages.")HIGH,@Description("Low-priority issues such as minor bugs or cosmetic problems.")LOW
}interface PriorityAnalyzer {@UserMessage("Analyze the priority of the following issue: {{it}}")Priority analyzePriority(String issueDescription);
}PriorityAnalyzer priorityAnalyzer = AiServices.create(PriorityAnalyzer.class, model);Priority priority = priorityAnalyzer.analyzePriority("The main payment gateway is down, and customers cannot process transactions.");
// 输出:CRITICAL

注意:@Description 注解是可选的。当枚举名称不够直观时,建议使用它来帮助 LLM 更好地理解。

3. 返回类型为 POJO 的示例

class Person {@Description("first name of a person") // 可选描述,帮助 LLM 更好地理解String firstName;String lastName;LocalDate birthDate;Address address;
}@Description("an address") // 可选描述,帮助 LLM 更好地理解
class Address {String street;Integer streetNumber;String city;
}interface PersonExtractor {@UserMessage("Extract information about a person from {{it}}")Person extractPersonFrom(String text);
}PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, model);String text = """In 1968, amidst the fading echoes of Independence Day,a child named John arrived under the calm evening sky.This newborn, bearing the surname Doe, marked the start of a new journey.He was welcomed into the world at 345 Whispering Pines Avenuea quaint street nestled in the heart of Springfieldan abode that echoed with the gentle hum of suburban dreams and aspirations.""";Person person = personExtractor.extractPersonFrom(text);System.out.println(person); 
// 输出:Person { firstName = "John", lastName = "Doe", birthDate = 1968-07-04, address = Address { ... } }

JSON 模式(JSON Mode)

当提取自定义 POJO(实际上是 JSON,然后解析为 POJO)时,建议在模型配置中启用“JSON 模式”。这样,LLM 将被强制以有效的 JSON 格式响应。

注意:JSON 模式和工具/函数调用是类似的功能,但它们有不同的 API,并且用于不同的目的。

  • JSON 模式:当你总是需要 LLM 以结构化格式(有效的 JSON)响应时非常有用。此外,通常不要状态/记忆,因此每次与 LLM 的交互都是独立的。例如,你可能希望从文本中提取信息,例如文本中提到的人的列表,或者将自由格式的产品评论转换为具有字段(如 String productName、Sentiment sentiment、List claimedProblems 等)的结构化形式。
  • 工具/函数调用:当 LLM 应该能够执行某些操作时(例如查询数据库、搜索网络、取消用户的预订等),此功能非常有用。在这种情况下,向 LLM 提供一组工具及其期望的 JSON 模式,LLM 将自主决定是否调用其中的任何一个来满足用户请求。
    以前,函数调用常用于结构化数据提取,但现在我们有了 JSON 模式功能,它更适合此目的。

以下是启用 JSON 模式的方法:

OpenAI

  • 对于支持结构化输出的较新模型(例如 gpt-4o-mini、gpt-4o-2024-08-06)
OpenAiChatModel.builder()....responseFormat("json_schema").strictJsonSchema(true).build();
  • 对于较旧的模型(例如 gpt-3.5-turbo、gpt-4):
OpenAiChatModel.builder()....responseFormat("json_object").build();

Azure OpenAI

AzureOpenAiChatModel.builder()....responseFormat(new ChatCompletionsJsonResponseFormat()).build();

Vertex AI Gemini

VertexAiGeminiChatModel.builder()....responseMimeType("application/json").build();

或者通过指定一个 Java 类的显式模式:

GoogleAiGeminiChatModel.builder()....responseFormat(ResponseFormat.builder().type(JSON).jsonSchema(JsonSchemas.jsonSchemaFrom(Person.class).get()).build()).build();

或者通过指定一个 JSON 模式:

GoogleAiGeminiChatModel.builder()....responseFormat(ResponseFormat.builder().type(JSON).jsonSchema(JsonSchema.builder()...build()).build()).build();

Mistral AI

MistralAiChatModel.builder()....responseFormat(MistralAiResponseFormatType.JSON_OBJECT).build();

Ollama

OllamaChatModel.builder()....responseFormat(JSON).build();

其他模型提供商

如果底层模型提供商不支持 JSON 模式,提示工程(Prompt Engineering)是你的最佳选择。此外,尝试降低 temperature 参数以获得更确定性的结果。

流式响应(Streaming)

AI Service 可以通过使用 TokenStream 返回类型逐个流式传输响应令牌:

interface Assistant {TokenStream chat(String message);
}StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).modelName(GPT_4_O_MINI).build();Assistant assistant = AiServices.create(Assistant.class, model);TokenStream tokenStream = assistant.chat("Tell me a joke");tokenStream.onNext((String token) -> System.out.println(token)).onRetrieved((List<Content> contents) -> System.out.println(contents)).onToolExecuted((ToolExecution toolExecution) -> System.out.println(toolExecution)).onComplete((Response<AiMessage> response) -> System.out.println(response)).onError((Throwable error) -> error.printStackTrace()).start();

使用 Flux

你也可以使用 Flux 替代 TokenStream。为此,请导入 langchain4j-reactor 模块:

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId><version>1.0.0-beta1</version>
</dependency>

代码示例

interface Assistant {Flux<String> chat(String message);
}

聊天记忆(Chat Memory)

AI Service 可以使用聊天记忆来“记住”之前的交互:

Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).build();

在这种情况下,相同的 ChatMemory 实例将用于所有 AI Service 的调用。然而,这种方法在有多个用户时将无法工作,因为每个用户都需要自己的 ChatMemory 实例来维护各自的对话。
解决这个问题的方法是使用 ChatMemoryProvider:

interface Assistant {String chat(@MemoryId int memoryId, @UserMessage String message);
}Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)).build();String answerToKlaus = assistant.chat(1, "Hello, my name is Klaus");
String answerToFrancine = assistant.chat(2, "Hello, my name is Francine");

在这种情况下,ChatMemoryProvider 将为每个内存 ID 提供两个不同的 ChatMemory 实例。

注意

  1. 如果 AI Service 方法没有带有 @MemoryId 注解的参数,则 ChatMemoryProvider 中的 memoryId 默认值为字符串 “default”。
  2. 目前,AI Service 不支持对同一个 @MemoryId 的并发调用,因为这可能导致 ChatMemory 被破坏。AI Service 目前没有实现任何机制来防止对同一个 @MemoryId 的并发调用。

工具(Tools)

AI Service 可以配置工具,LLM 可以使用这些工具:

class Tools {@Toolint add(int a, int b) {return a + b;}@Toolint multiply(int a, int b) {return a * b;}
}Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).tools(new Tools()).build();String answer = assistant.chat("What is 1+2 and 3*4?");

在这种情况下,LLM 将请求执行 add(1, 2) 和 multiply(3, 4) 方法,然后才提供最终答案。LangChain4j 将自动执行这些方法。

关于工具的更多信息可以参考 LangChain4j 文档。

RAG(检索增强生成)

AI Service 可以配置 ContentRetriever 来启用简单的 RAG:

EmbeddingStore embeddingStore = ...;
EmbeddingModel embeddingModel = ...;ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).contentRetriever(contentRetriever).build();

配置 RetrievalAugmentor 可以提供更大的灵活性,启用高级的 RAG 功能,例如查询转换、重新排序等:

RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder().queryTransformer(...).queryRouter(...).contentAggregator(...).contentInjector(...).executor(...).build();Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).retrievalAugmentor(retrievalAugmentor).build();

关于 RAG 的更多信息可以参考 LangChain4j 文档。

自动审核(Auto-Moderation)

(示例略)

链接多个 AI Services

随着你的 LLM 驱动应用程序的逻辑变得越来越复杂,将其分解为更小的部分变得至关重要,这在软件开发中是一种常见的实践。

例如,将大量指令塞入系统提示中以涵盖所有可能的场景,容易出错且效率低下。如果指令过多,LLM 可能会忽略一些。此外,指令的呈现顺序也很重要,这使得整个过程更加复杂。

这一原则也适用于工具、RAG 和模型参数(例如 temperature、maxTokens 等)。
你的聊天机器人可能并不需要在所有情况下都了解你所有的工具。例如,当用户仅仅是问候聊天机器人或说再见时,让 LLM 访问数十个甚至数百个工具(每个工具都会消耗大量的 token)是成本高昂的,有时甚至是危险的,可能会导致意外的结果(LLM 可能会幻觉或被操纵以调用工具并输入意外的内容)。

关于 RAG:同样,有时需要为 LLM 提供一些上下文,但并非总是如此,因为这会增加额外的成本(更多上下文 = 更多 token)并增加响应时间(更多上下文 = 更高的延迟)。

关于模型参数:在某些情况下,你可能需要 LLM 高度确定性,因此你会设置较低的 temperature。在其他情况下,你可能会选择较高的 temperature,依此类推。

要点是,更小且更具体的组件更容易、更便宜开发、测试、维护和理解。

另一个需要考虑的方面是两个极端:

  • 你是否希望你的应用程序高度确定性,其中应用程序控制流程,LLM 只是其中一个组件?
  • 或者你希望 LLM 完全自主并驱动你的应用程序?

或许根据情况,两者都有?所有这些选项都可以通过将你的应用程序分解为更小、更易于管理的部分来实现。

AI Services 可以作为常规(确定性)软件组件使用,并与其他组件结合:

  • 你可以依次调用一个 AI Service(即链式调用)。
  • 你可以使用确定性和基于 LLM 的 if/else 语句(AI Services 可以返回 boolean)。
  • 你可以使用确定性和基于 LLM 的 switch 语句(AI Services 可以返回 enum)。
  • 你可以使用确定性和基于 LLM 的 for/while 循环(AI Services 可以返回 int 和其他数值类型)。
  • 你可以模拟 AI Service(因为它是一个接口)以进行单元测试。
  • 你可以单独集成测试每个 AI Service。
  • 你可以分别评估每个 AI Service 并找到每个子任务的最优参数。
    等等。
    让我们考虑一个简单的例子。我想为我的公司构建一个聊天机器人。如果用户问候聊天机器人,我希望它用预定义的问候语回答,而不依赖 LLM 生成问候语。如果用户提问,我希望 LLM 使用公司的内部知识库生成回答(即 RAG)。
    以下是如何将此任务分解为两个独立的 AI Services:
interface GreetingExpert {@UserMessage("Is the following text a greeting? Text: {{it}}")boolean isGreeting(String text);
}interface ChatBot {@SystemMessage("You are a polite chatbot of a company called Miles of Smiles.")String reply(String userMessage);
}class MilesOfSmiles {private final GreetingExpert greetingExpert;private final ChatBot chatBot;public MilesOfSmiles(GreetingExpert greetingExpert, ChatBot chatBot) {this.greetingExpert = greetingExpert;this.chatBot = chatBot;}public String handle(String userMessage) {if (greetingExpert.isGreeting(userMessage)) {return "Greetings from Miles of Smiles! How can I make your day better?";} else {return chatBot.reply(userMessage);}}
}GreetingExpert greetingExpert = AiServices.create(GreetingExpert.class, llama2);ChatBot chatBot = AiServices.builder(ChatBot.class).chatLanguageModel(gpt4).contentRetriever(milesOfSmilesContentRetriever).build();MilesOfSmiles milesOfSmiles = new MilesOfSmiles(greetingExpert, chatBot);String greeting = milesOfSmiles.handle("Hello");
System.out.println(greeting); // 输出:Greetings from Miles of Smiles! How can I make your day better?String answer = milesOfSmiles.handle("Which services do you provide?");
System.out.println(answer); // 输出:At Miles of Smiles, we provide a wide range of services ...

注意我们如何使用较便宜的 Llama2 来完成简单的问候识别任务,而使用更昂贵的 GPT-4(带有内容检索器,即 RAG)来完成更复杂的任务。

这是一个非常简单且有些幼稚的例子,但希望它能说明这个想法。
现在,我可以分别模拟 GreetingExpert 和 ChatBot,并独立测试 MilesOfSmiles。此外,我还可以分别集成测试 GreetingExpert 和 ChatBot,分别评估它们,并为每个子任务找到最优化的参数,甚至在长期内为每个特定任务微调一个小的专用模型。

总结

这篇文章详细介绍了 LangChain4j 中的 AI Services 概念,展示了如何通过高层次的抽象来简化与大语言模型(LLM)的交互。AI Services 的核心思想是隐藏底层复杂性,让开发者专注于业务逻辑,同时支持聊天记忆、工具调用和 RAG 等高级功能。通过示例和代码片段,文章展示了如何定义和使用 AI Services,以及如何将它们组合起来构建复杂的 LLM 驱动的应用程序。

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

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

相关文章

二叉树(数据结构)

二叉树 二叉树也是用过递归定义的结构 先序遍历又称前序遍历 ​​ ​​ 按照先序遍历的方法去手算处理这个二叉树 ​​ 先A B C 再 A B D E C&#xff08;也就是把B换成BDE再放进去&#xff09; 再 A B D E C F 看这个插入的方法要掌握像二叉树这样向一个…

机器学习笔记——常用损失函数

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记介绍机器学习中常见的损失函数和代价函数&#xff0c;各函数的使用场景。 热门专栏 机器学习 机器学习笔记合集 深度学习 深度学习笔记合集 文章目录 热门…

Wireshark使用介绍

文章目录 Wireshark介绍Wireshark使用工作模式介绍1. 混杂模式&#xff08;Promiscuous Mode&#xff09;2. 普通模式&#xff08;Normal Mode&#xff09;3. 监视模式&#xff08;Monitor Mode&#xff09; 界面分区捕获过滤器语法基本语法逻辑运算符高级语法使用示例捕获过滤…

#渗透测试#批量漏洞挖掘#畅捷通T+SQL注入漏洞

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 目录 一、漏洞全景解析 1. 高危漏洞案例库 2.…

【小游戏】C++控制台版本俄罗斯轮盘赌

制作团队&#xff1a;洛谷813622&#xff08;Igallta&#xff09; 989571&#xff08;_ayaka_&#xff09; Mod&#xff1a;_ayaka_ 双人模式&#xff1a;Igallta 公告&#xff1a; 原先的9.8改名为 Alpha 1.0&#xff0c;以后每次更新都增加 0.1。 Alpha 1.11 改为 Beta 1…

nvm安装、管理node多版本以及配置环境变量【保姆级教程】

引言 不同的项目运行时可能需要不同的node版本才可以运行&#xff0c;由于来回进行卸载不同版本的node比较麻烦&#xff1b;所以需要使用node工程多版本管理。 本人在配置时&#xff0c;通过网络搜索教程&#xff0c;由于文章时间过老&#xff0c;或者文章的互相拷贝导致配置时…

框架--Mybatis3

一.特殊符号处理 < < > > " &quot; &apos; & &amp; 除了可以使用上述转义字符外&#xff0c;还可以使<![CDATA[ ]]>用来包裹特殊字符。 二.mybatis 一级缓存二级缓存 1.为什么缓存 缓存&#xff1a;数据缓存&#xf…

纯新手教程:用llama.cpp本地部署DeepSeek蒸馏模型

0. 前言 llama.cpp是一个基于纯C/C实现的高性能大语言模型推理引擎&#xff0c;专为优化本地及云端部署而设计。其核心目标在于通过底层硬件加速和量化技术&#xff0c;实现在多样化硬件平台上的高效推理&#xff0c;同时保持低资源占用与易用性。 最近DeepSeek太火了&#x…

Netty入门详解

引言 Netty 是一个基于 Java 的高性能、异步事件驱动的网络应用框架&#xff0c;用于快速开发可维护的高性能网络服务器和客户端。它提供了一组丰富的 API&#xff0c;使得开发人员能够轻松地处理各种网络协议&#xff0c;如 TCP、UDP 等&#xff0c;并且支持多种编解码方式&a…

物联网简介集合

物联网&#xff08;IoT&#xff09;指的是物理设备&#xff08;如电器和车辆&#xff09;之间的互联互通。这些设备嵌入了软件、传感器和连接功能&#xff0c;使其能够相互连接并交换数据。这项技术实现了从庞大的设备网络中收集和共享数据&#xff0c;为打造更高效、自动化的系…

【分布式理论11】分布式协同之分布式事务(一个应用操作多个资源):从刚性事务到柔性事务的演进

文章目录 一. 什么是分布式事务&#xff1f;二. 分布式事务的挑战三. 事务的ACID特性四. CAP理论与BASE理论1. CAP理论1.1. 三大特性1.2. 三者不能兼得 2. BASE理论 五. 分布式事务解决方案1. 两阶段提交&#xff08;2PC&#xff09;2. TCC&#xff08;Try-Confirm-Cancel&…

【Quest开发】全身跟踪

软件&#xff1a;Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件&#xff1a;Meta Quest3 最终效果&#xff1a;能像meta的操作室沉浸场景一样根据头盔移动来推断用户姿势&#xff0c;实现走路、蹲下、手势匹配等功能 需要借助UnityMovement这个包 GitHub …

AI全栈开发_人工智能AI大模型 Prompt提示词工程详解(全方位介绍及运用)

AI引领的第四次工业革命正席卷而来&#xff0c;如何精准把握这一历史性的机遇&#xff0c;将成为我们这一代人不容忽视且需深入思考与积极行动的重要课题。未来几年AI将会像计算机一样快速普及&#xff0c;面对这一历史性的第一波红利&#xff0c;你是否已准备好把握机遇&#…

小米平板怎么和电脑共享屏幕

最近尝试使用小米平板和电脑屏幕分屏互联 发现是需要做特殊处理的&#xff0c;需要下载一款电脑安装包&#xff1a;小米妙享 关于这个安装包&#xff0c;想吐槽的是&#xff1a; 没有找到官网渠道&#xff0c;是通过其他网络方式查到下载的 不附录链接&#xff0c;原因是因为地…

java | MyBatis-plus映射和golang映射对比

文章目录 Java实体类和数据库的映射1.默认驼峰命名规则2.自定义字段映射3.关闭驼峰命名规则4.JSON序列化映射 Golang1. 结构体与表的映射2. 字段与列的映射3. 关联关系映射4. 其他映射相关标签 这篇也是做数据库映射方面的对比&#xff1a; Java 实体类和数据库的映射 1.默认…

讯方·智汇云校华为官方授权培训机构

1.官方授权 讯方智汇云校是华为领先级授权培训机构&#xff08;华为授权培训合作伙伴&#xff08;HALP&#xff09;体系&#xff0c;分为认证、优选、领先三个等级&#xff0c;领先级是HALP最高级&#xff09;&#xff0c;代表着华为对培训合作伙伴在专业能力、师资队伍、合作…

避免踩雷!CUDA与Anaconda兼容性配置完全手册

CUDA与Anaconda深度学习环境配置指南 目录 核心概念解析安装场景分析版本冲突处理最佳实践指南常见问题解答 核心概念解析 1. 组件对比表 组件作用域包含内容查看方式NVIDIA驱动系统级GPU底层通信支持nvidia-smiCUDA Toolkit系统级完整开发工具链(nvcc等)nvcc --versioncon…

掌握.NET Core后端发布流程,如何部署后端应用?

无论你是刚接触.NET Core的新手还是已有经验的开发者&#xff0c;在这篇文章中你将会学习到一系列实用的发布技巧与最佳实践&#xff0c;帮助你高效顺利地将.NET Core后端应用部署到生产环境中 目录 程序发布操作 Docker容器注册表 文件夹发布 导入配置文件 网站运行操作 …

一周学会Flask3 Python Web开发-request请求对象与url传参

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili request请求对象封装了从客户端发来的请求报文信息&#xff0c;我们可以从中获取所有数据。 request对象包含的常用属性&…

2025年2月深度实测!DeepSeek、OpenAI o1、Gemini打造爆款应用及对比

我在网上看到了关于DeepSeek R1的各种说法,这是一个开源模型,其能力即便不比OpenAI o1等付费模型强,也与之相当: 由于我在日常工作中广泛使用这些人工智能模型(使用Cursor AI),我决定看看哪种模型最适合我。 在进行了200次Cursor请求后,我将分享我的实验结果。 一、…