DeepSeek Harness: Desenvolvendo Sua Primeira Ferramenta
Última atualização: 2026-08-31
Ferramentas são as mãos do Agent — definir uma ferramenta dá ao Agent uma nova capacidade. defineTool é a DSL de definição de ferramentas do DSH, usando uma abordagem declarativa para descrever o nome, parâmetros e lógica de execução de uma ferramenta, para que o Agent entenda quando e como chamar sua ferramenta.
📋 Pré-requisitos: Ter completado 14-inject.md, entender injeção de dependência
1. O Que Você Vai Aprender
- Sintaxe da DSL defineTool
- Declarações name/description/parameters
- Definições de parâmetros JSON Schema
- Implementação da função execute
- Registro de ferramenta em ctx.tools
- Exibição de ferramenta na Web UI
- Exemplo completo: ferramenta de contagem de arquivos
2. Sintaxe da DSL defineTool
(1) Estrutura Básica
▶ Exemplo 1: Estrutura Básica do defineTool
import { defineTool } from '@deepseek-ai/dsh'
export default defineTool({
name: 'tool_name',
description: 'O que esta ferramenta faz',
parameters: {
type: 'object',
properties: { /* ... */ },
required: [ /* ... */ ]
},
async execute(params, ctx) {
// Lógica da ferramenta
return result
}
})
(2) Descrições de Campos
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name |
string | ✅ | Identificador único da ferramenta, minúsculas + underscores |
description |
string | ✅ | Descrição da funcionalidade da ferramenta, para o LLM ler |
parameters |
JSON Schema | ✅ | Definição de parâmetros |
execute |
function | ✅ | Lógica de execução |
(3) Métodos de Exportação
defineTool retorna um objeto de definição de ferramenta que pode ser diretamente usado como export padrão:
// Método 1: Export padrão
export default defineTool({ ... })
// Método 2: Export nomeado
export const myTool = defineTool({ ... })
3. name e description
(1) Convenção de Nomenclatura
Nomes de ferramentas usam formato snake_case:
name: 'file_count' // ✅
name: 'fileCount' // ❌ Não recomendado
name: 'FileCount' // ❌ Não recomendado
name: 'file-count' // ❌ Não recomendado
(2) Escrita da Descrição
A descrição é a base para o LLM escolher uma ferramenta. Pontos-chave:
- Explique o que a ferramenta faz
- Explique quando deve ser usada
- Evite descrições sem sentido como "ferramenta de teste" ou "ferramenta de exemplo"
// ❌ Descrição ruim
description: 'A tool for testing'
// ✅ Boa descrição
description: 'Count the number of files in a directory. Returns total count and breakdown by file extension. Use when user asks about file statistics or directory...
(3) Descrição Multi-idioma
Descrições atualmente suportam apenas inglês. O LLM entende o propósito da ferramenta a partir da descrição, mesmo que a entrada do usuário esteja em outro idioma.
4. Definição de Parâmetros JSON Schema
(1) Estrutura Básica
parameters seguem a especificação JSON Schema:
parameters: {
type: 'object',
properties: {
param_name: {
type: 'string',
description: 'Descrição do parâmetro'
}
},
required: ['param_name']
}
(2) Tipos Suportados
| Tipo JSON Schema | Tipo TypeScript | Descrição |
|---|---|---|
string |
string | String |
number |
number | Número |
integer |
number | Inteiro |
boolean |
boolean | Booleano |
object |
object | Objeto aninhado |
array |
array | Array |
(3) Parâmetros String
properties: {
path: {
type: 'string',
description: 'Caminho do diretório para contar arquivos'
},
pattern: {
type: 'string',
description: 'Padrão Glob para filtrar arquivos (ex. *.ts)',
default: '*'
}
}
(4) Parâmetros Enum
properties: {
sort_by: {
type: 'string',
enum: ['name', 'size', 'date'],
description: 'Critério de ordenação'
}
}
(5) Parâmetros Array
properties: {
extensions: {
type: 'array',
items: { type: 'string' },
description: 'Extensões de arquivo para incluir (ex. [".ts", ".js"])'
}
}
(6) Objetos Aninhados
properties: {
options: {
type: 'object',
properties: {
recursive: { type: 'boolean', default: false },
includeHidden: { type: 'boolean', default: false }
}
}
}
(7) Campo required
required: ['path'] // path é obrigatório
// pattern tem default, não obrigatório
5. Implementação da Função execute
(1) Assinatura da Função
async execute(params: Params, ctx: Context): Promise<Result>
params: Parâmetros passados pelo usuário (Agent), tipo inferido da definição parametersctx: Contexto Cordis, pode acessar serviços injetados- Valor de retorno: Qualquer dado serializável em JSON
(2) Implementação Simples
async execute({ path }, ctx) {
const count = await countFiles(path)
return { count, path }
}
(3) Acessando Serviços
Dentro de execute, você pode acessar serviços registrados via ctx:
export const inject = ['fs']
export default defineTool({
name: 'file_count',
// ...
async execute({ path }, ctx) {
const files = await ctx.fs.readdir(path)
return { count: files.length }
}
})
(4) Tratamento de Erros
▶ Exemplo 2: Ferramenta com Tratamento de Erros
async execute({ path }, ctx) {
try {
const files = await ctx.fs.readdir(path)
return { count: files.length, path }
} catch (error) {
return {
error: true,
message: `Falha ao ler diretório: ${error.message}`
}
}
}
Ferramentas não devem lançar exceções não capturadas — retornar um objeto de erro permite ao Agent entender o motivo da falha, o que é mais amigável do que um crash.
(5) Formato do Valor de Retorno
Recomendado retornar objetos estruturados:
// ✅ Retorno estruturado
return {
count: 42,
path: '/home/alice/project',
breakdown: {
typescript: 28,
javascript: 10,
other: 4
}
}
// ❌ Retorno em texto simples
return 'Encontrados 42 arquivos em /home/alice/project'
Retornos estruturados permitem ao Agent usar resultados programaticamente, em vez de apenas exibir texto.
6. Registro de Ferramenta em ctx.tools
(1) Registrando em um Plugin
Ferramentas definidas com defineTool precisam ser registradas em ctx.tools através de um plugin:
import { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh'
const fileCountTool = defineTool({
name: 'file_count',
description: 'Contar arquivos em um diretório',
parameters: {
type: 'object',
properties: {
path: { type: 'string', description: 'Caminho do diretório' }
},
required: ['path']
},
async execute({ path }, ctx) {
const files = await ctx.fs.readdir(path)
return { count: files.length }
}
})
export const name = 'tool-file-count'
export const inject = ['tools', 'fs']
export function apply(ctx: Context) {
ctx.tools.register(fileCountTool)
}
(2) Registrando Múltiplas Ferramentas
export function apply(ctx: Context) {
ctx.tools.register(fileCountTool)
ctx.tools.register(fileSizeTool)
ctx.tools.register(fileSearchTool)
}
(3) Registro Dinâmico
export function apply(ctx: Context) {
const tools = [fileCountTool, fileSizeTool]
for (const tool of tools) {
ctx.tools.register(tool)
ctx.logger.info(`ferramenta registrada: ${tool.name}`)
}
}
7. Exibição de Ferramenta na Web UI
(1) Exibição Automática
Ferramentas registradas em ctx.tools aparecem automaticamente na lista de ferramentas da Web UI:
┌─────────────────────────────────────┐
│ 🔧 Ferramentas │
├─────────────────────────────────────┤
│ file_count │ Contar arquivos em dir │
│ file_size │ Info de tamanho arquivo│
│ file_search │ Buscar por arquivos │
└─────────────────────────────────────┘
(2) Exibição de name e description
name: Exibido como identificador da ferramentadescription: Exibido como descrição da ferramenta
(3) Exibição de Chamada do Agent
Quando o Agent chama sua ferramenta, a Web UI exibe:
🤖 Agent:
🔧 Usando ferramenta: file_count
→ path: /home/alice/project
Resultado: { count: 42, path: "/home/alice/project" }
▶ Exemplo 8:Ferramenta de Contagem de Arquivos
Combine todo o conhecimento em um plugin de ferramenta completo:
import { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh'
export const name = 'tool-file-count'
export const inject = ['tools', 'fs']
const fileCountTool = defineTool({
name: 'file_count',
description: 'Count the number of files in a directory. Returns total count and breakdown by file extension. Use when user asks about file statistics or directory contents.',
parameters: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Absolute or relative path to the directory'
},
recursive: {
type: 'boolean',
description: 'Whether to count files in subdirectories',
default: false
},
extensions: {
type: 'array',
items: { type: 'string' },
description: 'Filter by file extensions (e.g. [".ts", ".js"]). Count all if omitted.'
}
},
required: ['path']
},
async execute({ path, recursive, extensions }, ctx) {
try {
const entries = recursive
? await ctx.fs.readdirRecursive(path)
: await ctx.fs.readdir(path)
let files = entries.filter(e => !e.isDirectory)
if (extensions && extensions.length > 0) {
files = files.filter(f =>
extensions.some(ext => f.name.endsWith(ext))
)
}
const breakdown: Record<string, number> = {}
for (const f of files) {
const ext = f.name.includes('.')
? '.' + f.name.split('.').pop()
: '(no extension)'
breakdown[ext] = (breakdown[ext] || 0) + 1
}
return {
count: files.length,
path,
recursive,
breakdown
}
} catch (error: any) {
return {
error: true,
message: `Failed to count files: ${error.message}`
}
}
}
})
export function apply(ctx: Context) {
ctx.tools.register(fileCountTool)
ctx.logger.info('file_count tool registered')
}
Inicie e verifique:
# Registrar no cordis.yml
pnpm dsh web --patch
# Testar na Web UI
# 👤 Alice: Quantos arquivos há no diretório /home/alice/project?
# 🤖 Agent: 🔧 file_count → { count: 42, breakdown: { ".ts": 28, ".js": 10, ".json": 4 } }
❓ Perguntas Frequentes
defineTool é uma DSL que cria um objeto de definição de ferramenta. ctx.tools.register é o método de registro que adiciona a definição ao serviço de ferramentas. Eles trabalham juntos: primeiro defineTool define, depois register registra.ctx.tools.execute('other_tool', params). Mas cuidado para evitar chamadas circulares.typescript async execute(params, ctx) { ctx.logger.info('params:', JSON.stringify(params)) // ... } 📖 Resumo
- defineTool é a DSL de definição de ferramentas do DSH: name + description + parameters + execute
- name usa snake_case; description deve explicar claramente o propósito e casos de uso da ferramenta
- parameters seguem JSON Schema, suportando string/number/boolean/object/array
- execute recebe params e ctx, retorna resultados estruturados
- Ferramentas são registradas via
ctx.tools.register()e aparecem automaticamente na Web UI - Retorne objetos estruturados em vez de texto simples; em erros, retorne objetos de erro em vez de lançar exceções
📝 Exercícios
1. ⭐ Básico: Use defineTool para criar uma ferramenta current_time que aceita um parâmetro opcional de fuso horário (padrão UTC) e retorna a string de hora atual. Registre-a no DSH e faça o Agent chamá-la com sucesso.
2. ⭐⭐ Intermediário: Estenda a ferramenta file_count adicionando parâmetros min_size e max_size (em bytes) para filtrar por faixa de tamanho de arquivo. Teste: conte arquivos em /tmp maiores que 1KB e menores que 1MB.
3. ⭐⭐⭐ Desafio: Crie uma ferramenta code_stats que conte linhas de código em um diretório especificado. Parâmetros: path (caminho do diretório), languages (array de filtro de linguagem). Retorne total de linhas, linhas por linguagem, linhas em branco, linhas de comentário. Use regex para distinguir linhas de código / linhas em branco / linhas de comentário.