Документация
Напишите плагин помощника
Создайте автономный инструмент чтения, проверьте его манифест и аргументы, включите его намеренно и протестируйте фактически полученную рабочую границу.
Автоматически переведено с английского. Смотреть оригинал на английском
Выберите формат расширения помощника
Загрузчик расширений Production Assistant принимает каталог, содержащий плагин Plugin.json и связанную запись JavaScript. Он импортирует модуль в рабочий процесс Bun и предоставляет помощнику объявленные функции из записи aiTools. В настоящее время поддерживается возможность ai:tool. В этом руководстве создается калькулятор окружности болтов, доступный только для чтения, который возвращает координаты; он не вставляет геометрию в чертеж.
В каталоге /api/plugins также имеется ZIP API для плагина браузера с манифестом.json, main, разрешениями браузера и ресурсами начальной загрузки. Этот установщик не включает рабочий плагин, описанный здесь. ссылка на HTTP-плагин документы, разделяющие формат; Плагины объясняет обе поверхности. Не осуществляйте перевод между ними, просто переименовывая манифест.
Инструмент-помощник — это код, выполняемый внутри границ времени выполнения приложения. Проверки возможностей хоста ограничивают доступ через RPC хоста, но текущий рабочий процесс не является изолированной программной средой операционной системы. Используйте код расширения, выполнение которого подходит для этой среды, и согласуйте его объявленное поведение чтения/записи с тем, что на самом деле делает функция.
Создайте каталог и запись согласия.
Используйте точный каталог данных приложения той сайдкара, которую вы собираетесь расширить. Создайте эту структуру, сохраняя идентификатор каталога идентичным идентификатору обратного DNS манифеста:
<dataDir>/plugins/
enabled.json
com.example.bolt-circle/
plugin.json
index.js Рабочий загрузчик считывает файл Enabled.json как массив безопасных идентификаторов JSON. Он игнорирует повторяющиеся/недействительные записи, а неверный формат или отсутствие JSON означает, что плагины не включены. Папка, не указанная в списке, не загружается. Загрузчик также отклоняет несоответствие идентификатора папки и манифеста и манифесты, запрашивающие неподдерживаемые возможности.
Загрузка происходит при инициализации службы плагина помощника. Он не настраивает наблюдатель файловой системы для горячей перезагрузки. Сначала подготовьте и проверьте файлы, затем включите/перезапустите этот экземпляр. Неисправный плагин регистрирует предупреждение, в то время как запуск других плагинов и приложений продолжается; проверяйте эти предупреждения вместо того, чтобы предполагать, что запись в включенном файле гарантирует работу инструмента.
Объявите точный контракт на инструмент
Этот полный манифест называет один инструмент чтения. Идентификатор вклада — это локальный идентификатор, связанный с моделью; запись называет функцию, экспортируемую модулем. Эти строки могут различаться, как в данном случае Bolt_circle и BoltCircle.
{
"id": "com.example.bolt-circle",
"name": "Bolt circle calculator",
"version": "1.0.0",
"apiVersion": "1",
"entry": "index.js",
"capabilities": ["ai:tool"],
"contributes": {
"aiTools": [{
"id": "bolt_circle",
"description": "Calculate equally spaced XY hole centers around the origin for a bolt circle. Returns coordinates without changing a drawing.",
"kind": "read",
"entry": "boltCircle",
"parameters": {
"type": "object",
"properties": {
"count": { "type": "integer", "minimum": 1, "maximum": 64 },
"radius": { "type": "number", "exclusiveMinimum": 0 }
},
"required": ["count", "radius"],
"additionalProperties": false
}
}]
}
} Для SDK требуются идентификаторы обратного DNS, состоящие из символов 3–128, семантическая версия major.minor.patch с дополнительной предварительной версией, версия API "1", непустое имя/запись и признанные возможности. Идентификаторы инструментов начинаются со строчной буквы и могут содержать строчные буквы, цифры или знаки подчеркивания длиной до 40 символов. Описания содержат символы 10–2,000. Тип инструмента — чтение или запись; не существует деструктивного типа расширения.
Сохраняйте схему параметров небольшой и описательной. Его роль — помочь модели выдавать действительные аргументы и позволить адаптеру отклонять обычные ошибки перед выполнением. Он не заменяет проверку внутри кода.
Реализуйте автономный модуль
Запись экспортирует aiTools с функцией записи, указанной в манифесте. Он снова проверяет аргументы времени выполнения и возвращает небольшой результат, совместимый с JSON. Арифметика с конечными координатами и ограниченный счетчик не позволяют некорректному вводу создавать неограниченный список или бессмысленный вывод.
export const aiTools = {
boltCircle: async (args) => {
if (!args || typeof args !== "object") {
return { ok: false, error: "Expected count and radius" }
}
const { count, radius } = args
if (!Number.isInteger(count) || count < 1 || count > 64) {
return { ok: false, error: "count must be an integer from 1 to 64" }
}
if (typeof radius !== "number" || !Number.isFinite(radius) || radius <= 0) {
return { ok: false, error: "radius must be a positive finite number" }
}
const holes = Array.from({ length: count }, (_, index) => {
const angle = 2 * Math.PI * index / count
return {
x: Number((radius * Math.cos(angle)).toFixed(4)),
y: Number((radius * Math.sin(angle)).toFixed(4))
}
})
return { ok: true, units: "mm", holes }
}
} Count 4 and radius 10 return centers at approximately (10,0), (0,10), (−10,0), and (0,−10), in counter-clockwise order around the origin. The result deliberately contains no database mutation or drawing identifier. A client wanting to insert holes must use a separate supported application action with its own access and permission behavior.
Объедините зависимости в запись, а не предполагайте, что относительный импорт в стиле Node разрешится. Хост считывает текст ввода и импортирует его через модуль BLOB-объектов. Код верхнего уровня модуля запускается при импорте, поэтому избегайте побочных эффектов от простой загрузки калькулятора.
Проверьте манифест и оба пути аргументов.
Подтвердите перед добавлением плагина в файл Enabled.json. Эта проверка разработки использует тот же синтаксический анализатор манифеста SDK и адаптер схемы AI, что и приложение. Поместите скрипт в рабочую область с этими зависимостями, например apps/sidecar, и передайте в качестве аргумента абсолютный каталог плагина.
import { join } from "node:path"
import { Effect } from "effect"
import { parseManifest } from "@cadcamfun/plugin-sdk"
import { pluginParameters } from "@cadcamfun/ai"
const directory = process.argv[2]
if (!directory) throw new Error("Pass the absolute plugin directory")
const manifest = await Effect.runPromise(parseManifest(
await Bun.file(join(directory, "plugin.json")).text()))
if (manifest.capabilities.some((capability) => capability !== "ai:tool")) {
throw new Error("This production loader accepts only ai:tool")
}
const contribution = manifest.contributes?.aiTools?.[0]
if (!contribution) throw new Error("No tool contribution")
const parameters = pluginParameters(contribution.parameters)
console.log(parameters.safeParse({ count: 4, radius: 10 }).success) // true
console.log(parameters.safeParse({ count: 0, radius: 10 }).success) // false
const module = await import(join(directory, manifest.entry))
console.log(await module.aiTools[contribution.entry]({ count: 4, radius: 10 }))
console.log(await module.aiTools[contribution.entry]({ count: 0, radius: 10 })) Последние два вызова проверяют поведение на функциональном уровне независимо от отклонения адаптера. SDK проверяет структуру манифеста, но рабочий загрузчик добавляет свои собственные правила возможностей и идентификаторов папок. Проверьте все три границы: объявление, адаптер аргументов и фактическую функцию модуля.
Включите и узнайте действительный идентификатор инструмента
После проверки включите идентификатор в каталоге данных приложения:
["com.example.bolt-circle"] Перезапустите/переинициализируйте предполагаемую дополнительную службу и проверьте журналы на наличие предупреждений о загрузке. Затем перечислите GET /api/ai/actions. Инструменты плагина чтения доступны там, а также во встроенном помощнике; политика может сузить каталог. Идентификатор инструмента генерируется на основе идентификатора плагина и локального идентификатора инструмента, нормализованных и привязанных к размеру идентификатора хоста.
Для этого манифеста текущий производный идентификатор равен plugin_com_example_bolt_circle_bolt_circle. Откройте для себя это, а не заново реализуйте правило именования в каждом клиенте. Длинный идентификатор плагина может быть урезан, чтобы оставить место для суффикса инструмента. Описание каталога включает название плагина, чтобы пользователь мог определить источник.
Инструмент записи помечен для одобрения человеком во встроенном помощнике, независимо от обычного режима разрешений. Внешний мост предоставляет только инструменты плагина для чтения и отказывается от вызовов расширения записи даже при наличии токена чтения/записи. Будьте добры и аккуратны; приложение не может вывести произвольные побочные эффекты модуля из схемы JSON.
Вызов инструмента чтения через внешний доступ с ограниченной областью действия.
Используйте токен моста, выданный приложением, для реального проекта. Его носитель открывает только GET /api/ai/actions и POST /api/ai/actions/invoke. Он не позволяет вызывающим сторонам устанавливать плагины, редактировать учетные данные или перечислять несвязанные проекты. Проект/режим токена проверяется обработчиком действий; токен всего приложения не нужен для автономного клиента калькулятора.
{
"projectId": "replace-with-your-project-id",
"tool": "plugin_com_example_bolt_circle_bolt_circle",
"input": { "count": 4, "radius": 10 }
} curl --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_BRIDGE_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @bridge-call.json \
"$CADCAMFUN_URL/api/ai/actions/invoke" Входные данные плагина — это объявленный объект аргумента; в отличие от встроенных сгруппированных инструментов, этот плагин чтения не требует дискриминатора действий внутри ввода. Мост оборачивает возвращаемое значение метаданными инструмента/действия; действие запущено. На уровне функции ok=false остается читаемым результатом, а не успехом HTTP-транспорта, подтверждающим полезный список координат.
Политика моста проверяет Tool.use и Bridge.use. Отсутствующий, просроченный, отозванный или неправильный токен приводит к сбою авторизации. Токен, привязанный к другому проекту, не является способом расширить область действия этого вызова.
Понимать преобразование схемы и ограничения возможностей.
Адаптер преобразует схему JSON вклада в zod, если она представима. Если преобразование завершается неудачей, схема превышает 20,000 сериализованных символов или не описывает объект, она возвращается к схеме произвольного объекта. Вот почему функция из примера сама проверяет счетчик и радиус. Отрицательное фикстуру поддерживаемой схемы, передаваемое локально, не доказывает, что каждая функция схемы останется представимой.
SDK распознает рисование: чтение, рисование: запись, путь к инструменту: чтение, хранилище, пользовательский интерфейс: панель и ai: инструмент; рисование-запись подразумевает рисование-чтение. Загрузчик производственного помощника в настоящее время принимает только ai:tool. Добавление draw:write в этот манифест приводит к его пропуску без предоставления контекста редактора рисунков.
Общая рабочая загрузка содержит заполнители для рисования/траекторий/обработчиков пользовательского интерфейса. Эти обработчики не являются интеграцией производственной геометрии. Аналогичным образом, объявление вклада постпроцессора или операций не заставляет помощника загрузчика вносить их в производственные реестры. Выберите поддерживаемое действие приложения, когда функции необходимо изменить реальные данные CAD/CAM.
Проектируйте с учетом реализованного жизненного цикла работника
SDK описывает модуль с перехватчиками манифеста, активации, дополнительной очистки и деактивации. Текущий производственный путь AI импортирует запись и вызывает экспортированные функции aiTools. Его рабочий процесс активации/деактивации обработчиков RPC подтверждает сообщения жизненного цикла, но не вызывает перехватчики экспортированных модулей. Таким образом, калькулятор может использовать показанный здесь минимальный модуль, не зависящий от обратных вызовов активации.
Хост запускает по одному работнику для каждого включенного плагина, сопоставляет ответы с идентификаторами RPC и отслеживает ожидающие вызовы. Исключение функции завершает этот вызов; целенаправленные тесты на реальных рабочих показывают, что последующие вызовы все еще могут быть успешными. Возвращайте структурированные отказы в случае ожидаемых недопустимых входных данных и резервируйте выданные сбои для непредвиденных ошибок вычислений.
Тайм-аут вызова завершает работу его работника и немедленно приводит к сбою плагина. Сбои воркера и ошибки публикации также удаляют работающий экземпляр и отмечают его как неисправный. История сбоев имеет десятиминутное окно, но текущий обработчик дает сбой при первом сбое; он не ждет трех успешных попыток перезапуска. Исправьте источник и намеренно перезагрузите/повторно включите вместо автоматического перезапуска.
Соблюдайте пропорциональность гарантий времени выполнения
Рабочий загрузчик предоставляет 10-секундный бюджет хоста для обычного жизненного цикла/командного RPC. Вызовы инструментов искусственного интеллекта имеют отдельный крайний срок в 30 секунды. Хост ограничивает плагин 2, 000 вызовами в минуту и 64 ожидающими вызовами. Он передает параметр ограничения ресурсов 256 МБ в Bun при создании рабочего процесса. Это элементы управления во время выполнения, а не отдельный процесс или политика сдерживания операционной системы.
Начальная загрузка начинается с пустой среды и пытается удалить выбранные глобальные переменные процесса/сети. Удаление выполняется с максимальной эффективностью, и некоторые глобальные элементы Bun остаются доступными. Таким образом, проверка возможностей имеет смысл для API, предоставляемых хостом, но не может быть представлена как полная изоляция произвольного JavaScript.
В схеме манифеста существуют необязательные поля подписи и разработки. Текущий путь установки работника записывает текст подписи без криптографической проверки. Не помечайте издателя как проверенного, поскольку это поле присутствует. Политика автономной проверки исходного кода и установки остаются отдельными решениями от принятия схемы.
Плагины, в которых происходит сбой трижды в течение десяти минут, автоматически отключаются, а вызов помощника прекращается через 30 секунд. Оба значения являются константами в apps/sidecar/src/plugins/host.ts.
Обновление, удаление и диагностика инструмента
Для обновления отредактируйте и проверьте связанный модуль/манифест перед перезапуском целевой службы. Сохраняйте его идентификатор стабильным, если это одно и то же расширение, и увеличьте версию, чтобы определить, что было загружено. Чтобы прекратить его загрузку при будущей инициализации, удалите его идентификатор из Enabled.json и перезапустите/повторно инициализируйте. Изменение файла само по себе не удаляет уже работающий рабочий процесс.
Если инструмент отсутствует, проверьте включенный массив, точное равенство идентификаторов папки и манифеста, список поддерживаемых возможностей и ошибку декодирования манифеста в журналах. NoSuchTool обычно означает, что запись не называет экспортированную функцию aiTools. Отказ от аргумента указывает на защиту схемы или функции. Тайм-аут указывает на блокировку работы или невыполненное обещание. Конечные точки включения/конфигурации ZIP браузера не управляют этим файлом согласия работника.
Целенаправленное освещение находится в apps/sidecar/test/plugins-ai-tools.test.ts для реальных работников и ai-plugin-tools.test.ts для производственной загрузки, проверки аргументов, доступа к чтению/записи и поведения моста. Источники реализации: packages/plugin-sdk/src/manifest.ts, packages/ai/src/tools/plugin-tool.ts, apps/sidecar/src/plugins/host.ts, и services/ai-plugins.ts. Используйте эти границы, чтобы определить, что на самом деле доказал успешный тест.
Коротко о правилах контракта
| Элемент | Ограничение | Что произойдет, если нарушить |
|---|---|---|
Инструмент id | ^[a-z][a-z0-9_]{0,39}$ | Декодирование манифеста не удалось; плагин не загружается. |
description | 10–2,000 символов | Декодирование манифеста не удалось. |
kind | read или write; нет разрушительного вида | Декодирование манифеста не удалось. |
parameters | Объект схемы JSON, не более 20,000 символов при сериализации | Негабаритные или неконвертируемые схемы возвращаются к принятию любого объекта. |
| Возможности | Только ai:tool в помощнике ведущего | Плагин пропускается с предупреждением. |
| Имя папки | Равен манифесту id | Плагин пропускается с предупреждением, в котором указаны оба значения. |
| Обычный бюджет звонка | 5 с на вызов по умолчанию | Рабочий остановлен, как и любой не отвечающий плагин. |
| Бюджет вызова помощника | 30 s | Вызов возвращает неудачный результат. |
| Память | 256 МБ кучи | Рабочий убит. |
| Ставка | 2, 000 вызовов на плагин в минуту, 64 ожидающих вызовов | Звонки сверх тарифа отклоняются; полная очередь терпит неудачу с plugin RPC queue is full. |
| Сбои и таймауты | Насчитывается более 10 минут, ограничение — 3. | Каждый сбой или тайм-аут завершает работу работника и помечает плагин как неисправный. |
Напишите описание модели. Укажите, что вычисляет инструмент, его единицы измерения и когда его использовать; например, «Возвращает центры отверстий окружности болтов в миллиметрах. Используйте, когда пользователь запрашивает равномерное расположение отверстий по диаметру». Расплывчатые описания приводят к тому, что инструменты вызываются не вовремя или вообще не вызываются.
Контрольный список перед выпуском
- Объедините запись в один автономный модуль. Рабочий не может разрешить импорт рабочей области во время выполнения.
- Бегать
parseManifestв финалеplugin.jsonи исправить каждую сообщенную проблему. - Вызовите каждую экспортированную функцию инструмента напрямую с допустимыми, граничными и недопустимыми аргументами и убедитесь, что недопустимый ввод приводит к читаемому результату ошибки, а не к исключению исключения.
- Сохраняйте результаты небольшими и совместимыми с JSON. Ассистент читает их, а большие результаты заполняют контекст.
- Выбирать
readесли инструмент что-то не изменит. Аwriteинструмент запрашивает пользователя перед каждым вызовом. - Поместите папку под
<data dir>/plugins/<id>/, добавьте идентификатор вenabled.json, перезапустите сайдкар и подтвердите производный идентификатор инструмента из списка инструментов помощника. - Ударяться
versionза каждое отправленное вами изменение и сохранитеidстабильный, поэтому идентификатор производного инструмента не изменяется.
Проверка манифеста перед установкой
import { decodeManifest, requestedCapabilities } from "@cadcamfun/plugin-sdk"
import { Effect } from "effect"
const raw = JSON.parse(await Bun.file(process.argv[2] ?? "plugin.json").text())
const manifest = await Effect.runPromise(decodeManifest(raw)) // throws a SchemaError naming the bad field
console.log(manifest.id, manifest.version, requestedCapabilities(manifest))
for (const tool of manifest.contributes?.aiTools ?? [])
console.log(`plugin_${manifest.id}_${tool.id}`, tool.kind) | Распространенная ошибка | Исправить |
|---|---|
"version": "1.0" | Используйте три цифры: 1.0.0 |
"apiVersion": 1 | Это строка: "1" |
Идентификатор плагина gear или My.Plugin | Обратный DNS, нижний регистр: com.example.gear |
Идентификатор инструмента Lookup-Thread | Нижний регистр, цифры и символы подчеркивания, сначала буква, символы ≤ 40 |
| Описание инструмента короче 10 символов. | Опишите, что он делает и когда его использовать. |
Объявление обоих drawing:read и drawing:write | Разрешено, но drawing:write само по себе уже подразумевает чтение |
Ключ хранения, начинающийся с / или содержащий .. | Используйте относительные ключи, такие как presets/m2 |