Self-Hosted AI Coding: A Solução Inteligente Para Combater Alucinações

Olá a todos!
Perguntam-me frequentemente: “Nuno, porque é que insistes em soluções self-hosted quando temos o GitHub Copilot, ChatGPT e outros assistentes de programação na nuvem tão avançados?”
A resposta é simples: alucinações. E não, não estou a falar de algo que se resolve com mais café ou uma boa noite de sono.
Vi recentemente desenho de arquiteturas self hosted para combater o problema e trago-vos uma que depois de experimentar me pareceu bastante boa.

O Problema Real das Alucinações em Código

Vamos ser brutalmente honestos: as alucinações em IA são o elefante na sala que todos fingimos não ver. É impossível preveni-las completamente – são literalmente um bug, não uma feature, do funcionamento dos modelos de linguagem modernos. E quando estamos a falar de código? Vibe coding ftw?!

O Código que Mente (E Mente Bem)

Já passaram por aquela situação em que o Copilot vos sugere uma função que parece saída diretamente do manual da API? Perfeita, elegante, exatamente o que precisavam? E depois passam duas horas a descobrir que essa função nunca existiu na vida real? Bem-vindos ao clube.
O problema das alucinações em programação não é só irritante – é perigoso. Estamos a falar de:

Funções fantasma: APIs que parecem reais mas são pura ficção científica. O modelo viu padrões, extrapolou, e criou uma realidade paralela onde essa função existe.
Lógica traiçoeira: Código que compila na boa, passa nos testes básicos, mas depois falha espetacularmente em produção. É como ter um carro que arranca bem no stand, mas as rodas caem quando entram na autoestrada.
Padrões do Jurássico: Sugestões baseadas em versões de bibliotecas que já têm barbas. Resultado? Código que introduz vulnerabilidades que já foram resolvidas há anos.
O cavalo de Tróia digital: O mais insidioso de todos – código que parece inocente mas introduz falhas de segurança que só são descobertas quando já é tarde demais.

O Verdadeiro Problema

O pior de tudo isto? Este código parece correto. Não é como aqueles erros óbvios que qualquer júnior apanha. Não, este código tem boa pinta, segue convenções, até pode ter comentários explicativos. É o crime perfeito do mundo digital.
E aqui surge a pergunta que não quer calar: quantos de nós já fizeram “vibe coding” com base numa sugestão de IA? Aquela programação por intuição, onde confiamos que a máquina sabe o que está a fazer porque, honestamente, também nós não sabemos bem?

A Realidade Nua e Crua

As alucinações não vão desaparecer. Fazem parte do ADN destes modelos. São o preço que pagamos por ter assistentes de código que conseguem entender contexto e gerar soluções criativas.
Mas isso não significa que devemos aceitar passivamente. Significa que temos de ser mais espertos, mais céticos, mais… humanos no nosso approach à programação assistida por IA.
Porque no final do dia, o código pode mentir, mas a realidade não perdoa.

Então Porque é que Self-Hosted é a Resposta?

Quais os pré requisitos fundamentais para ter o controle sobre a alucinação? O que podemos fazer?

1. Controlo Total dos Dados

Primeira razão óbvia: o nosso código proprietário nunca sai da nossa infraestrutura. Zero. Nada. Com as recentes decisões judiciais sobre preservação de dados, isto não é paranóia – é necessidade.
Seja com uma KB conforme falamos no post anterior sobre MCP, seja com documentação interna a nossa empresa ou startup.

2. Fine-Tuning com o Nosso Context

Aqui é onde a coisa fica interessante. Podem treinar o modelo com:

  • Codebase interno – os nossos padrões e convenções específicas
  • Bibliotecas proprietárias – APIs e frameworks que desenvolvemos internamente
  • Histórico de bugs – padrões que já causaram problemas antes
  • Standards organizacionais – as nossas melhores práticas

3. Validação em Tempo Real

A magia acontece quando integram o sistema com o vosso ambiente de desenvolvimento:

class CodeValidator:
    def __init__(self, project_context):
        self.project_context = project_context
        self.syntax_checker = SyntaxChecker()
        self.semantic_analyzer = SemanticAnalyzer()
    
    def validate_suggestion(self, code_suggestion):
        # Verificação sintática
        if not self.syntax_checker.is_valid(code_suggestion):
            return ValidationResult.SYNTAX_ERROR
        
        # Verificação semântica contra o contexto local
        if not self.semantic_analyzer.analyze(code_suggestion, self.project_context):
            return ValidationResult.SEMANTIC_ERROR
        
        # Verificação contra bibliotecas instaladas
        if not self.check_compatibility(code_suggestion):
            return ValidationResult.COMPATIBILITY_ERROR
        
        return ValidationResult.VALID

A Arquitetura Anti-Alucinação

A chave para combater alucinações está numa arquitetura em camadas onde cada nível adiciona uma barreira de proteção. Pensem nisto como um sistema de defesa em profundidade – se uma camada falha, as outras apanham o erro. Assim sendo, temos o seguinte esquema:

Camada 1: O Modelo Local (O Cérebro)
Esta é a base de tudo – o modelo de linguagem que gera as sugestões. A escolha aqui determina 80% do sucesso do sistema.

CodeLlama (7B-34B parâmetros)

  • Pontos fortes: Excelente para Python, C++, JavaScript. Fine-tuning relativamente simples
  • Recursos: 7B precisa de 8GB VRAM mínimo, 34B precisa de 24GB+
  • Uso prático: Ideal para equipas que trabalham principalmente com estas linguagens
  • Licença: Permite uso comercial com restrições menores

StarCoder (3B-15B parâmetros)

  • Pontos fortes: Suporte para 80+ linguagens, treinado em código open-source de qualidade
  • Recursos: Mais eficiente em GPU, 15B funciona bem com 16GB VRAM
  • Uso prático: Melhor escolha para equipas polyglot ou com linguagens menos comuns
  • Licença: OpenRAIL, permite uso comercial

Tabby (1B-3B parâmetros)

  • Pontos fortes: Setup plug-and-play, interface web incluída, consume menos recursos
  • Recursos: Funciona até em RTX 3080, 3B modelo precisa apenas 6GB VRAM
  • Uso prático: Perfeito para começar ou equipas pequenas
  • Licença: Apache 2.0, totalmente livre

Qwen-Coder (30B parâmetros)

  • Pontos fortes: Extremamente afinado para o pretendido. Excelente em código complexo, com contextos longos.
  • Recursos: Funciona até em duas RTX 4060ti com 16GB de VRAM, modelo precisa de 30GB VRAM dependendo do tamanho do contexto.
  • Uso prático: Perfeito para equipas avançadas
  • Licença: Apache 2.0, totalmente livre

A escolha prática: Para a maioria das equipas, recomendo começar com Tabby 3B. Se tiverem uma RTX 4090 ou superior, experimentem o CodeLlama 13B.

Camada 2: Validação Contextual (O Conhecimento Local)
Esta camada é onde a magia começa a acontecer – é aqui que o sistema “aprende” sobre o vosso projeto específico e pode detetar quando o modelo está a alucinar baseado no vosso contexto real.

Parser de Documentação Local

class DocumentationParser:
    def __init__(self, project_root):
        self.project_root = project_root
        self.api_registry = {}
        self.type_definitions = {}
        
    def parse_local_apis(self):
        # Scanning de todos os ficheiros Python/JS/etc
        for file_path in self.scan_source_files():
            # Extrai definições de funções, classes, tipos
            definitions = self.extract_definitions(file_path)
            self.api_registry.update(definitions)
    
    def validate_function_call(self, function_name, context):
        if function_name not in self.api_registry:
            return ValidationError(f"Function {function_name} doesn't exist in codebase")
        return ValidationSuccess()

Integração com Schemas de Base de Dados

O sistema conecta-se às nossas bases de dados (de novo referencia ao posts anteriores) e mantém um registo atualizado de:

  • Tabelas e colunas existentes – evita sugestões de campos que não existem
  • Relacionamentos entre tabelas – sugere JOINs corretos baseados nas foreign keys reais
  • Índices disponíveis – otimiza queries sugeridas baseado nos índices existentes
  • Tipos de dados – evita erros de casting ou comparações inválidas

Análise de Contratos de API Para microserviços e APIs externas:

  • OpenAPI/Swagger specs – valida chamadas contra especificações reais
  • GraphQL schemas – verifica queries e mutations possíveis
  • gRPC definitions – valida service calls e message types
  • Histórico de respostas – aprende padrões reais de dados retornados

Gestão de Dependências em Tempo Real

Finalmente e para manter tudo coerente chegamos a esta parte:

class DependencyTracker:
    def __init__(self, package_files):
        self.installed_packages = self.parse_requirements(package_files)
        self.version_constraints = {}
    
    def validate_import(self, import_statement):
        package_name = self.extract_package(import_statement)
        if package_name not in self.installed_packages:
            return ValidationError(f"Package {package_name} not in requirements.txt")
        
        # Verifica se a versão instalada suporta a funcionalidade
        if not self.check_version_compatibility(package_name, import_statement):
            return ValidationError(f"Function not available in installed version")
        
        return ValidationSuccess()

Camada 3: Verificação Automática (O Laboratório de Testes)

Esta é a camada mais técnica mas também a mais importante – é aqui que testamos tudo antes de sugerir ao developer.

Compilação e Análise Sintática Antes de qualquer sugestão chegar ao IDE, o sistema:

  • Compila o código num ambiente sandboxed
  • Verifica sintaxe específica da linguagem
  • Analisa imports e dependências
  • Deteta erros óbvios como variáveis não definidas

Execução de Testes Unitários O sistema mais avançado vai ainda mais longe:

class SuggestionTester:
    def __init__(self, test_runner):
        self.test_runner = test_runner
        self.sandbox = CodeSandbox()
    
    def test_suggestion(self, code_suggestion, affected_functions):
        # Aplica a sugestão num ambiente isolado
        modified_code = self.apply_suggestion(code_suggestion)
        
        # Executa testes relacionados
        test_results = self.test_runner.run_tests_for(affected_functions)
        
        if test_results.failed_count > 0:
            return ValidationError("Suggestion breaks existing tests")
        
        # Executa testes de performance simples
        perf_impact = self.measure_performance_impact(modified_code)
        if perf_impact.degradation > 0.2:  # >20% slower
            return ValidationWarning("Suggestion may impact performance")
            
        return ValidationSuccess()

Análise Estática Avançada Usando ferramentas como:

  • ESLint/TSLint para JavaScript/TypeScript
  • Pylint/Flake8 para Python
  • SonarQube para análise multi-linguagem
  • Semgrep para deteção de padrões de segurança
  • PuppetPDK para análise de codigo puppet

Verificação de Segurança Automática

class SecurityValidator:
    def __init__(self):
        self.security_rules = [
            NoHardcodedSecretsRule(),
            NoSQLInjectionRule(),
            NoXSSVulnerabilitiesRule(),
            NoUnsafeDeserializationRule()
        ]
    
    def validate_security(self, code_suggestion):
        violations = []
        for rule in self.security_rules:
            if rule.violates(code_suggestion):
                violations.append(rule.get_violation_message())
        
        if violations:
            return SecurityViolation(violations)
        
        return SecurityClearance()

Camada 4: Feedback Loop (A Aprendizagem Contínua)

Esta camada é essencial – o sistema tem de aprender com os erros.

Coleta de Métricas de Utilização

  • Que sugestões são aceites vs rejeitadas
  • Quanto tempo developers demoram a modificar sugestões
  • Que tipos de erro são mais comuns
  • Padrões de utilização por equipa/projeto

Refinamento Automático do Modelo Com base no feedback:

  • Re-ranking de sugestões baseado em aceitação histórica
  • Fine-tuning periódico com exemplos específicos da organização
  • Ajuste de confidence thresholds para diferentes tipos de código
  • Personalização por developer (alguns preferem sugestões mais conservadoras)

Sistema de Alertas Inteligente

class FeedbackCollector:
    def record_suggestion_outcome(self, suggestion_id, outcome, developer_id):
        # Regista se foi aceite, modificada, ou rejeitada
        self.metrics_db.insert({
            'suggestion_id': suggestion_id,
            'outcome': outcome,  # ACCEPTED, MODIFIED, REJECTED
            'developer_id': developer_id,
            'timestamp': datetime.now(),
            'context': self.get_code_context(suggestion_id)
        })
        
        # Analisa padrões para melhorar sugestões futuras
        if outcome == 'REJECTED':
            self.analyze_rejection_reason(suggestion_id)

Esta arquitetura em camadas garante que mesmo quando o modelo base alucina, o sistema tem múltiplas oportunidades para detetar e corrigir o erro antes de chegar ao developer. É como ter vários pares de olhos a rever cada sugestão – mas de forma automática e em segundos.

Use Cases  Onde Isto Brilha

Refactoring de Legacy Code

Tenho visto equipas a usar isto para:

  • Identificar padrões obsoletos em codebases antigos
  • Sugerir alternativas modernas mantendo compatibilidade
  • Validar que as mudanças não quebram funcionalidade existente

Nota: Cuidado com divida técnica, apenas acrescentem ou modifiquem o vosso código legacy se souberem o que estão a escrever. Não assumo responsabilidades por vibe coding irresponsável.

Desenvolvimento de APIs

Para equipas que criam APIs (sim estou a olhar para ti P):

  • Validação automática contra especificações OpenAPI
  • Sugestões baseadas em contratos existentes
  • Verificação de consistência entre documentação e implementação

Microserviços

Em arquiteturas distribuídas:

  • Garantir consistência entre serviços
  • Validar contratos de comunicação
  • Sugerir padrões de resiliência específicos da organização

Os Custos Reais (Que Ninguém Conta)

Chegamos ao elefante na sala. Os custos…. tem custos. Quanto melhor fizermos a nossa torre melhor a teremos de suportar e melhores materiais teremos de utilizar para garantir a sua estabilidade.

Recursos Computacionais

  • Hardware especializado – GPUs para inferência eficiente (mínimo RTX 4090 ou equivalente)
  • Armazenamento – modelos ocupam entre 7GB a 70GB+
  • RAM – pelo menos 32GB para modelos médios
  • Largura de banda – para sincronização e atualizações

Conhecimento Especializado

É necessário que alguém na equipa perceba de:

  • MLOps – gestão de modelos em produção
  • DevOps – automação e monitorização
  • Análise de dados – interpretação de métricas de performance

Manutenção Contínua

  • Atualizações de modelos (releases trimestrais)
  • Patches de segurança
  • Evolução da base de conhecimento local
  • Monitorização e ajuste fino contínuo

Métricas que Importam

Não basta implementar – as métricas tem de ser medidas, e baseadas nelas, efetuar ajustes.

Qualidade do Código:

  • Taxa de sugestões que compilam sem erro
  • Percentagem de sugestões aceites pelos developers
  • Redução de bugs em comparação com período anterior

Performance do Sistema:

  • Tempo de resposta das sugestões (target: <2 segundos)
  • Throughput (sugestões por minuto)
  • Utilização de recursos (não pode consumir toda a GPU)

Adopção pela Equipa:

  • Quantos developers usam regularmente
  • Feedback qualitativo sobre utilidade
  • Tipos de tarefas onde é mais usado

Alternativas Mais Simples (Para Começar)

Se isto parece demasiado complexo, há opções intermédias:

Tabby – Self-hosted coding assistant com setup mais simples Code Server – VS Code no browser com extensões de IA local Continue.dev – Plugin que funciona com modelos locais LocalAI – API compatível com OpenAI mas executado localmente.

A Realidade: Vale a Pena?

Vou ser brutalmente honesto: não é para toda a gente.
Vale a pena se:

  • Trabalham com código altamente sensível
  • Têm requisitos rigorosos de compliance
  • A equipa tem 10+ developers
  • Já têm infraestrutura GPU disponível
  • Há budget para conhecimento especializado
  • Tem gosto pela coisa

Não vale a pena se:

  • São uma startup pequena com recursos limitados
  • O código não é particularmente sensível
  • Não têm capacidade para MLOps interno
  • Preferem focar recursos no produto core

Começar com Uma Implementação Piloto

A minha recomendação: começem pequeno.

  1. Escolham um projeto interno não-crítico
  2. Instalem o Tabby numa máquina com GPU decente
  3. Integrem com um IDE (VS Code é o mais fácil)
  4. Meçam tudo durante 2-3 meses
  5. Avaliem os resultados antes de escalar

O Futuro (Que Já Está Aqui)

Os modelos estão a ficar mais eficientes:

  • Quantização reduz o tamanho sem perder qualidade
  • Modelos especializados para domínios específicos
  • Integração mais profunda com ferramentas de desenvolvimento

Mas a tendência é clara: quem controla os dados, controla a qualidade.

Conclusão: Controlo vs Conveniência

As soluções cloud são convenientes, mas vêm com riscos ocultos que muitas equipas só descobrem tarde demais. As soluções self-hosted requerem mais trabalho inicial, mas oferecem:

  • Privacidade garantida dos nossos dados
  • Controlo total sobre a qualidade das sugestões
  • Customização para o nosso contexto específico
  • Custos previsíveis a longo prazo

A pergunta não é se as alucinações vão acontecer – é se vão estar preparados quando acontecerem. Para equipas que levam a sério a qualidade do código e a segurança dos dados, self-hosted não é luxo – é necessidade.
Se precisarem de ajuda na implementação, há empresas especializadas que nos podem orientar neste processo.

Até ao próximo post da semana, quase quase de férias e com esperança que não alucinar até lá. E o resto já sabem: Se houver alguma coisa que discordem, ou se virem algo menos correcto já sabem onde me encontrar.
Abraço!
Nuno