ИИ-проекты без RAG: стратегия дробления кода для больших проектов | AiManual
AiManual Logo Ai / Manual.
06 Июн 2026 Гайд

Когда код тяжелее контекста: анатомия дробления без RAG

Как создавать ИИ-проекты без RAG, когда исходный код превышает 100 КБ. Стратегия дробления на автономные блоки, вайб-кодинг и лимиты контекста — полный гайд.

Реклама
vec_recv1

Когда монолит не лезет в контекстное окно

Вы когда-нибудь пытались попросить GPT-5 или Claude 4 Opus написать весь проект за один промпт? Даже с токенными окнами под 200K, в 2026 году это всё ещё ад блока проекта. Только представьте: у вас микросервис на 150 КБ исходного кода — логика, валидация, работа с внешним API. Вы скармливаете это нейросети, просите добавить новую фичу, а в ответ — гигантский скрипт, где половина — просто забытый старый код, а другая — галлюцинации о том, чего в проекте никогда не было. Знакомо?

Проблема не в моделях, а в их ограничениях. Даже самые умные LLM не могут удержать в голове 100+ КБ кода без потери логики, если не использовать специальные архитектурные трюки.

Обычный совет — натягивать RAG: разбить код на чанки, загнать в векторную базу, настроить ретривер. Но RAG — это оверхед, который не всегда оправдан. Особенно когда проект на стадии прототипа или вайб-кодинга — когда слепая вера в ИИ даёт быстрый результат. Но как только код переваливает за 100 КБ, вера заканчивается, а начинается борьба за читаемость и адекватность.

Почему контекст — враг больших проектов

Давайте честно: даже 200K токенов — это не так много, как кажется. Промпт с описанием фичи, парой примеров кода и всей базой проекта — и вот уже 50% окна занято. LLM начинает забывать предыдущие инструкции, переписывать модули, которые уже были, и генерировать мусор. В статье про антипаттерны AI-кода мы разбирали, как 6 МБ мусора появляется из-за слепого доверия к структуре. Здесь то же самое — модель пытается скопировать шаблон из обучения, не понимая, что у вас уже есть другой код.

Главная причина — отсутствие грануляции. Если модель видит весь проект как один огромный контекст, она не может выделить чёткие границы между модулями. Ей проще родить ещё один файл, чем разбираться, что уже написано. А если вы пошлёте ей больше одного файла — начнётся путаница, какой из них главный.

⚠️
Грабли: попытка затолкать весь проект в один промпт — самый частый антипаттерн вайб-кодинга. ИИ не просто галлюцинирует, он начинает забывать собственные решения в пределах одной сессии. Поток ошибок растёт в геометрической прогрессии.

Альтернатива RAG: стратегия дробления

Вместо того чтобы тащить инфраструктуру для ретривала, можно сделать код самодостаточным. Идея простая: разбить проект на такие блоки, каждый из которых помещается в один промпт, а связь между ними — только через чёткие интерфейсы. Тогда ИИ может работать с каждым блоком отдельно, не зная, что творится в соседнем. Это похоже на микросервисы, но внутри одного репозитория, без лишних HTTP-запросов.

Такая стратегия решает несколько проблем:

  • Контекстная изоляция — модель не перегружается лишними деталями.
  • Повторяемость — изменив один блок, вы не сломаете другой, если интерфейс остался неизменным.
  • Тестируемость — каждый блок можно тестировать в изоляции, а значит, контролировать качество генерации.

В 2026 году, когда модели вроде GPT-5 стали умнее, но всё ещё страдают от деградации контекста при длинных сессиях, дробление — буквально спасательный круг.

1Разделяй и властвуй: грануляция по функциям

Не надо дробить по слоям (контроллеры, сервисы, репозитории) — это приведёт к тому, что один модуль всё равно будет держать половину проекта. Дробите по логическим единицам функциональности. Например, в парсере данных: DataLoader, DataValidator, DataTransformer, ReportBuilder. Каждый из них — отдельный файл с единственной публичной функцией или классом.

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

# data_loader.py
def load_csv(file_path: str) -> list[dict]:
    """Загружает CSV и возвращает список словарей."""
    pass  # реализация

# data_validator.py
def validate(records: list[dict], schema: dict) -> list[str]:
    """Проверяет записи по схеме. Возвращает список ошибок."""
    pass

# report_builder.py
def build_summary(records: list[dict], errors: list[str]) -> str:
    """Строит текстовый отчёт на основе записей и ошибок."""
    pass

Каждый файл — до 100 строк. Идеально для одного промпта: «Допиши функции в data_loader.py по такой-то спецификации».

2Жёсткие интерфейсы как контракты с LLM

ИИ склонен менять сигнатуры функций, если ему дать волю. Поэтому каждый модуль должен иметь фиксированный публичный API. В Python это Protocol или абстрактные классы. В Go — интерфейсы. В TypeScript — типы. Зафиксируйте их отдельным файлом interfaces.py или api_definitions.ts, на который вы будете ссылаться в каждом промпте.

Пример для Python с дата-классами:

# interfaces.py
from dataclasses import dataclass
from typing import Protocol

@dataclass
class Record:
    name: str
    value: float
    tags: list[str]

class DataSaver(Protocol):
    def save(self, records: list[Record]) -> None: ...

class FilterStrategy(Protocol):
    def filter(self, records: list[Record]) -> list[Record]: ...

Теперь, когда вы просите ИИ реализовать CsvSaver, вы скармливаете ему interfaces.py и свой файл-заглушку. Модель не может изменить типы — она просто реализует контракт.

Важно: не давайте LLM менять файл с интерфейсами. Пусть он будет read-only контекстом. Иначе модель «оптимизирует» его, объединяя типы, и всё сломается.

3Тесты как спецификация для одноразовых промптов

Самый недооценённый приём: сначала пишите unit-тесты для модуля (руками или с помощью ИИ), а потом просите LLM реализовать код, проходящий тесты. Это и контракт, и верификация, и мотивация не трогать соседние модули. Заодно решается проблема профессиональных практик AI-кодинга.

Пример:

# test_data_loader.py
from data_loader import load_csv

def test_load_csv_returns_list():
    result = load_csv("test.csv")
    assert isinstance(result, list)

def test_load_csv_skips_header():
    result = load_csv("test.csv")
    assert all(isinstance(r, dict) for r in result)

Теперь промпт: «Реализуй load_csv, чтобы проходили тесты из test_data_loader.py. Импорт из interfaces уже есть». Такой промпт редко галлюцинирует.

4Сборка: склейка блоков в главный модуль

Когда все блоки реализованы, остаётся только собрать их вместе. Главный файл main.py или app.py должен быть минимальным — только импорты и вызовы. Его тоже можно генерировать отдельно, давая ИИ список доступных модулей и описание бизнес-логики.

Пример main.py:

from data_loader import load_csv
from data_validator import validate
from report_builder import build_summary

records = load_csv("input.csv")
errors = validate(records, SCHEMA)
report = build_summary(records, errors)
print(report)

Если интерфейсы соблюдены, этот код будет работать без изменений, даже если вы переписывали load_csv три раза разными промптами.

Где стратегия ломается: типичные ошибки дробления

  • Мелкая грануляция до абсурда. Когда каждый блок — 5 строк, вы утопаете в интерфейсах и тестах, а промпты становятся однотипными. Золотая середина — один модуль на одну нетривиальную функцию (10-50 строк кода).
  • Глобальное состояние. Если модули используют один глобальный объект (база данных, конфиг), они перестают быть изолированными. Решение — передавать зависимости явно (Dependency Injection).
  • Изменение интерфейсов по ходу. Решили, что load_csv должен возвращать не список словарей, а генератор? Вы обновили interfaces.py — и сломали все реализованные модули. Приходится переписывать. Поэтому интерфейсы меняйте редко и осознанно.
  • Игнорирование типов. В Python без типов ИИ может возвращать что угодно. Используйте mypy или Pydantic — модель станет аккуратнее.

Забавно, но в статье про провал ИИ в генерации кода описан ровно тот же антипаттерн: нейросеть спроектировала чип, не разбивая его на модули, и получила размером с участок. Дробление спасает от таких катастроф.

Когда дробление не нужно (и стоит собрать RAG)

Стратегия дробления хороша, когда проект пишется с нуля или эволюционирует под контролем человека. Но если у вас легаси-монолит на 500 КБ, который вы не хотите переписывать, и вы просто хотите, чтобы ИИ помог найти баг — тогда RAG с чанками по 500 строк и семантическим поиском будет быстрее. Дробление требует архитектурной дисциплины с самого начала. Если вы уже в аду из 30 файлов с циклическими импортами, сначала приведите проект в порядок, а потом уже дробите.

Также RAG незаменим, когда кода много (миллионы строк), и его нереально разбить на независимые блоки — они связаны неявно. Но для 95% стартапов и pet-проектов дробление — это дешёвая альтернатива, которая работает.

Неочевидный совет: дробление как тест архитектуры

Если вам кажется, что какой-то модуль сложно разбить на блоки для ИИ — пересмотрите его дизайн. Разбиение на промпт-френдли блоки заставляет вас думать о связности и ответственности. В каком-то смысле, вы не просто генерируете код — вы улучшаете архитектуру, даже если ИИ пишет всё за вас. А когда проект дорастёт до продакшена, у вас уже будет чистая архитектура с модульными тестами.

Попробуйте этот подход на следующем проекте. Начните с interfaces.py и тестов для одной функции. Увидите, как исчезнут галлюцинации, а вайб-кодинг превратится в предсказуемую сборку из кубиков Лего.

Подписаться на канал