The missing layer between your AI agents and you

Published: (March 7, 2026 at 01:14 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

The three patterns developers settle for

Pattern 1: Terminal babysitting

python my_agent.py >> agent.log 2>&1 &
tail -f agent.log

You run the agent, watch the logs, and restart it when it crashes.

  • Works fine for one‑shot batch jobs (scraping, processing, tasks that run to completion and write an output file).
  • It falls apart the moment your agent needs a human decision mid‑flight, or when you run several agents concurrently and need to understand the global state.

Pattern 2: Polling (REST API)

# Check status
curl http://localhost:8000/status
# {"status": "running", "task": "scraping page 42/100", "errors": 0}

# Ask it something
curl -X POST http://localhost:8000/query \
  -d '{"question": "what have you found so far?"}'

Better – there’s a programmatic dialogue.
But the power dynamic is one‑way: the agent can only respond to requests. If it hits a rate limit at 2 AM or encounters an unexpected anomaly, it can’t tell you; you’d need a separate script just to check on it.

  • Fine for synchronous tools, but interesting agents are long‑running and need the ability to reach out.

Pattern 3: Bidirectional messaging

The agent pushes messages over WebSockets or Server‑Sent Events (SSE) when it has state changes or needs help. You reply asynchronously. The channel tracks the history, so you don’t lose context if the container restarts.

# Agent sends you a message when it hits an edge case
import requests

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

# The agent encountered a decision point and escalates
requests.post(
    f"{API}/channels/{CHANNEL_ID}/messages",
    headers=headers,
    json={
        "content": (
            "Found 3 candidates matching the criteria. "
            "Should I contact all three, or just the top one? @human"
        )
    },
)

You get a push notification, reply, and the agent resumes the workflow.

This is how humans work together. It’s odd that we don’t build agent orchestration this way by default.


Architectural requirements for agent communications

To build a bidirectional pub/sub layer for your agents you run into a few non‑trivial infrastructure requirements:

  1. Persistent conversation history – If an agent’s only memory is its token window, history evaporates when the process restarts. You need a fast, durable datastore (Postgres, Redis, etc.) tracking channel history.
  2. Unified authentication – Agents use API keys; humans use session cookies or OAuth. Most web frameworks handle one well and bolt the other on. A proper communication layer treats both as first‑class citizens in the same chat rooms.
  3. Real‑time transport – Polling a REST endpoint every few seconds is brittle and wasteful. WebSockets or SSE make the interaction feel like an actual terminal session.
  4. Low friction – If setting up the comms layer is harder than writing the agent itself, developers will fall back to print() statements.

A minimal working example

Below is a full loop – the agent bootstraps its own workspace, gets credentials, and starts messaging.

Step 1: Bootstrap

curl -X POST http://localhost:8080/api/v1/bootstrap \
  -H "Content-Type: application/json" \
  -d '{
        "owner_email": "admin@local",
        "owner_password": "changeme",
        "agent_name": "research-bot",
        "agent_description": "Handles research tasks"
      }'

Response

{
  "api_key":    "au_abc123...",
  "channel_id": "ch_xyz789...",
  "invite_url": "http://localhost:3001/invite/TOKEN"
}

Open the invite_url in a browser. Now you and the agent share the same channel.

Step 2: Agent sends messages

import json, requests, websocket

API      = "http://localhost:8080/api/v1"
KEY      = "au_abc123..."
CHANNEL  = "ch_xyz789..."
headers  = {
    "Authorization": f"Bearer {KEY}",
    "Content-Type": "application/json"
}

# REST: fire‑and‑forget
requests.post(
    f"{API}/channels/{CHANNEL}/messages",
    headers=headers,
    json={"content": "Started research run. Will update with findings."}
)

# WebSocket: real‑time back‑and‑forth
ws = websocket.create_connection(f"ws://localhost:8080/ws?token={KEY}")

# Listen for human replies
while True:
    msg = json.loads(ws.recv())
    if msg["type"] == "new_message":
        sender  = msg["message"]["author_name"]
        content = msg["message"]["content"]
        if sender != "research-bot":          # message is from a human
            handle_human_reply(content)

No SDK, no framework lock‑in. If your code can make HTTP calls or open a WebSocket, this works – Python, Node, Go, Bash, whatever.


Multi‑agent patterns

Once you have a messaging layer, multi‑agent coordination follows naturally.

  • An orchestrator creates tasks, routes them to specialists, and aggregates results. Each agent gets its own API key; the orchestrator reads from all channels and handles escalation.
  • Agents post to a shared channel; others read and react. This loose coupling avoids a single central dispatcher that becomes a bottleneck.
# Agent 1 posts result to shared channel
ws.send(json.dumps({
    "type":       "send_message",
    "channel_id": SHARED_CHANNEL,
    "content":    "Research complete: found 3 key findings. See attached."
}))

# Agent 2 is subscribed to the same channel
msg = json.loads(ws.recv())
if msg["type"] == "new_message":
    if "Research complete" in msg["message"]["content"]:
        trigger_analysis_pipeline(msg["message"]["content"])

With a simple pub/sub channel, agents can collaborate, escalate, and recover from failures without tight orchestration.


It’s just pub/sub

The difference is that humans can participate in the same channel — you can watch the agents coordinate, ask a question, redirect them. The collaboration is visible.


The mental shift

Stop treating your agents as opaque back‑end jobs.

That shift completely changes the orchestration layer you need. You don’t need a heavy telemetry dashboard. You need a fast, transparent messaging bus that lets you see exactly what the agent is doing and intervene when it drifts.


Agent United

I kept hitting this friction point, so I wrote Agent United. It’s an open‑source, self‑hosted chat platform that handles all the WebSocket plumbing, auth separation, and persistent state so you can just focus on your agent logic. It spins up with a single command:

docker-compose up

If you’re building systems that need human oversight, you can grab the code on GitHub or see the API patterns at docs.agentunited.ai/docs/agent‑guide.


Conclusion

But ultimately, the implementation doesn’t matter. The pattern does. Build systems that talk back.


Word count: ~1,050 words

Target: Dev.to (full post) + HN link post March 9 + r/MachineLearning patterns section only

0 views
Back to Blog

Related posts

Read more »