第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.pswp.cn/web/70315.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70315.shtml
英文地址,請注明出處:http://en.pswp.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請求后,我將分享我的實驗結果。 一、…