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.
Why ground agents in authoritative knowledge
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_tolinks – conversational references to products or policies resolve to authoritative nodes.Content addressing – uploading the same document twice is idempotent.
Step 1 – Load authoritative knowledge
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,
});Step 2 – Query authoritative knowledge in the agent loop
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 responseasync 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;
} Step 3 – The resolves_to mechanism
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.
Step 4 – Handling authoritative and experiential streams conflicts
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:
Creates an experiential memory attribute:
return_window_days: 60on the policy entity.Detects the conflict with the authoritative knowledge node (which says 30).
Surfaces the clash in
uncertaintiesand 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.
Exact node retrieval
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.