Главная беда ИИ в 1С — он уверенно выдумывает. Метод библиотеки стандартных подсистем, которого нет. Параметр, которого не существует. Синтаксис, похожий на правильный. Код компилируется, а работает не так — и ты сидишь и проверяешь за ним каждую строку.
Я пишу с ассистентом почти всё: обработки, расширения, интеграции. И довольно быстро понял, что скорость упирается не в генерацию кода, а в проверку. Пока не доверяешь результату, выигрыша нет.
Решение оказалось не в промте, а в доступе к документации. Ниже — как я собрал локальный поиск по своей библиотеке документации 1С, подключил его к ассистенту и что из этого вышло. Код выкладываю, повторяется на любой ОС.
Почему ИИ выдумывает и почему промт это не лечит
Языковая модель не сверяется с документацией — она достраивает наиболее вероятное продолжение. Имя метода БСП, которого она не помнит точно, собирается по образцу: выглядит как настоящее, читается как настоящее, а в базе такого нет.
Просьбы «не выдумывай» работают плохо, потому что проблема не в мотивации, а в отсутствии источника. Модель физически не может отличить запомненное от достроенного. Значит, нужен не запрет, а источник, к которому она обязана обратиться.
Заземление вместо уговоров
Идея простая: у ассистента должен быть инструмент поиска по настоящей документации, и правило — искать перед тем, как писать код по незнакомой теме. Тогда в ответе появляется не «примерно так называется метод», а конкретный фрагмент документации с путём до файла, который можно открыть и проверить.
Технически это RAG: поиск нужных фрагментов и подстановка только их в запрос. Подключается к ассистенту по протоколу MCP — как отдельный локальный сервер, который объявляет свои инструменты.
Как это устроено
Цепочка получилась короткая:
- Корпус. Библиотека документации в markdown — папка с файлами, вложенность любая.
- Нарезка. Индексатор режет файлы по заголовкам на фрагменты примерно по 1600 символов с перехлёстом в 200. Заголовок сохраняется отдельным полем — по нему потом видно, откуда фрагмент.
- Эмбеддинги. Каждый фрагмент превращается в вектор из 768 чисел моделью
nomic-embed-textчерез Ollama. Локально, бесплатно, пачками по 16 фрагментов. - Хранение. SQLite плюс расширение
sqlite-vec: обычная таблица с текстом и виртуальная таблица с векторами. Никакого отдельного сервера баз данных. - Поиск. Запрос превращается в вектор той же моделью, дальше поиск ближайших соседей и выдача top-k фрагментов с путём, заголовком и расстоянием.
- MCP-сервер. Тонкая обёртка, которая объявляет три инструмента и общается с ассистентом по stdio.
Что видит ассистент:
search_its(query, k, section)— семантический поиск, top-k фрагментов;sectionограничивает поиск разделом корпуса;read_its(path)— читает файл целиком, когда фрагмента мало;rag_stats()— сколько файлов и фрагментов в индексе, какая модель эмбеддингов.
Самое важное здесь — не код, а то, что ассистент вызывает поиск сам. Правило «перед написанием кода по незнакомой теме сначала искать в документации» живёт в его инструкциях, а не в моей голове.
Цифры моего индекса
На момент написания статьи:
| Параметр | Значение |
|---|---|
| Файлов в корпусе | 6 777 |
| Фрагментов в индексе | 28 760 |
| Размер базы SQLite | 161 МБ |
| Размерность вектора | 768 |
| Время запроса | 0,06–0,16 с |
Стоимость эксплуатации — ноль: эмбеддинги считаются локально, платных API в цепочке нет. Интернет для поиска тоже не нужен, что отдельно приятно, когда работаешь в чужой сети через VPN.
Как собрать такой же у себя
Нужны Python 3.10+ и Ollama. Оба работают на Windows, macOS и Linux.
1. Модель эмбеддингов:
ollama pull nomic-embed-text
2. Окружение:
python -m venv .venv
.venv/bin/pip install httpx sqlite-vec mcp
3. Индексация корпуса:
.venv/bin/python indexer.py --docs-root /путь/к/корпусу --db-path ./data/index.db
Повторный запуск доиндексирует только новые файлы, так что пополнять корпус можно хоть по расписанию. Полная пересборка — флаг --rebuild.
Несколько независимых корпусов спокойно живут в одном индексе: второй добавляется с ключом --meta-key docs_root:имя_папки, и дальше первый сегмент пути определяет, из какого корня читать файл. У меня так уживаются документация разработчика и отдельный корпус по методике учёта.
4. Подключение к ассистенту. В Claude Code:
claude mcp add rag-1c --scope user \
-e OLLAMA_URL=http://127.0.0.1:11434 \
-e EMBED_MODEL=nomic-embed-text \
-e RAG_DB_PATH=/полный/путь/data/index.db \
-- /полный/путь/.venv/bin/python /полный/путь/server.py
Проверка — claude mcp list, должно быть rag-1c ✔ Connected. Cursor и другие клиенты с поддержкой MCP подключаются так же: команда запуска, транспорт stdio, те же переменные окружения.
Три грабли, которые стоили вечеров
Контекст модели эмбеддингов
У nomic-embed-text контекст 2048 токенов, а русский текст в токенах дороже английского: там, где английская фраза укладывается в 10 токенов, русская занимает 20 и больше. Если не ограничить фрагмент жёстко по символам, часть фрагментов не векторизуется — и, что хуже, молча. Индекс собирается, поиск работает, а куска документации в нём просто нет. Я ограничиваю фрагмент 2400 символами.
Фильтр по разделу — это постфильтр
Векторный индекс отдаёт ближайших соседей по всей базе, и только потом список фильтруется по префиксу пути. Пока разделы примерно равны, это незаметно. Но у меня один из разделов занимает около 5% индекса — и запросы по нему честно возвращали «ничего не найдено» при том, что нужная статья в корпусе лежала.
Лечится расширением окна поиска: берём вчетверо больше кандидатов, чем нужно, если попаданий в раздел мало — вдесятеро, потом ещё, пока не наберётся k результатов. Нашёл я это, когда готовил материал для этой статьи и решил проверить фильтр на узком разделе.
Пустой путь в Python — это точка
Корень корпуса берётся из переменной окружения, а если она не задана — из базы. Проверка выглядела разумно: взять путь и посмотреть, задан ли он. Беда в том, что Path("") в Python — это Path("."), а её строковое представление равно ".", то есть непустая строка. Проверка «путь задан» всегда проходила успешно, корень молча подменялся на рабочий каталог процесса, и чтение файлов не находило вообще ничего.
Поиск при этом работал: текст фрагментов лежит в самой базе, файл открывать не требуется. Поэтому дефект и прожил незамеченным — сломана была только вторая функция, к которой обращаются реже.
Мораль, применимая далеко за пределами RAG: проверяйте значение переменной окружения, а не результат его превращения в объект.
Про корпус и права
Здесь важное. Документация ИТС, книги и материалы методических сайтов — подписной контент правообладателей. Его нельзя раздавать, и я этого не делаю: выкладываю только код индексатора и сервера.
Корпус каждый собирает сам из того, к чему у него есть легальный доступ: собственная база знаний и конспекты, внутренние регламенты, документация по своей подписке. Формат простой — markdown-файлы в папке. Инструмент к содержимому равнодушен: он одинаково хорошо ищет по документации платформы, по вашим внутренним инструкциям и по накопленным решениям задач.
Скачать код
Индексатор и MCP-сервер, два файла плюс README с командами подключения к Claude Code и Cursor. Бесплатно, без регистрации — архив 10 КБ. В телеграм-боте лежит он же, а рядом OneBridge и промты для 1С.
Скачать архив, 10 КБ → Забрать в TelegramЧастые вопросы
Зачем RAG, если можно просто загрузить документацию в контекст?
На объёмах реальной библиотеки это невозможно и не нужно. В запрос уходит три-пять фрагментов вместо десятков мегабайт: дешевле, быстрее и точнее — модель не тонет в лишнем тексте.
Нужна ли для этого мощная машина?
Нет. Эмбеддинги считает модель на 768 измерений, она запускается на обычном рабочем компьютере. Тяжёлая операция здесь одна — первичная индексация корпуса, и она делается один раз.
Подойдёт ли это для чего-то кроме 1С?
Да, инструмент не знает, что внутри. Любая своя библиотека markdown-файлов — внутренние регламенты, документация продукта, накопленные решения — индексируется той же командой.
Что с обновлением корпуса?
Повторная индексация добирает только новые файлы, поэтому её можно ставить в расписание. Изменившиеся файлы пока требуют пересборки — это следующее, что доделаю.
Ещё по теме 1С и ИИ
Расширение 1С загрузилось, но не работает — 16 причин молчаливых поломок расширений и готовый промт, чтобы ассистент их не повторял.
Сделал HTTP-сервер прямо внутри 1С — OneBridge, через который ассистент читает метаданные и данные живой базы.
Если соберёте себе такой поиск — расскажите, на каком корпусе и что получилось. Интереснее всего чужие грабли: у меня три, но подозреваю, что это далеко не все.