我们对知识库和LLM已经有了基本的理解,现在是将它们巧妙地融合并打造成一个富有视觉效果的界面的时候了。这样的界面不仅对操作更加便捷,还能便于与他人分享。
Gradio 是一种快速便捷的方法,可以直接在 Python 中通过友好的 Web 界面演示机器学习模型。在本课程中,我们将学习_如何使用它为生成式人工智能应用程序构建用户界面_。在构建了应用程序的机器学习或生成式人工智能后,如果你想构建一个demo给其他人看,也许是为了获得反馈并推动系统的改进,或者只是因为你觉得这个系统很酷,所以想演示一下:Gradio 可以让您通过 Python 接口程序快速实现这一目标,而无需编写任何前端、网页或 JavaScript 代码。 加载 HF API 密钥和相关 Python 库
Gradio 可以包装几乎任何 Python 函数为易于使用的用户界面。
常用的基础模块构成如下:
大部分功能模块都可以通过以下三个参数进行初始化:
# 导入所需的库
import gradio as gr # 用于创建 Web 界面
import os # 用于与操作系统交互,如读取环境变量
# 定义一个函数来根据输入生成文本
def generate(input, temperature):
"""
该函数用于根据输入生成文本。
参数:
input: 输入内容。
temperature: LLM 的温度系数。
返回:
output: 生成的文本。
"""
# 使用预定义的 client 对象的 predict 方法,从输入生成文本
# slider 的值限制生成的token的数量
output = llm.predict(input, temperature=temperature)
return output # 返回生成的文本
# 创建一个 Web 界面
# 输入:一个文本框和一个滑块
# 输出:一个文本框显示生成的文本
demo = gr.Interface(
fn=generate,
inputs=[
gr.Textbox(label="Prompt"), # 文本输入框
gr.Slider(label="Temperature", value=0, maximum=1, minimum=0) # 滑块用于选择模型的 temperature
],
outputs=[gr.Textbox(label="Completion")], # 显示生成文本的文本框
title="Chat Robot", # 界面标题
description="Local Knowledge Base Q&A with llm", # 界面描述
# allow_flagging="never",
)
# 关闭可能已经启动的任何先前的 gradio 实例
gr.close_all()
# 启动 Web 界面
# 使用环境变量 PORT1 作为服务器的端口号
# demo.launch(share=True, server_port=int(os.environ['PORT1']))
demo.launch() # 直接启动页面
上述代码进行运行:
然后输出一下URL:将下面的 URL 复制,在浏览器打开:
Running on local URL: http://127.0.0.1:7860
To create a public link, set `share=True` in `launch()`.
功能介绍:
通过 demo.launch() 启动整个可视化前端界面。
我们可以对demo.launch 中的参数进行配置:demo.launch(share=True, server_port=8080))
看文件提示,每次生成的 外部访问链接,如果没有付费的话,每次只能存活72小时,72小时之后,就会中断;如果想要长期使用,可能需要付费。
这样,外部的用户也可以通过生成的链接直接访问我们的界面。
现在我们已经搭建了一个非常简单的 Gradio 界面,它有一个文本框输入和一个输出。我们已经可以非常简单地向 LLM 提问。但我们还是不能对话,因为如果你再问一个后续问题,它就无法理解或保留上下文。
因此,基本上我们要做的是,向模型发送我们之前的问题、它自己的回答以及后续问题。但建立所有这些都有点麻烦。这就是 Gradio 聊天机器人组件的作用所在,因为它允许我们简化向模型发送对话历史记录的过程。
因此,我们要解决这个问题。为此,我们将引入一个新的 Gradio 组件–Gradio Chatbot。
让我们开始使用 Gradio Chatbot 组件。这里实例化了一个带有文本框 prompt 和提交按钮的 Gradle ChatBot 组件,是一个非常简单的用户界面。但我们现在还不是在和 LLM 聊天。
我们必须格式化聊天 prompt。此处正在定义这个格式化聊天 prompt 函数。 在这里,我们要做的就是使其包含聊天历史记录,这样 LLM 就能知道上下文。 但这还不够。我们还需要告诉它,哪些信息来自用户,哪些信息来自 LLM 本身,也就是我们正在调用的助手。 因此,我们设置了格式聊天 prompt 功能,在聊天记录的每一轮中,都包含一条用户信息和一条助手信息,以便我们的模型能准确回答后续问题。 现在,我们要将格式化的 prompt 传递给我们的 API。
相比 Interface,Blocks 提供了一个低级别的 API,用于设计具有更灵活布局和数据流的网络应用。Blocks 允许控制组件在页面上出现的位置,处理复杂的数据流(例如,输出可以作为其他函数的输入),并根据用户交互更新组件的属性可见性。可以定制更多组件。
# 定义一个函数,用于格式化聊天 prompt。
def format_chat_prompt(message, chat_history):
"""
该函数用于格式化聊天 prompt。
参数:
message: 当前的用户消息。
chat_history: 聊天历史记录。
返回:
prompt: 格式化后的 prompt。
"""
# 初始化一个空字符串,用于存放格式化后的聊天 prompt。
prompt = ""
# 遍历聊天历史记录。
for turn in chat_history:
# 从聊天记录中提取用户和机器人的消息。
user_message, bot_message = turn
# 更新 prompt,加入用户和机器人的消息。
prompt = f"{prompt}\nUser: {user_message}\nAssistant: {bot_message}"
# 将当前的用户消息也加入到 prompt中,并预留一个位置给机器人的回复。
prompt = f"{prompt}\nUser: {message}\nAssistant:"
# 返回格式化后的 prompt。
return prompt
# 定义一个函数,用于生成机器人的回复。
def respond(message, chat_history):
"""
该函数用于生成机器人的回复。
参数:
message: 当前的用户消息。
chat_history: 聊天历史记录。
返回:
"": 空字符串表示没有内容需要显示在界面上,可以替换为真正的机器人回复。
chat_history: 更新后的聊天历史记录
"""
# 调用上面的函数,将用户的消息和聊天历史记录格式化为一个 prompt。
formatted_prompt = format_chat_prompt(message, chat_history)
# 使用llm对象的predict方法生成机器人的回复(注意:llm对象在此代码中并未定义)。
bot_message = llm.predict(formatted_prompt,
max_new_tokens=1024,
stop_sequences=["\nUser:", ""])
# 将用户的消息和机器人的回复加入到聊天历史记录中。
chat_history.append((message, bot_message))
# 返回一个空字符串和更新后的聊天历史记录(这里的空字符串可以替换为真正的机器人回复,如果需要显示在界面上)。
return "", chat_history
# 下面的代码是设置Gradio界面的部分。
# 使用Gradio的Blocks功能定义一个代码块。
with gr.Blocks() as demo:
# 创建一个Gradio聊天机器人组件,设置其高度为240。
chatbot = gr.Chatbot(height=240)
# 创建一个文本框组件,用于输入 prompt。
msg = gr.Textbox(label="Prompt")
# 创建一个提交按钮。
btn = gr.Button("Submit")
# 创建一个清除按钮,用于清除文本框和聊天机器人组件的内容。
clear = gr.ClearButton(components=[msg, chatbot], value="Clear console")
# 设置按钮的点击事件。当点击时,调用上面定义的respond函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。
btn.click(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
# 设置文本框的提交事件(即按下Enter键时)。功能与上面的按钮点击事件相同。
msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
# 关闭所有已经存在的 Gradio 实例。
gr.close_all()
# 启动新的 Gradio 应用,设置分享功能为 True,并使用环境变量 PORT1 指定服务器端口。
# demo.launch(share=True, server_port=int(os.environ['PORT1']))
demo.launch()
Closing server running on port: 7860
Running on local URL: http://127.0.0.1:7860
To create a public link, set `share=True` in `launch()`.
现在,我们的问答机器人可以回答后续问题了。 我们可以看到,我们向它发送了上下文。我们向它发送了信息,然后要求它完成。一旦我们进入另一个迭代循环,我们就会向它发送我们的整个上下文,然后要求它完成。这很酷。但是,如果我们一直这样迭代下去,那么模型在一次对话中所能接受的信息量就会达到极限,因为我们总是给它越来越多的之前对话的内容。
这里,我们创建了一个简单但功能强大的用户界面,用于与LLM聊天。如果需要进一步Gradio 所能提供的最佳功能,我们可以创建一个包含更多功能的用户界面。
现在我们可以将本地知识库的内容接入进来,让 llm 运用本地数据库进行回答。
我们之前已经学习过如何运用 LLM 对本地知识库进行问答,现在让我们将这个功能加入到我们的前端界面中。 回忆一下,我们学习了两种运用本地知识库进行问答的方式:
我们需要创建两个按钮,分别与对应的函数相绑定。
初始化按钮:
db_with_his_btn = gr.Button("Chat db with history")
db_wo_his_btn = gr.Button("Chat db without history")
将按钮与函数进行绑定,并且配置相应的输入和输出:
# 设置按钮的点击事件。当点击时,调用上面定义的 Chat_QA_chain_self 函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。
db_with_his_btn.click(Chat_QA_chain_self.answer, inputs=[msg, chatbot, llm, embeddings, history_len, top_k, temperature], outputs=[msg, chatbot])
# 设置按钮的点击事件。当点击时,调用上面定义的 QA_chain_self 函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。
db_wo_his_btn.click(QA_chain_self.answer, inputs=[msg, chatbot, llm, embeddings, top_k, temperature], outputs=[msg, chatbot])
这里我们是直接运用前面章节生成好的向量数据库。
当用户想要在前端页面上传自己的文件生成新的数据库时,gradio.File 可以很方便的完成这部分功能。
File 用于创建一个文件组件,允许用户上传通用文件(用作输入)或显示通用文件(用作输出)。
**file_count **允许用户上传文件的数量,返回类型将根据 “multiple” 或 “directory” 的情况为每个文件返回一个列表。
file_types: 要上传的文件扩展名或文件类型的列表(例如[‘image’,‘.json’,‘.mp4’])。字符串表示支持上传的文件类型,格式后缀表示支持上传的文件格式。
注意:当 file_count 为 “directory” 时,会忽略 “file_types” 的参数配置。
gr.File(label='请选择知识库目录',file_count='directory',
file_types=['.txt', '.md', '.docx', '.pdf'])
对于本项目来说,存在很多可以配置的参数,比如 LLM 的温度系数(temperature),这个参数的取值范围为 0-1,控制着 LLM 生成内容的稳定性。
温度基本上就是希望模型的变化程度。因此,如果将温度设为零,模型就会倾向于始终对相同的输入做出相同的反应。所以同样的问题,同样的答案。温度越高,信息的变化就越多。但如果温度过高,它就会开始给出无意义的答案。
我们想通过前端来进行参数的配置,但同时希望将参数限制在一个区间内,这时 gr.text 无法满足我们的需求。gradio.Slider 是可以胜任这个任务的组件。
gradio.Slider 允许用户通过滑动滑块在指定的范围内选择一个值:
temperature = gr.Slider(0,
1,
value=0.00,
step=0.01,
label="llm temperature",
interactive=True)
我们可以将其他类似的参数采用相同的构建方式,如:
刚刚我们介绍了 gradio 对于连续值的选择方法,现在我们来介绍下对于离散值的选择方法。
我们可以切换不同的模型,尝试不同模型的效果。我们用 gradio.Dropdown 来建立一个下拉列表,让用户从提供的模型中选择一个模型。
llm = gr.Dropdown(
llm_model_list,
label="large language model",
value=init_llm,
interactive=True)
同样,我们可以对生成 Embedding 的模型进行对应的配置。
将组件作为输入绑定在对应的函数中,即可完成对应参数的切换。
对于 llm 和 Embedding 来说,通常我们选择一次后就不会再做调整,一直将整个组件展示在界面中显得占位置,我们可以用 gradio.Accordion 组件来将其折叠。
gradio.Accordion 默认是展开的,对于组件内容提供折叠按钮。对于不需要的组件我们可以配置 open=“False”, 将其设置为默认折叠的状态。
我们先初始化 gradio.Accordion 组件,再将需要折叠的内容用 with 包起来即可。
model_select = gr.Accordion("模型选择")
with model_select:
llm = gr.Dropdown(...)
embedding = gr.Dropdown(...)
之前介绍的都是交互式组件,我们可以用 markdown 为我们的界面加一些说明,使整个界面看起来更加美观。同时可以增加一些辅助信息。
gr.Markdown("""Chat Robot
Local Knowledge Base Q&A with llm
""")
现在我们已经有了足够多的组件,是时候将他们按照一定的布局格式进行调整了。
gradio.Row() 和 gradio.Column() 分别是新建一行和新建一列的组件。我们将界面划分成不同的行列区域,将所需组件摆放在对应位置即可。
gradio.Row() 的常用参数:
gradio.Column() 的常用参数:
例如,我们可以将所有的对话组件放在一行中。将所有参数配置放在一列, 并将 chatBot 和参数配置以 4:1 的比例进行布置。
with gr.Row():
# 创建提交按钮。
db_with_his_btn = gr.Button("Chat db with history")
db_wo_his_btn = gr.Button("Chat db without history")
llm_btn = gr.Button("Chat with llm")
with gr.Column(scale=4):
chatbot = gr.Chatbot(height=480)
...
with gr.Column(scale=1):
...
model_argument = gr.Accordion("参数配置", open=False)
with model_argument:
...
model_select = gr.Accordion("模型选择")
with model_select:
...
当项目部署时,可能同一时间有多个用户进行访问,这时我们可以将 demo.queue(concurrency_count=3) 进行配置,表示用三个线程来并行。
现在是时候将我们的界面分享给别人了
下面是 demo.launch() 的几种场景分享配置
现在我们已经实现了 《llm 通过本地数据库进行回答》的基本功能和界面。快去进行自己的尝试吧。