Build an Agentic Browser Research Workflow with LlamaIndex

Published • guides
OpenAI

This guide is adapted from LlamaIndex’s agent_workflow_research_assistant.ipynb, available in the LlamaIndex repository under the MIT licence. It shows how to coordinate retrieval, browser automation, and structured extraction into a single reproducible research workflow.

The technique combines three tool families under one LlamaIndex AgentWorkflow: DuckDuckGo search discovers candidate resources, Playwright navigates to pages and clicks through links, and AgentQL extracts metadata such as titles, authors, publication dates, journal names, volume/issue numbers, and abstracts. Instead of writing brittle per-site scrapers, you give an OpenAI model a constrained toolset and a research goal, then stream its reasoning and tool calls as it hunts for extractable sources.

This matters for engineers building research assistants, literature-review helpers, due-diligence tools, or any workflow that needs to move from a natural-language question to structured source data. Because the model is hosted through OpenAI and the browser runs in a Colab or local environment, there is no local GPU requirement. The notebook does not publish cost or timing figures. The main operational concern is API usage: each search, navigation, and extraction step can become a model tool call, so token consumption is driven by how many pages the agent visits and how many retries it makes.

Prerequisites

The source notebook establishes these requirements:

  • A Google Colab environment, or a local notebook environment adapted from Colab’s secrets handling.
  • An OpenAI API key.
  • An AgentQL API key.
  • Python packages llama-index, llama-index-tools-agentql, llama-index-tools-playwright, and llama-index-tools-duckduckgo.
  • Playwright’s Chromium browser installed through playwright install.
  • nest_asyncio, imported in the source. If it is not already present in your environment, install it before running the async setup.

The notebook stores both keys in Colab secrets and reads them with google.colab.userdata. If you run this outside Colab, replace that access with environment variables, but the rest of the workflow stays the same.

Step 1: Install the LlamaIndex toolchain and Playwright

The first step installs the core LlamaIndex package and the three tool integrations, then downloads the Playwright browser binary. Run the browser install after the Python packages; otherwise Playwright may not find its command-line entrypoint.

%pip install llama-index
%pip install llama-index-tools-agentql
%pip install llama-index-tools-playwright
%pip install llama-index-tools-duckduckgo

!playwright install

Step 2: Load API credentials from Colab secrets

The notebook expects AGENTQL_API_KEY and OPENAI_API_KEY to be stored in Google Colab’s secrets manager. The source copies them into environment variables so the LlamaIndex integrations can read them.

import os

from google.colab import userdata

os.environ["AGENTQL_API_KEY"] = userdata.get("AGENTQL_API_KEY")
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")

Keep these keys out of version control. In local environments, read from a .env file or your shell instead of userdata.

Step 3: Enable nested event loops

AgentQL’s async interface needs nested event loop support in notebook environments. The source applies nest_asyncio before creating any async browser or workflow objects.

import nest_asyncio

nest_asyncio.apply()

Step 4: Create the Playwright browser and select tools

The workflow needs a browser session, but exposing every Playwright tool to the model would make the agent’s action space unnecessarily large. The source creates a headless async browser and filters the full Playwright tool list down to three navigation actions: click, get_current_page, and navigate_to.

from llama_index.tools.playwright.base import PlaywrightToolSpec

async_browser = await PlaywrightToolSpec.create_async_playwright_browser(
    headless=True
)

playwright_tool = PlaywrightToolSpec(async_browser=async_browser)
playwright_tool_list = playwright_tool.to_tool_list()
playwright_agent_tool_list = [
    tool
    for tool in playwright_tool_list
    if tool.metadata.name in ["click", "get_current_page", "navigate_to"]
]

Note that this cell uses top-level await, which works in a notebook but not in a normal Python file. If you move this into a script, wrap the browser creation in an async function or use an event loop.

Step 5: Add AgentQL and DuckDuckGo search tools

The source imports AgentQL’s browser extraction tools and DuckDuckGo’s search integration. It filters DuckDuckGo’s tool list down to the full search tool, then creates an AgentQL browser spec that reuses the same async browser.

from llama_index.tools.agentql import AgentQLBrowserToolSpec
from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec

duckduckgo_search_tool = [
    tool
    for tool in DuckDuckGoSearchToolSpec().to_tool_list()
    if tool.metadata.name == "duckduckgo_full_search"
]

agentql_browser_tool = AgentQLBrowserToolSpec(async_browser=async_browser)

These three tool groups have distinct responsibilities and failure modes:

ToolRole in the workflowWhat it gives the agentMain fragility
DuckDuckGo full searchDiscoveryURLs and snippets for a research queryResults can be irrelevant or low-quality; no scholarly filter is applied.
Playwright click, navigate_to, get_current_pageNavigationAbility to open and move through pagesSelectors and page state can be brittle; headless detection may block some sites.
AgentQL browser toolsExtractionStructured fields from the current pageExtraction can fail on bot walls, cookie banners, or unstructured page layouts.

Step 6: Build the AgentWorkflow

AgentWorkflow.from_tools_or_functions combines the selected Playwright tools, all AgentQL browser tools, and the filtered DuckDuckGo search tool into one coordinator. The source uses gpt-4o and a system prompt that frames the model as a browser-automation and extraction expert.

from llama_index.llms.openai import OpenAI
from llama_index.core.agent.workflow import AgentWorkflow

llm = OpenAI(model="gpt-4o")

workflow = AgentWorkflow.from_tools_or_functions(
    playwright_agent_tool_list
    + agentql_browser_tool.to_tool_list()
    + duckduckgo_search_tool,
    llm=llm,
    system_prompt="You are an expert that can do browser automation, data extraction and text summarization for finding and extracting data from research resources.",
)

The system prompt matters here because the model must decide when to search, when to navigate, and when to extract. A prompt that is too vague can produce circular behaviour; one that is too restrictive can stop the agent from using all three tool groups.

Step 7: Run the research task and stream events

The final cell sends a research goal to the workflow and streams AgentStream events. The prompt asks the agent to search for resources about exercise and stress, visit each result, extract publication metadata, and continue until two resources have been successfully extracted.

from llama_index.core.agent.workflow import (
    AgentStream,
)

handler = workflow.run(
    user_msg="""
    Use DuckDuckGoSearch to find URL resources on the web that are relevant to the research topic: What is the relationship between exercise and stress levels?
    Go through each resource found. For each different resource, use Playwright to click on link to the resource, then use AgentQL to extract information, including the name of the resource, author name(s), link to the resource, publishing date, journal name, volume number, issue number, and the abstract.
    Find more resources until there are two different resources that can be successfully extracted from.
    """
)

async for event in handler.stream_events():
    if isinstance(event, AgentStream):
        print(event.delta, end="", flush=True)

Replace the research topic inside user_msg to adapt this to a different domain. Keep the stop condition explicit — “two different resources” — or the agent may keep searching indefinitely.

What to watch out for

The notebook works, but it hides several practical risks.

Top-level await and script portability. The source is designed for a notebook. If you move it into a regular Python file, async_browser = await ... will fail. Wrap the browser creation and workflow execution in an async main() function, and keep the event-loop policy consistent.

Tool-name filtering is brittle. The code selects tools by exact metadata.name values such as duckduckgo_full_search and the Playwright trio. If the LlamaIndex tool packages rename these tools, the list can silently become empty or wrong. After upgrading packages, print the available tool names and compare them with the filter.

Playwright browser lifecycle is not managed here. The notebook creates one browser instance. If the agent fails or the cell is re-run, you can end up with stale pages, multiple browser processes, or memory growth. For a robust version, close the browser in a finally block or use a context manager around the workflow run.

AgentQL extraction can fail even on reachable pages. AgentQL needs access to a page that has loaded meaningfully. Sites with heavy bot protection, login walls, cookie overlays, or page structures that do not expose the requested fields may produce partial or empty results. The agent can also mistake a search result snippet for the actual article page.

The model can loop if the stop condition is ambiguous. The prompt asks for two successfully extracted resources, but the model may revisit the same resource, misclassify a failed extraction as success, or chase irrelevant links. Use a timeout or a maximum iteration cap for latency and cost control.

Headless browser flakiness is real. Some sites block headless Chromium. If click or get_current_page returns nothing, the agent may hallucinate page contents or fail silently. Run with a headless browser only for prototyping; production research assistants usually need retries, visible rendering, or proxies.

Cost scales with model calls. Every search, click, and extraction is a tool call that expands the context window. A short question can generate a long reasoning loop. The source does not measure token consumption; log your own per-run totals, consider a cheaper model for exploration, reserve gpt-4o for final extraction, and keep the stop condition explicit.

Where to go next

This workflow is a foundation, not a finished product. You can add a vector index for domain-specific retrieval before the web-search step, replace DuckDuckGo with a scholarly API for higher-quality sources, or persist the extracted metadata to a database. When you move beyond the notebook, consider managed agent runtimes such as the DigitalOcean managed agents micro-VM runtime to handle browser sandboxing and action-gateway security. For production, also add explicit timeout handling, browser cleanup, and evaluation checks that verify each extracted field rather than trusting the agent’s self-report.

Frequently asked questions

What is LlamaIndex AgentWorkflow used for in this research agent?

It coordinates multiple tools under one LLM-driven loop. In the notebook, AgentWorkflow.from_tools_or_functions combines Playwright navigation tools, AgentQL extraction tools, and a DuckDuckGo search tool behind an OpenAI gpt-4o model. The agent decides when to search, click, navigate, and extract until two resources have been processed.

Do I need a local GPU to run this LlamaIndex research workflow?

No. The OpenAI model runs via API, and the Playwright Chromium browser runs in a Colab or local environment. The main local requirement is installing the Python packages and the Chromium browser binary. API usage and token consumption depend on your query and how many tool calls the agent makes.

Can I run this code outside Google Colab?

Yes, but you must replace google.colab.userdata with environment variables or a .env file. The code also uses top-level await, so a plain Python script needs the async browser creation and workflow execution wrapped in an async function. The rest of the tool setup is unchanged.

Why does the code filter the Playwright and DuckDuckGo tools?

Filtering reduces the agent's action space to the operations the task actually uses. Playwright is limited to click, get_current_page, and navigate_to, while DuckDuckGo is limited to duckduckgo_full_search. This helps avoid unnecessary or confusing tool choices during agent reasoning.

What should I do if AgentQL returns empty or partial extraction results?

Check whether the page actually loaded and whether the site uses bot walls, login walls, cookie overlays, or layouts that hide the requested fields. The agent can also confuse a search result snippet with the article page, so use a timeout or iteration cap and verify the extracted fields separately. For production, consider retries or visible rendering through a managed browser runtime.

How much does this workflow cost to run?

The source notebook does not publish pricing or token measurements. The model is hosted through OpenAI and each search, navigation, and extraction action can become a tool call that expands context, so repeated long runs can consume significant tokens. Monitor token usage per run and consider cheaper models or tighter stop conditions if cost is a concern.

Free interactive tools for the decisions this piece raises.

Related Guides