Gary
AI Agent for Company Knowledge
An AI agent for company knowledge. Gary reads Slack, Jira, Notion, meeting notes and GitHub into a FalkorDB graph, then answers by walking it: what is true right now, what used to be true, who decided it, why, and which docs are quietly lying.

- 12
- Fixed graph tools
- ~20 ms
- Graph time per answer
- 65
- Synthetic records
- 5
- Source tools
Demo video
A three-minute walkthrough of Gary on the Kitestring demo company.
Overview
Every company has a Gary. He has been there since the early days. Ask him why the refund window is 14 days and he tells you the whole story in two minutes: the refund abuse in July, the Slack thread where the CEO approved the change, and the old Notion page you should stop trusting.
The day Gary goes on holiday, things slow down. The day he leaves, the company simply forgets. So I built a Gary that never leaves.
Instead of throwing every document into a search index, Gary builds a graph of people, teams, systems, projects, every record, and most importantly the decisions people made and which older decisions they replaced. Questions are answered by walking that graph, so every answer carries its path.
Built for Graph Hacks 2026, the FalkorDB and WeMakeDevs hackathon, in October 2026. Main track: Company Brain.
The Problem
Company knowledge does not live in one place, and it does not stay true.
In the demo company, a Notion page from February says customers can get a full refund within 30 days. In June, a pricing meeting changes the rule for annual plans. In August, the CEO cuts the monthly window to 14 days in a Slack thread because of refund abuse. Nobody updates the Notion page, the support playbook or the public help center article.
Now support asks: “Can a customer get a full refund after 20 days?” A normal document chatbot finds the pages with the right words and says yes, 30 days. Very confident. Completely wrong.
Similarity search can find text that sounds like the answer. It cannot tell you which of three contradicting documents is still true, because that information lives in the relationships between them, not in the words.
Goals of the Project
| Goal | How it is met |
|---|---|
| Know what is true right now | Every decision is linked to the one it replaced, so answers follow SUPERSEDES to the current one. |
| Find stale docs on its own | Any doc that still ASSERTS a replaced decision is flagged, together with its author. |
| Never invent a connection | The model picks from 12 fixed, parameterised Cypher tools and never writes its own queries. |
| Show the work | Every step lists the exact Cypher, the row count and the time it took. |
| Say “I don't know” | Names are resolved against the graph first. Anything unknown is reported as unknown, not guessed. |
| Work with other agents | The same tools are exposed over MCP for Claude Desktop, Claude Code or any MCP client. |
System Architecture
Records flow in through one ingestion pipeline and come out as nodes and edges. Questions flow through an agent that can only read the graph through a fixed tool catalogue, and every answer is checked against the rows those tools returned before it reaches the screen.
Decision extraction is the interesting part of ingestion. The language model reads each record while looking at the decisions currently in force, so it can say which older decision a new one replaces, why it was made and which docs repeat it. Reads go through GRAPH.RO_QUERY, so the agent side physically cannot write.
The Graph Model

| Node | What it is |
|---|---|
| Person, Team | Who works here, who leads what, and when they joined or go on leave |
| System, Project, Topic | The things people work on and talk about, like ledger-db or the refund policy |
| Message, Ticket, Doc, Meeting, PR | Every record from the five tools, with its author, date and link |
| Decision | A rule, plan or date someone agreed on, with the day it started and the day it was replaced |
| Finding | Gary's own memory of past answers |
Two relationships carry most of the weight. SUPERSEDES links a newer decision to the one it replaced, and it is the heart of Gary. ASSERTS links a doc or ticket to a decision it repeats, so the moment that decision is replaced, the doc is stale.
Key Features
Decision lineage and stale docs
Gary answers with the decision that is in force today, shows the chain of decisions it replaced, and lists every living doc that still states an old one. Fix it opens a ticket for each stale doc, assigned to the person who wrote it.

The time machine
Gary never deletes a decision. A replaced decision stays in the graph with the date it stopped being true, so the whole company can be rewound to any day. Press play and records appear on the day they were written, people show up when they joined, decisions turn grey when they are replaced and docs turn red on the day they went stale.

Who actually knows, and risk radar
Not the org chart. Expertise is weighted by role, with authored work counting more than reviewed work and asking a question counting less than answering one, and older work fades over about four months. If one person holds most of the knowledge about a system and is about to go on leave, Gary flags it and suggests backups computed in Cypher.

Memory that expires
Every answer is saved as a Finding node that CITES the decisions it relied on. When one of those decisions is superseded, the memory is flagged as outdated instead of being trusted blindly.

More of what Gary can do
- Catch up: “I'm back from three weeks off, what did I miss?” returns only the changes in your part of the company.
- The real why: usage pricing launches on December 1 because the ledger migration moved, which moved because of the SOC2 audit freeze. Three hops, all walked.
- Live feed: post a new Slack message and the graph updates in milliseconds, superseding old decisions on the spot.
- Graph algorithms as tools: shortest path, betweenness centrality and label propagation run as FalkorDB procedures.
- MCP: every tool is available to Claude Desktop, Claude Code or any MCP client.
Proof the Graph Does the Work
A fair test for any graph project is simple: take the graph away and see what breaks. The app runs a plain document chatbot next to Gary with the same records, the same language model and FalkorDB's own full-text index, just without the relationships.
| Question | Plain RAG | Gary |
|---|---|---|
| Can a customer get a full refund after 20 days? | Yes, 30 days | No, 14 days since August 12, decided by the CEO in #billing, and three docs still say 30 |
| Who should I talk to about ledger-db? | Whoever is mentioned most | Tomasz, but he is on leave from November 2, so here are two backups |
| Who owns the mobile app? | A plausible-sounding team | There is no mobile app in the graph |
Current versus replaced decisions, stale documents, who knows what, single points of knowledge, the time machine, multi-hop “why” answers and memory that expires are all walks through the graph. None of them can come from text similarity alone.
The Cypher Behind It
The agent picks from 12 tools: decision_lineage, who_knows, profile, connect, risk_radar, real_teams, as_of, catch_up, search, open_ticket, fix_stale_docs and remember. Each one is a fixed query with parameters. Three of them do most of the heavy lifting.
MATCH (s:Entity {key:$key})
MATCH (cur:Decision {status:'active'})-[:ABOUT]->(x)
WHERE x = s OR (s)-[:USES]->(x)
OPTIONAL MATCH chain = (cur)-[:SUPERSEDES*1..6]->(:Decision)
WITH cur, chain ORDER BY length(chain) DESC
WITH cur, collect(chain)[0] AS longest
RETURN cur.key AS key, [n IN nodes(longest) | n.key] AS lineageMATCH (cur:Decision)-[:SUPERSEDES*1..6]->(old:Decision) WHERE cur.key IN $heads
MATCH (doc:Doc {living:true})
WHERE ((doc)-[:ASSERTS]->(old) OR (old)-[:DECIDED_IN]->(doc))
AND NOT (doc)-[:ASSERTS]->(cur)
OPTIONAL MATCH (a:Person)-[:AUTHORED]->(doc)
RETURN DISTINCT doc.key AS doc, doc.title AS title, old.key AS old,
cur.key AS current, a.name AS authorMATCH (s:Entity {key:$key})
MATCH (d:Decision)-[:ABOUT]->(x)
WHERE (x = s OR (s)-[:USES]->(x))
AND d.at <= $t
AND (d.superseded_at IS NULL OR d.superseded_at > $t)
OPTIONAL MATCH (n:Decision)-[:SUPERSEDES]->(d)
RETURN d.statement, d.at, n.statement AS later_replaced_by, n.at AS replaced_onApplication Flow
Challenges and Solutions
| Challenge | Solution |
|---|---|
| The model inventing graph connections | No model-written Cypher. The model chooses from 12 fixed, parameterised tools. |
| Real citations next to an invented team | My own demo recording caught a made-up “Product-Mobile team”. Every proper name in an answer must now exist in the graph or the turn's tool results, otherwise the answer is rebuilt from the rows. |
| Guessing when a subject does not exist | Names are resolved first: exact, then alias, then full-text with at least 50% word coverage. No match means unknown_subject. |
| The model getting supersessions wrong | Extraction ran with gpt-oss-120b and was reviewed by hand. One PR was wrongly treated as replacing the release policy and was fixed. |
| Rate limits on free model hosting | A fallback chain from gpt-oss-120b to gpt-oss-20b to qwen3.8-27b, and cached extractions for the seed data. |
| Remembered answers going quietly wrong | Memory is just nodes. A Finding CITES its decisions and expires when any of them is superseded. |
| Visitors breaking the shared demo | A Reset demo button rebuilds the whole graph from the raw data in a few seconds. |
Tech Stack
| Layer | Choice | Why |
|---|---|---|
| Graph | FalkorDB on FalkorDB Cloud | Cypher, graph algorithms and full-text search in one database |
| Framework | Next.js 16, App Router | API routes stream NDJSON agent events as they happen |
| Language | TypeScript | One language across ingestion, tools, agent and UI |
| LLM | Groq: gpt-oss-120b with fallbacks | Any OpenAI-compatible endpoint works, including local Ollama |
| Graph view | react-force-graph-2d | Highlights exactly the nodes an answer used |
| Agents | Model Context Protocol SDK | Gary as a company brain for other agents |
| Motion | Motion, anime.js, Lenis | The landing page, the live graph and the time machine |
| Hosting | Vercel | The app and the API routes |
Deployment and Data Ethics
The app runs on Vercel against a FalkorDB Cloud instance, and the same graph can be run locally with Docker Compose, which also brings up the FalkorDB Browser for poking at the graph directly. One graph per company is already supported through GRAPH_NAME.
All data is synthetic. Kitestring is a made-up 21-person billing startup with 65 records across Slack, Jira, Notion, meetings and GitHub. Its storylines, like two refund policy reversals, a SOC2-delayed migration and an engineer going on leave mid-cutover, are written so the right answer always depends on how things connect. No private, personal or scraped data is used anywhere.
Conclusion
Most “chat with your docs” tools treat company knowledge as a pile of text. Gary treats it as a history of decisions, and that one change is what lets it tell current from replaced, find the docs that are lying, and admit when it does not know.
Next up are real connectors with webhooks instead of exported files, permissions that travel with every record so people only see what they are allowed to see, and a multi-tenant UI on top of the one-graph-per-company support that already exists.