DeepSeek Harness (dsh): instalei o agente de codificação open-source da DeepSeek, conectei-o ao meu aplicativo de chat e fiz benchmark dele

Publicado em
18 agosto 2026
Por
Jacob Lloyd — escrito com ajuda de IA, depois do projeto
Tempo de leitura
16 min de leitura

Em termos simples: A DeepSeek lançou um 'agente de codificação' gratuito e open-source — um programa que lê seu projeto, edita arquivos e executa comandos para você, como o Claude Code. Este artigo mostra como o instalei no meu PC Linux, fiz com que ele iniciasse automaticamente, conectei-o ao meu aplicativo de chat caseiro e o testei em pequenas tarefas reais de codificação, tanto com os modelos em nuvem da DeepSeek quanto com um modelo rodando no meu próprio hardware. Funcionou, foi barato, e há algumas arestas.

Em 13 de agosto de 2026, a DeepSeek lançou o DeepSeek Harnessdsh — um agente de codificação licenciado sob MIT construído sobre a ideia de que tudo é um plugin: o adaptador de modelo, as ferramentas, o sandbox, até o loop do agente. Ele atingiu 95.000 estrelas no GitHub em dois dias. Instalei-o na minha máquina caseira na mesma semana, dei a ele um lugar ao lado do Reasonix, meu agente de codificação existente, dentro do DisPatch, apontei-o tanto para os modelos na nuvem da DeepSeek quanto para um modelo no meu próprio setup com GPU, e o passei por uma pequena suíte de tarefas. Isto é o que foi preciso, o que custou e onde doeu.

Teste rápido antes de continuar: passe o mouse sobre ele e clique nele. O dsh criou esta versão em pixel art do logotipo do site a partir de uma descrição escrita — JavaScript puro, sem dependências — e toda a história de como isso aconteceu está mais abaixo.

tl;dr

  • O que é: um agente da classe do Claude Code (lê/edita arquivos, executa shell, mantém um plano, gera subagentes) distribuído como um pacote npm. Dois modos: uma interface web em 127.0.0.1:3080 e um modo headless de execução única que imprime uma resposta e encerra — feito para scripts e outros agentes.
  • Quanto custa: o software é gratuito. Todo o meu benchmark de 5 tarefas custou cerca de 3 centavos no V4-Flash e cerca de 7 centavos no V4-Pro nas tarifas de pico; nada no modelo local.
  • O que você precisa: Node.js 22.19+/24, uma chave de API da DeepSeek ou qualquer servidor compatível com OpenAI (usei os dois).
  • Com o que você fica: dsh --profile headless "fix the failing test" em qualquer pasta de projeto, uma interface web como serviço em segundo plano, troca de modelo ao editar um arquivo YAML e — no meu caso — um bot "DeepSeek Harness" no meu aplicativo de chat com um botão Run.
  • O resultado: 15/15 aprovações em V4-Flash, V4-Pro e um Gemma-4-26B local, 2–18 s por tarefa. Prévia para desenvolvedores — o README adverte em maiúsculas que as coisas vão quebrar entre versões, e eu esbarrei em algumas das arestas listadas abaixo.

O percurso, do básico ao avançado

Os passos 1–3 dão a você um agente funcional em dez minutos. Os passos 4–5 são o que fiz por cima para que outros softwares — e outros agentes — possam chamá-lo.

O que você acaba tendo

O que eu realmente uso é o modo headless. De dentro de um diretório de projeto:

$ cd ~/Projects/dsh-playground
$ dsh --profile headless "Create fizz.py that prints FizzBuzz for 1..15 and run it; reply with the program output only."

1
2
Fizz
4
Buzz
…
FizzBuzz
$ echo $? 0

Seis segundos, arquivo criado, programa executado, resposta impressa, código de saída 0. Ele não escreve nada fora da pasta em que você o iniciou (modo de permissão padrão workspace-write), imprime apenas a mensagem final no stdout e persiste cada execução para que você possa abri-la depois na interface web e ler exatamente o que ele fez — cada chamada de ferramenta, cada contagem de tokens.

A interface web é o layout familiar de 2026 — sessões à esquerda, chat no meio, uma página de configurações para modelos — com um bom hábito: a chave da API é colada na página de configurações, nunca em um arquivo de configuração. A minha roda como um serviço de usuário do systemd e está incorporada no meu aplicativo de chat ao lado do Reasonix:

Aplicativo de chat DisPatch com o bot 'DeepSeek Harness' selecionado: a interface web do dsh incorporada no painel principal mostrando uma sessão concluída 'Create and run hello.py' com as etapas Think, Write e Bash e a resposta; uma barra de status abaixo com Running, um menu suspenso Model definido como DeepSeek-V4-Flash e botões Start/Restart/Stop
A interface web do dsh dentro do DisPatch, mostrando uma sessão concluída com sua trajetória de etapas (Think → Write → Bash) e a linha de estatísticas por turno. A barra na parte inferior controla a unit do systemd e o modelo padrão.

Passo 1: instalar (dois minutos)

Um pacote npm. Eu mantenho um prefixo Node privado para ferramentas de agente, para que nada caia na árvore do sistema — em uma distro imutável como a Bluefin, esse é o único lugar sensato para isso, de qualquer forma — mas uma instalação global simples é o mesmo comando:

npm install -g @deepseek-ai/dsh
dsh --version          # 0.1.0-rc.7 at the time of writing
dsh web                # starts the UI, prints http://127.0.0.1:3080

Abra a URL, vá para Configurações → Modelos, cole sua chave DeepSeek e salve. Isso grava ~/.dsh/.credentials.yaml (modo 0600) e a rota do modelo funciona imediatamente, sem reiniciar. Para automatizar, o arquivo é um mapeamento YAML simples — DEEPSEEK_API_KEY: sk-… — e eu escrevi o meu a partir do arquivo de env que meus outros serviços já leem, então o segredo existe em mais um lugar, mas nunca em um histórico de shell ou em um arquivo de unit.

Duas coisas que vale a pena saber antes de continuar:

  • A telemetria está desativada por padrão (DSH_TELEMETRY_MODE não definido = desativada). Eu verifiquei a configuração enviada, não o material de marketing; o exportador OTLP existe, só não está ligado.
  • O sandbox é real, mas limitado. workspace-write confina as escritas ao diretório a partir do qual você o iniciou. As leituras não são confinadas — a documentação diz isso claramente — então não o execute a partir do seu diretório pessoal para uma tarefa real e não o aponte para pastas com segredos.

Passo 2: executá-lo como um serviço

A interface web é um processo Node de longa duração; eu queria que ela estivesse ativa no login, sem terminal, apenas loopback. Uma unit do systemd --user resolve isso:

# ~/.config/systemd/user/dsh-web.service
[Unit]
Description=DeepSeek Harness web UI (dsh web) on 127.0.0.1:3080
After=network.target

[Service]
WorkingDirectory=%h
Environment=DSH_HOME=%h/.dsh
Environment=DSH_PERMISSION_MODE=workspace-write
ExecStart=/usr/local/bin/dsh web --host 127.0.0.1 --port 3080   # `which dsh`
Restart=on-failure
RestartSec=3

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now dsh-web.service
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3080/   # 200

WorkingDirectory é a raiz do espaço de trabalho padrão da interface web, mas a interface ainda faz você escolher um espaço de trabalho explicitamente antes de permitir que você digite. Bom padrão. A CLI também recusa --host 0.0.0.0 — os autores descrevem a vinculação a todas as interfaces como "intencionalmente ainda não suportada", e como a interface não tem autenticação alguma, concordo. Quer acessá-la de outra máquina? Coloque um proxy reverso com autenticação na frente, ou alcance o navegador do host pela sua área de trabalho remota (é o que eu faço).

Etapa 3: modelos — um arquivo YAML, recarregado a quente

~/.dsh/settings.yaml contém o modelo padrão e quaisquer provedores adicionais, e o dsh o relê para a próxima solicitação — sem reiniciar, sem novo login. O meu:

agent-default-model:
  provider: deepseek-official
  model: deepseek-v4-flash          # or deepseek-v4-pro

llm-deepseek:
  reasoningEffort: high             # off | low | high | max

# A local OpenAI-compatible server (mine is llama.cpp-based on a GPU rig).
llm-pi-ai:
  providers:
    buildpc:
      displayName: StudioForge (GPU rig)
      apiKeyEnv: STUDIOFORGE_PLACEHOLDER_KEY   # a reference, not a value
      api: openai-completions
      baseURL: http://my-gpu-rig:1234/v1      # your server; mine sits on the tailnet
      defaultContextWindow: 65536
      models:
        - id: unsloth/gemma-4-26B-A4B-it-qat-GGUF/gemma-4-26B-A4B-it-qat-UD-Q4_K_XL
          name: Gemma 4 26B-A4B (rig)

Dois detalhes que me custaram dez minutos cada: apiKeyEnv é uma referência resolvida a partir de .credentials.yaml ou do ambiente, nunca um literal — e um servidor local sem chave ainda precisa de alguma credencial referenciada, porque o cliente compatível com OpenAI exige um bearer token. Um valor de espaço reservado em .credentials.yaml resolve isso. E o ID do provedor (buildpc) é permanente depois que as sessões o referenciam; para renomear, adicione um novo.

Como o arquivo é a interface inteira, “mudar o dsh para o modelo local” é uma edição de duas linhas que qualquer script — ou qualquer outro agente — pode fazer. Meu agente principal redireciona o harness para um modelo local gratuito para tarefas rotineiras e de volta para o V4-Pro para problemas difíceis, sem tocar em mais nada.

Etapa 4: ao lado do Reasonix no DisPatch

Eu já tinha o Reasonix vivendo dentro do DisPatch como um pseudo-bot: clique nele e o painel de bate-papo vira um terminal executando a TUI dele sobre um PTY. Eu queria o dsh ao lado dele. O problema: o pacote oficial não tem TUI. Plugins de terceiros no estilo “Claude Code TUI” existem no npm, mas tinham quatro dias de vida com dependências workspace:* quebradas, e um pacote não revisado com acesso ao shell não vai para um servidor de família. Então o painel do dsh é construído a partir do que o pacote oficial oferece:

  • a interface web embutida em um iframe (ela não envia cabeçalhos de bloqueio de frame), mostrada apenas quando o navegador consegue alcançar o loopback do host — um celular na tailnet recebe uma explicação simples;
  • Iniciar / Reiniciar / Parar para a unit do systemd, além da saúde;
  • um menu suspenso de Modelo que reescreve agent-default-model naquele arquivo YAML;
  • uma aba Trabalhos headless: digite uma tarefa, escolha uma pasta dentro do home, pressione Executar. O servidor executa dsh --profile headless "…" como uma lista de argumentos fixa (sem shell — a tarefa é um único elemento argv, então uma tarefa contendo ; rm -rf / é apenas texto), um trabalho por vez, e mantém um histórico curto com cada resposta final.
A aba Trabalhos headless no DisPatch: uma caixa de tarefa, um campo de diretório de trabalho e três cartões de trabalhos concluídos em ~/Projects/dsh-playground — 'Concluído em 19s' (adicionar um docstring ao fizz.py e executá-lo novamente), 'Concluído em 5s' (criar primes.py, exibir 2 3 5 7 11…), 'Concluído em 5s' (listar os arquivos e descrever cada um) — cada um mostrando a tarefa e o texto da resposta
Trabalhos headless a partir do aplicativo de bate-papo. Cada cartão é uma execução de dsh --profile headless; a resposta é a última coisa que o dsh imprimiu. Três execuções reais: 5 s, 5 s, 19 s.

Tudo nesse painel fica atrás do desbloqueio de administrador do aplicativo — um trabalho headless é execução arbitrária de código, e os dispositivos familiares bloqueados nunca ficam sabendo que o painel existe. Se você conectar o dsh a algo seu, copie isso: trate “executar uma tarefa” exatamente como trata um shell.

dsh vs Reasonix, lado a lado

Ambos são agentes com formato Claude Code que cobram por token na DeepSeek. Eles diferem em quem os fez e qual superfície oferecem:

DeepSeek Harness (dsh)Reasonix
QuemDeepSeek, oficial, MITTerceiros, estilo Claude-Code
SuperfícieInterface web + one-shot headless; sem TUITUI no terminal (sessão interativa em um PTY)
Chamar de um scriptdsh --profile headless "…" — uma resposta, código de saídaFeito para um humano no comando; uso via script é incômodo
Troca de modeloEdite o settings.yaml, recarregado a quente; sem flag --modelCamadas no config.toml (flash/pro) + roteamento por skill
Modelos locaisQualquer servidor compatível com OpenAI via um bloco de providerA mesma tríplice: base URL, env da chave, ids de modelo
ExtensibilidadeTudo é um plugin (modelo, ferramentas, sandbox, loop)Subagentes, skills, memória por projeto
No meu DisPatchiframe + controles da unit + aba de trabalhos headlessTerminal xterm.js sobre um PTY
MaturidadeDeveloper preview (rc.7), quebras entre versõesv1.18, auto-atualizador, configuração estável

Na prática: o Reasonix é o que eu abro quando vou sentar com o agente; o dsh é o que meus outros softwares chamam. Essa divisão é o motivo de ambos permanecerem.

O benchmark: 5 tarefas, 3 modelos, 15/15

Nada de científico — cinco tarefas pequenas que eu realmente entregaria a um agente de codificação, cada uma em uma pasta scratch nova, cada uma verificada automaticamente (o arquivo existe e roda? os testes passam sem tocar no arquivo de teste? a renomeação deixou zero referências antigas?). O tempo total (wall time) é o processo inteiro, incluindo o system prompt de ~7.500 tokens; os tokens vêm do log de sessão do próprio dsh.

TarefaV4-FlashV4-ProGemma-4-26B (local, na máquina)
Responder "PONG" (inicialização + uma chamada)✅ 2,1 s✅ 2,8 s✅ 13,2 s*
Escrever e executar FizzBuzz✅ 5,3 s · 2 ferramentas✅ 8,4 s · 2 ferramentas✅ 4,9 s · 2 ferramentas
Corrigir 2 bugs para os testes unitários passarem (testes intactos)✅ 11,7 s · 8 ferramentas✅ 15,8 s · 7 ferramentas✅ 10,4 s · 8 ferramentas
Resumir uma base de código de 6 módulos (<150 palavras)✅ 8,9 s · 9 ferramentas✅ 10,9 s · 7 ferramentas✅ 12,1 s · 7 ferramentas
Renomear uma função em 3 arquivos + testes, comprovar que está verde✅ 15,1 s · 14 ferramentas✅ 18,2 s · 12 ferramentas✅ 12,2 s · 11 ferramentas
Tempo total (wall time)43 s56 s53 s
Tokens (entrada sem cache / leitura de cache / saída)42,6k / 136k / 4,5k41,4k / 107k / 3,2k40,5k / 237k / 5,6k
Custo nas tarifas de pico (fora do pico é a metade)≈ US$ 0,027≈ US$ 0,072US$ 0 (eletricidade)

*primeira chamada depois que o modelo foi carregado a frio na máquina; as tarefas seguintes mostram a velocidade já aquecida. Preços conforme a página de preços da DeepSeek em 2026-08-18: Flash US$ 0,014 / US$ 0,44 / US$ 1,32 por milhão (cache hit / cache miss / saída), Pro US$ 0,044 / US$ 1,32 / US$ 3,96.

O que a tabela diz:

  • O cache de prompt é o que paga a conta. Cada tarefa gasta ~7,5 mil tokens de system prompt, mas depois da primeira etapa é quase tudo leitura de cache a 3% do preço de cache miss. Tarefas de várias etapas saem baratas porque o harness mantém o prefixo estável.
  • O Pro usou menos etapas e menos ferramentas para o mesmo resultado (12 contra 14 chamadas de ferramenta na renomeação; 3 contra 4 etapas no resumo). Neste conjunto, o Flash foi mais rápido e custou um terço do preço, então continua sendo o meu padrão.
  • O modelo local se saiu bem. O Gemma-4-26B (um MoE de 4B ativos, quantização Q4, servido pelo llama.cpp em duas RTX 5090s) passou em tudo, com mais etapas e mais tokens, mas com tempo total competitivo. Foi a primeira vez que um modelo local foi uma opção real para as tarefas daqui, em vez de uma novidade — embora cinco tarefas pequenas não digam nada sobre uma refatoração de 40 arquivos.

Depois, o teste do "trabalho comum": apontei o agente para o repositório deste site e pedi que lesse o runbook do projeto, executasse o build-and-link-check e fizesse um relatório — sem publicar, sem editar. Ele leu o runbook, executou o comando certo, informou as linhas exatas da verificação (290 páginas, 1.752 imagens, nenhum link quebrado), o tempo de build e — sem que eu pedisse — notou que o runbook ainda dizia "288 páginas" e sinalizou a divergência. Cinquenta e três segundos. É esse o tipo de tarefa de baixo risco que eu passo adiante o dia inteiro.

O teste divertido: construa um brinquedo para mim

Benchmarks são uma coisa; eu também queria ver o que ele faz com um briefing criativo aberto. Então: "faça uma versão interativa em pixel art do logotipo deste site — JavaScript puro, embutível em qualquer lugar, passar o mouse faz algo físico, clicar faz algo legal, sem dependências, teste você mesmo." Aqui está o que ele construiu — está no ar, pode testar:

Passe o mouse sobre ele e clique. Os pixels se afastam do ponteiro e voltam com um efeito elástico; um clique estilhaça a marca em pixels saltitantes que encontram o caminho de volta para casa. Toque também funciona; ele respeita prefers-reduced-motion.
Quatro quadros do widget do logotipo em pixels: ocioso, pixels empurrados para os lados ao redor do cursor com um brilho azul, uma nuvem ciano de pixels quicando nas bordas logo após um clique e os pixels voltando a formar o anel com dois L
Ocioso → repulsão no hover → estilhaçamento → remontagem, capturado em Chromium headless. 61 fps, zero erros no console, e ele realmente remonta.

Como foi, honestamente:

  • Tentativa 1 (V4-Flash): um loop de 10 minutos, sem arquivos. O briefing permitia um bitmap desenhado à mão ou um procedural. Ele escolheu desenhar à mão um bitmap 40×40 dentro do próprio raciocínio e caiu em um loop degenerado — o log da sessão tem centenas de linhas de ################ / .... — até que meu timeout o encerrasse. Cerca de 2¢ desperdiçados. Lição: nunca deixe um modelo desenhar pixels à mão na cabeça.
  • Tentativa 2 (V4-Flash, briefing alterado para "rasterizar a partir da geometria, sem bitmaps"): 25 minutos, tudo entregue. 100 etapas de modelo, 201 chamadas de ferramenta, 172 mil tokens de saída (123 mil deles de raciocínio), 16,3M tokens de leitura de cache — ≈ 49¢ a preços de pico. Ele escreveu um ll-pixel-logo.js de 399 linhas com uma API de um único global e opções data-, uma página de demonstração, um README, um teste unitário Node para o rasterizador e — sem ser solicitado — um script Playwright que captura a demo em três tamanhos. Ele atingiu meu limite de 25 minutos enquanto polia o README, então o código de saída foi timeout, mas o trabalho estava concluído. Códigos de saída mentem nos dois sentidos.
  • A arte: o anel e os dois L inclinados são reconhecidos como a marca à primeira vista; eles são mais encorpados e mais parecidos com um Z do que o logotipo real, e eu passaria dez minutos ajustando as constantes de cisalhamento antes de usá-lo a sério. Não ajustei — o que você vê está intocado.
  • Incorporá-lo aqui levou uma linha porque o CSP do site é script-src 'self' e o widget não faz nenhuma requisição de rede. Isso estava no briefing; ele honrou isso.

Gotchas

  • Prévia para desenvolvedores, e isso está em letras maiúsculas. 0.1.0-rc.7 na instalação; a rc.6 foi três dias antes. Perfis, chaves de configuração e o layout de plugins podem mudar. Fixe a versão em qualquer automação e rode um teste de fumaça após cada atualização.
  • O modo headless é silencioso até terminar. Nada é transmitido para o stdout — uma tarefa longa parece travada. Leia o log da sessão (~/.dsh/sessions/…/session.jsonl.zstd, JSONL comprimido com zstd) ou acompanhe-o na interface web. E o código de saída 0 significa "a rodada foi concluída", não "a tarefa foi bem-sucedida". Confira o resultado.
  • Não há flag --model no modo headless. O modelo padrão vem do settings.yaml; altere-o lá (hot-reload) ou na interface.
  • Servidores locais sem chave de API precisam de uma credencial fictícia referenciada por apiKeyEnv, e os IDs de provedores são permanentes. Já abordamos isso acima; vai te morder.
  • Os assets da interface web usam caminhos absolutos (/assets/…, /api), então não dá para montá-la em um subcaminho atrás do seu próprio proxy reverso sem reescrever; incorpore-a ou dê a ela um hostname próprio.
  • A localidade da interface segue o seu navegador — o index.html enviado diz lang="zh-CN", e a primeira coisa que você vê é uma caixa de diálogo "Internal Testing Notice". Clique em Continuar; tudo depois disso estava em inglês para mim.
  • A instalação executa scripts pós-instalação (node-pty, koffi, protobufjs). O npm avisa sobre isso. Nada malicioso que eu pudesse ver, mas é código nativo compilando no seu prefixo — mais um motivo para mantê-lo em um prefixo privado, não no do sistema.
  • Apenas Node 22.19+ ou 24. Versões LTS mais antigas se recusam a executá-lo.

Onde isso me deixa

Ganhou seu lugar em uma tarde. O modo headless é a forma certa para um agente de codificação que outros softwares chamam — um comando, uma resposta, um código de saída, um log auditável — e a definição do modelo por YAML significa que minha stack de agentes pode apontá-lo para qualquer cérebro que sirva para o trabalho. Se você já executa um servidor de modelo local, experimente as mesmas cinco tarefas; dez centavos de crédito de API e uma pasta de rascunho são tudo o que é preciso.

Relacionados: Reasonix (o outro agente de codificação deste artigo), DeepSeek Everywhere (integrando DeepSeek ao Claude Code e a uma stack de agentes), DisPatch (o aplicativo de chat onde o painel está), e bench-llm (avaliando modelos locais com mais rigor do que fiz aqui).


← Mais de IA e LLM Local