How to Build an AI Agent
How to Build an AI Agent
s09

Times de Agentes

Colaboracao

Teammates + Mailboxes

348 LOC10 ferramentasTeammateManager + file-based mailbox
When one agent can't finish, delegate to persistent teammates via async mailboxes

s01 > s02 > s03 > s04 > s05 > s06 | s07 > s08 > [ s09 ] s10 > s11 > s12

"Quando a tarefa é grande demais para um, delegue para companheiros" -- companheiros persistentes + caixas de correio assíncronas.

Problema

Subagentes (s04) são descartáveis: spawn, trabalha, retorna resumo, morre. Sem identidade, sem memória entre invocações. Tarefas em segundo plano (s08) executam comandos shell mas não conseguem tomar decisões guiadas por LLM.

Trabalho em equipe real precisa de: (1) agentes persistentes que sobrevivem a um único prompt, (2) identidade e gerenciamento de ciclo de vida, (3) um canal de comunicação entre agentes.

Solução

Ciclo de vida do companheiro:
  spawn -> WORKING -> IDLE -> WORKING -> ... -> SHUTDOWN

Comunicação:
  .team/
    config.json           <- roster da equipe + statuses
    inbox/
      alice.jsonl         <- append-only, drain-on-read
      bob.jsonl
      lead.jsonl

              +--------+    send("alice","bob","...")    +--------+
              | alice  | -----------------------------> |  bob   |
              | loop   |    bob.jsonl << {json_line}    |  loop  |
              +--------+                                +--------+
                   ^                                         |
                   |        BUS.read_inbox("alice")          |
                   +---- alice.jsonl -> read + drain ---------+

Como Funciona

  1. TeammateManager mantém config.json com o roster da equipe.
class TeammateManager:
    def __init__(self, team_dir: Path):
        self.dir = team_dir
        self.dir.mkdir(exist_ok=True)
        self.config_path = self.dir / "config.json"
        self.config = self._load_config()
        self.threads = {}
  1. spawn() cria um companheiro e inicia seu loop de agente em uma thread.
def spawn(self, name: str, role: str, prompt: str) -> str:
    member = {"name": name, "role": role, "status": "working"}
    self.config["members"].append(member)
    self._save_config()
    thread = threading.Thread(
        target=self._teammate_loop,
        args=(name, role, prompt), daemon=True)
    thread.start()
    return f"Spawned teammate '{name}' (role: {role})"
  1. MessageBus: caixas de entrada JSONL append-only. send() appenda uma linha JSON; read_inbox() lê tudo e drena.
class MessageBus:
    def send(self, sender, to, content, msg_type="message", extra=None):
        msg = {"type": msg_type, "from": sender,
               "content": content, "timestamp": time.time()}
        if extra:
            msg.update(extra)
        with open(self.dir / f"{to}.jsonl", "a") as f:
            f.write(json.dumps(msg) + "\n")

    def read_inbox(self, name):
        path = self.dir / f"{name}.jsonl"
        if not path.exists(): return "[]"
        msgs = [json.loads(l) for l in path.read_text().strip().splitlines() if l]
        path.write_text("")  # drain
        return json.dumps(msgs, indent=2)
  1. Cada companheiro verifica sua caixa de entrada antes de cada chamada LLM, injetando mensagens recebidas no contexto.
def _teammate_loop(self, name, role, prompt):
    messages = [{"role": "user", "content": prompt}]
    for _ in range(50):
        inbox = BUS.read_inbox(name)
        if inbox != "[]":
            messages.append({"role": "user",
                "content": f"<inbox>{inbox}</inbox>"})
            messages.append({"role": "assistant",
                "content": "Noted inbox messages."})
        response = client.messages.create(...)
        if response.stop_reason != "tool_use":
            break
        # execute tools, append results...
    self._find_member(name)["status"] = "idle"

O Que Mudou Desde s08

ComponenteAntes (s08)Depois (s09)
Ferramentas69 (+spawn/send/read_inbox)
AgentesÚnicoLead + N companheiros
PersistênciaNenhumconfig.json + JSONL inboxes
ThreadsComandos em backgroundLoops de agente completos por thread
Ciclo de vidaFire-and-forgetidle -> working -> idle
ComunicaçãoNenhummessage + broadcast

Experimente

python agents/s09_agent_teams.py
  1. Spawn alice (coder) e bob (tester). Faça alice enviar uma mensagem para bob.
  2. Broadcast "status update: phase 1 complete" para todos os companheiros
  3. Verifique a caixa de entrada do lead por quaisquer mensagens
  4. Digite /team para ver o roster da equipe com statuses
  5. Digite /inbox para verificar manualmente a caixa de entrada do lead