How to Build an AI Agent
How to Build an AI Agent
s08

Hintergrundaufgaben

Nebenläufigkeit

Background Threads + Notifications

198 LOC6 ToolsBackgroundManager + notification queue
Run slow operations in the background; the agent keeps thinking ahead

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

"Langsame Operationen im Hintergrund ausführen; der Agent denkt weiter" -- Daemon-Threads führen Befehle aus und fügen Benachrichtigungen nach Abschluss ein.

Problem

Manche Befehle dauern Minuten: npm install, pytest, docker build. Mit einer blockierenden Schleife sitzt das Modell untätig da. Wenn der Benutzer "installiere Abhängigkeiten und während das läuft, erstelle die Konfigurationsdatei" fragt, führt der Agent sie sequenziell aus, nicht parallel.

Lösung

Hauptthread                 Hintergrund-Thread
+-----------------+        +-----------------+
| agent loop      |        | subprocess runs |
| ...             |        | ...             |
| [LLM call] <---+------- | enqueue(result) |
|  ^drain queue   |        +-----------------+
+-----------------+

Zeitachse:
Agent --[spawn A]--[spawn B]--[andere Arbeit]----
             |          |
             v          v
          [A läuft]  [B läuft]     (parallel)
             |          |
             +-- Ergebnisse eingefügt vor nächstem LLM-Aufruf --+

Wie es funktioniert

  1. BackgroundManager verfolgt Aufgaben mit einer threadsicheren Benachrichtigungs-Warteschlange.
class BackgroundManager:
    def __init__(self):
        self.tasks = {}
        self._notification_queue = []
        self._lock = threading.Lock()
  1. run() startet einen Daemon-Thread und kehrt sofort zurück.
def run(self, command: str) -> str:
    task_id = str(uuid.uuid4())[:8]
    self.tasks[task_id] = {"status": "running", "command": command}
    thread = threading.Thread(
        target=self._execute, args=(task_id, command), daemon=True)
    thread.start()
    return f"Background task {task_id} started"
  1. Wenn der Subprocess fertig ist, geht sein Ergebnis in die Benachrichtigungs-Warteschlange.
def _execute(self, task_id, command):
    try:
        r = subprocess.run(command, shell=True, cwd=WORKDIR,
            capture_output=True, text=True, timeout=300)
        output = (r.stdout + r.stderr).strip()[:50000]
    except subprocess.TimeoutExpired:
        output = "Error: Timeout (300s)"
    with self._lock:
        self._notification_queue.append({
            "task_id": task_id, "result": output[:500]})
  1. Die Agent-Schleife leert die Benachrichtigungen vor jedem LLM-Aufruf.
def agent_loop(messages: list):
    while True:
        notifs = BG.drain_notifications()
        if notifs:
            notif_text = "\n".join(
                f"[bg:{n['task_id']}] {n['result']}" for n in notifs)
            messages.append({"role": "user",
                "content": f"<background-results>\n{notif_text}\n"
                           f"</background-results>"})
            messages.append({"role": "assistant",
                "content": "Noted background results."})
        response = client.messages.create(...)

Die Schleife bleibt Single-Threaded. Nur die Subprocess-E/A wird parallelisiert.

Was sich von s07 geändert hat

KomponenteVorher (s07)Nachher (s08)
Tools86 (basis + background_run + check)
AusführungNur blockierendBlockierend + Hintergrund-Threads
BenachrichtigungKeineWarteschlange pro Schleife geleert
ParallelitätKeineDaemon-Threads

Ausprobieren

python agents/s08_background_tasks.py
  1. Führe "sleep 5 && echo done" im Hintergrund aus, erstelle dann eine Datei während es läuft
  2. Starte 3 Hintergrundaufgaben: "sleep 2", "sleep 4", "sleep 6". Überprüfe ihren Status.
  3. Führe pytest im Hintergrund aus und arbeite weiter an anderen Dingen