Skip to main content

Overview

This example demonstrates using Monty to generate and execute web scraping code. An LLM generates Python code to navigate websites with Playwright and parse HTML with BeautifulSoup, extracting structured pricing data from model provider documentation.
This example uses Pydantic AI to generate code, but avoids the built-in CodeExecutionToolset to showcase Monty features not yet available in Pydantic AI, such as iterative execution and type checking.

Key Features

  • Type-safe external functions: BeautifulSoup and Playwright APIs exposed as typed dataclasses
  • Iterative agent loop: LLM generates code, Monty executes it, results feed back to LLM
  • Type checking: Generated code is validated against stubs before execution
  • Browser automation: Headless Playwright integration for dynamic web pages

Architecture

1

Generate Type Stubs

Use stubgen to create type stubs for external functions, giving the LLM precise type information.
2

LLM Generates Code

The agent receives instructions about available functions and generates Python code to scrape the target site.
3

Type Check & Execute

Monty validates the code against stubs, then executes it with access to open_page(), beautiful_soup(), and record_model_info().
4

Process Results

If execution succeeds, results are recorded. If it fails, error messages feed back to the LLM for correction.

Example Code Structure

Main Loop

""" scrape_agent = Agent(‘gateway/anthropic:claude-sonnet-4-5’, instructions=instrunctions)

Generated Code Example

Here’s the kind of code Claude Sonnet 4.5 generates for this task:

External Functions API

The Page object provides methods like:
  • go_to(url) - Navigate to a new URL
  • click(selector) - Click an element
  • fill(selector, value) - Fill a form field
  • get_text(selector) - Extract text content
  • screenshot() - Take a screenshot

HTML Parsing

The Tag object mirrors BeautifulSoup’s API:
  • find(name, attrs) - Find first matching tag
  • find_all(name, attrs, limit) - Find all matching tags
  • select(selector) - CSS selector query
  • get_text(separator, strip) - Extract text content
  • children() - Get direct children
The HTML returned from web pages can be very large. Always process it with beautiful_soup() to extract only the data you need, rather than returning full HTML to the LLM.

Running the Example

The example scrapes pricing data from:
  • OpenAI’s pricing page
  • Anthropic’s pricing page
  • Groq’s pricing page

Key Takeaways

  1. Code > Tool Calls: Writing a loop to process tables is more natural than sequential tool calls
  2. Type Safety: Type stubs catch errors before execution
  3. Iterative Refinement: Failed executions feed errors back to the LLM for correction
  4. Resource Efficiency: HTML parsing happens in the sandbox, keeping tokens out of context

Next Steps