Fact Integration
Last Updated: 2026-01-13
Facts are automatically integrated into recall(), search results, and memory enrichment. This page covers advanced patterns for working with extracted facts.
v0.30.0: Facts now support semantic search via embeddings. When an embedding is available in recall(), facts are searched by semantic similarity, not just keywords.
How Embeddings Work in Batteries-Included Mode
When using the Vercel AI Provider, embeddings are generated automatically if you configure an embeddingProvider:
import { createCortexMemoryAsync } from '@cortex-platform/vercel-ai-provider';
import { openai } from '@ai-sdk/openai';
const cortexMemory = await createCortexMemoryAsync({
memorySpaceId: 'user-space',
// Configure embedding provider - recall() gets embeddings automatically
embeddingProvider: openai.embedding('text-embedding-3-small'),
});
// Now every recall() automatically:
// 1. Generates an embedding for the query
// 2. Uses semantic search for both memories AND facts
// No manual embedding code needed!
v0.30.0+: Configure embedding once at SDK initialization for batteries-included semantic search:
import { Cortex } from '@cortexmemory/sdk';
const cortex = new Cortex({
convexUrl: process.env.CONVEX_URL!,
// Configure embedding provider - batteries included!
embedding: {
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY,
model: 'text-embedding-3-small', // optional, this is the default
},
});
// Now every recall() automatically:
// 1. Generates an embedding for the query
// 2. Uses semantic search for both memories AND facts
// No manual embedding code needed!
const result = await cortex.memory.recall({
memorySpaceId: 'user-space',
query: "What are the user's preferences?",
// Embedding is auto-generated from the query!
});
// remember() also auto-generates embeddings for facts
await cortex.memory.remember({
memorySpaceId: 'user-space',
userMessage: "I prefer dark mode",
agentResponse: "Got it!",
// Facts extracted are automatically given embeddings
});
You can also use environment variables for zero-config setup:
# Set these env vars
export CORTEX_EMBEDDING=true
export OPENAI_API_KEY=sk-...
// SDK auto-configures embedding from env vars!
const cortex = await Cortex.create({
convexUrl: process.env.CONVEX_URL!,
});
You can still pass embeddings manually for full control:
const result = await cortex.memory.recall({
memorySpaceId: 'user-space',
query: "What are the user's preferences?",
// Override: provide your own embedding
embedding: await myCustomEmbeddingFn("What are the user's preferences?"),
});
This is useful when you want to use a different embedding model per-query, have pre-computed embeddings, or need custom embedding logic.
Without embedding configuration, recall() still works but uses keyword-based text search for facts:
// This works, but facts are searched by keywords only
const result = await cortex.memory.recall({
memorySpaceId: 'user-space',
query: "What colors does the user like?",
// No embedding config - falls back to text search
});
See Fact Extraction for how to configure automatic or custom fact extraction from conversations.
Using Facts in Retrieval
recall() searches facts as a primary source, not just enrichment. No extra configuration needed.
// With embedding - uses semantic search for facts (recommended)
const result = await cortex.memory.recall({
memorySpaceId: 'user-space',
query: "What are the user's preferences?",
embedding: await embed("What are the user's preferences?"), // Enables semantic fact search
});
// Facts included automatically in sources
console.log(`Found ${result.sources.facts?.items.length || 0} facts`);
result.sources.facts?.items.forEach(fact => {
console.log(`- ${fact.fact} (${fact.confidence}% confidence)`);
});
// LLM-ready context includes facts
console.log(result.context);
// "## Known Facts
// - User prefers dark mode (95% confidence)
// - User likes TypeScript (88% confidence)
//
// ## Recent Memories
// ..."
v0.30.0+: When embedding is provided, facts are searched semantically—meaning "What colors does the user like?" can find "User prefers purple" even without the word "color" in the fact.
Filter Facts in recall()
// Get only high-confidence facts
const result = await cortex.memory.recall({
memorySpaceId: 'user-space',
query: 'user preferences',
sources: {
facts: {
minConfidence: 80,
factType: 'preference',
isSuperseded: false, // Current beliefs only
},
},
});
Direct Fact Operations
await cortex.facts.store({
memorySpaceId: 'user-space',
userId: 'user-123',
fact: 'User prefers TypeScript over JavaScript',
factType: 'preference',
subject: 'user-123',
predicate: 'language_preference',
object: 'TypeScript',
confidence: 90,
sourceType: 'conversation',
tags: ['coding', 'preferences'],
});
const facts = await cortex.facts.search(
'user-space',
'language preferences',
{
factType: 'preference',
minConfidence: 70,
userId: 'user-123',
}
);
facts.forEach(f => console.log(`${f.fact} (${f.confidence}%)`));
// Get all preferences
const prefs = await cortex.facts.list({
memorySpaceId: 'user-space',
factType: 'preference',
isSuperseded: false, // Current only
});
// Get all identity facts
const identity = await cortex.facts.list({
memorySpaceId: 'user-space',
factType: 'identity',
});
// Update confidence after validation
await cortex.facts.update('user-space', factId, {
confidence: 95, // Increased confidence
tags: [...existingTags, 'verified'],
});
Fact Types
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
preference | FactType | No | — | User likes/dislikes something (e.g., 'User prefers dark mode') |
identity | FactType | No | — | Who someone is (e.g., 'User is a software engineer') |
knowledge | FactType | No | — | Something the user knows (e.g., 'User knows Python') |
relationship | FactType | No | — | Connection between entities (e.g., 'User works_at Google') |
event | FactType | No | — | Something that happened (e.g., 'User joined on 2025-01-15') |
observation | FactType | No | — | System-observed behavior (e.g., 'User logs in daily') |
Fact Versioning
When a fact is updated or superseded via belief revision, previous versions are preserved for audit trails.
// Get complete version history
const history = await cortex.facts.getHistory('user-space', factId);
history.forEach(version => {
console.log(`v${version.version}: ${version.fact}`);
console.log(` Confidence: ${version.confidence}%`);
console.log(` Updated: ${new Date(version.updatedAt).toISOString()}`);
});
// Get supersession chain (evolution of belief)
const chain = await cortex.facts.getSupersessionChain(factId);
// Returns: [oldest] -> [older] -> [current]
chain.forEach((entry, i) => {
console.log(`${i + 1}. ${entry.fact}`);
if (entry.supersededBy) {
console.log(` Superseded by: ${entry.supersededBy}`);
}
});
Best Practices
// Bad: Compound fact
{ fact: 'User is 25 years old from California and likes hiking' }
// Good: Atomic facts
[
{ fact: 'User is 25 years old', factType: 'identity' },
{ fact: 'User is from California', factType: 'identity' },
{ fact: 'User likes hiking', factType: 'preference' },
]
// Always use same format for subject
extractFacts: async (userMsg, agentResp) => [{
fact: 'User prefers email notifications',
subject: userId, // Consistent ID
confidence: 90,
}],
{
fact: 'User speaks English and Spanish',
factType: 'knowledge',
tags: ['language', 'communication', 'multilingual'],
}