Claude Code Self hosted e sem a Anthropic: KV Cache, telemetria silenciosa, e a importância de controlar as ferramentas que usamos

Olá a todos!

Full disclaimer: Sou um utilizador assíduo do Claude Code para as minhas experiencias. Assumo-me como uma nulidade como programador, e preciso de ajuda em alguns pontos. Quando a segurança dos dados não é a parte mais fundamental, recorro ao Claude Code sem nenhuma vergonha ou pudor.

Hoje venho falar de algo que descobri num post do r/LocalLLaMA há uns dias e que me fez parar o que estava a fazer durante uns bons minutos, simplesmente a contemplar o absurdo da situação. Não porque seja tecnicamente surpreendente — quem conhece bem como estas ferramentas funcionam por baixo vai encolher os ombros e dizer “claro que sim.” Mas porque é o tipo de coisa que passa completamente despercebida, custa dinheiro real e tempo real às pessoas, e ninguém nos avisa.

A história começa com um developer frustrado, um GPU a suar, e um contador de sessenta segundos que não devia existir.

O problema…

O utilizador One-Cheesecake389 descobriu que ao usar o Claude Code contra um backend local — neste caso o llama-server, que é o servidor de inferência do llama.cpp — cada tool call simples estava a demorar mais de um minuto. Não era lentidão do modelo. Não era o hardware. Era o facto de que o llama-server estava a reprocessar mais de vinte mil tokens de sistema do zero, em cada chamada individual.

Para perceber porque é que isso acontece, é preciso entender como o KV cache funciona no llama.cpp.

O llama.cpp — e o llama-server por extensão — guarda em memória os resultados dos cálculos de atenção para os tokens que já processou. Na prática, isto significa que se o início de uma conversa é sempre o mesmo — o system prompt, as instruções do agente, a lista de ferramentas disponíveis — esse trabalho é feito uma vez e fica em cache. As chamadas seguintes só processam os novos tokens, o delta. Em vez de processar vinte e quatro mil tokens para responder a uma chamada de cem tokens, o servidor processa cem. É a diferença entre um segundo e um minuto.

A condição para que isto funcione é simples: os tokens do início da sequência têm de ser exatamente iguais. Exatamente. Um único carácter diferente no início e o cache invalida, e o servidor começa do zero.

E é aqui que o Claude Code entra como a pés juntos na grande area…

O que o Caude Code faz a cada pedido

A partir da versão 2.1.36, o Claude Code faz duas coisas que mutam o system prompt em cada chamada:

Primeiro, o telemetry hash. É injetado um header de billing — x-anthropic-billing-header: cch=xxxxx — cujo valor muda em cada pedido. Não é o mesmo valor. É diferente sempre. Do ponto de vista do servidor de inferência local, é como se o utilizador tivesse mudado de identidade entre cada frase.

Segundo, o git snapshot. O Claude Code injecta o output de git status no bloco de ambiente do system prompt. Cada vez que um ficheiro é tocado — e num workflow de desenvolvimento com um agente a escrever código, os ficheiros são tocados constantemente — o snapshot muda, o prompt muda, e o cache invalida.

O resultado é que numa sessão de trabalho normal, com um agente a fazer operações de leitura e escrita em ficheiros e a correr testes, o sistema está a reprocessar o system prompt completo dezenas de vezes. Vinte e quatro mil tokens em cada chamada. Numa Quadro RTX-8000 — que não é hardware de brincadeira, é uma GPU de workstation de geração Turing com quarenta e oito gigabytes de VRAM — cada reprocessamento demorava mais de sessenta segundos.

Sessenta segundos para cada tool call. Num agente que faz dezenas de tool calls por tarefa.

A matemática é dolorosa.

A correção é surpreendentemente simples

Quem encontrou o problema também encontrou a solução, e tem o mérito de ser elegante. No ficheiro ~/.claude/settings.json, basta adicionar o seguinte bloco:

{
  "includeGitInstructions": false,
  "env": {
    "ANTHROPIC_BASE_URL": "<o-vosso-servidor-aqui>",
    "ANTHROPIC_API_KEY": "<qualquer-string>",
    "CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
    "DISABLE_TELEMETRY": "1",
    "DISABLE_ERROR_REPORTING": "1",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  }
}

Com estas configurações, o telemetry hash deixa de ser injetado, o git snapshot deixa de ser injetado, e o system prompt passa a ser estável entre chamadas. O resultado reportado é eloquente: em vez de processar vinte e quatro mil tokens do zero, o llama-server reconhece 97,3% do prompt como idêntico ao da chamada anterior e processa apenas um delta de seiscentos tokens. De sessenta segundos para quatro segundos. Na mesma GPU. Sem tocar no modelo. Sem alterar o hardware. Simplesmente deixando o cache fazer o seu trabalho.

Há um detalhe importante aqui que merece atenção. A nota ANTHROPIC_BASE_URL não é só para quem usa inferência local — é o mecanismo que permite apontar o Claude Code para qualquer endpoint compatível com a API da Anthropic. Pode ser o llama-server. Pode ser o LM Studio. Pode ser uma instância do Ollama. Pode ser qualquer proxy ou gateway que exponha a API no formato correto. O Claude Code, como cliente, não sabe nem precisa de saber onde está a correr o modelo.

Isto é importante. Vou voltar a este ponto.

Mas porquê?  Qual o motivo?

Há uma explicação técnica benigna para ambos os comportamentos.

O telemetry hash existe porque a Anthropic, quando o Claude Code corre contra os servidores deles, precisa de associar cada pedido a uma sessão de billing. O hash muda porque não seria útil para o billing ter um identificador fixo que não distingue pedidos. Do ponto de vista da Anthropic, este header é perfeitamente razoável — os seus servidores não usam KV cache baseado em prefix matching da mesma forma que o llama.cpp, têm infraestrutura dedicada com prompt caching explicitamente configurável.

O git snapshot existe porque dar ao modelo contexto sobre o estado atual do repositório faz sentido para um agente de coding. Saber que há ficheiros modificados, que há ficheiros untracked, que o working tree não está limpo — isso é informação relevante. Para os servidores da Anthropic, este contexto tem valor.

O problema é que o Claude Code foi claramente desenhado a pensar nos servidores da Anthropic como destino. A ideia de que alguém podia querer usar a mesma ferramenta contra um backend local, onde prefix-matching é a mecânica de caching, parece ter ficado de fora do design.

Não é sabotagem deliberada. É simplesmente o mundo a partir do princípio de que a infraestrutura da empresa é a única infraestrutura que existe. Um problema de perspetiva mais do que de intenção.

A parte que mais me interessa: Usar outros modelos com o Claude Code :]

Agora chegamos à parte que acho genuinamente importante e que não está a receber atenção suficiente.

O Claude Code não é apenas um cliente para os modelos da Anthropic. É uma harness de agente para coding — uma das mais capazes disponíveis neste momento, com um ciclo de desenvolvimento ativo e uma comunidade que lhe dá suporte. A interface, o loop de agente, a forma como gere ferramentas e contexto — isso é valioso independentemente do modelo que corre por baixo.

E com a configuração ANTHROPIC_BASE_URL, é possível apontar essa harness para qualquer modelo que se tenha a correr localmente. Qwen2.5-Coder. DeepSeek-Coder-V3. Llama 3.3. Code Llama. Qualquer modelo que se sirva via llama-server, LM Studio, ou Ollama numa porta local.

E porquê? Por várias razões, e algumas delas são mais importantes do que podem parecer à primeira vista.

A questão da privacidade dos Clientes

Esta é provavelmente a razão mais imediata e a que devia preocupar mais qualquer pessoa que trabalhe com clientes.

Quando se usa o Claude Code no seu modo padrão, a trabalhar contra os servidores da Anthropic, todo o contexto da sessão de trabalho passa pelos servidores deles. O código que se está a escrever. As mensagens de erro. As estruturas de ficheiros. Os comentários. Os nomes de variáveis que revelam a arquitetura do sistema. Os paths que revelam como o projeto está organizado. A lógica de negócio que está a ser implementada.

Para um developer que trabalha num projeto pessoal ou open source, isto pode não ser problemático. Mas para quem trabalha com clientes — empresas, organizações, projetos com requisitos de confidencialidade — a situação muda completamente.

Pensem no que passa pelo contexto de uma sessão de Claude Code numa semana de trabalho normal: nomes de clientes em comentários, arquitetura de sistemas proprietários, lógica de negócio específica do setor, estruturas de base de dados que revelam como uma empresa organiza a informação, detalhes de integração com sistemas de terceiros. Em muitos contextos, enviar tudo isto para servidores externos é incompatível com os acordos de confidencialidade que assinámos, com os contratos com clientes, com as políticas de segurança das organizações onde trabalhamos.

Com um modelo selfhosted a correr localmente, nada disto sai da máquina ou do nosso datacenter. O contexto fica no hardware da empresa ou do developer. Não há política de privacidade de terceiros que se aplique. Não há logs em servidores externos. Não há forma de a informação ser usada para treinar modelos futuros sem consentimento explícito.

Isto não é paranoia. É simplesmente tomar a sério os compromissos que assumimos com as pessoas que confiam em nós.

A questão da Soberania Digital

Há uma dimensão mais ampla aqui que vai além da privacidade individual ou organizacional, e que acho cada vez mais importante articular claramente.

A soberania digital é a capacidade de uma organização — seja uma empresa, uma agência pública, ou um estado — controlar a sua própria infraestrutura de informação. Não depender de terceiros para capacidades críticas. Não estar sujeita a mudanças unilaterais de política de um fornecedor. Não ter a continuidade das operações dependente de uma relação comercial que pode terminar ou mudar de termos a qualquer momento.
No contexto da IA, isto é particularmente relevante porque as ferramentas de IA estão rapidamente a tornar-se infraestrutura crítica. Um developer que depende de uma ferramenta de AI coding para ser produtivo está, em certa medida, dependente do fornecedor dessa ferramenta. Se o fornecedor muda os preços, altera os termos de serviço, decide introduzir limitações, ou simplesmente tem uma interrupção de serviço — o developer fica pendurado.
O mesmo se aplica a organizações que usam IA em produção. Uma empresa que construiu os seus workflows em torno de um modelo específico num serviço cloud está, em certa medida, refém da disponibilidade e das políticas desse serviço. Não é uma dependência com a qual ninguém possa viver — há serviços cloud excelentes e não há nada de fundamentalmente errado em usá-los. Mas é uma dependência que deve ser uma escolha consciente, não uma situação em que se acabou por cair por omissão.
Modelos open weights — os modelos que se podem descarregar, correr localmente, e modificar — são a resposta a esta dependência. E o facto de existirem ferramentas como o Claude Code que podem ser redirecionadas para estes modelos sem perda de funcionalidade da harness é genuinamente importante. Significa que a camada de ferramentas pode ser separada da camada de inferência. Pode-se beneficiar de uma harness de agente bem desenvolvida sem estar amarrado ao fornecedor do modelo por baixo.

Selfhosted/On-Prem: Mais acessível do que parece

Uma objeção comum a esta conversa é que selfhosting de modelos é tecnicamente complexo e requer hardware especializado. Há uma parte de verdade nisto — um modelo de setenta bilhões de parâmetros precisa de hardware sério para correr razoavelmente. Mas a paisagem de modelos locais evoluiu de forma surpreendente nos últimos dois anos.
Modelos como o Qwen2.5-Coder de sete bilhões de parâmetros correm confortavelmente em GPUs de gaming relativamente acessíveis — uma RTX 3060 de doze gigabytes, por exemplo, corre este modelo com performance razoável para tarefas de coding. O DeepSeek-Coder-V2 Lite de dezasseis bilhões corre em GPUs de dezasseis gigabytes. Há modelos quantizados que funcionam perfeitamente em hardware que muita gente já tem.
Para quem não tem GPU adequada, há a opção de um servidor dedicado — um mini PC com uma GPU discreta, uma máquina dentro da rede da empresa, ou uma instância de cloud em que se tenha total controlo e cuja localização geográfica seja conhecida e controlada. Para contextos europeus com obrigações GDPR, esta última opção tem relevância adicional: saber onde os dados estão processados geograficamente é um requisito real em muitos contextos.
O llama-server tem uma API que é compatível com o formato OpenAI, o que significa que funciona com uma lista crescente de clientes — incluindo, com a configuração que vimos, o Claude Code. O LM Studio tem uma interface gráfica que torna o processo de descarregar e servir modelos acessível a pessoas sem experiência em linha de comando. O Ollama é talvez a opção mais simples para começar — ollama run qwen2.5-coder é literalmente tudo o que é preciso escrever para ter um modelo de coding a correr localmente.

Ainda um comentário sobre o ecossistema de alternativas

Há um comentário no thread original que merece atenção. Um utilizador — coder543 — questiona a obsessão com o Claude Code quando existem alternativas open source: codex, opencode, crush, gemini-cli, vibe. É uma crítica legítima e merece uma resposta honesta.
As alternativas open source têm vantagens reais. São transparentes em termos do que fazem com o contexto. Não injetam headers de telemetria. Não têm comportamentos que mudam com atualizações de versão de formas não documentadas. São extensíveis.
A razão pela qual o Claude Code continua a ser relevante para quem já o usa não é porque seja intrinsecamente superior — é porque o loop de agente, a gestão de ferramentas, e a qualidade da experiência de uso estão num ponto muito maduro. Para quem já tem workflows construídos em torno dele, a mudança tem um custo real de aprendizagem e adaptação.

Mas — e este é um ponto importante — a lógica de redirecionar o Claude Code para modelos locais como forma de preservar privacidade não é mutuamente exclusiva com eventualmente migrar para uma harness mais aberta. São passos diferentes no mesmo caminho. O passo imediato — redirecionar para local — tem um custo próximo de zero e benefícios imediatos. A migração para uma harness open source é uma conversa separada com os seus próprios trade-offs.

O padrão que aqui (novamente) se repete

Este incidente insere-se num padrão que tenho observado com crescente preocupação. Escrevi aqui antes sobre o GitHub e o treino de modelos com código de repositórios privados. Escrevi sobre Cline e tokens comprometidos a tornarem-se vetores de supply chain. A história é sempre variações do mesmo tema: ferramentas que se tornaram infraestrutura crítica para o trabalho de desenvolvimento têm comportamentos não documentados que afetam privacidade, performance, ou segurança, e a maioria das pessoas não sabe porque ninguém as avisa de forma clara.

A resposta a este padrão não é paranoia generalizada — é uma combinação de ceticismo saudável sobre o que as ferramentas fazem com o nosso contexto, preferência por ferramentas cujo comportamento é auditável, e investimento gradual em infraestrutura que se controla.

Selfhosting/on-premnão é para todos e não tem de ser. Mas como comunidade técnica, devemos pelo menos ter a conversa de forma honesta: o que é que as ferramentas que usamos fazem com a informação que lhes passamos? Qual é o modelo de negócio por trás delas? Onde estão os dados? Quem tem acesso?

Para quem trabalha com clientes e tem responsabilidades fiduciárias em relação à informação que manuseia, estas não são perguntas filosóficas. São perguntas operacionais.
E quem quiser mas não conseguir, existem empresas para vos ajudar. Se estão interessados, perguntem-me quem.

Como começar hoje

Para quem quiser experimentar a configuração descrita no post original, aqui está o caminho mais curto:

Passo 1: Instalar o Ollama a partir de ollama.com. É provavelmente o caminho de menor resistência para começar.

Passo 2: Puxar um modelo de coding. ollama pull qwen2.5-coder:7b é um bom ponto de partida se estiverem com recursos limitados. Para hardware mais capaz, qwen2.5-coder:32b ou deepseek-coder-v2:16b têm performance muito mais próxima dos modelos frontier.

Passo 3: Verificar que o Ollama está a servir na porta local. Por defeito é http://localhost:11434, mas o endpoint compatível com OpenAI fica em http://localhost:11434/v1.

Passo 4: Criar ou editar o ficheiro ~/.claude/settings.json com:

{
  "includeGitInstructions": false,
  "env": {
    "ANTHROPIC_BASE_URL": "http://localhost:11434/v1",
    "ANTHROPIC_API_KEY": "ollama",
    "CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
    "DISABLE_TELEMETRY": "1",
    "DISABLE_ERROR_REPORTING": "1",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  }
}

Passo 5: Reiniciar o Claude Code e observar a diferença nos tempos de resposta.

Uma nota: a qualidade das respostas vai depender do modelo que escolherem. Os modelos locais de menor dimensão não atingem a qualidade dos modelos frontier para tarefas complexas. Mas para tarefas mais simples — leitura de código, pequenas alterações, geração de boilerplate — a diferença pode ser surpreendentemente pequena, especialmente com modelos de coding especializados como o Qwen2.5-Coder.

Fechando

Há algo ligeiramente absurdo no facto de que a solução para um problema de performance num cliente de agente de AI é desligar a telemetria de billing. Mas é também, de certa forma, revelador.

As ferramentas que usamos foram desenhadas com um conjunto de pressupostos sobre o contexto em que são usadas. Quando saímos desse contexto — quando decidimos que preferimos controlar onde os nossos dados são processados, quando temos obrigações de confidencialidade que limitam o que podemos enviar para servidores externos, quando simplesmente queremos que o KV cache do llama.cpp funcione como foi desenhado — é preciso às vezes olhar por baixo do capot e perceber o que está realmente a acontecer.
A boa notícia é que o capot é aberto. A configuração existe. Os modelos locais existem e são bons. A infraestrutura para os correr está acessível a quem quiser investir o tempo e, em alguns casos, o hardware.
A soberania digital não é uma proposição de tudo ou nada. É uma direção. Cada passo nessa direção — cada ferramenta cujo comportamento se entende melhor, cada componente da stack que se coloca sob controlo próprio, cada dado que fica na rede da organização em vez de viajar para servidores externos — é um passo que vale a pena dar.
Sessenta segundos por tool call é um preço alto a pagar por não ler o ficheiro de configuração. Mas é também uma oportunidade de perceber que a alternativa existe, funciona, e tem benefícios que vão muito além da performance.

Um abraço, Nuno

P.S.: Para quem quiser ir além do Ollama e configurar um llama-server mais otimizado com suporte a múltiplos slots de KV cache — que permite múltiplos agentes a partilhar o mesmo backend sem colidirem — deixem um comentário. É exatamente o tipo de how-to que gosto de escrever.

P.P.S.: Se usarem o LM Studio em vez de Ollama, o processo é idêntico mas com uma interface gráfica para descarregar e servir modelos. O endpoint fica em http://localhost:1234/v1 por defeito. A configuração do settings.json é a mesma.