HARD-задача: прятки в репозитории

Домашка

Учебная домашка модуля — «агент с одним инструментом» — была милосердна: сценарий из двух шагов. Настоящая задача жёстче: агент должен найти ответ в репозитории за ограниченное число шагов и уложиться в бюджет токенов промпта. Проверяться будет не «нашёл вообще», а «нашёл дёшево». Это первая задача курса, где качество измеряется сразу в двух валютах: шагах и токенах.

Описание задачи

Файл scavenger.py с функцией:

python
def find_answer(llm, root, question: str,
                max_steps: int = 4, max_prompt_tokens: int = 700) -> str

Она собирает конвейер из уже написанных модулей: make_tools(root) для инструментов, петлю и сжатие истории под бюджет max_prompt_tokens (на каждый промпт, через budget_history и estimate_tokens). Возвращает ответ агента (DONE) или статус лимита.

Требования:

  • Ни один промпт, отправленный модели, не должен превышать max_prompt_tokens токенов (по estimate_tokens).

  • Число обращений к модели — не больше max_steps.

  • Петля, инструменты, сжатие — переиспользуются из loop.py, tools.py, history.py; копипаста кода между модулями — дисквалификация стиля (и моя, и ваша).

Как проверить себя

test_scavenger.py:

python
from pathlib import Path

from fake_llm import ScriptedLLM
from scavenger import find_answer
from tokens import estimate_tokens


def make_repo(tmp_path):
    src = tmp_path / "src"
    src.mkdir()
    (src / "auth.py").write_text(
        "def validate_email(email: str) -> bool:\n"
        "    return '@' in email and '.' in email.split('@')[-1]\n",
        encoding="utf-8",
    )
    (src / "billing.py").write_text(
        "def charge(amount: int) -> str:\n"
        "    if amount <= 0:\n"
        "        raise ValueError('amount must be positive')\n"
        "    return 'ok'\n",
        encoding="utf-8",
    )
    return tmp_path


def test_finds_answer_within_budget(tmp_path):
    root = make_repo(tmp_path)
    llm = ScriptedLLM([
        "ACT: search validate_email",
        "DONE: src/auth.py, функция validate_email",
    ])
    answer = find_answer(llm, root, "где валидируется email?", max_prompt_tokens=400)
    assert answer == "src/auth.py, функция validate_email"
    assert len(llm.prompts) <= 4
    for prompt in llm.prompts:
        assert estimate_tokens(prompt) <= 400, estimate_tokens(prompt)


def test_survives_huge_observation(tmp_path):
    root = make_repo(tmp_path)
    (root / "big.txt").write_text("шум " * 2000, encoding="utf-8")
    llm = ScriptedLLM([
        "ACT: read_file big.txt",
        "DONE: нашёл не там, но уложился",
    ])
    answer = find_answer(llm, root, "прочитай большой файл", max_prompt_tokens=300)
    assert answer == "нашёл не там, но уложился"
    for prompt in llm.prompts:
        assert estimate_tokens(prompt) <= 300

Второй тест — сердце задачи: наблюдение от big.txt (16000 токенов по оценке) обязано срезаться сжатием истории, чтобы второй промпт остался в бюджете. Если ваш find_answer просто копирует run_agent — тест это поймает.

Критерии готовности

  • Оба теста зелёные, на любом размере бюджета из тестов.

  • Код конвейера — композиция трёх модулей, без дублирования их логики.

  • Коммит: scavenger.py, test_scavenger.py.

Проверьте на живой модели (необязательно)

Прогоните find_answer(LLMClient(), "путь-к-своему-репо", "где читается конфигурация?") с max_steps=4. Посмотрите в сохранённых промптах (добавьте их запись, как в ScriptedLLM), по какому пути модель шла. Если циклится на list_dir — это не код плохой, это промпт: подсказка «начинай с search по ключевому слову из вопроса» лечит за одну правку.

  • theory icon

    Петля агента: решить → сделать → посмотреть

    5 мин

  • homework icon

    Домашка: агент с одним инструментом

    3 мин

  • quiz icon

    Квиз: Петля агента: решить → сделать → посмотреть

    6 мин

  • theory icon

    Инструменты: руки агента

    5 мин

  • homework icon

    Домашка: набор разведчика

    3 мин

  • quiz icon

    Квиз: Инструменты: руки агента

    5 мин

  • theory icon

    Память и контекст: что тащим, что выбрасываем

    5 мин

  • homework icon

    Домашка: сжатие истории

    3 мин

  • quiz icon

    Квиз: Память и контекст: что тащим, что выбрасываем

    6 мин

  • theory icon

    Свой цикл против LangChain и LlamaIndex

    4 мин

  • homework icon

    HARD-задача: прятки в репозитории

    3 мин

  • quiz icon

    Квиз: Свой цикл против LangChain и LlamaIndex

    5 мин

  • project icon

    Проект «Разведчик»: агент, который знает ваш репозиторий

    3 мин

🎯
Тренажёр собеседованияЗакрепите знания перед интервью