当前位置: 首页 > news >正文

spring-ai-alibaba第八章使用searxng构建大模型联网搜索应用

1、searxng安装配置 详见

anythingLLM结合searXNG实现联网搜索_anythingllm 配置 searxng-CSDN博客

2、本文介绍如何使用 Spring AI Alibaba 构建大模型联网搜索应用结合模块化 RAG(Module RAG)和信息检索服务(SearXNG)赋能大模型

总体上分为以下几个步骤:

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 的相关性对 Dcoument 进行排序和排名;
  2. DocumentSelector:用于从检索到的 Document 列表中删除不相关或冗余文档;
  3. DocumentCompressor:用于压缩每个 Document,减少检索到的信息中的噪音和冗余。

生成

生成用户 Query 对应的大模型输出。

Web Search 实践

接下来,将演示如何使用 Spring AI Alibaba 和searXNG搭建联网搜索 RAG 的实现。

资源准备

DashScope apiKey:如何获取API Key_大模型服务平台百炼(Model Studio)-阿里云帮助中心

 

Pre-Retrieval

将用户 Query 使用 qwen-plus 大模型进行增强改写。

CustomContextQueryAugmenter.java 

package com.alibaba.example.chatmemory.rag.prompt;import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.generation.augmentation.QueryAugmenter;
import org.springframework.ai.util.PromptAssert;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/public class CustomContextQueryAugmenter implements QueryAugmenter {private static final Logger logger = LoggerFactory.getLogger(CustomContextQueryAugmenter.class);private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = new PromptTemplate("""Context information is below:{{context}}Given the context information and no prior knowledge, answer the query.Follow these rules:1. If the answer is not in the context, just say that you don't know.2. Avoid statements like "Based on the context...." or "The provided information...".Query: {query}Answer:""");private static final PromptTemplate DEFAULT_EMPTY_PROMPT_TEMPLATE = new PromptTemplate("""The user query is outside your knowledge base.Politely inform the user that you cannot answer the query.""");private static final boolean DEFAULT_ALLOW_EMPTY_PROMPT = false;private final PromptTemplate promptTemplate;private final PromptTemplate emptyPromptTemplate;private final boolean allowEmptyContext;public CustomContextQueryAugmenter(@Nullable PromptTemplate promptTemplate,@Nullable PromptTemplate emptyPromptTemplate,@Nullable Boolean allowEmptyContext) {this.promptTemplate = promptTemplate != null ? promptTemplate : DEFAULT_PROMPT_TEMPLATE;this.emptyPromptTemplate = emptyPromptTemplate != null ? emptyPromptTemplate : DEFAULT_EMPTY_PROMPT_TEMPLATE;this.allowEmptyContext = allowEmptyContext != null ? allowEmptyContext : DEFAULT_ALLOW_EMPTY_PROMPT;logger.debug("CustomContextQueryAugmenter promptTemplate: {}", promptTemplate.getTemplate());logger.debug("CustomContextQueryAugmenter emptyPromptTemplate: {}", emptyPromptTemplate);logger.debug("CustomContextQueryAugmenter allowEmptyContext: {}", allowEmptyContext);PromptAssert.templateHasRequiredPlaceholders(this.promptTemplate, "query", "context");}@NotNull@Overridepublic Query augment(@Nullable Query query,@Nullable List<Document> documents) {Assert.notNull(query, "Query must not be null");Assert.notNull(documents, "Documents must not be null");logger.debug("Augmenting query: {}", query);if (documents.isEmpty()) {logger.debug("No documents found. Augmenting query with empty context.");return augmentQueryWhenEmptyContext(query);}logger.debug("Documents found. Augmenting query with context.");// 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));}private 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 {private PromptTemplate promptTemplate;private PromptTemplate emptyPromptTemplate;private Boolean allowEmptyContext;public Builder() {}public Builder withPromptTemplate(PromptTemplate promptTemplate) {this.promptTemplate = promptTemplate;return this;}public Builder withEmptyPromptTemplate(PromptTemplate emptyPromptTemplate) {this.emptyPromptTemplate = emptyPromptTemplate;return this;}public Builder withAllowEmptyContext(Boolean allowEmptyContext) {this.allowEmptyContext = allowEmptyContext;return this;}public CustomContextQueryAugmenter build() {return new CustomContextQueryAugmenter(promptTemplate, emptyPromptTemplate, allowEmptyContext);}}}

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

@Beanpublic 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("Web Search").build();}

PromptTemplate  配置bean  transformerPromptTemplate

	@Beanpublic PromptTemplate transformerPromptTemplate() {return new PromptTemplate("""Given a user query, rewrite the user question to provide better results when querying {target}.You should follow these rules:1. Remove any irrelevant information and make sure the query is concise and specific;2. The output must be consistent with the language of the user's query;3. Ensure better understanding and answers from the perspective of large models.Original query:{query}Query after rewrite:""");}

QueryExpander.java 查询变体  MultiQueryExpander.java   implements QueryExpander.java

package com.alibaba.example.chatmemory.rag.preretrieval.query.expansion;import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.preretrieval.query.expansion.QueryExpander;
import org.springframework.ai.util.PromptAssert;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/public class MultiQueryExpander implements QueryExpander {private static final Logger logger = LoggerFactory.getLogger(MultiQueryExpander.class);private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = new PromptTemplate("""You are an expert in information retrieval and search optimization.Generate {number} different versions of a given query.Each variation should cover a different perspective or aspect of the topic while maintaining the core intent ofthe original query. The goal is to broaden your search and improve your chances of finding relevant information.Don't interpret the selection or add additional text.Query variants are provided, separated by line breaks.Original query: {query}Query variants:""");private static final Boolean DEFAULT_INCLUDE_ORIGINAL = true;private static final Integer DEFAULT_NUMBER_OF_QUERIES = 3;private final ChatClient chatClient;private final PromptTemplate promptTemplate;private final boolean includeOriginal;private final int numberOfQueries;public MultiQueryExpander(ChatClient.Builder chatClientBuilder,@Nullable PromptTemplate promptTemplate,@Nullable Boolean includeOriginal,@Nullable Integer numberOfQueries) {Assert.notNull(chatClientBuilder, "ChatClient.Builder must not be null");this.chatClient = chatClientBuilder.build();this.promptTemplate = promptTemplate == null ? DEFAULT_PROMPT_TEMPLATE : promptTemplate;this.includeOriginal = includeOriginal == null ? DEFAULT_INCLUDE_ORIGINAL : includeOriginal;this.numberOfQueries = numberOfQueries == null ? DEFAULT_NUMBER_OF_QUERIES : numberOfQueries;PromptAssert.templateHasRequiredPlaceholders(this.promptTemplate, "number", "query");}@NotNull@Overridepublic List<Query> expand(@Nullable Query query) {Assert.notNull(query, "Query must not be null");logger.debug("Generating {} queries for query: {}", this.numberOfQueries, query.text());String resp = this.chatClient.prompt().user(user -> user.text(this.promptTemplate.getTemplate()).param("number", this.numberOfQueries).param("query", query.text())).call().content();logger.debug("MultiQueryExpander#expand() Response from chat client: {}", resp);if (Objects.isNull(resp)) {logger.warn("No response from chat client for query: {}. is return.", query.text());return List.of(query);}List<String> queryVariants = Arrays.stream(resp.split("\n")).filter(StringUtils::hasText).toList();if (CollectionUtils.isEmpty(queryVariants) || this.numberOfQueries != queryVariants.size()) {logger.warn("Query expansion result dose not contain the requested {} variants for query: {}. is return.",this.numberOfQueries, query.text());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);}logger.debug("Rewrite queries: {}", queries);return queries;}public static Builder builder() {return new Builder();}public static final class Builder {private ChatClient.Builder chatClientBuilder;private PromptTemplate promptTemplate;private Boolean includeOriginal;private Integer numberOfQueries;private Builder() {}public Builder chatClientBuilder(ChatClient.Builder chatClientBuilder) {this.chatClientBuilder = chatClientBuilder;return this;}public Builder promptTemplate(PromptTemplate promptTemplate) {this.promptTemplate = promptTemplate;return this;}public Builder includeOriginal(Boolean includeOriginal) {this.includeOriginal = includeOriginal;return this;}public Builder numberOfQueries(Integer numberOfQueries) {this.numberOfQueries = numberOfQueries;return this;}public MultiQueryExpander build() {return new MultiQueryExpander(this.chatClientBuilder, this.promptTemplate, this.includeOriginal, this.numberOfQueries);}}}

Retrieval

从不同数据源查询和用户 query 相似度最高的数据。(这里使用 Web Search)

WebSearchRetriever.java 
package com.alibaba.example.chatmemory.rag;import java.util.List;import com.alibaba.example.chatmemory.entity.websearch.GenericSearchResult;
import com.alibaba.example.chatmemory.rag.core.SearXNGSearchEngine;
import com.alibaba.example.chatmemory.rag.data.DataClean;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.ai.document.Document;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.postretrieval.ranking.DocumentRanker;
import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.lang.Nullable;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/public class WebSearchRetriever implements DocumentRetriever {private static final Logger logger = LoggerFactory.getLogger(WebSearchRetriever.class);//使用 searXNGprivate final SearXNGSearchEngine searchEngine;private final int maxResults;private final DataClean dataCleaner;private final DocumentRanker documentRanker;private final boolean enableRanker;private WebSearchRetriever(Builder builder) {this.searchEngine = builder.searchEngine;this.maxResults = builder.maxResults;this.dataCleaner = builder.dataCleaner;this.documentRanker = builder.documentRanker;this.enableRanker = builder.enableRanker;}@NotNull@Overridepublic List<Document> retrieve(@Nullable Query query) {// 搜索GenericSearchResult searchResp = searchEngine.search(query.text());// 清洗数据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 Builder builder() {return new Builder();}public static final class Builder {private SearXNGSearchEngine searchEngine;private int maxResults;private DataClean dataCleaner;private DocumentRanker documentRanker;// 默认开启 rankingprivate Boolean enableRanker = true;public Builder searchEngine(SearXNGSearchEngine searchEngine) {this.searchEngine = searchEngine;return this;}public Builder dataCleaner(DataClean dataCleaner) {this.dataCleaner = dataCleaner;return this;}public Builder maxResults(int maxResults) {this.maxResults = maxResults;return this;}public Builder documentRanker(DocumentRanker documentRanker) {this.documentRanker = documentRanker;return this;}public Builder enableRanker(Boolean enableRanker) {this.enableRanker = enableRanker;return this;}public WebSearchRetriever build() {return new WebSearchRetriever(this);}}}

 

ConcatenationDocumentJoiner.java implements  DocumentJoiner.java 合并 Document 

package com.alibaba.example.chatmemory.rag.join;import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.ai.document.Document;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.retrieval.join.DocumentJoiner;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/public class ConcatenationDocumentJoiner implements DocumentJoiner {private static final Logger logger = LoggerFactory.getLogger(ConcatenationDocumentJoiner.class);@NotNull@Overridepublic List<Document> join(@Nullable Map<Query, List<List<Document>>> documentsForQuery) {Assert.notNull(documentsForQuery, "documentsForQuery cannot be null");Assert.noNullElements(documentsForQuery.keySet(), "documentsForQuery cannot contain null keys");Assert.noNullElements(documentsForQuery.values(), "documentsForQuery cannot contain null values");logger.debug("Joining documents by concatenation");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) {List<String> keys = new ArrayList<>();if (Objects.nonNull(document)) {keys.add(document.getId());}if (Objects.nonNull(document.getMetadata())) {Object src = document.getMetadata().get("source");if (src instanceof String) {keys.add("SOURCE:" + src);}Object fn = document.getMetadata().get("file_name");if (fn instanceof String) {keys.add("FILE_NAME:" + fn);}}return keys;}}

Post-Retrieval

处理从联网搜索种获得的 Document,以获得最佳输出。

DashScopeDocumentRanker.java  implements DocumentRanker.java

package com.alibaba.example.chatmemory.rag.postretrieval;import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankOptions;
import com.alibaba.cloud.ai.model.RerankModel;
import com.alibaba.cloud.ai.model.RerankRequest;
import com.alibaba.cloud.ai.model.RerankResponse;
import com.alibaba.example.chatmemory.exception.SAAAppException1;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.ai.document.Document;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.postretrieval.ranking.DocumentRanker;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/public class DashScopeDocumentRanker implements DocumentRanker {private static final Logger logger = LoggerFactory.getLogger(DashScopeDocumentRanker.class);private final RerankModel rerankModel;public DashScopeDocumentRanker(RerankModel rerankModel) {this.rerankModel = rerankModel;}@NotNull@Overridepublic List<Document> rank(@Nullable Query query,@Nullable List<Document> documents) {if (documents == null || documents.isEmpty()) {return new ArrayList<>();}try {List<Document> reorderDocs = new ArrayList<>();// The caller controls the number of documentsDashScopeRerankOptions rerankOptions = DashScopeRerankOptions.builder().withTopN(documents.size()).build();if (Objects.nonNull(query) && StringUtils.hasText(query.text())) {// The assembly parameter calls rerankModelRerankRequest rerankRequest = new RerankRequest(query.text(),documents,rerankOptions);RerankResponse rerankResp = rerankModel.call(rerankRequest);rerankResp.getResults().forEach(res -> {Document outputDocs = res.getOutput();// Find and add to a new listOptional<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) {// Further processing is done depending on the type of exceptionthrow new SAAAppException1(e.getMessage());}}
}

 

大模型输出

SAAWebSearchService.java

package com.alibaba.example.chatmemory.service;import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.example.chatmemory.advisor.ReasoningContentAdvisor1;
import com.alibaba.example.chatmemory.rag.WebSearchRetriever;
import com.alibaba.example.chatmemory.rag.core.SearXNGSearchEngine;
import com.alibaba.example.chatmemory.rag.data.DataClean;
import com.alibaba.example.chatmemory.rag.join.ConcatenationDocumentJoiner;
import com.alibaba.example.chatmemory.rag.prompt.CustomContextQueryAugmenter;
import reactor.core.publisher.Flux;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.rag.postretrieval.ranking.DocumentRanker;
import org.springframework.ai.rag.preretrieval.query.expansion.QueryExpander;
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/@Service
public class SAAWebSearchService {private final ChatClient chatClient;private final SimpleLoggerAdvisor simpleLoggerAdvisor;private final ReasoningContentAdvisor1 reasoningContentAdvisor;private final QueryTransformer queryTransformer;private final QueryExpander queryExpander;private final PromptTemplate queryArgumentPromptTemplate;private final WebSearchRetriever webSearchRetriever;// It works better here with DeepSeek-R1private static final String DEFAULT_WEB_SEARCH_MODEL = "deepseek-r1";public SAAWebSearchService(ChatClient.Builder chatClientBuilder,QueryTransformer queryTransformer,QueryExpander queryExpander,SearXNGSearchEngine searchEngine,DataClean dataCleaner,DocumentRanker documentRanker,@Qualifier("queryArgumentPromptTemplate") PromptTemplate queryArgumentPromptTemplate) {this.queryTransformer = queryTransformer;this.queryExpander = queryExpander;this.queryArgumentPromptTemplate = queryArgumentPromptTemplate;// reasoning content for DeepSeek-r1 is integrated into the outputthis.reasoningContentAdvisor = new ReasoningContentAdvisor1(1);// Build 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();}//Handle user inputpublic Flux<String> chat(String prompt) {return chatClient.prompt().advisors(createRetrievalAugmentationAdvisor(),reasoningContentAdvisor,simpleLoggerAdvisor).user(prompt).stream().content();}private RetrievalAugmentationAdvisor createRetrievalAugmentationAdvisor() {return RetrievalAugmentationAdvisor.builder().documentRetriever(webSearchRetriever).queryTransformers(queryTransformer).queryAugmenter(new CustomContextQueryAugmenter(queryArgumentPromptTemplate,null,true)).queryExpander(queryExpander).documentJoiner(new ConcatenationDocumentJoiner()).build();}}

PromptTemplate  配置bean  queryArgumentPromptTemplate

@Beanpublic PromptTemplate queryArgumentPromptTemplate() {return new PromptTemplate("""You'll get a set of document contexts that are relevant to the issue.Each document begins with a reference number, such as [[x]], where x is a number that can be repeated.Documents that are not referenced will be marked as [[null]].Use context and refer to it at the end of each sentence, if applicable.The context information is as follows:---------------------{context}---------------------Generate structured responses to user questions given contextual information and without prior knowledge.When you answer user questions, follow these rules:1. If the answer is not in context, say you don't know;2. Don't provide any information that is not relevant to the question, and don't output any duplicate content;3. Avoid using "context-based..." or "The provided information..." said;4. Your answers must be correct, accurate, and written in an expertly unbiased and professional tone;5. The appropriate text structure in the answer is determined according to the characteristics of the content, please include subheadings in the output to improve readability;6. When generating a response, provide a clear conclusion or main idea first, without a title;7. Make sure each section has a clear subtitle so that users can better understand and refer to your output;8. If the information is complex or contains multiple sections, make sure each section has an appropriate heading to create a hierarchical structure;9. Please refer to the sentence or section with the reference number at the end in [[x]] format;10. If a sentence or section comes from more than one context, list all applicable references, e.g. [[x]][[y]];11. Your output answers must be in beautiful and rigorous markdown format.12. Because your output is in markdown format, please include the link in the reference document in the form of a hyperlink when referencing the context, so that users can click to view it;13. If a reference is marked as [[null]], it does not have to be cited;14. Except for Code. Aside from the specific name and citation, your answer must be written in the same language as the question.User Issue:{query}Your answer:""");}

异常类

/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You 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**     http://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 com.alibaba.example.chatmemory.exception;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/public class SAAAppException1 extends RuntimeException {private final static Logger logger = LoggerFactory.getLogger(SAAAppException1.class);public SAAAppException1(String msg) {super(msg);logger.error("Spring AI Playground app exception: {}", msg);}}

searXNG 搜索结果转 java类

package com.alibaba.example.chatmemory.entity.websearch;import java.util.List;
import java.util.Map;import com.aliyun.tea.NameInMap;
import com.aliyun.tea.TeaModel;public class GenericSearchResult extends TeaModel {@NameInMap("results")public List<ScorePageItem> results;@NameInMap("query")public String query;@NameInMap("number_of_results")public Integer numberOfResults;public GenericSearchResult() {}public static GenericSearchResult build(Map<String, ?> map) throws Exception {GenericSearchResult self = new GenericSearchResult();return (GenericSearchResult) TeaModel.build(map, self);}public List<ScorePageItem> getResults() {return results;}public GenericSearchResult setResults(List<ScorePageItem> results) {this.results = results;return this;}public String getQuery() {return query;}public GenericSearchResult setQuery(String query) {this.query = query;return this;}public Integer getNumberOfResults() {return numberOfResults;}public GenericSearchResult setNumberOfResults(Integer numberOfResults) {this.numberOfResults = numberOfResults;return this;}
}

package com.alibaba.example.chatmemory.entity.websearch;import com.aliyun.tea.NameInMap;
import com.aliyun.tea.TeaModel;public class ScorePageItem extends TeaModel {/*** <p>This parameter is required.</p>** <strong>example:</strong>* <p>structure_web_info</p>*/@NameInMap("engine")public String engine;/*** <p>This parameter is required.</p>** <strong>example:</strong>* <p>baijiahao.baidu.com</p>*/@NameInMap("template")public String template;/*** <strong>example:</strong>* <p><a href="https://s2.zimgs.cn/ims?kt=url&at=smstruct&key=aHR0cHM6Ly9ndy5hbGljZG4uY29tL0wxLzcyMy8xNTY1MjU2NjAwLzJhL2YwL2I0LzJhZjBiNDQxMGI5YmVlMDVjOGVlNGJmODk3MTNkNTFjLnBuZw==&sign=yx:CUlNNQVJQjFrk3Kxt2F3KWhTOFU=&tv=400_400">https://s2.zimgs.cn/ims?kt=url&amp;at=smstruct&amp;key=aHR0cHM6Ly9ndy5hbGljZG4uY29tL0wxLzcyMy8xNTY1MjU2NjAwLzJhL2YwL2I0LzJhZjBiNDQxMGI5YmVlMDVjOGVlNGJmODk3MTNkNTFjLnBuZw==&amp;sign=yx:CUlNNQVJQjFrk3Kxt2F3KWhTOFU=&amp;tv=400_400</a></p>*/@NameInMap("category")public String category;/*** <p>This parameter is required.</p>** <strong>example:</strong>* <p><a href="https://baijiahao.baidu.com/s?id=1787881554557805096">https://baijiahao.baidu.com/s?id=1787881554557805096</a></p>*/@NameInMap("url")public String url;/*** <strong>example:</strong>* <p>昨天	,	小米	汽车	没有	发布	,	但	相关	的	信息	透露	的	差	不	多	了	。*         在	发布	会	现场	,	雷军	直接	称	小米	S	U	7	对	标	特斯拉	保时捷	,	有	100	项	行业	领先	,	可见	“	遥遥	领先	”	已经	不再	是	华为	专利	了	?		</p>* <pre><code>    而	在	介绍	技术	时	,	雷军	也	从	电机	、	电池	、	大	压铸	、	自动	驾驶	、	智能	座舱	等	五	大	方面	展开	,	充分	展示	了	小米	汽车	的	技术	以及	技术	储备	,		 		能	堆	的	料	,	全都	堆	上去	了	…	…*     大家	比较	感	兴趣	的	性能	方面	,	2	.	78	s	的	0	-	100	km	/	h	加速	,	265	km	/	h	的	最高	时速* </code></pre>*/@NameInMap("content")public String content;/*** <p>This parameter is required.</p>** <strong>example:</strong>* <p>1704426524000</p>*/@NameInMap("publishedDate")public String  publishedDate;/*** <strong>example:</strong>* <p>0.234325235</p>*/@NameInMap("score")public Double score;/*** <p>This parameter is required.</p>** <strong>example:</strong>* <p>小米SU7售价22.99万元起 高管亲自辟谣:发布前不会有价格</p>*/@NameInMap("title")public String title;public static ScorePageItem build(java.util.Map<String, ?> map) throws Exception {ScorePageItem self = new ScorePageItem();return TeaModel.build(map, self);}public String getEngine() {return engine;}public ScorePageItem setEngine(String engine) {this.engine = engine;return this;}public String getTemplate() {return template;}public ScorePageItem setTemplate(String template) {this.template = template;return this;}public String getCategory() {return category;}public ScorePageItem setCategory(String category) {this.category = category;return this;}public String getUrl() {return url;}public ScorePageItem setUrl(String url) {this.url = url;return this;}public String getContent() {return content;}public ScorePageItem setContent(String content) {this.content = content;return this;}public String getPublishedDate() {return publishedDate;}public ScorePageItem setPublishedDate(String publishedDate) {this.publishedDate = publishedDate;return this;}public Double getScore() {return score;}public ScorePageItem setScore(Double score) {this.score = score;return this;}public String getTitle() {return title;}public ScorePageItem setTitle(String title) {this.title = title;return this;}
}

其他bean配置

	@Beanpublic QueryExpander queryExpander(ChatClient.Builder chatClientBuilder) {ChatClient chatClient = chatClientBuilder.defaultOptions(DashScopeChatOptions.builder().withModel("qwen-plus").build()).build();return MultiQueryExpander.builder().chatClientBuilder(chatClient.mutate()).numberOfQueries(2).build();}@Beanpublic SimpleLoggerAdvisor simpleLoggerAdvisor() {return new SimpleLoggerAdvisor();}@Beanpublic DashScopeDocumentRanker dashScopeDocumentRanker(RerankModel rerankModel) {return new DashScopeDocumentRanker(rerankModel);}

controller

package com.alibaba.example.chatmemory.controller;import com.alibaba.example.chatmemory.service.SAAWebSearchService;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import reactor.core.publisher.Flux;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>* <p>* The deepseek-r1 model is used by default, which works better.*/@RestController
@Tag(name = "Web Search APIs")
@RequestMapping("/api/v1/")
public class SAAWebSearchController1 {private final SAAWebSearchService webSearch;public SAAWebSearchController1(SAAWebSearchService webSearch) {this.webSearch = webSearch;}@GetMapping("/search")public Flux<String> search(@RequestParam(value = "query") String query,HttpServletResponse response) {response.setCharacterEncoding("UTF-8");return webSearch.chat(query);}}

不联网测试如下

联网测试如下

 

相关文章:

spring-ai-alibaba第八章使用searxng构建大模型联网搜索应用

1、searxng安装配置 详见 anythingLLM结合searXNG实现联网搜索_anythingllm 配置 searxng-CSDN博客 2、本文介绍如何使用 Spring AI Alibaba 构建大模型联网搜索应用结合模块化 RAG&#xff08;Module RAG&#xff09;和信息检索服务&#xff08;SearXNG&#xff09;赋能大模…...

C#:is关键字

目录 is 关键字的核心是什么&#xff1f; 1. 什么是 is 关键字&#xff0c;为什么要用它&#xff1f; 2. 如何使用 is 关键字&#xff1f; 3. is 的作用和场景 4. is 与 as 的区别 5. 模式匹配的扩展&#xff08;C# 8.0&#xff09; 6. 常见陷阱和注意事项 总结&#x…...

SpringCloud第二篇:注册中心Eureka

注册中心的意义 注册中心 管理各种服务功能包括服务的注册、发现、熔断、负载、降级等&#xff0c;比如dubbo admin后台的各种功能。 有了注册中心&#xff0c;调用关系的变化&#xff0c;画几个简图来看一下。(了解源码可求求: 1791743380) 服务A调用服务B 有了注册中心之后&a…...

CSS语言的硬件驱动

CSS语言的硬件驱动探讨 引言 随着信息技术的迅猛发展&#xff0c;硬件和软件之间的交互愈发复杂&#xff0c;特别是在嵌入式系统、物联网设备等领域&#xff0c;硬件驱动程序的开发变得至关重要。而在众多编程语言中&#xff0c;CSS&#xff08;层叠样式表&#xff09;作为一…...

浅入浅出:从传统开发者角度去了解区块链和智能合约之间的关系

前言 在传统开发者视角:智能合约与区块链数据库探秘文中我为大家简单的讲解了DApp开发中智能合约开发和传统开发中数据存储层面的不同。而智能合约则是DApp中重要的组成部分,如同传统开发中的后端。 但是我们不要忘记的是:智能合约是应区块链而生的。 那么对于区块链来说…...

使用人工智能大模型DeepSeek,如何免费辅助教学?

今天我们学习DeepSeek工具如何辅助教学?DeepSeek功能很强大&#xff0c;带动人工智能快速发展&#xff0c;这里给DeepSeek点个赞。免费手把手学习视频地址&#xff1a;https://edu.csdn.net/learn/40402/666415 第一步&#xff0c;进入DeepSeek官网。打开google浏览器&#x…...

leetcode-代码随想录-链表-链表理论基础

链表&#xff1a; 通过指针串联在一起的线性结构&#xff1b;每个节点包含两部分&#xff1a;数据域、指针域&#xff08;存放下一个节点的指针&#xff09;入口节点&#xff1a;称为 头节点 head最后一个节点的指针指向 NULL&#xff08;空指针&#xff09; 链表的类型 1. 单…...

dify中配置使用Ktransformer模型

一共是两个框架一个是Ktransformer,一个是dify。 Ktransformer用来部署LLM,比如Deepseek,而LLm的应用框架平台Dify主要用来快速搭建基于LLM应用。 这篇教程主要是用来介绍两个框架的交互与对接的,不是部署Ktransformer也部署部署Dify,要部署Dify、Ktransformer可以直接参考…...

解释区块链技术的应用场景和优势

区块链技术是一种基于分布式账本的技术&#xff0c;被广泛应用于多个领域。以下是区块链技术的主要应用场景和优势&#xff1a; 应用场景&#xff1a; 金融领域&#xff1a;区块链可以用于支付结算、跨境汇款、智能合约等金融服务&#xff0c;提高交易效率和降低成本。物联网…...

明清两朝全方位对比

明清两朝是中国历史上最后两个封建王朝&#xff0c;在政治、经济、文化等方面存在显著差异&#xff0c;以下为主要区别&#xff1a; 一、政治制度 皇权集中程度 明朝&#xff1a;废除丞相制度&#xff0c;设内阁辅助皇帝&#xff0c;但中后期宦官专权&#xff08;如刘瑾、魏…...

Mysql的事务

事务的概念 简单的说事务就是一个连贯性任务,只有一起成功或者一起失败的说法。在mysql的事务中要么事务里的sql语句成功执行,其中有出错就回滚到事务开始时候的状态。对于已经提交的事务来说,该事务对数据库所做的修改将永久生效事务的四大特性ACID 原子性(Atomicity):一件…...

chromium魔改——绕过无限debugger反调试

在进行以下操作之前&#xff0c;请确保已完成之前文章中提到的 源码拉取及编译 部分。 如果已顺利完成相关配置&#xff0c;即可继续执行后续操作。 在浏览器中实现“无限 debugger”的反调试技术是一种常见的手段&#xff0c;用于防止他人通过开发者工具对网页进行调试或逆向…...

【力扣hot100题】(051)腐烂的橘子

我讨厌图论。 这道题写了特别久&#xff0c;不过好歹也是写出来了…… 方法是先将橘子全部遍历一遍&#xff0c;做两件事&#xff1a;①找出所有连通的橘子②找出所有腐烂的橘子&#xff0c;设置一个vector<queue<int>>&#xff0c;每个vector元素代表一片连通的…...

PyTorch实现线性回归的基础写法与封装API写法

目录 1. 基础写法 1.1导包 2.2加载读取数据 2.3原始数据可视化(画图显示) 2.4线性回归的(基础)分解写法 2.5定义训练过程 2.PyTorch实现 线性回归的封装写法(实际项目中的常用写法) 2.1创建线性回归模型 2.2定义损失函数 2.3定义优化器 2.4定义训练过程 1…...

【蓝桥杯】算法笔记3

1. 最长上升子序列(LIS) 1.1. 题目 想象你有一排数字,比如:3, 1, 2, 1, 8, 5, 6 你要从中挑出一些数字,这些数字要满足两个条件: 你挑的数字的顺序要和原来序列中的顺序一致(不能打乱顺序) 你挑的数字要一个比一个大(严格递增) 问:最多能挑出多少个这样的数字? …...

【Linux】条件变量封装类及环形队列的实现

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

wsl2 配置ubuntu 固定ip

提示&#xff1a;环境搭建 文章目录 前言一、安装sshd 服务1. ubuntu 子系统安装 openssh-server2.配置sshd 开启密码链接3.配置sshd 服务开机启动 二、配置固定IP1 查看i2 查看路由3 查看wsl虚拟网卡4 配置wsl 子系统网卡4 设置生效 三、问题1. ssh 无法远程 前言 提示&#…...

电机控制学习路线

一、基础理论准备阶段 电路与电子技术 电路分析基础&#xff08;基尔霍夫定律、动态电路分析&#xff09; 模拟电子技术&#xff08;放大器、滤波电路、功率器件&#xff09; 数字电子技术&#xff08;逻辑电路、微控制器基础&#xff09; 数学工具 线性代数&#xff08;矩…...

Sensodrive力控关节模组SensoJoint:TÜV安全认证助力机器人开发

在机器人技术领域&#xff0c;安全性和开发效率是行业关注的重点。SensoDrive的SensoJoint 机器人力控关节模组&#xff0c;凭借其可靠的安全性能和高效的开发优势&#xff0c;正在为机器人开发提供有力支持。 2025年3月31日&#xff0c;SensoDrive的 SensoJoint 力控关节模组获…...

【橘子大模型】Runnable和Chain以及串行和并行

一、Runnable 前面我们实现了一些关于如何和大模型进行交互的操作。那么我们此时来回顾一下我们当前进行的结构。 我们已经很清楚这些操作的具体含义了&#xff0c;所以我这里就不在多介绍了。我们来看其中的几个点 1、用户那边就是客户&#xff0c;没啥说的。 2、langchain&…...

数据结构 -- 图的存储

图的存储 邻接矩阵法 邻接矩阵存储不带权图 0 - 表示两个顶点不邻接 1 - 表示两个顶点邻接 在无向图中&#xff0c;每条边在矩阵中对应两个1 在有向图中&#xff0c;每条边在矩阵中对应一个1 //不带权图的邻接矩阵存储 #define MaxVertexNum 100 //顶点数目的最大值 typed…...

基于大模型预测不稳定性心绞痛的多维度研究与应用

目录 一、引言 1.1 研究背景与意义 1.2 研究目的 1.3 国内外研究现状 二、不稳定性心绞痛概述 2.1 定义与分类 2.2 发病机制 2.3 临床表现 三、大模型技术原理与应用基础 3.1 大模型介绍 3.2 在医疗领域的应用现状 3.3 用于不稳定性心绞痛预测的可行性 四、术前预…...

【Java集合】LinkedList源码深度分析

参考笔记&#xff1a;java LinkedList 源码分析&#xff08;通俗易懂)_linkedlist源码分析-CSDN博客 目录 1.前言 2.LinkedList简介 3.LinkedList的底层实现 4.LinkedList 与 ArrayList 的对比 4.1 如何选择 4.2 对比图 5.LinkedList 源码Debug 5.1 add(E e) &#xff…...

Java 大视界 -- Java 大数据在智能供应链库存优化与成本控制中的应用策略(172)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

高并发系统架构设计核心要点的结构化提炼【大模型总结】

以下是对高并发系统架构设计核心要点的结构化提炼&#xff0c;结合技术深度与实践视角&#xff0c;以清晰的层次呈现关键策略与实现路径&#xff1a; 一、核心理念重塑 1. 容错优先思维 设计哲学&#xff1a;从"零故障"转向"可恢复性"设计&#xff0c;接…...

【C#深度学习之路】如何使用C#实现Stable Diffusion的文生图功能

【C#深度学习之路】如何使用C#实现Stable Diffusion的文生图功能 项目背景项目实现写在最后项目下载链接 本文为原创文章&#xff0c;若需要转载&#xff0c;请注明出处。 原文地址&#xff1a;https://blog.csdn.net/qq_30270773/article/details/147002073 项目对应的Github地…...

k8s的pod的概述和配置

概念 Pod 容器组 是一个k8s中一个抽象的概念&#xff0c;用于存放一组 container&#xff08;可包含一个或多个 container 容器&#xff0c;即图上正方体)&#xff0c;以及这些 container &#xff08;容器&#xff09;的一些共享资源。这些资源包括&#xff1a; 共享存储&…...

RTOS任务句柄的作用

任务句柄(Task Handle)在 FreeRTOS 中的作用详解 任务句柄(TaskHandle_t)是 FreeRTOS 中用于 唯一标识和管理任务 的核心机制,本质是一个指向任务控制块(TCB)的指针。说明即便创建的任务名相同,但对应的任务句柄一定是不同。 它在任务管理、通信、调试中起到关键作用,…...

OpenVLA-OFT——微调VLA的三大关键设计:并行解码、动作分块、连续动作表示以及L1回归目标

前言 25年3.26日&#xff0c;这是一个值得纪念的日子&#xff0c;这一天&#xff0c;我司「七月在线」的定位正式升级为了&#xff1a;具身智能的场景落地与定制开发商 &#xff0c;后续则从定制开发 逐步过渡到 标准产品化 比如25年q2起&#xff0c;在定制开发之外&#xff0…...

LocaDate、LocalTime、LocalDateTime

Java8的时间处理 Java的时间处理在早期版本中存在诸多问题&#xff08;如 java.util.Date 和 java.util.Calendar 的混乱设计&#xff09;&#xff0c;但Java8引入了引入了全新的 java.time包&#xff08;基于JSR 310&#xff09;&#xff0c;提供了更清晰、线程安全且强大的时…...

哈密尔顿路径(Hamiltonian Path)及相关算法题目

哈密尔顿路径要求访问图中每个顶点恰好一次&#xff0c;通常用于解决旅行商问题&#xff08;TSP&#xff09;或状态压缩DP问题。 哈密尔顿路径&#xff08;Hamiltonian Path&#xff09;是指在一个图中经过每个顶点恰好一次的路径。如果这条路径的起点和终点相同&#xff08;即…...

突破传统限制!全新端到端开放词汇多目标跟踪框架OVTR,开启视觉追踪新纪元

在自动驾驶和智能监控等场景中&#xff0c;多目标跟踪&#xff08;MOT&#xff09;技术需要应对现实世界中层出不穷的新物体类别。传统方法依赖预定义类别&#xff0c;面对“无人机配件”“新型宠物”等未知目标时往往失效。上海人工智能实验室团队提出的OVTR&#xff08;Open-…...

Springboot + Vue + WebSocket + Notification实现消息推送功能

实现功能 基于Springboot与Vue架构&#xff0c;首先使用Websocket实现频道订阅&#xff0c;在实现点对点与群发功能后&#xff0c;在前端调用windows自带的消息通知&#xff0c;实现推送功能。 开发环境 Springboot 2.6.7vue 2.6.11socket-client 1.0.0 准备工作 在 Vue.js…...

Linux内核物理内存组织结构

一、系统调用sys_mmap 系统调用mmap用来创建内存映射&#xff0c;把创建内存映射主要的工作委托给do_mmap函数&#xff0c;内核源码文件处理&#xff1a;mm/mmap.c 二、系统调用sys_munmap 1、vma find_vma (mm, start); // 根据起始地址找到要删除的第一个虚拟内存区域 vma 2…...

Redis高级技能进阶

什么是事务的ACID 事务是由一系列对系统中数据进行访问或更新的操作组成的程序执行逻辑单元。这些操作要么都执行&#xff0c;要么都不执行。 为了保证数据库的一致性&#xff0c;在事务处理之前和之后&#xff0c;都应该遵循某些规则&#xff0c;也就是大家耳熟能详的ACID。 …...

PCB设计基础:面向嵌入式工程师的系统性指南

嵌入式系统的性能、稳定性和可靠性&#xff0c;很大程度上依赖于电路硬件的设计质量。在硬件设计中&#xff0c;PCB&#xff08;Printed Circuit Board&#xff09;设计是连接系统功能与实际运行的关键一环。本文将从嵌入式工程师的视角&#xff0c;系统性地介绍PCB设计的关键基…...

aspark 配置2

编写Hadoop集群启停脚本 1.建立新文件&#xff0c;编写脚本程序 在hadoop101中操作&#xff0c;在/root/bin下新建文件&#xff1a;myhadoop&#xff0c;输入如下内容&#xff1a; 2.分发执行权限 保存后退出&#xff0c;然后赋予脚本执行权限 [roothadoop101 ~]$ chmod x /r…...

【统计方法】LASSO筛变量

启 比较原始做LASSO包是library(glmnet) 若目标是纯 LASSO 分析&#xff0c;alpha 必须设为 ​​1 ​​标准化数据​​&#xff1a;LASSO 对特征的尺度敏感&#xff0c;需对数据进行标准化&#xff08;均值为0&#xff0c;方差为1&#xff09;。 cv.glmnet​获得的lambda.m…...

拥抱健康生活,书写养生新篇

在快节奏的现代生活中&#xff0c;健康愈发成为人们关注的焦点。践行健康养生&#xff0c;并非是一种选择&#xff0c;而是我们对自己和家人应尽的责任。掌握正确的养生之道&#xff0c;不仅能提升生活品质&#xff0c;更能让生命焕发出新的活力。​ 合理饮食是健康的基石。一…...

Shiro学习(五):Shiro对权限的缓存

一、问题描述 由前边的学习中了解&#xff0c;用户的角色权限一般存储在数据库中&#xff0c;每次进行权限校验时都要从 数据库查询用户的角色权限信息&#xff1b;对数据库来说这样频繁的查询压力太大了&#xff0c;也影响程序的 性能。 Shiro 中执行权限角色校验时&#xff0…...

QGIS实战系列(六):进阶应用篇——Python 脚本自动化与三维可视化

欢迎来到“QGIS实战系列”的第六期!在前几期中,我们从基础操作到插件应用逐步提升了 QGIS 技能。这一篇,我们将迈入进阶领域,探索如何用 Python 脚本实现自动化,以及如何创建三维可视化效果,让你的 GIS 项目更高效、更立体。 第一步:Python 脚本自动化 QGIS 内置了 Py…...

redis-cpp-cpp如何使用lua脚本

1.前言 我今天要在项目中使用lua脚本&#xff0c;结果搞半天都没有弄明白这个函数怎么调用&#xff0c;而且也似乎很少有redis相关的博客介绍&#xff0c;ai也回答的不准确&#xff01; 2.正文 今天用一个例子演示一下 下面是lua脚本 const std::string LuaScript R"…...

C# Winform 入门(6)之不同类之间的值传递

承接上一个教程&#xff0c;利用委托事件来进行值传递 声明变量 public static double plx, ply,plxx,plyy;声明委托、事件 //声明委托 //事件 public delegate void transferDistance(double dis); public static transferDistance doTransfer; 直接读取form1中的变量 publ…...

PyTorch 深度学习 || 6. Transformer | Ch6.1 注意力机制

...

JWT 秘钥的作用机制

JWT 秘钥的作用并不是给前端使用的&#xff0c;而是用于后端服务器内部的一个重要安全机制。 JWT 秘钥的作用 签名与验证&#xff1a; 秘钥主要用于对 JWT&#xff08;JSON Web Token&#xff09;进行签名和验证后端使用这个秘钥对令牌进行签名&#xff0c;确保令牌的完整性…...

sun.misc.BASE64Encoder 和 sun.misc.BASE64Decoder包

1. 在将别人的项目导入eclipse之后,出现了"sun.misc.BASE64Encoder找不到jar"的错误&#xff0c;我解决的办法是&#xff1a;右键项目》属性》Java Build Path》jre System Library 》access rules 》resolution选择accessible,下面填上**点击确定即可&#xff0…...

Java面试黄金宝典34

1. 主键索引底层的实现原理 定义 主键索引是数据库中用于唯一标识表中每一行记录的索引&#xff0c;常见的底层实现是 B 树结构。B 树是一种平衡的多路搜索树&#xff0c;由内部节点和叶子节点组成。内部节点只存储索引键和指向下一层节点的指针&#xff0c;不存储实际数据&am…...

计算机系统---CPU

定义与功能 中央处理器&#xff08;Central Processing Unit&#xff0c;CPU&#xff09;&#xff0c;是电子计算机的主要设备之一&#xff0c;是计算机的核心部件。CPU是计算机的运算核心和控制核心&#xff0c;负责执行计算机程序中的指令&#xff0c;进行算术运算、逻辑运算…...

AWS云安全基线:构建企业级安全防护体系的完整指南

1. 引言 随着越来越多的企业将其业务和数据迁移到云端,云安全已成为一个不容忽视的关键议题。AWS作为全球领先的云服务提供商,提供了丰富的安全工具和最佳实践。本文将深入探讨如何构建一个全面的AWS云安全基线,以确保您的企业在云环境中的安全性。 2. AWS共享责任模型 在深…...

(三十三)Dart 中使用 Pub 包管理系统与 HTTP 请求教程

Dart 中使用 Pub 包管理系统与 HTTP 请求教程 Pub 包管理系统简介 Pub 是 Dart 和 Flutter 的包管理系统&#xff0c;用于管理项目的依赖。通过 Pub&#xff0c;开发者可以轻松地添加、更新和管理第三方库。 使用 Pub 包管理系统 1. 找到需要的库 访问以下网址&#xff0c…...