С чего все началось
KOMPAS Guard — это набор инструментов, который бьет AI-агента по рукам, когда тот пытается выдумать несуществующий код для КОМПАС-3D.
В первой версии мы решили главную проблему: заставили агента опираться на факты. Существование методов доказывал граф типов, сигнатуры сверял компилятор C#, а отдельный процесс запускал код в реальном CAD. После этого соврать агент уже физически не мог.
Зато всплыла другая беда: разрыв между человеческим языком и документацией. Граф типов знает все про IPart7.DefaultObject, но ничего не смыслит в команде «построй эскиз на базовой плоскости». Искать по 19 тысячам страниц сухой документации оказалось неэффективно, агенту приходилось угадывать имена методов.
Индустриальный стандарт сегодня — воткнуть в пайплайн еще одну тяжелую эмбеддинг модель + реранкер для перевода с русского на API-шный. Мы вместо этого дообучили крошечный двуязычный энкодер на 34 млн параметров с единственной задачей: взять человеческую формулировку и выдать подходящие элементы КОМПАС API. А достоверность найденного по-прежнему доказывает железо: компилятор и живой инстанс.
Все цифры и листинги ниже взяты из реальных прогонов в продакшене пакета kompas-3d-guard 0.6.2.
Как мы учили модель понимать запросы агента
За основу взяли USER2-small. Каждый элемент API превратили в отдельный документ: владелец, тип операции (чтение, запись или метод), сигнатура, краткое описание и известный путь получения объекта.
Для обучения нужны пары «запрос и правильный ответ». Проще всего нарезать их прямо из документации, но такие пары учат модель искать по названию метода, тогда как агент обычно описывает задачу, и имя нужного символа в его запросе вообще не звучит. Модель, обученная на документации, видела только «метод Save интерфейса IKompasDocument» и на запрос «сохранить деталь» отвечать не научится.
Поэтому большую часть формулировок мы синтезировали внешними языковыми моделями на русском и английском, воспроизводя реальные сценарии обращения к инструменту. Генерировали запросы четырех типов: естественные инженерные задачи, короткие вопросы, запросы в контексте работы агента и технические, ориентированные на API. В отдельной волне генерации моделям запрещалось называть искомый метод по имени — разрешалось лишь описывать, что нужно сделать, потому что ровно в таком виде приходит запрос агента, который еще не знает нужного символа.
Оговорюсь отдельно: генеративная модель данные не размечала. Правильный ответ для каждой пары был известен заранее и назначался кодом. Сгенерированные формулировки проходили проверку структуры, затем из них удалялись дубликаты и слишком похожие пересказы. Удачные результаты сохранялись сразу, поэтому исчерпанная квота провайдера не заставляла начинать заново.
Отдельная работа ушла на «неправильные ответы» для обучения. Случайных примеров недостаточно, если нужно научить модель различать похожее. Мы специально подбирали коварные случаи: одноименные методы разных интерфейсов, соседние пары «прочитать/записать» одного свойства, похожие операции одного владельца и кандидатов, которых исходная модель реально ставила выше правильного ответа. При этом действовало правило честности: если альтернативный вариант тоже мог быть правильным, мы не объявляли его ошибкой. Такой пример либо исключался, либо получал несколько правильных ответов.
Итоговое обучение составило больше 200 тысяч пар, по два сложных негатива на пример, контрастивный режим. На одной видеокарте все заняло меньше пяти часов. Раздувать модель мы не стали: размерность векторов осталась прежней, 384.

Честные цифры: три независимых прогона
Одной красивой метрикой легко себя обмануть, поэтому финальную систему мы прогнали на трех наборах: dev (регрессионный, ловит деградации между версиями), manual (задачи, написанные вручную, без оглядки на стиль генератора) и held-out (документы, которых не было в обучающей выборке).
Кратко о метриках. Hit@1 показывает долю запросов, где правильный ответ оказался первым в выдаче. Hit@5 и Hit@10 фиксируют попадание в топ-5 и топ-10. MRR@10 учитывает позицию правильного ответа.
Дополнительно мы измерили небольшой переранжировщик. Он переставляет местами только первые три результата и на полноту поиска не влияет. Ниже приведено сравнение базового энкодера до обучения (zero-shot) и нашей итоговой модели.
|
Benchmark |
Модель |
Запросов |
Hit@1 |
Hit@5 |
Hit@10 |
MRR@10 |
+ Top-3: Hit@1 |
+ Top-3: MRR@10 |
|---|---|---|---|---|---|---|---|---|
|
Regression/dev |
Исходная (zero-shot) |
77 |
0,079 |
0,171 |
0,197 |
0,114 |
— |
— |
|
|
Наша V1 |
77 |
0,468 |
0,727 |
0,779 |
0,566 |
0,481 |
0,573 |
|
Manual |
Исходная (zero-shot) |
49 |
0,019 |
0,058 |
0,096 |
0,036 |
— |
— |
|
|
Наша V1 |
49 |
0,592 |
0,796 |
0,796 |
0,685 |
0,653 |
0,713 |
|
Held-out |
Исходная (zero-shot) |
91 |
0,470 |
0,650 |
0,700 |
0,547 |
— |
— |
|
|
Наша V1 |
91 |
0,901 |
0,967 |
0,989 |
0,932 |
0,901 |
0,932 |

Вопросы из бенчмарков хранятся на гугл диске: https://drive.google.com/file/d/11eoi2cTO3bEoAf1ZIVGzRjDQdAJAiSxR/view?usp=sharing
Чистый эффект дообучения ярче всего проявляется на ряде manual. Эти запросы составлялись без подстройки под систему, в естественных формулировках (например, «длина отрезка прямой» или «масштаб вида»). Базовая модель до файнтьюнинга не понимает специфику КОМПАСа и в 90% случаев вообще не находит нужный метод. Наша V1 поднимает правильный ответ в топ-5 почти в 80% случаев.
Агенту этого хватает: он получает несколько кандидатов, проверяет их через граф типов и выбирает подтвержденного. Так устроен весь рабочий цикл, поэтому смотреть на одну лишь первую позицию (Hit@1) смысла мало. Held-out выглядит впечатляюще, но синтетические формулировки закономерно проще человеческих, и обольщаться этими цифрами не стоит.
Слабые места мы тоже знаем. Элементы дерева построения модель путает между собой, а у метода Save слишком много владельцев, чтобы уверенно выбрать ровно нужного с первой позиции. После V1 мы обучили V2 и V3. Вторая версия значимого выигрыша не дала, третья ухудшила основные метрики. В финальной версии осталась V1.
Как агент работает с поиском
SDK принимает запросы пачкой, чтобы модель запускалась один раз. Агенту это выгодно вдвойне: меньше вызовов инструмента и меньше токенов на прочтение результата.
batch = guard.batch([ "Получить радиус окружности на чертеже", "Длина отрезка прямой", "Какой масштаб у вида?", "Сохранить документ детали на диск",], k=1)print(batch.render("compact"))
Реальный вывод нарочно сжат. Агент работает с короткими ссылками вроде Q3.R1 и запрашивает полные сведения только для выбранного кандидата через inspect():
Sceaf0075|k=1|model=kompas-guard-sdk-core-v2Q1|Получить радиус окружности на чертежеQ1.R1|30cb63c0ea|0.770|ICircle.Radius|get|ICircle.Radius() -> double path|partial:ICircle>RadiusQ3|Какой масштаб у вида?Q3.R1|08e80aced8|0.836|IView.Scale|get|IView.Scale() -> double path|IApplication>ActiveDocument>IKompasDocument>cast(_, 'IKompasDocument2D')>...
Когда поиска недостаточно: константы и рецепты
Семантический поиск промахивается, и мы это не прячем. Запрос «получить базовую плоскость XOY» в реальном прогоне поднял методы проекции точки выше правильного DefaultObject. Поэтому сложную задачу агент начинает сразу с запроса контекста:
ctx = guard.context( "создать деталь, эскиз на XOY, окружность, выдавливание и сохранить", k=3,)
Помимо найденных кандидатов реальный ответ содержит готовые рецепты и точные значения констант:
RECIPE|api7-solid-workflow-v1,ensure_3d_doc_top_part,save_active_documentENUM|DocumentTypeEnum|source=Interop.Kompas6Constants.dll|values=1 value|834ceac0ae|ksDocumentPart|4ENUM|ksObj3dTypeEnum|source=Interop.Kompas6Constants3D.dll|values=1 value|ff305b6946|o3d_planeXOY|1|XOY plane
Даже если поиск промахнулся, константы приходят напрямую из настоящих библиотек КОМПАСа, так что в код попадает проверенное значение вместо «магического числа», которое языковая модель помнит с обучения.
Приведение типов: только доказанные касты
В COM-мире один объект часто скрывает за собой несколько интерфейсов, и библиотека типов не всегда честно это описывает. Классическое место, где агент выдумывает несуществующие преобразования. Guard возвращает путь между интерфейсами вместе с указанием, откуда взято доказательство:
PATH|found=true|from=IPart7|to=IModelContainer cast|IPart7|IModelContainer|verified=true| source=agent_live_probe:cast(TopPart,'IModelContainer').Sketchs/.Extrusions.Count==0
Поле source показывает происхождение доказательства: часть связей взята из документации API, часть проверена живым запуском на реальном КОМПАСе. Всего Core V2 хранит 5 443 значения из 420 перечислений и 2 296 проверенных связей между интерфейсами. Если доказанного маршрута между типами нет, система возвращает found=false. Такой отказ полезнее правдоподобной выдумки: агент хотя бы знает, что дальше идти этим путем нельзя.
Исследовательские зонды: когда документация молчит
Документация КОМПАС API описывает сигнатуры, но редко объясняет физический смысл поведения объектов. В каком формате, например, возвращаются точки из свойства прямоугольника ContourSegmentsPoints — массивом структур или плоским списком координат? А про то, как локальные 2D-координаты эскиза на плоскости XOZ пересчитываются в глобальные 3D, документация вообще молчит.
Раньше в таких ситуациях LLM пыталась угадывать и закономерно ломала код. Теперь, если агент заходит в тупик, он формирует гипотезу и пишет исследовательский зонд — микро-скрипт, который запускается в песочнице исключительно для сбора фактов.
Из логов реальной сессии видно, как агент прощупывает API для конвертации локальных точек эскиза в глобальные:
RunResult(ran=True, timeout=False, value={ 'xoy_0_80': '(True, 0.0, 80.0, 0.0)', 'xoz_0_80': '(True, 0.0, 4.898587196589413e-15, -80.0)'})
Агент ставит эксперимент в живом КОМПАСе и эмпирически доказывает, что локальная ось Y эскиза на плоскости XOZ направлена по глобальной минус Z. В другом зонде он считывает вершины прямоугольника и выясняет, что возвращается плоский кортеж координат (30.0, 40.0, 50.0, 40.0...) типа <class 'tuple'>.
Получив эти факты, агент добавляет их в свой контекст и пишет целевой скрипт уже без ошибок.
Проверка кода и работа с ГОСТами
Прежде чем запускать сгенерированный код, агент отдает его на проверку графу и компилятору:
print(guard.verify(code, backend="auto").feedback)
VERIFY|ok=true|backend=auto compiler|ok=true graph|ok=true
При ошибке в ответ попадают строка и код диагностики, список подходящих кандидатов из поиска и компактная подсказка для исправления. Сам Guard код не переписывает и внешнюю языковую модель не зовет: чинить остается агенту, которому теперь есть от чего оттолкнуться вместо слепого перебора.
С нормативной базой та же логика проверяемости. Агент, которому пользователь дал ссылку на ГОСТ, обязан проверить его статус:
gost_id=2.104-2006validity_status=supersededwithdrawn_date=2024-03-01superseded_by=Р 2.104-2023
Стандарт отменен, действует новый. Полнотекстовый поиск guard.gost_text_search() возвращает фрагмент текста нового стандарта Р 2.104-2023, исходный файл и контрольную сумму. Каждое утверждение, которое агент передаст пользователю, проверяемо до первоисточника.
Архитектура
Раньше компоненты общались через локальные сервисы: что-то нужно было поднять, что-то могло не подняться. Для агентной инфраструктуры это лишние точки отказа. Теперь весь инструментарий живет внутри одного Python-процесса рядом с агентом:
процесс агента└── KompasGuard ├── поиск по API (RU/EN) ├── граф API + перечисления + проверенные касты ├── полнотекстовый поиск по ГОСТам ├── проверка кода (компилятор + граф) └── run() -> изолированный воркер -> KOMPAS COM
Модель, индекс и метаданные скачиваются при первом запуске, кешируются и занимают около 255 МБ; память процесса после первого поиска составляет примерно 382 МБ.
Единственная внешняя часть осталась там, где без нее нельзя: изолированный воркер, запускающий проверенный код агента на живом КОМПАСе. Выполнять сгенерированный нейросетью скрипт в реальном CAD всегда рискованно: после него легко остаются десятки незакрытых скрытых документов и утечки памяти, а в худшем случае виснет весь kompas.exe.
Поэтому воркер берет жесткое управление ресурсами на себя. В каждом выводе лога можно заметить блок: cleanup={'policy': 'created', 'created': 1, 'closed': 1, 'failed': 0}. Песочница отслеживает COM-дескрипторы и безусловно закрывает все созданные документы без сохранения. Упал код с исключением или успешно отработал — окружение в любом случае возвращается в исходное чистое состояние.
Что в итоге
Guard из одного «умного поиска» вырос в цепочку доказательств, через которую агент проводит каждую задачу:задача от пользователя-> кандидаты из API-> точные константы и пути получения объектов-> проверенный код-> изолированный запуск на КОМПАСе-> безопасная проверка результатаЗа понимание запросов отвечает маленькая модель, существование пути к объекту гарантирует граф, точные числа и типы приходят из библиотек КОМПАСа, структуру кода сверяет компилятор, а поведение экспериментально подтверждает живой инстанс. Вероятностная часть осталась ровно там, где она полезна: в сопоставлении формулировки задачи с реальным API. Все остальное детерминировано.
Как попробовать и куда писать
Ставится через pip install kompas-3d-guard на Windows x64, поддерживается Python 3.10–3.14. В репозитории лежит skill.md для подключения к агенту: https://github.com/dwnmf/kompas-3d-guard-bin
Если у вас есть своя задача на стыке LLM и CAD, напишите на hello@kompasmcp.ru, обсудим. Необязательно про КОМПАС и даже про Guard. Туда же с вопросами по установке и багами, хотя баги лучше сразу в issues на GitHub.
ссылка на оригинал статьи https://habr.com/ru/articles/1058800/