| π Documentation Index | β¬ οΈ Back to Main README |
The Hierarchical Planner is an intelligent 3-level decision tree that reduces LLM token usage by ~87% through iterative, context-aware data reduction.
Traditional approach sent 53KB+ of DOM data in a single request:
Break communication into 3 interactive levels:
LEVEL 1 (STRATEGIC): ~2KB outline
ββ Question: "What's on the page? What details do you need?"
ββ Data: 2-level outline without field details
ββ LLM decides:
ββ decision: "use_form" | "extract_articles" | "complete"
ββ need_details: ["forms[0].fields"] | null
LEVEL 2 (TACTICAL): ~5KB (only if LLM requested)
ββ Question: "What tool to call?"
ββ Data: EXACTLY what LLM requested in need_details
ββ Decision: tool_name="form.fill", args={...}
LEVEL 3 (EXECUTION): 0 KB (direct)
ββ Execute: form.fill(name="John Doe", email="john@example.com")
FAST PATH: If LLM sets need_details=null in Level 1
ββ Skip Level 2, parse args directly from instruction
# Enable/disable hierarchical planner (default: true)
CURLLM_HIERARCHICAL_PLANNER=true
# Auto-optimization threshold (default: 25000 chars)
# If page_context > this size, automatically use hierarchical planner
CURLLM_HIERARCHICAL_PLANNER_CHARS=25000
# LLM timeout for each request (default: 300s)
CURLLM_LLM_TIMEOUT=300
# Disable for specific request
curllm -d '{
"instruction": "Fill form...",
"params": {
"hierarchical_planner": false
}
}'
# Adjust threshold per-request
export CURLLM_HIERARCHICAL_PLANNER_CHARS=30000
Hierarchical planner activates when:
page_context size > 25KB (configurable)What LLM receives:
{
"title": "Contact β’ prototypowanie.pl",
"url": "https://www.prototypowanie.pl/kontakt/",
"page_type": "form",
"form_outline": [{
"id": "forminator-module-5635",
"field_count": 5,
"field_types": {"text": 2, "email": 1, "textarea": 1}
}],
"headings": ["Contact", "DevOps Engineer"]
}
LLM responds:
{
"decision": "use_form",
"need_details": ["forms[0].fields"],
"reason": "Need field names to map instruction values"
}
Data reduction: 51,024 β 2,156 chars (95.8% less!)
What LLM receives (only requested details):
{
"forms": [{
"id": "forminator-module-5635",
"fields": [
{"name": "name-1", "type": "text", "required": true},
{"name": "email-1", "type": "email", "required": true},
{"name": "phone-1", "type": "text"},
{"name": "textarea-1", "type": "textarea", "required": true}
]
}]
}
LLM responds:
{
"tool_name": "form.fill",
"args": {
"name": "John Doe",
"email": "john@example.com",
"message": "Hello"
},
"reason": "Filling contact form with user-provided values"
}
System directly executes form.fill() with parsed args. No LLM involved.
π Level 1: LLM sees form_outline
ββ "I need forms[0].fields to proceed"
π Level 2: System sends ONLY fields for form[0]
ββ LLM: "Call form.fill(...)"
β
Level 3: Execute form.fill
π Level 1: LLM sees form_outline
ββ "I have enough info, need_details=null"
β‘ SKIP Level 2 entirely!
β
Level 3: Parse instruction directly β form.fill
π Level 1: LLM sees page summary
ββ "This is an article list, not a form"
β Fallback to standard planner
| Metric | Before | After | Improvement |
|---|---|---|---|
| Data per request | 53KB | 2KB + 5KB | 87% less |
| LLM time (qwen2.5:14b) | 40-60s | 15-20s | 60% faster |
| Tokens per request | ~13,000 | ~2,000 | 85% less |
| Cost per request | $0.013 | $0.002 | 85% cheaper |
curllm --model qwen2.5:14b \
"https://example.com/contact" \
-d '{"instruction":"Fill contact form: name=John Doe, email=john@example.com"}'
Log output:
π― Using hierarchical planner (3-level decision tree)
Original context: 51,024 chars (threshold: 25,000)
π Level 1 (Strategic): 1,247 chars prompt, 2,156 chars context (95.8% reduction)
β Strategic decision: use_form
LLM requests details: ["forms[0].fields"]
π Level 2 (Tactical): 1,543 chars
β Tactical decision: form.fill
β Hierarchical planner generated action
# LLM skips Level 2 when it has enough info
curllm --model qwen2.5:14b \
"https://example.com/simple-form" \
-d '{"instruction":"Fill form: name=John, email=john@example.com"}'
Log output:
π― Using hierarchical planner (3-level decision tree)
π Level 1 (Strategic): 1,247 chars prompt
β Strategic decision: use_form
LLM has enough info, proceeding without Level 2
β‘ Skipping Level 2 - LLM has sufficient info
should_use_hierarchical_planner(instruction, page_context)
True if optimization should be appliedextract_strategic_context(page_context)
fields from forms, replaces with form_outline{"text": 2, "email": 1}extract_requested_details(page_context, need_details)
["forms[0].fields", "interactive"]forms[N].fields, interactive, headingshierarchical_plan(instruction, page_context, llm, run_logger)
need_details=nullCheck logs for:
π― Using hierarchical planner (3-level decision tree)
If missing:
page_context < 25KB
export CURLLM_HIERARCHICAL_PLANNER_CHARS=10000CURLLM_HIERARCHICAL_PLANNER=trueIssue: Strategic/Tactical response not valid JSON
Solution:
qwen2.5:14b or llama3.2:latestCURLLM_LLM_TIMEOUT=600Check:
./curllm --status-servicesGPU Status outputCURLLM_LLM_TIMEOUT >= 300Extend extract_requested_details() to support new paths:
# In hierarchical_planner.py
elif path == "custom_data":
result["custom_data"] = page_context.get("custom_data", [])
Different models may need adjusted prompts. Edit generate_strategic_prompt():
# For smaller models
prompt = f"""Simple question: Is this a form page? Reply: yes/no"""
# For larger models
prompt = f"""Analyze this page and determine..."""
| Use Case | Recommended Threshold |
|---|---|
| Forms only | 20,000 chars |
| Mixed content | 25,000 chars (default) |
| Always optimize | 10,000 chars |
| Disable auto | 999,999 chars |
| Model | Best For | Avg Speed |
|---|---|---|
qwen2.5:7b |
Fast, simple forms | 10-15s |
qwen2.5:14b |
Recommended | 15-20s |
llama3.2:11b |
Complex decisions | 20-25s |
qwen3:30b |
High accuracy | 30-40s |
| π Documentation Index | β¬οΈ Back to Top | Main README |