English | ็ฎไฝไธญๆ | Espaรฑol | ๆฅๆฌ่ช | ํ๊ตญ์ด | ุงูุนุฑุจูุฉ
Give four coding agents a shared radio channel. They divide the work, negotiate the plan, and keep broadcasting discoveries while they work โ because listening runs as a background task instead of stealing a turn.
This repository contains the code and data to reproduce the experiments of the paper *AgentRadio: Passive Awareness for Long-Horizon Multi-Agent Collaboration* ([arXiv:2607.28430](https://arxiv.org/abs/2607.28430)). > ### โ๏ธ Prefer the product? โ **[Coral Code](https://coralcode.dev/)** > > **[Coral Code](https://coralcode.dev/)** is the product version of AgentRadio. > **New users get $30 in free credit.** ### ๐ One protocol, four agents โ +29.8 points over a single agent | Configuration | What it adds | Task acc. (Opus 4.6) | Task acc. (DeepSeek V4 Pro) | |---|---|:---:|:---:| | **B0** single agent | โ | 32.3 % | 29.0 % | | **B1** best of six single runs | 6ร budget, no coordination | 37.9 % | 31.4 % | | **L1** four agents + division | division of labor | 39.5 % | 31.4 % | | **L2** + negotiation | joint planning + cross-review (blocking receive) | 51.6 % | 39.5 % | | **L3** + passive awareness (**AgentRadio**) | background `wait_for_mention` | **62.1 %** | **50.8 %** | The step from L2 to L3 changes **only** the communication mode. It wins 15 tasks and loses 2 with Opus 4.6 (exact McNemar test, p = 0.0023) and wins 17 while losing 3 with DeepSeek (p = 0.0026). Four Opus 4.6 agents under AgentRadio (62.1 %) surpass the strongest single-agent leaderboard entry, Claude Code with the newer Opus 4.8 (57.2 %). โ [See the full results](#-results) ยท [paper](https://arxiv.org/abs/2607.28430) ยท [run it yourself](#-running-the-four-configurations) ## ๐ฃ News - **2026-08** โ AgentRadio was covered by [VentureBeat](https://venturebeat.com/): ["Four AI agents coordinating in real time outperformed Claude Opus 4.8 on enterprise coding tasks"](https://venturebeat.com/orchestration/four-ai-agents-coordinating-in-real-time-outperformed-claude-opus-4-8-on-enterprise-coding-tasks). ๐ฐ - **2026-07** โ The AgentRadio paper is released on [arXiv](https://arxiv.org/abs/2607.28430). ๐ - **2026-07** โ Code, adapters, and the full 124-task SWE-Atlas QnA setup are open-sourced. ๐ ## ๐ก Why AgentRadio * **Communication stops costing work** โ `wait_for_mention` runs as a *background task* of the harness, so a teammate's message surfaces at the next step boundary instead of consuming a turn. Agents no longer choose between working and listening. * **Mid-execution correction** โ in blocking systems a discovery cannot reach a teammate until the next phase boundary. Under passive awareness it lands immediately, and the teammate folds it into the task already in flight. * **No harness modification** โ the harness only has to run a shell command in the background, which mainstream coding harnesses already do. AgentRadio ships as a standalone message server plus three thin shell scripts. * **No extra LLM calls** โ the watcher is an ordinary OS process, not an agent step. The only new tokens an agent pays for are the messages that actually surface. * **Model-agnostic** โ the same protocol, prompts, and startup scripts run on Claude Opus 4.6 and on DeepSeek-V4-Pro through a LiteLLM translation proxy. * **A clean ablation ladder** โ B0 โ L1 โ L2 โ L3 isolates division of labor, negotiation, and passive awareness one layer at a time, on identical harness settings. ## ๐งฉ How It Works ### The three primitives AgentRadio exposes three operations to every agent: | Primitive | Behavior | |---|---| | `create_thread(name, participants)` | Opens a named conversation on the message server and returns its identifier. | | `send_message(thread, content, mentions)` | Appends a message to a thread and returns immediately, whether or not anyone is listening. May @-mention specific agents. | | `wait_for_mention(timeout)` | Blocks until a message mentioning the caller arrives, then returns it together with a full snapshot of every thread โ so the caller never needs a second read to reconstruct context. | The layer takes no position on *when* an agent listens. Where `wait_for_mention` runs is the single degree of freedom separating the two communication modes: - **Foreground** โ *blocking receive*. The agent stops working in order to listen. Every message heard costs a step of work. This is the L2 baseline. - **Background task** โ *passive awareness*. The agent keeps working and any mention surfaces at the next step boundary, with no step spent listening. This is L3, full AgentRadio. Everything else โ the primitives, the threads, the protocol โ stays fixed. That single-bit difference is what the experiments isolate. ### The five-phase protocol Four agents run a fixed protocol of division of labor and negotiation. Agent-1 additionally serves as the **assembler**: it opens the planning, worklog, and final-answer threads, and gates every transition โ a phase ends only after it collects an explicit approval from every agent. 1. **P1 ยท Explore** โ every agent starts its background watcher, independently explores the repository, and drafts the sub-questions it sees. Nothing is sent. 2. **P2 ยท Divide** โ the assembler opens a planning thread. Agents pool their findings, negotiate a partition of the sub-questions, and revise it until every agent approves. 3. **P3 ยท Execute** โ each agent works its own sub-questions. A discovery triggers a worklog post the moment it is made: a finding that bears on a teammate, a contradiction with the agreed plan, an obstacle, or an abandoned dead end. 4. **P4 ยท Review** โ each agent broadcasts its findings with evidence in its own results thread. Reviewers post factual conflicts, thin evidence, and unmentioned observations, and can send a sub-question back to P3. 5. **P5 ยท Submit** โ the assembler composes the final answer from the approved results, broadcasts the draft for a last round of approvals, and submits. Under blocking receive the same five phases run unchanged, but the live sharing of P3 disappears: hearing a message costs a foreground wait, so agents fall silent while they work and a discovery cannot reach a teammate before P4. ## ๐๏ธ Repository Layout ``` data/qa/ 124 SWE-Atlas QnA tasks (harbor dataset scale-ai/swe-atlas-qna) multi_agent/ coral_multi_agent.py L2 adapter: division + negotiation (blocking receive) coral_multi_agent_ablation.py L1 adapter: division only coral_multi_agent_passive.py L3 adapter: full AgentRadio (passive awareness) startup.sh / startup_ablation.sh / startup_passive.sh per-agent bootstrap + protocol prompts (CLAUDE.md) coral-agent*.toml message-server agent definitions passive_scripts/ MCP-over-HTTP shell primitives (create_thread / send_message / wait_for_mention / read_resource) coral-server.jar message server (download from Releases, see below) monitor_coral_log.sh live thread/message monitor for running containers run_config/qa/ claude-token OAuth token helper full_run.sh B0 baseline batch runner (all 124 tasks) run_passive_multi_agent.sh L3 batch runner verify_local.py rubric verifier (LLM judge), run locally on a trial dir ``` Every task directory under `data/qa/` carries the instruction, the pinned execution environment, and the rubric set used by the verifier. --- ## ๐ฆ Setup Runs execute in Docker containers on [Modal](https://modal.com), orchestrated by [Harbor](https://github.com/laude-institute/harbor). One task = one container running the message server plus four Claude Code agents. ### 1. Docker Desktop Install from https://www.docker.com/products/docker-desktop/ and verify with `docker run hello-world`. ### 2. uv ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### 3. Harbor (pinned to 0.6.4) Newer Harbor releases (0.7+) have breaking API changes that make these adapters fail. Pin the versions: | Component | Working version | |-----------|-----------------| | harbor | **0.6.4** | | modal | **1.4.2** | ```bash uv tool uninstall harbor 2>/dev/null || true uv tool install 'harbor[modal]==0.6.4' harbor --version # must show 0.6.4 ``` ### 4. Modal ```bash pip install 'modal==1.4.2' modal --version # must show 1.4.2 modal setup # opens browser to log in ``` ### 5. Claude Code ```bash curl -fsSL https://claude.ai/install.sh | sh claude --version ``` You need a **Claude Max subscription** for the agents. The verifier additionally needs an **Anthropic API key**. ### 6. Message server JAR The 106 MB server JAR is hosted as an anonymized artifact (too large for a git blob). The `confirm=t` parameter bypasses the large-file scan interstitial so `curl` gets the binary directly: ```bash curl -L -o multi_agent/coral-server.jar \ "https://drive.usercontent.google.com/download?id=17b40_1kXFrAC0pnN8w_7PPY13O7pYVke&export=download&confirm=t" ``` The adapters upload this JAR into each task container. Nothing needs to run locally, so no local JDK is required. ### 7. Token helper and .env ```bash cp run_config/qa/claude-token ~/.local/bin/claude-token chmod +x ~/.local/bin/claude-token cp .env.example .env # then fill in your Anthropic API key ``` ### Before each run: refresh the OAuth token The Claude Code OAuth token rotates. Each container gets a static snapshot at launch, and a stale token kills all four agents with 401 mid-run. Refresh before every session: ```bash claude /login # opens browser security find-generic-password -s "Claude Code-credentials" -w | python3 -c " import json, sys, os data = json.loads(sys.stdin.read()) oauth = data.get('claudeAiOauth', {}) with open(os.path.expanduser('~/.claude/.credentials.json'), 'w') as f: json.dump({'claudeAiOauth': oauth}, f, indent=2) print(f'Token refreshed. Expires at: {oauth.get(\"expiresAt\")}') " ~/.local/bin/claude-token --check source .env ``` --- ## โก Running the Four Configurations All commands run from the repository root, after `source .env`. Task IDs are the directory names under `data/qa/` (repeat `-i` to batch; drop `-i` entirely to run all 124). `-n` is the number of concurrent tasks (one task = four agents for L1โL3). ### B0 โ single agent (baseline) ```bash source .env harbor run \ -p ./data/qa \ -a claude-code \ -m "anthropic/claude-opus-4-6" \ -e modal -k 1 -n 1 \ -i "task-6905333b74f22949d97ba998" \ --ak reasoning_effort=high \ -o results/qa/ \ --job-name "baseline-ba998" \ -y ``` ### L1 โ four agents + division of labor Agent-1 explores briefly, partitions the question, and each agent solves its share independently. Answers are merged without review. ```bash source .env export PYTHONPATH="$(pwd):${PYTHONPATH:-}" harbor run \ -p ./data/qa \ --agent-import-path='multi_agent.coral_multi_agent_ablation:CoralMultiAgentAblation' \ -m "anthropic/claude-opus-4-6" \ -e modal -k 1 -n 1 \ -i "task-6905333b74f22949d97ba998" \ --ak reasoning_effort=high \ -o results/qa/ \ --job-name "division-ba998" \ -y ``` ### L2 โ + negotiation (blocking receive) The full five-phase protocol โ joint exploration, negotiated partition to unanimity, live execution, cross-review, assembled submission โ with `wait_for_mention` running in the **foreground**, so agents stop working in order to listen. ```bash source .env export PYTHONPATH="$(pwd):${PYTHONPATH:-}" harbor run \ -p ./data/qa \ --agent-import-path='multi_agent.coral_multi_agent:CoralMultiAgent' \ -m "anthropic/claude-opus-4-6" \ -e modal -k 1 -n 1 \ -i "task-6905333b74f22949d97ba998" \ --ak reasoning_effort=high \ -o results/qa/ \ --job-name "divneg-ba998" \ -y ``` ### L3 โ + passive awareness (full AgentRadio) Same protocol, but `wait_for_mention` runs as a **background task**: agents keep working and messages surface between steps. Claude Code gets no MCP config โ all communication goes through the thin shell wrappers in `passive_scripts/`. ```bash source .env export PYTHONPATH="$(pwd):${PYTHONPATH:-}" harbor run \ -p ./data/qa \ --agent-import-path='multi_agent.coral_multi_agent_passive:CoralMultiAgentPassive' \ -m "anthropic/claude-opus-4-6" \ -e modal -k 1 -n 1 \ -i "task-6905333b74f22949d97ba998" \ --ak reasoning_effort=high \ -o results/qa/ \ --job-name "passive-ba998" \ -y ``` `run_config/qa/run_passive_multi_agent.sh` wraps the same command as a batch runner, one harbor job per task id. --- ## ๐ Running with DeepSeek-V4-Pro The multi-agent configurations (L1โL3) can be run with **DeepSeek-V4-Pro** agents instead of Opus 4.6, reproducing the DeepSeek column of the results table. Everything about the protocol, prompts, startup scripts, and resume guard is identical; only the LLM backend changes. Claude Code speaks only the Anthropic Messages API, while DeepSeek is served through OpenRouter (OpenAI-compatible only). We bridge the two with a **LiteLLM translation proxy hosted once on Modal**. The task containers install nothing โ they just point `ANTHROPIC_BASE_URL` at the proxy's public URL. The rubric verifier is unchanged: it still uses your Anthropic judge (`OPENAI_API_KEY` / `EVAL_MODEL`). DeepSeek is only the *agent* backend. ### One-time proxy setup ```bash # 1. An OpenRouter API key with deepseek-v4-pro access (https://openrouter.ai/keys) # is stored as a Modal secret โ it never leaves your Modal account. modal secret create openrouter-deepseek OPENROUTER_API_KEY=sk-or-... # 2. Deploy the proxy. This prints your personal URL. modal deploy multi_agent/deepseek_litellm_modal.py # -> https://