CH 20 · defineTool: Fabrica una Herramienta para el Agent
Objetivo del capítulo
En CH 18 y 19, los plugins que escribimos solo hacían log — eso era solo para verificar "el plugin está cargado". Este capítulo escribe la cosa verdaderamente valiosa en los plugins: herramientas.
Las herramientas son las "manos" del Agent: el modelo dice algo, se llama al código que escribes, hace trabajo real, y envía el resultado de vuelta al modelo. En los capítulos anteriores has estado usando las herramientas integradas de dsh (leer archivos, ejecutar comandos, buscar); este capítulo fabricamos una nosotros mismos, dejamos que el modelo realmente se acerque y la llame.
Las herramientas son el alma de los plugins
Recuerda lo que usas cada día: el Agent de dsh puede leer archivos, escribir archivos, ejecutar comandos — esas son herramientas (capacidades registradas en ctx.tools). El modelo en sí solo puede "hablar"; las herramientas le permiten "actuar".
El esqueleto mínimo de un plugin de herramienta:
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
// Las cuatro partes de una herramienta, desglosadas una por una abajo
}))
}inject: ['tools'] declara "quiero usar el registro de herramientas", aprendido en CH 19; ctx.tools.register(...) monta una herramienta en el registro.
Las cuatro piezas de defineTool
defineTool toma un objeto que le dice a dsh "cómo se llama esta herramienta, cuándo debe usarse, qué parámetros necesita, cómo hace su trabajo". Una herramienta = un "JD de contratación para el Agent":
| Campo | Significado | Una analogía |
|---|---|---|
name | El nombre de la herramienta, el modelo lo usa para llamar | Título del puesto |
description | Dile al modelo "qué hace esta herramienta, cuándo debe usarse" | Responsabilidades del puesto |
parameters | Declara qué parámetros son obligatorios, cuáles son opcionales | Materiales a presentar |
execute | La función que realmente hace el trabajo | Trabajo tras la incorporación |
Mira una herramienta mínima completa (igual que el tutorial oficial, ligeramente traducida):
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet by name',
parameters: {
name: { type: 'string', required: true, description: 'Name of the person to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}Cuatro puntos clave:
parametersse escribe en JSON Schema:type: 'string'declara el tipo del parámetro,required: truedeclara que es obligatorio. El framework valida automáticamente los parámetros que pasa el modelo; los no conformes dan error — no necesitas hacer type-check manualmente enexecute.execute(args)es la función que realmente hace el trabajo:argsya ha sido validado, solo úsalo con confianza. Devuelve un "valor canónico" (una cadena aquí).output.schemadeclara la forma del valor de retorno,output.renderconvierte el valor de retorno en contenido que el modelo puede ver (un bloque de texto aquí). El valor de retorno primero existe en forma canónica, la capa de renderizado se encarga de "traducirlo" al modelo.descriptiones extremadamente importante: el modelo lo usa para juzgar "¿debo usar esta herramienta ahora mismo?". Escríbelo claro, escríbelo específico, y el modelo sabrá cuándo llamarla.
Práctica: escribe una herramienta greet, haz que el modelo realmente la llame
En un workspace donde quieras poner plugins, crea un directorio:
New-Item -ItemType Directory -Path "tool-demo\src" -ForcePaso 1: instala dependencias
defineTool viene de @deepseek-ai/dsh-tools, instálalo en el directorio del plugin primero:
cd "E:\software-workspace\DeepSeek harness demo\tool-demo" # sustituye por tu directorio
npm init -y
npm install @deepseek-ai/dsh-toolsRecuerda añadir una línea "type": "module" en package.json (CH 19 lo mencionó, sin ella habrá un montón de avisos).
Paso 2: escribe la herramienta
Crea tool-demo\src\greet.js, con la misma herramienta greet de arriba. Añadí una línea de log en execute para confirmar fácilmente en la terminal que fue realmente llamada:
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet by name',
parameters: {
name: { type: 'string', required: true, description: 'Name of the person to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
console.log('[greet] called with', args.name)
return `Hello, ${args.name}!`
},
}))
}Paso 3: declara y verifica
Crea tool-demo\cordis.yml (sustituye la ruta por la tuya, recuerda %20 para los espacios):
- insert:
- id: greet
name: 'file:///E:/tu-workspace/tool-demo/src/greet.js'Usa headless para ejecutar una vez, deja que el modelo realmente la llame:
cd tu-directorio-de-workspace
dsh --profile headless --patch "./tool-demo/cordis.yml" "You must call the greet tool, say hi to Ada, then tell me verbatim what the tool returned"En la salida de la terminal verás:
[greet] called with AdaEsta es la evidencia de que execute fue realmente llamado por el modelo — tu código fue realmente ejecutado por el modelo acercándose. Y la respuesta del modelo contendrá el Hello, Ada! devuelto por la herramienta.

El [greet] called with Ada amarillo en la terminal es el log de execute siendo realmente llamado por el modelo; el Hello, Ada! de abajo es el resultado que la herramienta devuelve al modelo.
Deja que dsh lo haga: un prompt lo resuelve
El flujo de escritura de herramientas es exactamente el mismo que escribir un plugin, dsh puede hacerlo por sí mismo, y conoce los campos de defineTool y cómo debe escribirse el schema mejor que tú.
Envía este prompt directamente en el cuadro de entrada de la Web UI:
In my current workspace, help me write a tool plugin: use defineTool to define the simplest tool (with one required parameter, returning some text in execute). First read the dsh official docs to understand defineTool's fields and parameter validation rules, implement per the official spec, then run a headless to have the model actually call it, and finally tell me the result and where the files are.Irá a consultar los docs por sí mismo, instalará dependencias, escribirá la herramienta, verificará que el modelo realmente la llamó. Tú solo verificas.

Este es el resultado de ejecutar yo realmente este prompt: leyó los docs por sí mismo, construyó las tres piezas del plugin (package.json declarando bundle, defineTool entry index.js, un insert de una línea cordis.patch.yml), e incluso descubrió que las rutas de Windows con espacios romperían el parseo del comando de instalación, y movió proactivamente el plugin a una ruta sin espacios antes de instalar — cayó en el error, y lo sorteó por sí mismo.

Tras la ejecución, también organizó los errores en una lista: requisitos de la forma de export del plugin, inject debe declararse explícitamente, los paquetes del workspace necesitan artefactos de build para ejecutarse, error de espacios en la ruta.
Errores comunes
| Problema | Qué está pasando | Cómo manejarlo |
|---|---|---|
Reporta Cannot find package '@deepseek-ai/dsh-tools' | El directorio del plugin no instaló dependencias | npm install @deepseek-ai/dsh-tools |
| El modelo nunca llama a tu herramienta | description no está claro, el modelo no sabe cuándo usarla | Haz la description específica: "Use when user requests X" |
| Parámetros no pasados correctamente | El schema y el entendimiento del modelo no coinciden | Escribe una description clara para cada parámetro en parameters |
| La herramienta reportó un error de parámetro | El modelo pasó parámetros ilegales | Marca los obligatorios con required: true, escribe el tipo correctamente |
| El modelo la llamó pero el resultado es incorrecto | La lógica en execute está mal | Añade console.log en execute para depurar, comprueba los logs |
Lo que aprendiste en este capítulo
Apruebas si puedes completar los puntos de abajo:
- [ ] Enuncias las cuatro piezas de defineTool:
name/description/parameters/execute - [ ] Sabes que
parameterses JSON Schema, el framework valida automáticamente los parámetros - [ ] Escribes un plugin de herramienta y lo registras en
ctx.tools - [ ] Usas headless para verificar que el modelo realmente llamó a tu herramienta
- [ ] Sabes que
descriptiondecide cuándo el modelo usa tu herramienta
