1. 聊天机器人
  2. 使用 Blocks 创建自定义聊天机器人

如何使用 Gradio Blocks 创建自定义聊天机器人

简介

重要提示:如果您刚开始使用 Gradio,我们建议您使用 gr.ChatInterface 来创建聊天机器人——这是一个高级抽象,可以快速创建漂亮的聊天机器人应用程序,通常只需一行代码。 在此处了解更多信息

本教程将展示如何使用 Gradio 的低级 Blocks API 从头开始制作聊天机器人 UI。这将使您能够完全控制您的聊天机器人 UI。您将首先创建一个简单的聊天机器人来显示文本,然后是第二个流式传输文本响应的聊天机器人,最后是一个可以处理媒体文件的聊天机器人。我们创建的聊天机器人界面将如下图所示

先决条件:我们将使用 gradio.Blocks 类来构建我们的聊天机器人演示。如果您还不熟悉它,可以先阅读 Blocks 指南。另外,请确保您使用的是 Gradio 的最新版本pip install --upgrade gradio

一个简单的聊天机器人演示

让我们开始重新创建上面的简单演示。正如您可能已经注意到的,我们的机器人只是随机地回应任何输入“你好吗?”,“今天是美好的一天”或“我非常饿”。这是使用 Gradio 创建此功能的代码

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):
        bot_message = random.choice(["How are you?", "Today is a great day", "I'm very hungry"])
        chat_history.append({"role": "user", "content": message})
        chat_history.append({"role": "assistant", "content": bot_message})
        time.sleep(2)
        return "", chat_history

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

demo.launch()

这里有三个 Gradio 组件

  • 一个 Chatbot,其值以用户和机器人之间响应对的列表形式存储整个对话历史。
  • 一个 Textbox,用户可以在其中输入消息,然后按回车/提交以触发聊天机器人响应
  • 一个 ClearButton 按钮,用于清除文本框和整个聊天机器人历史记录

我们有一个单独的函数 respond(),它接收聊天机器人的整个历史记录,追加一条随机消息,等待 1 秒钟,然后返回更新后的聊天历史记录。respond() 函数在返回时也会清除文本框。

当然,在实践中,您会用自己更复杂的函数替换 respond(),该函数可能会调用预训练模型或 API 来生成响应。

为了在 IDE 中获得更好的类型提示和自动完成功能,您可以使用 gr.ChatMessage 数据类

from gradio import ChatMessage

def chat_function(message, history):
    history.append(ChatMessage(role="user", content=message))
    history.append(ChatMessage(role="assistant", content="Hello, how can I help you?"))
    return history

为您的聊天机器人添加流式传输

我们可以通过几种方式来改善上面聊天机器人的用户体验。首先,我们可以流式传输响应,这样用户就不必等待很长时间来生成消息。其次,我们可以让用户消息立即出现在聊天历史中,而聊天机器人的响应正在生成中。这是实现此目的的代码

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: list):
        return "", history + [{"role": "user", "content": user_message}]

    def bot(history: list):
        bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
        history.append({"role": "assistant", "content": ""})
        for character in bot_message:
            history[-1]['content'] += 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.launch()

您会注意到,当用户提交消息时,我们现在使用 .then() 链接两个事件

  1. 第一个方法 user() 使用用户消息更新聊天机器人并清除输入字段。因为我们希望这立即发生,所以我们设置 queue=False,如果启用了队列,这将跳过任何队列。聊天机器人的历史记录中会追加 {"role": "user", "content": user_message}

  2. 第二个方法 bot() 使用机器人的响应更新聊天机器人历史记录。最后,我们逐个字符地构建消息并 yield 正在构建的中间输出。Gradio 会自动将任何带有 yield 关键字的函数转换为流式输出接口

当然,在实践中,您会用自己更复杂的函数替换 bot(),该函数可能会调用预训练模型或 API 来生成响应。

添加 Markdown、图片、音频或视频

gr.Chatbot 组件支持一部分 markdown,包括粗体、斜体和代码。例如,我们可以编写一个函数,以粗体 That's cool! 响应用户的消息,如下所示

def bot(history):
    response = {"role": "assistant", "content": "**That's cool!**"}
    history.append(response)
    return history

此外,它还可以处理媒体文件,例如图片、音频和视频。您可以使用 MultimodalTextbox 组件轻松地将所有类型的媒体文件上传到您的聊天机器人。您可以通过传入 sources 参数进一步自定义 MultimodalTextbox,该参数是一个要启用的源列表。要传入媒体文件,我们必须传入一个字典,其中包含指向本地文件的 path 键和 alt_text 键。alt_text 是可选的,因此您也可以只传入一个包含单个元素 {"path": "filepath"} 的元组,如下所示

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

将所有这些放在一起,我们可以创建一个多模态聊天机器人,其中包含一个多模态文本框,供用户提交文本和媒体文件。其余代码与之前基本相同

import gradio as gr
import time

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


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


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


def bot(history: list):
    response = "**That's cool!**"
    history.append({"role": "assistant", "content": ""})
    for character in response:
        history[-1]["content"] += character
        time.sleep(0.05)
        yield history


with gr.Blocks() as demo:
    chatbot = gr.Chatbot(elem_id="chatbot", like_user_message=True)

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

    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.launch()

大功告成!构建聊天机器人模型的界面所需的全部代码就是这些。最后,我们将通过一些指向在 Spaces 上运行的聊天机器人的链接来结束我们的指南,以便您可以了解还有哪些可能性

  • project-baize/Baize-7B:一个风格化的聊天机器人,允许您停止生成以及重新生成响应。
  • MAGAer13/mPLUG-Owl:一个多模态聊天机器人,允许您对响应进行点赞和点踩。
gradio