Habilidad de Desarrollo de Plugins para DeepSeek Harness
Permite que cualquier agente desarrolle plugins para DeepSeek Harness (DSH) de forma correcta, eficiente y conforme a las normas oficiales. Esta habilidad es el manual de operación para el desarrollo de plugins DSH: primero se presentan el modelo mental y las reglas de hierro, luego las plantillas de código y el proceso paso a paso que se pueden copiar directamente, y finalmente la lista de verificación. Para profundizar, consulte los documentos resumidos en el directorio
References/.
0. Cuándo usar esta habilidad
Esta habilidad debe cargarse cuando la tarea implique cualquiera de los siguientes:
- Escribir, modificar o depurar plugins (tool, adaptador LLM, proveedor de servicios, hooks, UI, puente de protocolo, etc.) para DeepSeek Harness
- Escribir o modificar las configuraciones
cordis.yml/cordis.patch.yml/dsh.profile/dsh.bundle - Usar
dsh plugin ...para instalar, eliminar o empaquetar plugins - Comprender servicios como
ctx.tools,ctx.llm,ctx.agents,ctx.sessionso eventos comotools/*,agent/*,session/eventen DSH - Investigar cómo funciona el modelo de plugins de DSH (framework Cordis)
Lea primero todo este SKILL.md y luego comience a trabajar. Antes de comenzar, lea los archivos cordis.yml / profile / código fuente relacionados existentes en el proyecto para confirmar las convenciones actuales.
1. Modelo Mental: DSH es un sistema de plugins construido sobre Cordis
En una frase: DeepSeek Harness es un SDK de Agent Harness donde cada capacidad (herramientas, adaptadores LLM, acceso a archivos, el propio bucle del agente) es un plugin (componente) que se monta en un contexto compartido.
Tres conceptos clave (del artículo 'A Programming Paradigm for Spatiotemporal Composability', consulte References/10-spatiotemporal.md):
| Concepto | Significado | Manifestación en el código |
|---|---|---|
| Efecto reversible (revertible effect) | Cada modificación del componente al entorno compartido lleva una operación inversa; el runtime la rastrea y la restaura en orden LIFO cuando se descarga el componente | ctx.effect(() => () => cleanup); ctx.on(), ctx.tools.register(), etc. son efectos que se deshacen automáticamente al descargarse |
| Coefecto reactivo (reactive coeffect) | El componente declara de qué servicios depende; cuando los servicios aparecen/desaparecen/cambian de implementación, el componente se activa/desactiva/recarga automáticamente | export const inject = ['tools']; cuando la dependencia desaparece, el plugin se elimina automáticamente; cuando se restaura, se recarga automáticamente |
| Fiber (instancia de componente) | Cada instancia de plugin cargada es un fiber, con una máquina de estados de ciclo de vida | PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED (si apply lanza un error, pasa a FAILED) |
Reglas de hierro derivadas:
- Todo registro debe realizarse a través de
ctx(ctx.on/ctx.tools.register/ctx.effect/ctx.plugin/ctx.provide). Todo lo que se registra a través dectxse limpia automáticamente al descargarse. Nunca useremoveListener/clearIntervalmanualmente. - Cualquier recurso no gestionado por
ctx(conexiones de red, temporizadores propios, manejadores de archivos, watchers) debe encapsularse enctx.effect(() => { ...; return () => cleanup }). - Las dependencias deben declararse (
inject). No usectx.get()para detectar dependencias obligatorias ni asuma el orden de carga; el orden lo determinan las dependencias, no el orden de las líneas encordis.yml. - La configuración debe ser configurable: cualquier parámetro que pueda tener un valor diferente en diferentes implementaciones debe ser un campo
Config. No debe haber valores codificados (criterio: ¿se puede cambiar este valor encordis.ymlsin cambiar el código?). - Los errores de configuración deben ser evidentes: use el esquema de Schemastery para validar en el momento de la carga. Una configuración no válida debe provocar un error de carga claro y no permitir la ejecución en mal estado.
- Los callbacks deben ser reproducibles/recargables en caliente: los callbacks de presentación (
output.render,presentCall,presentResult) deben ser funciones puras; los plugins pueden ser descargados y recargados por HMR en cualquier momento, no dependa del estado global a través de las recargas.
2. Entorno y cadena de herramientas
- Los plugins son módulos TypeScript (también se acepta JavaScript) que se ejecutan en un entorno Node.js ESM.
- Paquetes de dependencia principales (todos del ámbito
@deepseek-ai):@deepseek-ai/cordis— Framework de plugins (contexto, fiber, eventos, registro)@deepseek-ai/schemastery— Validación de esquemas de configuración (implementación de Standard Schema)@deepseek-ai/dsh-tools—defineTooly servicio de registro de herramientas@deepseek-ai/dsh-llm—LlmAdapter,StreamChunk,GenerateOptions,CallId- Paquetes de costuras de capacidades:
dsh-shell,dsh-session,dsh-agent,dsh-system-prompt,dsh-fs,dsh-jobs,dsh-credentials, etc. (lista completa enReferences/08-capability-layering.md)
- Dos puntos de entrada de desarrollo (a elegir):
- Con el código fuente descargado (recomendado, para tutoriales): clone
deepseek-ai/deepseek-harness,pnpm install, inicie la interfaz web conpnpm dsh web --patch <tu-cordis.yml>, ejecute ejemplos de tutoriales de Cordis puro connode --import tsx ../../vendor/cordis/bin.js. - Con la CLI instalada:
dsh --profile <nombre> ...para iniciar un perfil,dsh plugin --profile <nombre> <argumentos pnpm>para gestionar plugins.
- Con el código fuente descargado (recomendado, para tutoriales): clone
3. La forma mínima de un plugin (aprende esto primero)
Un plugin es un módulo que exporta una función apply:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin' // Opcional, solo para visualización en diagnóstico
export function apply(ctx: Context) {
// Aquí se registran las capacidades
}
Se monta a través de cordis.yml o patch:
- name: './src/my-plugin.ts' # Ruta relativa (código fuente/desarrollo) o nombre del paquete npm (después de la instalación)
id: my-plugin # Identificador estable, el cargador lo usa para la conciliación de configuración
Tres formas de plugin
- Forma de función (recomendada por defecto):
export const name = 'greet-tool' export const inject = ['tools'] export function apply(ctx: Context) { /* ... */ } - Forma de objeto:
export default { name, inject, apply(ctx) {} } - Forma de clase (cuando se necesita proporcionar un servicio):
export default class MyService extends Service { constructor(ctx) { super(ctx, 'myService') } }
Hasta que se necesite proporcionar un servicio, use siempre la forma de función.
4. Desarrollo de una Herramienta (la tarea más frecuente)
Las herramientas se registran en ctx.tools usando defineTool. Plantilla (se puede copiar directamente):
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'my-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'my_tool', // Nombre de la herramienta visto por el modelo (convención snake_case)
description: 'Una frase sobre lo que hace.', // Descripción funcional vista por el modelo, debe ser específica y clara sobre cuándo usarla
parameters: {
path: { type: 'string', required: true, description: 'Qué significa el parámetro' },
limit: { type: 'number' }, // Sin 'required' significa opcional
},
output: {
schema: { type: 'string' }, // Declara el valor JSON canónico devuelto por execute
render: (_args, value) => [{ type: 'text', text: value }], // Convierte el valor a contenido visible para el modelo
},
async execute(args, exec) {
// args ya ha sido validado por el esquema y se han inferido los tipos
// exec.signal es la señal de cancelación; las tareas largas deben responder a ella
return `resultado para ${args.path}`
},
}))
}
Contrato completo de defineTool (debe cumplirse)
parameters: Mapa de esquemas de parámetros; la raíz es un objeto abierto implícito. Cada propiedad puede tenerrequired: true. Soportastring/number/integer/boolean/null/array/object/oneOf; los nodos de objeto explícitos deben declararadditionalProperties: true | false.output.schema: Declara el valor JSON canónico que devuelveexecute(objeto/array/escalar/null). Esta es la API para llamadas programáticas (elawait tools.xxx()en Code Mode); debe diseñarse para devolver directamente identificadores y campos; la explicación orientada a humanos va enoutput.render.execute(args, exec):- args ya está validado en tiempo de ejecución, pero las restricciones que no se pueden expresar con el DSL del esquema (cadena no vacía, número positivo, entre campos) deben manejarse con
throw new Error(...). - Devuelve el valor canónico declarado en
output.schema. No devuelva bloques de contenido, no obligue a la parte llamante a analizar identificadores del lenguaje natural. - Obedezca
exec.signal: cancele el trabajo en curso cuando se active la señal. - Fallos de infraestructura (excepción/valor de retorno no válido) → el resultado se marca como
isError. Éxito pero resultado no ideal (por ejemplo, código de salida distinto de cero) → devuelva el valor canónico y explíquelo en render.
- args ya está validado en tiempo de ejecución, pero las restricciones que no se pueden expresar con el DSL del esquema (cadena no vacía, número positivo, entre campos) deben manejarse con
output.render(args, value): Función pura que convierte el valor canónico enContentBlock[](texto que ve el modelo). Nunca haga E/S, ni lea el estado de la sesión aquí.- Opcional
output.presentationMeta(args, value): Deriva metadatos de interfaz de usuario reproducibles a partir del valor canónico (función pura). - Opcional
presentCall(args)/presentResult(args, result): Declara la intención de renderizado de la tarjeta de la herramienta en la interfaz de usuario (card: 'generic' | 'terminal' | 'diff' | 'search' | 'web' | 'read'). Deben ser funciones puras (se ejecutan tanto en el flujo en tiempo real como en la reproducción de la sesión), no hagan E/S. - No incluya metadatos de tiempo de ejecución como
timeoutMs,isConcurrencySafe,finalizeContentcomo esquema visible para el modelo; no se filtrarán al modelo. - Los parámetros son inmutables: trate
argscomo de solo lectura; después de registrarlos, no modifique el esquema ni reemplace los callbacks.
Tareas de larga duración (tareas en segundo plano)
Para soportar run_in_background:
const jobs = ctx.get('jobs')
if (!jobs) throw new Error('tareas en segundo plano no disponibles: cargue @deepseek-ai/dsh-jobs')
return {
kind: 'background',
jobId: jobs.start({
kind: 'my-work',
label: args.command,
...exec.agent ? { owner: exec.agent } : {},
run: () => { /* Devuelve { cancel, done, readOutput? } */ },
}),
}
Puntos clave: después de publicar jobId, use la señal de cancelación propia de la tarea (gestionada por job_kill / owner dispose), ya no use exec.signal; el esquema de salida debe poder contener un identificador canónico como { kind: 'background', jobId }. Consulte References/03-tools.md y la implementación de @deepseek-ai/dsh-tool-bash.
Estrategias de ejecución de herramientas (puntos de extensión, usar según sea necesario)
| Punto de extensión | Modo | Uso |
|---|---|---|
tools/pre-execute | waterfall | Permitir/denegar/preguntar estrategias (control de acceso); devuelve {kind:'allow'} / {kind:'deny',reason} / {kind:'ask'} |
ctx.tools.guard() | Guardia monótona | Denegación final, los oyentes posteriores no pueden revertirla (devolver reason significa denegación) |
tools/execute | waterfall | Envolver la distribución: timeout/reintentos/métricas; puede reemplazar exec.signal pero no se puede eliminar |
tools/post-execute | waterfall | Reemplazar contenido/valor, evitar resultados, anexar contexto del modelo |
tools/result | emit | Observación de solo lectura del resultado final inmutable (registro, auditoría) |
No integre las estrategias en la herramienta; coloque las estrategias de implementación en plugins de hooks (plugins Cordis normales que escuchan estos eventos, como el ejemplo de control de acceso en References/11-cookbook.md).
5. Configuración de plugins (Schemastery)
El plugin exporta Config (interfaz TypeScript del mismo nombre + esquema Schemastery); Cordis valida y llena los valores por defecto antes de apply:
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export interface Config {
greeting: string
maxRetries: number
verbose?: boolean
}
export const Config: Schema<Config> = Schema.object({
greeting: Schema.string().default('Hola'),
maxRetries: Schema.number().default(3),
verbose: Schema.boolean().default(false),
})
export function apply(ctx: Context, config: Config) {
// config siempre está completo y validado
}
Pasar configuración en cordis.yml:
- name: './src/my-plugin.ts'
config:
greeting: 'Hola'
maxRetries: 5
Puntos clave:
Configdebe ser un objeto Standard Schema (construido con Schemastery), no puede exportar un objeto simple.- Constructores comunes:
Schema.string().required()/.default(x),Schema.number(),Schema.boolean(),Schema.array(String),Schema.union(['a','b']),Schema.object({...})anidados. - Soporta expresiones
!!jsevaluadas en tiempo de carga (por ejemplo,greeting: !!js process.env.GREETING ?? 'Hola');!!jssolo es válido para los camposconfigydisabled. - Los cambios de configuración activan HMR: la instancia anterior se descarga (los registros se limpian automáticamente) → se carga la nueva instancia. No almacene en caché la configuración fuera del plugin.
6. Servicios y dependencias (desarrollo del lado del servidor)
Uso de servicios (consumidor)
export const inject = ['tools'] // Dependencia obligatoria: si no se cumple, no se carga
export function apply(ctx: Context) {
ctx.tools.register(/* ... */) // Garantizado que está listo en apply
}
Dependencia opcional: no escriba inject, use ctx.get('service') para detectar (devuelve undefined si no hay proveedor).
Proporcionar un servicio (proveedor)
Use una subclase de Service (plugin de forma de clase):
import { Service, type Context } from '@deepseek-ai/cordis'
declare module '@deepseek-ai/cordis' {
interface Context {
metrics: MetricsService // Declaración de fusión: da tipo a ctx.metrics
}
}
export default class MetricsService extends Service {
static inject = ['llm'] // Los servicios también pueden depender de otros servicios
constructor(ctx: Context) {
super(ctx, 'metrics') // 'metrics' es el nombre del servicio
}
record(event: string, value: number) { /* ... */ }
}
export const name = 'metrics'
export function apply(ctx: Context) {
ctx.plugin(MetricsService)
}
API de bajo nivel (no común pero debe conocer): ctx.provide(name, value) registra una implementación de servicio (efecto, se da de baja automáticamente al descargar), ctx.set/get para leer/escribir almacenamiento, ctx.accessor para definir propiedades computadas, ctx.mixin para montar miembros del servicio en ctx.
Semántica del comportamiento del servicio (comprender más que memorizar)
injectno es una verificación de arranque única: si un servicio obligatorio desaparece durante la ejecución, el plugin dependiente se elimina automáticamente; si el servicio se restaura, se recarga automáticamente. Esto garantiza que el consumidor nunca tenga una referencia a un servicio descargado.- Los nombres de servicio son un espacio de nombres global plano:
tools,llm, etc. ya están ocupados; los nombres de servicio propios deben tener un prefijo distintivo (por ejemplo,myCap,metrics). - Aislamiento de servicios: en
cordis.yml, use group +isolatepara que diferentes grupos de plugins vean diferentes instancias del mismo nombre de servicio (por ejemplo, dos grupos con proveedores deshellconfigurados con diferentes timeouts).
7. Sistema de eventos (comunicación entre plugins)
Modos de distribución (parte del nombre del evento, debe elegirse correctamente)
| Modo | Llamada | Semántica |
|---|---|---|
emit | ctx.emit(name, ...args) | Síncrono, no espera ni recoge valores de retorno |
parallel | await ctx.parallel(...) | Todos los oyentes se ejecutan en paralelo y se espera |
serial | await ctx.serial(...) | Se ejecuta en orden, el primer valor no null/false/undefined gana y detiene la ejecución |
bail | ctx.bail(...) | Versión síncrona de serial |
waterfall | await ctx.waterfall(name, ...args, next) | Middleware envolvente; los oyentes reciben next, envuelven el valor de retorno descendente |
Regla de hierro de waterfall: los oyentes que solo observan/anotan deben llamar a next(); no llamar a next() y devolver directamente = cortocircuito intencionado (veto/intercepción). Olvidar llamar a next() consumirá silenciosamente el comportamiento predeterminado descendente.
Eventos con seguridad de tipos
declare module '@deepseek-ai/cordis' {
interface Events {
'my-plugin/ready': (payload: { id: string }) => void
'my-plugin/check': (input: string) => boolean | undefined
}
}
Convención de nombres de eventos: namespace/action (por ejemplo, agent/step, tools/result, session/event). Los oyentes se registran con ctx.on() y se limpian automáticamente al descargar el plugin.
Eventos comunes de Harness (se usan a menudo en el desarrollo)
tools/pre-execute/tools/execute/tools/post-execute/tools/result— Canalización de herramientas (ver tabla anterior)agent/*(agent/request,agent/pre-step,agent/session-start,agent/turn-stopping, etc.) — Ciclo de vida del agente y construcción de solicitudessession/event— Flujo de eventos de sesión persistente (turn/*,step/*,tool/call,tool/result,assistant/chunksonevent.typeaquí, no son eventos Cordis del mismo nombre)approval/request— Waterfall de aprobaciónsystem-prompt/assemble— Ensamblaje completo del mensaje del sistema (retorno autoritario, el oyente tiene la responsabilidad de preservar las contribuciones existentes)
Las firmas/modos de activación completos se encuentran en los bloques cordis-surface generados por cada página de subsistema (consulte References/08-capability-layering.md).
8. Adaptadores LLM (conectar nuevos proveedores de modelos)
Un adaptador = una clase que hereda LlmAdapter e implementa stream(), que convierte las solicitudes independientes del proveedor de Harness en llamadas API específicas y las devuelve como fragmentos StreamChunk.
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
class MyAdapter extends LlmAdapter {
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
// 1. options.messages → formato del proveedor
// 2. Llamar API de transmisión (debe pasar options.signal)
// 3. Respuesta → secuencia de StreamChunk
}
}
export const name = 'my-llm-adapter'
export const inject = ['llm']
export const Config = Schema.object({
apiKey: Schema.string().required(),
providers: Schema.array(String).required(),
})
export function apply(ctx: Context, config: Config) {
ctx.llm.registerAdapter(config.providers, new MyAdapter(config.apiKey))
}
Protocolo StreamChunk (seguir estrictamente)
async function* chunks(): AsyncIterable<StreamChunk> {
yield { type: 'block-start', index: 0, blockType: 'text' }
yield { type: 'text-delta', index: 0, text: 'Hola' }
yield { type: 'block-end', index: 0, block: { type: 'text', text: 'Hola mundo' } }
// Bloque de llamada a herramienta:
yield { type: 'block-start', index: 1, blockType: 'tool-call' }
yield { type: 'tool-call-delta', index: 1, id: CallId('c1'), name: 'bash', argumentsDelta: '{"command":"ls"}' }
yield { type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('c1'), name: 'bash', arguments: '{"command":"ls"}' } }
yield { type: 'usage', usage: { inputTokens: 100, outputTokens: 50 } } // Debe ir antes de finish
yield { type: 'finish', reason: { kind: 'stop' } } // Debe ser el último
}
Reglas: cada block-start debe tener su correspondiente block-end; index comienza en 0 y se incrementa; arguments de tool-call es una cadena JSON sin procesar durante todo el proceso; finish es el último bloque; usage va antes de finish.
Errores y metadatos
- Fallos de transporte/protocolo:
throw new LlmError(msg, 'STABLE_CODE')(con código estable), no dependa de la conversión automática deErrornormal. - El proveedor no soporta un campo de
GenerateOptions:throw new LlmError(..., 'UNSUPPORTED'), nunca lo ignore silenciosamente. - Cada solicitud HTTP debe incluir
attributionHeaders()y pasaroptions.signal. - Opcional: anular
resolveModel()(devuelve la identidad del proveedor/modelo + metadatos opcionales de contexto/razonamiento) ylistModels()(publica opciones de modelos).
Tutorial completo en References/05-llm-adapter.md; implementaciones de referencia: packages/llm/llm-deepseek (SSE compatible con OpenAI) y packages/llm/llm-pi-ai.
9. Capas de capacidad (tres roles, patrón de diseño de capacidades reemplazables)
Cuando una capacidad necesita un proveedor reemplazable (como la ejecución de Bash), se divide en tres roles, cada uno en un paquete diferente:
- Definición del Servicio (por ejemplo,
dsh-shell): Define la interfaz del servicio Cordis + tipos Request/Result.abstract class MyCapService extends Service { super(ctx,'myCap'); abstract execute(req): Promise<res> } - Proveedor del Servicio (por ejemplo,
dsh-bash-local): Implementa la interfaz.class MyCapLocal extends MyCapService { ... };apply(ctx){ ctx.plugin(MyCapLocal) } - Consumidor (por ejemplo,
dsh-tool-bash): Expone la capacidad como una herramienta del modelo.inject: ['tools','myCap']
Dirección de dependencia: El proveedor depende de la definición; el consumidor depende de la definición; el proveedor y el consumidor no dependen el uno del otro. Para cambiar de proveedor, solo se modifica una línea en cordis.yml; la definición y el consumidor no se modifican.
No divida de forma preventiva: solo divida en paquetes si los roles necesitan evolucionar de forma independiente. Un plugin de herramienta simple es suficiente con un solo paquete.
10. Empaquetado e instalación (entregar un plugin a los usuarios)
Dos conceptos
- Paquete combinado (bundle): Un paquete npm con una capa de configuración adjunta, cuyo manifiesto declara
dsh.bundle. Es lo que usted escribe y distribuye. - Perfil (profile): Un combo iniciable ubicado en
$DSH_HOME/profiles/<nombre>, cuyo manifiesto declaradsh.profile(que contiene una lista ordenada debundles). Es lo que el usuario inicia.
Empaquetar un paquete combinado
hello-plugin/
├── package.json # Declara dsh.bundle
├── cordis.patch.yml # Contenido de la capa
└── index.js # Módulo del plugin
// package.json
{
"name": "dsh-hello-plugin",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"files": ["index.js", "cordis.patch.yml"],
"dsh": { "bundle": { "patch": "cordis.patch.yml" } }
}
cordis.patch.yml es una capa de configuración (parche de cordis.yml) que puede contener plugins, servicios, inyecciones, etc. Se fusiona con la configuración del perfil activo en el momento de la carga.
Instalación y resolución de dependencias
Para usar un paquete combinado, el usuario lo agrega a la lista bundles del perfil:
# $DSH_HOME/profiles/mi-perfil/dsh.profile
name: mi-perfil
version: "1.0.0"
bundles:
- npm:dsh-hello-plugin
pnpm dsh install descarga e instala las dependencias de todos los paquetes combinados en el perfil.
Complementar plugins con cordis.patch.yml
El archivo de parche se usa para agregar, modificar o eliminar plugins de la configuración base. Ejemplo típico:
# cordis.patch.yml
plugins:
- name: './src/my-tool.ts'
id: my-tool
config:
verbose: true
Puntos clave:
- El archivo de parche no es un
cordis.ymlcompleto, sino una capa que se superpone. - Se pueden usar varios archivos de parche: se aplican en orden.
- Los parches pueden deshabilitar plugins con
disabled: true.
11. Verificación (lista de verificación antes de enviar)
Antes de enviar el código, verifique los siguientes puntos:
- ¿Están todos los registros de efectos a través de
ctx? (sinaddEventListener/setTimeout/fs.watchsinctx.effect) - ¿Están declaradas todas las dependencias en
inject? (no usarctx.get()para dependencias obligatorias) - ¿Está todo el comportamiento sensible a la configuración en
Config? (sin valores codificados) - ¿Se valida la configuración con el esquema y se proporciona un error claro?
- ¿Los callbacks de presentación (
render,presentCall,presentResult) son funciones puras? ¿No hay E/S? - ¿Responde
executeaexec.signal? (tareas largas: cancelar con la señal) - ¿Devuelve
executeun valor canónico (no texto en lenguaje natural)? - ¿Se utiliza
LlmErrorcon un código estable para errores de protocolo del adaptador LLM? - ¿Sigue la secuencia
StreamChunkel formato correcto? (block-start/block-end, uso, finalización) - ¿Se ha probado la herramienta con diferentes configuraciones? (valores válidos, no válidos, límites)
- ¿Se ha probado la desinstalación/recarga del plugin? (sin fugas de memoria, sin referencias colgantes)
12. Referencias
Consulte el directorio References/ para obtener documentos detallados sobre:
| Documento | Contenido |
|---|---|
References/01-quickstart.md | Guía de inicio rápido para el desarrollo de plugins |
References/02-cordis.md | Tutorial del framework Cordis |
References/03-tools.md | Referencia detallada de la API de herramientas |
References/04-services.md | Patrones de servicios y dependencias |
References/05-llm-adapter.md | Tutorial de creación de adaptadores LLM |
References/06-events.md | Sistema de eventos y puntos de extensión |
References/07-config.md | Validación de esquemas y configuración |
References/08-capability-layering.md | Capas de capacidad y patrones de reemplazo |
References/09-bundle.md | Empaquetado y distribución de bundles |
References/10-spatiotemporal.md | Artículo sobre el paradigma de programación espaciotemporal |
References/11-cookbook.md | Recetario de ejemplos comunes |