Зачем этот проект?

Модуль 02 охватывает семь независимых тем: архитектура агентов, tool calling, ReAct, Plan-and-Execute, память, MCP, паттерны. На каждой теме пишешь небольшой код-пример — но не понимаешь, как всё это работает вместе. Проект соединяет всё в одну систему.

Research-агент — это не учебная игрушка. Это реальный инструмент, который разработчики и аналитики используют ежедневно: «собери мне всё по теме X за последние полгода, сравни подходы, дай ссылки». Реализовать это правильно — значит освоить все паттерны модуля на практике.

Паттерн ReAct в действии. Ключевая идея проекта — агент не знает заранее, сколько шагов потребуется. Он рассуждает (Reason): «мне нужно найти больше источников» → действует (Act): вызывает search_web → наблюдает результат → снова рассуждает. Это и есть ReAct loop: LLM как оркестратор, инструменты как руки агента.

Как это выглядит в работе

╔═══════════════════════════════════════════════════════════╗ Research Agent · v1.0 · DataTalks.ru ╚═══════════════════════════════════════════════════════════╝ Модель: claude-sonnet-4-6 · Макс. шагов: 10 · Timeout: 30s you › Исследуй тему: "Лучшие практики построения RAG-систем в 2024" ───────────────────────────── шаг 1 / 10 ───────────────────────────── 🔍 search_web(query="RAG best practices 2024 retrieval augmented generation") → 8 результатов · Tavily · 0.4 сек [1] lilianweng.github.io/posts/2023-03-15-prompt-engineering [2] arxiv.org/abs/2312.10997 · "RAG Survey" [3] towardsdatascience.com/advanced-rag-techniques... ───────────────────────────── шаг 2 / 10 ───────────────────────────── 📄 fetch_pages(urls=["lilianweng.github.io/...", "towardsdatascience.com/..."]) → 2 страницы параллельно · 5 840 + 3 210 символов · 1.2 сек ───────────────────────────── шаг 3 / 10 ───────────────────────────── 📝 summarize(pages=[...]) → Lilian Weng: chunk size selection, overlap strategies, hybrid retrieval → TDS: advanced indexing, reranking with cross-encoders, query expansion ───────────────────────────── шаг 4 / 10 ───────────────────────────── 🔍 search_web(query="RAG evaluation RAGAS metrics faithfulness 2024") → 6 результатов · 0.3 сек ───────────────────────────── шаг 5 / 10 ───────────────────────────── 📄 fetch_pages(urls=["docs.ragas.io/...", "arxiv.org/abs/2309.15217"]) → 2 страницы параллельно · 1.1 сек ───────────────────────────── шаг 6 / 10 ───────────────────────────── ✍️ write_report() → Синтезирую финальный отчёт из 4 источников... ══════════════════════════════ ОТЧЁТ ══════════════════════════════ # Лучшие практики построения RAG-систем (2024) ## 1. Стратегии чанкинга Оптимальный размер чанка — 256–512 токенов при overlap 10–20%. Для структурированных документов рекомендуется document-aware chunking по разделам, а не фиксированный размер [1]. ## 2. Hybrid Search Комбинирование BM25 и векторного поиска через RRF даёт +12–18% к recall по сравнению с чистым векторным поиском [2]. ## 3. Оценка качества (RAGAS) Ключевые метрики: Faithfulness, Answer Relevancy, Context Recall. Для production рекомендуется автоматический eval pipeline [3, 4]. 📎 Источники: [1] lilianweng.github.io · "Prompt Engineering Guide" [2] towardsdatascience.com · "Advanced RAG Techniques" [3] docs.ragas.io · "Evaluation Framework" [4] arxiv.org/abs/2309.15217 · "RAGAS: Automated Evaluation" Использовано: 6 шагов · 5 инструментальных вызовов · 3 840 токенов · 18.2 сек ─────────────────────────────────────────────────────────────────── you › /save report.md ✓ Отчёт сохранён → research/2024-04-04_rag-best-practices.md

Архитектура проекта

В центре системы — ReAct Loop: оркестратор в бесконечном цикле отправляет сообщения LLM, получает решение (думать дальше или вызвать инструмент), выполняет инструмент и возвращает результат обратно. Цикл завершается, когда LLM решает вызвать write_report.

100%
колёсико — масштаб  ·  зажать и тянуть — перемещение
CLI AGENT TOOLS ASYNC I/O EXTERNAL 👤 Пользователь Terminal · stdin / stdout query report + Rich main.py CLI · argparse display.py Rich · progress · Markdown config.py Pydantic · .env · secrets orchestrator.py ReAct Loop · step limit stop condition state.py AgentState · messages scratchpad · sources llm_client.py Anthropic / OpenAI SDK streaming · retry registry.py Tool registry JSON Schema → fn search_web Tavily API query fetch_pages httpx · async · BeautifulSoup summarize_page LLM · сжатие контента write_report LLM · Markdown · citations schemas httpx.AsyncClient asyncio.gather() · параллельные запросы · timeout · retry LLM async calls async client · streaming · token counting Tavily API web search Web Pages HTML → plain text Anthropic API claude-sonnet-4-6 OpenAI API gpt-4o-mini (fallback) tool_use / text response

ReAct Loop: шаг за шагом

Цикл оркестратора (orchestrator.py)
1. Добавить запрос в history
2. Вызвать LLM с tools
3. Разобрать ответ

4a. tool_use → dispatch
5a. Выполнить инструмент
6a. Добавить result в history
GOTO 2

4b. text → финальный ответ
5b. Форматировать отчёт
STOP

Инструменты агента

Четыре инструмента покрывают полный цикл исследования. Каждый описывается JSON Schema (модуль 02, tool calling), агент сам решает когда и какой использовать.

Инструмент
Что делает
Источник данных
search_web(query)
Поиск релевантных URL по запросу. Возвращает title, url, snippet для топ-N результатов.
Tavily API
fetch_pages(urls)
Загружает несколько URL параллельно через asyncio.gather(). Извлекает plain text из HTML (BeautifulSoup). Обрезает до max_chars.
httpx async
summarize_page(url, content)
Суммаризирует текст страницы через LLM. Извлекает ключевые факты, относящиеся к теме исследования.
LLM API
write_report(findings)
Финальный инструмент. Синтезирует все суммаризации в структурированный Markdown-отчёт с нумерованными ссылками.
LLM API
Почему fetch_pages (во множественном числе)? Параллельный запрос через asyncio.gather() — это конкретный навык из модуля 01 (asyncio) плюс модуля 02 (parallel tool calling). Загружать 3 страницы за 1.2 секунды вместо 3×1.2=3.6 — важно для удобства пользователя. Инструмент принимает список URL именно по этой причине.

Функциональность

🔁
Автономный ReAct Loop
Агент сам решает сколько шагов нужно: ищет, читает, ищет снова, синтезирует. Не нужно вручную управлять процессом.
Параллельная загрузка
Несколько URL загружаются одновременно через asyncio. Экономит в 3–5 раз время на I/O-операциях.
📎
Цитаты в отчёте
Каждый факт в итоговом отчёте пронумерован и привязан к конкретному источнику с заголовком и URL.
🛡️
Обработка ошибок
Упавший инструмент не убивает агента: ошибка передаётся в LLM как tool_result, агент решает продолжить или сменить тактику.
💾
Сохранение отчёта
Команда /save сохраняет отчёт в Markdown с автоматическим именем по дате и теме.
🔧
Настраиваемые параметры
Макс. шагов, число результатов поиска, max_chars на страницу, LLM-провайдер — всё через .env и флаги CLI.

Технологический стек

🐍 Python 3.11+ anthropic SDK openai SDK asyncio httpx[async] beautifulsoup4 tavily-python pydantic rich python-dotenv
Никаких фреймворков. Никакого LangChain, LangGraph, CrewAI. Это намеренно: модуль 02 посвящён пониманию того, как агенты работают изнутри. Цикл ReAct, диспетчер инструментов, управление историей — всё пишется вручную. Фреймворки придут в следующих модулях, но понимать что они делают под капотом — ваше конкурентное преимущество.

Структура проекта

research_agent/ ├── main.py # точка входа, CLI (argparse) ├── agent/ │ ├── orchestrator.py # ReAct loop, stop condition │ ├── state.py # AgentState: messages, scratchpad, sources │ └── llm_client.py # Anthropic/OpenAI с retry и streaming ├── tools/ │ ├── registry.py # регистрация + JSON Schema + dispatch │ ├── search.py # search_web → Tavily │ ├── fetch.py # fetch_pages → httpx async + BeautifulSoup │ ├── summarize.py # summarize_page → LLM │ └── report.py # write_report → LLM + Markdown formatter ├── ui/ │ └── display.py # Rich: прогресс, spinner, Markdown render ├── config/ │ └── settings.py # Pydantic Settings, .env загрузка ├── research/ # папка для сохранённых отчётов ├── tests/ │ ├── test_tools.py # unit-тесты инструментов (mock HTTP) │ └── test_agent.py # интеграционный тест loop (mock LLM) ├── pyproject.toml └── .env.example # ANTHROPIC_API_KEY, TAVILY_API_KEY, ...

Что закрепляет этот проект

🔁
ReAct Loop — реализуете цикл «рассуждение → действие → наблюдение» с явным state machine и условием остановки.
🔧
Tool Calling — пишете JSON Schema для каждого инструмента, реализуете диспетчер dispatch(tool_name, tool_input).
Parallel Tool Callingfetch_pages использует asyncio.gather(), понимаете разницу с sequential.
🛡️
Tool Error Handling — ошибки инструментов передаются в LLM через tool_result с is_error: true, агент адаптируется.
🧠
Short-term Memory — управляете историей сообщений вручную: truncation при превышении context window, scratchpad для заметок.
📐
Чистая архитектура — слои CLI / Agent / Tools / External не знают о реализации друг друга, легко заменить Tavily на другой поиск.

Следующий шаг: реализация

Пошаговое руководство по написанию кода — от скелета оркестратора до финального тестирования. Разбираем каждый компонент по отдельности.

Реализация агента →