Индустрия строит костыли (обёртки, цепочки, фреймворки), чтобы компенсировать фундаментальную особенность LLM — она генерирует статистически среднее, а не логически правильное.
Прописывать примеры хорошего и плохого кода в промптах — эффективный метод.
1. Почему LLM стремится к «усреднённому коду»
LLM — это не инженер (внезапно), а архив частот. Она видела миллиарды примеров кода. На вопрос «напиши функцию для FTP» она выдаёт то, что чаще всего встречала: стандартное имя функции, стандартную обработку ошибок, стандартное логирование.
Проблема: «Стандартное» ≠ «то, что нужно». У меня проверенная специфическая архитектура (NS_Container, Railway-декораторы, режимы локальных файлов).
LLM не может «догадаться» об этом, потому что в её обучающей выборке такого мало.
2. Что работает: примеры кода в промпте (few-shot)
Вместо того чтобы описывать правила (делай то, не делай это), я даю конкретные примеры. LLM подражает им, а не своей усреднённой статистике.
Плохой промпт (zero-shot): «Напиши функцию FTP_Data_Get, которая читает данные из FTP или локальных файлов.» LLM напишет «средний» код, который, скорее всего, не будет использовать NS_Container, не будет логировать, не будет проверять режимы.
Хороший промпт (few-shot с примерами):
text
Напиши функцию FTP_Data_Get для NS_Container. Вот пример хорошего кода в нашем проекте:
✅ ХОРОШИЙ ПРИМЕР (функция B24_Data_Get):
function B24_Data_Get(NS_Container &$ns): void {
_log($ns, __FUNCTION__, 'start');
if ($ns->s_DataGet_From !== 'Локальные файлы') {
$ns->B24_Data_Get = fetchFromB24($ns->config);
}
_log($ns, __FUNCTION__, 'end', count($ns->B24_Data_Get));
}
❌ ПЛОХОЙ ПРИМЕР (чего НЕ делать):
function FTP_Data_Get($config) {
// Нет контейнера NS_Container
// Нет логирования
// Нет проверки режима
return ftp_get(...);
}
Теперь напиши FTP_Data_Get по аналогии с хорошим примером. Обязательно используй:
- NS_Container как аргумент
- _log() для логирования
- проверку $ns->s_DataGet_From
- соответствующий источник данных
Результат: LLM скопирует паттерн хорошего примера и подставит свою логику.
3. Почему это лучше, чем обёртки и фреймворки
Обёртки (LangChain, AutoGPT) пытаются управлять LLM через цепочки вызовов. Проблема в том, что они добавляют сложность и оверхед, а LLM внутри всё равно генерирует усреднённый код. Я не решаю проблему, я строю вокруг неё забор, кривой.
Дообучение (fine-tuning) учит LLM на моих примерах. Это дорого, требует много данных, а модель быстро устаревает. Каждое обновление базовой модели — и я плачу снова.
Few-shot с примерами просто кладёт примеры в промпт. Работает сразу, ничего не стоит, легко менять. Я не борюсь со статистикой LLM — я подменяю её своей статистикой.
Few-shot — это самый простой и эффективный способ заставить LLM писать мой код, а не усреднённый.
4. Как строить примеры для проекта
Часть 1. Определить хороший паттерн
Беру одну идеальную функцию из проекта, например B24_Data_Get. Очищаю её от специфики, оставляя только структуру:
php
function ИмяФункции(NS_Container &$ns): void {
_log($ns, __FUNCTION__, 'start');
if ($ns->s_DataGet_From !== 'Локальные файлы') {
$ns->ИмяФункции = реальная_функция($ns->config);
}
_log($ns, __FUNCTION__, 'end', count($ns->ИмяРезультата));
}
Часть 2. Определить плохой паттерн (чего не делать)
Показываю LLM, какой код она не должна генерировать:
php
// ❌ ПЛОХО: нет NS_Container, нет логирования, нет проверки режима
function FTP_Data_Get($ftp_server, $username, $password) {
return ftp_get(...);
}
Часть 3. Собрать промпт для любой новой функции
Шаблон, который работает для всех функций проекта. Подставляю название функции, описание задачи и имя свойства в контейнере:
text
Напиши функцию {ИмяФункции} для NS_Container.
Следуй ХОРОШЕМУ ПРИМЕРУ (аналогично B24_Data_Get):
[вставить хороший пример]
НЕ делай как в ПЛОХОМ ПРИМЕРЕ:
[вставить плохой пример]
Твоя задача: реализовать {ИмяФункции}, которая:
- {что делает}
- Кладёт результат в $ns->{ИмяРезультата}
- Использует $ns->config для доступа к настройкам
5. Как добавить рекурсивное улучшение
Можно скомбинировать few-shot с самоулучшением. Добавляю в конец промпта чек-лист и прошу LLM проверить себя:
text
Ты — PHP-разработчик. Ты не любишь ООП, ты любишь функции и полный контекст.
Вот ХОРОШИЙ ПРИМЕР кода в нашем проекте: [пример]
Вот ПЛОХОЙ ПРИМЕР (так не делать): [антипример]
Теперь напиши функцию FTP_Data_Get.
После того как напишешь, проверь себя по чек-листу:
Первый пункт: функция принимает (NS_Container &$ns)?
Второй пункт: использует _log() в начале и конце?
Третий пункт: проверяет $ns->s_DataGet_From?
Четвёртый пункт: кладёт результат в $ns->FTP_Data_Get?
Пятый пункт: нет ли в коде чего-то из плохого примера?
Если хоть один пункт не выполнен — исправь. Покажи только итоговый код.
LLM сама сделает итерации, сверяясь с примерами. Внешние обёртки не нужны.
Итог: вернулся к коду.
Когда пишу длинные художественные правила — «сделай так, потом так, учти это, не забудь то» — описываю архитектуру словами. Борюсь с математикой.
Чтобы не бороться, а управлять, даю пример нужного кода. Использую few-shot в промпте. Обращаюсь не к статистике LLM, а показываю нужное.
Нужный мне код в промпте заменяет архитектора, кодера, тестировщика и рефактора. LLM не нужно объяснять — ей нужно показать.
Индустрия строит сложные обёртки, потому что борьба со сложностью хорошо оплачивается.
Я жадный.