<?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: LAIS</title>
    <description>The latest articles on DEV Community by LAIS (@lais_localaisystem).</description>
    <link>https://dev.to/lais_localaisystem</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3957978%2F2753833d-bee6-4b07-90ae-96b86af6d2d4.png</url>
      <title>DEV Community: LAIS</title>
      <link>https://dev.to/lais_localaisystem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lais_localaisystem"/>
    <language>en</language>
    <item>
      <title>Building a Multi-Agent System from 23 Open-Source Projects — What Worked, What Broke</title>
      <dc:creator>LAIS</dc:creator>
      <pubDate>Fri, 29 May 2026 07:56:30 +0000</pubDate>
      <link>https://dev.to/lais_localaisystem/building-a-multi-agent-system-from-23-open-source-projects-what-worked-what-broke-4lba</link>
      <guid>https://dev.to/lais_localaisystem/building-a-multi-agent-system-from-23-open-source-projects-what-worked-what-broke-4lba</guid>
      <description>&lt;h2&gt;
  
  
  The Single-Agent Ceiling
&lt;/h2&gt;

&lt;p&gt;One LLM, one context window, one shot. That is the constraint every AI agent operates under today.&lt;/p&gt;

&lt;p&gt;You can give an agent more tokens — 200K, 1M, even 2M context windows exist now — but that only pushes the ceiling higher, it does not remove it. A single agent still has to hold everything in one head: research, planning, coding, debugging, deployment. When context runs out, it forgets what it knew three turns ago.&lt;/p&gt;

&lt;p&gt;The obvious solution is &lt;strong&gt;multiple agents that hand off work to each other&lt;/strong&gt;. But multi-agent systems face their own problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do agents in different processes find each other?&lt;/li&gt;
&lt;li&gt;How do they share state without a shared database?&lt;/li&gt;
&lt;li&gt;How do you know which agent to trust with a critical task?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This series documents how we built &lt;strong&gt;LAIS (Local AI System)&lt;/strong&gt; — a multi-agent architecture that solves these problems using 23 integrated open-source projects. Every integration was tested live against real running components, and the complete test suite is published.&lt;/p&gt;

&lt;p&gt;In this part: the cross-process agent handoff that breaks through the single-agent ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────┐     ACP Bus (JSON)     ┌──────────────┐
│   OpenCode   │ ◄─────────────────────► │   Claude     │
│  (orchestrator)│   task → result        │  (executor)  │
└──────┬───────┘                         └──────┬───────┘
       │                                       │
       │          CoComm (Shared Memory)        │
       ├───────────────────────────────────────┤
       │  memory · sessions · roles · trust    │
       └───────────────────────────────────────┘
              │             │
       ┌──────┴──┐   ┌─────┴─────┐
       │  A2A    │   │  Qdrant   │
       │  HTTP   │   │  Vector   │
       │  API    │   │  Storage  │
       └─────────┘   └───────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four integrated systems, all tested live:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Test Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACP Bus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cross-process agent tasking&lt;/td&gt;
&lt;td&gt;✅ Confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CoComm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared memory, roles, trust&lt;/td&gt;
&lt;td&gt;✅ Confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qdrant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local vector search&lt;/td&gt;
&lt;td&gt;✅ Confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A2A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP agent discovery &amp;amp; tasks&lt;/td&gt;
&lt;td&gt;✅ Confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The ACP Bus: Cross-Process Agent Handoff
&lt;/h2&gt;

&lt;p&gt;The core idea is simple: a JSON file on disk acts as the message bus. Agent A writes a task, Agent B reads it, processes it, writes the result back, and Agent A picks it up.&lt;/p&gt;

&lt;p&gt;The message structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ACPMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;           &lt;span class="c1"&gt;# UUID
&lt;/span&gt;    &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;       &lt;span class="c1"&gt;# who sent it
&lt;/span&gt;    &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;    &lt;span class="c1"&gt;# who should receive it
&lt;/span&gt;    &lt;span class="n"&gt;msg_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;     &lt;span class="c1"&gt;# "task", "result", "ack"
&lt;/span&gt;    &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;      &lt;span class="c1"&gt;# the actual data
&lt;/span&gt;    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;       &lt;span class="c1"&gt;# "pending", "delivered", "failed"
&lt;/span&gt;    &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;          &lt;span class="c1"&gt;# time-to-live in seconds
&lt;/span&gt;    &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;  &lt;span class="c1"&gt;# unix timestamp
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handoff flow has five steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Send&lt;/strong&gt; — Agent A writes a task message to the bus&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sync&lt;/strong&gt; — Agent B reloads the bus file from disk (cross-process sync)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Receive&lt;/strong&gt; — Agent B picks up pending messages addressed to it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process&lt;/strong&gt; — Agent B executes the task (in our case, via OpenRouter)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt; — Agent B writes the result back to the bus&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was tested live with two real processes: OpenCode (the orchestrator) dispatched a task to Claude (the executor), which processed it via the OpenRouter API and returned the result. Full round-trip confirmed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_comm_bus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lais_bus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# OpenCode sends task
&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ACPMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opencode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Run `python -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;print(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HANDOFF_OK_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Claude picks up, processes, returns result
&lt;/span&gt;&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ... process via OpenRouter ...
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ACPMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opencode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# OpenCode receives
&lt;/span&gt;&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opencode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No message broker. No shared database. No server process. Just a JSON file and a polling loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Most multi-agent demos require cloud infrastructure: a hosted message queue, a vector database service, serverless functions. The ACP bus proves you can have reliable cross-process agent communication on a single machine with &lt;strong&gt;zero infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This matters for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt; — everything stays local&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt; — no cloud services to pay for&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt; — you can understand the entire system by reading one JSON file&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next in This Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: Shared memory for agents — CoComm's store/retrieve/search, role registry, and trust scoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: Vector search without Docker — Qdrant in-memory mode with semantic ranking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: A2A HTTP protocol — standards-compatible agent discovery and task delegation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: What live testing taught us — the 3 API mismatches that documentation missed&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;All test code is open source under Apache 2.0: &lt;a href="https://github.com/StefSNS/LAIS-integration-proofs" rel="noopener noreferrer"&gt;https://github.com/StefSNS/LAIS-integration-proofs&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>python</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
