Skip to content
nodus

Building through graphs

UNDER the HOOD

For judges and engineers: how NODUS actually works, what it was tested on, and where to read the code.

The graph

FalkorDB holds everything: the made-up company (plans, systems, alert inboxes, people, past incidents), the lessons with the sentences they quote, and the agent's memory of every warning and answer. Remove FalkorDB and nothing can be matched.

Lessons as data

Each lesson is 3 to 6 conditions of four kinds: something is present, something is absent, a property has a value, or there is exactly a certain number of something. Every condition quotes its source word for word, checked by a script; a person approves the lesson.

A compiler, not an AI

A small compiler turns each condition into one parameterised Cypher query from a hand-written template. Names are checked against the graph model, paths are type-checked, values travel as parameters. No AI writes a query that runs.

Anchor-first matching

Every query starts from the plan. When one condition finds something (an alert inbox, a procedure), its id is passed to the next, so “the inbox” means the same inbox throughout. With two candidates, the one giving the most matches wins.

A published rule

All conditions true: full shape. All but the lesson's last step: one step away. Otherwise a note or nothing. It's a rule anyone can read, not a judgement.

The agent and AG-UI

Saving a plan runs the loop: notice, compare, count, warn, remember. The same run streams as standard AG-UI events, so every graph query appears as a tool call with its arguments and result.

The real queries

Two conditions, as they run

These are produced by the compiler for the demo plan. Relationships are always named when counted: we found that FalkorDB counts two parallel unnamed relationships as one.

Read the compiler ↗
// "This plan does not follow the staged procedure" (an absence)
MATCH (s:Plan) WHERE s.id = $start
OPTIONAL MATCH (s)-[r1:FOLLOWS]->(t) WHERE t.id = $target
RETURN count(r1) = 0 AS holds, [s.id] AS ids

// "Nobody active is watching that inbox"
MATCH (s) WHERE s.id = $start
OPTIONAL MATCH (s)<-[r1:WATCHES]-(t:Person) WHERE t.active = $tp0
RETURN count(r1) = 0 AS holds, [s.id] AS ids
Evaluation, declared before it was run

Synthetic test, run once

Test splitNODUSKeyword search
Same structure, different words: right lesson raised18 of 1818 of 18 only at threshold 0; 1 of 18 above it
Same words, different structure: false alarms0 of 1818 of 18
Unrelated plans: false alarms0 of 1010 of 10
One condition missing: the right one named20 of 20not applicable

The test plans are generated from the same definitions as the lessons, so this shows the queries implement the lessons and that wording is neither needed nor enough. It is not evidence of accuracy on real company data. Full details are in eval/RESULTS.md.

Code ↗

Python engine, FastAPI service, this Next.js site, 129 tests.

Graph model ↗

Every node and relationship, and the condition each one exists for.

Build diary ↗

Every step, decision, problem and fix, written as it happened.