How to Build an AI Agent
How to Build an AI Agent
s04

Под-агенты

Планирование и координация

Clean Context Per Subtask

151 LOC5 инструментовSubagent spawn with isolated messages[]
Subagents use independent messages[], keeping the main conversation clean

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

"Разбивайте большие задачи; каждая подзадача получает чистый контекст" -- под-агенты используют независимые messages[], сохраняя главный разговор чистым.

Проблема

По мере работы агента его массив сообщений растёт. Каждый прочитанный файл, каждый вывод bash остаётся в контексте навсегда. "Какой тестовый фреймворк использует этот проект?" может потребовать чтения 5 файлов, но родителю нужен только ответ: "pytest".

Решение

Parent agent                     Subagent
+------------------+             +------------------+
| messages=[...]   |             | messages=[]      | <-- fresh
|                  |  dispatch   |                  |
| tool: task       | ----------> | while tool_use:  |
|   prompt="..."   |             |   call tools     |
|                  |  summary    |   append results |
|   result = "..." | <---------- | return last text |
+------------------+             +------------------+

Parent context stays clean. Subagent context is discarded.

Как Это Работает

  1. Родитель получает инструмент task. Ребёнок получает все базовые инструменты кроме task (без рекурсивного спавна).
PARENT_TOOLS = CHILD_TOOLS + [
    {"name": "task",
     "description": "Spawn a subagent with fresh context.",
     "input_schema": {
         "type": "object",
         "properties": {"prompt": {"type": "string"}},
         "required": ["prompt"],
     }},
]
  1. Под-агент начинается с messages=[] и запускает свой собственный цикл. Только финальный текст возвращается родителю.
def run_subagent(prompt: str) -> str:
    sub_messages = [{"role": "user", "content": prompt}]
    for _ in range(30):  # safety limit
        response = client.messages.create(
            model=MODEL, system=SUBAGENT_SYSTEM,
            messages=sub_messages,
            tools=CHILD_TOOLS, max_tokens=8000,
        )
        sub_messages.append({"role": "assistant",
                             "content": response.content})
        if response.stop_reason != "tool_use":
            break
        results = []
        for block in response.content:
            if block.type == "tool_use":
                handler = TOOL_HANDLERS.get(block.name)
                output = handler(**block.input)
                results.append({"type": "tool_result",
                    "tool_use_id": block.id,
                    "content": str(output)[:50000]})
        sub_messages.append({"role": "user", "content": results})
    return "".join(
        b.text for b in response.content if hasattr(b, "text")
    ) or "(no summary)"

Вся история сообщений ребёнка (возможно 30+ вызовов инструментов) отбрасывается. Родитель получает одно предложение-резюме как обычный `tool Изменилось с s03

|_result`.

Что Компонент | До (s03) | После (s04) |

| ------------ | ----------- | ----------------------------- | | Инструменты | 5 | 5 (base) + task (parent) | | Контекст | Один общий | Изоляция Parent + Child | | Под-агент | Нет | Функция run_subagent() | | Возвращаемое значение | Н/Д | Только текст-резюме |

Попробуйте

python agents/s04_subagent.py
  1. Используйте подзадачу, чтобы узнать, какой тестовый фреймворк использует этот проект
  2. Делегируйте: прочитайте все .py файлы и резюмируйте, что делает каждый
  3. Используйте task для создания нового модуля, затем проверьте его отсюда