spring-ai-alibaba使用Agent實現智能機票助手

示例目標是使用 Spring AI Alibaba 框架開發一個智能機票助手,它可以幫助消費者完成機票預定、問題解答、機票改簽、取消等動作,具體要求為:

  • 基于 AI 大模型與用戶對話,理解用戶自然語言表達的需求
  • 支持多輪連續對話,能在上下文中理解用戶意圖
  • 理解機票操作相關的術語與規范并嚴格遵守,如航空法規、退改簽規則等
  • 在必要時可調用工具輔助完成任務
使用 RAG 增加機票退改簽規則

基于以上架構圖,應用是由 AI 模型理解用戶問題,決策下一步動作、驅動業務流程。但任何一個通用的大模型都能幫我們解決機票相關的問題嗎?依賴模型的決策是可靠的嗎?比如有用戶提出了機票改簽的訴求,模型一定是能夠很好的理解用戶的意圖的,這點沒有疑問。但它怎么知道當前用戶符不符合退票規則呢?要知道每個航空公司的改簽規則可能都是不一樣的;它怎么知道改簽手續費的規定那?在這樣一個可能帶來經濟糾紛、法律風險的應用場景下,AI模型必須要知道改簽規則的所有細節,并逐條確認用戶信息符合規則后,才能最終作出是否改簽的決策。

很顯然,單純依賴 AI 模型本身并不能替我們完成上面的要求,這個時候就要用到 RAG(檢索增強)模式了。通過 RAG 我們可以把機票退改簽相關的領域知識輸入給應用和 AI 模型,讓 AI 結合這些規則與要求輔助決策

使用 Function Calling 執行業務動作

AI 智能體可以幫助應用理解用戶需求并作出決策,但是它沒法代替應用完成決策的執行,決策的執行還是要由應用自己完成,這一點和傳統應用并沒有區別,不論是智能化的還是預先編排好的應用,都是要由應用本身去調用函數修改數據庫記錄實現數據持久化。

通過 Spring AI 框架,我們可以將模型的決策轉換為對某個具體函數的調用,從而完成機票的最終改簽或者退票動作,將用戶數據寫入數據庫,這就是我們前面提到的 Function Calling 模式。

使用 Chat Memory 增加多輪對話能力

最后一點是關于多輪連續對話的支持,我們要記住一點,大模型是無狀態的,它看到的只有當前這一輪對話的內容。因此如果要支持多輪對話效果,需要應用每次都要把之前的對話上下文保留下來,并與最新的問題一并作為 prompt 發送給模型。這時,我們可以利用 Spring AI Alibaba 提供的內置 Conversation Memory 支持,方便的維護對話上下文。

總結起來,我們在這個智能機票助手應用中用到了 Spring AI Alibaba 的核心如下能力:

  1. 基本模型對話能力,通過 Chat Model API 與阿里云通義模型交互
  2. Prompt 管理能力
  3. Chat Memory 聊天記憶,支持多輪對話
  4. RAG、Vector Store,機票預定、改簽、退票等相關規則

1、jdk17 ,spring-ai-alibaba版本 1.0.0-M6.1,使用阿里云大模型,若使用其他模型添加對應依賴即可

pom文件

	<properties><java.version>17</java.version><vaadin.version>24.4.7</vaadin.version><maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version></properties><dependencies><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- Other spring dependencies --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency></dependencies>

2、application.properties 內容如下

# spring.ai.chat.client.enabled=false
server.port=19000
spring.threads.virtual.enabled=truespring.mvc.static-path-pattern=/templates/**
spring.thymeleaf.cache=false
###################
# Anthropic Claude 3
#################### spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
# spring.ai.openai.chat.options.model=llama3-70b-8192
# spring.ai.anthropic.chat.options.model=claude-3-5-sonnet-20240620###################
# Groq
#################### spring.ai.openai.api-key=${GROQ_API_KEY}
# spring.ai.openai.base-url=https://api.groq.com/openai
# spring.ai.openai.chat.options.model=llama3-70b-8192###################
# dashscope
###################
spring.ai.dashscope.api-key=${AI_DASHSCOPE_API_KEY}
spring.ai.dashscope.chat.options.model=qwen-plus
spring.ai.dashscope.embedding.enabled=true
spring.ai.dashscope.embedding.options.model=text-embedding-v2###################
# OpenAI
###################
# spring.ai.openai.chat.options.functions=getBookingDetails,changeBooking,cancelBooking
# spring.ai.openai.chat.enabled=false# Disable the OpenAI embedding when the local huggingface embedding (e.g. spring-ai-transformers-spring-boot-starter) is used.
# spring.ai.openai.embedding.enabled=false###################
# Azure OpenAI
###################
# spring.ai.azure.openai.api-key=${AZURE_OPENAI_API_KEY}
# spring.ai.azure.openai.endpoint=${AZURE_OPENAI_ENDPOINT}
# spring.ai.azure.openai.chat.options.deployment-name=gpt-4o###################
# Mistral AI
#################### spring.ai.mistralai.api-key=${MISTRAL_AI_API_KEY}
# spring.ai.mistralai.chat.options.model=mistral-small-latest# spring.ai.mistralai.chat.options.model=mistral-small-latest
# spring.ai.mistralai.chat.options.functions=getBookingDetails,changeBooking,cancelBooking
# # spring.ai.retry.on-client-errors=true
# # spring.ai.retry.exclude-on-http-codes=429###################
# Vertex AI Gemini
#################### spring.ai.vertex.ai.gemini.project-id=${VERTEX_AI_GEMINI_PROJECT_ID}
# spring.ai.vertex.ai.gemini.location=${VERTEX_AI_GEMINI_LOCATION}
# spring.ai.vertex.ai.gemini.chat.options.model=gemini-1.5-pro-001
# # spring.ai.vertex.ai.gemini.chat.options.model=gemini-1.5-flash-001
# spring.ai.vertex.ai.gemini.chat.options.transport-type=REST# spring.ai.vertex.ai.gemini.chat.options.functions=getBookingDetails,changeBooking,cancelBooking###################
#  Milvus Vector Store
###################
# Change the dimentions to 384 if the local huggingface embedding (e.g. spring-ai-transformers-spring-boot-starter) is used.
# spring.ai.vectorstore.milvus.embedding-dimension=384###################
# PGVector
###################
# spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
# spring.datasource.username=postgres
# spring.datasource.password=postgres###################
# QDrant
###################
# spring.ai.vectorstore.qdrant.host=localhost
# spring.ai.vectorstore.qdrant.port=6334###################
# Chroma
###################
# spring.ai.vectorstore.chroma.client.host=http://localhost
# spring.ai.vectorstore.chroma.client.port=8000

3、知識庫文本文件??terms-of-service.txt? 內容如下

These Terms of Service govern your experience with Funnair. By booking a flight, you agree to these terms.1. Booking Flights
- Book via our website or mobile app.
- Full payment required at booking.
- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee.2. Changing Bookings
- Changes allowed up to 24 hours before flight.
- Change via online or contact our support.
- Change fee: $50 for Economy, $30 for Premium Economy, Free for Business Class.3. Cancelling Bookings
- Cancel up to 48 hours before flight.
- Cancellation fees: $75 for Economy, $50 for Premium Economy, $25 for Business Class.
- Refunds processed within 7 business days.

4、初始化向量庫和對話記憶等

package ai.spring.demo.ai.playground.config;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.web.client.RestClient;@Configuration
public class InitConfig {private static final Logger logger = LoggerFactory.getLogger(InitConfig.class);@Beanpublic VectorStore vectorStore(EmbeddingModel embeddingModel) {//初始化向量庫return SimpleVectorStore.builder(embeddingModel).build();}@BeanCommandLineRunner ingestTermOfServiceToVectorStore(VectorStore vectorStore,@Value("classpath:rag/terms-of-service.txt") Resource termsOfServiceDocs) {//加載機票票務相關知識到向量庫return args -> {// Ingest the document into the vector storevectorStore.write(new TokenTextSplitter().transform(new TextReader(termsOfServiceDocs).read()));vectorStore.similaritySearch("Cancelling Bookings").forEach(doc -> {logger.info("Similar Document: {}", doc.getText());});};}@Beanpublic ChatMemory chatMemory() {//對話記憶return new InMemoryChatMemory();}@Bean@ConditionalOnMissingBeanpublic RestClient.Builder restClientBuilder() {return RestClient.builder();}}

5、機票信息、機票類型、機票狀態、等相關實體類

package ai.spring.demo.ai.playground.data;public enum BookingClass {
//	機票類型 頭等 商務FIRST ,BUSINESS}
package ai.spring.demo.ai.playground.data;public enum BookingStatus {
//機票狀態 已確認  已使用 已取消CONFIRMED, COMPLETED, CANCELLED}

package ai.spring.demo.ai.playground.data;import java.time.LocalDate;/*** 機票信息*/
public class Booking {/*** 訂單號*/private String bookingNumber;/*** 訂單日期*/private LocalDate date;/*** 購票信息*/private Customer customer;/*** 出發城市*/private String from;/*** 到達城市*/private String to;/*** 票務狀態*/private BookingStatus bookingStatus;/*** 票務種類*/private BookingClass bookingClass;public Booking(String bookingNumber, LocalDate date, Customer customer, BookingStatus bookingStatus, String from,String to, BookingClass bookingClass) {this.bookingNumber = bookingNumber;this.date = date;this.customer = customer;this.bookingStatus = bookingStatus;this.from = from;this.to = to;this.bookingClass = bookingClass;}public String getBookingNumber() {return bookingNumber;}public void setBookingNumber(String bookingNumber) {this.bookingNumber = bookingNumber;}public LocalDate getDate() {return date;}public void setDate(LocalDate date) {this.date = date;}public Customer getCustomer() {return customer;}public void setCustomer(Customer customer) {this.customer = customer;}public BookingStatus getBookingStatus() {return bookingStatus;}public void setBookingStatus(BookingStatus bookingStatus) {this.bookingStatus = bookingStatus;}public String getFrom() {return from;}public void setFrom(String from) {this.from = from;}public String getTo() {return to;}public void setTo(String to) {this.to = to;}public BookingClass getBookingClass() {return bookingClass;}public void setBookingClass(BookingClass bookingClass) {this.bookingClass = bookingClass;}}
package ai.spring.demo.ai.playground.data;import java.util.ArrayList;
import java.util.List;/*** 購票信息*/
public class Customer {/*** 購票人姓名*/private String name;/*** 購票人的票務信息*/private List<Booking> bookings = new ArrayList<>();public Customer() {}public Customer(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<Booking> getBookings() {return bookings;}public void setBookings(List<Booking> bookings) {this.bookings = bookings;}}

下面的類 是模擬數據庫

package ai.spring.demo.ai.playground.data;import java.util.ArrayList;
import java.util.List;/*** 模擬數據庫表 包含票務相關信息*/
public class BookingData {private List<Customer> customers = new ArrayList<>();private List<Booking> bookings = new ArrayList<>();public List<Customer> getCustomers() {return customers;}public void setCustomers(List<Customer> customers) {this.customers = customers;}public List<Booking> getBookings() {return bookings;}public void setBookings(List<Booking> bookings) {this.bookings = bookings;}}

6、機票服務類(包含數據初始化、退票、改簽等業務方法)、智能體工具和智能體實現

package ai.spring.demo.ai.playground.services;import ai.spring.demo.ai.playground.data.*;
import ai.spring.demo.ai.playground.services.BookingTools.BookingDetails;import org.springframework.stereotype.Service;import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;/*** 模擬票務業務層*/
@Service
public class FlightBookingService {private final BookingData db;public FlightBookingService() {// 機票預定等服務db = new BookingData();//初始化機票票務信息 模擬數據庫initDemoData();}private void initDemoData() {//模擬數據庫List<String> names = List.of("趙敏", "周芷若", "小昭", "阿秀", "黃蓉","穆念慈","陸無雙","程英","郭芙","郭襄","鐘靈","曾柔","雙兒");List<String> airportCodes = List.of("北京", "上海", "廣州", "深圳", "杭州", "南京", "青島", "成都", "武漢", "西安", "重慶", "大連","天津");Random random = new Random();var customers = new ArrayList<Customer>();var bookings = new ArrayList<Booking>();for (int i = 0; i < 13; i++) {String name = names.get(i);String from = airportCodes.get(random.nextInt(airportCodes.size()));String to = airportCodes.get(random.nextInt(airportCodes.size()));BookingClass bookingClass = BookingClass.values()[random.nextInt(BookingClass.values().length)];Customer customer = new Customer();customer.setName(name);LocalDate date = LocalDate.now().plusDays(2 * (i + 1));Booking booking = new Booking("10" + (i + 1), date, customer, BookingStatus.CONFIRMED, from, to,bookingClass);customer.getBookings().add(booking);customers.add(customer);bookings.add(booking);}// Reset the database on each startdb.setCustomers(customers);db.setBookings(bookings);}/*** 獲取數據庫所有票務信息* @return*/public List<BookingDetails> getBookings() {return db.getBookings().stream().map(this::toBookingDetails).toList();}/*** 根據訂單號 合購票者姓名查找票務信息* @param bookingNumber* @param name* @return*/private Booking findBooking(String bookingNumber, String name) {return db.getBookings().stream().filter(b -> b.getBookingNumber().equalsIgnoreCase(bookingNumber)).filter(b -> b.getCustomer().getName().equalsIgnoreCase(name)).findFirst().orElseThrow(() -> new IllegalArgumentException("Booking not found"));}/*** 根據訂單號 合購票者姓名查找票務信息* @param bookingNumber* @param name* @return*/public BookingDetails getBookingDetails(String bookingNumber, String name) {var booking = findBooking(bookingNumber, name);return toBookingDetails(booking);}/*** 改簽票務* @param bookingNumber* @param name* @param newDate* @param from* @param to*/public void changeBooking(String bookingNumber, String name, String newDate, String from, String to) {var booking = findBooking(bookingNumber, name);if (booking.getDate().isBefore(LocalDate.now().plusDays(1))) {throw new IllegalArgumentException("Booking cannot be changed within 24 hours of the start date.");}booking.setDate(LocalDate.parse(newDate));booking.setFrom(from);booking.setTo(to);}/*** 退票* @param bookingNumber* @param name*/public void cancelBooking(String bookingNumber, String name) {var booking = findBooking(bookingNumber, name);if (booking.getDate().isBefore(LocalDate.now().plusDays(2))) {throw new IllegalArgumentException("Booking cannot be cancelled within 48 hours of the start date.");}booking.setBookingStatus(BookingStatus.CANCELLED);}private BookingDetails toBookingDetails(Booking booking) {return new BookingDetails(booking.getBookingNumber(), booking.getCustomer().getName(), booking.getDate(),booking.getBookingStatus(), booking.getFrom(), booking.getTo(), booking.getBookingClass().toString());}}
package ai.spring.demo.ai.playground.services;import java.time.LocalDate;
import java.util.function.Function;import ai.spring.demo.ai.playground.data.BookingStatus;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.core.NestedExceptionUtils;@Configuration
public class BookingTools {private static final Logger logger = LoggerFactory.getLogger(BookingTools.class);@Autowiredprivate FlightBookingService flightBookingService;/*** 獲取票務信息工具請求* @param bookingNumber* @param name*/public record BookingDetailsRequest(String bookingNumber, String name) {}/*** 改簽票務工具請求* @param bookingNumber* @param name* @param date* @param from* @param to*/public record ChangeBookingDatesRequest(String bookingNumber, String name, String date, String from, String to) {}/*** 退票工具請求* @param bookingNumber* @param name*/public record CancelBookingRequest(String bookingNumber, String name) {}/*** 獲取票務信息工具返回* @param bookingNumber* @param name* @param date* @param bookingStatus* @param from* @param to* @param bookingClass*/@JsonInclude(Include.NON_NULL)public record BookingDetails(String bookingNumber, String name, LocalDate date, BookingStatus bookingStatus,String from, String to, String bookingClass) {}/*** 獲取機票預定詳細信息 工具* @return*/@Bean@Description("獲取機票預定詳細信息")public Function<BookingDetailsRequest, BookingDetails> getBookingDetails() {return request -> {try {return flightBookingService.getBookingDetails(request.bookingNumber(), request.name());}catch (Exception e) {logger.warn("Booking details: {}", NestedExceptionUtils.getMostSpecificCause(e).getMessage());return new BookingDetails(request.bookingNumber(), request.name(), null, null, null, null, null);}};}/*** 修改機票預定日期工具* @return*/@Bean@Description("修改機票預定日期")public Function<ChangeBookingDatesRequest, String> changeBooking() {return request -> {flightBookingService.changeBooking(request.bookingNumber(), request.name(), request.date(), request.from(),request.to());return "";};}/*** 取消機票預定 工具* @return*/@Bean@Description("取消機票預定")public Function<CancelBookingRequest, String> cancelBooking() {return request -> {flightBookingService.cancelBooking(request.bookingNumber(), request.name());return "";};}}

/** Copyright 2024-2024 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package ai.spring.demo.ai.playground.services;import java.time.LocalDate;import reactor.core.publisher.Flux;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;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;/*** 智能機票助手 agent*/
@Service
public class CustomerSupportAssistant {private final ChatClient chatClient;public CustomerSupportAssistant(ChatClient.Builder modelBuilder, VectorStore vectorStore, ChatMemory chatMemory) {// 初始化 Agent 智能體this.chatClient = modelBuilder.defaultSystem("""您是“Funnair”航空公司的客戶聊天支持代理。請以友好、樂于助人且愉快的方式來回復。您正在通過在線聊天系統與客戶互動。您能夠支持已有機票的預訂詳情查詢、機票日期改簽、機票預訂取消等操作,其余功能將在后續版本中添加,如果用戶問的問題不支持請告知詳情。在提供有關機票預訂詳情查詢、機票日期改簽、機票預訂取消等操作之前,您必須始終從用戶處獲取以下信息:預訂號、客戶姓名。在詢問用戶之前,請檢查消息歷史記錄以獲取預訂號、客戶姓名等信息,盡量避免重復詢問給用戶造成困擾。在更改預訂之前,您必須確保條款允許這樣做。如果更改需要收費,您必須在繼續之前征得用戶同意。使用提供的功能獲取預訂詳細信息、更改預訂和取消預訂。如果需要,您可以調用相應函數輔助完成。請講中文。今天的日期是 {current_date}.""").defaultAdvisors(//對話記憶new PromptChatMemoryAdvisor(chatMemory), // Chat Memory// new VectorStoreChatMemoryAdvisor(vectorStore)),//RAG知識庫new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder().topK(4).similarityThresholdAll().build()), // RAG// new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()// 	.withFilterExpression("'documentType' == 'terms-of-service' && region in ['EU', 'US']")),// loggernew SimpleLoggerAdvisor())//定義工具  對應 BookingTools中的三個方法.defaultFunctions("getBookingDetails", "changeBooking", "cancelBooking") // FUNCTION CALLING.build();}public Flux<String> chat(String chatId, String userMessageContent) {return this.chatClient.prompt()
//				current_date  defaultSystem中的變量.system(s -> s.param("current_date", LocalDate.now().toString())).user(userMessageContent).advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)).stream().content();}}

7、機票列表接口和智能體對話接口

package ai.spring.demo.ai.playground.client;import ai.spring.demo.ai.playground.services.BookingTools.BookingDetails;
import ai.spring.demo.ai.playground.services.FlightBookingService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controller
@RequestMapping("/")
public class BookingController {private final FlightBookingService flightBookingService;public BookingController(FlightBookingService flightBookingService) {this.flightBookingService = flightBookingService;}@RequestMapping("/")public String index() {return "index";}/*** 獲取數據庫所有票務信息* @return*/@RequestMapping("/api/bookings")@ResponseBodypublic List<BookingDetails> getBookings() {return flightBookingService.getBookings();}}

package ai.spring.demo.ai.playground.client;import ai.spring.demo.ai.playground.services.CustomerSupportAssistant;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RequestMapping("/api/assistant")
@RestController
public class AssistantController {private final CustomerSupportAssistant agent;public AssistantController(CustomerSupportAssistant agent) {this.agent = agent;}@RequestMapping(path="/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> chat(String chatId, String userMessage) {return agent.chat(chatId, userMessage);}}

8、頁面展示如下

訪問? http://127.0.0.1:19000/

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76962.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76962.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76962.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

嵌入式C語言高級編程:OOP封裝、TDD測試與防御性編程實踐

一、面向對象編程(OOP) 盡管 C 語言并非面向對象編程語言&#xff0c;但借助一些編程技巧&#xff0c;也能實現面向對象編程&#xff08;OOP&#xff09;的核心特性&#xff0c;如封裝、繼承和多態。 1.1 封裝 封裝是把數據和操作數據的函數捆綁在一起&#xff0c;對外部隱藏…

藍橋杯 web 常考到的一些知識點

filter&#xff1a;filter方法創建一個新數組&#xff0c;其包含通過所提供函數實現的測試的所有元素。這個 方法不會改變原數組&#xff0c;而是返回一個新的數組。 map&#xff1a;map方法創建一個新數組&#xff0c;其結果是該數組中的每個元素都調用一個提供的函數后的 返回…

音視頻小白系統入門筆記-0

本系列筆記為博主學習李超老師課程的課堂筆記&#xff0c;僅供參閱 音視頻小白系統入門課 音視頻基礎ffmpeg原理 緒論 ffmpeg推流 ffplay/vlc拉流 使用rtmp協議 ffmpeg -i <source_path> -f flv rtmp://<rtmp_server_path> 為什么會推流失敗&#xff1f; 默認…

mysql按條件三表并聯查詢

下面為你呈現一個 MySQL 按條件三表并聯查詢的示例。假定有三個表&#xff1a;students、courses 和 enrollments&#xff0c;它們的結構和關聯如下&#xff1a; students 表&#xff1a;包含學生的基本信息&#xff0c;有 student_id 和 student_name 等字段。courses 表&…

UML之序列圖的消息

序列圖表現各參與者之間為完成某個行為而發生的交互及其時間順序&#xff0c;序列圖中的交互通過消息實現。消息是從一條生命線到另一條生命線的通信&#xff0c;它們通常是水平或傾斜向下的箭頭&#xff0c;從發送方生命線離開&#xff0c;到達接收方生命線。如果需要&#xf…

UniAD:自動駕駛的統一架構 - 創新與挑戰并存

引言 自動駕駛技術正經歷一場架構革命。傳統上&#xff0c;自動駕駛系統采用模塊化設計&#xff0c;將感知、預測和規劃分離為獨立組件。而上海人工智能實驗室的OpenDriveLab團隊提出的UniAD&#xff08;Unified Autonomous Driving&#xff09;則嘗試將這些任務整合到一個統一…

如何寫好合同管理系統需求分析

引言 在當今企業數字化轉型的浪潮中&#xff0c;合同管理系統作為企業法律合規和商業運營的重要支撐工具&#xff0c;其需求分析的準確性和完整性直接關系到系統建設的成敗。本文基于Volere需求過程方法論&#xff0c;結合江鈴汽車集團合同管理系統需求規格說明書實踐案例&…

libevent服務器附帶qt界面開發(附帶源碼)

本章是入門章節&#xff0c;講解如何實現一個附帶界面的服務器&#xff0c;后續會完善與優化 使用qt編譯libevent源碼演示視頻qt的一些知識 1.主要功能有登錄界面 2.基于libevent實現的服務器的業務功能 使用qt編譯libevent 下載這個&#xff0c;其他版本也可以 主要是github上…

八、自動化函數

1.元素的定位 web自動化測試的操作核心是能夠找到頁面對應的元素&#xff0c;然后才能對元素進行具體的操作。 常見的元素定位方式非常多&#xff0c;如id,classname,tagname,xpath,cssSelector 常用的主要由cssSelector和xpath 1.1 cssSelector選擇器 選擇器的功能&#x…

Web三漏洞學習(其二:sql注入)

靶場&#xff1a;NSSCTF 、云曦歷年考核題 二、sql注入 NSSCTF 【SWPUCTF 2021 新生賽】easy_sql 這題雖然之前做過&#xff0c;但為了學習sql&#xff0c;整理一下就再寫一次 打開以后是杰哥的界面 注意到html網頁標題的名稱是 “參數是wllm” 那就傳參數值試一試 首先判…

單片機非耦合業務邏輯框架

在小型單片機項目開發初期&#xff0c;由于業務邏輯相對簡單&#xff0c;我們往往較少關注程序架構層面的設計。 然而隨著項目經驗的積累&#xff0c;開發者會逐漸意識到模塊間的耦合問題&#xff1a;當功能迭代時&#xff0c;一處修改可能引發連鎖反應。 此時&#xff0c;構…

Zookeeper三臺服務器三節點集群部署(docker-compose方式)

1. 準備工作 - 服務器:3 臺服務器,IP 地址分別為 `10.10.10.11`、`10.10.10.12`、`10.10.10.13`。 - 安裝 Docker:確保每臺服務器已安裝 Docker 和 Docker Compose。 - 網絡通信:確保三臺服務器之間可以通過 IP 地址互相訪問,并開放以下端口: - `2181`:Zookeeper 客戶…

Mac關閉sip方法

Mac關閉sip方法 導航 文章目錄 Mac關閉sip方法導航完整操作流程圖詳細步驟 完整操作流程圖 這東西是我在網上搬運下來的&#xff0c;但是我在為業務實操過程中&#xff0c;根據實操情況還是有新的注意點的 詳細步驟 1.在「關于本機」-「系統報告」-「軟件」;查看SIP是否開啟…

C++| 深入剖析std::list底層實現:鏈表結構與內存管理機制

引言 std::list的底層實現基于雙向鏈表&#xff0c;其設計哲學與std::vector截然不同。本文將深入探討其節點結構、內存分配策略及迭代器實現原理&#xff0c;揭示鏈表的性能優勢和潛在代價。 1. 底層數據結構&#xff1a;雙向鏈表 每個std::list節點包含&#xff1a; 數據域…

漢諾塔問題——用貪心算法解決

目錄 一&#xff1a;起源 二&#xff1a;問題描述 三&#xff1a;規律 三&#xff1a;解決方案 遞歸算法 四&#xff1a;代碼實現 復雜度分析 一&#xff1a;起源 漢諾塔&#xff08;Tower of Hanoi&#xff09;問題起源于一個印度的古老傳說。在世界中心貝拿勒斯&#…

【Python】Python 100題 分類入門練習題 - 新手友好

Python 100題 分類入門練習題 - 新手友好篇 - 整合篇 一、數學問題題目1&#xff1a;組合數字題目2&#xff1a;利潤計算題目3&#xff1a;完全平方數題目4&#xff1a;日期天數計算題目11&#xff1a;兔子繁殖問題題目18&#xff1a;數列求和題目19&#xff1a;完數判斷題目21…

【linux】--- 進程概念

進程概念 1.認識馮諾依曼結構2. 操作系統&#xff08;Operator system)2.1 概念2.2 設計OS的目的2.3 理解操作系統2.4 如何理解管理2.5 理解系統調用和庫函數 3. 進程3.1 基本概念和基本操作3.1.1 描述進程 - PCB3.1.2 task_struct3.1.3 查看進程 3.2 進程狀態3.2.1 運行&&…

算法堆排序記錄

【算法】排序算法之堆排序 - 知乎 應用場景&#xff1a;獲取第n個大或者小的數 操作步驟&#xff1a; 1、將數組構造成堆 2、調整根節點為最大堆 ->倒序對每個根節點執行最大化 ->根節點最大化過程中如果發生交換&#xff0c;需要保證子節點也為最大堆&#xff08;執行…

STM32 模塊化開發實戰指南:系列介紹

本文是《STM32 模塊化開發實戰指南》系列的導讀篇,旨在介紹整個系列的寫作目的、適用讀者、技術路徑和每一篇的主題規劃。適合從事 STM32、裸機或 RTOS 嵌入式開發的個人開發者、初創工程師或企業項目團隊。 為什么要寫這個系列? 在嵌入式開發中,很多人剛開始都是從點亮一個…

【眼底輔助診斷開放平臺】項目筆記

這是一個標題 任務一前端頁面開發&#xff1a;后端接口配置&#xff1a; 任務二自行部署接入服務 日志修改樣式和解析MD文檔接入服務 Note前端登陸不進去/更改后端api接口304 Not Modifiedlogin.cache.jsonERR_CONNECTION_TIMED_OUT跨域一般提交格式proxy.ts src/coponents 目錄…