TL;DR: I built Khoj, an AI agent that checks Bangalore property launches by following who is connected to whom in a FalkorDB graph. It scores risk as a sum over graph paths (Katz centrality), explains every score as a path you can see, and remembers each buyer across sessions. On 200 held-out projects that all look clean on paper, looking at each project's own records finds 0% of the hidden risk. The graph finds 81%, with zero false alarms.
Code: https://github.com/ibrxhimj/khoj
If you've looked at new residential launches in Bangalore, you know the brochure: rendered towers, a clubhouse, "RERA approved", a launch offer. What you don't see is the history of the people behind it.
Developers usually create a fresh company for each project. That's normal and legal, but it means a brand-new company with a clean record can be run by the same directors whose last company went insolvent and left buyers with half-built towers. The plot might have a previous owner tied up in a title suit. The contractor might be two years late on three other sites.
None of that is hidden. It sits in RERA filings, company records, court records and land records. It's just never connected for the buyer.
💡 The answer to "is this safe?" doesn't live in any single record. It lives in the relationships between them.
That's a graph problem. So I built Khoj (Hindi for "search").
The knowledge graph models what a careful property lawyer would look at:
CONTROL companies; people are DIRECTOR_OF companies.DEVELOPS a project, which is BUILT_ON a land parcel that was PREVIOUSLY_OWNED_BY someone.BUILDS projects, and approvals hang off each project.AGAINST projects; court cases INVOLVE land, companies, people or contractors.
Anything that's a warning sign on its own (a stalled project, an insolvent company, a pending court case, a B-khata plot) gets a :RedFlag label with a severity and a reason.
The data is synthetic but realistic: 91 projects, 46 companies and 100 people across real Bangalore localities, generated from a fixed seed. All names are fictional. My favourite planted story is Skyvista Altura: a brand-new launch in Sarjapur Road with RERA, A-khata land and zero complaints. It looks perfect, until you notice its company shares two directors with Northstar Habitats, which is in insolvency proceedings and left two projects stalled.
I wanted a risk score that was explainable by construction, so that every point of it had a path behind it.
For each project, one Cypher query finds every path of up to four hops to a red flag:
MATCH path = (p:Project {id: $id})-[:DEVELOPS|DIRECTOR_OF|CONTROLS|BUILT_ON|
PREVIOUSLY_OWNED_BY|INVOLVES|AGAINST|HAS_APPROVAL|BUILDS*1..4]-(f:RedFlag)
WHERE f <> p
AND none(n IN nodes(path)[1..-1] WHERE n.kind = 'Project')
AND size([r IN relationships(path) WHERE type(r) = 'PREVIOUSLY_OWNED_BY']) <= 1
RETURN f.severity AS severity, [r IN relationships(path) | type(r)] AS rels, nodes(path) AS nodes
Each path contributes its flag's severity, weakened by distance and by how strong each link is:
K(p) = Σ over paths P: severity(f) · 0.6^(hops−1) · Π edge weights
score = 100 · (1 − e^(−K))
If that looks familiar, it's Katz centrality, restricted to red-flag targets. Summing over all paths was deliberate: a company that shares two directors with a failed developer should look worse than one that shares one. For Skyvista Altura, the 10 paths add up to K = 1.87, which gives 84.6 out of 100.
My first version scored Skyvista Altura correctly, and also scored the median project at 94 out of 100. Contractors and land aggregators are hubs. Paths ran from a project to its contractor, to another project, to that project's 30 complaints, and everything looked dangerous.
Two structural rules fixed it:
The median dropped to 28, clean projects scored 0, and the planted stories still stood out.
Two questions every buyer should ask, "who is really behind this brand?" and "is anyone here a serial operator?", map directly onto FalkorDB's built-in algorithms:
Betweenness in FalkorDB follows edge direction. My edges all pointed Person → Company, so no shortest path ever passed through a person, and every score came back zero. Adding a reverse HAS_DIRECTOR edge for each directorship fixed it. I've documented this, with a minimal reproduction, in the repo.
It's tempting to hand the model the schema and let it write queries. I didn't, following the organizers' own guide to building graph agents. The agents get a fixed catalogue of eight human-written, parameterised tools served over MCP. The model picks a tool and fills in arguments, and every query runs read-only.
Every result also says what kind of answer it is:
| Status | Meaning |
|---|---|
ANSWERED_WITH_PATH |
the graph held the relationships; here are the paths |
ANSWERED_FROM_MEMORY |
an earlier finding, with its date |
NOT_IN_GRAPH |
no known connection, which is not the same as safe |
UNKNOWN_ENTITY |
the name doesn't resolve, so no query runs |
🧱 Agents propose, code disposes. Before a finding is saved, a deterministic verifier recomputes the score from the graph, checks the verdict against fixed rules, and rejects any evidence that names an entity the tools never returned. The model's own number is always replaced by the computed one.
A due-diligence assistant that forgets your budget every time isn't much use. FalkorDB can hold many graphs in one instance, so each buyer gets their own memory graph and nothing leaks between buyers. It holds:
SUPERSEDES the old one);
There are two agents. The Concierge talks to you and owns memory. The Investigator owns the market graph. They hand off through the graph itself: the Concierge opens a Case node, the Investigator writes Finding nodes into it, and the Concierge reads it back.
The moment I like most: shortlist two clean projects, then add some market events to the graph (a contractor's insolvency petition, a new title suit). When you come back, Khoj greets you with "Whispering Woods went from 0 to 52" and the exact new path, before you've asked anything.
"Remove the database and it breaks" is easy to claim, so I measured it. A benchmark injects 200 new projects into a copy of the graph, every one with a clean surface. Half hide one of four risk patterns; half are clean controls with traps, like sharing a locality and a bank with a stalled project.
The surprising row is the ablation without my two structural rules: it finds nearly everything but falsely flags 93% of clean projects. The graph matters, but so does how you walk it. I also recomputed every score with a plain-Python path enumeration: 0 mismatches with FalkorDB.
A title suit naming only the plot's previous owner scored just under the threshold, because the model decayed it like any other hop. In property law that's wrong: you inherit your vendor's title. So title-chain hops now carry no decay. I made that change after seeing the benchmark, and the report says so.
The schema maps directly onto real public sources: K-RERA, MCA company filings, eCourts and Bhoomi land records. With real outcomes, the hand-set severities could be fitted instead of chosen.
The graph isn't just storage for an agent. It's what makes the agent's answers checkable. When Khoj says "avoid", it shows you the path, and you can verify it yourself.
*Khoj is open source: https://github.com/ibrxhimj/khoj. Built with FalkorDB, MCP, Streamlit.