Feb 20, 20266 min read

SurrealDB for AI Agents: Relational, Graph, and Vector in One Database

I replaced three databases with one. Here's why SurrealDB works for AI agent systems and what I gave up.

SurrealDB for AI Agent Systems

The typical AI stack has three databases:

  1. Postgres for structured data (users, agents, workflows)
  2. Neo4j or similar for graph relationships (knowledge graphs, memory provenance)
  3. Pinecone or Qdrant for vector search (semantic retrieval, embeddings)

Three databases means three connection pools, three migration systems, three query languages, three failure modes.

I use one database for all three: SurrealDB.

Why Three Databases Is a Problem

It's not just operational overhead. It's consistency.

When an AI agent stores a memory, three things need to happen:

  1. Store the fact (relational)
  2. Link it to source messages and entities (graph)
  3. Index the embedding for semantic search (vector)

With three databases, you need distributed transactions or eventual consistency. Both are painful.

Store fact in Postgres → success
Create edge in Neo4j → success
Index embedding in Pinecone → timeout
→ Now what? Fact exists but isn't searchable. Inconsistent state.

With one database:

Store fact + create edges + index embedding → single transaction
→ All or nothing. Consistent.

What SurrealDB Offers

SurrealDB is a multi-model database. It handles relational, document, graph, and vector data in a single engine.

Relational

Standard tables with schemas:

DEFINE TABLE agents SCHEMAFULL;
DEFINE FIELD name ON agents TYPE string;
DEFINE FIELD workspace_id ON agents TYPE record<workspaces>;
DEFINE FIELD status ON agents TYPE string
    ASSERT $value IN ['active', 'inactive', 'paused'];
DEFINE FIELD capabilities ON agents TYPE array<string>;

This is familiar. Tables, fields, types, constraints.

Graph

First-class edges between records:

-- Create a relationship
RELATE message:abc -> DERIVED_FROM -> fact:xyz
    SET confidence = 0.85, extracted_at = time::now();

-- Traverse the graph
SELECT <-DERIVED_FROM<-message FROM fact:xyz;

Edges are records too. They have fields. You can query them, filter them, traverse them.

Vector

Native vector indexing and similarity search:

DEFINE FIELD embedding ON facts TYPE array<float>;
DEFINE INDEX fact_embedding ON facts
    FIELDS embedding MTREE DIMENSION 1536 DIST COSINE;

-- Semantic search
SELECT *, vector::similarity::cosine(embedding, $query_embedding) AS score
    FROM facts
    WHERE workspace_id = $workspace
    ORDER BY score DESC
    LIMIT 10;

Embeddings live alongside the data. No separate service. No sync pipeline.

How I Use It for AI Agents

Memory System

The agent memory system is where SurrealDB's multi-model nature pays off most.

Storing a memory:

-- 1. Create the fact (relational)
CREATE fact SET
    content = "Customer prefers email communication",
    workspace_id = workspace:abc,
    agent_id = agent:xyz,
    confidence = 0.9,
    embedding = $embedding;

-- 2. Link to source message (graph)
RELATE message:m123 -> DERIVED_FROM -> fact:f456
    SET extraction_method = "llm", model = "claude-3.5-sonnet";

-- 3. Link to entity (graph)
RELATE fact:f456 -> ABOUT -> entity:customer_123
    SET relation_type = "preference";

One transaction. Three models. Consistent.

Retrieving memories with provenance:

-- Find relevant facts by semantic similarity
LET $facts = SELECT *, vector::similarity::cosine(embedding, $query) AS score
    FROM facts
    WHERE workspace_id = $workspace AND score > 0.7
    ORDER BY score DESC
    LIMIT 5;

-- Get provenance for each fact
SELECT
    content,
    score,
    <-DERIVED_FROM<-message.* AS sources,
    ->ABOUT->entity.* AS entities
FROM $facts;

One query returns the facts, their source messages, and the entities they relate to. In a three-database world, this would be three queries with application-level joins.

Knowledge Graph

Entities and their relationships form a knowledge graph:

-- Entity relationships
RELATE entity:customer_123 -> WORKS_AT -> entity:company_abc;
RELATE entity:customer_123 -> PURCHASED -> entity:product_xyz;
RELATE entity:company_abc -> LOCATED_IN -> entity:city_sf;

-- Graph traversal: "What do we know about this customer's company?"
SELECT
    ->WORKS_AT->entity.* AS company,
    ->WORKS_AT->entity->LOCATED_IN->entity.* AS location
FROM entity:customer_123;

This is natural in a graph database. SurrealDB handles it without a separate Neo4j instance.

Workflow Execution State

Execution checkpoints are documents with relational references:

CREATE execution SET
    workflow_id = workflow:abc,
    status = "running",
    current_node = "check_inventory",
    state = {
        node_outputs: {
            "parse_request": { intent: "order", product_id: "xyz" },
            "check_inventory": { available: true, quantity: 5 }
        },
        budget_remaining: 1.50
    },
    started_at = time::now();

The state is a document (flexible schema). The references are relational (typed foreign keys). Both in one record.

Access Control

SurrealDB has built-in record-level permissions:

DEFINE TABLE facts SCHEMAFULL
    PERMISSIONS
        FOR select WHERE workspace_id = $auth.workspace_id
        FOR create WHERE workspace_id = $auth.workspace_id
        FOR delete WHERE workspace_id = $auth.workspace_id AND $auth.role = 'admin';

Facts are scoped to workspaces. Users only see their workspace's data. Admins can delete. Enforced at the database level, not application level.

The Trade-offs

SurrealDB isn't free. Here's what I gave up.

Maturity

Postgres has decades of battle-testing. SurrealDB is younger. The community is smaller. Edge cases in query planning exist.

Mitigation: I run extensive integration tests. I monitor query performance. I have a migration plan to Postgres + pgvector if needed.

Ecosystem

Postgres has pgAdmin, hundreds of ORMs, every monitoring tool. SurrealDB's tooling ecosystem is growing but not there yet.

Mitigation: I built a thin DAL (Data Access Layer) in Rust that abstracts database operations. If I need to swap databases, I change the DAL, not the application.

Vector Search Maturity

Pinecone and Qdrant are purpose-built for vector search. They have more tuning options, better performance at scale, more index types.

Mitigation: For my current scale, SurrealDB's vector search is sufficient. If I outgrow it, I can add a dedicated vector store for just the search layer while keeping SurrealDB for storage and graph.

Hosting

Managed Postgres is everywhere. Managed SurrealDB options are more limited.

Mitigation: Self-hosted with Docker. SurrealDB is a single binary, so deployment is straightforward.

Query Language

SurrealQL is its own language. Not standard SQL. Developers need to learn it.

Mitigation: SurrealQL is close enough to SQL that the learning curve is small. The graph traversal syntax is actually more intuitive than SQL JOINs for relationship queries.

When SurrealDB Makes Sense

SurrealDB fits when you need multiple data models and consistency between them:

  1. AI agent systems — relational (users, agents), graph (memory provenance), vector (semantic search)
  2. Knowledge management — documents, relationships, embeddings
  3. Social platforms — users, connections, content, recommendations
  4. IoT with relationships — devices, telemetry, topology

It doesn't fit when:

  1. You only need one model — Just use Postgres or MongoDB
  2. Vector search is your bottleneck — Use a dedicated vector DB
  3. You need enterprise support guarantees — Postgres has more support options
  4. Your team knows Postgres deeply — Familiarity has value

The Architecture

In my system:

flowchart TD
    A[dal] --> B[SurrealDB Client]
    B --> C[SurrealDB]
    C --> C1["Relational: users, workspaces, agents"]
    C --> C2["Graph: memory provenance, relationships"]
    C --> C3["Vector: fact & message embeddings"]

    D[Redis] --> D1[Hot checkpoints]
    D --> D2[Rate limit counters]
    D --> D3[Session tokens & OTP codes]

    class C,D special
    class C1,C2,C3 worker
    class D1,D2,D3 default

SurrealDB handles persistent, consistent data. Redis handles ephemeral, high-speed data. Clear boundary.


Three databases for three data models is the default architecture. It works. But for AI agent systems where relational, graph, and vector data intersect on every operation, the consistency and simplicity of one database is worth the trade-offs.

SurrealDB isn't the only option. But the question — "do I really need three databases?" — is worth asking.

Enjoyed this article?

Share it with others or connect with me