AI agents need two things to give useful answers: fresh structured context (current prices, live inventory, recent orders) and semantic search (finding relevant knowledge from a large corpus). A streaming database like RisingWave serves the first need; a vector database serves the second. They are not competing tools. They solve different halves of the AI context problem.
This article explains when each system is the right tool, shows the architecture for combining both, and includes verified SQL examples you can run on RisingWave today.
The Two Data Problems AI Agents Face
When an AI agent answers a question like "What is the current status of order #1008 and are there any similar open issues in our knowledge base?", it is solving two fundamentally different retrieval problems at once.
Problem 1: Fresh structured facts. The order status is a specific, structured fact that changes over time. It lives in a transactional database and can be out of date within seconds. The agent needs the current value, not yesterday's cached snapshot.
Problem 2: Semantic similarity. "Similar open issues" requires searching unstructured text by meaning, not by exact match. That is what vector databases are built for: storing text as high-dimensional embeddings and finding nearest neighbors by cosine similarity.
Most AI agent architectures either ignore Problem 1 (connecting only to a vector store full of stale documents) or ignore Problem 2 (connecting only to a relational database with no semantic search). Production agents need both.
What a Streaming Database Does for AI Agents
A streaming database continuously ingests events from Kafka, database CDC, or direct inserts, and maintains materialized views that update incrementally as new data arrives. When the agent queries a materialized view, it gets the latest computed result, not a cached snapshot from a batch job that ran an hour ago.
RisingWave is a PostgreSQL-compatible streaming database. It speaks the PostgreSQL wire protocol, so any framework that connects to PostgreSQL (LangChain, LlamaIndex, custom Python with psycopg2) connects to RisingWave without modification.
The three things a streaming database gives AI agents that a vector database cannot:
Exact current facts. Stock levels, account balances, order statuses, sensor readings. These change constantly and must be exact, not approximate.
Pre-computed aggregations. Materialized views store computed answers: "total revenue by product this week," "customer lifetime value," "average latency by API endpoint." The agent reads one row instead of running a GROUP BY over millions of events at query time.
Join across live streams. An agent asking "which users placed orders for products that are now low in stock?" needs a join between the orders stream and the product inventory stream, both of which are moving. A streaming database maintains this join continuously.
What a Vector Database Does for AI Agents
A vector database stores documents as dense embeddings and retrieves the most semantically similar ones to a query. When the agent needs to answer "what do our docs say about configuring Kafka connectors?" it searches by meaning, not by exact keyword.
Vector databases excel at:
- Semantic document retrieval for RAG pipelines
- Finding similar items in large unstructured corpora
- Knowledge base search across support articles, documentation, and policy documents
- Fuzzy user intent matching when the query does not contain the exact words in the document
The critical limitation for real-time AI agent use cases: vector stores reflect the state of the world when their embeddings were last generated. If a product changes price, a new support ticket is opened, or a policy document is updated, that change does not appear in the vector store until someone runs the embedding pipeline again.
For a support agent answering questions about live system status, a vector store full of last week's runbooks is dangerously stale. For answering "what is our refund policy?", it is perfectly appropriate.
Side-by-Side Comparison
| Capability | Streaming Database (RisingWave) | Vector Database (Pinecone, Weaviate, Qdrant) |
| Query type | Exact SQL, aggregations, joins | Approximate nearest-neighbor search |
| Data freshness | Milliseconds (incremental MV updates) | Minutes to hours (batch embedding pipeline) |
| Data type | Structured events and tables | Unstructured text and embeddings |
| Best for | Live facts, computed metrics, alerts | Semantic search, RAG, knowledge retrieval |
| Query interface | PostgreSQL SQL | Vector similarity API |
| Handles fast-changing data | Yes, natively | No, requires re-embedding |
| Schema required | Yes | No |
| Supports aggregations | Yes | No |
| Scales to semantic search | No | Yes |
| Open source | Yes (Apache 2.0) | Varies by vendor |
Neither system is a superset of the other. They are designed for orthogonal use cases and work best together.
Building the Streaming Database Layer
Let's set up the structured, always-fresh data layer that AI agents will query in real time.
Schema Design
The scenario: an AI agent handles customer questions for a software marketplace. It needs to answer questions about current pricing, inventory levels, order history, and product availability. All of these are structured, fast-changing facts.
-- Product catalog with pricing and inventory
CREATE TABLE aidata_products (
product_id INT PRIMARY KEY,
name VARCHAR,
category VARCHAR,
price DECIMAL,
stock_level INT,
last_updated_at TIMESTAMPTZ
);
-- Order events stream
CREATE TABLE aidata_orders (
order_id INT PRIMARY KEY,
user_id VARCHAR,
product_id INT,
quantity INT,
total_amount DECIMAL,
status VARCHAR,
ordered_at TIMESTAMPTZ
);
-- AI agent request log
CREATE TABLE aidata_agent_requests (
request_id INT PRIMARY KEY,
user_id VARCHAR,
session_id VARCHAR,
query_text VARCHAR,
query_category VARCHAR,
urgency_score FLOAT,
created_at TIMESTAMPTZ
);
In production, the aidata_products and aidata_orders tables would typically be populated via CDC from PostgreSQL or MySQL, capturing every insert and update from the source database the moment it is committed. The agent request log could come from a Kafka topic.
Materialized View 1: Always-Fresh Product Context
This view joins products with their order history. When the agent needs to answer "Is the CDC Replication Module still available?", it reads from this view and gets the current stock level plus real-time order velocity.
CREATE MATERIALIZED VIEW aidata_mv_product_context AS
SELECT
p.product_id,
p.name,
p.category,
p.price,
p.stock_level,
COUNT(o.order_id) AS orders_count,
COALESCE(SUM(o.total_amount), 0) AS total_revenue,
MAX(o.ordered_at) AS last_order_at,
p.last_updated_at
FROM aidata_products p
LEFT JOIN aidata_orders o ON p.product_id = o.product_id
GROUP BY
p.product_id, p.name, p.category,
p.price, p.stock_level, p.last_updated_at;
The agent queries this view with a simple SELECT:
SELECT
product_id,
name,
price,
stock_level,
orders_count,
total_revenue,
last_order_at
FROM aidata_mv_product_context
ORDER BY orders_count DESC;
product_id | name | price | stock_level | orders_count | total_revenue | last_order_at
------------+-------------------------------+-------+-------------+--------------+---------------+---------------------------
3 | Stream Analytics Dashboard | 199 | 750 | 2 | 597.00 | 2026-04-01 11:15:00+00:00
2 | Kafka Connector Pack | 299 | 500 | 2 | 897.00 | 2026-04-01 11:45:00+00:00
1 | RisingWave Enterprise License | 4999 | 999 | 2 | 9998.00 | 2026-04-01 11:30:00+00:00
4 | CDC Replication Module | 599 | 200 | 1 | 599.00 | 2026-04-01 10:00:00+00:00
5 | Real-Time Alert Engine | 149 | 1000 | 1 | 447.00 | 2026-04-01 11:00:00+00:00
(5 rows)
The materialized view updates within milliseconds whenever a new order lands or a product record changes. The agent never reads a stale price or a stock level from three hours ago.
Materialized View 2: Per-User Order Summary
When a user asks "What did I order last month?", the agent can pull a pre-computed order summary instead of scanning the raw orders table:
CREATE MATERIALIZED VIEW aidata_mv_user_order_summary AS
SELECT
o.user_id,
COUNT(o.order_id) AS total_orders,
SUM(o.total_amount) AS lifetime_value,
MAX(o.ordered_at) AS last_order_at,
COUNT(DISTINCT o.product_id) AS distinct_products_bought
FROM aidata_orders o
GROUP BY o.user_id;
SELECT
user_id,
total_orders,
lifetime_value,
distinct_products_bought,
last_order_at
FROM aidata_mv_user_order_summary
ORDER BY lifetime_value DESC;
user_id | total_orders | lifetime_value | distinct_products_bought | last_order_at
---------+--------------+----------------+--------------------------+---------------------------
u-alice | 2 | 5598.00 | 2 | 2026-04-01 10:00:00+00:00
u-carol | 2 | 5198.00 | 2 | 2026-04-01 11:30:00+00:00
u-bob | 2 | 1045.00 | 2 | 2026-04-01 11:00:00+00:00
u-dave | 2 | 697.00 | 2 | 2026-04-01 11:45:00+00:00
(4 rows)
Materialized View 3: Real-Time Agent Query Analytics
Track what categories of questions AI agents receive, with urgency scoring, to prioritize escalations:
CREATE MATERIALIZED VIEW aidata_mv_agent_query_stats AS
SELECT
query_category,
COUNT(*) AS request_count,
ROUND(AVG(urgency_score)::NUMERIC, 2) AS avg_urgency,
MAX(created_at) AS last_seen_at
FROM aidata_agent_requests
GROUP BY query_category;
SELECT query_category, request_count, avg_urgency, last_seen_at
FROM aidata_mv_agent_query_stats
ORDER BY avg_urgency DESC;
query_category | request_count | avg_urgency | last_seen_at
----------------+---------------+-------------+---------------------------
alert | 1 | 0.95 | 2026-04-01 11:25:00+00:00
inventory | 1 | 0.90 | 2026-04-01 10:05:00+00:00
pricing | 1 | 0.80 | 2026-04-01 10:00:00+00:00
history | 1 | 0.70 | 2026-04-01 10:15:00+00:00
search | 1 | 0.60 | 2026-04-01 10:10:00+00:00
analytics | 1 | 0.50 | 2026-04-01 11:20:00+00:00
(6 rows)
This view tells an operations team which query categories are most time-sensitive. Alert and inventory queries score highest on urgency -- exactly the queries where stale data causes the most harm.
Proactive Stock Alert Context
One pattern unique to streaming databases: the agent can proactively check whether any products need attention, without a user asking first. This query reads the pre-joined materialized view to flag low-stock products:
SELECT
product_id,
name,
stock_level,
price,
orders_count,
CASE
WHEN stock_level < 250 THEN 'CRITICAL'
WHEN stock_level < 600 THEN 'LOW'
ELSE 'OK'
END AS stock_status
FROM aidata_mv_product_context
ORDER BY stock_level ASC;
product_id | name | stock_level | price | orders_count | stock_status
------------+-------------------------------+-------------+-------+--------------+--------------
4 | CDC Replication Module | 200 | 599 | 1 | CRITICAL
2 | Kafka Connector Pack | 500 | 299 | 2 | LOW
3 | Stream Analytics Dashboard | 750 | 199 | 2 | OK
1 | RisingWave Enterprise License | 999 | 4999 | 2 | OK
5 | Real-Time Alert Engine | 1000 | 149 | 1 | OK
(5 rows)
The CDC Replication Module is at 200 units -- a CRITICAL threshold. The agent can surface this to a customer success rep or send an automated alert before the product goes out of stock, not after. This is only possible because the materialized view reflects every order as it arrives, not after a scheduled batch job runs.
The Combined Architecture
Here is the full architecture for an AI agent that uses both a streaming database and a vector database:
graph TD
subgraph "Data Sources"
PG[(PostgreSQL / MySQL)]
KF[Kafka Topics]
API[REST APIs]
end
subgraph "Streaming Database: RisingWave"
RW[(RisingWave)]
MV1[MV: Product Context]
MV2[MV: User Order Summary]
MV3[MV: Agent Query Stats]
RW --> MV1
RW --> MV2
RW --> MV3
end
subgraph "Vector Database"
VDB[(Pinecone / Weaviate / Qdrant)]
EMB[Embedding Pipeline]
end
PG -->|CDC| RW
KF -->|Streaming Source| RW
API -->|Batch / Event| EMB
EMB --> VDB
subgraph "AI Agent"
AGENT[Agent Orchestrator]
LLM[LLM - GPT / Claude]
AGENT -- "SELECT price, stock\nFROM aidata_mv_product_context" --> MV1
AGENT -- "SELECT lifetime_value\nFROM aidata_mv_user_order_summary" --> MV2
AGENT -- "similarity search\nembedding query" --> VDB
MV1 --> AGENT
MV2 --> AGENT
VDB --> AGENT
AGENT --> LLM
end
The agent orchestrator makes two types of calls:
Structured SQL queries to RisingWave for exact, live facts: "What is the current price of product 4?" or "How many orders has u-alice placed?"
Similarity searches to the vector database for semantic retrieval: "Find knowledge base articles about configuring Kafka connectors" or "What do our docs say about handling late-arriving events?"
The LLM assembles both into a coherent response.
Assembling Context in the Agent
Here is how a Python agent would combine both data sources when handling a user question:
import psycopg2
from pinecone import Pinecone # or your preferred vector DB client
def get_agent_context(user_id: str, query_text: str) -> dict:
"""
Fetch fresh structured context from RisingWave and
semantic context from the vector database.
"""
# --- Structured, fresh data from RisingWave ---
conn = psycopg2.connect(
host="localhost", port=4566, user="root", dbname="dev"
)
cur = conn.cursor()
# Always-fresh user order summary (pre-computed by streaming MV)
cur.execute("""
SELECT total_orders, lifetime_value, last_order_at,
distinct_products_bought
FROM aidata_mv_user_order_summary
WHERE user_id = %s
""", (user_id,))
user_summary = cur.fetchone()
# Current product context the user might be asking about
cur.execute("""
SELECT name, price, stock_level, orders_count, stock_status
FROM (
SELECT name, price, stock_level, orders_count,
CASE
WHEN stock_level < 250 THEN 'CRITICAL'
WHEN stock_level < 600 THEN 'LOW'
ELSE 'OK'
END AS stock_status
FROM aidata_mv_product_context
) sub
WHERE stock_status != 'OK'
ORDER BY stock_level ASC
LIMIT 5
""")
at_risk_products = cur.fetchall()
cur.close()
conn.close()
# --- Semantic search from vector database ---
pc = Pinecone(api_key="YOUR_API_KEY")
index = pc.Index("knowledge-base")
query_embedding = embed(query_text) # your embedding function
semantic_results = index.query(
vector=query_embedding,
top_k=3,
include_metadata=True
)
knowledge_snippets = [
r["metadata"]["text"] for r in semantic_results["matches"]
]
return {
"user_summary": user_summary,
"at_risk_products": at_risk_products,
"relevant_docs": knowledge_snippets,
}
The user_summary and at_risk_products come from RisingWave materialized views and are always current to within milliseconds. The relevant_docs come from the vector database and provide semantic context from the knowledge base.
The agent passes both to the LLM prompt:
context = get_agent_context("u-bob", query_text)
system_prompt = f"""You are a support agent for a software marketplace.
USER ACCOUNT (live data as of now):
- Total orders: {context['user_summary'][0]}
- Lifetime spend: ${context['user_summary'][1]}
- Last order: {context['user_summary'][2]}
PRODUCTS NEEDING ATTENTION (live inventory):
{context['at_risk_products']}
RELEVANT KNOWLEDGE BASE ARTICLES:
{chr(10).join(context['relevant_docs'])}
Answer the user's question using the live data for facts and
the knowledge base for policy/procedure questions.
User question: {query_text}"""
This prompt structure makes the division explicit: live facts from the streaming database, policy context from the vector store. The LLM benefits from both.
Why Not Just Keep Everything in the Vector Store?
This is the most common objection: "Can't I just update my vector store more frequently?"
The answer is: even with a fast embedding pipeline, a vector store is not the right tool for structured, exact-match, fast-changing data. Here is why:
Vector search returns approximate matches. If you ask "what is the current stock level for product 4?", a vector search will return the most semantically similar documents to that question. It will not return the number 200. For exact structured facts, approximate similarity is the wrong retrieval primitive.
Re-embedding is expensive. Generating embeddings for millions of product records every few minutes to keep them fresh would consume significant compute and API budget. Materialized views in a streaming database update incrementally -- only the changed rows are recomputed, not the entire dataset.
Aggregations require structure. "What is the average order value by product category this week?" requires a GROUP BY across structured data with typed columns and arithmetic operations. You cannot express this as a similarity search.
Schema changes are hard. Vector stores do not enforce schema. When a field changes meaning or a new dimension is added to the data model, regenerating embeddings for the entire corpus is required. Structured tables in a streaming database handle schema evolution with ALTER TABLE.
For stable, unstructured text (documentation, policies, FAQs, historical articles), vector stores are the right tool. For live, structured, fast-changing operational data, streaming databases are.
Keeping the Vector Store Fresh with RisingWave
A streaming database can also help keep your vector store more current. RisingWave supports sinks to Kafka and other systems. You can define a materialized view that prepares documents for embedding (joining product data with descriptions, formatting the text), and sink the results to a Kafka topic that an embedding pipeline consumes.
-- Prepare enriched product documents for embedding
CREATE MATERIALIZED VIEW aidata_mv_product_for_embedding AS
SELECT
p.product_id,
p.name || ': ' || p.category ||
'. Price: $' || p.price::TEXT ||
'. Stock: ' || p.stock_level::TEXT ||
' units available.' AS document_text,
p.last_updated_at AS updated_at
FROM aidata_products p;
-- Sink to Kafka for the embedding pipeline to consume
CREATE SINK aidata_sink_product_docs
FROM aidata_mv_product_for_embedding
WITH (
connector = 'kafka',
properties.bootstrap.server = 'kafka:9092',
topic = 'product-docs-for-embedding'
) FORMAT PLAIN ENCODE JSON;
The embedding pipeline consumes this Kafka topic, calls the embedding API (OpenAI, Cohere, or a local model), and upserts the result into the vector store. This is still not real-time at the millisecond level (embedding generation adds latency), but it means your vector store refreshes as soon as source data changes rather than waiting for a nightly batch job.
This is the complementary pattern at its clearest: the streaming database detects what changed and prepares the content; the vector store handles the semantic index. For a full walkthrough of this pipeline, see our article on building a real-time data pipeline for RAG applications.
Architecture Decision Guide
Use this table when deciding which system handles a given piece of data in your agent:
| Data type | Changes how often | Use this |
| Product prices, inventory | Seconds to minutes | Streaming database |
| Order status, account balances | Seconds to minutes | Streaming database |
| User activity aggregations | Continuously | Streaming database |
| Real-time alerts and thresholds | Continuously | Streaming database |
| Product documentation | Weekly | Vector database |
| Support knowledge base articles | Daily to weekly | Vector database |
| Policy and compliance docs | Monthly | Vector database |
| Historical transaction records | Never (immutable) | Either |
| User-generated text (reviews, tickets) | Hourly to daily | Vector database (with streaming freshness pipeline) |
When in doubt: if the agent needs to give an exact answer to a time-sensitive question, use a streaming database. If the agent needs to find relevant background knowledge, use a vector database.
FAQ
What is the difference between a streaming database and a vector database for AI agents?
A streaming database processes continuous data streams and maintains always-fresh materialized views of structured data. AI agents use it to answer questions about live facts: current prices, order status, account balances, real-time inventory. A vector database stores text embeddings and retrieves semantically similar documents. Agents use it to find relevant background knowledge from large corpora like documentation or support articles. The two systems solve different retrieval problems and work best together.
Can I use only a vector database for my AI agent?
You can, but only if your agent's questions are purely about static knowledge. If the agent ever needs to answer questions about live operational data (current prices, user account state, system status, inventory), a vector database alone will give stale or incorrect answers. Vector stores reflect the state of the world when embeddings were last generated, which could be hours or days ago. For fast-changing structured facts, a streaming database is required.
How does RisingWave keep materialized views fresh without batch jobs?
RisingWave maintains materialized views incrementally. When a new event arrives (a new order, a price update, a status change), RisingWave computes only the delta to the affected rows in the materialized view and applies it immediately. There is no full recomputation, no REFRESH command, and no schedule to manage. The view is always consistent with the underlying data within milliseconds of each new event.
How do I connect RisingWave to my AI agent framework?
RisingWave is wire-compatible with PostgreSQL. Use any PostgreSQL client library: psycopg2 in Python, pgx in Go, node-postgres in JavaScript, or any JDBC driver. In LangChain, use SQLDatabase with a connection string pointing to postgresql://root@localhost:4566/dev. In LlamaIndex, use the standard PostgreSQL database reader. No special SDK or plugin is required. You can also connect via MCP for standardized tool access.
What to Build Next
The streaming database and vector database are the two pillars of a well-grounded AI agent. One provides exact, live structured context. The other provides semantic background knowledge. Neither replaces the other, and using both is straightforward: RisingWave speaks PostgreSQL, so integrating it alongside any vector store requires only a second database connection in your agent code.
Key takeaways:
- Use a streaming database for exact, fast-changing structured facts. Prices, inventory, account state, and aggregated metrics should come from continuously maintained materialized views, not batch snapshots.
- Use a vector database for semantic retrieval over stable unstructured text. Documentation, FAQs, and knowledge base articles are the right workload for embedding-based search.
- Connect them in a single agent by querying both: SQL to RisingWave for structured context, similarity search to the vector store for background knowledge.
- Use RisingWave's sink capability to keep the vector store fresher. Stream changed documents to an embedding pipeline so semantic search stays current without nightly batch jobs.
Get started with RisingWave in 5 minutes with the quickstart guide. Join the Slack community to ask questions about streaming SQL, AI agent patterns, and materialized view design.

