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:
<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.
{
"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.
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.
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:
["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.
{
"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" 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
| Item | Restrição | O 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. |
description | 10–2,000 caracteres | A decodificação do manifesto falha. |
kind | read ou write; não existe nenhum tipo destrutivo | A decodificação do manifesto falha. |
parameters | Objeto JSON Schema, no máximo 20,000 caracteres quando serializado | Esquemas superdimensionados ou não conversíveis voltam a aceitar qualquer objeto. |
| Capacidades | Apenas ai:tool no anfitrião assistente | O plugin é ignorado com um aviso. |
| Nome da pasta | Igual ao manifesto id | O plugin é ignorado com um aviso nomeando ambos os valores. |
| Orçamento de chamada normal | 5 s por chamada por padrão | O trabalhador é interrompido como qualquer plugin que não responde. |
| Orçamento de chamada de ferramenta assistente | 30 s | A chamada retorna um resultado de falha. |
| Memória | 256 MB de pilha | O trabalhador é morto. |
| Avaliar | 2,000 chamadas por plug-in por minuto, 64 chamadas pendentes | Chamadas acima da tarifa são recusadas; uma fila completa falha com plugin RPC queue is full. |
| Falhas e tempos limite | Contado em 10 minutos, limite de 3 | Cada 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
- 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.
- Correr
parseManifestna finalplugin.jsone corrija todos os problemas relatados. - 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.
- Mantenha os resultados pequenos e compatíveis com JSON. O assistente os lê e resultados grandes ocupam o contexto.
- Escolher
reada menos que a ferramenta mude alguma coisa. UMwriteferramenta avisa o usuário antes de cada chamada. - Coloque a pasta em
<data dir>/plugins/<id>/, adicione o ID aenabled.json, reinicie o sidecar e confirme o ID da ferramenta derivada da lista de ferramentas do assistente. - Ressalto
versionpara cada mudança que você envia e mantémidestável para que o ID da ferramenta derivada não mude.
Lint um manifesto antes de instalar
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 comum | Consertar |
|---|---|
"version": "1.0" | Use três números: 1.0.0 |
"apiVersion": 1 | É uma sequência: "1" |
ID do plug-in gear ou My.Plugin | DNS reverso, letras minúsculas: com.example.gear |
ID da ferramenta Lookup-Thread | Minúsculas, dígitos e sublinhados, primeira letra, ≤ 40 caracteres |
| Descrição da ferramenta com menos de 10 caracteres | Descreva o que ele faz e quando usá-lo |
Declarando ambos drawing:read e drawing:write | Permitido, mas drawing:write sozinho já implica ler |
Chave de armazenamento começando com / ou contendo .. | Use chaves relativas, como presets/m2 |