<?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: Vishal Kumar</title>
    <description>The latest articles on DEV Community by Vishal Kumar (@vishal9032).</description>
    <link>https://dev.to/vishal9032</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%2F4192663%2F9b590a24-1ffd-468a-ac5c-9f63122f6731.png</url>
      <title>DEV Community: Vishal Kumar</title>
      <link>https://dev.to/vishal9032</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vishal9032"/>
    <language>en</language>
    <item>
      <title>How We Built an Autonomous Company Brain &amp; Multi-Agent Memory Engine with FalkorDB</title>
      <dc:creator>Vishal Kumar</dc:creator>
      <pubDate>Sun, 18 Oct 2026 18:19:56 +0000</pubDate>
      <link>https://dev.to/vishal9032/how-we-built-an-autonomous-company-brain-multi-agent-memory-engine-with-falkordb-2687</link>
      <guid>https://dev.to/vishal9032/how-we-built-an-autonomous-company-brain-multi-agent-memory-engine-with-falkordb-2687</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"Why did we deprecate the v1 Auth Service, who approved it, and what breaks if we shut it down on Monday?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every engineering team has asked a question like this, and nobody can answer it quickly. The reasoning is in a meeting transcript. The spec is an RFC in Google Docs. The commitment is a GitHub issue. The actual change is spread across three PRs. The only thing that connects them is somebody's memory, and that person is on holiday.&lt;/p&gt;

&lt;p&gt;For the WeMakeDevs Graph Hacks Hackathon we built &lt;strong&gt;CompanyMind AI&lt;/strong&gt;: a living knowledge graph of a company, plus a small team of AI agents that reason over it and share memory through the same graph. Everything runs on &lt;strong&gt;FalkorDB&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/vishal-kumar9032/companymind-ai" rel="noopener noreferrer"&gt;https://github.com/vishal-kumar9032/companymind-ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo: &lt;a href="https://youtu.be/FOdQao7GC4A" rel="noopener noreferrer"&gt;https://youtu.be/FOdQao7GC4A&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post covers why we used a graph, how the agents use it, and the parts that were harder than we expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why vector RAG falls over on this question
&lt;/h2&gt;

&lt;p&gt;The default way to build "chat with your company data" is to chunk documents, embed them, and retrieve the nearest chunks. That works for "what is our refund policy?". It breaks on our question, because the answer isn't in any single chunk. It's a path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;CTO&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;APPROVED&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;Decision&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;GOVERNS&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;auth&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;v1&lt;/span&gt;
&lt;span class="nv"&gt;RFC&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;PROPOSES&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;Decision&lt;/span&gt;          &lt;span class="nv"&gt;Meeting&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;RECORDED_IN&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;RFC&lt;/span&gt;
&lt;span class="nv"&gt;mobile&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;web&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;checkout&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;DEPENDS_ON&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;api&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;gateway&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;DEPENDS_ON&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;auth&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nearest-neighbour search returns text that sounds related. It can't follow a variable number of hops, and it can't tell you that a link doesn't exist. SQL can model the path, but every new shape of question needs another set of joins, and the depth of the dependency chain isn't fixed.&lt;/p&gt;

&lt;p&gt;In a graph database the path is the data. A question like "what breaks?" becomes one traversal, and every hop in the answer is an edge somebody recorded, so it can be cited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FalkorDB
&lt;/h2&gt;

&lt;p&gt;FalkorDB is a graph database that runs as a Redis module, stores graphs as sparse matrices (GraphBLAS) and is queried with OpenCypher. Four things mattered to us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Variable-length traversals are one line of Cypher&lt;/strong&gt;, with a bounded depth so a bad edge can't make a query run away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph algorithms are built in.&lt;/strong&gt; PageRank, betweenness, label propagation and weakly connected components are &lt;code&gt;CALL algo.*&lt;/code&gt; procedures. We expose them to the agent directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-text search lives in the same database.&lt;/strong&gt; We didn't need a second system for uploaded documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphs are cheap and separate.&lt;/strong&gt; Each signed-in user gets their own graph, &lt;code&gt;org_&amp;lt;uid&amp;gt;&lt;/code&gt;, so tenant isolation is a namespace rather than a &lt;code&gt;WHERE&lt;/code&gt; clause we might forget.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The model
&lt;/h2&gt;

&lt;p&gt;People, teams, meetings, docs, decisions, services, GitHub issues and PRs, connected by relationships such as &lt;code&gt;APPROVED&lt;/code&gt;, &lt;code&gt;PROPOSES&lt;/code&gt;, &lt;code&gt;SUPERSEDES&lt;/code&gt;, &lt;code&gt;GOVERNS&lt;/code&gt;, &lt;code&gt;OWNS&lt;/code&gt;, &lt;code&gt;DEPENDS_ON&lt;/code&gt; and &lt;code&gt;TOUCHES&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Agent state goes in the same graph, as &lt;code&gt;Memory&lt;/code&gt;, &lt;code&gt;AgentRun&lt;/code&gt;, &lt;code&gt;Task&lt;/code&gt; and &lt;code&gt;Approval&lt;/code&gt; nodes. That decision turned out to matter, and I'll come back to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-hop reasoning as a tool
&lt;/h2&gt;

&lt;p&gt;Our "blast radius" tool answers &lt;em&gt;what is affected if this service goes away&lt;/em&gt;. The core of it is this query:&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="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;upstream:&lt;/span&gt;&lt;span class="n"&gt;Service&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;:DEPENDS_ON&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;4&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="py"&gt;target:&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;$serviceName&lt;/span&gt;&lt;span class="ss"&gt;})&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="n"&gt;upstream&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:OWNS&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="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;upstream.name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;affected_service&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
       &lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&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;hops_count&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;t.name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;owning_team&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.lead&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;team_lead&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="nf"&gt;nodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n.name&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;via&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;hops_count&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;affected_service&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;auth-v1&lt;/code&gt; it returns the API gateway at one hop, then the mobile app and web checkout at two hops, each with the owning team and its lead and the exact chain that links them. The &lt;code&gt;via&lt;/code&gt; list is what the UI uses to light up the path on screen.&lt;/p&gt;

&lt;p&gt;Notice &lt;code&gt;$serviceName&lt;/code&gt;. The query is parameterised, never built from model output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph algorithms as agent tools
&lt;/h2&gt;

&lt;p&gt;Some questions don't name an entity: "what's our biggest risk?" or "which services are single points of failure?" For those we gave the agent one &lt;code&gt;graph_algorithm&lt;/code&gt; tool that wraps FalkorDB's procedures:&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;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pagerank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`CALL algo.pageRank($label, $rel) YIELD node, score
     RETURN &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; AS name, score ORDER BY score DESC LIMIT 10`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;label&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="nx"&gt;relationship&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;betweenness&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`CALL algo.betweenness($cfg) YIELD node, score
     RETURN &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; AS name, score ORDER BY score DESC LIMIT 10`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PageRank&lt;/strong&gt; finds the services the most other services depend on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Betweenness&lt;/strong&gt; finds the bottlenecks that sit on the most paths. These are the single points of failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Label propagation&lt;/strong&gt; finds natural clusters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WCC&lt;/strong&gt; finds disconnected islands, which are often forgotten systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent doesn't need to know how any of this is computed. It calls &lt;code&gt;graph_schema&lt;/code&gt; first to learn the real labels and relationship names, picks an algorithm, and explains the result in plain language.&lt;/p&gt;

&lt;p&gt;Alongside these there is &lt;code&gt;shortest_path&lt;/code&gt;, which explains how two entities are connected hop by hop, and a read-only &lt;code&gt;graph_query&lt;/code&gt; (&lt;code&gt;GRAPH.RO_QUERY&lt;/code&gt;) for anything the specific tools don't cover. Because the free-form route is read-only, the model can explore freely without being able to write to the graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small team of agents with shared memory
&lt;/h2&gt;

&lt;p&gt;An orchestrator hands work to three specialists:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Researcher&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Graph lookups and web search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Governance Watchdog&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Checks PRs against past decisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scribe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Drafts content and performs actions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The interesting part is how they share context. We didn't pass a long transcript between agents. A hand-off is a &lt;code&gt;Task&lt;/code&gt; node. What an agent learns is a &lt;code&gt;Memory&lt;/code&gt; node, typed as episodic (what happened), semantic (what is true) or procedural (how to do something). Later runs &lt;code&gt;recall&lt;/code&gt; what is relevant instead of re-reading everything.&lt;/p&gt;

&lt;p&gt;Because memory is in the same graph as the company data, a memory can link to the entity it's about. "Payments team prefers a two-week deprecation window" is an edge to the payments team, not a floating string. This also keeps the context window small, since agents fetch a few relevant nodes instead of carrying the whole history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring your own documents
&lt;/h2&gt;

&lt;p&gt;The Documents &amp;amp; Files view accepts PDF, Word, Excel, CSV, Markdown and text. Uploads are parsed, split into chunks and stored as &lt;code&gt;Chunk&lt;/code&gt; nodes linked to their &lt;code&gt;Doc&lt;/code&gt;, with a full-text index on the chunk text:&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;'Chunk'&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;MATCH&lt;/span&gt;&lt;span class="w"&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="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:PART_OF&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="py"&gt;d:&lt;/span&gt;&lt;span class="n"&gt;Doc&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;d.name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node.idx&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node.text&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;text&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;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="k"&gt;LIMIT&lt;/span&gt; &lt;span class="n"&gt;$limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answers cite their source as &lt;code&gt;[document, part N]&lt;/code&gt;. An "Add to graph" button extracts the decisions, services and people in a document and links them into the graph, so a pasted spec becomes something you can traverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Letting agents act, with a human in the loop
&lt;/h2&gt;

&lt;p&gt;Through Composio the agents can reach Gmail, Calendar, Drive, Docs and GitHub, using the user's own connected accounts. We were careful here. A tool runs without approval only if its name carries a read verb and no write verb. Anything unrecognised is treated as a write. Writes are queued as &lt;code&gt;Approval&lt;/code&gt; nodes and only run after the user approves. Failing closed matters more than being slightly less convenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Showing the reasoning
&lt;/h2&gt;

&lt;p&gt;The graph is drawn with Cytoscape.js. When the agent runs a traversal, the nodes and edges it touched are highlighted, and memory recalls, tool calls and hand-offs stream into the chat as they happen. Seeing the highlighted path is what makes people trust the answer, because they can check it against the graph themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Give the model tools, not a Cypher prompt.&lt;/strong&gt; Parameterised tools for the common questions, plus a read-only escape hatch, gave far more reliable answers than asking the model to write Cypher from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it check the schema first.&lt;/strong&gt; Calling &lt;code&gt;graph_schema&lt;/code&gt; before anything else removed most invented labels and relationship names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be honest when there's nothing to say.&lt;/strong&gt; An empty graph or an unconnected account returns "no data" or "not connected". It never returns invented content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never trust the client for identity.&lt;/strong&gt; The tenant graph is derived from a verified Firebase ID token on the server, not from anything the browser sends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put memory where the knowledge is.&lt;/strong&gt; Keeping agent memory in the company graph made it queryable, linkable and auditable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;FalkorDB, Next.js 15 (App Router, TypeScript), React 19, Tailwind, Cytoscape.js, an OpenAI-compatible LLM gateway with a model fallback chain, Composio for Google Workspace and GitHub, and Firebase Authentication.&lt;/p&gt;

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

&lt;p&gt;Clone the repo, start FalkorDB with Docker, and run the seeded sample workspace to try the &lt;code&gt;auth-v1&lt;/code&gt; question yourself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/vishal-kumar9032/companymind-ai" rel="noopener noreferrer"&gt;https://github.com/vishal-kumar9032/companymind-ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo video: &lt;a href="https://youtu.be/FOdQao7GC4A" rel="noopener noreferrer"&gt;https://youtu.be/FOdQao7GC4A&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building agents on top of a graph, I'd like to hear how you handle memory and approvals. Leave a comment below.&lt;/p&gt;

</description>
      <category>falkordb</category>
      <category>ai</category>
      <category>hackathon</category>
      <category>graphdb</category>
    </item>
  </channel>
</rss>
