2023-03-18
做模型或数据工具时,最容易卡住的地方经常不是算法本身,而是“怎么让别人顺手试一下”。 如果为了一个模型 demo 还要写前端、写接口、调样式、部署页面,节奏很容易被打断。
Gradio 的定位就很直接:把一个 Python 函数、模型推理函数、数据处理函数,快速包装成可交互的 Web 应用。
根据 Gradio GitHub README 的介绍,它是一个开源 Python 包,可以为机器学习模型、API 或任意 Python 函数快速构建 demo 或 Web 应用,并且可以通过内置分享能力把应用交给别人试用。
这篇文章不只写“Hello World”,而是从 Python 开发者的角度,把 Gradio 常用能力串起来:
Interface:几行代码把函数变成界面Blocks:自定义布局和多步骤交互ChatInterface:快速做聊天类应用queue:给耗时推理排队flagging:收集有价值的样本gradio_client:把 Gradio 应用当 API 调用先看一个非常小的例子。你写一个 Python 函数,然后告诉 Gradio 输入组件和输出组件,它就能生成一个可以在浏览器里操作的界面。
import gradio as gr
def greet(name: str, style: str):
if style == "活力一点":
return f"你好,{name}!很适合继续写点 Python。"
return f"你好,{name}。"
demo = gr.Interface(
fn=greet,
inputs=[
gr.Textbox(label="名字", placeholder="输入一个名字"),
gr.Radio(["活力一点", "稳重一点"], label="语气", value="活力一点"),
],
outputs=gr.Textbox(label="输出"),
title="Gradio 小试牛刀",
api_name="greet",
)
demo.launch()
这个例子里,真正关键的是三件事:
fn:你自己的 Python 函数inputs:函数参数对应的输入组件outputs:函数返回值对应的输出组件Gradio 的门槛低,是因为它没有要求你先理解前端工程。
你先把 Python 函数写好,界面可以顺着函数签名自然长出来。
Interface 适合快速 demo,但别什么都塞进去Interface 很适合这种场景:
比如我们写一个简单的文本分析器:
import gradio as gr
def analyze_text(text: str):
length = len(text)
keywords = [word for word in ["Python", "Gradio", "AI"] if word.lower() in text.lower()]
score = min(length / 120, 1.0)
label = {
"信息量": round(score, 2),
"轻量内容": round(1 - score, 2),
}
return {
"字符数": length,
"命中关键词": ", ".join(keywords) or "暂无",
}, label
demo = gr.Interface(
fn=analyze_text,
inputs=gr.Textbox(lines=6, label="输入文本"),
outputs=[
gr.JSON(label="结构化结果"),
gr.Label(label="粗略判断"),
],
examples=[
["Gradio 可以用 Python 快速搭建 AI 应用。"],
["我想给模型做一个可以交互测试的小工具。"],
],
api_name="analyze",
)
demo.launch()
这里可以看到,输出不一定是纯文本。
Gradio 内置了很多组件,比如 Textbox、Image、Audio、Dataframe、JSON、Label、Plot、File 等,能够覆盖不少机器学习和数据应用场景。
但 Interface 也有边界:如果你的界面开始出现多个区域、多条交互链路、组件状态联动,那就该换到 Blocks。
Blocks:真正做应用时,布局和事件都交给它Blocks 可以理解成 Gradio 的“自定义应用模式”。
你可以自己组织行、列、标签页、按钮、状态组件,也可以让一个组件的输出继续作为另一个步骤的输入。
下面这个例子做了一个“小型文案工坊”:
gr.State 保存最近一次生成结果import gradio as gr
def draft_copy(topic: str, tone: str):
text = (
f"主题:{topic}\n"
f"风格:{tone}\n\n"
f"这是一段围绕「{topic}」生成的初稿,语气偏「{tone}」。"
)
return text, text
def summarize_copy(latest_text: str):
if not latest_text:
return "还没有可总结的内容。"
first_line = latest_text.splitlines()[0]
return f"摘要:{first_line},适合继续扩写成博客、脚本或产品说明。"
with gr.Blocks(title="文案工坊", theme=gr.themes.Soft()) as demo:
gr.Markdown("## 文案工坊")
latest = gr.State("")
with gr.Row():
with gr.Column(scale=1):
topic = gr.Textbox(label="主题", placeholder="例如:Gradio 快速原型")
tone = gr.Dropdown(
["轻松", "专业", "简洁", "热情"],
label="风格",
value="轻松",
)
build_btn = gr.Button("生成文案", variant="primary")
summary_btn = gr.Button("提炼摘要")
with gr.Column(scale=2):
draft = gr.Textbox(label="生成结果", lines=10)
summary = gr.Textbox(label="摘要", lines=3)
build_btn.click(
fn=draft_copy,
inputs=[topic, tone],
outputs=[draft, latest],
api_name="draft",
)
summary_btn.click(
fn=summarize_copy,
inputs=latest,
outputs=summary,
api_name="summary",
)
demo.launch()
这类写法比 Interface 更适合真实小工具。
原因不是它“更高级”,而是它能把交互流程拆清楚:哪个按钮触发哪个函数,哪些组件参与输入输出,状态放在哪里,一眼就能看出来。
.click()、.change()、.then() 让流程动起来Gradio 的事件系统很像把 Python 函数接到组件上。
按钮可以 .click(),输入框可以 .change(),上传组件可以 .upload(),一个事件完成后还可以接 .then()。
下面是一个更像模型推理流程的写法:
import gradio as gr
def clean_prompt(prompt: str):
return prompt.strip()
def run_model(prompt: str, temperature: float):
return f"模型收到:{prompt}\n采样参数:{temperature}"
def make_report(result: str):
return {
"状态": "完成",
"结果长度": len(result),
}
with gr.Blocks() as demo:
prompt = gr.Textbox(label="Prompt", lines=4)
temperature = gr.Slider(0, 1, value=0.7, label="temperature")
run = gr.Button("运行")
cleaned = gr.Textbox(label="清洗后 Prompt")
output = gr.Textbox(label="模型输出", lines=6)
report = gr.JSON(label="运行报告")
run.click(
clean_prompt,
inputs=prompt,
outputs=cleaned,
).then(
run_model,
inputs=[cleaned, temperature],
outputs=output,
).then(
make_report,
inputs=output,
outputs=report,
)
demo.launch()
这段代码的好处是:
数据清洗、模型推理、结果统计被拆成三个函数,每一步都能独立测试,也能独立替换。
ChatInterface 负责把聊天框架搭好聊天类应用如果从 Blocks 开始手写,也能做,但会多写不少样板代码。
Gradio 提供了 ChatInterface,只要你给它一个函数,它就能搭出聊天 UI。
import gradio as gr
def bot(message, history):
if "gradio" in message.lower():
return "Gradio 很适合把 Python 函数快速包装成可交互应用。"
return f"我收到了:{message}"
demo = gr.ChatInterface(
fn=bot,
title="Gradio Chat Demo",
examples=["Gradio 适合做什么?", "帮我解释 Blocks"],
)
demo.launch()
如果要做流式输出,也可以让函数变成生成器:
import gradio as gr
def stream_bot(message, history):
answer = f"你刚刚说的是:{message}。我会把这句话拆成几段返回。"
buffer = ""
for char in answer:
buffer += char
yield buffer
demo = gr.ChatInterface(stream_bot, title="流式聊天 Demo")
demo.launch()
这个例子没有绑定具体大模型,但模式是一样的:
你可以在函数里调用本地模型、远程 API、RAG 检索链路或工具函数,然后把结果返回给 Gradio。
模型推理、图片生成、音频处理这类任务通常比较重。
如果多个用户同时操作,直接并发打到模型函数上,容易让机器压力突然升高。
Gradio 提供了 queue(),可以把请求放进队列中处理,也可以给事件设置并发限制。
import gradio as gr
def heavy_predict(text: str):
return text.upper()
with gr.Blocks() as demo:
text = gr.Textbox(label="输入")
out = gr.Textbox(label="输出")
btn = gr.Button("开始处理")
btn.click(
heavy_predict,
inputs=text,
outputs=out,
concurrency_limit=2,
api_name="heavy_predict",
)
demo.queue(default_concurrency_limit=1)
demo.launch()
简单理解:
demo.queue() 控制应用整体排队行为concurrency_limit 控制单个事件的并发上限模型 demo 做出来之后,真正有价值的数据往往来自使用者反馈:
哪些输入让模型答得不好?哪些输出值得二次标注?哪些样本应该进入下一轮评测?
Gradio 的 Interface 默认提供 flagging 能力,也可以自定义回调。
比如使用 CSV 记录用户标记样本:
import gradio as gr
def classify(text: str):
if "bug" in text.lower():
return {"问题反馈": 0.85, "普通内容": 0.15}
return {"普通内容": 0.78, "问题反馈": 0.22}
demo = gr.Interface(
fn=classify,
inputs=gr.Textbox(label="输入文本"),
outputs=gr.Label(label="分类结果"),
flagging_callback=gr.CSVLogger(),
flagging_options=["误判", "有价值样本", "需要人工复核"],
)
demo.launch()
对模型团队来说,flagging 不只是一个按钮。
它可以成为“模型评估样本池”的入口,让 demo 不只是展示工具,也能反向帮助模型改进。
gradio_client:把 Gradio 应用当 API 调用Gradio 应用不只能给人点按钮,也能被 Python 程序调用。
官方提供了 gradio_client,可以像调用远程函数一样调用一个 Gradio app。
服务端代码可以这样暴露接口:
import gradio as gr
def normalize_text(text: str):
return " ".join(text.split())
demo = gr.Interface(
fn=normalize_text,
inputs=gr.Textbox(label="原始文本"),
outputs=gr.Textbox(label="清洗后文本"),
api_name="normalize",
)
demo.launch()
客户端这样调用:
from gradio_client import Client
client = Client("http://127.0.0.1:7860")
result = client.predict(
text=" Gradio makes Python apps easy. ",
api_name="/normalize",
)
print(result)
如果任务比较重,还可以用 submit() 拿到 job 对象:
job = client.submit(
text="hello gradio",
api_name="/normalize",
)
print(job.result())
这就让 Gradio 不只是“网页 demo”,也可以作为自动化流程中的一个服务节点。
Gradio 很适合快速出效果,但代码结构还是要收住。
建议把项目拆成这样:
project/
├── app.py # Gradio 入口
├── services/
│ ├── model.py # 模型加载和推理
│ └── postprocess.py # 结果处理
├── examples_data/
│ └── samples/ # 示例文件
└── tests/
└── test_model.py # 核心函数测试
app.py 只负责界面和事件绑定,真正的业务函数放到 services/。
这样 Gradio 页面可以改,模型逻辑也可以单独测试,不会混成一大坨。
import gradio as gr
from services.model import predict
def build_demo():
with gr.Blocks(theme=gr.themes.Soft(), title="AI 工具台") as demo:
gr.Markdown("## AI 工具台")
with gr.Row():
inp = gr.Textbox(label="输入", lines=5)
out = gr.Textbox(label="输出", lines=5)
run = gr.Button("运行", variant="primary")
run.click(predict, inputs=inp, outputs=out, api_name="predict")
return demo
if __name__ == "__main__":
build_demo().queue().launch()