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

📁 Knowledge graph example app


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 .env

  • npm install graphlit-client dotenv

Need Python or .NET examples? Open Ask Graphlit in the Developer Portal (or visit ask.graphlit.dev) for autogenerated samples tailored to your SDK.


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


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?