Skip to content
Draft
40 changes: 40 additions & 0 deletions tutorials/signals-agentic-contexts/conclusion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
position: 5
title: "Next steps after building a Signals agentic context"
sidebar_label: "Conclusion"
description: "Review what you built with Signals agentic contexts, then combine them with attribute services or add them to a full agent framework."
keywords: ["agentic context", "signals", "ai agent", "attribute services", "next steps"]
date: "2026-08-04"
---

You've built an agentic context end to end. You defined which events to capture and which properties to keep, published it, and read the same session back as both JSON and a narrative. You then appended that narrative to a system prompt the way a customer-facing agent does per turn, and used the prompt to change how a model interpreted the activity without changing what the agentic context captured.

Nothing in this tutorial was framework-specific, which is the point. Any agent that can read a string can use what you just built.

## Combine it with attributes

An agentic context gives an agent the raw recent activity. [Attributes](/docs/signals/attributes/) give it computed values over longer windows, such as a lifetime order count or a running cart total, which no amount of reading recent page views can tell it.

Most production agents want both. Group your attributes into a [service](/docs/signals/applications/services/) so your application can [retrieve them in one call](/docs/signals/applications/retrieve-attributes/), then pass the attributes and the agentic context narrative to the model together: the aggregates say who this user is, and the agentic context says what they're doing in the moment.

You can also let Signals start the conversation rather than waiting for the user, by triggering an [intervention](/docs/signals/interventions/) when behavior crosses a threshold you define.

## Build it into an agent

These tutorials take the same building block into real frameworks, each with a working application at the end:

* [Build an AI agent with real-time user context using Signals and Vercel AI SDK](/tutorials/signals-ai-agent-context/introduction), for a Next.js and Vercel AI SDK agent
* [Build a real-time context-aware agent with Signals, Google ADK, and CopilotKit](/tutorials/signals-google-adk-agent/introduction), for a Google ADK agent embedded in React
* [Build a Signals-powered AI agent with AWS Bedrock AgentCore](/tutorials/signals-agentic-accelerator/intro), for Strands Agents with Bedrock AgentCore Memory

You can also keep working conversationally: the [Snowplow Assistant](/docs/llms-support/console-agent/) in Console and the [Snowplow MCP server](/docs/llms-support/snowplow-mcp/) manage agentic contexts alongside the rest of your Signals configuration.

## Tidy up

If you built this only to try it out, unpublish the agentic context and then delete it, using the `⋮` menu on its details page in Console.

Your assistant can do it for you:

```text
Unpublish and delete my session_context agentic context.
```
178 changes: 178 additions & 0 deletions tutorials/signals-agentic-contexts/define-agentic-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
---
position: 2
title: "Define and publish a Signals agentic context"
sidebar_label: "Define the agentic context"
description: "Define a Signals agentic context that buffers page views for the current session, choose the properties to keep, and publish it using an AI assistant, Snowplow Console, or the Python SDK."
keywords: ["agentic context", "event log", "snowplow assistant", "snowplow mcp", "signals python sdk", "publish", "event selection"]
date: "2026-08-04"
---

```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```

You'll define an agentic context that keeps the last 50 page views of a session, for up to 30 minutes, and attaches a prompt telling an agent how to read them. Signals scopes the buffer to a single session using the `domain_sessionid` [attribute key](/docs/signals/concepts/#attribute-keys), so each session gets its own rolling record.

Do this by asking an AI assistant, in Snowplow Console, or with the Signals Python SDK. All three produce the same thing, so pick whichever you prefer.

<Tabs groupId="signals-impl" queryString>
<TabItem value="assistant" label="AI assistant" default>

Use the [Snowplow Assistant](/docs/llms-support/console-agent/) in Console, which needs no configuration, or your own MCP-capable assistant connected to the [Snowplow MCP server](/docs/llms-support/snowplow-mcp/).

Paste this prompt into the chat:

```text
Create a Signals agentic context called session_context, keyed on domain_sessionid,
that keeps the last 50 page_view events for up to 30 minutes. Attach the atomic
properties event_name, page_urlpath, and page_title to the event, and use this as
the prompt:

"You are a support assistant on a product website. The activity below is what the
user has just been doing. Use it to work out what they are trying to achieve, then
answer their question in that context. If the activity is too thin to tell, say so
rather than guessing."
```

The assistant creates the definition and can publish it for you. Review it under **Signals** > **Agentic contexts** in Console before you publish.

</TabItem>
<TabItem value="console" label="Console">

Go to **Signals** > **Agentic contexts** in Snowplow Console and create a new agentic context. On the **Create context** form, fill in the **Details** and **Prompt** sections:

| Field | Value |
| ----- | ----- |
| Name | `session_context` |
| Primary owner | Your email address |
| Description | `Rolling record of the pages a user has viewed in their current session` |
| Prompt | The prompt text below |

Use this as the prompt:

```text
You are a support assistant on a product website. The activity below is what the
user has just been doing. Use it to work out what they are trying to achieve,
then answer their question in that context. If the activity is too thin to tell,
say so rather than guessing.
```

![The Create context form in Snowplow Console, with the Details section filled in with the name session_context and a description of the rolling record, and the Prompt section holding the support assistant instructions](./images/agentic-context-create-form.png)

Under **Lookback Window**, set **Max events** to `50` and **Max age** to `30` minutes. Underneath the fields, Console shows the window it uses.

Under **Events and Properties**, click **Add event** and choose `page_view` at version `1-0-0`. Then use **Add property** to attach `event_name`, `page_urlpath`, and `page_title` to it.

![The Lookback Window section of the form set to 50 max events and 30 minutes max age, and the Events and Properties section below it showing page_view version 1-0-0 with the three attached properties event_name, page_urlpath, and page_title](./images/agentic-context-event-selection.png)

</TabItem>
<TabItem value="sdk" label="Python SDK">

You'll need Python 3.9 or later. Install the SDK:

```bash
pip install snowplow-signals
```

The SDK models an agentic context as an `EventLog`, so that's the class you import. Store your [connection credentials](/docs/signals/connection/) in the environment first, then define and publish the agentic context:

```python
import os
from snowplow_signals import (
Signals,
EventLog,
EventSelection,
EventLogEvent,
EventLogAtomicProperty,
domain_sessionid,
)

sp_signals = Signals(
api_url=os.environ["SP_API_URL"],
api_key=os.environ["SP_API_KEY"],
api_key_id=os.environ["SP_API_KEY_ID"],
org_id=os.environ["SP_ORG_ID"],
)

session_context = EventLog(
name="session_context",
description="Rolling record of the pages a user has viewed in their current session",
owner="you@example.com",
prompt=(
"You are a support assistant on a product website. "
"The activity below is what the user has just been doing. "
"Use it to work out what they are trying to achieve, then answer their "
"question in that context. If the activity is too thin to tell, say so "
"rather than guessing."
),
attribute_key=domain_sessionid,
max_events=50,
max_age_seconds=1800,
events=[
EventSelection(
event=EventLogEvent(
name="page_view",
vendor="com.snowplowanalytics.snowplow",
version="1-0-0",
),
properties=[
EventLogAtomicProperty(name="event_name"),
EventLogAtomicProperty(name="page_urlpath"),
EventLogAtomicProperty(name="page_title"),
],
)
],
)

sp_signals.publish([session_context])
```

`SP_API_URL` is your Signals API URL, in the form `https://YOUR_ID.signals.snowplowanalytics.com`.

</TabItem>
</Tabs>

## Choose which properties to keep

An agentic context doesn't store whole events. For each event you select, you list the properties to project, and only those reach the agent. Properties can come from the [atomic event](/docs/fundamentals/canonical-event/), the event's own schema, or its entities.

Here you keep three atomic properties of each page view:

| Property | In the narrative |
| -------- | ---------------- |
| `event_name` | Fills the `event` column |
| `page_urlpath` | Fills the `url` column |
| `page_title` | Appears in the `event_context` field |

For the full range of selection options, including schema and entity properties, see [selecting events](/docs/signals/agentic-contexts/#selecting-events).

## Publish it

Publishing sends the definition to your Signals infrastructure and starts the buffering. Until you publish, Signals captures nothing.

<Tabs groupId="signals-impl" queryString>
<TabItem value="assistant" label="AI assistant" default>

Ask the assistant to publish it:

```text
Publish my session_context agentic context.
```

</TabItem>
<TabItem value="console" label="Console">

Click **Publish**. Console keeps your changes as a draft until you do.

</TabItem>
<TabItem value="sdk" label="Python SDK">

The `publish()` call in the code above both registers the agentic context and sends it to your Signals infrastructure.

To change an agentic context you've already published, edit it in Console and publish the draft, as you'll do later in this tutorial.

</TabItem>
</Tabs>

Publish first, then browse: an agentic context captures events from the moment it goes live. There's one live agentic context per name, and publishing replaces the one that was live.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions tutorials/signals-agentic-contexts/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"title": "Give an AI agent real-time session context with Signals",
"description": "Define an agentic context to capture a user's recent session activity, and retrieve it as a plain-language narrative for any agent framework.",
"label": "Signals implementation",
"useCase": "Customer-facing AI agents",
"technologies": ["AI assistants", "Python"],
"snowplowTech": ["Signals", "Console"]
}
151 changes: 151 additions & 0 deletions tutorials/signals-agentic-contexts/retrieve-the-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
---
position: 3
title: "Retrieve a Signals agentic context as JSON or a narrative"
sidebar_label: "Retrieve the context"
description: "Find the domain_sessionid for a live session, then read the agentic context back from Signals as structured JSON or as a plain-language narrative, in Python or Node.js."
keywords: ["agentic context", "get_agentic_context", "domain_sessionid", "narrative format", "signals node sdk"]
date: "2026-08-04"
---

```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```

Your agentic context is live, so browse a few pages on your tracked site to give it something to capture. Visit several different pages, and leave a few seconds between them, so the record has a shape you can recognize later.

## Get the session identifier

An agentic context is scoped to one session, so you read it for a single `domain_sessionid` value. There are two ways to get one.

To find the identifier for the session you just generated, connect the [Snowplow Inspector](/docs/testing/snowplow-inspector/signals-integration/) browser extension to Signals, then browse your site with the extension open. Inspector builds its list of attribute keys from the events it observes, so the **Attributes** tab shows the `domain_sessionid` value for the session you're in.

In application code, read it client-side with the JavaScript tracker's [`getDomainSessionId`](/docs/sources/web-trackers/cookies-and-local-storage/getting-cookie-values/#domain-session-id) method and send it to your back-end, because retrieval happens server-side:

```javascript
const domainSessionId = sp.getDomainSessionId();
```

## Read it as JSON

In your own code, use `format="json"` when you want to work with the activity programmatically, for example to build your own prompt or apply your own logic. If you defined the agentic context in Console or with an assistant, install the SDK with `pip install snowplow-signals` and connect it using your [Signals connection credentials](/docs/signals/connection/):

```python
import os
from snowplow_signals import Signals

sp_signals = Signals(
api_url=os.environ["SP_API_URL"],
api_key=os.environ["SP_API_KEY"],
api_key_id=os.environ["SP_API_KEY_ID"],
org_id=os.environ["SP_ORG_ID"],
)
```

Then pass the name of your agentic context and the session identifier:

```python
context = sp_signals.get_agentic_context(
name="session_context",
identifier="2f8b41d0-5c6e-4a1b-9f3a-7d21c4e8b905",
)
```

For a session that visited a product page, its reviews, the size guide, and then the returns policy, the response looks like this:

```json
{
"summary": "13 seconds on the current page. Session started 50 seconds ago. Based on last 50 recorded events for the last 1800 seconds.",
"attribute_key": "domain_sessionid",
"identifier": "2f8b41d0-5c6e-4a1b-9f3a-7d21c4e8b905",
"name": "session_context",
"version": 1,
"prompt": "You are a support assistant on a product website. The activity below is what the user has just been doing. Use it to work out what they are trying to achieve, then answer their question in that context. If the activity is too thin to tell, say so rather than guessing.",
"started_at_ms": 1785334905303,
"events": [
{
"derived_tstamp": "2026-07-29T14:21:45.303Z",
"event_name": "page_view",
"page_title": "Trail running shoes | Example Shop",
"page_urlpath": "/products/trail-running-shoes"
},
{
"derived_tstamp": "2026-07-29T14:21:57.373Z",
"event_name": "page_view",
"page_title": "Reviews: Trail running shoes | Example Shop",
"page_urlpath": "/products/trail-running-shoes/reviews"
},
{
"derived_tstamp": "2026-07-29T14:22:09.497Z",
"event_name": "page_view",
"page_title": "Size guide | Example Shop",
"page_urlpath": "/size-guide"
},
{
"derived_tstamp": "2026-07-29T14:22:21.632Z",
"event_name": "page_view",
"page_title": "Returns policy | Example Shop",
"page_urlpath": "/support/returns-policy"
}
]
}
```

The events are ordered oldest to most recent, so the last entry is the page the user is on, and each one carries the properties you selected plus a `derived_tstamp` that Signals adds for you. The `prompt` you wrote travels with the data, so whatever reads this context also gets its instructions.

## Read it as a narrative

Use `format="narrative"` when you want to drop the activity straight into a model's context without writing any formatting logic yourself. Signals returns a single string:

```python
narrative = sp_signals.get_agentic_context(
name="session_context",
identifier="2f8b41d0-5c6e-4a1b-9f3a-7d21c4e8b905",
format="narrative",
)

print(narrative)
```

The same session comes back like this:

```text
You are a support assistant on a product website. The activity below is what the user has just been doing. Use it to work out what they are trying to achieve, then answer their question in that context. If the activity is too thin to tell, say so rather than guessing.
[START CONTEXT]
13 seconds on the current page. Session started 50 seconds ago. Based on last 50 recorded events for the last 1800 seconds.
## Real-time user behaviour
Events are ordered from oldest to most recent.
seconds_since_start_of_session, event, url, event_context
0, page_view, /products/trail-running-shoes, {page_title: 'Trail running shoes | Example Shop'}
12, page_view, /products/trail-running-shoes/reviews, {page_title: 'Reviews: Trail running shoes | Example Shop'}
24, page_view, /size-guide, {page_title: 'Size guide | Example Shop'}
36, page_view, /support/returns-policy, {page_title: 'Returns policy | Example Shop'}
[END CONTEXT]
```

Your prompt comes first, then the activity wrapped in `[START CONTEXT]` and `[END CONTEXT]` markers so a model can tell your instructions from the data. The summary line tells the model how fresh the activity is, and the table gives one row per event with the seconds elapsed since the session started. The `page_title` you selected lands in `event_context`, because only `event_name` and `page_urlpath` have columns of their own.

## Retrieve it from Node.js

If your agent runs on Node.js, use the [Node.js SDK](/docs/signals/connection/#signals-nodejs-sdk) instead. Install it with `npm i @snowplow/signals-node`, then call `getAgenticContext` with the same arguments:

```javascript
import { Signals } from "@snowplow/signals-node";

const signals = new Signals({
baseUrl: process.env.SNOWPLOW_SIGNALS_BASE_URL,
apiKey: process.env.SNOWPLOW_SIGNALS_API_KEY,
apiKeyId: process.env.SNOWPLOW_SIGNALS_API_KEY_ID,
organizationId: process.env.SNOWPLOW_SIGNALS_ORG_ID,
});

const narrative = await signals.getAgenticContext({
name: "session_context",
identifier: domainSessionId,
format: "narrative",
});

console.log(narrative);
```

Define agentic contexts in Console, with an assistant, or with the Python SDK, and read them back from either SDK.
Loading