Skip to content

Pair agentic contexts with profile attributes in the Vercel AI tutorial - #1896

Draft
jborlase-snowplow wants to merge 7 commits into
mainfrom
update/vercel-agentic-context
Draft

Pair agentic contexts with profile attributes in the Vercel AI tutorial#1896
jborlase-snowplow wants to merge 7 commits into
mainfrom
update/vercel-agentic-context

Conversation

@jborlase-snowplow

Copy link
Copy Markdown
Contributor

What changed?

Retrofits tutorials/signals-ai-agent-context/ to use agentic contexts natively: an agentic-context definition step (Console + Python SDK tabs), and lib/signals-context.ts now fetches BOTH the profile attributes (service) and the in-session narrative (getAgenticContext, format: "narrative") via Promise.allSettled, landing both in the system prompt with when-to-use-which guidance. Replaces the hand-rolled formatAttributes() block for session activity while keeping attributes as the computed-profile complement (per product direction). Also: fixes a real guard bug (a fresh session returns every attribute as null — the object is truthy, so the old empty-check never fired and page_views_count: null reached the prompt), and replaces the stale architecture PNG with a Mermaid diagram covering both fetches.

Why?

The flagship agent tutorial pre-dated agentic contexts and hand-rolled what the feature now provides.

Reviewer guidance

  • Verified on a live Next.js scaffold up to the model call: both fetches execute, the composed prompt contains both sections, and the fixed guard was proven side-by-side against real API responses (old guard leaks nulls, new one doesn't). The model call itself is unverified — the tutorial's provider key (AI_GATEWAY_API_KEY) wasn't available; no provider was substituted.
  • In flight: a browser pass is adding the two Console screenshots and verifying the Basic Web template's attribute list — those commits land on this PR shortly.
  • Docs error found while verifying: format: "narrative" output leads with the configured prompt line before [START CONTEXT]; the applications docs page omits it — needs its own fix.
  • Merge note: Add free trial note to Signals tutorial intros #1884 touches this tutorial's introduction too (trivial conflict); merging Add free trial note to Signals tutorial intros #1884 first is simplest. The personalize-travel cross-link PR (Cross-link agentic contexts from personalize-travel agent tutorial #1892) should merge at/after this one.

🤖 Generated with Claude Code

jborlase-snowplow and others added 4 commits July 29, 2026 16:47
The signals-ai-agent-context tutorial pre-dates agentic contexts and
hand-rolled what the feature now provides: build-ai-integration.md
composed a "Real-Time User Context" markdown block from attribute values
via a custom formatAttributes() helper.

Retrofit it as a pairing rather than a replacement. The tutorial now
fetches both kinds of Signals context and lands both in the system
prompt, and explains when each is the right tool:

- user-level profile attributes (computed aggregates, via the service)
- in-session agentic context (raw recent activity as an LLM-ready
  narrative, via getAgenticContext with format: "narrative")

Changes:
- configure-signals.md: add an agentic-context definition step with
  Console and Python SDK tabs, selecting event_name and page_urlpath so
  the narrative's table columns populate
- build-ai-integration.md: replace formatAttributes() for session
  activity with getAgenticContext(); both fetches now run in parallel and
  degrade independently. The narrative example is a real capture
- introduction.md: describe both context types and both fetches in the
  component flow; add the missing free-trial note; point the prerequisite
  at /docs/signals/setup/ instead of the credentials page
- test.md: verify both prompt sections separately; add the Snowplow
  Inspector step
- setup.md: correct the Signals base URL placeholder
- conclusion.md: list the agentic context in the summary

Verified against a live trial org: the Python SDK block publishes as
written, and a scaffolded Next.js app composes a prompt containing both
the profile attributes and the [START CONTEXT] narrative.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Signals service returns a key for every attribute it serves, with a
null value until the session has produced data. The guard checked only
for a missing or empty object:

    if (!attributes || Object.keys(attributes).length === 0)

Neither branch fires on {"page_views_count": null, ...} — the object is
present and its key count is non-zero — so the guard never fired and the
agent injected "page_views_count: null" into the system prompt as though
it were a fact about the user.

Filter the null values out before testing for emptiness, and explain the
behavior on the page so a reader who sees nulls on their first run knows
what they are looking at.

Also fix the profile JSON example, which showed "unique_pages_viewed": 5.
The page's own attribute table and the sibling signals-google-adk-agent
tutorial both describe that attribute as a list of URLs, so the scalar was
wrong. The other values now match the real capture the page already shows
for the narrative.

Verified against a live trial org with a fresh, never-seen session
identifier: the old guard emitted a list of nulls, the new one emits
nothing, and a session with five real page views is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vercel-architecture.png showed only the attributes-to-prompt flow, so it
contradicted the retrofit: the tutorial now fetches profile attributes and
the agentic-context narrative, and lands both in the system prompt. There
is no design source to regenerate the PNG from, and the style guide
prefers Mermaid for architecture visuals, so the diagram is now inline.

The flowchart covers both fetches, with the /api/chat route calling
getServiceAttributes against the service and getAgenticContext in
narrative format against the agentic context.

Delete the PNG, which nothing else references.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Console tab was written from the prose in docs/signals/agentic-contexts,
which describes the form's fields rather than quoting their labels. That
prose had been turned into literal control names that do not exist.

Corrected against a live-Console walk:

- it is not a stepped wizard but one scrolling Create context form, with
  Details, Prompt, Lookback Window, and Events and Properties sections
- the field is Prompt, not Prompt instructions
- the field is Primary owner, and Console fills it in for you, not Owner
- Lookback Window takes Max events and Max age with a unit dropdown, and
  restates the resulting window below the fields
- Events and Properties has Add event and Add property buttons
- the create form saves a draft; Publish and Edit live on the details page

The create button on the list page and the create form's submit button are
deliberately not named, because neither label has been seen.

Also carry over the publish() warning from the signals-agentic-contexts
tutorial. In snowplow-signals 0.4.6 both publish() and unpublish() fail
with a 409 for a name that already exists, so a Python-tab reader who
edits and re-publishes hits it with no warning otherwise.

Screenshots of this flow still need a browser; two markers show where they
go.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 30, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
documentation e69dbaf Commit Preview URL

Branch Preview URL
Jul 31 2026, 03:50 PM

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Documentation style review

Scope: writing quality, style guide compliance, terminology, structure, frontmatter, links, and MDX components. Overall this is a well-written expansion — the prose is clear, active, and free of marketing language, and the swap from the PNG architecture diagram to Mermaid follows the style guide's preference for Mermaid architecture visuals. Both new external links (https://pypi.org/project/snowplow-signals/, https://snowplow.io/get-started/snowplow-free-trial) return 200, and every internal link target resolves, including the #attribute-groups and #services anchors.

A few things to fix:

1. TODO placeholders left in published content

tutorials/signals-ai-agent-context/configure-signals.md:104 and :110

{/* TODO(pending-browser): screenshot of the Create context form, Details and Prompt sections */}
{/* TODO(pending-browser): screenshot of the Lookback Window and Events and Properties sections */}

These are the only TODO markers anywhere in tutorials/ or docs/signals/. Either add the screenshots (CLAUDE.md: images go in an images/ subdirectory with descriptive filenames and alt text) or drop the comments before merge — the surrounding prose stands on its own without them.

2. Wrong admonition type for the publish() note

configure-signals.md:163

:::warning[`publish()` only creates, it can't update]
In `snowplow-signals` 0.4.6, calling `publish()` again for a name that already exists fails with a `409` error rather than updating it. ... This is a bug in the SDK, not the intended behavior.
:::

Three issues:

  • Admonition type. CLAUDE.md reserves Warning for "important cautions about data loss or security risks", and the style guide says "Use Tip for actions, Note or Info for information, Warning for data loss or security risks." A 409 on re-publish is information, so this should be :::note.
  • Title is a comma splice. Suggest :::note[`publish()` creates but can't update].
  • Last sentence. "This is a bug in the SDK, not the intended behavior" is editorial and implies imminent change; the style guide says to stay in the present tense and avoid "in the future" / "Snowplow plans to" framing. The version pin plus the Console workaround already carry the meaning, so drop that sentence.

3. The same concept links to two different pages

Both /docs/signals/agentic-contexts/ (Define agentic contexts) and /docs/signals/applications/agentic-contexts/ (Retrieve agentic contexts) exist, and this PR uses them interchangeably for the same first-mention concept:

  • introduction.mdAn [agentic context](/docs/signals/agentic-contexts/)
  • configure-signals.mdAn [agentic context](/docs/signals/agentic-contexts/)
  • build-ai-integration.mdthe [agentic context](/docs/signals/applications/agentic-contexts/)

Nothing is broken. But in introduction.md the sibling bullet links to /docs/signals/concepts/#services, so the parallel bullet should point at the concepts page too: /docs/signals/concepts/#agentic-contexts (that anchor exists — docs/signals/concepts/index.md:200). Keep the how-to page for configure-signals.md (defining) and the applications page for build-ai-integration.md (fetching); both of those are the right targets for their context.

4. "markdown" should be "Markdown"

build-ai-integration.md — "which getProfileSection() formats into a markdown list"; also test.md — "the profile attributes as a markdown list".

The style guide says to match official third-party styling, and the repo overwhelmingly uses capitalized "Markdown" (CLAUDE.md itself: "All documentation is written in Markdown (MDX)"). Capitalize both occurrences.

5. Passive voice

test.md — "Because each fetch is handled independently, one section can appear without the other."

Style guide: "Use active voice: 'the Collector receives events' not 'events are received by the Collector'." Suggest: "Because the route fetches each independently, one section can appear without the other."

6. Comma before "and" in a compound predicate

conclusion.md — "A Vercel AI SDK agent that fetches both, and injects them into its system prompt"

Drop the comma: "A Vercel AI SDK agent that fetches both and injects them into its system prompt."

7. date frontmatter bumped on existing pages, inconsistently

Six pages get a new date, split between 2026-07-30 (introduction.md, configure-signals.md, build-ai-integration.md) and 2026-07-29 (setup.md, test.md, conclusion.md). CLAUDE.md documents date as the creation date, so either leave the original dates in place or use one consistent value across the tutorial — the two-day split within a single tutorial reads as an accident.

Optional wording nits

  • "The activity fetch needs no formatting at all" — "at all" is filler; "The activity fetch needs no formatting" is tighter.
  • "Here's a real capture from a five-page browsing session" — "a real capture" reads like a note about provenance; "Here's an example from a five-page browsing session" fits the reference tone better.
  • The Mermaid diagram replaces an <img> that carried long descriptive alt text. Mermaid has no alt-text equivalent, but the bullet list immediately above describes the same flow, so the accessible description is preserved — no change needed, just flagging that the prose is now doing that job.

Walked the attribute-group, service, and agentic-context flows in the live
Console and captured the two screenshots the Console tab was missing.

Blocking fix: the service name was invalid. Signals names take letters,
numbers, and underscores only, so `web-agent-context` was rejected outright
with "Name can only contain letters, numbers, and underscores". A reader
following the Console tab could not get past this step. Renamed to
`web_agent_context` in configure-signals.md, build-ai-integration.md, and
conclusion.md, which also matches the sibling signals-google-adk-agent
tutorial. The constraint is now stated on the page.

Removed the Run Preview instructions from configure-signals.md and test.md.
No such control exists in the attribute-group create form, edit form, or
details page, in either draft or published state.

Corrected against what the Console actually shows:

- the sidebar reads Attribute groups and the field is Attribute key, both
  sentence case
- the Basic Web template is applied with a Use button on its card, and its
  four attributes are now documented with the aggregation and property
  behind each one. unique_pages_viewed is a unique list of page_url, so it
  holds full URLs rather than paths
- the create-context form submits with Create, and saves with a Draft
  status, not a Not Published badge
- publishing an agentic context asks for confirmation
- the Attribute groups picker on the service form lists published groups
  only, so publishing first is required rather than merely tidy

Screenshots show the reader-facing resource name, so nothing on screen
contradicts the instructions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jborlase-snowplow

Copy link
Copy Markdown
Contributor Author

Pushed ee64aa81 — the Console verification pass, which closes the last content gaps:

  • Two live Console screenshots added to the definition step, captured on the reader-facing web_agent_activity resource
  • Reader-blocking bug fixed: the tutorial's pre-existing service name web-agent-context is rejected by the product — Signals names allow letters, numbers, and underscores only. Renamed to web_agent_context (now consistent with the ADK tutorial) with the constraint stated on the page
  • Run Preview claims removed — verified absent across the create/draft/published/edit lifecycle on a trial org (likely warehouse-gated; the unconditional instruction was wrong for the tutorial's stated trial-pipeline audience)
  • Basic Web template verified live: all four asserted attribute names correct; unique_pages_viewed confirmed as full URLs (unique_list over atomic page_url), stated explicitly and contrasted with the agentic context's page_urlpath

Remaining known gap (unchanged): the model call itself needs AI_GATEWAY_API_KEY; everything up to and including the composed system prompt is verified.

🤖 Generated with Claude Code

Remove the SDK bug warning from the Python tab and teach the Console
edit-and-publish flow instead, rewrite the null-attribute guidance as a
plain statement of product behavior, and drop references to captures
from our own runs.
@jborlase-snowplow

Copy link
Copy Markdown
Contributor Author

Pushed 9a340de8 — editorial sweep per product direction: the SDK-behavior warning is replaced by the positive Console edit-and-publish instruction, and the null-handling section teaches the defensive pattern factually without bug framing.

🤖 Generated with Claude Code

Featured the AI-assisted route: a new "Ask the Snowplow Assistant" section
carries a copy/paste prompt that creates and publishes the attribute group,
service, and agentic context, ahead of the manual Console steps.

Trimmed to the owner's lens: dropped the Micro/"full pipeline" panel in favour
of a minimal prerequisite, converted the "Existing projects" and model-provider
panels to prose, cut rationale that restated the attributes-vs-narrative
contrast, and reframed the verification steps positively instead of as failure
lists.

Load-bearing constraints verified by the earlier live-Console run are kept as
positive prose: the page_url/page_urlpath contrast, the published-groups-only
picker, the narrative column mapping, and the page-ping exclusion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jborlase-snowplow

Copy link
Copy Markdown
Contributor Author

Pushed e69dbaf7 — owner-lens edit round: the intro's Micro/full-pipeline panel removed (prerequisite now "A Snowplow account and pipeline with Signals enabled"), a new Ask the Snowplow Assistant section leads the Signals configuration page (copy/paste prompt, MCP-verified against the tutorial's definitions), rationale-after-example prose and duplicated instructions cut, troubleshooting reframed positively, and dates/titles tidied. Load-bearing verified constraints (the page_url distinction, publish-before-service ordering, the null-handling walkthrough) were deliberately retained as positive prose. Merge-order note: #1884 edits the same intro lines — whichever merges second should keep one prerequisite bullet and no panel.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants