1.2The Four Phases
+---------------------------------------------------+
| |
| OBSERVE --> THINK --> ACT --> EVALUATE --> LOOP |
| | | | | |
| Read page Decide Execute Check if |
| state, what to the test assertion |
| logs, test action passed or |
| errors next failed |
| |
+---------------------------------------------------+
Phase 1: OBSERVE
The agent gathers information about the current state of the system under test. In browser testing, this means reading the page content, URL, console errors, and network activity. In API testing, it means reading response status codes, headers, and bodies.
class Observation:
url: str # Current page URL
text: str # Visible text content (truncated)
errors: list[str] # Console errors
network_requests: list[dict] # Recent HTTP requests
screenshot_path: str | None # Path to current screenshot
page_title: str # Document title
Key insight: The quality of the observation determines the quality of the agent's reasoning. A rich observation (text + errors + network) produces better decisions than a sparse one (just the URL).
Phase 2: THINK
The agent analyzes the observation and decides what to do next. This is where the LLM's reasoning ability is essential. It considers:
- The test objective (what are we trying to verify?)
- The current state (where are we now?)
- The history (what have we already tried?)
- The constraints (how many steps remain? what actions are allowed?)
prompt = f"""
Objective: {test_objective}
Current URL: {observation.url}
Page content (truncated): {observation.text[:2000]}
Console errors: {observation.errors}
History: {self.history[-5:]} # Last 5 actions
What should I do next? Choose one:
- NAVIGATE <url>
- CLICK <selector>
- TYPE <selector> <text>
- ASSERT <condition>
- DONE <pass|fail> <reason>
"""
decision = self.llm.generate(prompt)
Phase 3: ACT
The agent executes the decided action against the system under test. This is the only phase that changes state.
def execute(self, decision: str) -> ActionResult:
if decision.startswith("NAVIGATE"):
url = decision.split(" ", 1)[1]
self.browser.navigate(url)
elif decision.startswith("CLICK"):
selector = decision.split(" ", 1)[1]
self.browser.click(selector)
elif decision.startswith("TYPE"):
parts = decision.split(" ", 2)
self.browser.type(parts[1], parts[2])
elif decision.startswith("ASSERT"):
condition = decision.split(" ", 1)[1]
return self.evaluate_assertion(condition)
elif decision.startswith("DONE"):
return self.parse_final_result(decision)
Phase 4: EVALUATE
After acting, the agent evaluates whether the action succeeded and whether the test objective is met. If the objective is not yet met, the loop continues.
if result.is_terminal:
return TestResult(
status=result.status,
reason=result.reason,
steps_taken=self.step_count,
history=self.history
)
# Otherwise, loop back to OBSERVE