Most browser agent tutorials start with one agent and one task. Real workflows need more: a researcher that digs into competitor sites, an analyst that makes sense of what was found, and a writer that assembles the deliverable. That's what CrewAI was built for — and Anchor is what keeps each agent's browser session isolated, reliable, and production-ready.
This guide builds a three-agent competitive intelligence crew. Each researcher controls its own Anchor browser session, extracts pricing and positioning data from a different competitor, and hands structured findings to a synthesis agent that produces a final markdown report.
Why CrewAI for Browser Agents?
LangGraph gives you fine-grained control over graph edges and state. CrewAI trades that for something different: roles. You describe agents by what they do — researcher, analyst, writer — give them goals and backstories, and let the framework handle task sequencing. The result is code that reads like a team spec rather than a state machine, which makes it much easier to reason about agent responsibilities at a glance.
The tradeoff is less explicit control over execution flow. For competitive research pipelines, that's a good deal.
Setup
pip install crewai anchorbrowser playwright
playwright install chromium
export ANCHOR_API_KEY="..."
export OPENAI_API_KEY="..."
Building the Anchor Browser Tool
CrewAI agents get capabilities through tools — callable objects they invoke to interact with the world. We'll wrap Anchor's session API in a BaseTool subclass that creates an isolated browser, navigates to a URL, and returns the page's text content.
import os
from anchorbrowser import Anchorbrowser
from crewai.tools import BaseTool
from playwright.sync_api import sync_playwright
anchor = Anchorbrowser(api_key=os.environ["ANCHOR_API_KEY"])
class BrowseTool(BaseTool):
name: str = "web_browser"
description: str = (
"Navigates to a URL and returns the page's text content. "
"Use this to research websites, extract pricing data, or read documentation."
)
def _run(self, url: str) -> str:
session = anchor.sessions.create(
session={"max_duration": 60, "idle_timeout": 30}
)
try:
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(session.data.cdp_url)
page = browser.contexts[0].pages[0]
page.goto(url, wait_until="domcontentloaded")
text = page.inner_text("body")
browser.close()
return text[:8000]
finally:
anchor.sessions.terminate(session.id)
Each call to _run spins up a fresh, isolated Anchor session. Three agents researching three competitors never share state, cookies, or browser fingerprints — Anchor handles session isolation at the infrastructure level so you don't have to.
Defining the Crew
Three researcher agents each target a different competitor. They all share the same BrowseTool instance, but run independent sessions:
from crewai import Agent, Task, Crew, Process
browse = BrowseTool()
competitors = [
("Browserbase", "https://www.browserbase.com/pricing"),
("Steel", "https://steel.dev/pricing"),
("Browserless", "https://www.browserless.io/pricing"),
]
researchers = [
Agent(
role=f"Competitor Researcher — {name}",
goal=f"Extract pricing tiers, feature lists, and positioning from {url}",
backstory=(
"You are a meticulous analyst who reads competitor websites carefully "
"and returns structured, factual summaries without editorializing."
),
tools=[browse],
verbose=True,
)
for name, url in competitors
]
analyst = Agent(
role="Market Analyst",
goal="Synthesize competitor research into a structured competitive landscape report",
backstory=(
"You turn raw competitor data into clear, actionable market intelligence. "
"You write in plain English with tables and bullet points."
),
verbose=True,
)
Tasks chain the researcher outputs into the analyst's context:
research_tasks = [
Task(
description=(
f"Navigate to {url} and research {name}'s pricing and positioning. "
"Return a structured summary."
),
agent=agent,
expected_output=(
"JSON with keys: name, pricing_tiers (list), "
"key_features (list), positioning (string)"
),
)
for (name, url), agent in zip(competitors, researchers)
]
synthesis_task = Task(
description=(
"Given the three competitor summaries, produce a competitive landscape report: "
"a pricing comparison table, a feature gap analysis, and three bullet-point "
"recommendations for Anchor's positioning."
),
agent=analyst,
context=research_tasks,
expected_output="Markdown report with a comparison table and recommendations section",
)
Running It
crew = Crew(
agents=[*researchers, analyst],
tasks=[*research_tasks, synthesis_task],
process=Process.sequential,
verbose=True,
)
report = crew.kickoff()
print(report.raw)
The three research tasks run first (sequentially by default), then the synthesis task receives all three outputs as context. Switch to Process.hierarchical to give a manager agent full control over task delegation — useful when you want the LLM to decide dynamically which researcher to deploy based on what it already knows.
What This Pattern Gives You
Isolated sessions per agent. Each Anchor session has a unique fingerprint, separate cookies, and a dedicated proxy exit node. No cross-contamination between competitor research, and no risk of one agent's session state polluting another's.
Role-driven reasoning. CrewAI's role/goal/backstory structure shapes how each agent interprets the same tool call — the Browserbase researcher approaches its task differently from the Browserless researcher, without any custom prompt engineering on your end.
Clean lifecycle management. The finally block in _run ensures Anchor terminates every session, even if the agent raises an exception mid-task. No orphaned browser instances, no runaway costs.
This pattern extends to lead enrichment, OSINT pipelines, content monitoring, or any workflow where specialized agents need to work different corners of the web simultaneously.
Ready to build? Start with Anchor's free tier — no infrastructure to manage, first sessions free.



