Урок 08 · Параллелизм

Пусть задачи работают сами — агент не блокируется

«Fire and forget — the agent doesn't block while the command runs.»

⏱ ~10 мин · 📝 3 интерактивных компонента · 🧑‍💻 На основе shareAI-lab · s08_background_tasks.py

Боль синхронных вызовов

Инструмент bash из S02 синхронный: subprocess.run(..., timeout=120). Если запущен npm install на 90 секунд — весь agent loop стоит 90 секунд. Пользователь смотрит в терминал и не понимает, завис ли агент или работает.

Решение s08: дать агенту инструмент background_run. Он немедленно возвращает task_id, команда выполняется в отдельном потоке. Агент продолжает цикл и делает другие вещи; когда фоновая задача завершается, результат попадает в очередь уведомлений.

def run(self, command: str) -> str:
    task_id = str(uuid.uuid4())[:8]
    self.tasks[task_id] = {"status":"running", ...}
    thread = threading.Thread(target=self._execute, args=(task_id, command), daemon=True)
    thread.start()
    return f"Background task {task_id} started"   # возвращается немедленно

Как результат возвращается агенту?

Ключевой элемент — потокобезопасная очередь: фоновый поток при завершении добавляет в неё результат; главный поток дренирует очередь перед каждым LLM-вызовом и добавляет уведомления в messages как user-сообщение.

def agent_loop(messages):
    while True:
        # Drain bg notifications before each LLM call
        notifs = BG.drain_notifications()
        if notifs:
            messages.append({
                "role": "user",
                "content": f"<background-results>{notif_text}</background-results>",
            })
        response = client.messages.create(...)
        ...

Так агент запускает фоновую задачу в раунде N, а через 3 раунда — когда она завершится — следующий LLM-вызов автоматически получит результат. Модель видит блок <background-results> и понимает: «та задача завершилась, продолжаю».

Визуализация временной шкалы

Виджет ниже: главный поток тикает раз в секунду (имитация цикла агента); вы можете в любой момент spawn фоновую задачу. Наблюдайте, как два потока исполнения встречаются в «точке дренирования».

Какие команды стоит отправлять в фон?

Не каждую команду нужно запускать фоново. Два критерия:

  1. Время выполнения: короткие (несколько секунд) проще запускать синхронно — накладные расходы на очередь дороже ожидания.
  2. Важность результата прямо сейчас: если следующий шаг сразу использует этот результат (например, cat file.txtgrep), фон бессмысленен — всё равно придётся ждать.
Интерактив

Виджет 1 · Timeline · главный поток + 2 фоновых потока

Нажмите Spawn, чтобы дать задание фоновому потоку. Главный поток проверяет notification queue при каждом тике. Наблюдайте, как три swim lane переплетаются.

🧠 Главный поток (agent loop)
⚙ Фоновый поток A
(idle)
⚙ Фоновый поток B
(idle)
queue: []
Интерактив

Виджет 2 · Fire & Forget · 8 команд, какие стоит запускать фоново?

Для каждой команды выберите foreground (синхронно) или background (асинхронно). Думайте о двух измерениях: «нужен ли результат сразу» + «сколько времени займёт».

Правильно: 0 / 8
Интерактив

Виджет 3 · Drain Timing · на каком раунде агент увидит результат?

Ключевое правило: главный поток дренирует очередь перед каждым LLM-вызовом. 5 сценариев — ответьте, в каком раунде фоновый результат попадёт в поле зрения агента.

Правильно: 0 / 5