
В прошлой части железо перестало быть проблемой. Индекс конфигурации считался один раз на машине с видеокартой, готовый слепок расходился по команде, и у всех наконец стало работать одинаково.
И ровно в этот момент выяснилось, что «работает» и «работает хорошо» — совсем не одно и то же. Ответы приходили быстро и выглядели уверенно. А нужную процедуру нейронка находила примерно в трёх случаях из четырёх — и по самому ответу понять, какой это случай, было нельзя.
Дальше пошли улучшения. Их было сильно больше трёх, но эти три я запомнил лучше остальных. Каждое выглядело очевидно правильным. Два сделали хуже, а единственное сработавшее поначалу обходилось так дорого, что его пришлось переделывать.
Сначала — чем я это меряю
Без этого дальше не будет смысла ни в одной цифре, поэтому сначала пара абзацев про измеритель.
Пока у нас не было измерителя, решения принимались на глаз: попробовали, «ну вроде получше отвечает», оставили. И вот это самое опасное, что может быть с нейронкой. Она отвечает складно всегда. Ответ по неверно найденной процедуре выглядит ровно так же уверенно, как ответ по верной: те же обороты, та же готовность помочь. Разницу видно, только если пойти и проверить руками, а руками каждый ответ никто проверять не будет.
Поэтому мы сделали стенд. Устроен он проще некуда: берётся набор вопросов, у каждого заранее известен правильный ответ, прогоняется поиск, считается доля попаданий.
Наш главный набор — 110 вопросов по коду и метаданным УТ 11.5. Вопросы обычные, человеческие, как их задают в чате: «какой механизм отвечает за запись движений при проведении», «как рассчитывается сумма в заказе клиента», «какой справочник хранит характеристики номенклатуры». Правильные ответы мы разметили вручную ещё до первого прогона: по каждому вопросу сами нашли в конфигурации нужную процедуру или объект и записали.
Кроме него есть ещё четыре набора поменьше — по другим сводам, в которых мы ищем: справка разработчика по платформе (40 вопросов), описание Библиотеки стандартных подсистем (БСП — набор готовых механизмов, который используется почти в каждой типовой конфигурации) по документации (46 вопросов) и она же по исходникам (42 вопроса), и наши стандарты разработки (40 вопросов).
Мера одна: доля вопросов, где правильный ответ попал в первую десятку выдачи. 0,864 значит, что из 110 вопросов правильный ответ оказался в первой десятке у 95.
Почему именно десятка, а не первое место. Потому что нейронке уходит не одна строка, а десять: она их читает и разбирается сама. Что в десятку не попало — для неё не существует вовсе. Так что первая десятка — это и есть та полка, ради которой всё затевается.
Саму десятку мы выбрали не на глаз. Прогоняли один и тот же набор, отдавая нейронке первые 5, 10, 15, 20 и 50 строк выдачи, и смотрели на две вещи: сколько правильных ответов до неё доезжает и сколько токенов уходит на запрос. Токен — это кусочек текста примерно в половину слова. Пятёрка теряла ответы, которые стояли чуть ниже. Всё, что сверх десятки, прибавляло находимости совсем немного, а токенов — ровно столько, сколько добавилось строк. Десять оказались самой выгодной точкой между этими двумя кривыми.
Мест на полке ровно десять, и это будет главной мыслью всей статьи.
И сразу оговорка про сами цифры. Само по себе число 0,864 не значит ничего: это доля попаданий на нашем наборе, по нашей конфигурации, в наших условиях. Смысл появляется только в сравнении «до и после» на одном и том же наборе. Поэтому дальше я везде называю обе цифры, а не одну.
Улучшение первое: «пусть нейронка пишет по нашим правилам»
С этим мы жили давно. У нас есть свои стандарты разработки, плюс мы активно пользуемся стандартами 1С — это большой свод правил, от именования до работы с транзакциями. И мысль напрашивалась сама: раз нейронка пишет код, пусть пишет его по нашим правилам, а не по своим представлениям.
Ребята собрали справочник стандартов и подключили его к нейронке. Не так быстро, как хотелось, но собрали. Результат улучшился сходу и заметно: код стал приходить в наших соглашениях, и на разборе кода пропала половина однотипных замечаний.
А потом мы посмотрели, во что это обходится.
Пятнадцать тысяч токенов дополнительного контекста в каждом запросе. Контекст — всё, что нейронка получает вместе с вопросом. То есть к каждому, даже самому мелкому вопросу пристёгивался весь свод правил целиком. Спрашиваешь про одну процедуру — везёшь с собой книжку.
Плохо это сразу с двух сторон. Первая понятная: за контекст платят, и платят каждый раз. Вторая менее очевидная и более неприятная: чем больше нейронке дано, тем хуже она держит в голове главное. Правила именования переменных начинают конкурировать за её внимание с самим вопросом.
Думали, экспериментировали, переделали. Пришли к варианту, при котором постоянно висит только оглавление свода и короткие инструкции — куда идти за подробностями. А сами правила подгружаются по требованию: понадобилось правило про транзакции — оно приезжает, не понадобилось — не едет. Экономия составила девяносто восемь процентов от исходных пятнадцати тысяч при том же качестве.
Это единственное из трёх улучшений, которое у нас выжило. И вывод из него я бы сформулировал так: улучшение, за которое платят в каждом запросе, — это не улучшение, а рассрочка. Оно работает ровно до того момента, когда переписка в чате разрастётся: весь свод едет заново с каждым сообщением, и чем длиннее разговор, тем дороже он обходится и тем меньше в контексте остаётся места для самого дела.
Улучшение второе: «пусть нейронка видит сам код»
Это тот случай, когда идея выглядела не просто правильной, а очевидной.
В типовых конфигурациях почти все процедуры и функции имеют описание — тот самый комментарий перед объявлением, где сказано, что процедура делает и что принимает. Для поиска по смыслу это золото: описание написано человеческими словами, ровно теми, которыми потом задают вопрос.
А вот когда типовую дорабатывают на проекте под конкретного заказчика, этот стандарт почти не соблюдают. Как правило, никто ничего не описывает — и это не в упрёк ребятам, это общая беда. В результате самая интересная часть конфигурации, та, ради которой всё и затевалось, оказывается для поиска почти немой.
И мы подумали: раз описаний нет — добавим в индекс сам код процедур. Пусть нейронка видит, что там внутри, и понимает назначение процедуры по её содержимому.
Контекста добавили. Результат получили противоположный.
Поиск по смыслу стал шумным. Находимость на нашем наборе упала разом на двадцать процентов — то есть каждый пятый вопрос, на который раньше находился правильный ответ, перестал находиться.
Причина, если разобраться, простая: весь код в конфигурации собран из одних и тех же конструкций. Отпечаток смысла у куска кода получается не про то, зачем эта процедура нужна, а про то, как она написана: цикл, запрос, обход результата, запись. Таких кусков в конфигурации десятки тысяч, они похожи друг на друга как близнецы — и все дружно лезут в выдачу на любой вопрос, где есть слова про запись или про обход. Описание отличало процедуру от соседки, а код — уравнял.
Как ни крутили, как ни пробовали резать код на части и взвешивать его пониже описаний — от идеи пришлось отказаться.
Улучшение третье: «пусть отдельная модель переспросит по-нашему»
Здесь мы шли по проторённой дорожке, знакомой многим разработчикам ИИ-решений.
У тех, кто строит поиск для нейронок, есть известный приём: прежде чем искать, дать вопрос пользователя отдельной модели, чтобы та переписала его в более понятный для поиска вид. Смысл понятен любому, кто работал с 1С: пользователь спрашивает «где считается себестоимость», а в конфигурации это называется совсем другими словами, и между вопросом и ответом лежит пропасть в терминологии.
Подключили модель, которая переводила вопрос в официальные термины платформы и делала до трёх формулировок на каждый вопрос. На 110 вопросов получилось 329 формулировок. Взяли для этого DeepSeek V4 Pro. В качестве самих переформулировок сомневаться не приходилось: они были грамотные и по делу.
К тому моменту сборка уверенно давала примерно 90 правильных ответов из 110. Прогнали.
Качество упало с 0,864 до 0,782. Спасено семь вопросов, сломано шестнадцать. Время прогона выросло с 88 секунд до 302.
Что произошло. Поиск и до переписывания находил правильный ответ для девяти вопросов из десяти. Улучшать там было почти нечего — а ломать было что. Каждая новая формулировка честно шла в поиск и приводила своих кандидатов: уверенных, правдоподобных, по делу. Их не десять, их втрое больше. А мест на полке — десять. И каждый новый уверенный кандидат кого-то оттуда выталкивает. Чаще всего — тот самый правильный ответ, который стоял последним, на десятой строчке, и держался там еле-еле.
Семь вопросов переписывание спасло. Это правда, и я эти семь видел: там формулировка действительно попала в терминологию, которой в исходном вопросе не было. Но шестнадцать оно сломало, и почти все шестнадцать — ровно тем механизмом, что описан абзацем выше.
Пытались докрутить: резать число формулировок, взвешивать исходный вопрос выше переписанных, пускать переписывание только для коротких вопросов. Без толку — где-то помогало, где-то ломало сильнее. Собрались с ребятами, обсудили, погрустили и всю работу выкинули.

Три улучшения, каждое из которых выглядело очевидно правильным. Первое работало, но стоило пятнадцать тысяч токенов в каждом запросе — переделали на подгрузку по требованию и вернули девяносто восемь процентов контекста. Второе уронило находимость на двадцать процентов. Третье уронило качество с 0,864 до 0,782 и растянуло прогон с 88 секунд до 302.
Что общего у всех трёх
Первое, что бросается в глаза, когда смотришь на них подряд: все три добавляли правду.
Стандарты разработки — правда, они у нас действительно есть и им действительно нужно следовать. Код процедуры — правда, причём самая точная из возможных: это буквально то, что там написано. Официальные термины платформы — тоже правда, вопрос в них и вправду звучит грамотнее.
Ни одно из трёх улучшений не подсовывало поиску ложь. И два из трёх всё равно сделали хуже.
Здесь я долго путал две разные вещи, и разделить их стоило дорого. Одно дело — правда ли то, что вы добавили. Совсем другое — умеет ли эта правда встать на своё место в очереди. Описание процедуры — правда, которая встаёт отлично: оно написано теми же словами, какими человек задаёт вопрос. Код той же самой процедуры — правда, которая вставать в очередь не умеет вовсе: он похож на любой другой код и поэтому лезет в ответ на что угодно. Так что польза для поиска — это не «правда или нет», а «попадёт ли эта правда на своё место и кого она по дороге столкнёт».
Отсюда закон, который я для себя вывел и с тех пор проверяю на каждом новом улучшении:
Место в первой десятке конечно. Каждый новый уверенный кандидат кого-то оттуда выталкивает — и выталкивает чаще всего того, кто стоял на десятом месте. То есть ровно того, ради кого вы всё это затевали.
Из этого следует неприятное. Чем лучше у вас уже работает поиск, тем опаснее любое добавление: у хорошего поиска правильные ответы стоят высоко, но не все — часть держится на последних строчках десятки, и именно они уходят первыми.
Второе наблюдение — про плату, которая не в качестве. Прогон вырос с 88 секунд до 302. Даже если бы качество не упало ни на сотую, это уже совсем другая вещь: одно дело ответ, который приходит, пока ты дочитываешь свой же вопрос, и другое — пять минут ожидания. Про время начинаешь думать сильно позже, чем про качество, а замечают его пользователи сильно раньше.
И третье, главное. Без замера все три улучшения выглядели работающими. Мы смотрели на ответы после каждого из них, и ответы были нормальные. Складные, уверенные, по делу. Ухудшение находимости на двадцать процентов на глаз не видно вообще никак — просто иногда нейронка отвечает мимо вопроса, а вы думаете, что сами неудачно спросили.
Поэтому единственный вопрос, который я теперь задаю первым, когда мне рассказывают про удачное улучшение поиска: а чем вы померили, что стало лучше? Если ответ «да видно же» — значит, не померили.

Почему добавление правды ухудшает поиск. Слева — как было: правильный ответ держится на десятой, последней строчке. Справа — как стало: формулировки вопроса привели четырёх уверенных кандидатов, те встали выше, все сдвинулись вниз на четыре места, и правильный ответ уехал на четырнадцатое — за край полки. Из индекса он никуда не делся — он просто больше не доезжает до нейронки.
Чем такое меряют
Единственно правильного способа тут нет, и наш точно не единственный. Что вообще бывает.
Смотреть глазами. Задал десяток вопросов, почитал ответы, сделал вывод. Это бесплатно и делается за полчаса — и до какого-то момента этого честно хватает: грубые поломки видны сразу. Ломается способ ровно там, где сломались мы: на изменениях, которые ухудшают то, во что вы в этот раз не заглянули.
Свой набор вопросов с заранее записанными правильными ответами. То, к чему пришли мы. Дорого один раз — вопросы надо придумать и честно разметить, у нас это несколько сотен вопросов по пяти сводам. Дальше дёшево: прогон занимает полторы минуты, и его можно делать после каждой правки.
Готовые открытые наборы для оценки поиска. Они существуют, их много, они бесплатны: например, BEIR — почти два десятка наборов по разным областям знаний, или CodeSearchNet — вопросы к коду на шести языках программирования. Беда одна: они про чужие данные, встроенного языка 1С там нет. Про то, как ваш поиск справляется с вашей конфигурацией, они не говорят ничего.
Сгенерировать вопросы моделью по своим же текстам. Быстрый способ набрать объём: нейронка читает описание процедуры и придумывает вопрос, ответом на который эта процедура является. Подводный камень серьёзный: вопрос и правильный ответ рождаются из одного абзаца, поэтому набор получается слишком лёгким — поиск на нём выглядит лучше, чем он есть.
Смотреть на живых пользователях. Что переспрашивали, где бросали, после какого ответа пошли делать руками. Самое честное мерило из всех, потому что оно про пользу, а не про попадание в десятку. Но нужен поток обращений и терпение: изменение видно не на следующий день.
Что выбрать — зависит от того, есть ли у вас этот поток и сколько вы готовы вложить в разметку один раз. Мы начинали с первого способа, а ко второму пришли ровно потому, что первый нас подвёл — дважды подряд, и оба раза описаны выше.
Сколько это заняло
Справочник стандартов ребята собирали не так быстро, как хотелось. Переделка на подгрузку по требованию — отдельная работа поверх, и она заняла больше, чем сам справочник: там пришлось придумывать, как резать свод на куски, которые имеют смысл поодиночке.
Код процедур в индексе — несколько заходов, все впустую. Из них не вышло ничего, кроме понимания, почему не вышло; но это понимание потом сэкономило нам не одну попытку.
Переписывание вопроса — подключение, прогоны, попытки докрутить и полный отказ. Точных сроков не назову — я их не считал, но это точно не одна неделя.
Сам прогон измерителя — 88 секунд на 110 вопросов. С переписыванием было 302 секунды. Обе цифры не по памяти: они записаны в отчётах прогонов.
А если бы я мог отмотать назад, я изменил бы ровно одно: завёл бы измеритель раньше, чем взялся улучшать. Первые улучшения принимались на глаз, и я до сих пор не знаю, сколько среди них было таких же, как второе и третье. Проверить это задним числом уже нельзя: сборка с тех пор изменилась целиком.
Чего я не измерял
Переписывание вопроса на других сводах. Мы гоняли его на наборе по коду конфигурации. Вполне может быть, что на справке платформы, где вопросы формулируются ближе к официальным терминам, оно ведёт себя иначе — а может, и лучше.
Другие модели для переписывания. Пробовали одну. Возможно, дело было в ней, а не в самой технике, — но проверять это второй моделью после такого результата мы не стали.
Что было бы, если бы описания у процедур были. Второе улучшение мы затевали ровно потому, что в доработках типовой на проекте описаний нет. Как повёл бы себя поиск, если бы разработчики их писали, я не знаю — подозреваю, что тогда и добавлять код не понадобилось бы.
Экономию в деньгах. Девяносто восемь процентов контекста — это доля, а не рубли. Сколько именно мы на этом сберегли, я не считал: цифра зависит от того, сколько запросов и по какой модели, а это у нас менялось быстрее, чем я успевал считать.
Чужие конфигурации. Все цифры — по одной конфигурации, УТ 11.5, и по нашим сводам. На вашей конфигурации и вопросы будут другие, и доли другие. Переносить мои числа на свою базу я бы не стал; переносить выводы — стоит.
Что было дальше
Из этих трёх работ одну мы выкинули целиком. А через некоторое время та же самая мысль — что разговорный вопрос надо переводить в официальные слова — вернулась к нам в совершенно другом виде, куда более скромном, и на этот раз сработала.
Но до этого был месяц, в течение которого меня мучил один вопрос. Простой и короткий: какой общий механизм отвечает за запись движений документа по регистрам при проведении. Правильный ответ я знал. Поиск его не давал — ни на первом месте, ни на десятом, ни на пятидесятом.
Разбираясь, почему так, мы наткнулись на вещь, которая дала лучший прирост за всё время и которой я до сих пор радуюсь больше всего остального.
Но об этом — в следующий раз.
Если вы попали сюда с середины: первая часть — про то, почему нейронка выдумывает несуществующие процедуры, вторая — про день, когда всё получилось с первого захода, а третья — про то, почему тот же приём не завёлся у команды.
Вопрос к вам: у кого есть свой набор вопросов для проверки поиска — сколько их у вас и как вы записывали правильные ответы?
Вступайте в нашу телеграмм-группу Инфостарт