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

RAG:如何从0到1搭建一个RAG应用

通过本文你可以了解到:

  • 什么是RAG?
  • 如何搭建一个RAG应用?
  • 目前开源的RAG应用有哪些?

大模型学习参考:
1.大模型学习资料整理:大模型学习资料整理:如何从0到1学习大模型,搭建个人或企业RAG系统,如何评估与优化(更新中…)
2.streamlit入门和简单使用:streamlit:如何快速构建一个应用,不会前端也能写出好看的界面

欢迎大家访问个人博客网址:https://www.maogeshuo.com,博主努力更新中…

RAG简介

在这里插入图片描述

检索增强生成Retrieval Augmented Generation,RAG)是一种强大的工具,整合了从庞大知识库中检索到的相关信息,并以此为基础,指导大型语言模型生成更为精准的答案,从而显著提升了回答的准确性与深度。

2020 年,Meta AI 研究人员提出了RAG的方法,用于提高 LLM 在特定任务上的性能。LLM 擅长语言理解、推理和生成等任务,但也存在一些问题:

  • 信息滞后:LLM 的知识是静态的,来源于当时训练时的数据,也就是 LLM 无法直接提供最新的信息。
  • 模型幻觉:实践表明,当前的生成式 AI 技术存在一定的幻觉,而在一些常见的业务应用中,我们是希望保证事实性的。
  • 私有数据匮乏:LLM 的训练数据主要来源于互联网公开的数据,而垂类领域、企业内部等有很多专属知识,这部分是 LLM 无法直接提供的。
  • 内容不可追溯: LLM 生成的内容往往缺乏明确的信息来源,影响内容的可信度。RAG 将生成内容与检索到的原始资料建立链接,增强了内容的可追溯性,从而提升了用户对生成内容的信任度。
  • 长文本处理能力较弱: LLM 在理解和生成长篇内容时受限于有限的上下文窗口,且必须按顺序处理内容,输入越长,速度越慢。RAG 通过检索和整合长文本信息,强化了模型对长上下文的理解和生成,有效突破了输入长度的限制,同时降低了调用成本,并提升了整体的处理效率。

RAG 通过将检索到的相关信息提供给 LLM,让 LLM 进行参考生成,可以较好地缓解上述问题。因此,合理使用 RAG 可以拓展 LLM 的知识边界,使其不仅能够访问专属知识库,还能动态地引入最新的数据,从而在生成响应时提供更准确、更新的信息。

RAG组成部分

自定义知识库,用于RAG检索的知识来源:

  • 结构化的数据库形态:比如MySQL
  • 非结构化的文档体系:比如文件、图片、音频、视频

RAG 是一个完整的系统,其工作流程可以简单地分为数据处理、检索、增强和生成四个阶段:

数据处理阶段

对原始数据进行清洗和处理。
将处理后的数据转化为检索模型可以使用的格式。
将处理后的数据存储在对应的数据库中。

检索阶段

将用户的问题输入到检索系统中,从数据库中检索相关信息。

增强阶段

对检索到的信息进行处理和增强,以便生成模型可以更好地理解和使用。

生成阶段

将增强后的信息输入到生成模型中,生成模型根据这些信息生成答案。

搭建RAG应用

数据处理

数据清洗和处理

数据处理阶段,一般需要对知识库中的数据进行数据清洗,比如去掉多余的换行、特殊符号,然后加载处理后的文件和分块:

  • 加载文件:使用langchain下的document_loaders加载pdf、docs、txt、md等格式文件
  • 文本分块:分块的方式有很多,选择不同的分块方法、分块大小、chunk_overlap,对最后的检索结果有影响,这一阶段也有RAG的优化点之一
import osfrom langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader, UnstructuredFileLoader
from langchain_text_splitters import RecursiveCharacterTextSplitterdef load_document(file):"""加载PDF、DOC、TXT文档:param file::return:"""name, extension = os.path.splitext(file)if extension == '.pdf':print(f'Loading {file}')loader = PyPDFLoader(file)elif extension == '.docx':print(f'Loading {file}')loader = Docx2txtLoader(file)elif extension == '.txt':loader = UnstructuredFileLoader(file)else:print('Document format is not supported!')return Nonedata = loader.load()return datadef chunk_data(data, chunk_size=256, chunk_overlap=150):"""将数据分割成块:param data::param chunk_size: chunk块大小:param chunk_overlap: 重叠部分大小:return:"""text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)chunks = text_splitter.split_documents(data)return chunks

embedding模型存储

将分块后的文本,使用embedding模型持久化存储,目前常用的中文模型bge-large-zh-v1.5。持久化存储后,避免每次都去embedding一次,消耗很长的时间。下次使用时,直接加载模型就可以了。

import osfrom langchain_community.embeddings import HuggingFaceBgeEmbeddings, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma, FAISSdef get_embedding(embedding_name):"""根据embedding名称去加载embedding模型:param embedding_name: 路径或者名称:return:"""if embedding_name == "bge":embedding_path = os.environ[embedding_name]model_kwargs = {'device': 'cpu'}return HuggingFaceBgeEmbeddings(model_name=embedding_path, model_kwargs=model_kwargs)if embedding_name == "bce":return None# create embeddings using OpenAIEmbeddings() and save them in a Chroma vector store
def create_embeddings_chroma(chunks):embeddings = OpenAIEmbeddings()vector_store = Chroma.from_documents(chunks, embeddings)# if you want to use a specific directory for chromadb# vector_store = Chroma.from_documents(chunks, embeddings, persist_directory='./mychroma_db')return vector_storedef create_embeddings_faiss(vector_db_path, embedding_name, chunks):"""使用FAISS向量数据库,并保存:param vector_db_path: 向量:param embedding_name::param chunks::return:"""embeddings = get_embedding(embedding_name)db = FAISS.from_documents(chunks, embeddings)if not os.path.isdir(vector_db_path):os.mkdir(vector_db_path)db.save_local(folder_path=vector_db_path)return dbdef load_embeddings_faiss(vector_db_path, embedding_name):"""加载向量库:param vector_db_path::param embedding_name::return:"""embeddings = get_embedding(embedding_name)db = FAISS.load_local(vector_db_path, embeddings, allow_dangerous_deserialization=True)return db

构建模型

采用了函数和类两种方式定义模型:

  • 函数:get_llm_model定义了基本的参数,model、prompt、temperature、max_tokens、n_ctx
  • 自定义类:
import os
import sys
import time
from abc import ABCfrom langchain_core.callbacks import CallbackManagerForLLMRun
from llama_cpp import Llama
from langchain.llms.base import LLM
from pydantic import Field
from typing import Dict, Any, Mapping, Optional, ListBASE_DIR = os.path.dirname(__file__)
# PRJ_DIR上层目录
# PRJ_DIR = os.path.abspath(os.path.join(BASE_DIR, ".."))
sys.path.append(BASE_DIR)def get_llm_model(prompt: str = None,model: str = None,temperature: float = 0.0,max_token: int = 2048,n_ctx: int = 512):"""根据模型名称去加载模型,返回response数据:param prompt::param model::param temperature::param max_token::param n_ctx::return:"""if model in ['Qwen_q2']:model_path = os.environ[model]llm = Llama(model_path=model_path, n_ctx=n_ctx)start = time.time()response = llm.create_chat_completion(messages=[{"role": "system","content": "你是一个智能超级助手,请用专业的词语回答问题,整体上下文带有逻辑性,如果不知道,请不要乱说",},{"role": "user","content": "{}".format(prompt)},],temperature=temperature,max_tokens=max_token,stream=False)cost = time.time() - startprint(f"模型生成时间:{cost}")print(f"大模型回复:\n{response}")return response['choices'][0]['message']['content']class QwenLLM(LLM):"""自定义QwenLLM"""model_name: str = "Qwen_q2"# 访问时延上限request_timeout: float = None# 温度系数temperature: float = 0.1# 窗口大小n_ctx = 2048# token大小max_tokens = 1024# 必备的可选参数model_kwargs: Dict[str, Any] = Field(default_factory=dict)def _call(self, prompt: str, stop: Optional[List[str]] = None,run_manager: Optional[CallbackManagerForLLMRun] = None,**kwargs: Any):qwen_path = os.environ[self.model_name]print("qwen_path:", qwen_path)llm = Llama(model_path=qwen_path, n_ctx=self.n_ctx)response = llm.create_chat_completion(messages=[{"role": "system","content": "你是一个智能超级助手,请用[中文]专业的词语回答问题,整体上下文带有逻辑性,并以markdown格式输出",},{"role": "user","content": "{}".format(prompt)},],temperature=self.temperature,max_tokens=self.max_tokens,stream=False)# prompt工程提示# print(f"Qwen prompt: \n{prompt}")# response = lla(#     prompt=prompt,#     temperature=self.temperature,#     max_tokens=self.max_tokens# )print(f"Qwen response: \n{response}")# return response['choices'][0]['text']return response['choices'][0]['message']['content']@propertydef _llm_type(self) -> str:return "Llama3"# 定义一个返回默认参数的方法@propertydef _default_params(self) -> Dict[str, Any]:"""获取调用默认参数。"""normal_params = {"temperature": self.temperature,"request_timeout": self.request_timeout,"n_ctx": self.n_ctx,"max_tokens": self.max_tokens}# print(type(self.model_kwargs))return {**normal_params}@propertydef _identifying_params(self) -> Mapping[str, Any]:"""Get the identifying parameters."""return {**{"model_name": self.model_name}, **self._default_params}

构建应用

import sysimport streamlit as st
import os
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
import tiktoken
from dotenv import load_dotenv, find_dotenv
from langchain_core.prompts import PromptTemplateBASE_DIR = os.path.dirname(__file__)
PRJ_DIR = os.path.abspath(os.path.join(BASE_DIR, ".."))
sys.path.append(PRJ_DIR)from streamlit_demo.custom_llm import QwenLLM
from streamlit_demo.embedding_oper import create_embeddings_faiss, create_embeddings_chroma, load_embeddings_faiss
from streamlit_demo.prepare_data import load_document, chunk_data_ = load_dotenv(find_dotenv(), override=True)
vector_db_path = os.path.join(BASE_DIR, "vector_db")
print(f"vector_db_path: {vector_db_path}")DEFAULT_TEMPLATE = """你是一个聪明的超级智能助手,请用专业且富有逻辑顺序的句子回复,并以中文形式且markdown形式输出。检索到的信息:{context}问题:{question}
"""def ask_and_get_answer_from_local(model_name, vector_db, prompt, top_k=5):"""从本地加载大模型:param model_name: 模型名称:param vector_db::param prompt::param top_k::return:"""docs_and_scores = vector_db.similarity_search_with_score(prompt, k=top_k)print("docs_and_scores: ", docs_and_scores)# knowledge = [doc.page_content for doc in docs_and_scores]# print("检索到的知识:", knowledge)if model_name == "Qwen_q2":llm = QwenLLM(model_name=model_name, temperature=0.4)prompt_template = PromptTemplate(input_variables=["context", "question"], template=DEFAULT_TEMPLATE)retriever = vector_db.as_retriever(search_type='similarity', search_kwargs={'k': top_k})chain = RetrievalQA.from_chain_type(llm=llm,chain_type="stuff",retriever=retriever,chain_type_kwargs={"prompt": prompt_template},return_source_documents=True)answer = chain({"query": prompt, "top_k": top_k})print(f"answers: {answer}")# answer = chain.run(prompt)# answer = answer['choices'][0]['message']['content']answer = answer['result']return answerdef ask_and_get_answer(vector_store, q, k=3):llm = ChatOpenAI(model='gpt-3.5-turbo', temperature=1)retriever = vector_store.as_retriever(search_type='similarity', search_kwargs={'k': k})chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)answer = chain.run(q)return answer# calculate embedding cost using tiktoken
def calculate_embedding_cost(texts):enc = tiktoken.encoding_for_model('text-embedding-ada-002')total_tokens = sum([len(enc.encode(page.page_content)) for page in texts])# print(f'Total Tokens: {total_tokens}')# print(f'Embedding Cost in USD: {total_tokens / 1000 * 0.0004:.6f}')return total_tokens, total_tokens / 1000 * 0.0004# clear the chat history from streamlit session state
def clear_history():if 'history' in st.session_state:del st.session_state['history']if __name__ == "__main__":# st.image('img.png')st.subheader('LLM Question-Answering Application 🤖')with st.sidebar:# text_input for the OpenAI API key (alternative to python-dotenv and .env)api_key = st.text_input('OpenAI API Key:', type='password')if api_key:os.environ['OPENAI_API_KEY'] = api_keyllm = st.selectbox(label="请选择本地大模型",options=('Qwen_q2', 'Qwen_q6'))# 向量数据库embedding = st.selectbox("请选择向量数据库",('FAISS', 'Chroma'))# file uploader widgetuploaded_file = st.file_uploader('上传文件:', type=['pdf', 'docx', 'txt'])# chunk size number widgetchunk_size = st.number_input('chunk_size:', min_value=100, max_value=2048, value=512, on_change=clear_history)# chunk overlapchunk_overlap = st.number_input(label="chunk_overlap", min_value=0, max_value=1024, value=150,on_change=clear_history)# k number input widgetk = st.number_input('top_k', min_value=1, max_value=20, value=3, on_change=clear_history)# add data button widgetadd_data = st.button('添加数据', on_click=clear_history)# 输出方式output_type = st.selectbox("选择输出方式", ('普通输出', '流式输出'))if uploaded_file and add_data:  # if the user browsed a filewith st.spinner('Reading, chunking and embedding file ...'):# writing the file from RAM to the current directory on diskbytes_data = uploaded_file.read()file_name = os.path.join('./', uploaded_file.name)with open(file_name, 'wb') as f:f.write(bytes_data)data = load_document(file_name)chunks = chunk_data(data, chunk_size=chunk_size, chunk_overlap=chunk_overlap)st.write(f'Chunk size: {chunk_size}, chunk_overlap: {len(chunks)} Chunks: {len(chunks)}')tokens, embedding_cost = calculate_embedding_cost(chunks)st.write(f'Embedding cost: ${embedding_cost:.4f}')# creating the embeddings and returning the Chroma vector store# 指定选择向量库和embedding类型,还可改进if embedding == "FAISS":vector_store = create_embeddings_faiss(vector_db_path=vector_db_path, embedding_name="bge",chunks=chunks)elif embedding == "Chroma":vector_store = create_embeddings_chroma(chunks)# saving the vector store in the streamlit session state (to be persistent between reruns)st.session_state.vs = vector_storest.success('File uploaded, chunked and embedded successfully.')# 初始化historyif "messages" not in st.session_state:st.session_state.messages = []# 展示对话for msg in st.session_state.messages:with st.chat_message(msg['role']):st.markdown(msg["content"])# React to user inputif prompt := st.chat_input("Say something"):# Display user message in chat message containerwith st.chat_message("user"):st.markdown(prompt)# Add user message to chat historyst.session_state.messages.append({"role": "user", "content": prompt})# load local vector dbif 'vs' not in st.session_state:# st.warning(body='正在努力加载模型中...', icon="⚠️")vector_store = load_embeddings_faiss(vector_db_path, "bge")st.session_state.vs = vector_storest.toast('Load vector store db success!', icon='😍')# 普通方式输出if prompt is not None:vector_store = st.session_state.vs# if vector_store is None:#     st.warning(body='正在努力加载模型中,稍后再试', icon="⚠️")if output_type == "普通输出" and vector_store is not None:response = ""if llm == "GPT":response = ask_and_get_answer(vector_store, prompt, k)elif llm == "Qwen_q2":response = ask_and_get_answer_from_local(model_name="Qwen_q2", vector_db=vector_store, prompt=prompt, top_k=k)# Display assistant response in chat message containerwith st.chat_message("assistant"):st.markdown(response)# Add assistant response to chat historyst.session_state.messages.append({"role": "assistant", "content": response})else:# 流式输出# stream_res = get_llm_model_with_stream(prompt=prompt, model="Qwen_q2")# with st.chat_message("assistant"):#     content = st.write_stream(stream_res)# print("流式输出:", content)# st.session_state.messages.append({"role": "assistant", "content": content})print("流式输出")# run the app: streamlit run ./chat_doc.py

结果展示

使用步骤:

  1. 选择参数,然后上传本地的文件
  2. 开始添加数据,用于数据处理和embedding持久化存储在这里插入图片描述
    在这里插入图片描述

开源的RAG应用

QAnything: https://github.com/netease-youdao/QAnything

AnythingLLM:https://github.com/Mintplex-Labs/anything-llm

ragflow:https://github.com/infiniflow/ragflow/blob/main/README_zh.md

相关文章:

RAG:如何从0到1搭建一个RAG应用

通过本文你可以了解到: 什么是RAG?如何搭建一个RAG应用?目前开源的RAG应用有哪些? 大模型学习参考: 1.大模型学习资料整理:大模型学习资料整理:如何从0到1学习大模型,搭建个人或企业…...

leetcode:67二进制求和

题目链接&#xff1a;67. 二进制求和 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string addBinary(string a, string b) {int stralen a.size(), strblen b.size();int curtc;int Maxlen max(stralen, strblen);vector<int> stra;vector<i…...

大模型日报2024-06-10

大模型日报 2024-06-10 大模型资讯 无需矩阵乘法的语言模型在亿参数规模上表现优异 摘要: 研究表明&#xff0c;无需矩阵乘法的语言模型在亿参数规模上仍能保持顶级性能。这一发现挑战了传统神经网络依赖矩阵乘法的观点&#xff0c;展示了在GPU优化之外的新可能性。 博弈论助力…...

【博士每天一篇文献-综述】Modularity in Deep Learning A Survey

阅读时间&#xff1a;2023-12-8 1 介绍 年份&#xff1a;2023 作者&#xff1a;孙浩哲&#xff0c;布朗克斯医疗卫生系统 会议&#xff1a; Science and Information Conference 引用量&#xff1a;4 论文主要探讨了深度学习中的模块化&#xff08;modularity&#xff09;概念…...

Sentinel不使用控制台基于注解限流,热点参数限流

目录 一、maven依赖 二、控制台 三、基于注解限流 四、热点参数限流 五、使用JMeter验证 一、maven依赖 需要注意&#xff0c;使用的版本需要和你的SpringBoot版本匹配&#xff01;&#xff01; Spring-Cloud直接添加如下依赖即可&#xff0c;baba已经帮你指定好版本了。…...

HTML做成一个端午节炫酷页面

做成端午节页面之前&#xff0c;先了解一下端午节的由来&#xff1a; 1.起源与历史&#xff1a; 端午节起源于中国&#xff0c;始于春秋战国时期&#xff0c;至今已有2000多年历史。 最初是古代百越地区&#xff08;长江中下游及以南一带&#xff09;崇拜龙图腾的部族举行图…...

解决Ubuntu系统/usr/lib/xorg/Xorg占用显卡内存问题原创

在Ubuntu系统中&#xff0c;/usr/lib/xorg/Xorg进程占用显卡内存的问题可能会影响系统性能&#xff0c;特别是在使用GPU进行计算任务时。以下是一些解决方法&#xff0c;可以帮助你减少或解决这个问题&#xff1a; 1. 更新显卡驱动 首先&#xff0c;确保你使用的是最新版本的…...

【Activiti7系列】基于Spring Security的Activiti7工作流管理系统简介及实现(附源码)(下篇)

作者&#xff1a;后端小肥肠 上篇&#xff1a;【Activiti7系列】基于Spring Security的Activiti7工作流管理系统简介及实现&#xff08;上篇&#xff09;_spring security activiti7-CSDN博客 目录 1.前言 2. 核心代码 2.1. 流程定义模型管理 2.1.1. 新增流程定义模型数据 …...

解密Spring Boot:深入理解条件装配与条件注解

文章目录 一、条件装配概述1.1 条件装配的基本原理1.2 条件装配的作用 二、常用注解2.1 ConditionalOnClass2.2 ConditionalOnBean2.3 ConditionalOnProperty2.4 ConditionalOnExpression2.5 ConditionalOnMissingBean 三、条件装配的实现原理四、实际案例 一、条件装配概述 1…...

【数据结构与算法】使用数组实现栈:原理、步骤与应用

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​ 目录 一、引言 &#x1f384;栈&#xff08;Stack&#xff09;是什么&#xff1f; &#x1…...

cell的复用机制和自定义cell

cell的复用机制和自定义cell UITableView 在学习cell之前&#xff0c;我们需要先了解UITableView。UITableView继承于UIScrollView&#xff0c;拥有两个两个相关协议 UITableViewDelegate和UITableViewDataSource&#xff0c;前者用于显示单元格&#xff0c;设置行高以及对单…...

Redis 双写一致原理篇

前言 我们都知道,redis一般的作用是顶在mysql前面做一个"带刀侍卫"的角色,可以缓解mysql的服务压力,但是我们如何保证数据库的数据和redis缓存中的数据的双写一致呢,我们这里先说一遍流程,然后以流程为切入点来谈谈redis和mysql的双写一致性是如何保证的吧 流程 首先…...

《软件定义安全》之四:什么是软件定义安全

第4章 什么是软件定义安全 1.软件定义安全的含义 1.1 软件定义安全的提出 虚拟化、云计算、软件定义架构的出现&#xff0c;对安全体系提出了新的挑战。如果要跟上网络演进的步伐和业务快速创新的速度&#xff0c;安全体系应该朝以下方向演变。 &#x1d7ed; 安全机制软件…...

将AIRNet集成到yolov8中,实现端到端训练与推理

AIRNet是一个图像修复网络,支持对图像进行去雾、去雨、去噪声的修复。其基于对比的退化编码器(CBDE),将各种退化类型统一到同一嵌入空间;然后,基于退化引导恢复网络(DGRN)将嵌入空间修复为目标图像。可以将AIRNet的输出与yolov8进行端到端集成,实现部署上的简化。 本博…...

hcache缓存查看工具

1、hcache概述 hcache是基于pcstat的&#xff0c;pcstat可以查看某个文件是否被缓存和根据进程pid来查看都缓存了哪些文件。hcache在其基础上增加了查看整个操作系统Cache和根据使用Cache大小排序的特性。官网:https://github.com/silenceshell/hcache 2、hcache安装 2.1下载…...

Java 数据类型 -- Java 语言的 8 种基本数据类型、字符串与数组

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 004 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…...

kafka-生产者事务-数据传递语义事务介绍事务消息发送(SpringBoot整合Kafka)

文章目录 1、kafka数据传递语义2、kafka生产者事务3、事务消息发送3.1、application.yml配置3.2、创建生产者监听器3.3、创建生产者拦截器3.4、发送消息测试3.5、使用Java代码创建主题分区副本3.6、屏蔽 kafka debug 日志 logback.xml3.7、引入spring-kafka依赖3.8、控制台日志…...

免费!GPT-4o发布,实时语音视频丝滑交互

We’re announcing GPT-4o, our new flagship model that can reason across audio, vision, and text in real time. 5月14日凌晨&#xff0c;OpenAI召开了春季发布会&#xff0c;发布会上公布了新一代旗舰型生成式人工智能大模型【GPT-4o】&#xff0c;并表示该模型对所有免费…...

DevOps的原理及应用详解(四)

本系列文章简介: 在当今快速变化的商业环境中,企业对于软件交付的速度、质量和安全性要求日益提高。传统的软件开发和运维模式已经难以满足这些需求,因此,DevOps(Development和Operations的组合)应运而生,成为了解决这些问题的有效方法。 DevOps是一种强调软件开发人员(…...

关于选择,关于处事

一个人选择应该选择的是勇敢&#xff0c;选择不应该选择的是无奈。放弃&#xff0c;不该放弃的是懦夫&#xff0c;不放弃应该放弃的是睿智。所以&#xff0c;碰到事的时候要先静&#xff0c;先不管什么事&#xff0c;先静下来&#xff0c;先淡定&#xff0c;先从容。在生活里要…...

大话设计模式解读02-策略模式

本篇文章&#xff0c;来解读《大话设计模式》的第2章——策略模式。并通过Qt和C代码实现实例代码的功能。 1 策略模式 策略模式作为一种软件设计模式&#xff0c;指对象有某个行为&#xff0c;但是在不同的场景中&#xff0c;该行为有不同的实现算法。 策略模式的特点&#…...

展会邀请 | 龙智即将亮相2024上海国际嵌入式展,带来安全合规、单一可信数据源、可追溯、高效协同的嵌入式开发解决方案

2024年6月12日至14日&#xff0c;备受全球嵌入式系统产业和社群瞩目的2024上海国际嵌入式展&#xff08;embedded world china 2024&#xff09;即将盛大开幕&#xff0c;龙智将携行业领先的嵌入式开发解决方案亮相 640展位 。 此次参展&#xff0c;龙智将全面展示专为嵌入式行…...

codeforce round951 div2

A guess the maximum 问题&#xff1a; 翻译一下就是求所有相邻元素中max - 1的最小值 代码&#xff1a; #include <iostream> #include <algorithm>using namespace std;const int N 5e4;int a[N]; int n;void solve() {cin >> n;int ans 0x3f3f3f3f;…...

arcgis开发记录

目录 文章目录 [toc]**arcgis JavaScript API安装**1. arcgisAPI下载地址&#xff1a;https://developers.arcgis.com/downloads/2. 4.4版本API&#xff1a;本地配置3. 3.18版本修改方法 **angular2中加载arcgis JS API**** arcgis加载图层 并显示图层上点的信息****使用图层上…...

RPA-UiBot6.0数据整理机器人—杂乱数据秒变报表

前言 友友们是否常常因为杂乱的数据而烦恼?数据分类、排序、筛选这些繁琐的任务是否占据了友友们的大部分时间?这篇博客将为友友们带来一个新的解决方案,让我们共同学习如何运用RPA数据整理机器人,实现杂乱数据的快速整理,为你的工作减负增效! 在这里,友友们将了…...

Application UI

本节包含关于如何用DevExpress控件模拟许多流行的应用程序ui的教程。 Windows 11 UI Windows 11和最新一代微软Office产品启发的UI。 Office Inspired UI Word、Excel、PowerPoint和Visio等微软Office应用程序启发的UI。 如何&#xff1a;手动构建Office风格的UI 本教程演示…...

关于 Redis 中集群

哨兵机制中总结到&#xff0c;它并不能解决存储容量不够的问题&#xff0c;但是集群能。 广义的集群&#xff1a;只要有多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称之为一个“集群”&#xff0c;例如主从结构中的哨兵模式。 狭义的集群&#xff1a;redis 提供的…...

C++必修:探索C++的内存管理

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. C/C的内存分布 我们首先来看一段代码及其相关问题 int globalVar 1; static…...

python列表---基本语法(浅拷贝,深拷贝等)

文章目录 引言:列表的注意事项1 list中的浅拷贝与深拷贝1.1浅拷贝(Shallow Copy)浅拷贝的方法浅拷贝的效果1.2深拷贝(Deep Copy)深拷贝的方法深拷贝的效果1.3 总结:浅拷贝 vs 深拷贝1.4 为什么浅拷贝顶层元素如果是不可变数据就不能共享,不是传的是引用就相当于传的是地…...

go语言接口之sort.Interface接口

排序操作和字符串格式化一样是很多程序经常使用的操作。尽管一个最短的快排程序只要15 行就可以搞定&#xff0c;但是一个健壮的实现需要更多的代码&#xff0c;并且我们不希望每次我们需要的时候 都重写或者拷贝这些代码。 幸运的是&#xff0c;sort包内置的提供了根据一些排序…...

曲靖 曲靖网站建设软件(app)开发/手机百度旧版本下载

命令模式 命令模式是最简单和优雅的模式之一&#xff0c;命令模式中的命令&#xff08;command&#xff09;指的是一个执行某些特定事情的指令。 命令模式最常见的应用场景是&#xff1a;有时候需要向某些对象发送请求&#xff0c;但是并不知道请求的接收者是谁&#xff0c;也不…...

长乐住房和城乡建设局网站/站点查询

从ICassFactory为CLSID为{17BCA6E8-A950-497E-B2F9-AF6AA475916F}的COM组件创建实例失败&#xff0c;原因是出现以下错误&#xff1a;c001f011.(Microsoft.Server.manageDTS) 今天在“新建维护计划”时&#xff0c;报错&#xff1a; 创建维护计划失败。其他信息&#xff1a;从 …...

it运维公司/seo的含义是什么意思

// 下面这个主要是对查询出来的数据&#xff0c; 经过筛选后得到的数据中进行反选操作 &#xff0c;然后对选择的数据进行修改&#xff08;全选或选择一部分也可以根据些代码修改&#xff09; Screen.Cursor : crHourGlass;cxGrid2DBTableView1.DataController.GotoFirst;for i…...

网站模板备份/对搜索引擎优化的认识

这是一个让我很头痛的问题&#xff0c;我是在一个其他的项目框架的基础上来开发新的项目。 当使用struts验证框架时&#xff0c;突然发现这个验证不起作用了&#xff0c;我就纳闷了之前用这个开发的项目好好的怎么到我这就不能用了呢&#xff1f; xml文件头 <?xml version&…...

wordpress参数传递的函数/免费注册网址

看了别人的题解&#xff0c;想了半天&#xff0c;我决定把它解释清楚一些。 题目大意 有N(2≤N≤105)N(2≤N≤105)个点的树&#xff0c;树边有权值a(0≤a≤15)a(0≤a≤15)&#xff0c;每次操作可以把树上一条路径上所有边异或一个任意值ww,最少多少次操作可以使所有边权都变…...

怎样设置网站/百度指数搜索热度大学

如下代码&#xff0c;想要循环删除列表中的元素b&#xff0c;该怎么处理&#xff1f; public class ListDemo {public static void main(String[] args) {ArrayList<String> arrayList new ArrayList<String>();arrayList.add("a");arrayList.add("…...