Skip to content

How It Works

Anvil bridges the gap between what you want a tool to do (intent) and the actual implementation (code). Here’s how it works under the hood.

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Intent │────▶│ LLM │────▶│ Python │
│ + Docs URL │ │ Generation │ │ Code │
└─────────────┘ └─────────────┘ └─────────────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Result │◀────│ Execute │◀────│ Cache │
│ │ │ │ │ & Load │
└─────────────┘ └─────────────┘ └─────────────┘

You describe what you want the tool to do:

tool = anvil.use_tool(
name="search_github",
intent="Search GitHub repositories by query",
docs_url="https://docs.github.com/en/rest/search"
)

If you provide a docs_url, Anvil fetches the latest documentation using FireCrawl. This gives the LLM accurate, up-to-date API information.

The LLM generates a complete Python function based on:

  • Your intent description
  • Fetched documentation (if provided)
  • Input parameter definitions
  • Best practices for error handling

Generated code is saved to disk:

  • File: anvil_tools/{tool_name}.py
  • Metadata stored in tool_registry.json
  • Intent hash computed for change detection

The code is loaded as a Python module and executed with your arguments.

Anvil uses smart caching to avoid unnecessary regeneration:

ConditionAction
Tool doesn’t existGenerate new code
Intent unchangedUse cached code
Intent changedRegenerate code
Tool manually editedSkip regeneration (ejected)
Execution failedAttempt self-healing

Anvil computes a hash of your intent string. If the hash matches the cached version, no regeneration occurs:

# First call - generates code
anvil.use_tool(name="search", intent="Search the web")
# Second call - uses cache (same intent)
anvil.use_tool(name="search", intent="Search the web")
# Third call - regenerates (different intent)
anvil.use_tool(name="search", intent="Search the web with filters")

After using Anvil, your project looks like:

my-project/
├── .env # API keys
├── .gitignore # Protects .env
├── anvil_tools/
│ ├── __init__.py
│ ├── tool_registry.json # Metadata for all tools
│ ├── search_github.py # Generated tool
│ └── get_weather.py # Generated tool
└── main.py # Your code

Each generated tool file includes:

1.0
# ---
# ANVIL-MANAGED: true
# INTENT-HASH: abc123...
# ---
import requests
def run(query: str, **kwargs) -> dict:
"""Search GitHub repositories by query."""
response = requests.get(
"https://api.github.com/search/repositories",
params={"q": query}
)
response.raise_for_status()
return response.json()

The header contains:

  • ANVIL-MANAGED: Whether Anvil can regenerate this file
  • INTENT-HASH: Hash of the original intent
  • VERSION: Increments on each regeneration

Metadata is stored in tool_registry.json:

{
"search_github": {
"name": "search_github",
"intent": "Search GitHub repositories by query",
"docs_url": "https://docs.github.com/en/rest/search",
"hash": "abc123...",
"version": "1.0",
"status": "active",
"created_at": "2025-01-18T10:00:00Z",
"last_generated": "2025-01-18T10:00:00Z"
}
}