Skip to content

Commit 037f118

Browse files
author
SentienceDEV
committed
improve agent with retries
1 parent c653d9a commit 037f118

File tree

10 files changed

+3454
-45
lines changed

10 files changed

+3454
-45
lines changed

docs/PLANNER_EXECUTOR_AGENT.md

Lines changed: 1063 additions & 0 deletions
Large diffs are not rendered by default.

examples/planner-executor/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
This directory contains examples for the `PlannerExecutorAgent`, a two-tier agent
44
architecture with separate Planner (7B+) and Executor (3B-7B) models.
55

6+
> **See also**: [Full User Manual](../../docs/PLANNER_EXECUTOR_AGENT.md) for comprehensive documentation.
7+
68
## Examples
79

810
| File | Description |
911
|------|-------------|
1012
| `minimal_example.py` | Basic usage with OpenAI models |
13+
| `automation_task_example.py` | Using AutomationTask for flexible task definition |
14+
| `captcha_example.py` | CAPTCHA handling with different solvers |
1115
| `local_models_example.py` | Using local HuggingFace/MLX models |
1216
| `custom_config_example.py` | Custom configuration (escalation, retry, vision) |
1317
| `tracing_example.py` | Full tracing integration for Predicate Studio |
@@ -139,3 +143,57 @@ agent = PlannerExecutorAgent(
139143

140144
tracer.close() # Upload trace to Studio
141145
```
146+
147+
## AutomationTask
148+
149+
Use `AutomationTask` for flexible task definition with built-in recovery:
150+
151+
```python
152+
from predicate.agents import AutomationTask, TaskCategory
153+
154+
# Basic task
155+
task = AutomationTask(
156+
task_id="search-products",
157+
starting_url="https://amazon.com",
158+
task="Search for laptops and add the first result to cart",
159+
category=TaskCategory.TRANSACTION,
160+
enable_recovery=True,
161+
)
162+
163+
# Add success criteria
164+
task = task.with_success_criteria(
165+
{"predicate": "url_contains", "args": ["/cart"]},
166+
{"predicate": "exists", "args": [".cart-item"]},
167+
)
168+
169+
result = await agent.run(runtime, task)
170+
```
171+
172+
## CAPTCHA Handling
173+
174+
Configure CAPTCHA solving with different strategies:
175+
176+
```python
177+
from predicate.agents.browser_agent import CaptchaConfig
178+
from predicate.captcha_strategies import HumanHandoffSolver, ExternalSolver
179+
180+
# Human handoff: wait for manual solve
181+
config = PlannerExecutorConfig(
182+
captcha=CaptchaConfig(
183+
policy="callback",
184+
handler=HumanHandoffSolver(timeout_ms=120_000),
185+
),
186+
)
187+
188+
# External solver: integrate with 2Captcha, CapSolver, etc.
189+
def solve_captcha(ctx):
190+
# Call your CAPTCHA solving service
191+
pass
192+
193+
config = PlannerExecutorConfig(
194+
captcha=CaptchaConfig(
195+
policy="callback",
196+
handler=ExternalSolver(resolver=solve_captcha),
197+
),
198+
)
199+
```
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
"""
2+
AutomationTask Example
3+
4+
Demonstrates using AutomationTask for flexible task definition with:
5+
- Task categories for better heuristics
6+
- Success criteria for verification
7+
- Recovery configuration for rollback
8+
- Extraction specification for data extraction tasks
9+
10+
Prerequisites:
11+
pip install predicate-sdk openai
12+
export OPENAI_API_KEY=sk-...
13+
"""
14+
15+
import asyncio
16+
17+
from predicate import AsyncPredicateBrowser
18+
from predicate.agent_runtime import AgentRuntime
19+
from predicate.agents import (
20+
AutomationTask,
21+
ExtractionSpec,
22+
PlannerExecutorAgent,
23+
PlannerExecutorConfig,
24+
TaskCategory,
25+
)
26+
from predicate.llm_provider import OpenAIProvider
27+
28+
29+
async def basic_task_example():
30+
"""Basic AutomationTask usage."""
31+
print("\n=== Basic AutomationTask Example ===\n")
32+
33+
# Create LLM providers
34+
planner = OpenAIProvider(model="gpt-4o")
35+
executor = OpenAIProvider(model="gpt-4o-mini")
36+
37+
# Create agent
38+
agent = PlannerExecutorAgent(
39+
planner=planner,
40+
executor=executor,
41+
)
42+
43+
# Create a basic task
44+
task = AutomationTask(
45+
task_id="search-example",
46+
starting_url="https://example.com",
47+
task="Find the main heading on the page",
48+
)
49+
50+
print(f"Task ID: {task.task_id}")
51+
print(f"Starting URL: {task.starting_url}")
52+
print(f"Task: {task.task}")
53+
54+
async with AsyncPredicateBrowser() as browser:
55+
page = await browser.new_page()
56+
await page.goto(task.starting_url)
57+
58+
runtime = AgentRuntime.from_page(page)
59+
result = await agent.run(runtime, task)
60+
61+
print(f"\nResult: {'Success' if result.success else 'Failed'}")
62+
print(f"Steps completed: {result.steps_completed}/{result.steps_total}")
63+
64+
65+
async def transaction_task_example():
66+
"""E-commerce transaction task with recovery."""
67+
print("\n=== Transaction Task Example ===\n")
68+
69+
planner = OpenAIProvider(model="gpt-4o")
70+
executor = OpenAIProvider(model="gpt-4o-mini")
71+
72+
agent = PlannerExecutorAgent(
73+
planner=planner,
74+
executor=executor,
75+
)
76+
77+
# Create a transaction task with category and recovery
78+
task = AutomationTask(
79+
task_id="purchase-laptop",
80+
starting_url="https://amazon.com",
81+
task="Search for 'laptop under $500' and add the first result to cart",
82+
category=TaskCategory.TRANSACTION, # Helps with element selection
83+
enable_recovery=True, # Enable rollback on failure
84+
max_recovery_attempts=2,
85+
max_steps=50,
86+
)
87+
88+
# Add success criteria
89+
task = task.with_success_criteria(
90+
{"predicate": "url_contains", "args": ["/cart"]},
91+
{"predicate": "exists", "args": [".cart-item, .sc-list-item"]},
92+
)
93+
94+
print(f"Task: {task.task}")
95+
print(f"Category: {task.category}")
96+
print(f"Recovery enabled: {task.enable_recovery}")
97+
print(f"Success criteria: {task.success_criteria}")
98+
99+
async with AsyncPredicateBrowser() as browser:
100+
page = await browser.new_page()
101+
await page.goto(task.starting_url)
102+
103+
runtime = AgentRuntime.from_page(page)
104+
result = await agent.run(runtime, task)
105+
106+
print(f"\nResult: {'Success' if result.success else 'Failed'}")
107+
print(f"Steps completed: {result.steps_completed}/{result.steps_total}")
108+
print(f"Replans used: {result.replans_used}")
109+
110+
if result.error:
111+
print(f"Error: {result.error}")
112+
113+
114+
async def extraction_task_example():
115+
"""Data extraction task with schema."""
116+
print("\n=== Extraction Task Example ===\n")
117+
118+
planner = OpenAIProvider(model="gpt-4o")
119+
executor = OpenAIProvider(model="gpt-4o-mini")
120+
121+
agent = PlannerExecutorAgent(
122+
planner=planner,
123+
executor=executor,
124+
)
125+
126+
# Create an extraction task with output schema
127+
task = AutomationTask(
128+
task_id="extract-product-info",
129+
starting_url="https://amazon.com/dp/B0EXAMPLE",
130+
task="Extract the product name, price, and rating",
131+
category=TaskCategory.EXTRACTION,
132+
extraction_spec=ExtractionSpec(
133+
output_schema={
134+
"name": "str",
135+
"price": "float",
136+
"rating": "float",
137+
"num_reviews": "int",
138+
},
139+
format="json",
140+
require_evidence=True,
141+
),
142+
)
143+
144+
print(f"Task: {task.task}")
145+
print(f"Category: {task.category}")
146+
print(f"Output schema: {task.extraction_spec.output_schema}")
147+
print(f"Format: {task.extraction_spec.format}")
148+
149+
# Note: This example won't run successfully as the URL is fake
150+
# In real usage, provide a valid product URL
151+
152+
153+
async def form_fill_task_example():
154+
"""Form filling task example."""
155+
print("\n=== Form Fill Task Example ===\n")
156+
157+
planner = OpenAIProvider(model="gpt-4o")
158+
executor = OpenAIProvider(model="gpt-4o-mini")
159+
160+
agent = PlannerExecutorAgent(
161+
planner=planner,
162+
executor=executor,
163+
)
164+
165+
# Create a form fill task
166+
task = AutomationTask(
167+
task_id="contact-form",
168+
starting_url="https://example.com/contact",
169+
task="Fill the contact form with name 'John Doe', email 'john@example.com', and message 'Hello, I have a question'",
170+
category=TaskCategory.FORM_FILL,
171+
)
172+
173+
# Add success criteria for form submission
174+
task = task.with_success_criteria(
175+
{"predicate": "any_of", "args": [
176+
{"predicate": "exists", "args": [".success-message"]},
177+
{"predicate": "url_contains", "args": ["/thank-you"]},
178+
]},
179+
)
180+
181+
print(f"Task: {task.task}")
182+
print(f"Category: {task.category}")
183+
184+
185+
async def from_string_example():
186+
"""Create task from simple string."""
187+
print("\n=== From String Example ===\n")
188+
189+
# Quick task creation from string
190+
task = AutomationTask.from_string(
191+
"Search for 'headphones' and filter by price under $50",
192+
"https://amazon.com",
193+
category=TaskCategory.SEARCH,
194+
)
195+
196+
print(f"Task ID: {task.task_id}") # Auto-generated UUID
197+
print(f"Task: {task.task}")
198+
print(f"Starting URL: {task.starting_url}")
199+
print(f"Category: {task.category}")
200+
201+
202+
async def with_extraction_example():
203+
"""Add extraction to existing task."""
204+
print("\n=== With Extraction Example ===\n")
205+
206+
# Create basic task
207+
task = AutomationTask(
208+
task_id="product-search",
209+
starting_url="https://amazon.com",
210+
task="Search for the cheapest laptop",
211+
)
212+
213+
# Add extraction specification using fluent API
214+
task_with_extraction = task.with_extraction(
215+
output_schema={"product_name": "str", "price": "float"},
216+
format="json",
217+
)
218+
219+
print(f"Original category: {task.category}")
220+
print(f"After with_extraction: {task_with_extraction.category}")
221+
print(f"Extraction spec: {task_with_extraction.extraction_spec}")
222+
223+
224+
async def main():
225+
"""Run all examples."""
226+
print("=" * 60)
227+
print("AutomationTask Examples")
228+
print("=" * 60)
229+
230+
# Show task creation patterns (no browser needed)
231+
await from_string_example()
232+
await with_extraction_example()
233+
234+
# These require browser and API keys
235+
# Uncomment to run:
236+
# await basic_task_example()
237+
# await transaction_task_example()
238+
# await form_fill_task_example()
239+
240+
241+
if __name__ == "__main__":
242+
asyncio.run(main())

0 commit comments

Comments
 (0)