에이전트 여러 개를 동시에 돌릴 때 가장 먼저 부딪히는 문제는 통신입니다. 에이전트가 서로 직접 호출하면 호출 관계가 그물망이 되고, 하나라도 응답을 늦게 하면 전체가 멈춥니다. guregodevo는 10월 31일 memdoor를 공개했습니다. Docker도 Python도 아닌, CLI 바이너리 하나로 에이전트 간 메시지를 중계하는 데몬을 만드는 도구입니다. 이 글은 원문과 저장소 문서를 읽은 뒤, 데몬 중계 구조를 Node.js로 작은 예제로 재현한 기록입니다. memdoor 자체를 빌드하거나 모델을 연결해 실행하지는 않았습니다.

“Docker도 Python도 아닌 바이너리”가 의미하는 것

원문은 memdoor를 “daemon agents CLI binary, multi-agent harness”라고 소개합니다. Docker 컨테이너로 격리하지도, Python 런타임에 의존하지도 않습니다. 대신 단일 실행 파일로 데몬을 띄우고, 에이전트는 그 데몬에 메시지를 보내고 받는 방식으로 통신합니다.

memdoor 공식 저장소의 구조도. 데몬 프로세스가 중앙에 있고, 여러 에이전트가 메시지를 주고받는다.

이미지: guregodevo/memdoor 공식 저장소 스크린샷.

이 설계가 의미하는 것은 두 가지입니다. 첫째, 환경 의존성이 적습니다. Python이 설치되지 않은 서버나 임베디드 기기에서도 데몬을 띄울 수 있습니다. 둘째, 에이전트는 데몬과만 알면 되므로 호출 관계가 별에서 중심점으로 바뀝니다. 에이전트 N개가 서로 N²개 호출할 필요가 없고, 모두 데몬에 한 곳만 보내면 됩니다.

메시지 중계가 왜 필요한가

에이전트 간 직접 호출은 세 가지 문제를 일으킵니다.

첫째, 연결성 관리입니다. 에이전트 A가 B를 호출할 때 B의 주소를 알고 있어야 하고, B가 내려가면 A도 실패합니다. 데몬을 두면 에이전트는 주소 하나만 알면 되고, 데몬이 다른 에이전트의 상태를 관리합니다.

둘째, 메시지 유실입니다. 직접 호출은 네트워크 오류나 타임아웃으로 쉽게 사라집니다. 데몬이 메시지를 큐에 저장하면, 에이전트가 다시 연결될 때 이전 메시지를 받을 수 있습니다.

셋째, 관찰 지점입니다. 모든 메시지가 데몬을 거치므로, 로그를 남기거나 정책을 적용하거나 디버깅하기가 쉽습니다.

Node.js로 재현한 작은 예제

CodeBridge는 Node.js로 데몬 중계 구조를 작은 예제로 구현했습니다. 실제 memdoor의 구현을 재현한 것이 아니라, 데몬 중계의 핵심 원리만 보여줍니다.

import { spawn } from "node:child_process";
import net from "node:net";

class MessageDaemon {
  constructor(port) {
    this.port = port;
    this.clients = new Map();
    this.queue = [];
  }

  async start() {
    const server = net.createServer((socket) => {
      const id = `agent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
      this.clients.set(id, socket);
      socket.write(`connected:${id}\n`);

      socket.on("data", (data) => {
        const [to, message] = data.toString().trim().split(" ", 2);
        this.queue.push({ to, message, from: id });

        if (this.clients.has(to)) {
          this.clients.get(to).write(`from-${id}:${message}\n`);
        } else {
          console.log(`[queue] ${to} not found, queued: ${message}`);
        }
      });

      socket.on("close", () => {
        this.clients.delete(id);
      });
    });

    await new Promise((resolve) => server.listen(this.port, resolve));
    console.log(`daemon listening on port ${this.port}`);
  }

  async send(port, agentId, target, message) {
    return new Promise((resolve, reject) => {
      const client = net.connect(port, "127.0.0.1", () => {
        client.write(`${target} ${message}\n`);
        client.on("data", (data) => {
          resolve(data.toString().trim());
          client.end();
        });
      });
      client.on("error", reject);
    });
  }
}

export { MessageDaemon };

이 예제는 memdoor의 구현을 그대로 옮긴 것이 아닙니다. 데몬이 클라이언트 ID를 관리하고, 메시지를 큐에 저장하며, 대상 에이전트가 연결되어 있으면 바로 전달한다는 원리만 보여줍니다. 실제 memdoor는 바이너리 실행 파일로, 프로토콜과 메시지 형식이 다를 수 있습니다.

데몬 중계를 고를 때 확인할 질문

memdoor의 문서를 읽고 바로 적용할 수 있는 점검 항목을 정리하면 다음과 같습니다.

질문 확인할 증거 흔한 오해
데몬이 메시지 유실을 보장하는가 큐 저장 여부, 재전송 정책 바이너리이면 빠르다 = 안전하다
에이전트가 내려갔을 때 메시지는 어떻게 되는가 큐 만료 정책, 재연결 절차 데몬이 살아 있으면 메시지도 안전하다
여러 데몬을 연결할 수 있는가 라우팅 테이블, 게이트웨이 문서 단일 데몬이 모든 것을 처리한다
보안 경계로 쓸 수 있는가 인증, 암호화 문서 데몬이 중계하면 보안도 된다
기존 에이전트와 호환되는가 프로토콜 문서, SDK 목록 CLI 바이너리이면 모든 에이전트가 연결된다

도입 전에 남는 제약

memdoor에는 모델 제공자가 기본으로 들어 있지 않습니다. 사용자가 원하는 에이전트 프로토콜을 직접 구현해야 합니다. 원문은 Docker나 Python 의존성이 없다는 장점을 강조하지만, 그만큼 프로토콜 설계 책임이 사용자에게 있습니다.

메시지 큐의 크기 제한, 데몬 단일 장애점(SPOF), 에이전트 인증 방식은 이 글에서 측정하지 않았습니다. 특히 “단일 바이너리”가 성능을 보장하는 것은 아니며, 메시지 처리량에 대한 독립 벤치마크가 나오기 전까지는 설계 방향을 설명하는 주장으로 읽는 편이 안전합니다.

memdoor에서 배울 점은 바이너리 크기나 데몬 구현 방법이 아닙니다. 에이전트 간 호출 관계를 별에서 중심으로 바꾸고, 메시지 유실을 큐로 막는 구조입니다. 다음에 여러 에이전트를 연결할 때는 모델 목록보다 중계 구조와 큐 정책을 먼저 확인해 보시길 권합니다.

아래 유료 강의는 Claude Code를 포함한 AI 코딩 하네스의 검증과 제약 설계를 다룹니다. memdoor 자체의 사용법이나 이 실험의 재현을 제공하는 강의는 아니며, 링크는 홍보 링크입니다.