Аккуратно даем LLM контекст проекта
- четверг, 15 мая 2025 г. в 00:00:07
Привет, на связи Лука.
Знаете, есть такая поговорка: "тише едешь – дальше будешь". Работая с LLM, я пришёл к выводу, что аккуратность и точность в подаче контекста – это один из самых важных ключиков к хорошему результату. Иначе получится как в другой поговорке - про дурака и стеклянный орган.
Чего греха таить – все мы пользуемся LLM в различных ситуациях. От генерации бойлерплейта до неожиданного, но изящного решения сложной логики. Ничего такого – очередной инструмент, которым можно, как молотком, забить гвоздь, а можно и... ну, вы поняли.
Но когда речь заходит о системе, в которой контекст содержится не в одном, не в двух, и даже не в десятке файлов – вопрос становится ребром. Просто скормить модели весь проект? Ну, можно, конечно. Модель, захлебнувшись в потоке зачастую ненужной информации, вряд ли выдаст что-то вменяемое. Она потратит драгоценные вычислительные ресурсы на анализ совершенно нерелевантных частей. Неэкологичненько.
А если как-то по-другому? Может, вручную отбирать файлы? Это мучительно долго, особенно если структура проекта ветвистая. К тому же, легко что-то упустить. Может, архивировать нужное и прикреплять архив? Тоже вариант, но не все LLM-интерфейсы это гладко переваривают, да и предварительная подготовка всё равно нужна.
Когда ты фулстек и горишь идеей быстренько поправить и контроллер, и репозиторий, и сервисный слой, и пару DTO, да ещё и на фронте композиты подправить под новый эндпоинт – вот тут-то и понимаешь, что существующие подходы к подаче контекста LLM напоминают попытку просунуть верблюда в игольное ушко.
Тогда у меня и родилась идея сборщика файлов, которая со временем, как снежный ком, начала обрастать новым функционалом.
Представляю вам projectson – утилиту на Go, которая собирает выбранные файлы вашего проекта в один аккуратный JSON-файл.
Да всё просто:
Компактность: JSON – это текстовый формат, но при должной обработке (о чём ниже) он может быть весьма лаконичным.
Удобство для LLM: Большинство современных LLM отлично понимают JSON. Его можно скормить модели как единый файл, что гораздо удобнее, чем вставлять километровые листинги кода в текстфилд, в результате чего весь интерфейс пыхтит и кряхтит в процессе рендера сего действа.
Структурированность: JSON сам по себе структурирован, что помогает модели лучше разделять и понимать отдельные фрагменты кода или текста.
Одна из главных фишек – это стремление максимально эффективно использовать ограниченное контекстное окно LLM. Для этого предусмотрены:
Опции для вырезки "мусора": Можно настроить правила для удаления из файлов определённых участков кода или текста (например, комментарии, ненужные в контексте текущего вопроса блоки).
Удаление лишних пробелов и переносов строк (по умолчанию): Автоматически сжимаем содержимое файлов, удаляя множественные пробелы и лишние переносы строк, заменяя их на одиночные пробелы. Это сокращает объём текста без потери семантики (для большинства языков программирования - привет, python). В будущем планируется добавить возможность более тонкой настройки этого процесса.
Таким образом, мы не просто собираем файлы, а предварительно причёсываем их, чтобы LLM получила только самое важное.
Сборка JSON-файла конфигурируется простым YAML файлом, который описывает, что и как собирать.
root
(Корневой путь):
Тип: String
Обязательно: Да
Описание: Абсолютный путь к корневой директории вашего проекта.
Пример:
root: "/path/to/your/project"
# На Windows:
# root: "C:\\Users\\YourName\\Projects\\MyProject"
include
(Что включаем):
Тип: List of Strings
Обязательно: Нет (По умолчанию сканируется вся root
директория, с учётом formats
и exclude_patterns
)
Описание: Список файлов или директорий для включения. Пути указываются относительно root
. Каждый элемент может определять путь и режим сбора:
"path/to/item"
: Собирает и путь, и содержимое (режим по умолчанию both
). Если item
– директория, сканируется рекурсивно.
"path/to/item:path"
: Собирает только путь.
"path/to/item:content"
: Собирает только содержимое.
"path/to/directory/*"
: Собирает файлы непосредственно внутри path/to/directory
(нерекурсивно), режим по умолчанию both
.
"path/to/directory/*:mode"
: Аналогично предыдущему, но с указанным режимом (path
или content
).
Пример:
include:
- "src" # Включить всё из src/, рекурсивно, путь и контент
- "README.md:content" # Только контент из README.md
- "assets/*:path" # Только пути файлов из assets/ (нерекурсивно)
- "docs/api.md" # Путь и контент docs/api.md
formats
(Форматы файлов):
Тип: List of Strings
Обязательно: Да
Описание: Список расширений файлов (без точки), которые нужно включать.
Пример:
formats:
- "go"
- "vue"
- "ts"
- "js"
- "py"
- "html"
output
(Выходной файл):
Тип: String
Обязательно: Да
Описание: Полный путь к выходному JSON-файлу.
Пример:
output: "project_data_output.json"
# output: "/tmp/my_project_collection.json"
exclude_patterns
(Паттерны исключения по имени):
Тип: List of Strings
Обязательно: Нет
Описание: Список паттернов для исключения файлов или директорий. Применяются после правил include
и formats
.
Glob-паттерны: Стандартные (например, node_modules
, .log
, dist/
). Метчатся по имени файла/директории или по относительному пути.
Регулярные выражения: Go-совместимые, должны быть заключены в слеши (например, /\.git/
, /private_.*\.key$/
).
Пример:
exclude_patterns:
- "node_modules"
- ".git"
- "*.min.js"
- "dist"
- "/test_data/" # Regex для исключения папок с именем test_data
- "/^\\.(svn|hg|DS_Store)/" # Regex для служебных файлов
content_exclusions
(Исключения содержимого из файлов):
Тип: List of Objects
Обязательно: Нет
Описание: Список правил для удаления определённых секций внутри файлов перед их добавлением в JSON. Это главное отличие от exclude_patterns
, которые работают с именами файлов/папок.
type
(String, Обязательно): delimiters
(по начальному и конечному тегам) или regexp
(по регулярному выражению).
file_pattern
(String, Обязательно): Glob-паттерн, на какие файлы (по расширению или имени файла, если оно уникально как расширение) применять правило. *
– для всех файлов, подходящих под formats
. Метчится с расширениями (vue
) или расширениями с точкой (.vue
).
start
(String, Опционально): Для type: "delimiters"
. Начальный тег.
end
(String, Опционально): Для type: "delimiters"
. Конечный тег.
pattern
(String, Опционально): Для type: "regexp"
. Регулярное выражение.
Пример:
content_exclusions:
- type: "delimiters"
file_pattern: "*.vue" # или просто "vue"
start: "<style>"
end: "</style>"
- type: "regexp"
file_pattern: "*.js" # или "js"
pattern: "(//.*)|(/\\*[\\s\\S]*?\\*/)" # Удалить JS комментарии
- type: "regexp"
file_pattern: "*" # Для всех типов файлов
pattern: "SECRET_API_KEY = '.*?'" # Удалить строку с секретным ключом
root: "/Users/username/goprojects/my-awesome-app"
include:
- "cmd"
- "internal/services"
- "internal/models:content" # Только контент моделей
- "pkg/*:path" # Только пути из pkg
- "README.md"
- "go.mod"
- "main.go"
formats:
- "go"
- "md"
- "mod"
output: "./collected_project_context.json"
exclude_patterns:
- "internal/services/legacy_service.go" # Исключить конкретный файл
- "/.*_test\\.go$/" # Исключить все тестовые файлы (regex)
- "vendor"
- "*.tmp"
content_exclusions:
- type: "delimiters"
file_pattern: "go" # Применить ко всем .go файлам
start: "// @exclude-start"
end: "// @exclude-end"
- type: "regexp"
file_pattern: "md" # Применить ко всем .md файлам
pattern: "[\\s\\S]*?"
Config: Здесь вы можете указать основные параметры: root
, formats
, output
, а также управлять списком include
(добавлять, удалять, изменять пути и режимы) и exclude_patterns
.
Exclusions: Эта вкладка посвящена правилам content_exclusions
. Вы можете добавлять новые правила, выбирать их тип (delimiters
или regexp
), указывать file_pattern
и соответствующие параметры (start
/end
или pattern
). Каждое правило наглядно представлено, и его легко отредактировать или удалить.
Config Docs: Чтобы не держать всю структуру YAML в голове, на этой вкладке вы найдете подробную документацию по всем полям конфигурационного файла. Очень удобно, когда нужно быстро освежить назначение параметра.
Preview: Прежде чем запускать полную сборку, вы можете посмотреть, какие именно файлы будут обработаны согласно текущей конфигурации. На этой вкладке отображается список файлов. Выбрав файл, можно увидеть его оригинальное содержимое и то, как оно будет выглядеть после применения правил content_exclusions
. Это помогает отладить правила исключений и убедиться, что в итоговый JSON попадет только нужное.
Run: Сборка JSON. Здесь вы видите краткую сводку текущей конфигурации и кнопку для запуска. После завершения можно сохранить результат.
Stats: После каждого запуска (или успешного Preview) здесь собирается статистика: количество обработанных файлов, общий размер выходных данных, время выполнения. Также отображается распределение файлов по форматам, режимам сбора и размерам (на основе данных из последнего Preview), и активные правила content_exclusions
.
Пилите YAML-конфиги и скармливайте результат нейросети. Сохраняйте и переиспользуйте свои конфиги.
Aistudio отлично читает такие JSON'ы. Копировать код пока приходится ручками - но есть задумки на получение от LLM ответа в виде JSON-патча.
Приглашаю вас на GitHub проекта. Предложения приветствуются.