Knowledge Graph
⏱️ Time to Complete: 20 minutes 🎯 Level: Intermediate 💻 Language: TypeScript
What You'll Build
A knowledge graph that automatically extracts:
✅ Entities (people, organizations, places, events, products)
✅ Relationships between entities
✅ Queryable structure from unstructured content
✅ Multi-hop reasoning across sources
Why Knowledge Graphs Matter
Traditional search: "Find documents mentioning Sarah" Knowledge graph: "Find all interactions between Sarah Chen at Acme Corp and our Sales team, including meetings, emails, and Slack conversations"
The difference:
✅ Entity recognition (Sarah Chen = person, Acme Corp = organization)
✅ Relationship tracking (Sarah works at Acme, spoke with Sales)
✅ Semantic understanding (group related mentions across sources)
✅ Structured queries on unstructured data
Real-world example: Zine uses knowledge graphs to answer "Who from Acme Corp have we talked to?" across Slack, email, meetings, and CRM notes.
Prerequisites
Complete the Quickstart tutorial
Graphlit project with API credentials configured in
.envnpm install graphlit-client dotenv
Step 1: Extract Entities from Content
Create a workflow, ingest content, and extract entities - all in one script:
import { Graphlit } from 'graphlit-client';
import {
SpecificationTypes,
ModelServiceTypes,
OpenAiModels,
FilePreparationServiceTypes,
EntityExtractionServiceTypes,
ObservableTypes,
} from 'graphlit-client/dist/generated/graphql-types';
const graphlit = new Graphlit();
const SAMPLE_NOTE = `
Met with Sarah Chen, CTO at Acme Corp, at their San Francisco office.
She's evaluating our API for their 800-user deployment.
Key attendees:
- Sarah Chen (CTO, Acme Corp)
- Mike Rodriguez (VP Engineering, Acme Corp)
- Alex Kim (Solutions Engineer, our team)
They need CRM integration and mentioned Salesforce as their current tool.
Looking to deploy by Q4 2025. Budget approved for Enterprise tier.
Follow-up: Technical demo scheduled for next Tuesday in Acme's office.
`;
async function main() {
console.log('⚙️ Creating extraction workflow...');
// 1️⃣ Configure the LLM that performs entity extraction
const extractionSpec = await graphlit.createSpecification({
name: 'Knowledge Graph Extraction',
type: SpecificationTypes.Extraction,
serviceType: ModelServiceTypes.OpenAi,
openAI: {
model: OpenAiModels.Gpt5_400K,
temperature: 0, // Deterministic for consistent extraction
},
});
// 2️⃣ Build a workflow that prepares documents and extracts entities
const workflow = await graphlit.createWorkflow({
name: 'Entity Extraction Workflow',
preparation: {
jobs: [
{
connector: {
type: FilePreparationServiceTypes.Document,
},
},
],
},
extraction: {
jobs: [
{
connector: {
type: EntityExtractionServiceTypes.ModelText,
modelText: {
specification: { id: extractionSpec.createSpecification.id },
tokenThreshold: 32,
},
extractedTypes: [
ObservableTypes.Person,
ObservableTypes.Organization,
ObservableTypes.Place,
ObservableTypes.Event,
ObservableTypes.Product,
ObservableTypes.Software,
ObservableTypes.Repo,
],
},
},
],
},
});
console.log('✅ Workflow created:', workflow.createWorkflow.id);
// 3️⃣ Ingest content with the workflow (extraction happens automatically)
console.log('📄 Ingesting content...');
const content = await graphlit.ingestText(
SAMPLE_NOTE,
'Sales Call Notes',
undefined,
undefined,
undefined,
undefined,
true, // Wait for processing to finish
{ id: workflow.createWorkflow.id },
);
console.log('✅ Content ready:', content.ingestText.id);
// 4️⃣ List extracted entities
console.log('\n🔍 Extracted entities:');
const observables = await graphlit.queryObservables();
const results = observables.observables?.results ?? [];
const format = (type: ObservableTypes) =>
results
.filter((entry) => entry?.type === type)
.map((entry) => entry?.observable.name)
.filter(Boolean) as string[];
console.log('👥 People:', format(ObservableTypes.Person));
console.log('🏢 Organizations:', format(ObservableTypes.Organization));
console.log('📍 Places:', format(ObservableTypes.Place));
console.log('🛠️ Products/Software:', [
...format(ObservableTypes.Product),
...format(ObservableTypes.Software),
]);
return content.ingestText.id;
}
main().catch((error) => {
console.error(error);
process.exit(1);
});What happens:
Creates extraction specification (GPT-5, temperature 0 for deterministic results)
Creates workflow with entity extraction
Ingests sample text with the workflow
Automatically extracts entities (people, organizations, places, etc.)
Lists all extracted entities
Run: npx tsx extract-entities.ts
Step 2: Explore Relationships in the Graph
Query content and visualize the entity relationships:
import { Graphlit } from 'graphlit-client';
const graphlit = new Graphlit();
async function main() {
// Query content by name (realistic production pattern)
const contents = await graphlit.queryContents({
filter: { name: 'Sales Call Notes' }
});
const contentId = contents.contents?.results?.[0]?.id;
if (!contentId) {
console.error('Content not found. Run Step 1 first.');
process.exit(1);
}
console.log('🕸️ Exploring knowledge graph...\n');
// Get the graph for this content
const graph = await graphlit.queryContentsGraph({
contents: [{ id: contentId }],
});
const nodes = (graph.contents?.graph?.nodes ?? []).filter(
(node): node is NonNullable<typeof node> => Boolean(node),
);
const edges = (graph.contents?.graph?.edges ?? []).filter(
(edge): edge is NonNullable<typeof edge> => Boolean(edge),
);
const nodeById = new Map(nodes.map((node) => [node.id, node]));
console.log('🕸️ Relationships in this document:');
edges.forEach((edge) => {
const from = nodeById.get(edge.from);
const to = nodeById.get(edge.to);
if (!from || !to) return;
const relation = edge.relation ? ` —${edge.relation}→ ` : ' → ';
console.log(`${from.name}${relation}${to.name}`);
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});This renders the knowledge graph for a document. Each node represents a person, organization, or event; edges capture relationships Graphlit inferred (e.g., "Sarah Chen → worksFor → Acme Corp").
Step 3: Find Every Document Mentioning an Entity
import { Graphlit } from 'graphlit-client';
import { ObservableTypes } from 'graphlit-client/dist/generated/graphql-types';
const graphlit = new Graphlit();
async function findContentForPerson(personName: string) {
const observables = await graphlit.queryObservables();
const matches = observables.observables?.results?.filter(
(entry) =>
entry?.type === ObservableTypes.Person &&
entry.observable.name?.toLowerCase() === personName.toLowerCase(),
);
if (!matches?.length) {
console.log(`No entities found for ${personName}`);
return;
}
const { id } = matches[0]!.observable;
const related = await graphlit.queryContents({
observations: [
{
type: ObservableTypes.Person,
observable: { id },
},
],
});
const docs = related.contents?.results ?? [];
console.log(`
📚 Documents mentioning ${personName}:`);
docs.forEach((doc) => console.log(`- ${doc?.name} (${doc?.id})`));
}
findContentForPerson('Sarah Chen').catch((error) => {
console.error(error);
process.exit(1);
});Filtering by observations lets you answer questions like "Show me every asset where Sarah Chen appears" or "Find all notes referencing Salesforce".
Production Patterns
Batch Ingestion + Polling
// Query for your extraction workflow by name
const workflows = await graphlit.queryWorkflows({
filter: { name: 'Entity Extraction Workflow' }
});
const workflowId = workflows.workflows?.results?.[0]?.id;
// Ingest multiple files
const batch = await graphlit.ingestBatch(
['https://example.com/questionnaire.pdf', 'https://example.com/demo-notes.docx'],
{ id: workflowId },
);
// Poll until processing completes
await Promise.all(
batch.ingestBatch.ids.map(async ({ id }) => {
let done = false;
while (!done) {
const status = await graphlit.isContentDone(id);
done = Boolean(status.isContentDone?.result);
if (!done) await new Promise((r) => setTimeout(r, 1500));
}
}),
);Build Collections from Entities
const observables = await graphlit.queryObservables();
const organizations = observables.observables?.results?.filter(
(entry) => entry?.type === ObservableTypes.Organization,
);
if (organizations?.length) {
const collection = await graphlit.createCollection({
name: `${organizations[0]!.observable.name} Knowledge Base`,
});
const related = await graphlit.queryContents({
observations: [
{
type: ObservableTypes.Organization,
observable: { id: organizations[0]!.observable.id },
},
],
});
const ids = (related.contents?.results ?? [])
.filter(Boolean)
.map((item) => ({ id: item!.id }));
if (ids.length) {
await graphlit.addContentsToCollections(ids, [{ id: collection.createCollection.id }]);
}
}Keep Graph Conversations Focused
// Query for the organization entity by name
const observables = await graphlit.queryObservables({
filter: {
types: [ObservableTypes.Organization],
name: 'Acme Corp'
}
});
const targetOrganizationId = observables.observables?.results?.[0]?.observable.id;
// Create conversation filtered to that organization
const conversation = await graphlit.createConversation({
name: 'Acme Corp CRM Review',
filter: {
observations: targetOrganizationId
? [
{
type: ObservableTypes.Organization,
observable: { id: targetOrganizationId },
},
]
: undefined,
},
});
const response = await graphlit.promptConversation(
'Summarize our interactions with Acme Corp this quarter.',
conversation.createConversation.id,
);
console.log(response.promptConversation.message?.message);Real-World Examples
1. Customer 360
Aggregate Slack, email, and meeting notes per account
Surface key contacts + topics before every call
Feed a sales agent that remembers context across teams
2. Incident Response Watchtower
Track mentions of outages across Sentry, Slack, and PagerDuty
Connect incidents to affected services and owners
Push summaries to on-call staff in real time
3. Market Intelligence Radar
Monitor competitor names across research docs and news
Attach relationships between mentions, products, and regions
Drive alerts when new entities (people or products) appear
Next Steps
Knowledge Graph Use Cases – Deep-dive patterns for extraction, enrichment, and queries
Context Engineering – Feed knowledge graph insights into retrieval
Full Example: Production Agent
See the complete Next.js agent in graphlit-samples:
Visual knowledge-graph explorer with streaming chat
Entity filtering and relationship queries in the UI
Production-ready environment configuration and error handling
Build agents that understand your data model. Build with Graphlit.
Last updated
Was this helpful?