在亚马逊云科技上部署开源大模型并利用RAG和LangChain开发生成式AI应用

news2024/11/17 1:25:31

项目简介:

小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案,帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践,并应用到自己的日常工作里。

本次介绍的是如何在亚马逊云科技上利用SageMaker机器学习服务部署开源大模型,使用LangChain框架调用大模型生成回复,并利用RAG技术增强内容生成,再通过Streamlit框架开发用户界面回答用于关于用户关于特定文档的问题。本架构设计全部采用了云原生Serverless架构,提供可扩展和安全的AI解决方案。本方案的解决方案架构图如下:

方案所需基础知识 

什么是 Amazon SageMaker?

Amazon SageMaker 是亚马逊云科技提供的一站式机器学习服务,旨在帮助开发者和数据科学家轻松构建、训练和部署机器学习模型。SageMaker 提供了从数据准备、模型训练到模型部署的全流程工具,使用户能够高效地在云端实现机器学习项目。

为什么利用 LangChain 框架调用大模型 API 生成回复?

LangChain 是一个强大的框架,旨在简化调用大型语言模型(如 GPT-3、Amazon Bedrock 上的模型等)API 的过程。通过 LangChain,开发者可以轻松地构建和管理复杂的对话逻辑,将多个 API 调用和自然语言处理(NLP)任务无缝集成到一起,从而实现智能、上下文丰富的对话体验。

利用 RAG 开发AI问答服务的好处

RAG(检索增强生成,Retrieval-Augmented Generation)是一种结合文档检索与生成式 AI 模型的技术,用于构建强大的问答系统。通过将文档检索与生成模型结合,RAG 可以在提供实时、准确的回答时,确保回复的内容基于最新的文档信息。

提高回答准确性

RAG 结合检索和生成模型,使系统能够从大量文档中提取相关信息,并生成符合上下文的准确答案,确保回答内容的可靠性和权威性。

支持实时更新

RAG 系统可以动态检索和生成答案,确保用户获得基于最新信息的回复,特别适用于内容频繁更新的领域。

增强用户体验

通过使用 LangChain 调用大模型 API 和 RAG 技术,问答服务不仅能提供流畅的自然语言交互,还能回答复杂、多样的问题,显著提升用户体验。

降低开发复杂性

LangChain 框架简化了与大模型的集成,RAG 技术则优化了信息检索和答案生成过程,二者结合有效降低了开发智能问答系统的复杂性。

本方案包括的内容

1. 利用Amazon SageMaker部署一个Flan-T5大语言模型

2. 通过LangChain框架调用大语言模型

3. 在SageMaker Studio上利用RAG技术开发一个基于文档内容回答问题的GenAI应用

4. 开发和部署Streamlit框架开发的网页应用

项目搭建具体步骤:

1. 登录亚马逊云科技控制台,进入Studio功能,点击Open Studio进入Studio,用于在Jupyter Notebook中运行大模型代码。

2.点击Studio Classic,再点击Open进入Jupyter Notebook

3. 点击主页中的JumpStart功能快速创建大语言模型

4. 搜索并选择”Falcon 7B Instruct BF16“大模型

5. 选择部署大模型的计算资源类型为”ml.g5.4xlarge“,再点击开始部署

6. 我们创建一个requirements.txt文件,复制以下内容用于安装必要依赖。

boto3==1.34.37
langchain==0.0.276
numpy==1.23.5
pandas==1.5.3
scikit_learn==1.2.1
seaborn==0.12.2
sentence-transformers
faiss-gpu-cu11

7. 创建一个Jupyter Notebook ipynb文件,首先运行以下代码指定模型id,并安装必要依赖。

endpoint_name = "jumpstart-dft-hf-llm-falcon-7b-instruct-bf16"
!pip install --upgrade pip
!pip install --upgrade --no-cache --force-reinstall -r requirements.txt

8. 运行以下代码,导入必要依赖

import boto3, json
import pandas as pd
import langchain
from langchain.embeddings import SagemakerEndpointEmbeddings
from langchain.llms.sagemaker_endpoint import ContentHandlerBase
from langchain.chains import RetrievalQA
from langchain.document_loaders import TextLoader
from langchain.indexes import VectorstoreIndexCreator
from langchain.vectorstores import Chroma, AtlasDB, FAISS
from langchain.text_splitter import CharacterTextSplitter
from langchain import PromptTemplate
from langchain.chains.question_answering import load_qa_chain
from langchain.document_loaders.csv_loader import CSVLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings

9. 接下来我们定义两个函数”query_endpoint_with_json_payload“和”parse_response_model“,用于调用大模型和提取生成回复中的内容,并配置模型区域、节点名、回复提取函数、提示词。

def query_endpoint_with_json_payload(encoded_json, endpoint_name, content_type="application/json"):
    client = boto3.client("runtime.sagemaker")
    response = client.invoke_endpoint(
        EndpointName=endpoint_name, ContentType=content_type, Body=encoded_json
    )
    return response

# parse the Sagemaker Endpoint response to the user query
def parse_response_model(query_response):
    model_predictions = json.loads(query_response["Body"].read())
    return [gen["generated_text"] for gen in model_predictions]


_MODEL_CONFIG_ = {
    
     "jumpstart-dft-hf-llm-falcon-7b-instruct-bf16" : {
        "aws_region": "us-east-1",
        "endpoint_name": endpoint_name,
        "parse_function": parse_response_model,
        "prompt": """{context}\n\nGiven the above context, answer the following question:\n{question}\nAnswer: """,
    },
    
}

10. 定义模型参数,定义模型输入、输出请求格式,并按该格式调用SageMaker上部署的Falcon模型。

import json
from langchain.llms.sagemaker_endpoint import LLMContentHandler, SagemakerEndpoint

parameters ={
        "max_new_tokens": 100,
        "num_return_sequences": 1,
        "top_k": 50,
        "top_p": 0.95,
        "do_sample": False,
        "return_full_text": False,
        "temperature": 0.2
    }

class ContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompt: str, model_kwargs={}) -> bytes:
        input_str = json.dumps({"inputs": prompt, "parameters": model_kwargs})
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        return response_json[0]["generated_text"]


content_handler = ContentHandler()

sm_llm_falcon_instruct = SagemakerEndpoint(
    endpoint_name=_MODEL_CONFIG_["jumpstart-dft-hf-llm-falcon-7b-instruct-bf16"]["endpoint_name"],
    region_name=_MODEL_CONFIG_["jumpstart-dft-hf-llm-falcon-7b-instruct-bf16"]["aws_region"],
    model_kwargs=parameters,
    content_handler=content_handler,
)

11. 向大模型提问示例问题,可以得到常规回复。

sm_llm_falcon_instruct("Which day comes after Friday?")

 12. 初始化一个HuggingFace向量模型

sm_llm_embeddings = HuggingFaceEmbeddings()

13. 创建一个关于Amazon SageMaker产品问题与回复的csv文件,再将该csv文件导入Pandas DataFrame,只提取答案后再存入到processd.csv文件中。

s3_path = "s3://jumpstart-cache-prod-us-east-2/training-datasets/Amazon_SageMaker_FAQs/Amazon_SageMaker_FAQs.csv"

!mkdir -p rag_data
!aws s3 cp $s3_path rag_data/Amazon_SageMaker_FAQs.csv

df_knowledge = pd.read_csv("rag_data/Amazon_SageMaker_FAQs.csv", header=None, usecols=[1], names=["Answer"])
df_knowledge.to_csv("rag_data/processed.csv", header=False, index=False)

14. 利用LangChain读取csv文件

loader = CSVLoader(file_path="rag_data/processed.csv")
documents = loader.load()

15. 设置llm大模型模型回复参数

sm_llm_falcon_instruct.model_kwargs = {
        "max_new_tokens": 50,
        "num_return_sequences": 1,
        "top_k": 50,
        "top_p": 0.95,
        "do_sample": False,
        "return_full_text": False,
        "temperature": 0.1
}

16. 利用LangChain将文档进行分割,再使用FAISS(Facebook AI Similarity Search)通过HuggingFace向量模型将文档转为向量并创建索引。

text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=5)
texts = text_splitter.split_documents(documents)
sm_llm_embeddings
docsearch = FAISS.from_documents(texts, sm_llm_embeddings)

17. 通过问题对文档内容进行语义搜索得到回复,问题为:”如何为Amazon SageMaker上的托管临时训练选择实例类型“。

question = "Which instances can I use with managed spot training in Amazon SageMaker?"
docs = docsearch.similarity_search(question, k=3)
docs

得到相关回复和所在文档元数据信息

18.  下面我们通过提示词模板构建问答链,再基于问题调用知识库进行语义搜索得到回复

prompt_template = """{context}\n\nGiven the above context, answer the following question:\n{question}\n\nAnswer:"""

PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

sm_llm_falcon_instruct.model_kwargs = {
        "max_new_tokens": 50,
        "num_return_sequences": 1,
        "top_k": 50,
        "top_p": 0.95,
        "do_sample": False,
        "return_full_text": True,
        "temperature": 0.1,
}
chain = load_qa_chain(llm=sm_llm_falcon_instruct, prompt=PROMPT)

result = chain({"input_documents": docs, "question": question}, return_only_outputs=True)["output_text"]

    
print(result)

我们可以看到LangChain问答链根据我们定义的提示词中的格式得到了正确回复。

19. 我们再创建一个新的Python函数”main.py“,复制以下代码。这个文件包括了我们刚刚通过LangChain文本向量化、利用RAG与大模型与知识库API交互全部完整代码。同时创建了一个streamlit服务器与用户在UI进行交互。

# From Cell 2 with small modifications
import os
import streamlit as st
import json
import boto3
import logging

from langchain.chains import RetrievalQA
from langchain.indexes import VectorstoreIndexCreator
from langchain_community.vectorstores import Chroma, FAISS
from langchain.prompts import PromptTemplate
from langchain_community.embeddings import SagemakerEndpointEmbeddings
from langchain_community.embeddings.sagemaker_endpoint import EmbeddingsContentHandler
from langchain_community.llms.sagemaker_endpoint import LLMContentHandler, SagemakerEndpoint
from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings


# Set up logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# From Cell 5
sm_llm_embeddings = HuggingFaceEmbeddings()

# From Cell 4 and 6
class ContentHandler(LLMContentHandler):
    
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompt: str, model_kwargs={}) -> bytes:
        input_str = json.dumps({"inputs": prompt, "parameters": model_kwargs})
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        return response_json[0]["generated_text"]

def query_endpoint_with_json_payload(encoded_json, endpoint_name, content_type="application/json"):
    client = boto3.client("runtime.sagemaker")
    response = client.invoke_endpoint(
        EndpointName=endpoint_name, ContentType=content_type, Body=encoded_json
    )
    return response

def parse_response_model(query_response):
    model_predictions = json.loads(query_response["Body"].read())
    return [gen["generated_text"] for gen in model_predictions]


# The following replaces cells 8 and 9
# loading PDF, DOCX and TXT files as LangChain Documents
def load_document(file):
    import os
    name, extension = os.path.splitext(file)

    if extension == '.pdf':
        from langchain.document_loaders import PyPDFLoader
        print(f'Loading {file}')
        loader = PyPDFLoader(file)
    elif extension == '.docx':
        from langchain.document_loaders import Docx2txtLoader
        print(f'Loading {file}')
        loader = Docx2txtLoader(file)
    elif extension == '.txt':
        from langchain.document_loaders import TextLoader
        loader = TextLoader(file)
    elif extension == '.csv':
        from langchain_community.document_loaders.csv_loader import CSVLoader
        loader = CSVLoader(file)
    else:
        print('Document format is not supported!')
        return None

    document = loader.load()
    return document


# From Cell 11
def split_text(document):
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=5)
    texts = text_splitter.split_documents(document)
    return texts


# Cell 11
def create_embeddings(texts):
    docsearch = FAISS.from_documents(texts, sm_llm_embeddings)
    return docsearch

# Not in notebook but needed for streamlite application
def clear_history():
    if 'history' in st.session_state:
        del st.session_state['history']


# Application build from notebook - see individual parts
def ask_and_get_answer(question, documents):
    from langchain_community.llms.sagemaker_endpoint import LLMContentHandler, SagemakerEndpoint
    from langchain.chains.question_answering import load_qa_chain
    
    # From Cell 13
    docs = documents.similarity_search(question, k=3)
    
    # From Cell 14
    prompt_template = """You are an AI assistant for answering questions.
    Refrane from providing any information that is not in the provide context.
    If there is not an answer in the provided context respond with "I don't know."
    {context}
    
    Question: {question}

    Answer:"""

    parameters ={
        "max_new_tokens": 100,
        "num_return_sequences": 1,
        "top_k": 50,
        "top_p": 0.95,
        "do_sample": False,
        "return_full_text": False,
        "temperature": 0.2
    }

    # From Cell 4
    _MODEL_CONFIG_ = {
    
     "jumpstart-dft-hf-llm-falcon-7b-instruct-bf16" : {
        "aws_region": "us-east-1",
        "endpoint_name": "jumpstart-dft-hf-llm-falcon-7b-instruct-bf16",
        "parse_function": parse_response_model,
        "prompt": prompt_template,
        },
    
    }
    
    # From Cell 6
    content_handler = ContentHandler()

    sm_llm_falcon_instruct = SagemakerEndpoint(
        endpoint_name=_MODEL_CONFIG_["jumpstart-dft-hf-llm-falcon-7b-instruct-bf16"]["endpoint_name"],
        region_name=_MODEL_CONFIG_["jumpstart-dft-hf-llm-falcon-7b-instruct-bf16"]["aws_region"],
        model_kwargs=parameters,
        content_handler=content_handler,
    )
    
    # From Cell 10
    sm_llm_falcon_instruct.model_kwargs = {
        "max_new_tokens": 50,
        "num_return_sequences": 1,
        "top_k": 50,
        "top_p": 0.95,
        "do_sample": False,
        "return_full_text": True,
        "temperature": 0.1,
        }
    
    
    # From Cell 14 and 15
    PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

    chain = load_qa_chain(llm=sm_llm_falcon_instruct, prompt=PROMPT)

    answer = chain({"input_documents": docs, "question": question}, return_only_outputs=True)["output_text"]
    
    return answer



# Code required for the Streamlite app
if __name__ == "__main__":
    import os
    st.subheader('Retrieval Augmented Generation (RAG)')
    with st.sidebar:
        # file uploader widget
        uploaded_file = st.file_uploader('Upload context file:', type=['pdf', 'docx', 'txt', 'csv'])
        
        # add data button widget
        add_data = st.button('Process Context File', on_click=clear_history)

        if uploaded_file and add_data: # if the user browsed a file
            with st.spinner('Reading, chunking and embedding file ... Please Wait'):

                # writing the file from RAM to the current directory on disk
                bytes_data = uploaded_file.read()
                file_name = os.path.join('./', uploaded_file.name)
                with open(file_name, 'wb') as f:
                    f.write(bytes_data)

                document = load_document(file_name)
                texts = split_text(document)

                # creating the embeddings and returning FAISS vector store.
                vector_store = create_embeddings(texts)

                # saving the vector store in the streamlit session state (to be persistent between reruns)
                st.session_state.vs = vector_store
                st.success('File processing completed successfully!  You can now ask questions.')
                
    
    # user's question text input widget
    question = st.text_input('Ask a question about the content of your file:')
    
    if question: # if the user entered a question and hit enter
        question = f"{question}"
        if 'vs' in st.session_state: # if there's the vector store (user uploaded, split and embedded a file)
            vector_store = st.session_state.vs
            # st.write(f'k: {k}')
            response = ask_and_get_answer(question, vector_store)
            answer = response.partition("Answer:")[2]

            # text area widget for the LLM answer
            st.text_area('LLM Answer: ', value=answer, height=400)

            st.divider()

            # if there's no chat history in the session state, create it
            if 'history' not in st.session_state:
                st.session_state.history = ''

            # the current question and answer
            value = f'Q: {question} \nA: {answer}'

            st.session_state.history = f'{value} \n {"-" * 100} \n {st.session_state.history}'
            h = st.session_state.history

            # text area widget for the chat history
            st.text_area(label='Chat History', value=h, key='history')

20. 我们通过以下命令启动streamlit服务器。启动后会返回服务器的URL地址,复制该地址在浏览器中打开。

streamlit run main.py --server.enableXsrfProtection false

21. 浏览器中打开后,我们进入了UI界面,点击”Browse files“上传文档,上传刚刚我们从Amazon Sagemaker产品常见问题csv文件中提取的问题csv文件,"processed.csv"。

22. 上传成功后,我们再问题界面提问”什么是SageMaker“,就可以得到利用RAG基于知识库中文档得到的相关回答。

 

以上就是在亚马逊云科技上利用亚马逊云科技上利用部署开源大模型,并利用RAG技术和Streamlit开发GenAI文档问答服务的全部步骤。欢迎大家未来与我一起,未来获取更多国际前沿的生成式AI开发方案。

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

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

相关文章

CMake 的快速应用

一,什么是CMake? 我们在学习了C 和 C后, 知道从c/c代码,变成可执行文件的过程如下: 如果使用的IDE开发,例如vs 2017,QT,等,那么一般IDE会帮你生成可执行文件,开发者一键点击&#…

Wandb使用指南

安装: pip install wandb 登录 wanbd login 在terminal中操作查看你的API key并粘贴回车进行授权(https://wandb.ai/authorize) 设置离线模式/在线模式 设置为offline会在无网络(内网)的时候使用,常用于de…

如何运用独特的产业运营体系打造一流的数字媒体产业园

如何运用独特的产业运营体系打造一流的数字媒体产业园 2024-08-15 17:37树莓集团 在数字经济蓬勃发展的今天,数字媒体产业作为其中的重要一环,正展现出巨大的潜力和活力。而如何运用独特的产业运营体系,打造一流的数字媒体产业园&#xff0…

SQLAlchemy 学习笔记

通信类型:AF_INET 协议家族一般是表示TCP通信的SOC_STREAM和UDP通信的SOCK_DGRAM。对于TCP通信,建立socket连接,: s socket.socket(socket.AF_INET, socket.SOCK_STREAM)连接socket, s.connect((host,port))socket通信…

PostWigger的xss漏洞

文章目录 Lab: Exploiting DOM clobbering to enable XSS Lab: Exploiting DOM clobbering to enable XSS 这是一道dom破坏题。 首先进入,发现都是一个个博客。 随便点击看看。 发现是一篇文章之后是一些评论以及咱们也可以发布评论。这里的Email使用了html的正…

Redis的缓存淘汰策略

1. 查看Redis 最大的占用内存 打开redis配置文件, 设置maxmemory参数,maxmemory 是bytes字节类型, 注意转换 2. Redis默认内存多少可以用 注意: 在64bit系统下, maxmemory 设置为 0 表示不限制Redis内存使用 3. 一般生产上如何配置 一般推荐Redis 设置内…

微信小程序骨架屏

骨架屏是常用的一种优化方案,针对于页面还未加载完时给用户的一种反馈方式。如果自己要写骨架屏有点复杂因为页面的元素过多且不稳定,这边直接使用微信开发工具生成骨架屏。也不只有微信开发工具有像常用的抖音开发工具,字节开发工具都有对应…

Python自准直仪双筒望远镜光学ABCD矩阵行为算法

🎯要点 🎯平面;曲面;圆柱面;非球面光,双凸透镜;90 度棱镜;分束立方体,双透镜棱;镜分光镜光线;横置隔膜;全内反射;多个分束…

【Django开发】前后端分离django美多商城项目第1篇:欢迎来到美多 项目主要页面介绍【附代码文档】

本教程的知识点为: 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计: 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后…

看图学sql之sql 中的窗口函数

数据分析社区直达 免费数据分析资料下载。定期分享数据分析领域的最新动态、实战案例、技术工具评测、数据可视化技巧以及行业洞察报告。

【Arduino】ATmega328PB 单片机初始化配置,连接使用配置 arduino

总览 1.下载资料 2.配置 arduino 首选项 3.配置开发板管理器 4.配置不同 晶振频率 的 mega328PB 的参数设置 一、下载资料 1.你也可以看着资料自己来弄,如果嫌我麻烦 网盘:https://pan.baidu.com/s/13FCKXE8t_AZeixcR_bEhXg 提取密码:123…

从Linux内核探索 Socket 的本质

目录 一、引言 二、Socket 的概念 三、Socket 的使用场景 四、Socket 的设计 五、提供 Socket 层 六、Socket 如何实现网络通信 (一)建立连接 (二)数据传输 七、Socket 怎么实现“继承” 八、总结 一、引言 相信大家刚…

[Zer0pts2020]Can you guess it?1

打开题目 看到信息随便输入一个数&#xff0c;显示错误 查看源代码 看到php代码&#xff0c;代码审计 <?php include config.php; // FLAG is defined in config.php if (preg_match(/config\.php\/*$/i, $_SERVER[PHP_SELF])) { exit("I dont know what you are t…

以node / link文件表征的道路网络-----dijkstra算法yyds-----基于南京公路公开数据做路径规划(上)

前文已经基于公开数据&#xff0c;获得了南京的全域高速公路的路网数据&#xff0c;这些以node / link文件表征的道路网络不仅延续了osm地图中所包含的经纬度、名称、容量等信息 &#xff0c;还包含了一个重要的道路等级字段 “link_type_name”。 交通部门一般以高速公路、国…

ThinkPHP的SQL注入漏洞学习

目录 漏洞环境 漏洞概要 函数学习 call_user_func函数 mplode函数 漏洞分析 漏洞修复 攻击总结 漏洞环境 漏洞存在于 Builder 类的 parseData 方法中。由于程序没有对数据进行很好的过滤&#xff0c;将数据拼接进 SQL 语句&#xff0c;导致 SQL注入漏洞 的产生。 漏洞…

Shell参考 - Linux Shell 训练营

出品方<Linux.cn & 阿里云开发者学堂> 一&#xff0c;Linux 可以划分为以下四个部分&#xff1a; 1. 应用软件 2. 窗口管理软件 Unity Gnome KDE 3. GNU 系统工具链 Software- GNU Project - Free Software Foundation 4. Linux 内核 二&#xff0c;什么是shell 1. L…

一款免费开源电脑流量监控软件,电脑流量统计工具!

TrafficMonitor是一个开源的网络速度监控工具&#xff0c;它能够在Windows平台上以悬浮窗的形式显示当前的网速、CPU和内存使用情况。该工具支持多种显示模式&#xff0c;包括悬浮窗和任务栏显示&#xff0c;并且允许用户更换不同的皮肤来自定义外观样式。此外&#xff0c;Traf…

【MySQL】数据库基础(库的操作)

目录 一、MySQL安装、连接、修改密码操作 二、库的操作 2.1 创建数据库 2.2 字符集和校验规则 2.3 操控数据库 2.4 修改数据库 2.5 删除数据库 2.6 数据库的备份和恢复 2.7 查看连接情况 前情提要&#xff1a; 我的服务器操作系统是Ubuntu20.04&#xff0c;安装的是M…

eNSP 华为远程访问路由器

华为远程访问路由器 前提&#xff1a;主机能与路由器通信就行&#xff0c;如果不同网段就配路由协议&#xff0c;这里直接模拟直连通信 Cloud&#xff1a; R&#xff1a; <Huawei>sys [Huawei]sys R [R]int g0/0/0 [R-GigabitEthernet0/0/0] [R-GigabitEthernet0/0/0]i…

Vue50 todolist自定义事件版本

代码 MyFooter.vue <template><div class"todo-footer" v-show"total"><label><!-- <input type"checkbox" :checked"isAll" change"checkAll"/> --><input type"checkbox" v…