Knowledge Graphs
Extract entities and relationships from conversations, store them in a graph, run inference rules, and enrich agent context with structured knowledge.
Overview
Knowledge graphs give agents structured memory beyond flat conversation history. Instead of treating past messages as opaque text, the knowledge graph extracts entities (people, organizations, concepts) and relationships (knows, works_at, part_of) into a queryable graph.
This enables agents to:
- Answer questions about relationships between entities
- Discover indirect connections through graph traversal
- Infer new facts from existing relationships
- Build richer context by injecting relevant graph data into prompts
Architecture
Conversation ──► LLMEntityExtractor ──► GraphAdapter (storage)
│
GraphInferenceEngine (rules)
│
GraphContextBuilder (retrieval)
│
Agent PromptEntity Extraction
The LLMEntityExtractor uses an LLM to identify entities and relationships from text. It prompts the model for structured JSON output, then validates and filters the results.
import { LLMEntityExtractor } from '@cogitator-ai/memory';
const extractor = new LLMEntityExtractor(llmBackend, {
minConfidence: 0.7,
maxEntitiesPerText: 20,
maxRelationsPerText: 30,
});
const result = await extractor.extract(
'Alice works at Acme Corp in New York. She knows Bob from the engineering team.'
);
console.log(result.entities);
// [
// { name: 'Alice', type: 'person', confidence: 0.95 },
// { name: 'Acme Corp', type: 'organization', confidence: 0.95 },
// { name: 'New York', type: 'location', confidence: 0.9 },
// { name: 'Bob', type: 'person', confidence: 0.9 },
// ]
console.log(result.relations);
// [
// { sourceEntity: 'Alice', targetEntity: 'Acme Corp', type: 'works_at', confidence: 0.95 },
// { sourceEntity: 'Acme Corp', targetEntity: 'New York', type: 'located_in', confidence: 0.85 },
// { sourceEntity: 'Alice', targetEntity: 'Bob', type: 'knows', confidence: 0.9 },
// ]Entity Types
person, organization, location, concept, event, object
Relation Types
knows, works_at, located_in, part_of, related_to, created_by, belongs_to, associated_with, causes, precedes
Extraction Context
Provide hints to improve extraction quality:
const result = await extractor.extract(text, {
entityTypeHints: ['person', 'organization'],
relationTypeHints: ['works_at', 'knows'],
existingEntities: ['Alice', 'Acme Corp'],
});Existing entities help the LLM disambiguate references and avoid creating duplicates.
Graph Adapter
The PostgresGraphAdapter stores nodes and edges in PostgreSQL with full indexing, semantic search via pgvector, and recursive traversal using CTEs.
import { PostgresGraphAdapter } from '@cogitator-ai/memory';
import pg from 'pg';
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL!,
});
const graph = new PostgresGraphAdapter({
pool,
schema: 'cogitator',
vectorDimensions: 1536,
});The adapter auto-creates graph_nodes and graph_edges tables on first use.
Adding Nodes and Edges
const { data: alice } = await graph.addNode({
agentId: 'agent-1',
type: 'person',
name: 'Alice',
aliases: ['alice'],
description: 'Engineer at Acme Corp',
properties: { role: 'backend' },
confidence: 0.95,
source: 'inferred',
});
const { data: acme } = await graph.addNode({
agentId: 'agent-1',
type: 'organization',
name: 'Acme Corp',
aliases: ['Acme'],
properties: {},
confidence: 0.95,
source: 'inferred',
});
await graph.addEdge({
agentId: 'agent-1',
sourceNodeId: alice.id,
targetNodeId: acme.id,
type: 'works_at',
weight: 1.0,
bidirectional: false,
properties: {},
confidence: 0.95,
source: 'inferred',
});Querying
const { data: people } = await graph.queryNodes({
agentId: 'agent-1',
types: ['person'],
namePattern: 'Ali',
minConfidence: 0.8,
limit: 10,
});
const { data: edges } = await graph.queryEdges({
agentId: 'agent-1',
types: ['works_at', 'knows'],
minConfidence: 0.7,
});Graph Traversal
Traverse the graph from a starting node, following edges up to a specified depth:
const { data: traversal } = await graph.traverse({
agentId: 'agent-1',
startNodeId: alice.id,
maxDepth: 2,
direction: 'both',
edgeTypes: ['knows', 'works_at'],
minConfidence: 0.6,
});
for (const path of traversal.paths) {
const names = path.nodes.map((n) => n.name).join(' -> ');
console.log(`${names} (weight: ${path.totalWeight})`);
}Shortest Path
Find the shortest path between two nodes using recursive CTEs:
const { data: path } = await graph.findShortestPath('agent-1', alice.id, bobNodeId, 5);
if (path) {
console.log(`Path length: ${path.length}, weight: ${path.totalWeight}`);
}Semantic Node Search
When nodes have embeddings, search by meaning:
const vector = await embeddingService.embed('backend engineering');
const { data: nodes } = await graph.searchNodesSemantic({
agentId: 'agent-1',
vector,
limit: 5,
threshold: 0.7,
entityTypes: ['person', 'concept'],
});Node Merging
Merge duplicate nodes, redirecting all edges to the target:
await graph.mergeNodes(primaryNodeId, [duplicateId1, duplicateId2]);Inference Engine
The GraphInferenceEngine applies rules to discover implicit relationships. It ships with default rules and supports custom ones.
import { GraphInferenceEngine } from '@cogitator-ai/memory';
const engine = new GraphInferenceEngine(graph);
const inferred = await engine.infer('agent-1', {
minConfidence: 0.6,
maxInferences: 100,
});
console.log(`Found ${inferred.length} inferred relationships`);Default Rules
| Rule | Pattern | Conclusion | Confidence |
|---|---|---|---|
| transitive_knows | A knows B, B knows C | A related_to C | 0.6 |
| colleagues | A works_at X, B works_at X | A associated_with B (colleague) | 0.8 |
| location_hierarchy | A located_in B, B located_in C | A located_in C | 0.9 |
| part_of_hierarchy | A part_of B, B part_of C | A part_of C | 0.85 |
| causality_chain | A causes B, B causes C | A causes C (indirect) | 0.7 |
Custom Rules
engine.registerRule({
name: 'team_membership',
description: 'People who belong to the same team are associated',
pattern: {
edgeTypes: ['belongs_to', 'belongs_to'],
minPathLength: 2,
maxPathLength: 2,
nodeTypeConstraints: {
0: ['person'],
1: ['concept'],
2: ['person'],
},
},
conclusion: {
edgeType: 'associated_with',
label: 'teammate',
weightFormula: 'min',
bidirectional: true,
},
confidence: 0.75,
enabled: true,
});Weight formulas: min, max, average, product -- applied to the weights of edges in the matched path.
Materializing Inferences
Save inferred edges back to the graph:
const { data: materialized } = await engine.materialize(inferred);
console.log(`Materialized ${materialized.length} edges`);Materialized edges include metadata tracking the rule that created them and the supporting path.
Graph Context Builder
The GraphContextBuilder retrieves relevant graph data for a given input and formats it as structured text to inject into the agent's prompt.
import { GraphContextBuilder } from '@cogitator-ai/memory';
const contextBuilder = new GraphContextBuilder(graph, embeddingService, {
maxNodes: 20,
maxEdges: 50,
maxDepth: 3,
});
const context = await contextBuilder.buildContext('agent-1', 'What does Alice do at Acme Corp?');
console.log(context.formattedContext);
// ## Knowledge Graph Context
//
// ### Entities
// - **Alice** (Person): Engineer at Acme Corp
// - **Acme Corp** (Organization)
//
// ### Relationships
// - Alice -> works at -> Acme Corp
console.log(`Nodes: ${context.nodes.length}, Edges: ${context.edges.length}`);
console.log(`Estimated tokens: ${context.tokenCount}`);The builder performs keyword matching and semantic search on graph nodes, then traverses outward from matched nodes to gather related context. Nodes are ranked by name match, alias match, keyword overlap, access frequency, and confidence.
| Option | Type | Default | Description |
|---|---|---|---|
maxNodes | number | 20 | Maximum nodes to include in context |
maxEdges | number | 50 | Maximum edges to include in context |
maxDepth | number | 3 | Graph traversal depth from matched nodes |
includeInferred | boolean | true | Include edges created by inference rules |
tokensPerNode | number | 30 | Estimated tokens per node for budget |
tokensPerEdge | number | 15 | Estimated tokens per edge for budget |
Hybrid Search
Combine BM25 keyword search with vector similarity using Reciprocal Rank Fusion for accurate, recall-maximizing retrieval.
RAG Pipeline
Build retrieval-augmented generation pipelines with @cogitator-ai/rag — document loading, chunking, embedding, retrieval, and reranking in a single composable pipeline.