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 Spectron'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.

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. Spectron 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 knowledge nodes. Use knowledge.search for natural-language queries or knowledge.get for exact node lookups.

from surrealdb import Spectron

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

async def grounded_response(session, user_message: str) -> str:
    # Search authoritative knowledge for relevant authoritative knowledge
    knowledge = await memory.knowledge.search(
        query=user_message,
        top_k=4,
    )

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

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

    if knowledge.nodes:
        system += "\n\n## Authoritative knowledge\n"
        for node in knowledge.nodes:
            system += f"\n{node.formatted}"

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

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

    await session.turn(role="user", content=user_message)
    await session.turn(role="assistant", content=response)

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

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

    if (knowledge.nodes.length > 0) {
        system += "\n\n## Authoritative knowledge\n";
        for (const node of knowledge.nodes) {
            system += `\n${node.formatted}`;
        }
    }

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

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

    await session.turn({ role: "user", content: userMessage });
    await session.turn({ role: "assistant", content: response });

    return response;
}

When a user mentions a product or policy, Spectron 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, Spectron 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.), use knowledge.get instead of a search:

node = await memory.knowledge.get(
    entity_type="Product",
    entity_name="airpods_pro",
)
const node = await memory.knowledge.get({
    entityType: "Product",
    entityName: "airpods_pro",
});

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

Was this page helpful?