Перейти к содержимому
Экосистема AI Vibe
← Назад к AI Vibe News

Редакция 17 августа 2026 г.

Разборы

Не отдавайте модели SQL: форма tools надёжнее длинного промпта

Не отдавайте модели SQL: форма tools надёжнее длинного промпта

Соло-разработчик, который собирает персональное приложение для данных о здоровье для одного аккаунта из белого списка, столкнулся с парадоксом: навык Claude Code удобен в терминале, но не открывается на iOS — а вопросы про сон и тренировки возникают «на кухне в шесть утра», не за столом. В эссе Matt Stratton он показывает, почему прямой SQL к «грязной» БД и длинный system prompt с перечислением ловушек — оба плохих интерфейса к LLM; надёжнее проектировать read-only tools так, чтобы ошибка была структурно невозможна.

Шесть ловушек, которых не видно в схеме

Автор ведёт личный проект на health data. В данных шесть семантических ловушек — каждая уже давала неверный ответ:

  1. Partial Day — «сегодня» ещё накапливает метрики; сравнение с завершённым днём выглядит как ложное ускорение.
  2. Gap ≠ zero — пропущенный день не равен нулевому потреблению.
  3. Дублирование тренировок Apple — Apple «тенево» копирует сессии Liftosaur; наивный запрос к workouts view примерно удваивает объём.
  4. energy_balance завышает дефицит ~2,7× — за 30 полных дней средний intake 1 602 kcal против 3 216 burned (net −1 614/день) предсказывает −3,2 lb/неделю, тогда как вес снижался на 1,2 lb/неделю.
  5. Одиночное взвешивание — шум — дневные колебания веса отражают воду, не тренд.
  6. reps: 0 — подход, который пытались выполнить и не смогли, а не пропущенный сет.

Дать модели SQL и read-only connection к маленькой БД дёшево — но она «наступает» на все шесть, потому что ловушки невидимы из схемы. Перечислить их в промпте проще; модель тогда обходит их в большинстве случаев. Автор называет этот исход хуже, чем стабильное попадание во все шесть через SQL.

Почему «правильно в 90% случаев» хуже стабильной ошибки

Контраст здесь не про точность ради точности — про доверие к агентному инструменту.

Инструмент, который всегда ошибается, ловят в первый день и выбрасывают. Инструмент, который прав в ~90+% случаев, начинают доверять — а редкая ошибка приходит в той же уверенной форме, что и верный ответ.

Пользователь не отличит редкий промах: он не знает ответа заранее. Для персональных данных это особенно коварно — модель звучит убедительно даже тогда, когда опирается на устаревшую строку в system prompt. Именно поэтому автор отвергает схему «SQL + схема + инструкции в промпте» как основной интерфейс к LLM.

Два режима: детерминированный coach и ask view с Claude

Вместо единого SQL-шлюза Stratton разделил продукт на два намеренно разных режима:

Режим Механизм LLM
coach view Чистые функции в lib/signals/, unit-тесты на фикстурах нет
ask view Claude server-side, 13 read-only tools да

Мотивация веб-приложения — перенести сценарий «спросить БД и рассудить над ответом» с терминального coaching skill на мобильный контекст. В coach-ветке сигналы — protein adherence, deficit vs scale, weight trend, overreaching, stalled lifts, data freshness; статус unknown отделён от ok.

В ask-ветке write tool отсутствует вовсе — не «отключён», а не существует. Изменения программы остаются в Liftosaur, макро-цели — в MacroFactor. Промпт по-прежнему описывает все шесть ловушек — модели легче понять, почему окно обрезано. Но если промпт удалить завтра, ответы станут хуже без ошибок в этих шести конкретных способах.

Как форма tools закрывает ловушки

Структурные ограничения вместо памяти модели:

  • оконные запросы заканчиваются AND observed_on < today_local() — Partial Day недостижим;
  • пропуски остаются отсутствующими строками, без zero-fill;
  • ни один tool не обращается к Apple workout view — дублирование недостижимо через API;
  • energy_balance нельзя получить без «reality check» в том же payload.

Отдельный урок — metric_catalog. Список метрик читал каталог: 38 из 81; 43 «uncatalogued» включали 3 865 дней walking/running distance. Фикс: drive from observations_daily с LEFT JOIN на каталог → catalogued: false вместо исчезновения метрики. Lookup table — не индекс реальности, если ничто не принуждает к синхронизации.

На границе tool layer автор округляет float до двух знаков — примерно треть меньше payload у get_nutrition за 7 дней. Это инженерия интерфейса агента, а не украшение промпта.

Когда промпт стареет быстрее, чем кажется

Эмерджентное поведение тоже проверяли: из 9 «намеренно коварных» probe-вопросов 3 ответа содержали рассуждения вне промпта — предупреждение про Apple-оценку VO2max, разделение T1/T2 без exposed tier hint, проверка coverage перед ответом про сон.

Но ответ про сон выдал «~7% coverage» — неверно: за 30 дней ~70% после смены ношения часов в июле. Источник — устаревшая строка в lib/coach/context.ts и system prompt, написанная месяцами ранее. Тест assert.match(prompt, /7% coverage/) удерживал ошибку. Фикс: убрать хардкод, полагаться на list_metrics; новый тест запрещает проценты coverage в промпте.

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

Что вынести соло-разработчику с LLM-агентом

Девять probe-вопросов обошлись в 37 центов суммарно; end-to-end latency — 6,9–16,8 с, TTFT 1,7–10,1 с. Кэшированный префикс — 7 069 токенов system prompt плюс 13 tool definitions; cacheWrite = 0 после первого turn — контрольная метрика стабильности префикса.

Практический вывод для того, кто один строит агентный слой поверх своих данных:

  • не отдавайте модели сырой SQL как единственный путь к «грязным» данным с невидимой семантикой;
  • не полагайтесь на длинный промпт как на страховку — он даёт ложное доверие;
  • проектируйте read-only tools так, чтобы типовая ошибка была недостижима, а промпт оставался объяснением, а не ограждением;
  • следите, чтобы unit-тесты не фиксировали устаревшие факты в system prompt.

Автор честно отмечает пробелы: ask UI ни разу не рендерился в браузере, путь unknown в сигналах не проверен на live data. Cross-domain корреляции (сон → пропуски лифтов, deficit → recovery) не shipped — событий в данных пока мало. Это зрелая позиция: агентный слой не обязан отвечать на всё, пока в данных нет основания.

Источники