Maintaining an organizational knowledge graph with an LLM and event sourcing

pdabrowski62 pts0 comments

Maintaining an organizational knowledge graph with an LLM and event sourcing | Arkency Blog

Maintaining an organizational knowledge graph with an LLM and event sourcing

Organizations are surprisingly good at forgetting.

Decisions are made on calls, insights get buried in Slack threads, and a month later no one remembers why things are the way they are.

At Arkency, I had a feeling that some things slip away from us too from time to time.

Weekly calls, ad-hoc meetings, our book clubs, Slack discussions, GitHub mentions, e-mail inbox - we could use some support in organizing all those signals.

Then Ruby Community Conference 2026 happened in March.

In Kraków, Obie Fernandez showed some parts of his NEXUS system.

He had already described it on his blog back in January, but the conference was where I first came across it.

That was the push I needed to start building our own software.

When it was already taking shape, Andrej Karpathy published his LLM Wiki note.

Instead of a RAG system rediscovering your documents on every query, an LLM incrementally maintains a persistent wiki: interlinked markdown pages, immutable sources underneath, and a human curating the loop.

It was quite exciting to realize I was working on something that had just become one of the hottest topics in the industry.

We ended up with Planet Arkency - a multi-tenant knowledge graph with a closed ontology , built on Rails Event Store.

In this post, I want to walk you through the design decisions I made.

Unstructured input is where LLMs actually shine

For structured data, you could have built such a system like twenty years ago.

Webhooks, forms, integrations - parsing structured input into a graph is a solved problem.

But the most interesting knowledge lives in the input no parser could ever handle: meeting transcripts, Slack discussions, emails, or anything coming from an integration nobody has built yet.

This is where LLMs changed the game for us.

Everything flows into the system through a single ingestion endpoint.

Transcripts, Slack threads someone flagged with a dedicated emoji reaction, emails arriving at a bridge inbox, RSS feeds, calendar invites, personal notes.

We don’t even write code for the integration points.

Tools like Zapier or n8n watch the sources and push the content to that single endpoint.

Every ingested piece of content then goes through an extraction - the heart of the system.

An LLM reads the content and works out what it means for our knowledge: which entities appear in it, what we learned about them, and how they relate to each other.

Most of this post is about what happens around that single step.

Why a graph?

The same names keep coming back in our conversations: people, projects, clients, tools, decisions.

What changes from week to week is what we know about them and how they relate to each other.

That maps naturally to a graph: entities with attributes, connected by typed relations.

Who works on what.

Who made which decision, and when.

Which project depends on which tool.

This is where we differ most from the LLM Wiki approach.

In a wiki, the fact that someone works on some project is written down in a sentence on a page, at best with a link between the two pages.

The knowledge is there, but only a reader can make use of it.

In a typed graph, person --works_on--> project is a piece of data: you can query it, traverse it, count it.

The graph itself sits on PostgreSQL: a nodes table, an edges table with a unique (source, target, relation) triple, jsonb attributes on both.

No rocket science here.

Dedicated graph databases (Neo4j, triple stores like the one NEXUS uses) could be a better fit for some specific workloads, like deep multi-hop traversal.

But that is a storage detail - the kind you could change later by writing another adapter for the data layer.

The ontology

Which kinds of nodes and relations may exist is defined in an ontology , stored in a plain YAML file:

# from config/ontology.yml<br>node_kinds:<br>- kind: person<br>description: "team member, candidate, client contact, external person"<br>- kind: decision<br>description: "formal decision requiring group verdict — for casual suggestions use idea"<br>edge_relations:<br>- relation: works_on<br>signature: "person --works_on--> project"

The ontology is closed - if a kind or relation is not on the list, the model cannot use it.

Initially I was thinking about an open ontology, where the LLM could introduce its own types.

It brought complete chaos into the graph surprisingly fast.

In my opinion, it is better to tell the model upfront what to look for.

Not one graph, but many

&ldquo;The organizational knowledge graph&rdquo; suggests one universal graph for all different purposes.

We don&rsquo;t believe in that, and DDD practitioners will recognize why.

We use multi-tenant architecture to maintain separate graphs with their own ontologies, which really means their own ubiquitous languages.

Our internal Arkency graph speaks in...

graph knowledge from ontology system organizational

Related Articles