Домашка: сжатие истории
Домашка
Петля devbuddy не знает слова «хватит»: наблюдения копятся до упора в окно. Сегодня вы это чините — history.py с функцией budget_history, которая держит свежие наблюдения и схлопывает старые в сводку, укладываясь в заданный бюджет токенов. Петля получит дешёвую память, а вы — ещё один переиспользуемый модуль.
Описание задачи
Файл history.py.
Основная функциональность
budget_history(history: list[str], max_tokens: int, estimate=estimate_tokens) -> list[str]:
если вся история укладывается в
max_tokens— вернуть её без изменений (копию);иначе оставить максимальный суффикс (самые свежие наблюдения), который вместе со сводкой влезает в бюджет; сводка — первой строкой:
(ранее: K наблюдений опущено), где K — число отброшенных;если не влезает даже одно наблюдение со сводкой — вернуть только сводку по всей истории.
Подсчёт токенов — через estimate_tokens из tokens.py (передаётся параметром — в тестах можно подменить). Считаем "\n".join(...) от списка строк вместе со сводкой.
Дополнительные условия
Только стандартная библиотека + ваш
tokens.py.Функция чистая: исходный список не менять.
Как проверить себя
test_history.py, запуск python -m pytest test_history.py -v:
from history import budget_history
from tokens import estimate_tokens
LINES = [f"наблюдение номер {i}: " + "данные " * 20 for i in range(10)]
def test_short_history_untouched():
history = ["одно", "два"]
assert budget_history(history, 10_000) == ["одно", "два"]
assert history == ["одно", "два"] # исходник не тронут
def test_result_fits_budget():
out = budget_history(LINES, 120)
assert estimate_tokens("\n".join(out)) <= 120
def test_tail_preserved():
out = budget_history(LINES, 400)
kept = out[1:] if out[0].startswith("(ранее:") else out
assert kept == LINES[-len(kept):] # хвост сохраняется подряд, без дыр
def test_summary_when_dropped():
out = budget_history(LINES, 120)
assert len(out) < len(LINES)
assert out[0].startswith("(ранее:")
assert "8" in out[0] or "9" in out[0] # отброшено почти всё
def test_tiny_budget_returns_summary_only():
out = budget_history(["очень длинное наблюдение " * 50, "второе"], 1)
assert len(out) == 1
assert out[0].startswith("(ранее:")Пример работы программы
>>> from history import budget_history
>>> history = ["шаг 1: нашли файл", "шаг 2: прочитали", "шаг 3: сверили", "шаг 4: готово"]
>>> budget_history(history, 12)
['(ранее: 2 наблюдений опущено)', 'шаг 3: сверили', 'шаг 4: готово'](точные границы зависят от estimate_tokens — в вашем запуске чисел в сводке может быть другое, тесты проверяют свойства, а не точную строку.)
Критерии готовности
Пять тестов зелёные.
Результат всегда в бюджете, хвост подряд, отброшенное — со сводкой.
Коммит:
history.py,test_history.py.
Петля агента: решить → сделать → посмотреть
5 мин
Домашка: агент с одним инструментом
3 мин
Квиз: Петля агента: решить → сделать → посмотреть
6 мин
Инструменты: руки агента
5 мин
Домашка: набор разведчика
3 мин
Квиз: Инструменты: руки агента
5 мин
Память и контекст: что тащим, что выбрасываем
5 мин
Домашка: сжатие истории
3 мин
Квиз: Память и контекст: что тащим, что выбрасываем
6 мин
Свой цикл против LangChain и LlamaIndex
4 мин
HARD-задача: прятки в репозитории
3 мин
Квиз: Свой цикл против LangChain и LlamaIndex
5 мин
Проект «Разведчик»: агент, который знает ваш репозиторий
3 мин