<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ankit Vohra</title>
    <description>The latest articles on DEV Community by Ankit Vohra (@ankitvohra).</description>
    <link>https://dev.to/ankitvohra</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4192101%2F3beb51ff-1842-41ad-a719-d2ba26939a7d.jpg</url>
      <title>DEV Community: Ankit Vohra</title>
      <link>https://dev.to/ankitvohra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ankitvohra"/>
    <language>en</language>
    <item>
      <title>One brain per business: building Allevia on FalkorDB</title>
      <dc:creator>Ankit Vohra</dc:creator>
      <pubDate>Sun, 18 Oct 2026 12:49:16 +0000</pubDate>
      <link>https://dev.to/ankitvohra/one-brain-per-business-building-allevia-on-falkordb-3dco</link>
      <guid>https://dev.to/ankitvohra/one-brain-per-business-building-allevia-on-falkordb-3dco</guid>
      <description>&lt;p&gt;Being a founder isn't easy. Hundreds of things land on your plate every day, and getting all that context to your team is&lt;br&gt;
a job of its own.&lt;/p&gt;

&lt;p&gt;Allevia is our answer, and our entry for Graph Hacks: Context for AI Agents, FalkorDB's hackathon. We built it in four&lt;br&gt;
days, 15 to 18 October 2026, in more than 800 commits. Here is what we built, how it works, and what it saves.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/fxCLRLmyP2M" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: context lives in one head
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every "why" in a small business comes back to the founder, because the context lives nowhere else.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why a price changed. What was promised to which client. Which post brought the enquiries. The founder knows. The team&lt;br&gt;
mostly doesn't, and the tools never do.&lt;/p&gt;

&lt;p&gt;The inbox doesn't know about the analytics. The ad account doesn't know the checkout is broken, so it keeps spending.&lt;/p&gt;

&lt;p&gt;Most AI tools don't fix this. They start each chat from nothing, or from a pile of pasted documents.&lt;/p&gt;
&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A company brain per business, and a team of 13 AI agents that must read it before they act.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each business gets one FalkorDB graph. It is built from the website, the founder's connected tools, uploaded documents and&lt;br&gt;
conversations. Every fact keeps its source and its date.&lt;/p&gt;

&lt;p&gt;The team works in nine departments: The Chief, Data Detective, Growth Hacker, Miss Influencer, Techie, Firefighter, Bug&lt;br&gt;
Hunter, Memory Keeper, Wordsmith, Fact Checker, Number Cruncher, Deal Closer and Taskmaster. The Chief takes each request&lt;br&gt;
and decides who does what. Each agent's instructions say what to read in the brain first, before any outside tool.&lt;/p&gt;

&lt;p&gt;Nothing goes out without the founder's OK. A post, an email, a fix or an ad change arrives as a card, on the web or in&lt;br&gt;
Discord.&lt;/p&gt;

&lt;p&gt;The live demo has three businesses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aamdani&lt;/strong&gt;, our own startup, with Gmail, Analytics, Search Console and Instagram connected for real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AVS Atelier&lt;/strong&gt;, an architecture and interiors studio, built from its website and Instagram.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue Tokai Coffee&lt;/strong&gt;, a known brand. Its public data is real. Its private side (orders, payments, ad spend) is sample
numbers, labelled as samples on every screen.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why a graph, not just vectors
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The honest answer to "why" is a path, and paths live in a graph.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Listen to what founders ask. "Why did online orders drop this week?" "What breaks if the Instagram token expires?" "Why did&lt;br&gt;
we stop posting daily stories?"&lt;/p&gt;

&lt;p&gt;None of these is a lookup. Each answer is a chain: from a number, to the step that fell, to the release that changed it, to&lt;br&gt;
the person who owns the fix.&lt;/p&gt;

&lt;p&gt;Vector search finds text that sounds like the question. That is a good place to start. But it can't tell you that a payment&lt;br&gt;
error began the day of a release, or that the release changed the payment page. Those are links.&lt;/p&gt;

&lt;p&gt;So we use both. Search finds where to start. The graph explains.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture at a glance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One server for the team, one FalkorDB for the brains, and the founder's tools at the edge.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpaia4eizxxe9mpv64ozs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpaia4eizxxe9mpv64ozs.png" alt="Architecture: the founder, Allevia's server and agents, FalkorDB's graphs, and the tools through Composio" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The founder talks to Allevia on the web or in Discord. The server runs the 13 agents (on the Claude Agent SDK), their graph&lt;br&gt;
tools, the answer checks and the approval cards.&lt;/p&gt;

&lt;p&gt;Every read an agent makes goes to FalkorDB as &lt;code&gt;GRAPH.RO_QUERY&lt;/code&gt;, so the database itself refuses a write. Writes go through&lt;br&gt;
our own upsert code and a gate.&lt;/p&gt;

&lt;p&gt;The founder's tools connect through Composio. Facts flow in from them, and actions go out through them only after an OK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1olifkyu3mc1lw3oys0m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1olifkyu3mc1lw3oys0m.jpg" alt="The integrations page: tools for every team" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  One graph per business
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Isolation is the graph boundary, not a &lt;code&gt;WHERE&lt;/code&gt; clause.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbi9keki3nz5t6f2apqao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbi9keki3nz5t6f2apqao.png" alt="One FalkorDB server: a graph per business, the registry and the shared reference graph" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each business is its own graph, named &lt;code&gt;allevia_&amp;lt;slug&amp;gt;&lt;/code&gt;. No query in the product filters by tenant, and no agent tool&lt;br&gt;
accepts a graph name. A request is bound to one graph before any tool runs.&lt;/p&gt;

&lt;p&gt;Two more graphs sit beside them. &lt;code&gt;allevia_meta&lt;/code&gt; is the registry: one &lt;code&gt;Tenant&lt;/code&gt; node per business, plus accounts, sessions&lt;br&gt;
and invites. &lt;code&gt;allevia_reference&lt;/code&gt; holds general know-how, such as playbook rules, once for everyone, read-only.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9v70biw32jywigsbii4y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9v70biw32jywigsbii4y.jpg" alt="Three businesses, three graphs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we recorded the demo, Blue Tokai's graph held 2,187 things, Aamdani's 3,390 and AVS Atelier's 271. Each one can be&lt;br&gt;
exported, measured (&lt;code&gt;GRAPH.MEMORY USAGE&lt;/code&gt;) or erased on its own.&lt;/p&gt;
&lt;h2&gt;
  
  
  Schema in numbers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A small, strict schema: every node has a type, every edge has two clocks and a source.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node types&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ENTITY_TYPES&lt;/code&gt; in &lt;code&gt;src/graph/schema.ts&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relationship types&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;&lt;code&gt;REL_TYPES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allowed (from, type, to) patterns&lt;/td&gt;
&lt;td&gt;386&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ALLOWED_PATTERNS&lt;/code&gt;; anything else is refused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-holder relationships&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FUNCTIONAL_RELS&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bookkeeping properties on every edge&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EDGE_META_PROPS&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indexes per business graph&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;INDEXES&lt;/code&gt; in &lt;code&gt;src/graph/indexes.ts&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every node carries two labels: &lt;code&gt;Entity&lt;/code&gt; and its type, such as &lt;code&gt;Task&lt;/code&gt;. The id index sits on &lt;code&gt;Entity.id&lt;/code&gt;, with UNIQUE and&lt;br&gt;
MANDATORY constraints. That one index serves every lookup by id, whatever the type.&lt;/p&gt;

&lt;p&gt;A third label, &lt;code&gt;Searchable&lt;/code&gt;, goes on 43 of the 46 types. It leaves out number readings (&lt;code&gt;Observation&lt;/code&gt;), old versions and&lt;br&gt;
text chunks. The full-text and vector indexes sit on &lt;code&gt;Searchable&lt;/code&gt;, so search only holds what search may return. We learned&lt;br&gt;
this the hard way: readings each had a display name, a business adds about 160,000 a year, and they crowded real things out&lt;br&gt;
of the ranking.&lt;/p&gt;
&lt;h2&gt;
  
  
  How a turn runs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Plan, work in parallel, review, revise once, answer, then check every claim.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyki8aogm1nuf7ixggg63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyki8aogm1nuf7ixggg63.png" alt="A turn: recall, plan, desks in parallel, reviews, the answer, the checks, the cards" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recall.&lt;/strong&gt; The server reads what the brain already knows: earlier turns on the same things, notes, open cards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan.&lt;/strong&gt; The Chief picks up to three desks for an ordinary question, and up to five for a "why did this drop?" or a
review of the whole business.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work.&lt;/strong&gt; Desks run in parallel, each with only its own tools. Each writes a &lt;code&gt;Finding&lt;/code&gt; node with citations. That
node is the handoff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review.&lt;/strong&gt; Each Finding gets at most two reviewers. Number Cruncher checks figures, Fact Checker checks claims,
Wordsmith checks voice, Bug Hunter checks Techie. A "fix" earns exactly one revision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer.&lt;/strong&gt; The Chief writes the answer from the Findings, then calls &lt;code&gt;show_path&lt;/code&gt; with the nodes that explain it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check.&lt;/strong&gt; The server checks citations and numbers against what the tools returned in this turn.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then the cards wait, and the turn is saved to the brain: the &lt;code&gt;Turn&lt;/code&gt;, every &lt;code&gt;Finding&lt;/code&gt;, every node it touched and cited.&lt;/p&gt;
&lt;h2&gt;
  
  
  Every team, one brain
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Connecting the tools is not about more dashboards. It is so what one team learns reaches the others.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Allevia connects through Composio, whose catalogue has over 1,600 tools: mail, calendars, analytics, ads, shops, CRMs.&lt;br&gt;
Whatever comes in lands in the same graph, so the ads team, the content team and the website team read the same facts.&lt;/p&gt;

&lt;p&gt;On Blue Tokai we asked one question: "What did our ads and posts teach us this month, and what should content, ads and the&lt;br&gt;
website team each do next?" Seven teammates answered together in 102 seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9uashcmu7oml9trbilb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9uashcmu7oml9trbilb.jpg" alt="The Chief's answer: the costliest campaign, found (sample numbers)" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ads&lt;/strong&gt; found the costliest campaign, the new-roast launch, at about 1.7 times the middle campaign's cost per sign-up.
It was sending people to a checkout that was failing UPI payments. Growth Hacker proposed a pause, and moving the money
to brand search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content&lt;/strong&gt; drafted test posts, each ending with the business's main goal: "Visit your nearest café". Fact Checker
caught one caption that implied a free tasting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt; got the payment fix, and a Contact page fix ranked by how much it costs café visits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Finzoopxiu3z2ii0a8h9j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Finzoopxiu3z2ii0a8h9j.jpg" alt="A post made for café visits" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All three wait for the founder's OK. The numbers here are Blue Tokai's sample numbers, and the answer says so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfp1gd186rvs2hgnzxrq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfp1gd186rvs2hgnzxrq.jpg" alt="The pause card, waiting in Needs your OK" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How retrieval works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Search finds where to start, Cypher walks the links, the server checks the claims, and the path is drawn.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fms568fxfm7odd13abkjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fms568fxfm7odd13abkjp.png" alt="The retrieval pipeline: find where to start, walk the graph, check the answer, show the path" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Find where to start
&lt;/h3&gt;

&lt;p&gt;There are three entry points: vector search (by meaning), full-text search (by words), and an exact name. Embeddings, the&lt;br&gt;
numbers behind vector search, are 384 numbers per text, from a multilingual model that runs on our own server. No API&lt;br&gt;
call, no cost per search.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc1syqp4yc6859eo3jsdk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc1syqp4yc6859eo3jsdk.jpg" alt="The two search entry points, in the demo video" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are two of the four searches the &lt;code&gt;search&lt;/code&gt; tool sends at once, from &lt;code&gt;src/agent/tools/search.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.idx.fulltext.queryNodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Searchable'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;$q&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="nf"&gt;labels&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;$types&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;AND&lt;/span&gt; &lt;span class="ow"&gt;NOT&lt;/span&gt; &lt;span class="py"&gt;node:&lt;/span&gt;&lt;span class="n"&gt;Chunk&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node.id&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="n"&gt;$k&lt;/span&gt;

&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.idx.vector.queryNodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Chunk'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'textEmbedding'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;$k&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vecf32&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;$v&lt;/span&gt;&lt;span class="ss"&gt;))&lt;/span&gt; &lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;OPTIONAL&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;d:&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:HAS_CHUNK&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node.id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A matching chunk answers as its source document, so citations point at sources. The &lt;code&gt;node.id&lt;/code&gt; at the end of each&lt;br&gt;
&lt;code&gt;ORDER BY&lt;/code&gt; keeps the order stable when scores tie.&lt;/p&gt;

&lt;p&gt;The result lists are merged by reciprocal rank: each hit scores one over (60 plus its rank) in every list it appears in.&lt;br&gt;
It needs no tuning, and it doesn't care that vector and full-text scores live on different scales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RRF_K&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Hit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;via&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;snippetRank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RRF_K&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nf"&gt;fuseEntities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ftE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;fuseChunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ftC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chunk-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;fuseEntities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;fuseChunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chunk-vector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unknown name comes back as "I don't have that in your records", never as an empty walk that reads like "nothing depends&lt;br&gt;
on it".&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Walk the graph
&lt;/h3&gt;

&lt;p&gt;The agents have 21 graph tools, and every one is a Cypher query or a graph algorithm. "What breaks if this stops" is one&lt;br&gt;
query per hop over &lt;code&gt;DEPENDS_ON&lt;/code&gt;, &lt;code&gt;AFFECTS&lt;/code&gt; and &lt;code&gt;BLOCKS&lt;/code&gt;, so the app can show one step away next to four steps away.&lt;/p&gt;

&lt;p&gt;The links behind an answer come from FalkorDB's shortest-path procedure, from &lt;code&gt;src/graph/algorithms.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt;&lt;span class="n"&gt;$from&lt;/span&gt;&lt;span class="ss"&gt;}),&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;b:&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt;&lt;span class="n"&gt;$to&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;algo.SPpaths&lt;/span&gt;&lt;span class="ss"&gt;({&lt;/span&gt;&lt;span class="py"&gt;sourceNode:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;targetNode:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;relDirection:&lt;/span&gt; &lt;span class="s1"&gt;'both'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;maxLen:&lt;/span&gt; &lt;span class="n"&gt;$maxLen&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;pathCount:&lt;/span&gt; &lt;span class="n"&gt;$pathCount&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;relTypes:&lt;/span&gt; &lt;span class="n"&gt;$rels&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;costProp:&lt;/span&gt; &lt;span class="s1"&gt;'closedCost'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;maxCost:&lt;/span&gt; &lt;span class="n"&gt;$maxCost&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fes6sn81blwzshu54ivqw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fes6sn81blwzshu54ivqw.jpg" alt="The real algo.SPpaths call, and the path it lights" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PageRank, a score for how much of the business leads to a thing, ranks what matters. It runs on a cached copy of the graph&lt;br&gt;
that holds current edges only (explained in the memory section). FalkorDB caps a result at 10,000 rows, so the scores come&lt;br&gt;
back packed into lists of 5,000:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;algo.pageRank&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Searchable'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;node.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="ow"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="ss"&gt;([&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="ss"&gt;])&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="k"&gt;UNWIND&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;$page&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i..i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;$page&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agents can also write their own Cypher, through one tool with five guards, from &lt;code&gt;src/agent/tools/cypher.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;READ_PROCS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.labels&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.relationshiptypes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.propertykeys&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.indexes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.constraints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.meta.stats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.idx.fulltext.querynodes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.idx.fulltext.queryrelationships&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.idx.vector.querynodes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;writeReason&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// no write keyword, only READ_PROCS, no reduce()&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InputError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;refused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;boundRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;groupInWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CYPHER_ROW_CAP&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bounded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bounded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CYPHER_TIMEOUT_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// GRAPH.RO_QUERY, 5 s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The text check comes first. &lt;code&gt;GRAPH.RO_QUERY&lt;/code&gt; is the guard that can't be talked around.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Check the answer
&lt;/h3&gt;

&lt;p&gt;Before the founder sees an answer, the server checks it. Every &lt;code&gt;[[node-id]]&lt;/code&gt; the answer cites must have been returned by a&lt;br&gt;
tool in this turn. Every number must appear in the tools' data, allowing for rounding, percentages, "55k" and "₹1.2 lakh".&lt;br&gt;
An answer that used sample data must say so, and one built on data over a week old must say "as of" a date.&lt;/p&gt;

&lt;p&gt;A failed check never hides the answer. It shows as "unverified", and the result is stored on the turn.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Show the path
&lt;/h3&gt;

&lt;p&gt;The Chief names the nodes behind the answer, in order. For each pair, the server looks for a direct edge, then a shortest&lt;br&gt;
path of up to four hops, and only through the business's own records, not through Allevia's own cards. The app lights that&lt;br&gt;
path on the map.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftgkddc1cosskb1duez5x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftgkddc1cosskb1duez5x.jpg" alt="The path under a real answer: from the funnel to the checkout release" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Blue Tokai, "Why did online orders drop this week?" walks from the online store to the UPI payment step, the payment&lt;br&gt;
error, and the checkout release that brought it. Those numbers are samples, and the answer says so.&lt;/p&gt;
&lt;h2&gt;
  
  
  Memory with two clocks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Nothing is deleted. A fact that stops being true is closed with a date and kept.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A business changes its mind all the time. A task changes hands. A decision replaces an older one. If memory overwrites,&lt;br&gt;
the agents end up confidently wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcj24acax5wd6u8bohk6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcj24acax5wd6u8bohk6v.png" alt="One fact, two clocks: an owner closed and kept, the new owner current" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So every edge carries two clocks. Valid time (&lt;code&gt;since&lt;/code&gt;, &lt;code&gt;until&lt;/code&gt;) says when a fact was true. Record time (&lt;code&gt;recordedAt&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;retiredAt&lt;/code&gt;) says when we learned it, and when we learned it had stopped. Every edge also carries its source (&lt;code&gt;src&lt;/code&gt;) and its&lt;br&gt;
date (&lt;code&gt;asOf&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Every tool takes an &lt;code&gt;asOf&lt;/code&gt; date, so "who owned this task on 1 October?" has a real answer. In our synthetic test company,&lt;br&gt;
the task "Fix Instagram token refresh" is owned by Priya now; ask as of 1 October and the answer is the founder.&lt;/p&gt;

&lt;p&gt;Closing an edge is one &lt;code&gt;SET&lt;/code&gt;, from &lt;code&gt;src/graph/edges.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLOSE_SET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="s2"&gt;`SET &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.until = CASE WHEN &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.since &amp;gt; $end THEN &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.since ELSE $end END, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.retiredAt = coalesce(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.retiredAt, $now), `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.retiredAsOfMs = coalesce(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.retiredAsOfMs, $endedAsOf), &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.closedCost = $cost, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.closeReason = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some facts can only have one holder at a time. A task has one current owner. A decision is replaced by at most one&lt;br&gt;
successor. Seven such rules live in the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FUNCTIONAL_RELS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionalRule&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OWNS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;toType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Task&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;why&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a task has one current owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SUPERSEDES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;why&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a decision (or rule, doc…) is superseded by at most one successor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DERIVED_FROM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fromType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;why&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;an asset has one parent: the one it was made from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The edge write is a single Cypher statement of about 50 lines. It reads the current and closed edges, finds a replay, and&lt;br&gt;
closes the old holder of a one-holder slot in the same statement as it opens the new one. Facts can arrive in any order;&lt;br&gt;
an older source never overrules a newer one. Re-running an ingest adds nothing.&lt;/p&gt;

&lt;p&gt;When a task, a decision or a goal changes, its old state is saved as a &lt;code&gt;Version&lt;/code&gt; node one hop away. Readings are never&lt;br&gt;
overwritten either: each is its own &lt;code&gt;Observation&lt;/code&gt; node, with a time window, a date and a source.&lt;/p&gt;

&lt;p&gt;One FalkorDB detail shaped all of this. The &lt;code&gt;algo.*&lt;/code&gt; procedures take labels and relationship types, but they can't filter&lt;br&gt;
on edge properties, so they would walk a closed edge. Our fix has two halves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A closed edge carries &lt;code&gt;closedCost = 1,000,000&lt;/code&gt;, and the shortest-path call sets &lt;code&gt;maxCost&lt;/code&gt; just under it. Closed edges are
pruned inside the search.&lt;/li&gt;
&lt;li&gt;PageRank and topic grouping run on a slim copy of the graph with current edges only. Building it ourselves took about
110 ms at 20,000 nodes, against 5.3 seconds for &lt;code&gt;GRAPH.COPY&lt;/code&gt;, which also rebuilds every search index. The copy is cached
until a write lands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meetings are memory too, and they arrive by themselves. A call recorded by Fireflies or Granola (or a transcript sent to&lt;br&gt;
the business's own meeting address) becomes minutes with nobody pasting anything. Every line of the call is a &lt;code&gt;Chunk&lt;/code&gt; of the&lt;br&gt;
transcript, linked to who said it; each decision is &lt;code&gt;BASED_ON&lt;/code&gt; the line it was agreed in, and each action points back at the&lt;br&gt;
line it came from. The action goes to the right teammate (a quote to Deal Closer, a checkout fix to Techie), and the minutes&lt;br&gt;
email waits for the founder's OK. Open any task and you see the exact words that created it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkbup1buwmdu8b8gxt24w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkbup1buwmdu8b8gxt24w.jpg" alt="A recorded call becomes minutes: who does what, and the line of the call each action came from (sample call)" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Keeping the brain clean as it grows
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A brain that only grows gets slow, costly and noisy. So every write passes a gate, and the brain tidies itself.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A shared reference graph&lt;/strong&gt;, &lt;code&gt;allevia_reference&lt;/code&gt;, read-only and loaded once. General know-how, such as about 200
playbook rules, lives there once instead of in every business. A business keeps a small stub of a shared rule only
when one of its own facts points at it, so citations still resolve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A write gate.&lt;/strong&gt; Every new node needs a known type, a source and a date, or it is refused and logged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity resolution on write.&lt;/strong&gt; A new person, business or product that matches an existing one by name or email is
linked to it, not created again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weekly roll-ups and an archive.&lt;/strong&gt; Old daily readings roll up into weeks the way their readers total them (sums for
flows, means for rates), and old closed facts move to an archive. Both run as a dry run first and can be undone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A hygiene report and size budgets.&lt;/strong&gt; Closed facts, old readings, cold and over-linked nodes, per business.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gate's core, from &lt;code&gt;src/graph/gate.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gateNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GateOpts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;asOf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;NodeInput&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isEntityType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RefusedWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`unknown label &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;: not in the schema`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;asOf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asOf&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asOf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RefusedWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no source (src): every new node says where it came from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;asOf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RefusedWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no date (asOf): every new node says as of when&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asOf&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;asOf&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;asOf&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How fast it is
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Walks take milliseconds, even on a graph many times our biggest business.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We timed the tools on FalkorDB 6.0.2, on a laptop, on a graph built for timing: 37,240 nodes and 49,983 edges.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What breaks, 1 step / 4 steps&lt;/td&gt;
&lt;td&gt;5–16 ms / 7–36 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Up to 3 shortest paths, 6 hops&lt;/td&gt;
&lt;td&gt;135–218 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PageRank, first call / cached&lt;/td&gt;
&lt;td&gt;696 ms / 12 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic groups (label propagation)&lt;/td&gt;
&lt;td&gt;136 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full-text search&lt;/td&gt;
&lt;td&gt;under 1.2 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The walk is never the slow part. The model is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured: 8× fewer tokens
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On a real business, one agent on the graph read 8× fewer tokens than pasting the documents, and finished faster.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We tested that on 17 October, on Aamdani, with two real questions: "What breaks if&lt;br&gt;
the Instagram token expires?" and "Why did we pause daily stories and image posts?"&lt;/p&gt;

&lt;p&gt;Each question was answered two ways, with the same model and rules: with all 114 of the business's documents pasted into&lt;br&gt;
the prompt, and by one agent with only the graph tools.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;8×&lt;/strong&gt; fewer tokens than pasting the documents: 75,000 against 604,000&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Way of answering&lt;/th&gt;
&lt;th&gt;Tokens read, both questions&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Against pasting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Paste all 114 documents&lt;/td&gt;
&lt;td&gt;603,665&lt;/td&gt;
&lt;td&gt;44 s&lt;/td&gt;
&lt;td&gt;the starting point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One agent on the graph&lt;/td&gt;
&lt;td&gt;75,043&lt;/td&gt;
&lt;td&gt;25 s&lt;/td&gt;
&lt;td&gt;8× fewer tokens, 43% less time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tokens are the units a model reads and is billed by. The agent's tools handed it only 1,200 to 2,100 tokens of the&lt;br&gt;
business's own records per answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe6clxdxairve90bkxlrp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe6clxdxairve90bkxlrp.jpg" alt="604k tokens against 75k" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Demo video (2:50):&lt;/strong&gt; &lt;a href="https://youtu.be/fxCLRLmyP2M" rel="noopener noreferrer"&gt;https://youtu.be/fxCLRLmyP2M&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://13-232-139-42.sslip.io" rel="noopener noreferrer"&gt;https://13-232-139-42.sslip.io&lt;/a&gt;. Sign up for 100 free credits, or open "See the demo".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Ankit-Vohra/allevia-company-brain" rel="noopener noreferrer"&gt;https://github.com/Ankit-Vohra/allevia-company-brain&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every query and algorithm&lt;/strong&gt;, with its file and line: &lt;code&gt;docs/falkordb-queries.md&lt;/code&gt; in the repo.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>falkordb</category>
      <category>ai</category>
      <category>agents</category>
      <category>graphrag</category>
    </item>
  </channel>
</rss>
