Skip to content

fix: explain_snippet падает на сниппетах длиннее ~450 символов - #31

Open
andy24kr wants to merge 1 commit into
zeegin:mainfrom
andy24kr:fix/explain-snippet-query-limit
Open

fix: explain_snippet падает на сниппетах длиннее ~450 символов#31
andy24kr wants to merge 1 commit into
zeegin:mainfrom
andy24kr:fix/explain-snippet-query-limit

Conversation

@andy24kr

Copy link
Copy Markdown

Проблема

v8std_explain_snippet заявляет лимит сниппета MAX_SNIPPET_CHARS = 4000, но на практике падает на любом сниппете длиннее примерно 450 символов:

Error executing tool v8std_explain_snippet: query is too long: max 500 characters

Под это попадает почти любая реальная процедура 1С — то есть основной сценарий использования инструмента.

Воспроизведение

Любой валидный BSL-фрагмент на 500–4000 символов, например обычный обработчик ПриЗаписи с Попытка/Исключение и ЗаписьЖурналаРегистрации. На 819 и 981 символе воспроизводится стабильно.

Причина

В explain_snippet из сниппета собирается signal_text (токены + выделенные сигналы), который затем уходит в self.search(...). Внутри search работает require_text(query, "query", MAX_QUERY_CHARS) с лимитом 500 — сборка сигналов легко его превышает, хотя сам сниппет укладывается в заявленные 4000.

Показательно, что в page() эта же ситуация уже обработана явной проверкой длины перед вызовом search():

if len(id_or_alias_or_url) <= MAX_QUERY_CHARS:
    candidates = self.search(id_or_alias_or_url, limit=5)["results"]

В explain_snippet аналогичной защиты не было — судя по всему, просто недосмотр, а не осознанное поведение.

Решение

Добавлена truncate_for_query(): усекает построенный поисковый текст до MAX_QUERY_CHARS по границе слова (без разрыва посередине токена).

На релевантность это не влияет: signal_text собирается в порядке значимости — сначала выделенные сигналы, поэтому усечение отбрасывает наименее значимый хвост. Проверил на реальных сниппетах — результаты diagnostics/standards остаются осмысленными.

Альтернативы, которые рассматривал и отклонил:

  • поднять MAX_QUERY_CHARS — затронет валидацию публичного search, где лимит 500 разумен;
  • отбивать длинный сниппет ошибкой — противоречит заявленному лимиту 4000.

Тесты

Добавлен регрессионный тест test_explain_snippet_accepts_long_snippet на сниппет длиной 500–4000 символов. Весь файл tests/test_v8std_mcp_index.py — 22/22 зелёные.

Вопрос по контрибьютингу

В репозитории нет CONTRIBUTING, а лицензия кода скриптов мне не до конца ясна: корневой LICENSE — Creative Commons (очевидно, про контент стандартов), в LICENSES/ лежат EPL-2.0 / GPL-3.0 / LGPL-3.0. Если для кода действуют отдельные правила или нужен другой формат вклада — подскажите, поправлю.

Отдельная мелочь, которую не стал тащить в этот PR: минимальная версия Python нигде не заявлена. Локальный pytest на 3.9/3.10 падает невнятным ModuleNotFoundError: No module named 'tomllib', потому что CI гоняет тесты внутри python:3.12-slim. Строчка в README сэкономила бы время новым контрибьюторам — могу прислать отдельно, если интересно.

explain_snippet заявляет лимит MAX_SNIPPET_CHARS (4000), но собранный из
сниппета signal_text уходил в search() без усечения и отбивался проверкой
MAX_QUERY_CHARS (500). В результате инструмент падал с "query is too long:
max 500 characters" на любом реальном сниппете длиннее ~450 символов —
то есть почти на любой процедуре 1С.

В page() эта ситуация уже обработана явной проверкой длины перед вызовом
search() (строка 533), в explain_snippet аналогичной защиты не было.

Добавлена truncate_for_query(): усекает текст до MAX_QUERY_CHARS по границе
слова. На релевантность не влияет — signal_text отсортирован по значимости,
первыми идут выделенные сигналы.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant