LlamaIndex——RAG概述

文章目录

  • 一、使用LLM
    • 1. 模型
    • 2. 词嵌入
    • 3. Prompt
  • 二、加载
    • 1. 加载
    • 2. 转换
      • (1)高级API
      • (2)低级API
  • 三、索引/Embedding
    • Top K Retrieval
  • 四、存储
  • 五、查询
  • 六、评估
    • 1. 生成结果质量评估
    • 2. 检索结果评估

RAG(检索增强生成)包括以下步骤:

  1. 首先从您的数据源检索信息。
  2. 将这些检索到的信息作为上下文添加到您的问题中。
  3. 根据增强的提示要求LLM(大型语言模型)进行回答,其中包括检索到的信息作为上下文。

这个过程通过利用从数据源检索到的额外信息,使LLM能够生成更准确和具有上下文相关性的回答。

LlamaIndex提供以下工具,可以快速搭建可投入生产的RAG系统:

  • 数据连接器:从各种原生源和格式摄取现有数据,包括API、PDF、SQL等等。
  • 数据索引:将数据结构化为中间表示。
  • 引擎:提供对数据的自然语言访问。例如:
    • 查询引擎:强大的检索接口,用于知识增强型输出。
    • 聊天引擎:用于与数据进行多消息“来回”交互的对话接口。
  • 数据代理:由LLM驱动的知识工作者,通过工具进行增强,从简单的辅助函数到API集成等等。
  • 应用集成:将LlamaIndex与生态系统其他部分进行集成,可以是LangChain、Flask、Docker、ChatGPT等等。

RAG(检索增强生成)包括五个关键阶段:加载(Loading)、索引(Indexing)、存储(Storing)、查询(Querying)和评估(Evaluation)。

  1. 加载(Loading):加载阶段涉及从数据源获取数据,可以是文本文件、PDF、其他网站、数据库或API。LlamaHub提供了数百个连接器可供选择,用于将数据导入到数据处理流程中。
  2. 索引(Indexing):索引阶段意味着创建一个数据结构,以便对数据进行查询。对于LLM(大型语言模型)来说,这几乎总是意味着创建向量嵌入(vector embeddings),即数据含义的数值表示,以及许多其他元数据策略,使得可以轻松准确地找到上下文相关的数据。
  3. 存储(Storing):将数据存储到向量数据库中。
  4. 查询(Querying):对于任何给定的索引策略,可以利用LLM和LlamaIndex数据结构进行查询,包括子查询、多步查询和混合策略等多种方式。
  5. 评估(Evaluation):在任何流程中,评估都是至关重要的步骤,用于检查相对于其他策略或在进行更改时流程的有效性。评估提供了关于对查询的响应有多准确、忠实和快速的客观度量标准。

一、使用LLM

1. 模型

在LlamaIndex中,我们可以使用大语言模型的API接口或者本地大语言模型。例如:

使用ChatGPT

from llama_index.llms.openai import OpenAI
from llama_index.core import Settings
from llama_index.core import VectorStoreIndex, SimpleDirectoryReaderSettings.llm = OpenAI(temperature=0.2, model="gpt-4")documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents,
)

使用HuggingFace的大语言模型

from llama_index.core import PromptTemplate# Transform a string into input zephyr-specific input
def completion_to_prompt(completion):return f"<|system|>\n</s>\n<|user|>\n{completion}</s>\n<|assistant|>\n"# Transform a list of chat messages into zephyr-specific input
def messages_to_prompt(messages):prompt = ""for message in messages:if message.role == "system":prompt += f"<|system|>\n{message.content}</s>\n"elif message.role == "user":prompt += f"<|user|>\n{message.content}</s>\n"elif message.role == "assistant":prompt += f"<|assistant|>\n{message.content}</s>\n"# ensure we start with a system prompt, insert blank if neededif not prompt.startswith("<|system|>\n"):prompt = "<|system|>\n</s>\n" + prompt# add final assistant promptprompt = prompt + "<|assistant|>\n"return promptimport torch
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core import SettingsSettings.llm = HuggingFaceLLM(model_name="HuggingFaceH4/zephyr-7b-beta",tokenizer_name="HuggingFaceH4/zephyr-7b-beta",context_window=3900,max_new_tokens=256,generate_kwargs={"temperature": 0.7, "top_k": 50, "top_p": 0.95},messages_to_prompt=messages_to_prompt,completion_to_prompt=completion_to_prompt,device_map="auto",
)

使用自定义大语言模型

from typing import Optional, List, Mapping, Anyfrom llama_index.core import SimpleDirectoryReader, SummaryIndex
from llama_index.core.callbacks import CallbackManager
from llama_index.core.llms import (CustomLLM,CompletionResponse,CompletionResponseGen,LLMMetadata,
)
from llama_index.core.llms.callbacks import llm_completion_callback
from llama_index.core import Settingsclass OurLLM(CustomLLM):context_window: int = 3900num_output: int = 256model_name: str = "custom"dummy_response: str = "My response"@propertydef metadata(self) -> LLMMetadata:"""Get LLM metadata."""return LLMMetadata(context_window=self.context_window,num_output=self.num_output,model_name=self.model_name,)@llm_completion_callback()def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse:return CompletionResponse(text=self.dummy_response)@llm_completion_callback()def stream_complete(self, prompt: str, **kwargs: Any) -> CompletionResponseGen:response = ""for token in self.dummy_response:response += tokenyield CompletionResponse(text=response, delta=token)# define our LLM
Settings.llm = OurLLM()# define embed model
Settings.embed_model = "local:BAAI/bge-base-en-v1.5"# Load the your data
documents = SimpleDirectoryReader("./data").load_data()
index = SummaryIndex.from_documents(documents)# Query and print response
query_engine = index.as_query_engine()
response = query_engine.query("<query_text>")
print(response)

2. 词嵌入

使用Embedding模型的几种方法;

  • 本地Embedding模型
  • HuggingFace Optimum ONNX模型
  • 自定义Embedding模型
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.embeddings.huggingface_optimum import OptimumEmbedding
from typing import Any, List
from InstructorEmbedding import INSTRUCTOR
from llama_index.core.embeddings import BaseEmbedding
from llama_index.core import Settings
# local
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"
)
# ONNX
OptimumEmbedding.create_and_save_optimum_model("BAAI/bge-small-en-v1.5", "./bge_onnx"
)
Settings.embed_model = OptimumEmbedding(folder_name="./bge_onnx")# custom
class InstructorEmbeddings(BaseEmbedding):def __init__(self,instructor_model_name: str = "hkunlp/instructor-large",instruction: str = "Represent the Computer Science documentation or question:",**kwargs: Any,) -> None:self._model = INSTRUCTOR(instructor_model_name)self._instruction = instructionsuper().__init__(**kwargs)def _get_query_embedding(self, query: str) -> List[float]:embeddings = self._model.encode([[self._instruction, query]])return embeddings[0]def _get_text_embedding(self, text: str) -> List[float]:embeddings = self._model.encode([[self._instruction, text]])return embeddings[0]def _get_text_embeddings(self, texts: List[str]) -> List[List[float]]:embeddings = self._model.encode([[self._instruction, text] for text in texts])return embeddingsasync def _get_query_embedding(self, query: str) -> List[float]:return self._get_query_embedding(query)async def _get_text_embedding(self, text: str) -> List[float]:return self._get_text_embedding(text)

3. Prompt

LlamaIndex提供了模板类,使用方法如下:

from llama_index.core import PromptTemplatetemplate = ("We have provided context information below. \n""---------------------\n""{context_str}""\n---------------------\n""Given this information, please answer the question: {query_str}\n"
)
qa_template = PromptTemplate(template)# you can create text prompt (for completion API)
prompt = qa_template.format(context_str=..., query_str=...)# or easily convert to message prompts (for chat API)
messages = qa_template.format_messages(context_str=..., query_str=...)
from llama_index.core import ChatPromptTemplate
from llama_index.core.llms import ChatMessage, MessageRolemessage_templates = [ChatMessage(content="You are an expert system.", role=MessageRole.SYSTEM),ChatMessage(content="Generate a short story about {topic}",role=MessageRole.USER,),
]
chat_template = ChatPromptTemplate(message_templates=message_templates)# you can create message prompts (for chat API)
messages = chat_template.format_messages(topic=...)# or easily convert to text prompt (for completion API)
prompt = chat_template.format(topic=...)

最常用的提示是text_qa_template 和refine_template。

  • text_qa_template: 用于使用检索到的节点获取查询的初始答案
  • refine_template: 当检索到的文本不适合使用,第一个查询的答案将作为现有答案插入,法学硕士必须根据新上下文更新或重复现有答案。
    • response_mode =“compact”(默认)的单个LLM调用时
    • response_mode =“refine”检索多个节点时使用
# shakespeare!
qa_prompt_tmpl_str = ("Context information is below.\n""---------------------\n""{context_str}\n""---------------------\n""Given the context information and not prior knowledge, ""answer the query in the style of a Shakespeare play.\n""Query: {query_str}\n""Answer: "
)
qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str)query_engine.update_prompts({"response_synthesizer:text_qa_template": qa_prompt_tmpl}
)
query_engine = index.as_query_engine(text_qa_template=custom_qa_prompt, refine_template=custom_refine_prompt
)

二、加载

在RAG(检索增强生成)中的加载阶段涉及以下内容:

  • 节点和文档(Nodes and Documents):
    • 文档是围绕任何数据源的容器,例如PDF、API输出或从数据库中检索的数据。
    • 节点是LlamaIndex中的数据的原子单位,代表源文档的“块”,具有与所在文档和其他节点相关联的元数据。
  • 连接器(Connectors):数据连接器(通常称为读取器)从不同的数据源和数据格式中摄取数据,并将其导入到文档和节点中。

1. 加载

加载数据

from llama_index.core import SimpleDirectoryReaderdocuments = SimpleDirectoryReader("./data").load_data()

使用LlamaHub加载数据:在此示例中,LlamaIndex 使用 DatabaseReader 连接器,该连接器对 SQL 数据库运行查询并将结果的每一行作为文档返回:

from llama_index.core import download_loaderfrom llama_index.readers.database import DatabaseReaderreader = DatabaseReader(scheme=os.getenv("DB_SCHEME"),host=os.getenv("DB_HOST"),port=os.getenv("DB_PORT"),user=os.getenv("DB_USER"),password=os.getenv("DB_PASS"),dbname=os.getenv("DB_NAME"),
)query = "SELECT * FROM users"
documents = reader.load_data(query=query)

2. 转换

加载数据后需要处理和转换数据,然后再将其放入存储系统。 这些转换包括分块、提取元数据和嵌入每个块。转换输入/输出是 Node 对象(Document 是 Node 的子类)。 转换也可以堆叠和重新排序。
转换包括高级和低级 API。

(1)高级API

index通过from_documents() 方法,它接受 Document 对象数组,并正确解析它们并将它们分块。

from llama_index.core.node_parser import SentenceSplittertext_splitter = SentenceSplitter(chunk_size=512, chunk_overlap=10)# global
from llama_index.core import SettingsSettings.text_splitter = text_splitter# per-index
index = VectorStoreIndex.from_documents(documents, transformations=[text_splitter]
)

(2)低级API

也可以明确定义这些步骤。通过使用转换模块(文本拆分器、元数据提取器等)作为独立组件,或在声明性转换管道接口中组合它们来实现此目的。

from llama_index.core import SimpleDirectoryReader
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import TokenTextSplitterdocuments = SimpleDirectoryReader("./data").load_data()
pipeline = IngestionPipeline(transformations=[TokenTextSplitter(), ...])
nodes = pipeline.run(documents=documents)document = Document(text="text",metadata={"filename": "<doc_file_name>", "category": "<category>"},
)

三、索引/Embedding

在RAG(检索增强生成)中的索引阶段涉及以下内容:

  • 索引(Indexes):在摄取数据后,LlamaIndex将数据索引到一个易于检索的结构中。通常这包括生成向量嵌入(vector embeddings),并将其存储在称为向量存储(vector store)的专门数据库中。索引还可以存储关于数据的各种元数据。
  • 嵌入(Embeddings):LLM(大型语言模型)生成称为嵌入(embeddings)的数据的数值表示。在过滤与查询相关的数据时,LlamaIndex会将查询转换为嵌入,并且您的向量存储将找到与查询嵌入数值相似的数据。

LlamaIndex 提供了几种不同的索引类型。 在这里介绍几种最常见的。

  1. Vector Store Index:向量数据库索引获取文档并将它们分成节点。 然后,它创建每个节点文本的向量嵌入,以供LLM查询。要使用向量存储索引,将在加载阶段创建的文档列表或者Node 对象列表传递给它。
from llama_index.core import VectorStoreIndexindex = VectorStoreIndex.from_documents(documents)
index = VectorStoreIndex(nodes)
  1. Summary Index:摘要索引是一种更简单的索引形式,最适合查询,目的是生成文档中文本的摘要。 它只是存储所有文档并将它们全部返回到您的查询引擎。
  2. knowledge graph index:

Top K Retrieval

当搜索嵌入时,查询本身会转换为向量嵌入,然后由 VectorStoreIndex 执行数学运算,根据所有嵌入与查询在语义上的相似程度对它们进行排名。排名完成后,VectorStoreIndex 会返回最相似的嵌入作为相应的文本块。 它返回的嵌入数量称为 k,因此控制返回多少嵌入的参数称为 top_k。 由于这个原因,整个类型的搜索通常被称为“top-k 语义检索”。Top-k 检索是查询向量索引的最简单形式。

四、存储

可以使用LlamaIndex提供的存储方法,也可能使用外部向量数据库。

内置方法:

from llama_index.core import StorageContext, load_index_from_storage# save the index
index.storage_context.persist(persist_dir="<persist_dir>")# rebuild storage context
storage_context = StorageContext.from_defaults(persist_dir="<persist_dir>")# load index
index = load_index_from_storage(storage_context)

外部向量数据库;LlamaIndex 支持很多向量数据库,这些向量存储的架构、复杂性和成本各不相同。 在此示例中,我们将使用 Chroma,一个开源向量数据库。

使用 Chroma 存储 VectorStoreIndex 中的嵌入步骤如下:

  1. 初始化 Chroma 客户端
  2. 创建一个collection来将数据存储在 Chroma 中
  3. 将 Chroma 指定为 StorageContext 中的 vector_store
  4. 使用 StorageContext 初始化 VectorStoreIndex
import chromadb
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext# load some documents
documents = SimpleDirectoryReader("./data").load_data()# initialize client, setting path to save data
db = chromadb.PersistentClient(path="./chroma_db")# create collection
chroma_collection = db.get_or_create_collection("quickstart")# assign chroma as the vector_store to the context
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)# create your index
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context
)# create a query engine and query
query_engine = index.as_query_engine()
response = query_engine.query("What is the meaning of life?")
print(response)

下面的代码是加载一个创建好的向量数据库

import chromadb
from llama_index.core import VectorStoreIndex
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext# initialize client
db = chromadb.PersistentClient(path="./chroma_db")# get collection
chroma_collection = db.get_or_create_collection("quickstart")# assign chroma as the vector_store to the context
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)# load your index from stored vectors
index = VectorStoreIndex.from_vector_store(vector_store, storage_context=storage_context
)# create a query engine
query_engine = index.as_query_engine()
response = query_engine.query("What is llama2?")
print(response)

五、查询

简单的查询只是对LLM的prompt调用:它可以是一个问题并获得答案,或者是一个总结请求。更复杂的查询可能涉及重复/链接prompt+ LLM 调用,甚至跨多个组件的推理循环。

在RAG(检索增强生成)中的查询阶段涉及以下内容:

  • 检索器(Retrievers):检索器定义了如何在给定查询时从索引中高效检索相关上下文。检索策略对于检索到的数据的相关性和效率至关重要。
  • 路由器(Routers):路由器确定将使用哪个检索器从知识库中检索相关上下文。更具体地说,RouterRetriever类负责选择一个或多个候选检索器来执行查询。它们使用选择器根据每个候选的元数据和查询选择最佳选项。
  • 节点后处理器(Node Postprocessors):节点后处理器接收一组检索到的节点,并对它们应用转换、过滤或重新排序逻辑。
  • 响应合成器(Response Synthesizers):响应合成器使用用户查询和一组检索到的文本块从LLM生成响应。

在下面的示例中,我们自定义检索器以对 top_k 使用不同的值,并添加一个后处理步骤,该步骤要求检索到的节点达到要包含的最小相似度分数。 当有相关结果时,这将提供大量数据,但如果没有任何相关结果,则可能不会提供任何数据。

from llama_index.core import VectorStoreIndex, get_response_synthesizer
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor# build index
index = VectorStoreIndex.from_documents(documents)# configure retriever
retriever = VectorIndexRetriever(index=index,similarity_top_k=10,
)# configure response synthesizer
response_synthesizer = get_response_synthesizer()# assemble query engine
query_engine = RetrieverQueryEngine(retriever=retriever,response_synthesizer=response_synthesizer,node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.7)],
)# query
response = query_engine.query("What did the author do growing up?")
print(response)

节点后处理器:支持高级节点过滤和增强,可以进一步提高检索到的节点对象的相关性。 这可以帮助减少 LLM 调用的时间/次数/成本或提高响应质量。例如:

  • KeywordNodePostprocessor:通过required_keywords和exclusion_keywords过滤节点。
  • SimilarityPostprocessor:通过设置相似度分数的阈值来过滤节点(因此仅受基于嵌入的检索器支持)
  • PrevNextNodePostprocessor:使用基于节点关系的附加相关上下文来增强检索到的节点对象。

响应合成:检索器获取相关节点后,BaseSynthesizer 通过组合信息来合成最终响应。配置query_engine = RetrieverQueryEngine.from_args(retriever, response_mode=response_mode),目前支持以下选项:

  • default:通过顺序遍历每个检索到的节点来“创建和完善”答案; 这使得每个节点都有一个单独的 LLM 调用。 适合更详细的答案。
  • compact:通过填充尽可能多的节点文本块来“压缩”每次 LLM 调用期间的提示,使其适合最大提示大小。 如果一个提示中的内容太多,无法填充,请通过多个提示来“创建和完善”答案。
  • tree_summarize:给定一组 Node 对象和查询,递归构造一棵树并返回根节点作为响应。 适合总结目的。
  • no_text:仅运行检索器来获取本应发送到 LLM 的节点,而不实际发送它们。 然后可以通过检查response.source_nodes来检查。 第 5 节更详细地介绍了响应对象。
  • accumulate:给定一组 Node 对象和查询,将查询应用于每个 Node 文本块,同时将响应累积到数组中。 返回所有响应的串联字符串。 适合当您需要对每个文本块单独运行相同的查询时。

六、评估

LlamaIndex 提供了衡量生成结果质量和检索质量的关键模块。

1. 生成结果质量评估

from llama_index.core import VectorStoreIndex
from llama_index.llms.openai import OpenAI
from llama_index.core.evaluation import FaithfulnessEvaluator# create llm
llm = OpenAI(model="gpt-4", temperature=0.0)# build index
...
vector_index = VectorStoreIndex(...)# define evaluator
evaluator = FaithfulnessEvaluator(llm=llm)# query index
query_engine = vector_index.as_query_engine()
response = query_engine.query("What battles took place in New York City in the American Revolution?"
)
eval_result = evaluator.evaluate_response(response=response)
print(str(eval_result.passing))

2. 检索结果评估

from llama_index.core.evaluation import RetrieverEvaluator# define retriever somewhere (e.g. from index)
# retriever = index.as_retriever(similarity_top_k=2)
retriever = ...retriever_evaluator = RetrieverEvaluator.from_metric_names(["mrr", "hit_rate"], retriever=retriever
)retriever_evaluator.evaluate(query="query", expected_ids=["node_id1", "node_id2"]
)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/788691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端开发语言有哪些

前端开发语言有哪些 在数字时代&#xff0c;前端开发已经成为软件开发领域的一个重要分支。前端开发主要关注的是用户界面&#xff08;UI&#xff09;和用户体验&#xff08;UX&#xff09;&#xff0c;而实现这些的关键在于选择适合的前端开发语言。本文将详细介绍前端开发中…

Kafka架构概述

Kafka的体系结构 Kafka是由Apache软件基金会管理的一个开源的分布式数据流处理平台。Kafka具有支持消息的发布/订阅模式、高吞吐量与低延迟、持久化、支持水平扩展、高可用性等特点。可以将Kafka应用于大数据实时处理、高性能数据管道、流分析、数据集成和关键任务应用等场景。…

IMBoy缓存系统深度解析:为何选择depcache而非ETS或Redis

在IMBoy即时通讯平台的开发过程中&#xff0c;我们面临了选择最佳缓存系统的关键技术决策。经过细致的考量&#xff0c;我们选择了depcache作为IMBoy的核心缓存机制。本文将阐述IMBoy缓存系统的选型理由&#xff0c;并对比分析depcache与纯ETS方案及Redis方案的不同优势。 1. …

windows C++ 进程遍历、线程遍历模板。后附模板代码

目录 一、进程遍历 1. 使用CreateToolhelp32Snapshot函数创建系统快照 2.Process32FirstW 3. Process32NextW 4. PROCESSENTRY32W 5. OpenProcess hProcess 1. 查询信息 2. 修改进程状态 3. 访问进程虚拟内存 4. 处理和线程 二、线程遍历 1. 创建系统快照 2. …

20240402—Qt如何通过动态属性设置按钮样式?

前言 正文 1、点击UI文件 2、选择Bool型或是QString 3、设置后这里出现动态属性 4、这qss文件中绑定该动态属性 QPushButton[PopBlueBtn"PopBlueBtn"]{background-color:#1050B7;color:#FFFFFF;font-size:20px;font-family:Source Han Sans CN;//思源黑体 CNbor…

【JavaEE初阶系列】——一万字带你了解 JUC常见类 以及 线程安全集合类(哈希表)

目录 &#x1f6a9;JUC(java.util.concurrent) 的常见类 &#x1f388;Callable 接口 &#x1f308;理解 Callable(相关面试题) &#x1f308;理解 FutureTask &#x1f4dd;线程创建方式 &#x1f388; ReentrantLock可重入锁 &#x1f308;ReentrantLock 优势&#x…

Codeforces Round 798 (Div. 2) (C 树形dp D 曼哈顿距离转换 E 位运算构造)

C&#xff1a;dp以f[i]为根的时候能获得多少个节点&#xff0c;那么dp就是全部儿子里面找一个切掉&#xff0c;其他就是 f[v]的总和了 #include<bits/stdc.h> using namespace std; const int N 1e610,M2*N,mod1e97; #define int long long typedef long long LL; typ…

4.2日java总结,以及窗口的创建

今日份学习——字符串的进阶 1.StringBulider StringBulider是一个java里的关键字&#xff0c;可以看做一个容器&#xff0c;但是其是一个可以改变的容器&#xff0c;对其有四种操作可以进行&#xff0c;分别是添加元素&#xff08;append&#xff09;&#xff0c;反转元素&a…

谷粒商城实战(009 缓存-分布式锁)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第158p-第p165的内容 分布式锁 原理和使用 使用下shell对产生的命令进行发送 查看 -> 撰写 -> 撰写栏 idea 选中的代码提取成方法 加锁…

【教学类-09-07】20240401细线迷宫图02+箭头图片(A4横版一页-2份竖版)

作品展示 作品展示 word模板 重点说明 代码展示 批量制作细线条的迷宫图(A4横板一面2张竖版)箭头图片 作者&#xff1a; 1、落难Coder https://blog.csdn.net/u014297502/article/details/124839912 2、AI对话大师 3、阿夏 作者&#xff1a;2024年4月3日 numint(input(几人&…

Android14之BpBinder构造函数Handle拆解(二百零四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Docker:探索容器化技术,重塑云计算时代应用交付与管理

一&#xff0c;引言 在云计算时代&#xff0c;随着开发者逐步将应用迁移至云端以减轻硬件管理负担&#xff0c;软件配置与环境一致性问题日益凸显。Docker的横空出世&#xff0c;恰好为软件开发者带来了全新的解决方案&#xff0c;它革新了软件的打包、分发和管理方式&#xff…

AIGC之gradio系列学习教程(一)

主题:本篇即为入门,后续将会从函数和使用场景介绍。 Gradio 是一个开源 Python 包,可让快速为机器学习模型、API 或任何任意 Python 函数构建演示或 Web 应用程序。然后,您可以使用 Gradio 的内置共享功能在短短几秒钟内共享演示或 Web 应用程序的链接。无需 JavaScript、…

【layui-table】转静态表格时固定表格列处理行高和单元格颜色

处理思路&#xff1a;覆盖layui部分表格样式 行高处理&#xff1a;获取当前行数据单元格的最高高度&#xff0c;将当前行所有数据单元格高度设置为该最高高度 单元格颜色处理&#xff1a;将原生表格转换为layui表格后&#xff0c;因为原生表格的表格结构和生成的layui表格结构…

C#中值类型与引用类型的存储

目录 值对象与引用对象的存储 引用对象的成员存储 值对象与引用对象的存储 数据项的类型定义了存储数据需要的内存大小及组成该类型的数据成员。类型还决定了对象在内存中的存储位置——栈或堆。 C#中类型分为两种&#xff1a;值类型和引用类型&#xff0c;这两种类型的对象…

中介者模式:优雅解耦的利器

在软件设计中&#xff0c;随着系统功能的不断扩展&#xff0c;对象之间的依赖关系往往会变得错综复杂&#xff0c;导致系统难以维护和扩展。为了降低对象之间的耦合度&#xff0c;提高系统的可维护性和可扩展性&#xff0c;设计模式应运而生。中介者模式&#xff08;Mediator P…

【蓝桥杯 C++高级组省赛以及2020年-蓝桥杯C++省赛合集+部分答案】

一、选择题&#xff08;单项选择&#xff0c;每空30分&#xff09; 请将选择题答案填入答题卡蓝色框内 第一题&#xff08;难度系数 1&#xff09; 结构化程序所要求的基本结构不包括( )。 A.顺序结构 B.GOTO()跳转 C.选择(分支)结构 D.重复(循环)结构 第二题&#xff…

记录一下做工厂的打印pdf程序

功能&#xff1a;在网页点击按钮调起本地的打印程序 本人想到的就是直接调起方式&#xff0c;网上大佬们说用注册表的形式来进行。 后面想到一种&#xff0c;在电脑开机时就开启&#xff0c;并在后台运行&#xff0c;等到有人去网页里面进行触发&#xff0c;这时候就有个问题&a…

银行监管报送系统介绍(十五):金融审计平台

《“十四五”国家审计工作发展规划》中重点强调&#xff0c;金融审计&#xff1a;以防范化解重大风险、促进金融服务实体经济&#xff0c;推动深化金融供给侧结构性改革、建立安全高效的现代金融体系为目标&#xff0c;加强对金融监管部门、金融机构和金融市场运行的审计。 —…

面试题:MySQL 事务 日志 MVCC

事务的特性 ACID 事务的隔离级别 并发事务问题 脏读&#xff1a;一个事务读到另一个事务还没有提交的数据不可重复读&#xff1a;一个事务先后读取同一条记录&#xff0c;但两次读取的数据不同幻读&#xff1a;一个事务按照条件查询数据时&#xff0c;没有对应的数据行&#xf…