Gradio 后端驱动:快速构建自定义 AI 应用前端
展示用 Gradio 后端灵活实现各种前端界面的方法。对 AI 应用快速原型有实用价值。
展示用 Gradio 后端灵活实现各种前端界面的方法。对 AI 应用快速原型有实用价值。
如果你想完全使用自己的前端框架来构建应用,比如 React、Svelte,甚至是原生 HTML/JS,同时又希望继续享受 Gradio 的队列系统、API 基础设施、MCP 支持,以及 Spaces 上的 ZeroGPU,该怎么办?
这正是 gradio.Server 要解决的问题。它拓展了 Gradio 和 Hugging Face Spaces 的能力边界。
Text Behind Image:一款图片编辑器。你上传一张照片后,它会使用 ML 模型移除背景,然后你可以在前景主体与背景之间放置风格化文字。最终效果看起来就像文字位于图片中的人物或物体后方。
这种 UI 无法用 Gradio components 表达出来。它是一款完整的 Web 应用。但我们仍然想要 Gradio 强大的后端能力:队列、并发管理、ZeroGPU 支持,以及无需为基础设施操心就能托管到 HF Spaces。
gradio.Server 扩展了 FastAPI。它既为你提供 FastAPI 的全部能力——自定义路由、middleware、文件上传和任意响应类型——又在其上加入了 Gradio 的 API 引擎:队列、SSE streaming、并发控制,以及对 gradio_client 的兼容。
下面就是 Text Behind Image 的完整后端:
import os
import torch
from PIL import Image
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
from gradio import Server
from gradio.data_classes import FileData
from fastapi.responses import HTMLResponse
import spaces
torch.set_float32_matmul_precision("high")
birefnet = AutoModelForImageSegmentation.from_pretrained(
"ZhengPeng7/BiRefNet", trust_remote_code=True
)
birefnet.to("cuda")
birefnet.float()
transform_image = transforms.Compose([
transforms.Resize((1024, 1024)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
app = Server()
@spaces.GPU
def segment(image: Image.Image) -> Image.Image:
"""Run BiRefNet segmentation to produce a transparency mask."""
image_size = image.size
input_images = transform_image(image).unsqueeze(0).to("cuda")
with torch.no_grad():
preds = birefnet(input_images)[-1].sigmoid().cpu()
pred = preds[0].squeeze()
mask = transforms.ToPILImage()(pred).resize(image_size)
image.putalpha(mask)
return image
@app.api(name="remove_background")
def remove_background(image_path: FileData) -> FileData:
"""Remove background from an image. Returns transparent PNG."""
im = Image.open(image_path["path"]).convert("RGB")
result = segment(im)
out_path = image_path["path"].rsplit(".", 1)[0] + ".png"
result.save(out_path)
return FileData(path=out_path)
@app.get("/", response_class=HTMLResponse)
async def homepage():
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
with open(html_path, "r", encoding="utf-8") as f:
return f.read()
app.launch(show_error=True)
就这些,大约 50 行 Python。模型会在启动时加载,@spaces.GPU 负责 ZeroGPU 资源分配,gradio.Server 则管理队列和并发。下面我们拆解一下具体发生了什么。
@app.api(),而不是普通的 FastAPI 路由?如果这是一个常规的 FastAPI 应用,你会为背景移除功能定义一个 @app.post() 路由。这样确实能运行——直到两名用户同时发起请求。由于没有并发管理,两次请求会争抢 GPU,导致应用崩溃或返回错误结果。
@app.api() 解决了这个问题。它使用 Gradio 的队列引擎封装你的函数:请求会被串行处理,并发受到控制;而在 ZeroGPU Spaces 上,GPU 资源也会通过 @spaces.GPU 自动分配。还有一个额外好处:任何 @app.api() endpoint 都可以通过 gradio_client 调用,因此其他应用或脚本也能以编程方式使用你的 Space:
from gradio_client import Client, handle_file
client = Client("ysharma/text-behind-image")
result = client.predict(
image_path=handle_file("photo.jpg"),
api_name="/remove_background"
)
与此同时,@app.get("/") 是一个提供 HTML 页面的标准 FastAPI 路由。因为 Server 本身就是一个 FastAPI 应用,所以两者可以自然共存。
这个示例中的 index.html 是一个自包含的 Web 应用,大约有 1300 行代码。不使用 React,不需要构建步骤,也没有 bundler,只使用原生 HTML,并实现了以下功能:
z-index 堆叠<canvas> 合成,在客户端导出 PNG前端通过 Gradio JS Client 与后端通信:
import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
const client = await Client.connect(window.location.origin);
const result = await client.predict("/remove_background", {
image_path: handle_file(file),
});
foregroundLayer.src = result.data[0].url; // transparent PNG
这里是关键:前端使用 Gradio JS client,而不是直接调用原始的 fetch(),因此所有请求都会经过 Gradio 的队列。这意味着并发会得到妥善管理,GPU 请求不会相互冲突,你甚至还能向用户显示队列位置或处理进度。至于文字渲染、图层合成和导出等其他工作,则全部在浏览器中完成。
下面这些能力,在 gradio.Server 出现之前无法实现:
借助 gradio.Server,Gradio 同时成为了一个后端框架:想使用它的 UI 系统时就使用;不想使用时,则可以接入自己的前端。
如果你想使用 Gradio 的 UI,可以选择 gr.Blocks、gr.Interface 或 gr.ChatInterface。如果你想使用自己的 UI,那就选择 gradio.Server,搭配任何你喜欢的前端。无论采用哪种方式,你都能获得 Spaces 托管、API 队列、gradio_client 访问能力、完整的 HF 生态系统,以及更多能力。
这款应用已经在 Spaces 上线:ysharma/text-behind-image
上传任意一张主体清晰的照片,就可以开始在主体后方叠加文字。试试 3D 挤出、透视倾斜和描边效果,它们组合起来的效果很不错。
本文介绍了核心理念:gradio.Server 让你可以将任意前端与 Gradio 后端搭配使用。还有更多内容值得探索,包括使用 @app.mcp.tool() 注册 MCP tool、通过 SSE streaming 实现实时更新、批处理,以及构建共享状态多页面应用的模式。
我们会在后续文章中深入探讨这些内容,敬请期待。
gr.HTML,一次生成任意 Web 应用