Skip to content

feat: native persona-driven website navigation#2

Open
lfiaschi wants to merge 1 commit into
mainfrom
feat/persona-driven-navigation
Open

feat: native persona-driven website navigation#2
lfiaschi wants to merge 1 commit into
mainfrom
feat/persona-driven-navigation

Conversation

@lfiaschi

Copy link
Copy Markdown
Owner

Why

AudienceKit can already make a persona react to a static stimulus (SyntheticPanel.run_survey). What it can't do programmatically is let that persona browse — read a live page, decide what matters, and pick its own next move. Today that lives only in the persona-browse skill: a manual narration recipe an agent follows by hand. That's great for a one-off walkthrough but can't be scripted, parallelised, cached, or tested, so it can't back a real study (e.g. 50 personas × 14 sites, structured output, reproducible).

This PR closes that gap by making persona-driven navigation a first-class, testable API — while keeping the move-by-move decision genuinely in the persona's hands rather than in heuristic glue code.

Why this shape

  • The persona is the driver. On each page it's shown the real on-page actions (the clickable elements, controlled-input fills, scroll, and an always-available leave) and returns the one id it chooses, in character. No keyword heuristic picks the click — that's the whole point, and the thing the manual skill did that prior glue code lost.
  • Browser-agnostic, like the backends. AudienceKit deliberately doesn't bundle an LLM vendor; it shouldn't bundle a browser either. Navigation is injected through a tiny Browser protocol, so callers wire in agent-browser, Playwright, Selenium, or a fixture. Zero new dependencies.
  • Reuses existing machinery. Same render_persona, parse_json_response, and make_backend (with vision) the survey path already uses.

How I want to use it

I'm running a synthetic competitive-UX study: sample a fixed GSS-grounded panel, then send each persona through real meal-kit checkout flows. I need each shopper to follow their own path — a price-sensitive shopper heads for pricing and may bail at sticker shock; a health-oriented one digs into the menu — and to record, per step, what they noticed, how the price felt, where trust moved, and whether they left. PersonaNavigator + run_browse_session give exactly that, with extra_fields for the domain-specific reactions (price perception) and a boundary hook to stop at payment/verification walls that are out of scope.

What's added

  • audiencekit/browse.py
    • PageView, BrowseAction, PersonaStep — small frozen dataclasses (survey.py style).
    • PersonaNavigator.step(page, actions, history=...) → in-character reaction + chosen action; extra_fields= to request domain-specific fields; vision via the page screenshot.
    • Browser protocol + run_browse_session(nav, browser, url, max_steps=, boundary=) driver loop.
    • build_browse_prompt, LEAVE, DEFAULT_REACTION_FIELDS.
  • Exported from the package root (__all__).
  • tests/test_browse.py — 7 network-free tests (injected backend + fixture browser): action resolution, leave, unparseable/unknown-choice fallback, extra fields, full session drive, boundary stop.
  • Docs: native "Programmatic API" section in skills/persona-browse/SKILL.md (with a sample agent-browser adapter) and a "Website Analysis" update in README.md.

Example

import audiencekit as ak

row = ak.sample_panel(ak.load_panel(), n=1, seed=13).iloc[0].to_dict()
nav = ak.PersonaNavigator(row, backend_type="gemini",
                          task="find a dinner plan for next week",
                          extra_fields=(("price_feel", "how the price feels to you"),))

steps = ak.run_browse_session(nav, browser, "https://example.com/", max_steps=8)
for s in steps:
    print(s.milestone, "->", s.quote, "| left" if s.left else f"| {s.action.label}")

Test plan

  • pytest -q35 passed (7 new, 28 existing unchanged).
  • No new dependencies; no network calls in tests.

🤖 Generated with Claude Code

Add a first-class API for letting a sampled persona browse a real website
and choose its own next action, turning the `persona-browse` skill (a manual
recipe) into reproducible, testable code.

`PersonaNavigator` reads each page (text + optional screenshot), reacts in
character, and picks ONE action from the real on-page options — AudienceKit
never guesses the next move with heuristics. The browser stays injected via
the `Browser` protocol (the same dependency-light philosophy as the pluggable
LLM backends), so callers wire in agent-browser, Playwright, or a fixture.

- audiencekit/browse.py: PageView, BrowseAction, PersonaStep, PersonaNavigator,
  Browser protocol, build_browse_prompt, run_browse_session, extra_fields for
  domain-specific reaction fields, and a `boundary` hook to stop at
  out-of-scope payment/verification walls.
- Export the new surface from the package root.
- tests/test_browse.py: 7 network-free tests using an injected backend + a
  fixture browser.
- Document the programmatic path in the persona-browse skill and README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant