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.
The Workflow
Section titled “The Workflow”┌─────────────┐ ┌─────────────┐ ┌─────────────┐│ Intent │────▶│ LLM │────▶│ Python ││ + Docs URL │ │ Generation │ │ Code │└─────────────┘ └─────────────┘ └─────────────┘ │ ▼┌─────────────┐ ┌─────────────┐ ┌─────────────┐│ Result │◀────│ Execute │◀────│ Cache ││ │ │ │ │ & Load │└─────────────┘ └─────────────┘ └─────────────┘Step 1: Define Intent
Section titled “Step 1: Define Intent”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")Step 2: Fetch Documentation (Optional)
Section titled “Step 2: Fetch Documentation (Optional)”If you provide a docs_url, Anvil fetches the latest documentation using FireCrawl. This gives the LLM accurate, up-to-date API information.
Step 3: Generate Code
Section titled “Step 3: Generate Code”The LLM generates a complete Python function based on:
- Your intent description
- Fetched documentation (if provided)
- Input parameter definitions
- Best practices for error handling
Step 4: Cache & Save
Section titled “Step 4: Cache & Save”Generated code is saved to disk:
- File:
anvil_tools/{tool_name}.py - Metadata stored in
tool_registry.json - Intent hash computed for change detection
Step 5: Load & Execute
Section titled “Step 5: Load & Execute”The code is loaded as a Python module and executed with your arguments.
Caching Strategy
Section titled “Caching Strategy”Anvil uses smart caching to avoid unnecessary regeneration:
| Condition | Action |
|---|---|
| Tool doesn’t exist | Generate new code |
| Intent unchanged | Use cached code |
| Intent changed | Regenerate code |
| Tool manually edited | Skip regeneration (ejected) |
| Execution failed | Attempt self-healing |
Intent Hashing
Section titled “Intent Hashing”Anvil computes a hash of your intent string. If the hash matches the cached version, no regeneration occurs:
# First call - generates codeanvil.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")File Structure
Section titled “File Structure”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 codeGenerated Code Format
Section titled “Generated Code Format”Each generated tool file includes:
# ---# 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
Tool Registry
Section titled “Tool Registry”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" }}