Pular para o conteúdo
CADCAMFUN
Docs

Documentação

Escreva um plugin de ferramenta assistente

Crie uma ferramenta de leitura independente, valide seu manifesto e argumentos, habilite-a deliberadamente e teste o limite do trabalhador que você realmente recebe.

Traduzido automaticamente do inglês. Ver o original em inglês

Escolha o formato da extensão do assistente

O carregador de extensão do assistente de produção aceita um diretório contendo plugin.json e uma entrada JavaScript agrupada. Ele importa o módulo para um trabalhador Bun e expõe funções declaradas de seu registro aiTools para o assistente. O recurso atualmente suportado é ai:tool. Este guia cria uma calculadora de círculo de parafuso somente leitura que retorna coordenadas; ele não insere geometria em um desenho.

Há também uma API ZIP de plug-in de navegador em /api/plugins com manifest.json, principal, permissões de navegador e ativos de inicialização. Esse instalador não habilita o plugin de trabalho descrito aqui. O referência HTTP do plug-in documentos que separam formato; Plug-ins explica ambas as superfícies. Não traduza entre eles simplesmente renomeando um manifesto.

Uma ferramenta assistente é um código executado dentro do limite de tempo de execução do aplicativo. As verificações de capacidade do host limitam o acesso por meio do RPC do host, mas o trabalhador atual não é uma sandbox do sistema operacional. Use código de extensão cuja execução seja apropriada para aquele ambiente e faça com que seu comportamento de leitura/gravação declarado esteja de acordo com o que a função realmente faz.

Crie o diretório e o registro de consentimento

Use o diretório de dados do aplicativo exato do sidecar que você pretende estender. Crie esta estrutura, mantendo o ID do diretório idêntico ao ID do DNS reverso do manifesto:

Diretório de plugins de aplicativos
<dataDir>/plugins/
  enabled.json
  com.example.bolt-circle/
    plugin.json
    index.js

O carregador de produção lê enabled.json como uma matriz JSON de identificadores seguros. Ele ignora entradas duplicadas/inválidas e JSON malformado ou ausente significa que nenhum plug-in está ativado. Uma pasta não listada não foi carregada. O carregador também rejeita uma incompatibilidade de ID de pasta/manifesto e manifestos que solicitam recursos não suportados.

O carregamento ocorre quando o serviço de plug-in assistente é inicializado. Ele não configura um observador de sistema de arquivos para recarga a quente. Prepare e valide os arquivos primeiro e, em seguida, ative/reinicie essa instância. Um plug-in com falha registra um aviso enquanto outros plug-ins e a inicialização do aplicativo continuam; inspecione esses avisos em vez de assumir que uma entrada de arquivo ativada garante uma ferramenta em execução.

Declare um contrato de ferramenta preciso

Este manifesto completo nomeia uma única ferramenta de leitura. O ID de contribuição é o identificador local voltado para o modelo; a entrada nomeia a função exportada pelo módulo. Essas strings podem ser diferentes, como bolt_circle e boltCircle fazem aqui.

plugin.json
{
  "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
      }
    }]
  }
}

O SDK requer IDs de DNS reverso de 3–128 caracteres, uma versão semântica major.minor.patch com pré-lançamento opcional, versão da API "1", nome/entrada não vazio e recursos reconhecidos. Os IDs das ferramentas começam com uma letra minúscula e usam letras minúsculas, dígitos ou sublinhados até 40 caracteres. As descrições contêm 10–2,000 caracteres. O tipo de ferramenta é leitura ou gravação; não existe nenhum tipo de extensão destrutiva.

Mantenha o esquema de parâmetros pequeno e descritivo. Sua função é ajudar o modelo a produzir argumentos válidos e permitir que o adaptador rejeite erros comuns antes da execução. Não substitui a validação dentro do código.

Implementar o módulo independente

A entrada exporta aiTools com a função de entrada nomeada no manifesto. Ele valida argumentos de tempo de execução novamente e retorna um pequeno resultado compatível com JSON. A aritmética de coordenadas finitas e a contagem limitada evitam que entradas malformadas produzam uma lista ilimitada ou uma saída sem sentido.

index.js
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.

Agrupe as dependências na entrada em vez de assumir que as importações relativas no estilo do nó são resolvidas. O host lê o texto de entrada e o importa por meio de um módulo blob. O código de nível superior do módulo é executado na importação, portanto, evite efeitos colaterais apenas ao carregar uma calculadora.

Valide o manifesto e ambos os caminhos de argumento

Valide antes de adicionar o plugin ao enabled.json. Essa verificação de desenvolvimento usa o mesmo analisador de manifesto do SDK e adaptador de esquema de IA que o aplicativo. Coloque o script em um espaço de trabalho com essas dependências, como apps/sidecar, e passe o diretório absoluto do plugin como argumento.

Script de validação de desenvolvimento
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 }))

As duas últimas chamadas testam o comportamento no nível da função independentemente da rejeição do adaptador. O SDK valida a estrutura do manifesto, mas o carregador de produção adiciona sua própria capacidade e regras de ID de pasta. Teste todos os três limites: declaração, adaptador de argumento e função real do módulo.

Habilite e descubra o identificador real da ferramenta

Após a validação, habilite o ID no diretório de dados da aplicação:

enabled.json
["com.example.bolt-circle"]

Reinicie/reinicialize o serviço sidecar pretendido e inspecione os logs em busca de avisos de carga. Então liste GET /api/ai/actions. As ferramentas de plug-in de leitura são expostas lá, bem como ao assistente incorporado; política pode restringir o catálogo. O identificador da ferramenta é gerado a partir do ID do plugin e do ID da ferramenta local, normalizado e limitado ao tamanho do identificador do host.

Para este manifesto, o ID derivado atual é plugin_com_example_bolt_circle_bolt_circle. Descubra-o em vez de reimplementar a regra de nomenclatura em cada cliente. Um ID de plug-in longo pode ser truncado para deixar espaço para o sufixo da ferramenta. A descrição do catálogo inclui o nome do plugin para que o usuário possa identificar a fonte.

Uma ferramenta de gravação é marcada para aprovação humana no assistente incorporado, independentemente do modo de permissão normal. A ponte externa expõe apenas ferramentas de plug-in de leitura e recusa chamadas de extensão de gravação, mesmo com um token de leitura/gravação. Mantenha a gentileza correta; o aplicativo não pode inferir efeitos colaterais de módulos arbitrários de um esquema JSON.

Chame uma ferramenta de leitura por meio de acesso externo com escopo definido

Use um token de ponte emitido pelo aplicativo para um projeto real. Seu portador abre apenas GET /api/ai/actions e POST /api/ai/actions/invoke. Ele não permite que os chamadores instalem plug-ins, editem credenciais ou enumere projetos não relacionados. O projeto/modo do token é verificado pelo manipulador de ações; um token para todo o aplicativo é desnecessário para um cliente de calculadora independente.

bridge-call.json, com um ID de projeto real
{
  "projectId": "replace-with-your-project-id",
  "tool": "plugin_com_example_bolt_circle_bolt_circle",
  "input": { "count": 4, "radius": 10 }
}
Invoque a ferramenta de plug-in em execução
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"

As entradas do plug-in são o objeto de argumento declarado; ao contrário das ferramentas agrupadas integradas, este plugin de leitura não requer um discriminador de ação dentro da entrada. A ponte envolve o valor retornado com metadados de ferramenta/ação; a ação é executada. O nível de função ok=false permanece um resultado legível em vez de um sucesso de transporte HTTP provando uma lista de coordenadas útil.

A política de ponte verifica tool.use e bridge.use. Um token ausente, expirado, revogado ou de tipo errado gera falha de autorização. Um token vinculado a outro projeto não é uma forma de ampliar o escopo desta chamada.

Entenda a conversão de esquema e os limites de capacidade

O adaptador converte o esquema JSON da contribuição em zod quando representável. Se a conversão falhar, o esquema exceder 20,000 caracteres serializados ou não descrever um objeto, ele retornará para um esquema de objeto arbitrário. É por isso que a função de exemplo verifica a própria contagem e o raio. O acessório negativo de um esquema suportado passando localmente não prova que todos os recursos do esquema permanecerão representáveis.

O SDK reconhece desenho:leitura, desenho:gravação, percurso da ferramenta:leitura, armazenamento, ui:painel e ai:ferramenta; desenhar escrever implica desenhar ler. O carregador do assistente de produção atualmente aceita apenas ai:tool. Adicionar drawing:write a este manifesto faz com que ele seja ignorado, não sendo concedido um contexto de editor de desenho.

O bootstrap do trabalhador geral contém manipuladores de desenho/percurso/UI de espaço reservado. Esses manipuladores não são uma integração de geometria de produção. Da mesma forma, declarar contribuições de pós-processamento ou de operação não obriga o carregador assistente a conectá-las aos registros de fabricação. Escolha uma ação de aplicativo suportada quando o recurso precisar alterar dados CAD/CAM reais.

Projete em torno do ciclo de vida do trabalhador implementado

O SDK descreve um módulo com ganchos de manifesto, ativação, limpeza opcional e desativação. O caminho de IA de produção atual importa a entrada e chama as funções exportadas do aiTools. Seus manipuladores RPC de ativação/desativação de trabalho reconhecem mensagens de ciclo de vida, mas não invocam ganchos de módulo exportados. Uma calculadora pode, portanto, usar o módulo mínimo mostrado aqui sem depender de retornos de chamada de ativação.

O host inicia um trabalhador por plug-in habilitado, correlaciona respostas com IDs RPC e rastreia chamadas pendentes. Uma exceção de função falha nessa chamada; testes focados em trabalhadores reais mostram que chamadas posteriores ainda podem ser bem-sucedidas. Retorne recusas estruturadas para entradas inválidas esperadas e reserve falhas lançadas para erros de computação inesperados.

Um tempo limite de chamada encerra seu trabalho e causa falha no plug-in imediatamente. Falhas de trabalho e falhas de postagem também removem a instância em execução e marcam-na com falha. O histórico de falhas tem uma janela de dez minutos, mas o manipulador atual falha na primeira falha; ele não espera três tentativas de reinicialização bem-sucedidas. Corrija a fonte e recarregue/reative deliberadamente em vez de assumir a reinicialização automática.

Mantenha as garantias de tempo de execução proporcionais

O carregador de produção fornece um orçamento de host de 10 segundos para RPC de ciclo de vida/comando comum. As chamadas de ferramentas de IA têm um prazo separado de 30 segundos. O host limita um plug-in a 2,000 chamadas por minuto e 64 chamadas pendentes. Ele passa uma configuração de limite de recursos de 256 MB para Bun ao criar o trabalhador. Esses são controles de tempo de execução, não um processo separado ou uma política de contenção de sistema operacional.

O bootstrap começa com um ambiente vazio e tenta excluir processos/redes globais selecionados. A exclusão é o melhor esforço e alguns Bun globais permanecem acessíveis. A verificação de capacidade é, portanto, significativa para as APIs fornecidas pelo host, mas não pode ser apresentada como um isolamento completo de JavaScript arbitrário.

Existem campos opcionais de assinatura e desenvolvimento no esquema do manifesto. O caminho de instalação do trabalhador atual registra o texto da assinatura sem verificação criptográfica. Não rotule um editor como verificado porque esse campo está presente. Uma revisão de origem independente e uma política de instalação permanecem decisões separadas da aceitação do esquema.

Plugins que falham três vezes em dez minutos são desativados automaticamente e uma chamada de ferramenta do assistente é interrompida após 30 segundos. Ambos os valores são constantes em apps/sidecar/src/plugins/host.ts.

Atualizar, remover e diagnosticar uma ferramenta

Para uma atualização, edite e valide o módulo/manifesto incluído antes de reiniciar o serviço de destino. Mantenha seu ID estável quando for a mesma extensão e aumente a versão para identificar o que foi carregado. Para parar de carregá-lo em inicialização futura, remova seu ID de enabled.json e reinicie/reinicialize. A alteração do arquivo por si só não descarta um trabalhador já em execução.

Se a ferramenta estiver faltando, verifique a matriz habilitada, a igualdade exata de ID de pasta/manifesto, a lista de recursos suportados e o erro de decodificação do manifesto nos logs. NoSuchTool geralmente significa que a entrada não nomeia uma função exportada do aiTools. Uma recusa de argumento aponta para o esquema ou guarda de função. Um tempo limite indica bloqueio de trabalho ou uma promessa não resolvida. Os pontos de extremidade de ativação/configuração ZIP do navegador não gerenciam esse arquivo de consentimento do trabalhador.

A cobertura focada está em apps/sidecar/test/plugins-ai-tools.test.ts para trabalhadores reais e ai-plugin-tools.test.ts para carregamento de produção, validação de argumentos, exposição de leitura/gravação e comportamento de ponte. As fontes de implementação são packages/plugin-sdk/src/manifest.ts, packages/ai/src/tools/plugin-tool.ts, apps/sidecar/src/plugins/host.ts, e services/ai-plugins.ts. Use esses limites para determinar o que um teste bem-sucedido realmente provou.

Resumo das regras do contrato

ItemRestriçãoO que acontece se violado
Ferramenta id^[a-z][a-z0-9_]{0,39}$A decodificação do manifesto falha; o plugin não está carregado.
description10–2,000 caracteresA decodificação do manifesto falha.
kindread ou write; não existe nenhum tipo destrutivoA decodificação do manifesto falha.
parametersObjeto JSON Schema, no máximo 20,000 caracteres quando serializadoEsquemas superdimensionados ou não conversíveis voltam a aceitar qualquer objeto.
CapacidadesApenas ai:tool no anfitrião assistenteO plugin é ignorado com um aviso.
Nome da pastaIgual ao manifesto idO plugin é ignorado com um aviso nomeando ambos os valores.
Orçamento de chamada normal5 s por chamada por padrãoO trabalhador é interrompido como qualquer plugin que não responde.
Orçamento de chamada de ferramenta assistente30 sA chamada retorna um resultado de falha.
Memória256 MB de pilhaO trabalhador é morto.
Avaliar2,000 chamadas por plug-in por minuto, 64 chamadas pendentesChamadas acima da tarifa são recusadas; uma fila completa falha com plugin RPC queue is full.
Falhas e tempos limiteContado em 10 minutos, limite de 3Cada falha ou tempo limite encerra o trabalhador e marca o plug-in com falha.

Escreva a descrição do modelo. Indique o que a ferramenta computa, suas unidades e quando utilizá-la; por exemplo, "Retorna os centros dos furos de um círculo de parafusos em milímetros. Use quando o usuário solicitar furos com espaçamento uniforme em um diâmetro." Descrições vagas fazem com que as ferramentas sejam chamadas na hora errada ou nem sejam chamadas.

Lista de verificação de pré-lançamento

  1. Agrupe a entrada em um único módulo independente. O trabalhador não pode resolver importações de espaço de trabalho em tempo de execução.
  2. Correr parseManifest na final plugin.json e corrija todos os problemas relatados.
  3. Chame cada função de ferramenta exportada diretamente com argumentos válidos, de limite e inválidos e confirme se a entrada inválida produz um resultado de erro legível em vez de uma exceção que escapa.
  4. Mantenha os resultados pequenos e compatíveis com JSON. O assistente os lê e resultados grandes ocupam o contexto.
  5. Escolher read a menos que a ferramenta mude alguma coisa. UM write ferramenta avisa o usuário antes de cada chamada.
  6. Coloque a pasta em <data dir>/plugins/<id>/, adicione o ID a enabled.json, reinicie o sidecar e confirme o ID da ferramenta derivada da lista de ferramentas do assistente.
  7. Ressalto version para cada mudança que você envia e mantém id estável para que o ID da ferramenta derivada não mude.

Matriz de solução de problemas

SintomaCausa provávelConsertar
A ferramenta nunca apareceID faltando em enabled.json, ou o JSON está malformadoCorrija a matriz; JSON malformado ou ausente não permite nada.
O log diz que a pasta se chama X, mas o manifesto diz YPasta e manifesto id diferirRenomeie um para que correspondam exatamente.
Log diz que pede desenho: leia, ...Capacidade não suportada neste hostDeclarar apenas ai:tool.
O Assistente transmite formas de argumentos inesperadasO esquema voltou para qualquer objetoUse um esquema de objeto com properties em 20,000 caracteres e valide dentro da função também.
Retorno de chamadas ok: false depois de cerca de 30 sA ferramenta excedeu seu orçamento de tempoFaça menos trabalho por chamada ou pré-computação; evite loops ilimitados.
O plugin para de responder após um erroUma falha ou tempo limite do trabalhador encerra o trabalhador e marca o plug-in faulted; as falhas também são contadas em uma janela de 10 minutos com um limite de 3Corrija a falha e reinicie o sidecar para carregá-lo novamente.

Lint um manifesto antes de instalar

check-manifest.ts
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)
Erro comumConsertar
"version": "1.0"Use três números: 1.0.0
"apiVersion": 1É uma sequência: "1"
ID do plug-in gear ou My.PluginDNS reverso, letras minúsculas: com.example.gear
ID da ferramenta Lookup-ThreadMinúsculas, dígitos e sublinhados, primeira letra, ≤ 40 caracteres
Descrição da ferramenta com menos de 10 caracteresDescreva o que ele faz e quando usá-lo
Declarando ambos drawing:read e drawing:writePermitido, mas drawing:write sozinho já implica ler
Chave de armazenamento começando com / ou contendo ..Use chaves relativas, como presets/m2

Em 3D

Tabelas de plug-ins Arraste para orbitar · role para ampliar · arraste com o botão direito para deslocar
PluginRegistryEntry e PluginConfiguration das migrações: as configurações fazem referência às entradas do registro.