Gradio 4.37.1官方教程三:Chatbot

文章目录

    • 一、使用ChatInterface创建聊天机器人
      • 1.1 定义聊天函数
      • 1.2 流式聊天机器人(Streaming chatbots)
      • 1.3 自定义聊天机器人
      • 1.4 添加多模态功能
      • 1.5 通过`additional_inputs`添加额外组件
      • 1.6 直接添加 Gradio 组件
      • 1.7 通过 API 使用聊天机器人
        • 1.7.1 调用托管的聊天机器人
        • 1.7.2 使用Langchain构建流式聊天机器人
        • 1.7.3 使用openai 构建流式聊天机器人
        • 1.7.4 使用本地开源LLM进行构建
    • 二、 使用Blocks 创建自定义聊天机器人
      • 2.1 快速入门
      • 2.2 为聊天机器人添加流式响应
      • 2.3 添加点赞和点踩功能
      • 2.4 添加 Markdown、图像、音频或视频
    • 三、通过Gradio Apps创建 Discord 机器人(略)

传送门:

  • 《Gradio官方教程一:Gradio生态系统、主要组件及Interface class简介》
  • 《Gradio 4.37.1官方教程二:Blocks》
  • 《Gradio 4.37.1官方教程三:Chatbot》

一、使用ChatInterface创建聊天机器人

gradio(GitHub)、Chatbot官方教程、ChatInterface官方文档

  聊天机器人是大型语言模型的一种流行应用。使用 Gradio,可以轻松构建聊天机器人模型的演示。gr.ChatInterface()是一个高级抽象,通常只需一行代码即可快速创建聊天机器人 UI,还可以与几个流行的 API 和库(包括 langchain、openai 和 Hugging Face)中的实际语言模型结合使用。

1.1 定义聊天函数

   在使用 gr.ChatInterface() 时,首先需要定义聊天函数。你的聊天函数应接受两个参数:messagehistory(参数名称可以随意,但顺序必须如此)。

  • message:表示用户输入的字符串。
  • history:表示到目前为止的对话历史记录的列表。每个内嵌列表由两个字符串组成,表示一个对话对:[user input, bot response]

最终返回返回一个字符串响应(bot response)。下面是一个简单的示例:

import random
import gradio as gr

def alternatingly_agree(message, history):
    if len(history) % 2 == 0:
        return f"Yes, I do think that '{message}'"
    else:
        return "I don't think so"

gr.ChatInterface(alternatingly_agree).launch()

在这里插入图片描述

1.2 流式聊天机器人(Streaming chatbots)

  流式聊天机器人的响应是逐步生成并显示的,这样用户在等待完整回复的过程中,可以逐步看到部分回复(实时反馈),提高了互动的流畅度和用户体验。在聊天函数中,使用 yield 逐步生成响应,就能实现一个流式聊天机器人。

import time
import gradio as gr

def slow_echo(message, history):
    for i in range(len(message)):
        time.sleep(0.3)
        yield "You typed: " + message[: i+1]

gr.ChatInterface(slow_echo).launch()

  slow_echo:在每次迭代中,函数都会暂停 0.3 秒,然后将用户输入的信息一个个字母的生成出来。另外,在响应流式传输时,“Submit”按钮会变成“Stop”按钮,用来停止生成器函数。你可以使用 stop_btn 参数自定义“Stop”按钮的外观和行为。

1.3 自定义聊天机器人

和Gradio的Interface类一样,gr.ChatInterface类也包括许多相同的参数,可以用来自定义聊天机器人的外观和感觉,比如:

  • 使用titledescription参数在聊天机器人上方添加标题和描述。
  • 使用themecss参数添加主题或自定义CSS。
  • 使用placeholder参数用于设置聊天界面的“占位符”(用户未输入时的起始显示,用于提示用户输入),该参数接受Markdown或HTML。占位符会在聊天机器人中居中显示。
  • 使用examples添加示例,并设置cache_examples=True缓存示例(启动demo时自动运行示例,缓存结果)。
  • 更改或禁用聊天界面中的各个按钮:submit_btnretry_btnundo_btnclear_btn
  • 自定义ChatInterface中的gr.Chatbotgr.Textbox等组件
import gradio as gr

def yes_man(message, history):
    if message.endswith("?"):
        return "Yes"
    else:
        return "Ask me anything!"

gr.ChatInterface(
    yes_man,
    chatbot=gr.Chatbot(height=300,placeholder="<strong>Your Personal Yes-Man</strong><br>Ask Me Anything"),
    textbox=gr.Textbox(placeholder="Ask me a yes or no question", container=False, scale=7),
    title="Yes Man",
    description="Ask Yes Man any question",
    theme="soft",
    examples=["Hello", "Am I cool?", "Are tomatoes vegetables?"],
    cache_examples=True,
    retry_btn=None,
    undo_btn="Delete Previous",
    clear_btn="Clear",
).launch()

在这里插入图片描述

1.4 添加多模态功能

  你可以向聊天机器人添加多模态功能。例如,你可能希望用户能够轻松上传图片或文件,并向聊天机器人提问。在gr.ChatInterface中设置multimodal=True可以使聊天机器人“多模态化”。

  当multimodal=True时,函数fn的签名会稍微变化。你的函数的第一个参数应该接受一个包含提交文本和上传文件的字典,比如{"text": "user input", "file": ["file_path1", "file_path2", ...]}。同样,你提供的任何示例也应该是这种形式的字典。函数返回值不变,仍旧返回一个单一的字符串消息。

import gradio as gr
import time

def count_files(message, history):
    num_files = len(message["files"])
    return f"You uploaded {num_files} files"

demo = gr.ChatInterface(fn=count_files, examples=[{"text": "Hello", "files": []}], title="Echo Bot", multimodal=True)

demo.launch()

在这里插入图片描述

✍️ 提示:如果你想自定义多模态聊天机器人的文本框的UI/UX,你应该传递一个gr.MultimodalTextbox实例到ChatInterface的textbox参数,而不是gr.Textbox实例。

1.5 通过additional_inputs添加额外组件

  通过additional_inputs参数可以添加额外的输入组件,例如,添加一个系统提示的文本框,或设置聊天机器人响应中token数量的滑块。以下是更具体的说明:

  • additional_inputs 参数接受一个组件或组件列表,你可以直接传递组件实例,或使用它们的字符串快捷方式(比如用“textbox”代替gr.Textbox()
  • 如果传递的组件尚未渲染,会出现在聊天机器人(和任何示例)下面的折叠面板中(gr.Accordion(),可以打开/折叠)。可以使用additional_inputs_accordion_name参数设置此折叠面板的标签

  下面创建一个带有额外输入参数的聊天机器人界面,用户可以在折叠面板的系统提示语文本框中输入自定义提示语,使用滑块设置响应的最大token数量。聊天机器人根据用户输入的消息和系统提示语生成响应,并逐字符显示响应内容,模拟打字效果。

import gradio as gr
import time

def echo(message, history, system_prompt, tokens):
    response = f"System prompt: {system_prompt}\n Message: {message}."
    for i in range(min(len(response), int(tokens))):
        time.sleep(0.05)
        yield response[: i + 1]


demo = gr.ChatInterface(
    echo,chatbot=gr.Chatbot(height=150),
    additional_inputs=[
        gr.Textbox("You are helpful AI.", label="System Prompt"),
        gr.Slider(10, 100),
    ],
)

if __name__ == "__main__":
    demo.queue().launch()

在这里插入图片描述

  ChatInterface的input参数通常用于主要的用户输入。而代码中的system_prompttokens是辅助配置,把这些参数放在additional_inputs中,可以与用户输入明确区分,让UI界面更清晰。

  如果组件已经在父gr.Blocks()中被渲染,则不会在折叠面板中重新渲染。这提供了灵活性,可以决定输入组件的布局位置。例如,我们可以将gr.Textbox()放在聊天机器人UI的顶部,同时将滑块放在下面:

import gradio as gr
import time

def echo(message, history, system_prompt, tokens):
    response = f"System prompt: {system_prompt}\n Message: {message}."
    for i in range(min(len(response), int(tokens))):
        time.sleep(0.05)
        yield response[: i+1]

with gr.Blocks() as demo:
    system_prompt = gr.Textbox("You are helpful AI.", label="System Prompt")
    slider = gr.Slider(10, 100, render=False)  # 设置滑块组件不渲染

    gr.ChatInterface(
        echo, additional_inputs=[system_prompt, slider],
    )

demo.launch()

在这里插入图片描述

如果需要创建更自定义的内容,最好使用底层的gr.Blocks() API构建聊天机器人UI(见下一章节)

1.6 直接添加 Gradio 组件

  在Gradio的Chatbot组件中,可以使用许多核心的Gradio组件(如gr.Imagegr.Plotgr.Audiogr.HTML),使聊天机器人可以返回更丰富的多媒体内容。只需从你的函数中返回这些组件之一,就可以在gr.ChatInterface中使用它们。

  下面是一个示例,用户可以通过输入框请求播放特定艺术家的音乐。如果用户输入有效信息,聊天机器人将返回一个音频文件的链接并播放;如果输入无效,聊天机器人会提示用户提供艺术家名称。

import gradio as gr

def fake(message, history):
    if message.strip():
        return gr.Audio("https://github.com/gradio-app/gradio/raw/main/test/test_files/audio_sample.wav")
    else:
        return "Please provide the name of an artist"

gr.ChatInterface(
    fake, 
    textbox=gr.Textbox(placeholder="Which artist's music do you want to listen to?", scale=7),
    chatbot=gr.Chatbot(placeholder="Play music by any artist!"),
).launch()

  textbox组件用于指定用户消息输入框,而chatbot组件用于指定聊天机器人的显示区域,都是主要的聊天功能组件。

1.7 通过 API 使用聊天机器人

1.7.1 调用托管的聊天机器人

  即使你在Hugging Face Spaces或其他地方托管了你的Gradio聊天机器人,可以通过一个简单的API在/chat端点与其进行交互。端点只需要用户的消息(以及可能的额外输入,如果你在创建聊天机器人时使用了additional_inputs参数),API内部会自动跟踪到目前为止发送的所有消息,以便生成上下文相关的响应。要调用这个API端点,可以使用 Gradio Python Client 或Gradio JS client。

  Gradio Python client可以让我们非常容易地将任何Gradio应用当作API来使用。以托管在Hugging Face Spaces的abidlabs/whisper为例,这个应用可以从麦克风录制音频文件并进行转录。通过Gradio Python client,可以编程方式地使用这个Gradio应用来转录音频文件。

在这里插入图片描述

# pip install --upgrade gradio_client

from gradio_client import Client, file

# 创建客户端实例,指向托管的Gradio应用
client = Client("abidlabs/whisper")

# 使用Gradio应用的API来转录音频文件
client.predict(
    audio=file("audio_sample.wav")
)

>> "This is a test of the whisper speech recognition model."
1.7.2 使用Langchain构建流式聊天机器人
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage, HumanMessage
import openai
import gradio as gr

os.environ["OPENAI_API_KEY"] = "sk-..."  # Replace with your key

llm = ChatOpenAI(temperature=1.0, model='gpt-3.5-turbo-0613')

def predict(message, history):
    history_langchain_format = []
    for human, ai in history:
        history_langchain_format.append(HumanMessage(content=human))
        history_langchain_format.append(AIMessage(content=ai))
    history_langchain_format.append(HumanMessage(content=message))
    gpt_response = llm(history_langchain_format)
    return gpt_response.content

gr.ChatInterface(predict).launch()

这段代码的逻辑与之前的类似,只是在predict函数中,通过LangChain加载了OpenAI的大语言模型来生成用户响应。

1.7.3 使用openai 构建流式聊天机器人

我们也可以直接使用openai来进行构建:

from openai import OpenAI
import gradio as gr

api_key = "sk-..."  # Replace with your key
client = OpenAI(api_key=api_key)

def predict(message, history):
    history_openai_format = []
    for human, assistant in history:
        history_openai_format.append({"role": "user", "content": human })
        history_openai_format.append({"role": "assistant", "content":assistant})
    history_openai_format.append({"role": "user", "content": message})
  
    response = client.chat.completions.create(model='gpt-3.5-turbo',
    messages= history_openai_format,
    temperature=1.0,
    stream=True)

    partial_message = ""
    for chunk in response:
        if chunk.choices[0].delta.content is not None:
              partial_message = partial_message + chunk.choices[0].delta.content
              yield partial_message

gr.ChatInterface(predict).launch()
1.7.4 使用本地开源LLM进行构建

  在更多情况下,你可能希望在本地运行聊天机器人。下面是一个使用Hugging Face上的
RedePajama model的示例,这需要GPU。

import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer
from threading import Thread

tokenizer = AutoTokenizer.from_pretrained("togethercomputer/RedPajama-INCITE-Chat-3B-v1")
model = AutoModelForCausalLM.from_pretrained("togethercomputer/RedPajama-INCITE-Chat-3B-v1", torch_dtype=torch.float16)
model = model.to('cuda:0')

class StopOnTokens(StoppingCriteria):
    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:
        stop_ids = [29, 0]
        for stop_id in stop_ids:
            if input_ids[0][-1] == stop_id:
                return True
        return False

def predict(message, history):  							# 接受用户的输入消息和聊天历史记录
    history_transformer_format = history + [[message, ""]]  # 将聊天历史转换为 Transformer 模型所需的格式
    stop = StopOnTokens()

    messages = "".join(["".join(["\n<human>:"+item[0], "\n<bot>:"+item[1]])
                for item in history_transformer_format])	# 将新消息附加到历史记录中

    model_inputs = tokenizer([messages], return_tensors="pt").to("cuda")
    # 创建 TextIteratorStreamer 对象用于逐步生成文本
    streamer = TextIteratorStreamer(tokenizer, timeout=10., skip_prompt=True, skip_special_tokens=True)
    generate_kwargs = dict(
        model_inputs,
        streamer=streamer,
        max_new_tokens=1024,
        do_sample=True,
        top_p=0.95,
        top_k=1000,
        temperature=1.0,
        num_beams=1,
        stopping_criteria=StoppingCriteriaList([stop])
        )
    # 通过多线程方式调用 model.generate 生成文本,并将结果逐步传给 streamer
    t = Thread(target=model.generate, kwargs=generate_kwargs)
    t.start()
	
	# 生成过程中逐步产出部分消息 (partial_message),实现流式输出
    partial_message = ""
    for new_token in streamer:
        if new_token != '<':
            partial_message += new_token
            yield partial_message

gr.ChatInterface(predict).launch()

  代码中, StopOnTokens:继承自 StoppingCriteria,用于实现自定义的停止条件。在生成文本时,如果生成的 token 序列中包含 ID 为 29 或 0 的标记,生成过程将终止( <pad> 或结束标记<eos>)。__call__方法会在生成每个新 token 时被调用,以检查是否应该停止生成。

二、 使用Blocks 创建自定义聊天机器人

2.1 快速入门

  Gradio 提供了两种不同的方式来创建聊天机器人:Gradio Blocksgr.ChatInterface。Gradio Blocks是更底层的API,允许开发者完全自定义聊天机器人的界面和行为。开发者可以自定义各种组件的排列、样式和交互方式,可以自定义事件处理逻辑,适合需要复杂布局或特殊功能的应用。

  下面我们先使用 gradio.Blocks 类来构建一个简单的聊天机器人示例,这个机器人会随机响应"How are you?", "I love you", "I'm very hungry"

import gradio as gr
import random
import time

with gr.Blocks() as demo:
    chatbot = gr.Chatbot()
    msg = gr.Textbox()
    clear = gr.ClearButton([msg, chatbot])

    def respond(message, chat_history):
    	# chat_history是一个包含(message, bot_message)二元组的列表,表示历史对话记录
        bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
        chat_history.append((message, bot_message))
        time.sleep(2)									# 模拟机器人响应的延迟
        return "", chat_history							# 返回一个二元组,第一个元素是一个空字符串,用于清空输入框

    msg.submit(respond, [msg, chatbot], [msg, chatbot])

demo.launch()

这里有三个 Gradio 组件:

  1. Chatbot:存储了整个对话历史,作为用户和机器人响应对的列表。
  2. Textbox:用户输入框,按回车或提交按钮以触发聊天机器人响应。
  3. ClearButton:清除 TextboxChatbot

   respond函数接收整个聊天机器人的历史记录,并追加一个随机消息。在等待 2 秒后返回更新后的聊天历史。函数在返回时也会清除文本框。在实际应用中,你可以用更复杂的函数替换 respond,比如调用预训练模型或 API 来生成响应。

2.2 为聊天机器人添加流式响应

  我们可以通过几种方式改进上面的聊天机器人用户体验。首先,我们可以流式传输响应,这样用户可以看到实时反馈。其次,我们可以在生成机器人的响应时也显示用户的消息。

import gradio as gr
import random
import time

with gr.Blocks() as demo:
    chatbot = gr.Chatbot()
    msg = gr.Textbox()
    clear = gr.Button("Clear")

    def user(user_message, history):
        return "", history + [[user_message, None]]

    def bot(history):
        bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
        history[-1][1] = ""
        for character in bot_message:
            history[-1][1] += character
            time.sleep(0.05)
            yield history

    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        bot, chatbot, chatbot
    )
    clear.click(lambda: None, None, chatbot, queue=False)
    
demo.queue()
demo.launch()
  • user函数 :处理用户输入,将用户消息(user_message, None)添加到聊天历史中(其中 None 表示机器人尚未响应),同时清空输入框。此方法还使输入字段非交互式,以便用户在聊天机器人响应时无法发送另一条消息。

  • bot 函数:生成机器人的回复,并逐个字母地显示出来,每次暂停0.05s,模拟打字效果。这里没有创建新消息,只是用机器人的响应替换之前创建的 None 消息。Gradio 会自动将任何包含 yield 关键字的函数转换为流式输出界面。

.then()方法:主要功能是链式连接多个事件,实现复杂的交互逻辑。

  • 顺序执行:使得一个事件在另一个事件完成后执行
  • 数据传递:一个事件的输出可以作为下一个事件的输入,使得数据流在整个链条中保持连贯。
  • 异步处理:可以处理异步操作,例如等待某个操作完成后再执行下一个操作。这对于处理API调用或复杂计算非常有用。
  • 提高可读性和维护性:通过链式调用,使代码更具可读性和结构化,方便理解和维护。

   .then()方法主要参数为(fn,input,output,queue),其中默认queue=True。通过设置queue=False可以禁用队列,以便立即执行。本例中,当用户提交消息时,通过.then()方法将三个事件链式连接:

  • msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False):第一个事件,调用user函数,处理用户输入,更新聊天记录并清空输入框。
  • .then(bot, chatbot, chatbot):第二个事件,调用bot函数,更新聊天历史,用机器人的回复替换之前创建的None消息,并逐字显示回复内容。
  • clear.click(lambda: None, None, chatbot, queue=False):第三个事件,清空聊天窗口。输入框重新变为可交互状态,允许用户继续输入消息。
    • 触发回调函数lambda: None
    • 回调函数返回None,不进行任何操作。
    • 输出目标是chatbot,因此Gradio清除chatbot组件的内容
    • queue=False确保这个操作立即执行,不会被排队。

最后,我们通过运行 demo.queue() 启用队列功能,以便实现中间流式输出的功能。

2.3 添加点赞和点踩功能

可以为聊天机器人添加点赞和点踩功能,以便用户可以对每条机器人的回复进行评价。通过附加.like()事件,就可以在每条机器人消息旁边自动显示点赞和点踩图标。

  .like()方法需要传入一个函数,当用户点击图标时调用该函数。该函数应有一个gr.LikeData类型的参数,Gradio会自动传入包含被点赞或点踩消息信息的对象。下面是一个简单的示例:

import gradio as gr

def greet(history, input):
    return history + [(input, "Hello, " + input)]

def vote(data: gr.LikeData):
    if data.liked:
        print("You upvoted this response: " + data.value)
    else:
        print("You downvoted this response: " + data.value)
    

with gr.Blocks() as demo:
    chatbot = gr.Chatbot()
    textbox = gr.Textbox()
    textbox.submit(greet, [chatbot, textbox], [chatbot])    # 调用greet函数更新聊天记录,显示在聊天机器人组件中
    chatbot.like(vote, None, None)  						# 绑定vote函数。当用户点击点赞或点踩图标时,调用vote函数处理投票数据。
    
demo.launch()
  • vote:投票函数,接收一个gr.LikeData类型的参数data,包含关于被点赞或点踩消息的信息。根据data.liked的值,打印用户是否点赞或点踩了该消息。
  • chatbot.like(vote, None, None)
    • inputs: None 表示回调函数 vote 不需要从其他组件接收输入。Gradio 会自动传递 gr.LikeData 对象给 vote 函数。
    • outputs: None 表示回调函数 vote 不需要更新任何组件的输出。因为点赞或点踩操作只是记录用户的反馈,没有必要更新界面上的其他组件。

2.4 添加 Markdown、图像、音频或视频

  gr.Chatbot组件支持一部分Markdown格式,包括加粗、斜体和代码。例如,可以用加粗的文本回复用户消息,如“That’s cool!”。

def bot(history):
    response = "**That's cool!**"
    history[-1][1] = response
    return history

  gr.Chatbot可以处理媒体文件,如图片、音频和视频。使用MultimodalTextbox组件,可以方便地上传各种类型的媒体文件到聊天机器人。传递媒体文件的格式是一个包含两个字符串的元组,如(filepath, alt_text)。后者是可选的,所以也可以只传一个包含单个元素的元组(filepath,)

def add_message(history, message):
    for x in message["files"]:
        history.append(((x["path"],), None))  
    if message["text"] is not None:
        history.append((message["text"], None))
    return history, gr.MultimodalTextbox(value=None, interactive=False, file_types=["image"])

  下面是完整的示例代码,我们使用MultimodalTextbox组件创建了一个带有多模态文本框的多模态聊天机器人。

import gradio as gr
import os
import plotly.express as px

# Chatbot demo with multimodal input (text, markdown, LaTeX, code blocks, image, audio, & video). Plus shows support for streaming text.

def random_plot():
    df = px.data.iris()
    fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species",
                    size='petal_length', hover_data=['petal_width'])
    return fig

def print_like_dislike(x: gr.LikeData):
    print(x.index, x.value, x.liked)

def add_message(history, message):
    for x in message["files"]:
        history.append(((x,), None))
    if message["text"] is not None:
        history.append((message["text"], None))
    return history, gr.MultimodalTextbox(value=None, interactive=False)

def bot(history):
    history[-1][1] = "Cool!"
    return history

fig = random_plot()

with gr.Blocks(fill_height=True) as demo:
    chatbot = gr.Chatbot(
        elem_id="chatbot",
        bubble_full_width=False,
        scale=1,
    )

    chat_input = gr.MultimodalTextbox(interactive=True,
                                      file_count="multiple",
                                      placeholder="Enter message or upload file...", show_label=False)

    chat_msg = chat_input.submit(add_message, [chatbot, chat_input], [chatbot, chat_input])
    bot_msg = chat_msg.then(bot, chatbot, chatbot, api_name="bot_response")
    bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])

    chatbot.like(print_like_dislike, None, None)

demo.queue()
demo.launch()

三、通过Gradio Apps创建 Discord 机器人(略)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

苹果可能与谷歌合作推AI订阅服务;全国首个司法审判大模型在深圳上线

&#x1f989; AI新闻 &#x1f680; 苹果可能与谷歌合作推AI订阅服务 摘要&#xff1a;苹果宣布将与OpenAI合作推出Apple Intelligence&#xff0c;并有望在今年秋季与谷歌达成合作&#xff0c;接入Gemini。Meta的Llama因质量不佳被拒。苹果计划推出订阅模式的智能功能服务&…

C语言--vs使用调试技巧

1.什么是bug? 1.产品说明书中规定要做的事情&#xff0c;而软件没有实现。 2.产品说明书中规定不要做的事情&#xff0c;而软件确实现了。 3.产品说明书中没有提到过的事情&#xff0c;而软件确实现了。 4.产品说明书中没有提到但是必须要做的事情&#xff0c;软件确没有实…

vue 组件el-tree添加结构指示线条

效果展示: 注意&#xff1a;组件中需要添加:indent"0" 进行子级缩进处理&#xff0c;否则会出现子级缩进逐级递增 :expand-on-click-node"false" 设置点击箭头图标才会展开或者收起 代码&#xff1a; <el-tree class"tree filter-tree" :da…

数据恢复篇:如何在电脑上恢复已删除和丢失的音乐文件

尽管流媒体网络非常流行&#xff0c;但许多人仍然选择将音乐下载并保存在 PC 本地。这会使文件面临丢失或意外删除的风险。 幸运的是&#xff0c;您可以使用数据恢复软件恢复已删除的音乐和其他文件类型。这篇文章讨论了这些解决方案以及如何使用奇客数据恢复检索丢失的音乐文…

【SpringCloud】Eureka源码解析 下

eurkea是一个服务发现与注册组件&#xff0c;它包含客户端和服务端&#xff0c;服务端负责管理服务的注册信息&#xff0c;客户端用于简化与服务端的交互。上一章分析了eureka的服务注册&#xff0c;这一章来分析eureka的心跳机制 参考源码&#xff1a;<spring-cloud.versi…

科普文:八大排序算法(JAVA实现)+ 自制动画 (袁厨的算法小屋)

我将我仓库里的排序算法给大家汇总整理了一下&#xff0c;写的非常非常细&#xff0c;还对每个算法制作了动画&#xff0c;一定能够对大家有所帮助&#xff0c;欢迎大家阅读。另外我也对 leetcode 上面可以用排序算法秒杀的算法题进行了总结&#xff0c;会在后面的文章中进行发…

OpenCV 调用自定义训练的 YOLO-V8 Onnx 模型

一、YOLO-V8 转 Onnx 在本专栏的前面几篇文章中&#xff0c;我们使用 ultralytics 公司开源发布的 YOLO-V8 模型&#xff0c;分别 Fine-Tuning 实验了 目标检测、关键点检测、分类 任务&#xff0c;实验后发现效果都非常的不错&#xff0c;但是前面的演示都是基于 ultralytics…

计算机组成原理——寄存器

文章目录 1. 寄存器 2. 带寄存器的加法器 3. 时钟信号与计算速度 1. 寄存器 上一篇D触发器可以在时钟上沿存储1位数据。如果想存储多个位&#xff08;bit&#xff09;的数据&#xff0c;就需要用多个D触发器并联实现&#xff0c;这种电路称之为寄存器。 寄存器是计算机中央…

MATLAB使用系统辨识工具箱建立PID水温的传递函数系数

概述 利用PID控制水温&#xff0c;由于实际在工程项目中&#xff0c;手动调节PID参数比较耗费时间&#xff0c;所以可以先利用MATLAB中的Simulink软件建立模型&#xff0c;先在仿真软件上调节大概的PID参数&#xff0c;再利用此PID参数为基础在实际的工程项目中手动调节PID参数…

spring boot(学习笔记第十一课)

spring boot(学习笔记第十一课) Session共享&#xff0c;JPA实现自动RESTful 学习内容&#xff1a; Session共享JPA实现自动RESTful 1. Session共享 Session共享面临问题 spring boot默认将session保存在web server的内存里面&#xff0c;会产生什么问题呢。 如上图所示&#…

《昇思25天学习打卡营第15天 | 昇思MindSpore基于MindSpore的红酒分类实验》

15天 本节学了通过MindSpore的完成红酒分类。 1.K近邻算法&#xff08;K-Nearest-Neighbor, KNN&#xff09;是一种用于分类和回归的非参数统计方法&#xff0c;是机器学习最基础的算法之一。 1.1分类问题 1.2回归问题 1.3距离的定义 2.数据处理 2.1 数据准备 2.2 数据读取与处…

isupper()方法——判断字符串是否全由大写字母组成

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 isupper()方法用于判断字符串中所有的字母是否都是大写。isupper()方法的语法格式如下&#xff1a; str.isupper() 如果字符串中包含至少…

OpenGL3.3_C++_Windows(24)

渲染平行光阴影 阴影作用&#xff1a; 有了阴影的渲染&#xff0c;更容易地区分出物体之间的位置关系&#xff0c;如何判断片段是否在阴影中&#xff1f; 普通思路&#xff1a; 以光的位置为视角进行渲染&#xff0c;我们绘制一条从光源出发的射线&#xff0c;测试更新射线经过…

数据结构-分析期末选择题考点(广义表)

莫道桑榆晚 为霞尚满天 数据结构-图期末选择题 数据结构-串、数组选择题 数据结构-排序选择题 数据结构-线性表、栈、队列、二叉树合集 契子✨ 广义表&#xff1a; <1>考点一&#xff1a;基本概念 广义表的基础概念 &#xff08;1&#xff09;什么是广义表 广义表&#…

小韩厂涨乌托邦公式源码

小韩厂涨&乌托邦&公式源码已经测试通过,可以发布云平台自行编辑 DRAWGBK(C>0, RGB(50,60,250),RGB(17,21,89),0,11,0); H1:=MAX(DYNAINFO(3),DYNAINFO(5)); L1:=MIN(DYNAINFO(3),DYNAINFO(6)); P1:=H1-L1; 阻力:=L1+P1*7/8,COLORGREEN; 支撑:=L1+P1*0.5/8,COLORRED;…

超详细的 C++中的封装继承和多态的知识总结<1.封装>

引言 小伙伴们都知道C面向对象难&#xff0c;可是大家都知道&#xff0c;这个才是C和C的真正区别的地方&#xff0c;也是C深受所有大厂喜爱的原因&#xff0c;它的原理更接近底层&#xff0c;它的逻辑更好&#xff0c;但是学习难度高&#xff0c;大家一定要坚持下来呀&#xff…

如何做好一个企业家IP:塑造独特的个人品牌

在当今数字化时代&#xff0c;个人品牌的力量愈发凸显&#xff0c;对于企业家而言&#xff0c;一个强大的IP&#xff08;Intellectual Property&#xff0c;即知识产权或个人品牌&#xff09;不仅有助于提升个人影响力&#xff0c;还能为企业的发展注入强大动力。那么&#xff…

Flutter【组件】点击类型表单项

简介 flutter 点击表单项组件&#xff0c;适合用户输入表单的场景。 点击表单项组件是一个用户界面元素&#xff0c;通常用于表单或设置界面中&#xff0c;以便用户可以点击它们来选择或更改某些设置或输入内容。这类组件通常由一个标签和一个可点击区域组成&#xff0c;并且…

【后端面试题】【中间件】【NoSQL】ElasticSearch索引机制和高性能的面试思路

Elasticsearch的索引机制 Elasticsearch使用的是倒排索引&#xff0c;所谓的倒排索引是相对于正排索引而言的。 在一般的文件系统中&#xff0c;索引是文档映射到关键字&#xff0c;而倒排索引则相反&#xff0c;是从关键字映射到文档。 如果没有倒排索引的话&#xff0c;想找…