Gradio 交互式UI框架

1. 简介与在 LLM 开发中的作用

Gradio 是一个开源 Python 库,用于快速构建机器学习和深度学习模型的交互式 Web 界面。它的核心理念是:用最少的代码创建可交互的 Demo

Gradio 的核心特点:

  • 极简 API:几行代码即可创建完整 Web 界面
  • 内置组件:文本框、图片、音频、视频、Chatbot 等开箱即用
  • 自动分享share=True 一键生成公网链接
  • HuggingFace 集成:一键部署到 HuggingFace Spaces
  • 流式输出:原生支持 LLM 逐 token 流式展示

Gradio 在 LLM 开发中的角色

  1. LLM Demo 快速搭建:将模型推理函数包装为交互式 Web 应用,快速展示效果
  2. 交互式评估:通过 Chatbot 界面进行人工评估和红队测试
  3. 原型验证:在产品开发早期快速验证 LLM 应用可行性
  4. 模型对比:并排展示不同模型的生成效果
  5. 数据标注:构建 LLM 输出质量的标注工具

2. 安装方式

基础安装

pip install gradio

完整安装(含额外依赖)

pip install gradio[full]  # 安装所有可选依赖

开发相关

pip install gradio                    # 核心库
pip install gradio-client             # Python 客户端,用于编程方式调用 Gradio API
pip install httpx                     # 异步 HTTP 客户端

验证安装

import gradio as gr
print(gr.__version__)

3. 核心类/函数/工具的详细说明

3.1 Interface — 快速创建 UI

gr.Interface 是 Gradio 最核心的高层 API,用最少的代码创建输入→处理→输出的界面。

import gradio as gr

def greet(name: str) -> str:
    return f"你好,{name}!"

demo = gr.Interface(
    fn=greet,                          # 处理函数,接收 inputs 的值作为参数
    inputs="text",                     # 输入组件类型(简写或组件对象)
    outputs="text",                    # 输出组件类型
    title="问候应用",                   # 界面标题
    description="输入名字获取问候",      # 界面描述
    examples=[                         # 示例输入,用户可点击快速填入
        ["张三"],
        ["李四"],
    ],
)

demo.launch()

Interface 关键参数

参数类型说明
fnCallable处理函数,输入参数对应 inputs,返回值对应 outputs
inputsstr/Component/list输入组件,可以是类型字符串或组件对象
outputsstr/Component/list输出组件
titlestr界面标题
descriptionstr界面描述(支持 Markdown)
exampleslist示例数据,显示在界面下方
livebool设为 True 时,输入变化自动触发(无需点按钮)
allow_flaggingstr标记模式:”never”/”auto”/”manual”
cache_examplesbool是否缓存示例的运行结果

组件类型简写

简写字符串对应组件适用场景
"text"gr.Textbox文本输入/输出
"textbox"gr.Textbox多行文本
"number"gr.Number数值
"slider"gr.Slider滑块
"checkbox"gr.Checkbox复选框
"dropdown"gr.Dropdown下拉选择
"image"gr.Image图片
"audio"gr.Audio音频
"file"gr.File文件上传

多输入多输出

import gradio as gr

def analyze(text: str, model: str, temperature: float) -> tuple:
    """多输入 → 多输出"""
    summary = f"摘要:{text[:20]}..."
    sentiment = "正面"
    word_count = len(text)
    return summary, sentiment, word_count

demo = gr.Interface(
    fn=analyze,
    inputs=[
        gr.Textbox(label="输入文本", lines=5, placeholder="请输入要分析的文本..."),
        gr.Dropdown(choices=["gpt-3.5", "gpt-4", "claude-3"], value="gpt-3.5", label="模型选择"),
        gr.Slider(minimum=0.0, maximum=2.0, value=0.7, step=0.1, label="Temperature"),
    ],
    outputs=[
        gr.Textbox(label="摘要"),
        gr.Textbox(label="情感"),
        gr.Number(label="字数"),
    ],
    title="文本分析工具",
)

demo.launch()

3.2 Blocks — 灵活布局与事件绑定

gr.Blocks 提供比 Interface 更灵活的低层 API,支持自定义布局、组件组合和事件绑定。

import gradio as gr

with gr.Blocks(title="LLM 对话应用") as demo:
    # 使用 Markdown 添加标题和说明
    gr.Markdown("# LLM 对话应用\n输入问题,获取 AI 回复")

    with gr.Row():                           # 水平布局
        with gr.Column(scale=3):             # 左侧列,比例3
            prompt = gr.Textbox(
                label="输入",
                placeholder="请输入你的问题...",
                lines=5,
            )
            with gr.Row():                   # 嵌套水平布局
                model_select = gr.Dropdown(
                    choices=["gpt-3.5-turbo", "gpt-4", "claude-3"],
                    value="gpt-3.5-turbo",
                    label="模型",
                )
                temperature = gr.Slider(
                    minimum=0.0, maximum=2.0,
                    value=0.7, step=0.1,
                    label="Temperature",
                )
            submit_btn = gr.Button("提交", variant="primary")  # primary 样式
            clear_btn = gr.Button("清空")

        with gr.Column(scale=2):             # 右侧列,比例2
            output = gr.Textbox(label="回复", lines=15)

    # 事件绑定
    submit_btn.click(
        fn=lambda prompt, model, temp: f"[{model}] 回复:{prompt}",
        inputs=[prompt, model_select, temperature],
        outputs=output,
    )
    clear_btn.click(
        fn=lambda: (None, None),             # 清空输入和输出
        inputs=[],
        outputs=[prompt, output],
    )

demo.launch()

Blocks 布局组件

组件用途
gr.Row()水平排列子组件
gr.Column()垂直排列子组件,scale 控制比例
gr.Tab() / gr.TabItem()标签页切换
gr.Accordion()可折叠区域
gr.Group()无边框分组
gr.TabbedInterface()多 Interface 标签页

事件绑定方法

# 组件支持的事件
component.click(fn, inputs, outputs)       # 点击事件
component.change(fn, inputs, outputs)      # 值变化事件
component.submit(fn, inputs, outputs)      # 回车提交(Textbox)
component.select(fn, inputs, outputs)      # 选中事件(Dropdown等)
component.upload(fn, inputs, outputs)      # 上传事件(File/Image)
component.clear(fn, inputs, outputs)       # 清除事件

# 流式输出事件(核心!用于 LLM 逐 token 输出)
component.click(fn, inputs, outputs, stream=True)   # 流式模式

3.3 Chatbot 组件 — 多轮对话

Chatbot 是 Gradio 专门为 LLM 对话场景设计的核心组件。

ChatInterface — 最简对话界面

import gradio as gr

def respond(message: str, history: list) -> str:
    """
    参数:
    - message: 用户当前输入的消息
    - history: 历史对话列表,格式为 [[user_msg, assistant_msg], ...]

    返回:助手回复的文本
    """
    # history 自动维护对话上下文
    context = "\n".join([f"用户:{h[0]}\n助手:{h[1]}" for h in history])
    response = f"基于上下文回复:{message}"
    return response

demo = gr.ChatInterface(
    fn=respond,
    title="LLM 对话助手",
    description="基于大语言模型的对话系统",
    chatbot=gr.Chatbot(height=500),            # 自定义 Chatbot 组件
    textbox=gr.Textbox(placeholder="输入消息...", container=False, scale=7),
    additional_inputs=[
        gr.Slider(minimum=0.0, maximum=2.0, value=0.7, step=0.1, label="Temperature"),
        gr.Dropdown(choices=["gpt-3.5", "gpt-4"], value="gpt-3.5", label="模型"),
    ],
    # additional_inputs 会出现在聊天框下方
)

demo.launch()

ChatInterface 关键参数

参数说明
fn对话处理函数,签名 (message, history) -> str
chatbot自定义 Chatbot 组件
textbox自定义输入框组件
additional_inputs额外输入控件(如 Temperature)
additional_inputs_accordion额外输入的折叠面板配置
retry_btn / undo_btn / clear_btn按钮文本配置

手动构建 Chatbot(更灵活)

import gradio as gr

with gr.Blocks() as demo:
    gr.Markdown("# LLM 多轮对话")

    chatbot = gr.Chatbot(
        height=500,
        bubble_full_width=False,          # 气泡不占满宽度
        avatar_images=(None, "🤖"),       # 用户和助手的头像
        show_copy_button=True,            # 显示复制按钮
    )

    with gr.Row():
        msg_input = gr.Textbox(
            placeholder="输入消息...",
            show_label=False,
            scale=8,
        )
        submit_btn = gr.Button("发送", variant="primary", scale=1)
        clear_btn = gr.Button("清空", scale=1)

    # 状态管理
    state = gr.State([])  # 存储对话历史

    def respond(message: str, history: list) -> tuple:
        """手动管理对话状态"""
        history.append([message, ""])  # 先添加用户消息

        # 模拟生成回复
        response = f"回复:{message}"
        history[-1][1] = response  # 更新助手回复

        return "", history  # 清空输入框,更新聊天记录

    submit_btn.click(
        fn=respond,
        inputs=[msg_input, state],
        outputs=[msg_input, chatbot],
    )
    msg_input.submit(
        fn=respond,
        inputs=[msg_input, state],
        outputs=[msg_input, chatbot],
    )
    clear_btn.click(
        fn=lambda: ([], []),
        inputs=[],
        outputs=[chatbot, state],
    )

demo.launch()

3.4 流式输出 — yield 生成器

流式输出是 Gradio 在 LLM 场景下最重要的功能,通过 Python 生成器实现逐 token 输出。

基础流式输出

import gradio as gr
import time

def stream_respond(message: str, history: list):
    """
    流式输出函数:
    - 使用 yield 逐步返回部分结果
    - Gradio 自动将每次 yield 的内容追加到输出
    - 函数结束时输出完成
    """
    response = f"这是对 '{message}' 的详细回复,包含多个token的流式输出。"
    partial = ""

    for char in response:
        partial += char
        time.sleep(0.05)        # 模拟逐 token 生成延迟
        yield partial           # 每次返回当前累积的完整文本

demo = gr.ChatInterface(fn=stream_respond, title="流式对话")
demo.launch()

带历史上下文的流式对话

import gradio as gr
import time

def stream_chat(message: str, history: list, model: str, temperature: float):
    """
    流式多轮对话函数

    参数:
    - message: 当前用户消息
    - history: 对话历史 [[user, assistant], ...]
    - model: 模型名称(来自 additional_inputs)
    - temperature: 采样温度(来自 additional_inputs)
    """
    # 构建上下文
    context = ""
    for user_msg, assistant_msg in history:
        context += f"用户:{user_msg}\n助手:{assistant_msg}\n"
    context += f"用户:{message}\n"

    # 模拟流式生成
    full_response = f"[{model}] 基于上下文({len(history)}轮对话)回复:{message}"
    partial = ""

    for char in full_response:
        partial += char
        time.sleep(0.03)
        yield partial

demo = gr.ChatInterface(
    fn=stream_chat,
    title="流式多轮对话",
    additional_inputs=[
        gr.Dropdown(choices=["gpt-3.5", "gpt-4", "claude-3"], value="gpt-3.5", label="模型"),
        gr.Slider(0.0, 2.0, value=0.7, step=0.1, label="Temperature"),
    ],
)

demo.launch()

Blocks 模式下的流式 Chatbot

import gradio as gr
import time

with gr.Blocks() as demo:
    gr.Markdown("# LLM 流式对话")

    chatbot = gr.Chatbot(height=500)
    msg = gr.Textbox(placeholder="输入消息...", show_label=False)

    def respond(message: str, chat_history: list):
        """Blocks 下的流式对话需要 yield chat_history"""
        chat_history.append([message, ""])  # 添加空回复

        response = f"回复:{message},这是流式生成的内容。"
        for i, char in enumerate(response):
            chat_history[-1][1] += char     # 逐步更新最后一条回复
            time.sleep(0.03)
            yield chat_history              # yield 更新后的历史

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

demo.launch()

3.5 自定义组件与状态管理

State 组件

import gradio as gr

with gr.Blocks() as demo:
    gr.Markdown("# 带状态的 LLM 对话")

    chatbot = gr.Chatbot(height=400)
    msg = gr.Textbox(placeholder="输入消息...")

    # 使用 State 存储复杂状态
    conversation_state = gr.State({
        "history": [],
        "total_tokens": 0,
        "model": "gpt-4",
    })

    def respond(message: str, state: dict):
        # 更新状态
        state["history"].append({"role": "user", "content": message})
        state["total_tokens"] += len(message)

        # 生成回复
        response = f"回复:{message}"
        state["history"].append({"role": "assistant", "content": response})
        state["total_tokens"] += len(response)

        # 转换为 Chatbot 格式
        chat_history = []
        for i in range(0, len(state["history"]), 2):
            user_msg = state["history"][i]["content"]
            assistant_msg = state["history"][i+1]["content"] if i+1 < len(state["history"]) else ""
            chat_history.append([user_msg, assistant_msg])

        return chat_history, state

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

demo.launch()

条件显示/隐藏组件

import gradio as gr

with gr.Blocks() as demo:
    mode = gr.Radio(["对话模式", "补全模式"], value="对话模式", label="选择模式")

    # 对话模式组件
    with gr.Row(visible=True) as chat_row:
        chatbot = gr.Chatbot(height=400)
        chat_input = gr.Textbox(placeholder="输入对话消息...")

    # 补全模式组件
    with gr.Row(visible=False) as complete_row:
        prompt_input = gr.Textbox(label="提示词", lines=5)
        complete_output = gr.Textbox(label="补全结果", lines=10)

    def switch_mode(mode: str):
        if mode == "对话模式":
            return gr.Row(visible=True), gr.Row(visible=False)
        else:
            return gr.Row(visible=False), gr.Row(visible=True)

    mode.change(
        fn=switch_mode,
        inputs=mode,
        outputs=[chat_row, complete_row],
    )

demo.launch()

3.6 常用输入/输出组件

import gradio as gr

with gr.Blocks() as demo:
    # 文本组件
    text = gr.Textbox(label="文本", placeholder="输入文本...", lines=3, max_lines=10)
    code = gr.Code(label="代码", language="python", interactive=True)

    # 数值组件
    number = gr.Number(label="数值", value=0, precision=2)
    slider = gr.Slider(minimum=0, maximum=100, value=50, step=1, label="滑块", info="拖动选择")

    # 选择组件
    dropdown = gr.Dropdown(choices=["选项1", "选项2", "选项3"], value="选项1", label="下拉选择", multiselect=False)
    radio = gr.Radio(choices=["A", "B", "C"], value="A", label="单选")
    checkbox = gr.Checkbox(label="勾选框", value=False)
    checkbox_group = gr.CheckboxGroup(choices=["特性1", "特性2", "特性3"], label="多选")

    # 文件组件
    file = gr.File(label="文件上传", file_count="single", file_types=[".txt", ".pdf"])
    files = gr.File(label="多文件上传", file_count="multiple")

    # 图片组件
    image = gr.Image(label="图片上传", type="pil", height=300)

    # 音频组件
    audio = gr.Audio(label="音频上传", type="filepath")
    mic = gr.Audio(label="录音", sources=["microphone"], type="filepath")

    # 视频
    video = gr.Video(label="视频上传")

    # DataFrame
    dataframe = gr.Dataframe(
        headers=["列1", "列2", "列3"],
        datatype=["str", "number", "str"],
        row_count=5,
        col_count=3,
        label="数据表格",
    )

    # JSON
    json_data = gr.JSON(label="JSON 数据")

    # HTML
    html = gr.HTML(value="<h3>自定义 HTML 内容</h3>")

    # Markdown
    markdown = gr.Markdown(value="**粗体** 和 *斜体*")

demo.launch()

3.7 分享与部署

本地运行与分享

demo.launch(
    server_name="0.0.0.0",   # 监听地址,0.0.0.0 允许外部访问
    server_port=7860,         # 端口号,默认 7860
    share=True,               # 生成公网分享链接(72小时有效)
    auth=("admin", "password"),  # 用户名密码认证
    # auth=lambda u, p: u == "admin",  # 自定义认证函数
    inbrowser=True,           # 自动打开浏览器
)

挂载到 FastAPI

from fastapi import FastAPI
import gradio as gr

# 创建 Gradio 应用
def predict(text):
    return f"回复:{text}"

demo = gr.Interface(fn=predict, inputs="text", outputs="text")

# 创建 FastAPI 应用
app = FastAPI(title="LLM Service")

# 将 Gradio 挂载到 FastAPI
app = gr.mount_gradio_app(
    app,                           # FastAPI 实例
    demo,                          # Gradio 应用
    path="/ui",                    # 挂载路径,访问 /ui 打开界面
)

# 现在:
# - /docs → FastAPI Swagger 文档
# - /ui   → Gradio 交互界面
# - /api/... → FastAPI API 端点

部署到 HuggingFace Spaces

# 1. 创建 Space 仓库
# 在 https://huggingface.co/new-space 创建,选择 Gradio SDK

# 2. 项目结构
# app.py         ← Gradio 应用入口(必须是 app.py)
# requirements.txt
# README.md      ← 包含 metadata 配置

# 3. requirements.txt
gradio>=4.0.0
torch
transformers

# 4. 推送代码
git remote add space https://huggingface.co/spaces/your-name/your-space
git push space main

4. 在 LLM 开发中的典型使用场景和代码示例

场景一:LLM 对话 Demo

import gradio as gr
import time

# 模拟 LLM 推理
def mock_llm_stream(message: str, history: list, model: str, temperature: float):
    """模拟 LLM 流式推理"""
    # 构建完整 prompt
    messages = []
    for user_msg, assistant_msg in history:
        messages.append(f"User: {user_msg}")
        messages.append(f"Assistant: {assistant_msg}")
    messages.append(f"User: {message}")

    # 模拟流式生成
    full_response = f"基于模型 {model} (temp={temperature}),对你问题 '{message}' 的回复:这是一个模拟的 LLM 生成过程,实际使用时替换为真实模型调用。"
    partial = ""

    for char in full_response:
        partial += char
        time.sleep(0.03)
        yield partial

demo = gr.ChatInterface(
    fn=mock_llm_stream,
    title="LLM 对话 Demo",
    description="基于大语言模型的对话演示,支持流式输出",
    additional_inputs=[
        gr.Dropdown(
            choices=["gpt-3.5-turbo", "gpt-4", "claude-3-sonnet", "claude-3-opus"],
            value="gpt-3.5-turbo",
            label="模型选择",
        ),
        gr.Slider(
            minimum=0.0, maximum=2.0, value=0.7, step=0.1,
            label="Temperature",
            info="值越高,输出越随机",
        ),
        gr.Slider(
            minimum=100, maximum=4096, value=2048, step=100,
            label="Max Tokens",
        ),
    ],
    chatbot=gr.Chatbot(
        height=600,
        show_copy_button=True,
        bubble_full_width=False,
    ),
)

demo.launch()

场景二:模型对比评测工具

import gradio as gr
import time

def generate_model_a(prompt: str, temperature: float):
    """模型A的推理"""
    response = f"[模型A] 对 '{prompt}' 的回复:这是模型A生成的详细内容..."
    for char in response:
        time.sleep(0.02)
        yield char

def generate_model_b(prompt: str, temperature: float):
    """模型B的推理"""
    response = f"[模型B] 对 '{prompt}' 的回复:这是模型B生成的不同风格的内容..."
    for char in response:
        time.sleep(0.02)
        yield char

with gr.Blocks(title="模型对比评测") as demo:
    gr.Markdown("# LLM 模型对比评测工具\n输入提示词,同时对比两个模型的输出")

    with gr.Row():
        prompt = gr.Textbox(label="提示词", lines=5, placeholder="输入测试提示词...", scale=4)
        temperature = gr.Slider(0.0, 2.0, value=0.7, step=0.1, label="Temperature", scale=1)
        submit_btn = gr.Button("生成", variant="primary", scale=1)

    with gr.Row():
        with gr.Column():
            gr.Markdown("### 模型 A (GPT-3.5)")
            output_a = gr.Textbox(label="输出A", lines=15, show_copy_button=True)
        with gr.Column():
            gr.Markdown("### 模型 B (GPT-4)")
            output_b = gr.Textbox(label="输出B", lines=15, show_copy_button=True)

    with gr.Row():
        vote_a = gr.Button("A 更好", variant="secondary")
        vote_b = gr.Button("B 更好", variant="secondary")
        vote_tie = gr.Button("差不多")
        vote_result = gr.Textbox(label="投票结果", interactive=False)

    votes = gr.State({"A": 0, "B": 0, "tie": 0})

    submit_btn.click(fn=generate_model_a, inputs=[prompt, temperature], outputs=output_a)
    submit_btn.click(fn=generate_model_b, inputs=[prompt, temperature], outputs=output_b)

    def record_vote(choice: str, vote_counts: dict):
        vote_counts[choice] += 1
        return vote_counts, f"A: {vote_counts['A']} | B: {vote_counts['B']} | 平局: {vote_counts['tie']}"

    vote_a.click(fn=lambda v: record_vote("A", v), inputs=[votes], outputs=[votes, vote_result])
    vote_b.click(fn=lambda v: record_vote("B", v), inputs=[votes], outputs=[votes, vote_result])
    vote_tie.click(fn=lambda v: record_vote("tie", v), inputs=[votes], outputs=[votes, vote_result])

demo.launch()

场景三:RAG 交互式问答

import gradio as gr
import time

def rag_query(question: str, top_k: int, temperature: float):
    """RAG 流式问答"""
    # 模拟检索过程
    retrieved = [
        f"文档片段{i}: 与 '{question}' 相关的内容..." for i in range(top_k)
    ]

    # 模拟生成过程
    response = f"基于 {top_k} 个检索文档的回答:\n\n"
    for doc in retrieved:
        response += f"- {doc}\n"
    response += f"\n综合回答:关于 '{question}',以上文档提供了以下信息..."

    partial = ""
    for char in response:
        partial += char
        time.sleep(0.02)
        yield partial, "\n".join(retrieved)  # 同时输出回答和检索来源

with gr.Blocks(title="RAG 问答系统") as demo:
    gr.Markdown("# RAG 交互式问答系统\n输入问题,系统检索相关文档并生成回答")

    with gr.Row():
        with gr.Column(scale=2):
            question = gr.Textbox(label="问题", lines=3, placeholder="输入你的问题...")
            with gr.Row():
                top_k = gr.Slider(1, 10, value=3, step=1, label="检索文档数")
                temperature = gr.Slider(0.0, 2.0, value=0.5, step=0.1, label="Temperature")
            submit_btn = gr.Button("查询", variant="primary")

        with gr.Column(scale=1):
            sources = gr.Textbox(label="检索来源", lines=10, interactive=False)

    answer = gr.Textbox(label="回答", lines=10, show_copy_button=True)

    submit_btn.click(
        fn=rag_query,
        inputs=[question, top_k, temperature],
        outputs=[answer, sources],
    )

demo.launch()

场景四:LLM 参数调优工具

import gradio as gr
import time
import random

def generate_with_params(prompt: str, temperature: float, top_p: float, top_k: int,
                          max_tokens: int, repetition_penalty: float, num_samples: int):
    """带参数的 LLM 生成,展示参数对输出的影响"""
    results = []
    for i in range(num_samples):
        random.seed(42 + i)  # 可复现
        # 模拟不同参数下的生成结果
        if temperature < 0.3:
            text = f"[低温度({temperature})] 确定性回复:{prompt}的答案是明确的。"
        elif temperature > 1.5:
            text = f"[高温度({temperature})] 创意性回复:{prompt}引发了无限遐想,如同星辰大海般浩瀚。"
        else:
            text = f"[中温度({temperature})] 平衡回复:关于{prompt},可以从多个角度来分析。"

        results.append(f"--- Sample {i+1} ---\n{text}")

    return "\n\n".join(results)

with gr.Blocks(title="LLM 参数调优") as demo:
    gr.Markdown("# LLM 参数调优工具\n调整参数观察对生成结果的影响")

    with gr.Row():
        with gr.Column(scale=2):
            prompt = gr.Textbox(label="提示词", lines=3, value="解释人工智能")
            output = gr.Textbox(label="生成结果", lines=20, show_copy_button=True)

        with gr.Column(scale=1):
            temperature = gr.Slider(0.0, 2.0, value=0.7, step=0.1, label="Temperature",
                                     info="控制随机性")
            top_p = gr.Slider(0.0, 1.0, value=0.9, step=0.05, label="Top-p",
                               info="核采样概率阈值")
            top_k = gr.Slider(1, 100, value=50, step=1, label="Top-k",
                               info="候选token数量")
            max_tokens = gr.Slider(64, 4096, value=512, step=64, label="Max Tokens")
            repetition_penalty = gr.Slider(1.0, 2.0, value=1.0, step=0.1,
                                            label="Repetition Penalty")
            num_samples = gr.Slider(1, 5, value=3, step=1, label="生成样本数")
            generate_btn = gr.Button("生成", variant="primary")

    generate_btn.click(
        fn=generate_with_params,
        inputs=[prompt, temperature, top_p, top_k, max_tokens, repetition_penalty, num_samples],
        outputs=output,
    )

demo.launch()

5. 数学原理

5.1 Temperature 采样

LLM 的 Temperature 参数控制输出概率分布的”锐度”:

原始 logits $z_i$ 经过 Temperature 缩放后计算 softmax:

\[P(x_i) = \frac{e^{z_i / T}}{\sum_{j} e^{z_j / T}}\]
  • $T \to 0$:分布趋向 one-hot(确定性输出,选概率最高的 token)
  • $T = 1$:原始分布
  • $T \to \infty$:分布趋向均匀(完全随机)

5.2 Top-p(核采样)

Top-p 采样选择累积概率达到 $p$ 的最小 token 集合:

\[\text{选择最小集合 } S \text{ 使得 } \sum_{x_i \in S} P(x_i) \geq p\]

然后在 $S$ 内按概率重新归一化采样。$p=0.9$ 意味着只考虑概率前 90% 的 token。

5.3 Top-k 采样

Top-k 简单地限制候选 token 数量为前 $k$ 个:

\[S = \text{argsort}(P(x_i))[:k]\]

Top-k 与 Top-p 的区别:Top-k 固定候选数量,Top-p 固定概率覆盖,Top-p 更自适应。


6. 代码原理 / 架构原理

6.1 整体架构

┌──────────────────────────────────────┐
│            浏览器 (React 前端)          │
│  ┌──────────┐  ┌──────────────────┐  │
│  │ Gradio UI │  │  WebSocket Client │  │
│  └─────┬────┘  └────────┬─────────┘  │
└────────┼─────────────────┼───────────┘
         │ HTTP/WS         │
└────────┼─────────────────┼───────────┘
│        ↓                 ↓           │
│  ┌──────────┐  ┌──────────────────┐  │
│  │  FastAPI  │  │  WebSocket Server│  │
│  │  (REST)   │  │  (实时通信)        │  │
│  └─────┬────┘  └────────┬─────────┘  │
│        │                │            │
│  ┌─────┴────────────────┴─────────┐  │
│  │       Gradio Python 后端         │  │
│  │  ┌─────────┐ ┌───────────────┐  │  │
│  │  │ 事件系统  │ │ 组件管理器      │  │  │
│  │  └─────────┘ └───────────────┘  │  │
│  │  ┌─────────┐ ┌───────────────┐  │  │
│  │  │ 流式队列  │ │ 状态管理       │  │  │
│  │  └─────────┘ └───────────────┘  │  │
│  └────────────────────────────────┘  │
│             Python 进程              │
└──────────────────────────────────────┘

6.2 前后端通信机制

Gradio 使用两种通信方式:

1. HTTP REST API(普通请求):

前端 → POST /api/predict → 后端处理函数 → 返回结果

2. WebSocket(流式输出和实时更新):

前端 ←→ WebSocket /queue/join ←→ 后端事件队列
         ↑
   流式输出时,后端持续推送
   yield 的部分结果到前端

6.3 流式输出的实现原理

# Gradio 流式输出的简化实现流程:

# 1. 前端发起请求,建立 WebSocket 连接
# 2. 后端将请求放入事件队列
# 3. 后端调用处理函数(generator)
# 4. 每次 yield 时:
#    → 将部分结果放入 WebSocket 消息队列
#    → 前端接收消息,追加显示内容
# 5. 生成器结束时,标记完成

# 关键代码路径:
# gr.ChatInterface / Blocks 事件绑定 (stream=True)
#     ↓
# 处理函数(generator,yield 部分结果)
#     ↓
# EventStream → WebSocket → 前端 React 组件更新

6.4 组件渲染原理

Python 组件定义                React 前端组件
─────────────                ──────────────
gr.Textbox          →        <TextArea>
gr.Chatbot          →        <ChatBot>
gr.Slider           →        <Slider>
gr.Image            →        <ImageUpload>
gr.Dropdown         →        <Dropdown>
gr.Button           →        <Button>

映射关系:
1. Python 定义组件 → 生成 JSON Schema
2. 前端根据 Schema 渲染 React 组件
3. 用户交互 → 发送事件到后端
4. 后端处理 → 返回更新值 → 前端重新渲染

6.5 mount_gradio_app 原理

# mount_gradio_app 的简化实现
def mount_gradio_app(app, blocks, path):
    """将 Gradio Blocks 挂载到 FastAPI/Starlette 应用"""
    # 1. Gradio 底层也是基于 Starlette 的 ASGI 应用
    # 2. 使用 Starlette 的 Mount 中间件将 Gradio 路由
    #    挂载到指定 path 下
    # 3. 路由结构:
    #    /path/          → Gradio UI 页面
    #    /path/api/      → Gradio REST API
    #    /path/queue/    → Gradio WebSocket 队列
    #    /path/assets/   → 静态资源
    from starlette.routing import Mount
    app.routes.append(Mount(path, app=blocks.app))
    return app

7. 常见注意事项和最佳实践

7.1 流式输出最佳实践

# 1. 流式函数必须使用 yield,不能 return
# 正确:
def stream_fn(message, history):
    for token in generate_tokens(message):
        yield token  # 逐步输出

# 错误:return 会直接结束,不会流式输出
def wrong_fn(message, history):
    return full_response

# 2. ChatInterface 中,yield 返回的是当前累积的完整文本
# Gradio 会自动替换输出内容(而非追加)
def chat_stream(message, history):
    partial = ""
    for token in generate_tokens(message):
        partial += token
        yield partial  # 每次返回完整累积文本

# 3. Blocks 中的 Chatbot 流式需要更新历史
def blocks_stream(message, chat_history):
    chat_history.append([message, ""])
    for token in generate_tokens(message):
        chat_history[-1][1] += token
        yield chat_history  # yield 更新后的完整历史

7.2 性能优化

# 1. 使用 queue 限制并发
demo = gr.Interface(fn=slow_inference, inputs="text", outputs="text")
demo.queue(
    max_size=20,          # 最大排队请求数
    default_concurrency_limit=2,  # 并发处理数
)

# 2. 避免在处理函数中加载模型(应全局加载)
# 错误:每次请求都加载模型
def predict(text):
    model = load_model()  # 非常慢!
    return model.predict(text)

# 正确:全局加载一次
model = load_model()  # 应用启动时加载

def predict(text):
    return model.predict(text)

# 3. 大输出使用流式,减少首字节时间
# 4. 使用 cache_examples 缓存示例结果
demo = gr.Interface(
    fn=predict,
    inputs="text",
    outputs="text",
    examples=[["示例1"], ["示例2"]],
    cache_examples=True,  # 启动时预计算示例结果并缓存
)

7.3 安全注意事项

# 1. 生产环境禁用 share
demo.launch(share=False)  # share=True 仅用于临时分享

# 2. 添加认证
demo.launch(auth=("username", "password"))

# 3. 自定义认证
def auth_fn(username, password):
    # 可对接数据库验证
    return username in ALLOWED_USERS and verify_password(username, password)

demo.launch(auth=auth_fn)

# 4. 防止 SSRF — 不要直接将用户输入作为 URL 请求
# 5. 限制文件上传大小和类型
gr.File(file_types=[".txt", ".pdf"], label="文档上传")  # 限制文件类型

7.4 部署最佳实践

# 1. 生产环境配置
demo.launch(
    server_name="0.0.0.0",
    server_port=7860,
    share=False,                 # 禁用公网分享
    max_threads=4,               # 最大线程数
)

# 2. 使用 queue 管理并发
demo.queue(
    max_size=50,                 # 排队上限
    default_concurrency_limit=4, # 并发限制
)

# 3. Docker 部署
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 7860

CMD ["python", "app.py"]
# docker-compose.yml
version: "3"
services:
  gradio-app:
    build: .
    ports:
      - "7860:7860"
    environment:
      - GRADIO_SERVER_NAME=0.0.0.0
      - GRADIO_SERVER_PORT=7860
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]  # GPU 支持
    restart: unless-stopped

7.5 调试技巧

# 1. 启用调试模式
demo.launch(debug=True)  # 显示详细错误信息

# 2. 查看事件日志
import logging
logging.basicConfig(level=logging.INFO)

# 3. 测试组件事件绑定
# 使用 Gradio 的 Python 客户端测试
from gradio_client import Client

client = Client("http://localhost:7860/")
result = client.predict("测试输入", api_name="/predict")
print(result)

# 4. 处理超时
demo.queue().launch(
    max_threads=4,
    # 长时间推理需要增大超时
)

7.6 常见问题

问题原因解决方案
流式输出不生效函数未使用 yield改为生成器函数,使用 yield
ChatInterface 不更新历史返回格式不对返回字符串,Gradio 自动管理历史
界面加载慢模型在请求时加载全局预加载模型
share 链接无法访问72小时后过期使用正式部署方式
GPU 内存不足多用户并发推理使用 queue 限制并发数
上传文件处理失败文件路径或类型问题检查 type 参数(”filepath”/”bytes”/”pil”)
事件不触发组件未正确绑定检查 inputs/outputs 参数和组件变量

7.7 与其他框架的集成

# 1. Gradio + FastAPI
from fastapi import FastAPI
import gradio as gr

app = FastAPI()

@app.get("/api/models")
async def list_models():
    return {"models": ["gpt-3.5", "gpt-4"]}

demo = gr.Interface(fn=predict, inputs="text", outputs="text")
app = gr.mount_gradio_app(app, demo, path="/ui")

# 2. Gradio + LangChain
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage

llm = ChatOpenAI(model="gpt-3.5-turbo", streaming=True)

def langchain_chat(message, history):
    # 将 Gradio 历史转为 LangChain 消息格式
    messages = [HumanMessage(content=msg) for msg, _ in history]
    messages.append(HumanMessage(content=message))

    partial = ""
    for chunk in llm.stream(messages):
        partial += chunk.content
        yield partial

demo = gr.ChatInterface(fn=langchain_chat)
demo.launch()

参考链接

  • Gradio 官方文档:https://www.gradio.app/docs/
  • Gradio GitHub:https://github.com/gradio-app/gradio
  • Gradio Playground:https://www.gradio.app/playground
  • HuggingFace Spaces:https://huggingface.co/spaces
  • Gradio Client 文档:https://www.gradio.app/docs/python-client/client