Patterns

Knowledge-grounded agents

Combine authoritative knowledge retrieval with experiential memory.

A knowledge-grounded agent answers from authoritative sources before consulting conversational memory. This pattern uses SurrealDB Agent Memory's authoritative knowledge to store canonical knowledge - product data, policies, technical documentation - and relies on the authority hierarchy to prevent conversational drift from corrupting those facts.

Note

Spectron was the project name for SurrealDB Agent Memory. These type names
will be renamed in a future release.

Without a knowledge layer, agents hallucinate or rely on stale training data for domain-specific questions. The common workaround - embedding documents in a vector store and retrieving chunks - improves recall but loses structure, provenance, and the ability to enforce authority.

authoritative knowledge gives you:

  • Structured knowledge nodes with typed attributes, not just text chunks.

  • Authority enforcement - authoritative knowledge wins when a user asserts something conflicting.

  • resolves_to links - conversational references to products or policies resolve to authoritative nodes.

  • Content addressing - uploading the same document twice is idempotent.

Documents are ingested through the knowledge API. SurrealDB Agent Memory processes them into knowledge nodes (typed entities with attributes) and keyword-indexed chunks.

import os
import httpx

client = httpx.Client(
    base_url="https://spectron.surrealdb.com/api/v1/my-context",
    headers={"Authorization": f"Bearer {os.environ['SPECTRON_API_KEY']}"},
)

# Upload a product specification as a JSON document
with open("product-specs.json", "rb") as f:
    client.post(
        "/documents",
        files={"file": ("product-specs.json", f, "application/json")},
        data={"title": "Product specifications"},
    )

# Upload a policy document as Markdown
with open("returns-policy.md", "rb") as f:
    client.post(
        "/documents",
        files={"file": ("returns-policy.md", f, "text/markdown")},
        data={"title": "Returns policy"},
    )
const formData = new FormData();
formData.append(
    "file",
    new Blob([productSpecsJson], { type: "application/json" }),
    "product-specs.json",
);
formData.append("title", "Product specifications");
formData.append("content_type", "product_data");

await fetch("https://spectron.surrealdb.com/api/v1/my-context/documents", {
    method: "POST",
    headers: { "Authorization": `Bearer ${process.env.SPECTRON_API_KEY}` },
    body: formData,
});

Before generating a response, search authoritative knowledge for relevant passages. Use documents.query for natural-language search over document chunks, or an entity read for exact lookups.

from surrealdb import Spectron

memory = Spectron(context="my-context", api_key="sk-...")

async def grounded_response(session, user_message: str) -> str:
    # Search authoritative knowledge for relevant authoritative knowledge
    knowledge = await memory.documents.query(
        query=user_message,
        mode="hybrid",
        k=4,
    )

    # Retrieve experiential memory user context for personalisation
    ctx = await session.context(query=user_message)

    # Assemble the prompt with authoritative facts taking precedence
    system = "You are a product assistant. Answer from the knowledge provided."

    # documents.query returns {results, queryMs}; each hit is {chunk, document, score}
    if knowledge["results"]:
        system += "\n\n## Authoritative knowledge\n"
        for hit in knowledge["results"]:
            system += f"\n{hit['chunk']['text']}  (source: {hit['document']['title']})"

    if ctx.items:
        system += f"\n\n## User context\n{ctx.formatted}"

    response = your_llm(system=system, user=user_message)

    await memory.remember(user_message, session_id=session.id, role="user")
    await memory.remember(response, session_id=session.id, role="assistant")

    return response
async function groundedResponse(session: Session, userMessage: string): Promise<string> {
    const [knowledge, ctx] = await Promise.all([
        memory.documents.query({ query: userMessage, mode: "hybrid", k: 4 }),
        session.context({ query: userMessage }),
    ]);

    let system = "You are a product assistant. Answer from the knowledge provided.";

    if (knowledge.results.length > 0) {
        system += "\n\n## Authoritative knowledge\n";
        for (const hit of knowledge.results) {
            system += `\n${hit.chunk.text}  (source: ${hit.document.title})`;
        }
    }

    if (ctx.items.length > 0) {
        system += `\n\n## User context\n${ctx.formatted}`;
    }

    const response = await yourLlm({ system, user: userMessage });

    await memory.remember(userMessage, { sessionId: session.id, role: "user" });
    await memory.remember(response, { sessionId: session.id, role: "assistant" });

    return response;
}

When a user mentions a product or policy, SurrealDB Agent Memory creates an experiential memory entity and automatically creates a resolves_to relation pointing to the matching authoritative knowledge node. The context retrieval traverses this relation so the agent sees both layers together.

// User says "I bought the AirPods Pro" - experiential memory entity created
{
  "id": "entity:[\"Product\", \"airpods_pro\"]",
  "layer": 1,
  "scope": ["org/acme/user/alice"]
}

// authoritative knowledge node - loaded from product catalogue
{
  "id": "knowledge:[\"Product\", \"airpods_pro\"]",
  "layer": 0,
  "name": "AirPods Pro (2nd generation)",
  "price": 279,
  "return_window_days": 30
}

// resolves_to - Spectron creates this automatically
{
  "in": "entity:[\"Product\", \"airpods_pro\"]",
  "out": "knowledge:[\"Product\", \"airpods_pro\"]"
}

At retrieval time, a query about the user's AirPods returns both the experiential-memory entity (the user owns them) and the authoritative knowledge node (authoritative specs). The agent receives a complete picture in a single context call.

When a user asserts something that contradicts authoritative knowledge, SurrealDB Agent Memory records the conflict rather than silently overwriting the authoritative fact.

Example: the return policy is 30 days (authoritative knowledge), and a user says "I thought it was 60 days".

The extraction pipeline:

  1. Creates an experiential memory attribute: return_window_days: 60 on the policy entity.

  2. Detects the conflict with the authoritative knowledge node (which says 30).

  3. Surfaces the clash in uncertainties and state/profile responses without modifying the curated record.

The agent is informed via the context retrieval:

{
  "type": "conflict",
  "l0_fact": { "key": "return_window_days", "value": 30 },
  "l1_belief": { "key": "return_window_days", "value": 60, "source": "user assertion" },
  "recommendation": "Inform the user of the authoritative value."
}

The agent can then politely correct the user without any custom conflict-detection code.

For lookups where you know the entity type and name (from a structured UI, a product SKU field, etc.), read the entity directly instead of searching. This hits GET /entities/{entity_type}/{entity_name}, not the document store - documents.get takes a document id.

entity = await memory.entities.get("product", "airpods_pro")
const entity = await memory.entities.get("product", "airpods_pro");

This is faster than semantic search and appropriate when the reference is unambiguous.

Was this page helpful?