使用 Spring AI Aliabab Module RAG 構建 Web Search 應用

使用 Spring AI Alibaba 構建大模型聯網搜索應用

Spring AI 實現了模塊化 RAG 架構,架構的靈感來自于論文“模塊化 RAG:將 RAG 系統轉變為類似樂高的可重構框架”中詳述的模塊化概念。

Spring AI 模塊化 RAG 體系

在這里插入圖片描述

總體上分為以下幾個步驟:

Pre-Retrieval

增強和轉換用戶輸入,使其更有效地執行檢索任務,解決格式不正確的查詢、query 語義不清晰、或不受支持的語言等。

  1. QueryAugmenter 查詢增強:使用附加的上下文數據信息增強用戶 query,提供大模型回答問題時的必要上下文信息;
  2. QueryTransformer 查詢改寫:因為用戶的輸入通常是片面的,關鍵信息較少,不便于大模型理解和回答問題。因此需要使用 prompt 調優手段或者大模型改寫用戶 query;
  3. QueryExpander 查詢擴展:將用戶 query 擴展為多個語義不同的變體以獲得不同視角,有助于檢索額外的上下文信息并增加找到相關結果的機會。

Retrieval

負責查詢向量存儲等數據系統并檢索和用戶 query 相關性最高的 Document。

  1. DocumentRetriever:檢索器,根據 QueryExpander 使用不同的數據源進行檢索,例如 搜索引擎、向量存儲、數據庫或知識圖等;
  2. DocumentJoiner:將從多個 query 和從多個數據源檢索到的 Document 合并為一個 Document 集合;

Post-Retrieval

負責處理檢索到的 Document 以獲得最佳的輸出結果,解決模型中的中間丟失和上下文長度限制等。

  1. DocumentRanker:根據 Document 和用戶 query 的相關性對 Document 進行排序和排名;
  2. DocumentSelector:用于從檢索到的 Document 列表中刪除不相關或冗余文檔;
  3. DocumentCompressor:用于壓縮每個 Document,減少檢索到的信息中的噪音和冗余。

生成

生成用戶 Query 對應的大模型輸出。

Web Search 實踐

接下來,將演示如何使用 Spring AI Alibaba 和阿里云 IQS 服務搭建聯網搜索 RAG 的實現。

資源準備

DashScope apiKey:https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key

阿里云 IQS 服務 apiKey:https://help.aliyun.com/product/2837261.html

Pre-Retrieval

將用戶 Query 使用 qwen-plus 大模型進行增強改寫。

CustomContextQueryAugmenter.java

public class CustomContextQueryAugmenter implements QueryAugmenter {// 定義 prompt tmpl。private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = new PromptTemplate(// ......);private static final PromptTemplate DEFAULT_EMPTY_PROMPT_TEMPLATE = new PromptTemplate(// ...);@NotNull@Overridepublic Query augment(@Nullable Query query,@Nullable List<Document> documents) {// 1. collect content from documents.AtomicInteger idCounter = new AtomicInteger(1);String documentContext = documents.stream().map(document -> {String text = document.getText();return "[[" + (idCounter.getAndIncrement()) + "]]" + text;}).collect(Collectors.joining("\n-----------------------------------------------\n"));// 2. Define prompt parameters.Map<String, Object> promptParameters = Map.of("query", query.text(),"context", documentContext);// 3. Augment user prompt with document context.return new Query(this.promptTemplate.render(promptParameters));}// 當上下文為空時,返回 DEFAULT_EMPTY_PROMPT_TEMPLATEprivate Query augmentQueryWhenEmptyContext(Query query) {if (this.allowEmptyContext) {logger.debug("Empty context is allowed. Returning the original query.");return query;}logger.debug("Empty context is not allowed. Returning a specific query for empty context.");return new Query(this.emptyPromptTemplate.render());}public static final class Builder {// ......}
}

QueryTransformer 配置 bean,用于 rewrite 用戶 query:

@Bean
public QueryTransformer queryTransformer(ChatClient.Builder chatClientBuilder,@Qualifier("transformerPromptTemplate") PromptTemplate transformerPromptTemplate
) {ChatClient chatClient = chatClientBuilder.defaultOptions(DashScopeChatOptions.builder().withModel("qwen-plus").build()).build();return RewriteQueryTransformer.builder().chatClientBuilder(chatClient.mutate()).promptTemplate(transformerPromptTemplate).targetSearchSystem("聯網搜索").build();
}

QueryExpander.java 查詢變體

public class MultiQueryExpander implements QueryExpander {private static final Logger logger = LoggerFactory.getLogger(MultiQueryExpander.class);private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = new PromptTemplate(// ...);@NotNull@Overridepublic List<Query> expand(@Nullable Query query) {// ...String resp = this.chatClient.prompt().user(user -> user.text(this.promptTemplate.getTemplate()).param("number", this.numberOfQueries).param("query", query.text())).call().content();// ...List<String> queryVariants = Arrays.stream(resp.split("\n")).filter(StringUtils::hasText).toList();if (CollectionUtils.isEmpty(queryVariants) || this.numberOfQueries != queryVariants.size()) {return List.of(query);}List<Query> queries = queryVariants.stream().filter(StringUtils::hasText).map(queryText -> query.mutate().text(queryText).build()).collect(Collectors.toList());// 是否引入原查詢if (this.includeOriginal) {logger.debug("Including original query in the expanded queries for query: {}", query.text());queries.add(0, query);}return queries;}public static final class Builder {// ......}}

Retrieval

從不同數據源查詢和用戶 query 相似度最高的數據。(這里使用 Web Search)

WebSearchRetriever.java

public class WebSearchRetriever implements DocumentRetriever {// 注入 IQS 搜索引擎private final IQSSearchEngine searchEngine;@NotNull@Overridepublic List<Document> retrieve(@Nullable Query query) {// 搜索GenericSearchResult searchResp = searchEngine.search(query.text());// 清洗數據,將數據轉換為 Spring AI 的 Document 對象List<Document> cleanerData = dataCleaner.getData(searchResp);logger.debug("cleaner data: {}", cleanerData);// 返回結果List<Document> documents = dataCleaner.limitResults(cleanerData, maxResults);logger.debug("WebSearchRetriever#retrieve() document size: {}, raw documents: {}",documents.size(),documents.stream().map(Document::getId).toArray());return enableRanker ? ranking(query, documents) : documents;}private List<Document> ranking(Query query, List<Document> documents) {if (documents.size() == 1) {// 只有一個時,不需要 rankreturn documents;}try {List<Document> rankedDocuments = documentRanker.rank(query, documents);logger.debug("WebSearchRetriever#ranking() Ranked documents: {}", rankedDocuments.stream().map(Document::getId).toArray());return rankedDocuments;} catch (Exception e) {// 降級返回原始結果logger.error("ranking error", e);return documents;}}public static final class Builder {// ...}
}

DocumentJoiner.java 合并 Document

public class ConcatenationDocumentJoiner implements DocumentJoiner {@NotNull@Overridepublic List<Document> join(@Nullable Map<Query, List<List<Document>>> documentsForQuery) {// ...Map<Query, List<List<Document>>> selectDocuments = selectDocuments(documentsForQuery, 10);Set<String> seen = new HashSet<>();return selectDocuments.values().stream()// Flatten List<List<Documents>> to Stream<List<Documents>..flatMap(List::stream)// Flatten Stream<List<Documents> to Stream<Documents>..flatMap(List::stream).filter(doc -> {List<String> keys = extractKeys(doc);for (String key : keys) {if (!seen.add(key)) {logger.info("Duplicate document metadata: {}",doc.getMetadata());// Duplicate keys found.return false;}}// All keys are unique.return true;}).collect(Collectors.toList());}private Map<Query, List<List<Document>>> selectDocuments(Map<Query, List<List<Document>>> documentsForQuery,int totalDocuments) {Map<Query, List<List<Document>>> selectDocumentsForQuery = new HashMap<>();int numberOfQueries = documentsForQuery.size();if (Objects.equals(0, numberOfQueries)) {return selectDocumentsForQuery;}int baseCount = totalDocuments / numberOfQueries;int remainder = totalDocuments % numberOfQueries;// To ensure consistent distribution. sort the keys (optional)List<Query> sortedQueries = new ArrayList<>(documentsForQuery.keySet());// Other sort// sortedQueries.sort(Comparator.comparing(Query::getSomeProperty));Iterator<Query> iterator = sortedQueries.iterator();for (int i = 0; i < numberOfQueries; i ++) {Query query = sortedQueries.get(i);int documentToSelect = baseCount + (i < remainder ? 1 : 0);List<List<Document>> originalDocuments = documentsForQuery.get(query);List<List<Document>> selectedNestLists = new ArrayList<>();int remainingDocuments = documentToSelect;for (List<Document> documentList : originalDocuments) {if (remainingDocuments <= 0) {break;}List<Document> selectSubList = new ArrayList<>();for (Document docs : documentList) {if (remainingDocuments <= 0) {break;}selectSubList.add(docs);remainingDocuments --;}if (!selectSubList.isEmpty()) {selectedNestLists.add(selectSubList);}}selectDocumentsForQuery.put(query, selectedNestLists);}return selectDocumentsForQuery;}private List<String> extractKeys(Document document) {// 提取 keyreturn keys;}
}

Post-Retrieval

處理從聯網搜索種獲得的 Document,以獲得最佳輸出。

DashScopeDocumentRanker.java

public class DashScopeDocumentRanker implements DocumentRanker {// ...@NotNull@Overridepublic List<Document> rank(@Nullable Query query,@Nullable List<Document> documents) {// ...try {List<Document> reorderDocs = new ArrayList<>();// 由調用者控制文檔數DashScopeRerankOptions rerankOptions = DashScopeRerankOptions.builder().withTopN(documents.size()).build();if (Objects.nonNull(query) && StringUtils.hasText(query.text())) {// 組裝參數調用 rankModelRerankRequest rerankRequest = new RerankRequest(query.text(),documents,rerankOptions);RerankResponse rerankResp = rerankModel.call(rerankRequest);rerankResp.getResults().forEach(res -> {Document outputDocs = res.getOutput();// 查找并添加到新的 list 中Optional<Document> foundDocsOptional = documents.stream().filter(doc ->{// debug rerank output.logger.debug("DashScopeDocumentRanker#rank() doc id: {}, outputDocs id: {}", doc.getId(), outputDocs.getId());return Objects.equals(doc.getId(), outputDocs.getId());}).findFirst();foundDocsOptional.ifPresent(reorderDocs::add);});}return reorderDocs;}catch (Exception e) {// 根據異常類型做進一步處理throw new SAAAppException(e.getMessage());}}
}

大模型輸出

WebSearchService.java

@Service
public class SAAWebSearchService {// ...private static final String DEFAULT_WEB_SEARCH_MODEL = "deepseek-r1";public SAAWebSearchService(ChatClient.Builder chatClientBuilder,QueryTransformer queryTransformer,QueryExpander queryExpander,IQSSearchEngine searchEngine,DataClean dataCleaner,DocumentRanker documentRanker,@Qualifier("queryArgumentPromptTemplate") PromptTemplate queryArgumentPromptTemplate) {this.queryTransformer = queryTransformer;this.queryExpander = queryExpander;this.queryArgumentPromptTemplate = queryArgumentPromptTemplate;// 用于 DeepSeek-r1 的 reasoning content 整合到輸出中this.reasoningContentAdvisor = new ReasoningContentAdvisor(1);// 構建 chatClientthis.chatClient = chatClientBuilder.defaultOptions(DashScopeChatOptions.builder().withModel(DEFAULT_WEB_SEARCH_MODEL)// stream 模式下是否開啟增量輸出.withIncrementalOutput(true).build()).build();// 日志this.simpleLoggerAdvisor = new SimpleLoggerAdvisor(100);this.webSearchRetriever = WebSearchRetriever.builder().searchEngine(searchEngine).dataCleaner(dataCleaner).maxResults(2).enableRanker(true).documentRanker(documentRanker).build();}// 處理用戶輸入public Flux<String> chat(String prompt) {return chatClient.prompt().advisors(createRetrievalAugmentationAdvisor(),// 整合到 reasoning content 輸出中reasoningContentAdvisor,simpleLoggerAdvisor).user(prompt).stream().content();}// 創建 advisorprivate RetrievalAugmentationAdvisor createRetrievalAugmentationAdvisor() {return RetrievalAugmentationAdvisor.builder().documentRetriever(webSearchRetriever).queryTransformers(queryTransformer).queryAugmenter(new CustomContextQueryAugmenter(queryArgumentPromptTemplate,null,true)).queryExpander(queryExpander).documentJoiner(new ConcatenationDocumentJoiner()).build();}}

演示

使用問題 杭州有什么推薦旅游的地方嗎 為例。

普通輸出
### 必游景點  
1. **西湖**  - **核心特色**:杭州的標志性景點,包含“西湖十景”(如蘇堤春曉、斷橋殘雪、三潭印月等),可泛舟湖上或沿湖騎行。  - **推薦活動**:夜游西湖燈光秀、漫步白堤/蘇堤。  2. **靈隱寺與飛來峰**  - **文化價值**:千年古剎靈隱寺被譽為“東南佛國”,飛來峰的摩崖石刻為宋代佛教藝術瑰寶。  ---### 自然與生態  
1. **西溪國家濕地公園**  - **亮點**:國內首個國家濕地公園,可乘搖櫓船游覽,春秋季觀蘆葦、賞梅花。  2. **九溪十八澗**  - **特色**:茶園、溪流、古樹構成的徒步路線,適合夏季避暑。  ---### 文化體驗  
1. **宋城景區**  - **必看演出**:《宋城千古情》通過歌舞演繹杭州歷史,沉浸式體驗南宋文化。  2. **中國茶葉博物館(龍井館)**  - **體驗**:了解龍井茶文化,參與采茶、制茶活動,品鑒正宗西湖龍井。  ---### 美食街區  
1. **河坊街**  - **推薦小吃**:蔥包檜、定勝糕、西湖醋魚、東坡肉。  2. **武林夜市**  - **特色**:本地人常去的夜宵聚集地,匯聚浙江風味與網紅美食。  ---### 溫馨提示  
- **最佳季節**:春季(3-5月)賞桃柳,秋季(9-11月)觀桂花。  
- **交通建議**:西湖周邊景點集中,建議騎行或步行;地鐵1號線覆蓋主城區。  如需更詳細的行程規劃,可補充具體需求(如親子游、攝影主題等)。
聯網搜索輸出
### 杭州旅游推薦#### 1. 西湖風景區  
杭州的核心景點,包含斷橋殘雪、蘇堤春曉等經典景觀,四季景色各異,適合漫步或乘船游覽。清晨和傍晚的光線最佳,湖光山色與人文遺跡交融,是攝影和休閑的首選 [2][3][6]。  #### 2. 靈隱寺  
千年古剎隱于山林,古木參天,佛教氛圍濃厚。寺內素齋體驗和祈福活動值得嘗試,適合尋求寧靜的游客 [2][5][6]。  #### 3. 西溪國家濕地公園  
城市內的生態綠肺,河道縱橫,可乘船觀賞濕地風光,偶遇白鷺等水鳥。春季踏青、秋季賞蘆的絕佳地 [2][3][6]。  #### 4. 天竺三寺  
位于西湖區靈隱寺附近,由三座歷史悠久的寺廟組成,建筑風格獨特,環境清幽,適合文化探訪和秋日游覽 [1]。  #### 5. 杭州宋城  
以宋代風貌為主題的景區,可換古裝沉浸式體驗市井生活,大型演出《宋城千古情》融合歷史與藝術,視覺震撼 [2][5]。  #### 6. 河坊街  
古色古香的商業街,聚集傳統小吃、手工藝品店,可品嘗蔥包檜、定勝糕等美食,適合拍攝人文題材照片 [2][3][5]。  #### 7. 千島湖風景區  
以1078座島嶼聞名,梅峰島觀景臺可俯瞰全景,湖光山色如畫卷。適合自駕游和山水攝影 [3][6]。  #### 8. 茅家埠景區  
西湖邊的隱逸之地,春季櫻花與湖柳相映,秋季蘆葦搖曳,人少景美,適合徒步和自然攝影 [1][3]。  #### 9. 九溪煙樹(九溪十八澗)  
山澗、茶園與楓葉交織的徒步路線,秋季紅葉似火,溪水潺潺,充滿詩意 [2][6]。  #### 10. 太子灣公園  
春季郁金香、櫻花盛開,色彩斑斕,是熱門打卡地。適合家庭游和花卉攝影 [2][3]。  ---**其他推薦**  
- **浙西大龍灣**:自然峽谷與瀑布群,夏季漂流項目刺激 [1]。  
- **中國印學博物館**:展示印章文化與歷史,適合文化愛好者 [1]。  
- **塘棲古鎮**:運河畔的江南水鄉,保留明清建筑與民俗風情 [3][6]。  **旅行提示**  
- 西湖、靈隱寺等熱門景點建議提前預約門票 [5]。  
- 春季多雨,需攜帶雨具;秋季適合戶外活動 [2][5]。  (參考文檔:[1][2][3][5][6])

參考文檔

  1. Spring AI RAG:https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html
  2. 阿里云 IQS:https://help.aliyun.com/product/2837261.html

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

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

相關文章

一些練習 C 語言的小游戲

一些練習 C 語言的小游戲 — 1. 猜數字游戲 描述&#xff1a;程序隨機生成一個數字&#xff0c;玩家需要猜測這個數字&#xff0c;并根據提示&#xff08;太高或太低&#xff09;調整猜測&#xff0c;直到猜中為止。 功能點&#xff1a; 隨機數生成 (rand() 函數)。循環和…

關于中文編程的一些思考

隨著信息化與數字化的發展&#xff0c;工業4.0時代亦將徐徐到來。當計算機的普及程度越來越高&#xff0c;數據的產生、傳輸、處理等變得越來越快、越來越大量的時候&#xff0c;人們想要自動化辦公的愿望也越來越強烈&#xff0c;希望能將自身從耗費腦力但是重復繁瑣的工作中解…

golang 日志log與logrus

目錄 一、Go 標準庫 log 詳解 1. 功能特點 2. 常用函數 3. 示例代碼 4. 優勢和局限 二、第三方庫 logrus 詳解 1. 功能特點 2. 核心功能 3. 示例代碼 4. 優勢和擴展性 三、總結 1. 何時選擇 log&#xff1f; 2. 何時選擇 logrus&#xff1f; 3. 對比總結 一、Go 標…

消費品行業創新創業中品類創新與數字化工具的融合:以開源 AI 智能客服、AI 智能名片及 S2B2C 商城小程序為例

摘要&#xff1a; 本文聚焦于消費品行業的創新與創業&#xff0c;深入探討“選擇大于努力”這一觀點&#xff0c;強調品類選擇在品牌發展中的關鍵作用。同時&#xff0c;詳細分析了品類創新對于新消費品牌崛起以及傳統品牌轉型的重要意義。在此基礎上&#xff0c;引入開源 AI 智…

Razer macOS v0.4.10快速安裝

鏈接點這里下載最新的 .dmg 文件。將下載的 .dmg 映像文件拖入 應用程序 文件夾中。若首次打開時出現安全警告【什么扔到廢紙簍】&#xff0c;這時候點擊 Mac 的“系統偏好設置”-> “安全性與隱私”-> “通用”&#xff0c;然后點擊底部的 “打開”。【或者仍然打開】 對…

Flask項目部署:Flask + uWSGI + Nginx

目錄 1,網絡架構 2,環境安裝 2.1,安裝yum:Shell軟件包管理器 2.2 安裝python 2.3 安裝uWSGI 2.4 安裝Flask 3,上傳工程包到服務器,打包Flask項目 4,創建和配置 uwsgi 配置文件 uwsgi.ini 4.1配置文件 4.2配置文件注釋詳解 5,啟動服務 6,安裝nginx 7,nginx配置 8,…

[FPGA基礎學習]實現流水燈與按鍵暫停

FPGA實現LED流水燈 1.vscode的安裝和使用 vscode下載 Visual Studio Code - Code Editing. Redefined vscode插件&#xff08;Verilog-HDL/SystemVerilog&#xff09;下載 quartus綁定vscode 2.用6個LED完成周期為1秒的跑馬燈效果 流水燈模塊設計 時鐘輸入 DE2-115開發板…

【TensorRT】TensorRT從安裝到推理——Python 環境下 MobileNetV4 三分類任務

我想開發一個基于深度學習的分類小軟件&#xff0c;逐漸了解到了TensorRT在模型推理速度上的優勢&#xff0c;經過一下午資料的查找實現了將onnx模型轉為TensorRT格式模型的推理及測試過程。將實現過程記錄下來方便日后查看。 本文實驗設備是MX350顯卡 2G顯存 一 、安裝Tenso…

1.兩數之和(Java)

1. 題目描述 LeetCode 1. 兩數之和&#xff08;Two Sum&#xff09; 給定一個整數數組 nums 和一個目標值 target&#xff0c;請你在該數組中找出和為目標值的那兩個整數&#xff0c;并返回它們的索引。 示例 1&#xff1a; 輸入&#xff1a;nums [2,7,11,15], target 9 …

《深入探索 Python 數據分析:用 Pandas 高效處理與可視化大型數據集》

《深入探索 Python 數據分析:用 Pandas 高效處理與可視化大型數據集》 引言:從零到分析高手 數據是當代社會最寶貴的資源,而數據分析技能是現代職業人不可或缺的一部分。在數據科學的領域中,Python 已成為當之無愧的“首選語言”,其強大的生態系統和簡潔的語法讓人如虎添…

將樹莓派5當做Ollama服務器,C#調用generate的API的示例

其實完全沒這個必要&#xff0c;性能用腳后跟想都會很差。但基于上一篇文章的成果&#xff0c;來都來了就先簡單試試吧。 先來看看這個拼夕夕上五百多塊錢能達到的效果&#xff1a; 只要對速度沒要求&#xff0c;那感覺就還行。 Ollama默認只在本地回環&#xff08;127.0.0…

python基礎學習二(列表及字典的使用)

文章目錄 列表列表的創建獲取列表中的多個元素判斷列表中元素是否存在列表元素的添加操作列表元素的刪除操作列表元素的修改列表的排序列表生成式 字典字典的創建字典的常規操作字典的常用操作字典的視圖操作字典元素的遍歷字典的特點字典的生成式 列表 一個對象由id&#xff0…

Android設計模式之代理模式

一、定義&#xff1a; 為其他對象提供一種代理以控制對這個對象的訪問。 二、角色組成&#xff1a; Subject抽象主題&#xff1a;聲明真是主題與代理的共同接口方法&#xff0c;可以是一個抽象類或接口。 RealSubject真實主題&#xff1a;定義了代理表示的真實對象&#xff0c…

國外計算機證書推薦(考證)(6 Sigma、AWS、APICS、IIA、Microsoft、Oracle、PMI、Red Hat)

文章目錄 證書推薦1. 六西格瑪 (6 Sigma)2. 亞馬遜網絡服務 (AWS)3. 美國生產與庫存控制學會 (APICS)4. 內部審計師協會 (IIA)5. 微軟 (Microsoft)6. 甲骨文 (Oracle)7. 項目管理協會 (PMI)8. 紅帽 (Red Hat) 證書推薦 1. 六西格瑪 (6 Sigma) 介紹&#xff1a;六西格瑪是一種…

用mkdocs寫文檔#自動更新github-page

https://wuyisheng.github.io/blog 背景是上一篇博客 使用mkdocs&#xff0c;最后提及可以部署github page。這里說明下怎么自動部署。 當然&#xff0c;這篇博客主要的目的還是提供下github page的鏈接 &#xff1a;&#xff09; 我是這樣做的&#xff1a; step 1: pip3 i…

QT五 文件系統,QFile,QfileInfo

總覽 QIODevice&#xff1a;所有 I/O 設備類的父類&#xff0c;提供了字節塊讀寫的通用操作以及基本接口&#xff1b;QFileDevice&#xff1a;Qt5新增加的類&#xff0c;提供了有關文件操作的通用實現。QFlie&#xff1a;訪問本地文件或者嵌入資源&#xff1b;QTemporaryFile&a…

EF Core表達式樹

文章目錄 前言一、表達式樹與委托的區別二、動態構建表達式樹示例1示例2示例3高級技巧&#xff1a;表達式合并 三、ExpressionTreeToString安裝方法基本用法支持的格式化風格 四、注意事項總結 前言 在 Entity Framework Core 中&#xff0c;表達式樹&#xff08;Expression T…

NVM安裝速通使用手冊(Windows版)NVM管理node版本命令手冊 NVM使用手冊

nvm&#xff08;Node Version Manager&#xff09;是一個用于管理Node.js版本的命令行工具。通過nvm&#xff0c;你可以在同一臺機器上安裝和切換多個Node.js版本&#xff0c;非常適合開發和測試在不同Node.js版本上運行的應用程序 一、安裝地址 1. 官方下載&#xff1a; &…

vLLM命令行使用方法詳解

vLLM 是一個針對大語言模型(LLMs)優化的高效推理和服務庫。以下是 vLLM 命令行工具的詳細使用方法解析,涵蓋常見場景和參數配置: 一、核心命令行工具 vLLM 提供兩個主要的命令行入口: 啟動 API 服務器 用于部署 HTTP/OpenAI 兼容的 API 服務: python -m vllm.entrypoint…

# 基于 OpenCV 的選擇題自動批改系統實現

在教育領域&#xff0c;選擇題的批改工作通常較為繁瑣且重復性高。為了提高批改效率&#xff0c;我們可以利用計算機視覺技術&#xff0c;通過 OpenCV 實現選擇題的自動批改。本文將詳細介紹如何使用 Python 和 OpenCV 實現一個簡單的選擇題自動批改系統。 1. 項目背景 選擇題…