Creating A Chatbot Fast
简介
聊天机器人是大型语言模型的一个流行应用。使用 gradio
,您可以轻松构建您的聊天机器人模型的演示,并与您的用户分享,或者使用直观的聊天机器人用户界面自己尝试。
本教程使用 gr.ChatInterface()
,这是一个高级抽象,允许您快速创建聊天机器人界面,通常只需一行代码。我们创建的聊天机器人界面将如下所示:
我们将从一些简单的例子开始,然后展示如何结合几个流行的 API 和库中的真实语言模型使用 gr.ChatInterface()
,包括 langchain
、 openai
和 Hugging Face。
先决条件:请确保您使用的是 Gradio 的最新版本:
$ pip install --upgrade gradio
定义聊天功能
在使用 gr.ChatInterface()
时,您应该首先定义您的聊天功能。您的聊天功能应该有两个参数: message
然后是 history
(参数可以任意命名,但必须按此顺序)。
message
:代表用户输入的str
。history
:一个list
,代表到那时为止的list
。每个内部列表由两个str
组成,代表一对:[user input, bot response]
。
您的函数应该返回一个字符串响应,这是机器人对特定用户输入 message
的响应。您的函数可以考虑消息的 history
,以及当前消息。
让我们看几个例子。
示例:一个回答是或否的聊天机器人
让我们编写一个聊天功能,它可以随机回应 Yes
或 No
。
这是我们的聊天功能:
import random# 定义一个函数用于生成随机响应
def random_response(message, history):# 从列表["Yes", "No"]中随机选择一个元素并返回return random.choice(["Yes", "No"])
现在,我们可以将其插入 gr.ChatInterface()
并调用 .launch()
方法来创建网络界面:
import gradio as gr# 定义一个随机响应函数,这个函数从"Yes"和"No"中随机选择一个作为响应
def random_response(message, history):import random # 导入random模块用于生成随机数return random.choice(["Yes", "No"]) # 随机选取“Yes”或“No”作为响应# 使用Gradio的ChatInterface模块创建一个聊天界面,这个界面会使用上面定义的random_response函数来响应用户的输入
gr.ChatInterface(random_response).launch() # 启动聊天界面
另一个例子是使用用户的输入和历史记录
当然,之前的例子非常简单,它甚至没有考虑用户输入或之前的历史记录!这里有另一个简单的例子,展示了如何结合用户的输入以及历史记录。
import random # 导入random模块
import gradio as gr # 导入gradio库,并简称为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" # 不同意用户的观点# 使用Gradio的ChatInterface模块创建一个聊天界面,使用上面定义的alternatingly_agree函数来响应用户的输入
gr.ChatInterface(alternatingly_agree).launch() # 启动聊天界面
流媒体聊天机器人
在您的聊天功能中,您可以使用 yield
生成一系列部分响应,每个响应都会替换前一个。这样,您最终将得到一个流式聊天机器人。就是这么简单!
import time # 导入time模块,用于实现等待(延时)
import gradio as gr # 导入gradio库,并简称为gr# 定义一个慢速回显函数,模拟逐字打印的效果
def slow_echo(message, history):# 遍历消息中的每一个字符for i in range(len(message)):time.sleep(0.3) # 每输出一个字符后暂停0.3秒yield "You typed: " + message[: i+1] # 逐渐展示已经输入的消息内容# 使用Gradio的ChatInterface模块创建一个聊天界面,使用上面定义的slow_echo函数来响应用户的输入
gr.ChatInterface(slow_echo).launch() # 启动聊天界面
提示:当响应正在流式传输时,“提交”按钮会变成“停止”按钮,可以用来停止生成器函数。您可以使用`stop_btn`参数自定义“停止”按钮的外观和行为。
定制您的聊天机器人
如果您熟悉 Gradio 的 Interface
类,那么 gr.ChatInterface
包含许多相同的参数,您可以使用这些参数来自定义您的聊天机器人的外观和感觉。例如,您可以:
在您的聊天机器人上方添加标题和描述,使用
title
和description
参数。使用
theme
和css
参数分别添加主题或自定义 CSS。添加
examples
并且甚至启用cache_examples
,这使得用户更容易尝试。您可以更改聊天机器人界面中出现的每个按钮的文本或禁用它们:
submit_btn
,retry_btn
,undo_btn
,clear_btn
。
如果您想自定义组成 ChatInterface
的 gr.Chatbot
或 gr.Textbox
,您也可以传入您自己的聊天机器人或文本框。以下是我们如何使用这些参数的示例:
import gradio as gr # 导入gradio库# 定义一个总是回答"Yes"或提示用户提问的函数
def yes_man(message, history):# 如果消息以问号结尾,则回答"Yes"if message.endswith("?"):return "Yes"# 否则,提示用户提出问题else:return "Ask me anything!"# 使用Gradio的ChatInterface模块创建一个聊天界面
gr.ChatInterface(yes_man, # 指定yes_man函数为回答逻辑chatbot=gr.Chatbot(height=300), # 创建一个聊天机器人并设置其容器高度为300textbox=gr.Textbox(placeholder="Ask me a yes or no question", container=False, scale=7), # 创建文本输入框并设置提示文本、不使用容器模式,并放大7倍title="Yes Man", # 设置聊天界面标题description="Ask Yes Man any question", # 设置聊天界面描述theme="soft", # 设置界面主题为softexamples=["Hello", "Am I cool?", "Are tomatoes vegetables?"], # 提供示例问题cache_examples=True, # 启用缓存示例功能retry_btn=None, # 不显示重试按钮undo_btn="Delete Previous", # 设置撤销按钮文本为"Delete Previous"clear_btn="Clear", # 设置清除按钮文本为"Clear"
).launch() # 启动聊天界面
特别是,如果您想为聊天界面添加一个“占位符”,该占位符会在用户开始聊天之前显示,您可以使用 gr.Chatbot
的 placeholder
参数来实现,它接受 Markdown 或 HTML。
gr.ChatInterface(yes_man,chatbot=gr.Chatbot(placeholder="<strong>Your Personal Yes-Man</strong><br>Ask Me Anything"),
...
占位符在聊天机器人中垂直和水平居中显示。
将多模态功能添加到您的聊天机器人
您可能希望为您的聊天机器人添加多模态功能。例如,您可能希望用户能够轻松地上传图片或文件到您的聊天机器人并对其提问。您可以通过向 gr.ChatInterface
类传递一个参数( multimodal=True
)来使您的聊天机器人“多模态”。
import gradio as gr # 导入gradio库,用于创建交互式界面
import time # 导入time模块,虽然在这个例子中未直接使用,但可用于其他功能如延迟# 定义一个函数,用于计算用户上传的文件数量
def count_files(message, history):num_files = len(message["files"]) # 计算上传文件的数量return f"You uploaded {num_files} files" # 返回一个字符串,包含上传的文件数量# 创建一个Gradio聊天界面
demo = gr.ChatInterface(fn=count_files, # 指定处理消息的函数是count_filesexamples=[{"text": "Hello", "files": []}], # 提供一个示例输入,没有附带文件title="Echo Bot", # 设置界面的标题为"Echo Bot"multimodal=True # 开启多模态支持,允许文本和文件同时作为输入
)# 启动Gradio界面
demo.launch()
当 multimodal=True
时, fn
的签名会略有变化。你的函数的第一个参数应该接受一个由提交的文本和上传的文件组成的字典,看起来像这样: {"text": "user input", "file": ["file_path1", "file_path2", ...]}
。同样,你提供的任何示例也应该是这种形式的字典。你的函数仍然应该返回一个单一的 str
消息。
小贴士:如果您想自定义多模态聊天机器人的文本框的 UI/UX,您应该将 gr.MultimodalTextbox
的实例传递给 ChatInterface
的 textbox
参数,而不是 gr.Textbox
的实例。
附加输入
您可能希望为您的聊天机器人添加额外的参数,并通过聊天机器人用户界面将它们暴露给用户。例如,假设您想添加一个系统提示的文本框,或者添加一个设置聊天机器人回应中的令牌数量的滑块。 ChatInterface
类支持一个 additional_inputs
参数,可用于添加额外的输入组件。
additional_inputs
参数接受一个组件或一组组件。您可以直接传递组件实例,或使用它们的字符串快捷方式(例如 "textbox"
而不是 gr.Textbox()
)。如果您传入组件实例,并且它们尚未被渲染,那么这些组件将出现在聊天机器人(和任何示例)下方的 gr.Accordion()
中。您可以使用 additional_inputs_accordion_name
参数设置此手风琴的标签。
这是一个完整的例子:
import gradio as gr # 导入gradio库,用于创建交云式界面
import time # 导入time模块,用于在循环中添加延迟效果# 定义一个echo函数,模拟逐字输出消息的效果
def echo(message, history, system_prompt, tokens):# 格式化字符串,包含系统提示和用户消息response = f"System prompt: {system_prompt}\n Message: {message}."# 通过最小值函数确定输出字符的最大长度,避免超出用户设置的token数for i in range(min(len(response), int(tokens))):time.sleep(0.05) # 在每个字符输出之间添加短暂延迟,增加逐字显示效果yield response[: i + 1] # 逐步输出字符串,达到“打字机”效果# 使用Gradio的ChatInterface构建一个支持多输入的聊天界面
demo = gr.ChatInterface(echo, # 将echo函数设为回调函数# 通过additional_inputs参数添加额外的输入控件additional_inputs=[gr.Textbox("You are helpful AI.", label="System Prompt"), # 添加一个文本输入框,用于输入系统提示gr.Slider(10, 100), # 添加一个滑动条,范围从10到100,用于控制输出的最大字符数(tokens)],
)if __name__ == "__main__":demo.queue().launch() # 在主程序中启动Gradio界面,在队列模式下运行
如果您传递到additional_inputs中的组件已经在父级gr.Blocks()中渲染过了,那么它们将不会在手风琴accordion中重新渲染。这在决定如何布局输入组件方面提供了灵活性。在下面的例子中,我们将gr.Textbox()放置在聊天机器人界面的顶部,同时将滑块保持在下方。
import gradio as gr # 导入gradio库,用于创建交互式Web应用
import time # 导入time模块,用于实现延迟效果# 定义一个函数,用来逐字输出响应消息
def echo(message, history, system_prompt, tokens):# 格式化响应消息,包含系统提示和用户消息response = f"System prompt: {system_prompt}\n Message: {message}."# 根据response的长度和tokens确定循环的次数,逐个字符输出for i in range(min(len(response), int(tokens))):time.sleep(0.05) # 每输出一个字符后暂停0.05秒,模拟打字效果yield response[:i+1] # 逐渐输出,每次迭代输出一个字符多于上一次# 使用gr.Blocks创建一个可视化布局
with gr.Blocks() as demo:# 创建一个文本输入框,用于输入系统提示system_prompt = gr.Textbox("You are helpful AI.", label="System Prompt")# 创建一个滑动条,用于选择tokens的数量,但不直接在界面中渲染显示slider = gr.Slider(10, 100, render=False)# 创建一个聊天界面,将echo函数作为处理函数,并通过additional_inputs添加上面创建的输入控件gr.ChatInterface(echo, additional_inputs=[system_prompt, slider])# 启动Gradio应用
demo.launch()
如果您需要创建更加定制化的内容,那么最好使用低级别的 gr.Blocks() API 来构建聊天机器人的用户界面。我们在这里有一个专门的指南。https://www.gradio.app/guides/creating-a-custom-chatbot-with-blocks
使用您的聊天机器人通过 API
一旦您构建了 Gradio 聊天机器人并将其托管在 Hugging Face Spaces 或其他地方,那么您可以在 /chat
端点使用简单的 API 查询它。该端点只期望用户的消息(如果您使用 additional_inputs
参数设置了任何附加输入,也可能包括这些输入),并将返回响应,同时内部跟踪到目前为止发送的消息。
要使用端点,您应该使用 Gradio Python 客户端或 Gradio JS 客户端。
一个 langchain
例子
现在,让我们实际使用 gr.ChatInterface
与一些真正的大型语言模型。我们将开始使用 langchain
在 openai
之上构建一个通用的流式聊天机器人应用程序,只需 19 行代码。对于这个例子,你将需要一个 OpenAI 密钥(继续阅读免费的开源等价物!)
from langchain.chat_models import ChatOpenAI # 从langchain库中导入ChatOpenAI聊天模型
from langchain.schema import AIMessage, HumanMessage # 从langchain库中导入消息模式
import openai # 导入openai库,用于访问OpenAI的API
import gradio as gr # 导入gradio库,用于创建交互式Web应用
import os # 导入os模块,用于设置环境变量# 设置环境变量,这里留空了OPENAI_BASE_URL。通常,您不需要更改API的基本URL
os.environ["OPENAI_BASE_URL"] = ""
# 设置环境变量,这里的API密钥使用了一个示例值,你需要替换为你自己的OpenAI API密钥
os.environ["OPENAI_API_KEY"] = "dummy" # 替换为你的密钥# 初始化ChatOpenAI实例,设置模型为'gpt-4',温度为1.0。这表示使用GPT-4模型,温度参数控制生成文本的创造性
llm = ChatOpenAI(temperature=1.0, model='gpt-4')# 定义预测函数,它接受用户的消息和历史对话记录,返回GPT模型的回复
def predict(message, history):history_langchain_format = [] # 创建一个空列表,用于存储历史对话记录for human, ai in history: # 遍历历史记录中的人机对话# 将人类和AI的消息转换为Langchain的消息格式,并添加到列表中history_langchain_format.append(HumanMessage(content=human))history_langchain_format.append(AIMessage(content=ai))# 将当前用户的消息也转换为相应格式,并加入到历史记录中history_langchain_format.append(HumanMessage(content=message))# 使用Langchain模型llm处理转换后的历史记录,获取模型的回应gpt_response = llm(history_langchain_format)return gpt_response.content # 返回GPT模型的回应内容# 使用Gradio的ChatInterface创建聊天界面,指定predict函数用于响应用户输入
gr.ChatInterface(predict).launch() # 启动Gradio应用,可以在Web浏览器中与之交互
使用 openai
的流媒体示例
当然,我们也可以直接使用 openai
库。这里有一个类似的例子,但这次是带有流式结果的:
from openai import OpenAI # 从openai模块导入OpenAI类,用于实例化客户端
import gradio as gr # 导入gradio库,用于创建交互式Web界面
import osapi_key = "dummy" # 将api_key变量设置为你的API密钥
# 设置环境变量
os.environ["OPENAI_BASE_URL"] = ''
# os.environ["OPENAI_API_KEY"] = "your-api-key"
client = OpenAI(api_key=api_key) # 使用你的API密钥实例化OpenAI客户端# 定义predict函数,它接受用户的消息和聊天历史作为输入
def predict(message, history):history_openai_format = [] # 创建一个空列表用于存储处理过的历史记录for human, assistant in history: # 遍历历史对话# 将每条历史消息按照OpenAI所需的格式添加到列表中history_openai_format.append({"role": "user", "content": human })history_openai_format.append({"role": "assistant", "content":assistant})# 将最新的用户消息也以相同的格式添加到列表history_openai_format.append({"role": "user", "content": message})# 使用客户端请求GPT-3.5-turbo模型的回复,传入处理过的历史消息和其他参数response = client.chat.completions.create(model='gpt-4',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 += chunk.choices[0].delta.content # 将其添加到累积的响应字符串yield partial_message # 使用yield返回累积的响应字符串,实现流式响应gr.ChatInterface(predict).launch() # 使用Gradio的ChatInterface调用predict函数,并启动Web界面
示例使用本地开源LLM与 Hugging Face
当然,在许多情况下,您可能希望在本地运行聊天机器人。这里有一个使用 Hugging Face 的 Together's RedePajama 模型的等效示例(这需要您拥有支持 CUDA 的 GPU)。
import gradio as gr # 导入gradio库来创建图形化的聊天界面
import torch # 导入PyTorch库
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') # 把模型移动到GPU上以加速计算class StopOnTokens(StoppingCriteria): # 定义一个停止准则,按照特定的词或标志来停止生成def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:stop_ids = [29, 0] # 设置停止IDfor stop_id in stop_ids:if input_ids[0][-1] == stop_id:return Truereturn Falsedef predict(message, history): # 定义预测函数,接受消息和历史记录history_transformer_format = history + [[message, ""]]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")# 设置文本生成参数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]))t = Thread(target=model.generate, kwargs=generate_kwargs) # 在后台线程中生成回复t.start()partial_message = ""for new_token in streamer: # 从生成器中逐个获取新的标记if new_token != '<': # 如果新标记不是特定的字符partial_message += new_token # 添加到部分消息中yield partial_message # 实时更新生成的消息gr.ChatInterface(predict).launch() # 启动Gradio界面
有了这些例子,你应该很快就能创建自己的 Gradio 聊天机器人演示了!要构建更多定制的聊天机器人应用程序,请查看使用低级别 gr.Blocks()
API 的专用指南https://www.gradio.app/guides/creating-a-custom-chatbot-with-blocks 。