LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引

这篇具有很好参考价值的文章主要介绍了LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

推荐阅读列表:

LLM本地知识库问答系统(一):使用LangChain和LlamaIndex从零构建PDF聊天机器人指南

       上一篇文章我们介绍了使用LlamaIndex构建PDF聊天机器人,本文将介绍一下LlamaIndex的基本概念和原理。

LlamaIndex简介

       LlamaIndex(也称为GPT Index)是一个用户友好的界面,可将外部数据连接到大型语言模型(LLM)。它提供了一系列工具来简化流程,包括可以与各种现有数据源和格式(如API、PDF、文档和SQL)集成的数据连接器。此外,LlamaIndex为结构化和非结构化数据提供索引,可以轻松地与LLM一起使用。

       本文将讨论LlamaIndex提供的不同类型的索引以及如何使用它们。LlamaIndex索引包括列表索引、矢量存储索引、树索引和关键字表索引,当然也包括一些特殊索引,比如图索引、Pandas索引、SQL索引和文档摘要索引。

       有了ChatGPT这么强大的LLM,搭建PDF聊天机器人不够吗?为什么还需要LlamaIndex?其实,如果想搭建企业级的聊天机器人,那么ChatGPT的上下文是不够的,常见LLM上下文大致如下:

  • GPT-3:约2000个tokens
  • GPT-3.5:约4000个tokens
  • GPT-4:最多32.000个tokens

Note:1000个tokens大约有750个words

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       LlamaIndex将文档分解为多个Node对象,Node表示源文档的“块”,这些源文档可以是文本块、图像或者其他内容。它们还包含元数据以及与其他节点和索引结构的关系信息。创建索引,其实就是创建这些Node,也可以手动为文档定义Node。

使用LlamaIndex构建索引

下面我们看一下如何创建这些索引:

安装LlamaIndex相关库

pip install llama-indexpip install openai

设置OpenAI API Key

import osos.environ['OPENAI_API_KEY'] = '<YOUR_OPENAI_API_KEY>'import loggingimport sys## showing logslogging.basicConfig(stream=sys.stdout, level=logging.INFO)logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))## load the PDFfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom llama_index import download_loader# define loaderUnstructuredReader = download_loader('UnstructuredReader', refresh_cache=True)loader = UnstructuredReader()# load the datadocuments = loader.load_data('../notebooks/documents/Apple-Financial-Report-Q1-2022.pdf',split_documents=False)

列表索引(List Index)

       列表索引是一个简单的数据结构,其中Node存储在一个序列中。文档文本被分块,转换为节点,并在索引构建过程中存储在列表中。如下图所示:

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       在查询期间,如果没有指定其他查询参数,LlamaIndex只需将列表中的所有节点加载到Response Synthesis模块中。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       列表索引提供了多种查询列表索引方式,比如通过embedding查询最相关的top-k块,或者使用关键字过滤,如下所示:

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       LlamaIndex为列表索引提供embedding支持。每个节点存储文本之外还可以选择存储embedding。在查询期间,我们可以使用embedding对节点进行最大相似度检索,然后调用LLM来生成答案。由于使用embedding的相似性查找(例如,使用余弦相似性)不需要LLM调用,因此embedding是一种代价更低的查询机制,而不需要遍历LLM节点。这意味着在索引构建过程中,LlamaIndex不会调用LLM来生成embedding,而是在查询时生成embedding。这种设计避免了在索引构建期间为所有文本块生成embedding,这对于大数据来说可能代价高昂。

下面是一个具体的使用案例:

from llama_index import GPTKeywordTableIndex, SimpleDirectoryReaderfrom IPython.display import Markdown, displayfrom langchain.chat_models import ChatOpenAI## by default, LlamaIndex uses text-davinci-003 to synthesise response# and text-davinci-002 for embedding, we can change to# gpt-3.5-turbo for Chat modelindex = GPTListIndex.from_documents(documents)query_engine = index.as_query_engine()response = query_engine.query("What is net operating income?")display(Markdown(f"<b>{response}</b>"))## Check the logs to see the different between th## if you wish to not build the index during the index construction# then need to add retriever_mode=embedding to query engine# query with embed_model specifiedquery_engine = new_index.as_query_engine(    retriever_mode="embedding",     verbose=True)response = query_engine.query("What is net operating income?")display(Markdown(f"<b>{response}</b>"))

向量存储索引(Vector Store Index)

向量存储索引是最常见且使用简单的,允许在大量数据中回答查询。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

      默认情况下,GPTVectorStoreIndex使用内存中的SimpleVectorStore。与List Index不同,Vector Store Index在构建索引过程中就生成了embedding,这意味着在构建索引以生成embedding数据期间就调用了LLM。

       查询Vector Store Index前k个最相似的节点,并将它们传递到我们的Response Synthesis模块中。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

from llama_index import GPTVectorStoreIndexindex = GPTVectorStoreIndex.from_documents(documents)query_engine = index.as_query_engine()response = query_engine.query("What did the author do growing up?")response

树索引(Tree Index)

       树索引是一个树结构索引,其中每个节点都是子节点的摘要。在索引构建过程中,树是以自下而上的方式构建的,直到我们最终得到一组根节点。树索引从一组节点(成为该树中的叶节点)构建一个层次树。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

     查询树索引涉及从根节点向下遍历到叶节点。默认情况下,(child_branch_factor=1),查询在给定父节点的情况下选择一个子节点。如果child_branch_factor=2,则查询会在每个级别选择两个子节点。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       与Vector Index不同,Tree Index是在查询时生成embedding,当然如果在查询时指定retriever_mode=“embedding”,那么embedding会延迟生成并被缓存起来。

from llama_index import GPTTreeIndexnew_index = GPTTreeIndex.from_documents(documents)response = query_engine.query("What is net operating income?")display(Markdown(f"<b>{response}</b>"))## if you want to have more content from the answer, # you can add the parameters child_branch_factor# let's try using branching factor 2query_engine = new_index.as_query_engine(    child_branch_factor=2)response = query_engine.query("What is net operating income?")display(Markdown(f"<b>{response}</b>"))

       要在查询期间构建Tree Index,我们需要向查询引擎添加retrier_mode和response_mode,并将GPTTreeIndex中的build_Tree参数设置为False。

index_light = GPTTreeIndex.from_documents(documents, build_tree=False)query_engine = index_light.as_query_engine(    retriever_mode="all_leaf",    response_mode='tree_summarize',)query_engine.query("What is net operating income?")

关键词表索引(Keyword Table Index)

       关键字表索引从每个节点提取关键字,并构建从每个关键字到该关键字的相应节点的映射。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       在查询期间,我们从查询中提取相关关键字,并将这些关键字与预先提取的Node关键字进行匹配,以获取相应的Node。提取的节点被传递到我们的Response Synthesis模块。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

        对于GPTKeywordTableIndex,一般情况是使用LLM从每个文档中提取关键字,这意味着它在构建时确实需要LLM调用。但是,如果使用GPTSimpleKeywordTableIndex(使用正则表达式关键字提取器从每个文档中提取关键字),则在构建时不会调用LLM。

from llama_index import GPTKeywordTableIndexindex = GPTKeywordTableIndex.from_documents(documents)query_engine = index.as_query_engine()response = query_engine.query("What is net operating income?")

可组合性图索引(Composability Graph Index)

       LlamaIndex通过在现有Index的基础上composite indices,此功能能够高效地索引完整的文档层次结构,并为GPT提供量身定制的知识。通过利用可组合性,您可以在多个级别定义索引,例如单个文档的较低级别索引和文档组的较高级别索引。考虑以下示例:

  • 可以为每个文档中的文本创建一个树索引。
  • 生成一个列表索引,该索引覆盖整个文档集合的所有树索引。

下面通过一个实例来说明一下可组合性图索引的能力:

  1. 从多个文档创建树索引

  2. 从树索引生成摘要。如前所述,树索引对于汇总文档集合非常有用。

  3. 接下来,我们将创建一个Graph,它在3个树索引之上有一个列表索引。为什么?因为列表索引适合于合成组合多个数据源上的信息的答案。

  4. 最后查询图形。

      我们加载了苹果从Q1–2022和Q1–2023 两个季度10k的金融数据,并进行问答,代码如下:

## reyears = ['Q1-2023', 'Q2-2023']UnstructuredReader = download_loader('UnstructuredReader', refresh_cache=True)loader = UnstructuredReader()doc_set = {}all_docs = []for year in years:    year_docs = loader.load_data(f'../notebooks/documents/Apple-Financial-Report-{year}.pdf', split_documents=False)    for d in year_docs:        d.extra_info = {"quarter": year.split("-")[0],                         "year": year.split("-")[1],                        "q":year.split("-")[0]}    doc_set[year] = year_docs    all_docs.extend(year_docs)

为每个季度数据创建索引

## setting up vector indicies for each year#---# initialize simple vector indices + global vector index# this will use OpenAI embedding as default with text-davinci-002service_context = ServiceContext.from_defaults(chunk_size_limit=512)index_set = {}for year in years:    storage_context = StorageContext.from_defaults()    cur_index = GPTVectorStoreIndex.from_documents(        documents=doc_set[year],        service_context=service_context,        storage_context=storage_context    )    index_set[year] = cur_index    # store index in the local env, so you don't need to do it over again    storage_context.persist(f'./storage_index/apple-10k/{year}')

从树索引生成摘要。如前所述,树索引对于汇总文档集合非常有用。

# describe summary for each index to help traversal of composed graphindex_summary = [index_set[year].as_query_engine().query("Summary this document in 100 words").response for year in years]

接下来,我们将在3个树索引之上创建一个包括列表索引Graph。

### Composing a Graph to Synthesize Answersfrom llama_index.indices.composability import ComposableGraphfrom langchain.chat_models import ChatOpenAIfrom llama_index import LLMPredictor# define an LLMPredictor set number of output tokensllm_predictor = LLMPredictor(llm=ChatOpenAI(temperature=0, max_tokens=512, model_name='gpt-3.5-turbo'))service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)storage_context = StorageContext.from_defaults()\## define a list index over the vector indicies ## allow us to synthesize information across  each indexgraph = ComposableGraph.from_indices(    GPTListIndex,    [index_set[y] for y in years],    index_summaries=index_summary,    service_context=service_context,    storage_context=storage_context)root_id = graph.root_id#save to diskstorage_context.persist(f'./storage_index/apple-10k/root')## querying graphcustom_query_engines = {    index_set[year].index_id: index_set[year].as_query_engine() for year in years}query_engine = graph.as_query_engine(    custom_query_engines=custom_query_engines)response = query_engine.query("Outline the financial statement of Q2 2023")response.response

Pandas Index and SQL Index

这个相对比较好理解一些,我们直接展示一下实例代码:

Pandas Index:

from llama_index.indices.struct_store import GPTPandasIndeximport pandas as pddf = pd.read_csv("titanic_train.csv")index = GPTPandasIndex(df=df)query_engine = index.as_query_engine(    verbose=True)response = query_engine.query(    "What is the correlation between survival and age?",)response

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

SQL Index:

       可以使用LLM去访问传统数据库了,对于传统行业是不有些激动呢?下面展示一个例子,更多例子可以参考(https://gpt-index.readthedocs.io/en/latest/examples/index_structs/struct_indices/SQLIndexDemo.html)

# install wikipedia python package!pip install wikipediafrom llama_index import SimpleDirectoryReader, WikipediaReaderfrom sqlalchemy import create_engine, MetaData, Table, Column, String, Integer, select, columnwiki_docs = WikipediaReader().load_data(pages=['Toronto', 'Berlin', 'Tokyo'])engine = create_engine("sqlite:///:memory:")metadata_obj = MetaData()# create city SQL tabletable_name = "city_stats"city_stats_table = Table(    table_name,    metadata_obj,    Column("city_name", String(16), primary_key=True),    Column("population", Integer),    Column("country", String(16), nullable=False),)metadata_obj.create_all(engine)from llama_index import GPTSQLStructStoreIndex, SQLDatabase, ServiceContextfrom langchain import OpenAIfrom llama_index import LLMPredictorllm_predictor = LLMPredictor(llm=LLMPredictor(llm=ChatOpenAI(temperature=0, max_tokens=512, model_name='gpt-3.5-turbo')))service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)sql_database = SQLDatabase(engine, include_tables=["city_stats"])sql_database.table_info# NOTE: the table_name specified here is the table that you# want to extract into from unstructured documents.index = GPTSQLStructStoreIndex.from_documents(    wiki_docs,     sql_database=sql_database,     table_name="city_stats",    service_context=service_context)# view current table to verify the answer laterstmt = select(    city_stats_table.c["city_name", "population", "country"]).select_from(city_stats_table)with engine.connect() as connection:    results = connection.execute(stmt).fetchall()    print(results)query_engine = index.as_query_engine(    query_mode="nl")response = query_engine.query("Which city has the highest population?")

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

文档摘要索引(Document Summary Index)

       Document Summary Index是LlamaIndex全新的数据结构,非常适合QA系统开发的。到目前为止,我们已经研究了单个索引,我们可以通过使用单个索引或将多个索引组合在一起来构建LLM QA应用程序。

一般来说,强大的LLM-QA系统通常由以下方式开发:

  1. 获取源文档并将其划分为文本块;

  2. 然后将文本块存储在矢量数据库中;

  3. 在查询时间期间,通过利用用于嵌入的相似性和/或关键字过滤器来检索文本块;

  4. 执行Response synthesis;

然而,这种方法有几个限制,会影响检索性能。

当前方法的缺点:

  1. 文本块没有完整的全局上下文,这通常会限制问答过程的有效性。

  2. 需要仔细调整top-k/相似性得分阈值,因为太小的值可能导致错过相关上下文,而太大的值可能会增加不相关上下文的成本和延迟。

  3. embedding可能并不总是为问题选择最合适的上下文,因为这个过程本质上是分别确定文本和上下文的。

       为了增强检索结果,添加了关键字过滤器。然而,这种方法有其自身的一系列挑战,例如通过手动工作或使用NLP关键字提取/主题标记模型来为每个文档识别适当的关键字,以及从查询中推断正确的关键字。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

       这就是LlamaIndex引入文档摘要索引的地方,该索引可以为每个文档提取和索引非结构化文本摘要,从而提高了现有方法之外的检索性能。这个索引比单个文本块包含更多的信息,并且比关键字标签具有更多的语义。它还允许灵活的检索,包括LLM和基于embedding的方法。

       在构建期间,该索引加载文档,并使用LLM从每个文档中提取摘要。在查询期间,它根据摘要检索要查询的相关文档,方法如下:

  • 基于LLM的检索:获取文档摘要集合,并请求LLM识别相关文档+相关性得分
  • 基于embedding的检索:利用摘要嵌入相似性来检索相关文档,并对检索结果的数量施加top-k限制。

注意:Document Summary Index的检索类检索任何选定文档的所有节点,而不是在节点级别返回相关块。

下面来看一个例子:

import nest_asyncionest_asyncio.apply()from llama_index import (    SimpleDirectoryReader,    LLMPredictor,    ServiceContext,    ResponseSynthesizer)from llama_index.indices.document_summary import GPTDocumentSummaryIndexfrom langchain.chat_models import ChatOpenAIwiki_titles = ["Toronto", "Seattle", "Chicago", "Boston", "Houston"]from pathlib import Pathimport requestsfor title in wiki_titles:    response = requests.get(        'https://en.wikipedia.org/w/api.php',        params={            'action': 'query',            'format': 'json',            'titles': title,            'prop': 'extracts',            # 'exintro': True,            'explaintext': True,        }    ).json()    page = next(iter(response['query']['pages'].values()))    wiki_text = page['extract']    data_path = Path('data')    if not data_path.exists():        Path.mkdir(data_path)    with open(data_path / f"{title}.txt", 'w') as fp:        fp.write(wiki_text)# Load all wiki documentscity_docs = []for wiki_title in wiki_titles:    docs = SimpleDirectoryReader(input_files=[f"data/{wiki_title}.txt"]).load_data()    docs[0].doc_id = wiki_title    city_docs.extend(docs)# # LLM Predictor (gpt-3.5-turbo)llm_predictor_chatgpt = LLMPredictor(llm=ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo"))service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor_chatgpt, chunk_size_limit=1024)# default mode of building the indexresponse_synthesizer = ResponseSynthesizer.from_args(response_mode="tree_summarize", use_async=True)doc_summary_index = GPTDocumentSummaryIndex.from_documents(    city_docs,     service_context=service_context,    response_synthesizer=response_synthesizer)doc_summary_index.get_document_summary("Boston")

知识图谱索引(Knowledge Graph Index)

       它通过在一组文档上提取形式为(主语、谓语、宾语)的知识三元组来构建索引,了解知识图谱的读者不陌生。

       在查询期间,它可以只使用知识图作为上下文进行查询,也可以利用每个实体的底层文本作为上下文。通过利用底层文本,我们可以针对文档的内容提出更复杂的查询。

       把一个图想象成相互连接的边和顶点。

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

更多例子,可以参考(https://gpt-index.readthedocs.io/en/latest/examples/index_structs/knowledge_graph/KnowledgeGraphDemo.html)

各种Index对比

LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引,ChatGPT,人工智能

文章来源地址https://www.toymoban.com/news/detail-684049.html

到了这里,关于LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AIGC:【LLM(四)】——LangChain+ChatGLM:本地知识库问答方案

    LangChain+ChatGLM项目(https://github.com/chatchat-space/langchain-ChatGLM)实现原理如下图所示 (与基于文档的问答 大同小异,过程包括:1 加载文档 - 2 读取文档 - 3/4文档分割 - 5/6 文本向量化 - 8/9 问句向量化 - 10 在文档向量中匹配出与问句向量最相似的top k个 - 11/12/13 匹配出的文本作为上下

    2024年02月13日
    浏览(54)
  • 从LangChain+LLM的本地知识库问答到LLM与知识图谱、数据库的结合

    过去半年,随着ChatGPT的火爆,直接带火了整个LLM这个方向,然LLM毕竟更多是基于过去的经验数据预训练而来,没法获取最新的知识,以及各企业私有的知识 为了获取最新的知识,ChatGPT plus版集成了bing搜索的功能,有的模型则会调用一个定位于 “链接各种AI模型、工具”的

    2024年02月12日
    浏览(69)
  • 从零实现Transformer、ChatGLM-6B、LangChain+LLM的本地知识库问答

    最近一直在做类ChatGPT项目的部署 微调,关注比较多的是两个:一个LLaMA,一个ChatGLM,会发现有不少模型是基于这两个模型去做微调的,说到微调,那具体怎么微调呢,因此又详细了解了一下微调代码,发现微调LLM时一般都会用到Hugging face实现的Transformers库的Trainer类 从而发现

    2024年02月08日
    浏览(53)
  • 开源大模型ChatGLM2-6B 2. 跟着LangChain参考文档搭建LLM+知识库问答系统

    租用了1台GPU服务器,系统 ubuntu20,Tesla V100-16GB (GPU服务器已经关机结束租赁了) SSH地址:* 端口:17520 SSH账户:root 密码:Jaere7pa 内网: 3389 , 外网:17518 VNC地址:* 端口:17519 VNC用户名:root 密码:Jaere7pa 硬件需求,ChatGLM-6B和ChatGLM2-6B相当。 量化等级    最低 GPU 显存 F

    2024年02月03日
    浏览(56)
  • 【chatglm3】(4):如何设计一个知识库问答系统,参考智谱AI的知识库系统,

    https://www.bilibili.com/video/BV16j411E7FX/?vd_source=4b290247452adda4e56d84b659b0c8a2 【chatglm3】(4):如何设计一个知识库问答系统,参考智谱AI的知识库系统,学习设计理念,开源组件 https://open.bigmodel.cn/knowledge 知识配置: 项目地址是: https://github.com/chatchat-space/Langchain-Chatchat gitee搬运的项

    2024年02月05日
    浏览(48)
  • ai聊天问答知识库机器人源码,基于gpt实现的本地知识库问答实现,聊天对话效果,发送回复以及流式输出...

    现在基于gpt做自己项目的问答机器人,效果非常的好。可以把自己的文档上传上去,让机器人根据文档来进行回答。 想要实现智能AI问答功能,现在大部分都是基于向量数据库的形式。 整体的流程就是:上传文档===openai向量接口 ==== 存入向量数据库 访客咨询:  咨询问题

    2024年02月10日
    浏览(49)
  • LangChain入门(四)-构建本地知识库问答机器人

    在这个例子中,我们会介绍如何从我们本地读取多个文档构建知识库,并且使用 Openai API 在知识库中进行搜索并给出答案。 目录 一、安装向量数据库chromadb和tiktoken 二、使用案例 三、embeddings持久化 四、在线的向量数据库Pinecone 一、安装向量数据库chromadb和tiktoken    其中h

    2024年02月05日
    浏览(60)
  • 用好Python自然语言工具包-- 实例“基于本地知识库的自动问答”

    首先鸣谢thomas-yanxin 本问中示例来自他在GitHub上的开源项目“基于本地知识库的自动问答”,链接如下: thomas-yanxin/LangChain-ChatGLM-Webui: 基于LangChain和ChatGLM-6B的针对本地知识库的自动问答 (github.com) 目录 1. 基础知识: 2. NLTK库的使用 3. 实例代码分析 设备的定义 函数定义:从网

    2024年02月05日
    浏览(72)
  • (一)AI本地知识库问答(可运行):LangChain+Chroma向量数据库+OpenAi大模型

    只需要看config目录下的config.py,data目录下的txt知识库文件,db向量数据库文件在持久化部署后会自动生成,route下的app.py,scripts目录下的Chroma向量库持久化部署.py这几个就可以,scripts目录下的考勤问答.py和test目录下都是单独的自己测试的小代码,可以不用关注 因为运行需要

    2024年02月03日
    浏览(53)
  • AI知识库进阶!三种数据处理方法!提高正确率!本地大模型+fastgpt知识库手把手搭建!22/45

    hi~ 在上一篇,我们成功搭建了本地知识库+大模型的完全体! 在知识星球收到很多朋友的打卡,有各种报错差点崩溃的,也有看到部署成功,开心得跳起来的! 除了自用,还有星球朋友学会搭建,成功接到商单(听说单子还不小)! 不管怎样,酸甜苦辣,总算把它部署了下

    2024年03月11日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包