Build Your Own 'Deep Search' (Part 2): Creating an Interactive Web UI with Gradio

pouya ghpouya gh
4 min read

In Part 1, we built the powerful "brain" of our research system—a team of AI agents capable of planning, searching, and writing. However, running it required executing Python code directly. To make our system truly accessible and user-friendly, we'll now build a clean, simple web interface using Gradio.

Gradio is a fantastic open-source Python library that makes it incredibly easy to create UIs for machine learning models and data science workflows. With just a few lines of code, we can wrap our agentic system in an interactive web app.

1. The Gradio Orchestration Function

First, we need a function that can manage the entire research process and communicate its progress back to the user interface. Instead of a regular function that returns a single value at the end, we will create an asynchronous generator using async def and yield. This allows our function to "yield" status updates in real-time as the process unfolds.

This is crucial for a good user experience, as the research process can take a minute or two. The user will see live updates instead of a frozen screen.

async def run_research_gradio(query: str):
    """
    Orchestrates the research process and yields status updates and the final report
    for the Gradio interface.
    """
    try:
        # Step 1: Planning
        yield "Starting research... (1/4)"
        # We re-use the functions from Part 1
        search_plan = await plan_searches(query)

        # Step 2: Searching
        yield "Searches planned, starting to search... (2/4)"
        search_results = await perform_searches(search_plan)
        # Filter out any None results from failed searches
        search_results = [res for res in search_results if res is not None]

        # Step 3: Writing
        yield "Searches complete, writing report... (3/4)"
        report_data = await write_report(query, search_results)

        # Step 4: Final Output
        yield "Research complete! Generating final report... (4/4)"
        # Instead of 'return', we yield the final markdown report
        yield report_data.markdown_report

    except Exception as e:
        # If anything goes wrong, show the user a clear error message.
        yield f"An error occurred during research: {e}"
        # Yield an empty string to clear the output area.
        yield ""

Explanation:

  • yield Keyword: Each time yield is called, it sends a piece of data (in our case, a status string) to the Gradio interface, which immediately displays it. The function then pauses until the next step is ready.

  • Reusing Logic: We don't need to rewrite our agent logic. We simply call the plan_searches, perform_searches, and write_report functions we created in Part 1.

  • Error Handling: The try...except block ensures that if any part of the process fails, the user is notified with a helpful error message instead of the app crashing.

2. Designing the Gradio UI

Now for the fun part: designing the interface itself. Gradio's Blocks API gives us full control over the layout of our components. We will create a simple layout with a title, an input box for the query, a button to start the research, and an output area for the report.

import gradio as gr

with gr.Blocks(theme=gr.themes.Default(primary_hue="sky")) as demo:
    gr.Markdown("# Deep Research Agent")
    gr.Markdown("Enter a topic below to generate a comprehensive research report.")

    # Input component for the user's query
    query_input = gr.Textbox(
        label="Research Topic",
        placeholder="e.g., 'Future of AI in healthcare by 2030'"
    )
    # The main button to trigger the research
    run_button = gr.Button("Start Research", variant="primary")

    # Output component to display status and the final report
    report_output = gr.Markdown(label="Research Report")

    # Connect the button click to our orchestration function
    run_button.click(
        fn=run_research_gradio,
        inputs=query_input,
        outputs=report_output,
    )

    # Also allow the user to press Enter to submit
    query_input.submit(
        fn=run_research_gradio,
        inputs=query_input,
        outputs=report_output,
    )

Explanation:

  • gr.Blocks: This creates a container for our UI. We've added a simple sky-blue theme for a nicer look.

  • gr.Markdown: Used to display static text like the title and the final report (which is in Markdown format).

  • gr.Textbox: A simple text input field.

  • gr.Button: The clickable button. variant="primary" makes it stand out.

  • .click() and .submit(): These are the event listeners. They tell Gradio: "When this component is interacted with (clicked or submitted), call the function specified in fn, take the value from the inputs component, and stream the results to the outputs component."

3. Launching the App

The final step is to launch our application. A single line of code is all it takes.

# Launch the Gradio web server
demo.launch(inbrowser=True, share=False)

When you run this code, Gradio will start a local web server and automatically open the interface in a new browser tab for you. Now you can interact with your powerful, multi-agent research system through a clean and simple UI!

0
Subscribe to my newsletter

Read articles from pouya gh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

pouya gh
pouya gh