Predicate Debugger integration
Attach Predicate’s verification & trace layer to your existing agent.
No rewrite required. Keep your planner, executor, and models.
Predicate can run as a sidecar that observes browser state, evaluates assertions, and produces traces and artifacts when things go wrong.
Not sure whether you should attach as a sidecar or drive the loop with the SDK? Choose your path →
1) Concept
What it does
- Adds verification to your agent loop with assertions and retries
- Generates traces and artifacts on failures
- Works with your existing browser stack, including Playwright or CDP backends
What it does not do
- It does not plan actions
- It does not invent steps
- It does not replace your agent framework
You keep control
Predicate Debugger only verifies outcomes. Your agent still decides and executes actions.
2) Universal Pattern
Use the same pattern no matter what framework you run:
from predicate import PredicateDebugger
from predicate.tracing import Tracer, JsonlTraceSink
# page is a Playwright Page from your framework
page = agent.get_page()
tracer = Tracer(run_id="run-123", sink=JsonlTraceSink("trace.jsonl"Step IDs start at step-0
Predicate step IDs are now 0-based. The first auto-generated step will be step-0.
SnapshotOptions without an API key
If you set use_api: false, snapshots are processed locally and do not require a Predicate API key:
await dbg.snapshot(use_api=False, limit=100)3) Dynamic Checks
Your LLM can decide what to verify, when to verify.
# inside your planner/executor loop
await dbg.snapshot(goal="verify:post-action")
# LLM decides which predicate to add
predicate = url_contains("checkout")
dbg.check(predicate, label="on_checkout", required=TrueAs long as your loop can provide a compatible Page or backend, verification can be added dynamically without changing the loop itself.
3.1) Improve trace readability with record_action / recordAction
Sidecar mode means Predicate doesn’t execute actions — your framework does. To keep traces readable, report what your framework just did:
# after your framework/tool executes an action
await dbg.record_action("page.click('Add to cart')", url=page.url)
await dbg.snapshot(goal="verify:post-action", use_api=False)
await dbg.checkAuto-step is self-contained
If you call dbg.check(...) without an explicit step, Debugger will open a verify:* step automatically and now auto-closes it after .once()/.eventually() completes. For cleaner traces, prefer explicit step boundaries.
4) Framework-Specific Adapters
Each adapter answers:
- Where do I get the page?
- Where do I insert verification?
- What do I get when it fails?
browser-use
Where is the page?
From BrowserSession.get_current_page().
Where do I verify?
Right after actions in your existing loop.
What happens on failure?
Predicate emits a trace + artifacts (snapshot + screenshot) for that step.
Example integration
See the full working demo (screenshots + stitched video + Studio trace): browser-use-debugging →
Benefits / what you’ll get
- Deterministic verification around a vision agent: assertions can fail even if the LLM “says” it succeeded.
- Structured snapshots (elements + roles + geometry) + optional screenshot overlays for auditability.
- Trace evidence (actions, snapshots, checks) you can replay in Studio or keep as local JSONL.
- Artifacts on failure (at minimum snapshot/screenshot; optionally richer media if your runner records it).
How to set up a browser-use agent with PredicateDebugger + AgentRuntime
import os
from predicate import PredicateDebugger, get_extension_dir
from predicate.agent_runtime import AgentRuntime
from predicate.backends import BrowserUseAdapter
from predicate.models import SnapshotOptions
from predicate.tracing import Tracer, JsonlTraceSink
from predicate.verification import any_of, exists, url_contains
# browser-use
from browser_use import BrowserProfile, BrowserSession
# 1) Start browser-use with the Predicate extension loaded
profile = BrowserProfile(args=[f"--load-extension={get_extension_dir()}"], headless=False)
session = BrowserSession(browser_profile=profile)
await session.start()
# 2) Create a Predicate backend from the browser-use session (CDP)
adapter = BrowserUseAdapter(session)
backend = await adapter.create_backend()
# 3) Wrap in AgentRuntime + PredicateDebugger (verification sidecar)
tracer = Tracer(run_id="run-1", sink=JsonlTraceSink("trace.jsonl"))
runtime = AgentRuntime(
backend=backend,
tracer=tracer,
predicate_api_key=os.getenv("PREDICATE_API_KEY"),
snapshot_options=SnapshotOptions(use_api=True, limit=100, screenshot=True, show_overlay=True),
)
dbg = PredicateDebugger(runtime=runtime)
# 4) Verify after actions in your existing loop
await dbg.snapshot(goal="verify:post-action", use_api=True, limit=80, show_overlay=True)
await dbg.check(
any_of(url_contains("dw.com"), exists("text~'DW'")),
label="on_domain",
required=True,
).eventually(timeout_s=10)LangChain
Where is the page?
Use your LangChain Playwright tool to access the Page.
Where do I verify?
Immediately after the tool executes an action.
Example integration
See the full working demo: langchain-debugging →
What happens on failure?
Your run stops early with a trace you can inspect.
page = langchain_agent.get_page()
dbg = PredicateDebugger.attach(page, tracer=tracer)LangGraph
Where is the page?
Use the same Page used by your graph’s executor node.
Where do I verify?
In the verify node, before branching or retrying.
What happens on failure?
A failed assertion becomes a clear branch condition.
async def verify(state):
await dbg.snapshot()
ok = dbg.check(url_contains("example.com"), label="on_domain", required=True).once()
return { **state, "ok": ok }Pydantic AI
Where is the page?
Use the same browser/page stored in your agent deps.
Where do I verify?
After tool calls or at the end of each action.
What happens on failure?
You get deterministic verification failure signals to guide retries.
deps = PredicatePydanticDeps(browser=browser)
page = deps.browser.page
dbg = PredicateDebugger.attach(page, tracer=tracer)5) CDP / Non‑Playwright Loops
If your loop is CDP-based, attach the debugger using a BrowserBackend:
from predicate.agent_runtime import AgentRuntime
from predicate.backends import CDPBackendV0
from predicate.debugger import PredicateDebugger
backend = CDPBackendV0(...)
runtime = AgentRuntime(backend=backend,Requirement: you still need Predicate snapshots. That means your CDP setup must expose the extension or a compatible snapshot provider.
6) When it Fails
When a required assertion fails, Predicate emits:
- A structured verification event
- A snapshot of the DOM + geometry
- A screenshot when enabled
- Optional buffered frames when artifacts are enabled
These traces are designed to explain why a step failed, not just that it failed.