How to Build a Real-Time Context Engine for AI Agents

How to Build a Real-Time Context Engine for AI Agents

·

11 min read

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is a context engine for AI agents?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A context engine is a data layer that maintains continuously-updated, pre-computed summaries of your operational data and serves them to AI agents at query time. It is not a standalone product — it is a pattern built from an ingest layer (CDC or Kafka), a processing layer (materialized views), and a serve layer (PostgreSQL protocol or MCP). The key property is that the data is always fresh, not batch-refreshed."
      }
    },
    {
      "@type": "Question",
      "name": "Why do AI agents fail without a real-time context layer?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "AI agents make decisions based on whatever data they retrieve at query time. If that data comes from a batch pipeline that refreshes every hour or every day, the agent is reasoning about a world that no longer exists. Common failures include recommending expired promotions, quoting incorrect inventory levels, or approving transactions based on outdated risk scores."
      }
    },
    {
      "@type": "Question",
      "name": "How does RisingWave CDC work without Kafka or Debezium?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "RisingWave includes a native CDC connector for PostgreSQL and MySQL. You create a source using the 'citus' or 'postgres-cdc' connector type, point it at your database with a connection string, and RisingWave streams changes directly — no Kafka cluster or Debezium deployment required as a separate component."
      }
    },
    {
      "@type": "Question",
      "name": "Can I connect an AI agent directly to RisingWave materialized views?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes, via two paths. First, because RisingWave is PostgreSQL wire-compatible, any PostgreSQL MCP server or JDBC/psycopg2 client connects to it directly. Second, the official RisingWave MCP server (risingwavelabs/risingwave-mcp) exposes 100+ tools including the ability to discover, describe, and query materialized views via natural language."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between a context engine and a RAG pipeline?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "RAG pipelines retrieve semantically similar text chunks from a vector store to augment an LLM prompt. A context engine maintains structured, pre-computed summaries of operational state — things like current order status, live inventory, or recent user behavior. Both can be used together: the context engine provides structured facts while the RAG pipeline provides relevant documents."
      }
    }
  ]
}

What a Context Engine Actually Is

The term sounds like something you buy. Vendor decks are full of it. But strip away the marketing and a context engine is just three things: a way to get live operational data in, a way to keep summaries of that data continuously up to date, and a way to serve those summaries to agents when they need them.

That is it. Ingest, process, serve.

The reason this pattern has a name is that getting all three right simultaneously is hard. Traditional databases handle serving well but require manual refresh cycles for precomputed summaries. Batch pipelines can compute any aggregation you want but only on a schedule. Event streaming systems handle ingest but leave the processing and serving to you.

A streaming database handles all three. You define your context logic in SQL, and the database maintains the results incrementally as new events arrive. When an agent queries a materialized view, it gets the current state — not a stale snapshot from the last refresh.

This article shows exactly how to build that pattern using RisingWave, a PostgreSQL-compatible streaming database. All SQL is runnable against RisingWave v2.8.

Why Agents Fail Without Live Context

Consider a concrete failure scenario.

An e-commerce company runs a customer service agent. The agent handles questions about orders, promotions, and account status. The underlying data comes from a warehouse that syncs every four hours.

A customer contacts the agent at 3:47 PM. The last sync ran at 2:00 PM. In the 107-minute gap:

  • A promotional discount code the customer wants to use expired at 2:30 PM
  • Their order status changed from "processing" to "shipped" at 3:15 PM
  • A refund they requested was processed at 3:40 PM

The agent has no visibility into any of these changes. It tells the customer the promotion is still valid. It says the order is still processing. It does not know about the refund. Every answer is confident and wrong.

This is not an unusual configuration. It is the default for organizations that treat the data warehouse as the source of truth for agent context. The warehouse is optimized for analytics queries, not for serving sub-minute-fresh data to agents making decisions one request at a time.

The failure mode is particularly insidious because the agent does not know what it does not know. LLM traces look fine. The agent reasoned correctly given its inputs. The inputs were wrong.

The Three-Layer Pattern

A real-time context engine has three layers:

Ingest: Get changes from operational systems into the streaming database as they happen. This can be CDC (streaming row-level changes from PostgreSQL or MySQL) or Kafka (streaming application events).

Process: Define materialized views that compute the context your agents need. These views update incrementally whenever the underlying data changes, without any scheduled refresh.

Serve: Agents query the views through the PostgreSQL protocol using any PostgreSQL-compatible client, or through the RisingWave MCP server which adds natural-language discovery on top.

Let's build each layer.

Layer 1: Ingest with CDC

RisingWave has native CDC connectors for PostgreSQL and MySQL. You do not need a separate Kafka cluster or Debezium deployment to get started. Connect directly to your operational database:

-- Create a CDC source that streams changes from PostgreSQL
-- No Kafka or Debezium required as a separate component
CREATE SOURCE pg_operational_data WITH (
    connector = 'postgres-cdc',
    hostname = 'your-postgres-host',
    port = '5432',
    username = 'replication_user',
    password = 'your_password',
    database.name = 'production_db',
    slot.name = 'risingwave_slot'
);

-- Declare the tables you want to stream from
CREATE TABLE orders (
    order_id     BIGINT PRIMARY KEY,
    user_id      BIGINT,
    status       VARCHAR,
    total_amount DECIMAL(10, 2),
    created_at   TIMESTAMPTZ,
    updated_at   TIMESTAMPTZ
) FROM pg_operational_data TABLE 'public.orders';

CREATE TABLE promotions (
    promo_id    INT PRIMARY KEY,
    code        VARCHAR,
    discount    DECIMAL(5, 2),
    valid_until TIMESTAMPTZ,
    active      BOOLEAN
) FROM pg_operational_data TABLE 'public.promotions';

CREATE TABLE user_preferences (
    user_id       BIGINT PRIMARY KEY,
    preferred_cat VARCHAR,
    loyalty_tier  VARCHAR,
    updated_at    TIMESTAMPTZ
) FROM pg_operational_data TABLE 'public.user_preferences';

If your architecture already has Kafka, you can also ingest from there for application-level events:

-- Ingest product inventory events from Kafka
CREATE SOURCE inventory_events (
    product_id  INT,
    warehouse   VARCHAR,
    quantity    INT,
    event_time  TIMESTAMPTZ
) WITH (
    connector = 'kafka',
    topic = 'inventory.updates',
    properties.bootstrap.server = 'kafka:9092'
) FORMAT PLAIN ENCODE JSON;

Both sources feed the same processing layer. RisingWave handles the union transparently in your materialized views.

Layer 2: Process with Materialized Views

This is where context engineering happens. Instead of having your agent issue complex multi-table queries at inference time, you pre-compute the context it needs and keep it fresh automatically.

User context view

This view combines a user's recent orders, their loyalty tier, and their category preference into a single row per user. The agent fetches one row by user ID and has everything it needs:

CREATE MATERIALIZED VIEW user_context AS
SELECT
    u.user_id,
    u.loyalty_tier,
    u.preferred_cat,
    COUNT(o.order_id)                       AS total_orders_90d,
    SUM(o.total_amount)                     AS spend_90d,
    MAX(o.updated_at)                       AS last_order_at,
    MAX(CASE WHEN o.status = 'shipped'
             THEN o.order_id END)           AS latest_shipped_order_id,
    MAX(CASE WHEN o.status = 'processing'
             THEN o.order_id END)           AS latest_pending_order_id
FROM user_preferences u
LEFT JOIN orders o
    ON u.user_id = o.user_id
    AND o.created_at > NOW() - INTERVAL '90 days'
GROUP BY u.user_id, u.loyalty_tier, u.preferred_cat;

Product context view

This view aggregates live inventory across warehouses and surfaces the current price and availability:

CREATE MATERIALIZED VIEW product_context AS
SELECT
    product_id,
    SUM(quantity)                                     AS total_inventory,
    COUNT(DISTINCT warehouse)                          AS warehouse_count,
    MAX(event_time)                                    AS last_updated,
    CASE WHEN SUM(quantity) > 100 THEN 'in_stock'
         WHEN SUM(quantity) > 0   THEN 'low_stock'
         ELSE 'out_of_stock'
    END                                                AS availability_status
FROM inventory_events
GROUP BY product_id;

Promotion context view

This is the view that solves the scenario from earlier. Instead of the agent checking a promotions table that might be hours behind, it queries a continuously updated view that knows exactly which promotions are currently active:

CREATE MATERIALIZED VIEW active_promotions AS
SELECT
    promo_id,
    code,
    discount,
    valid_until
FROM promotions
WHERE active = true
  AND valid_until > NOW();

Because this view updates incrementally from CDC, when a promotion expires in PostgreSQL, it disappears from this view within seconds. No scheduled refresh, no manual invalidation.

Order status context view

For the customer service use case, a view that surfaces each user's recent order statuses:

CREATE MATERIALIZED VIEW recent_order_status AS
SELECT
    user_id,
    order_id,
    status,
    total_amount,
    updated_at
FROM orders
WHERE created_at > NOW() - INTERVAL '30 days'
ORDER BY updated_at DESC;

Layer 3: Serve to Agents

Option A: Direct PostgreSQL query

Because RisingWave implements the PostgreSQL wire protocol, any PostgreSQL client connects to it with no modifications. Python agents using psycopg2:

import psycopg2

conn = psycopg2.connect(
    host="localhost",
    port=4566,
    dbname="dev",
    user="root",
    password=""
)

def get_user_context(user_id: int) -> dict:
    with conn.cursor() as cur:
        cur.execute(
            "SELECT * FROM user_context WHERE user_id = %s",
            (user_id,)
        )
        row = cur.fetchone()
        if row:
            cols = [desc[0] for desc in cur.description]
            return dict(zip(cols, row))
    return {}

def get_active_promotions() -> list:
    with conn.cursor() as cur:
        cur.execute(
            "SELECT code, discount, valid_until FROM active_promotions"
        )
        return cur.fetchall()

The agent fetches the context at the start of each conversation turn. The data it gets reflects the world as it is right now, not as it was hours ago.

Option B: MCP for natural-language discovery

The RisingWave MCP server, released in January 2026, exposes 100+ tools for interacting with RisingWave. Agents that support MCP — Claude, Copilot, and others — can discover and query your materialized views using natural language without you writing explicit tool definitions.

Configure it in Claude Desktop:

{
  "mcpServers": {
    "risingwave": {
      "command": "python",
      "args": ["/path/to/risingwave-mcp/src/main.py"],
      "env": {
        "RISINGWAVE_CONNECTION_STR": "postgresql://root:root@localhost:4566/dev"
      }
    }
  }
}

Once connected, an agent can ask questions like:

  • "What materialized views are available in this database?"
  • "What columns does the user_context view have?"
  • "Show me the context for user 4521"

The MCP server translates these into the appropriate SQL queries and returns structured results. The agent does not need pre-wired tool definitions — it discovers the context layer at runtime.

Add COMMENT ON metadata to your views to help the agent understand what each view is for:

COMMENT ON MATERIALIZED VIEW user_context IS
    'Per-user summary of recent orders, loyalty tier, and preferences. Query with WHERE user_id = <id>. Refreshes continuously from CDC.';

COMMENT ON MATERIALIZED VIEW active_promotions IS
    'Currently active promotion codes with discount rates. Only includes promotions valid right now — excludes expired codes automatically.';

COMMENT ON MATERIALIZED VIEW product_context IS
    'Real-time inventory and availability status per product. Use to check stock before confirming order fulfillment.';

These comments appear in the MCP tool descriptions, giving the agent the context it needs to choose the right view for each question.

Putting It Together: Agent Query at Inference Time

When a customer contacts the support agent, the agent's first step is context retrieval. With the live context layer in place, that retrieval looks like this:

-- Agent fetches user context before responding
SELECT
    uc.loyalty_tier,
    uc.total_orders_90d,
    uc.spend_90d,
    uc.latest_shipped_order_id,
    uc.latest_pending_order_id,
    ros.status           AS latest_order_status,
    ros.updated_at       AS status_as_of
FROM user_context uc
LEFT JOIN recent_order_status ros
    ON uc.latest_pending_order_id = ros.order_id
WHERE uc.user_id = $1;

The agent also checks whether the promotion the customer mentioned is still valid:

SELECT discount, valid_until
FROM active_promotions
WHERE code = $1;

Both queries run in milliseconds. Both return data that reflects the world as it is right now — because the materialized views update continuously from CDC, not on a schedule. The agent that used to give confidently wrong answers now gives confidently correct ones.

FAQ

What is a context engine for AI agents?

A context engine is a data layer that maintains continuously-updated, pre-computed summaries of your operational data and serves them to AI agents at query time. It is not a standalone product — it is a pattern built from an ingest layer (CDC or Kafka), a processing layer (materialized views), and a serve layer (PostgreSQL protocol or MCP). The key property is that the data is always fresh, not batch-refreshed.

Why do AI agents fail without a real-time context layer?

AI agents make decisions based on whatever data they retrieve at query time. If that data comes from a batch pipeline that refreshes every hour or every day, the agent is reasoning about a world that no longer exists. Common failures include recommending expired promotions, quoting incorrect inventory levels, or approving transactions based on outdated risk scores.

How does RisingWave CDC work without Kafka or Debezium?

RisingWave includes a native CDC connector for PostgreSQL and MySQL. You create a source using the postgres-cdc connector type, point it at your database with a connection string, and RisingWave streams changes directly — no Kafka cluster or Debezium deployment required as a separate component.

Can I connect an AI agent directly to RisingWave materialized views?

Yes, via two paths. First, because RisingWave is PostgreSQL wire-compatible, any PostgreSQL MCP server or psycopg2/JDBC client connects to it directly. Second, the official RisingWave MCP server (risingwavelabs/risingwave-mcp) exposes 100+ tools including the ability to discover, describe, and query materialized views via natural language.

What is the difference between a context engine and a RAG pipeline?

RAG pipelines retrieve semantically similar text chunks from a vector store to augment an LLM prompt. A context engine maintains structured, pre-computed summaries of operational state — things like current order status, live inventory, or recent user behavior. Both can be used together: the context engine provides structured facts while a RAG pipeline provides relevant documents.

Conclusion

A context engine is not a category of product. It is a pattern built from three things you can assemble today: an ingest layer that captures operational changes via CDC or Kafka, a processing layer that maintains those changes as incrementally updated materialized views, and a serve layer that lets agents query them through standard PostgreSQL protocol or MCP.

The result is that agents stop reasoning about a world that no longer exists. When a promotion expires, the agent knows immediately. When an order ships, the agent knows in seconds. When a user updates their preferences, the agent's next response reflects those preferences.

Every part of this stack is SQL-defined and incrementally maintained. You do not write ETL scripts, manage refresh schedules, or build cache invalidation logic. You write CREATE MATERIALIZED VIEW, and the database handles the rest.


Ready to build your context layer? Get started free at RisingWave Cloud — no credit card required.

Join our Slack community to ask questions and connect with other developers building AI data infrastructure.

Best-in-Class Event Streaming
for Agents, Apps, and Analytics
GitHubXLinkedInSlackYouTube
Sign up for our to stay updated.