Auto-hospedagem de LLM de código aberto para DiscourseAI

O plugin Discourse AI possui muitos recursos que precisam de um LLM para serem ativados — resumização, AI Helper, busca semântica, AI Bot e seus agentes. Você pode usar uma API de terceiros, como Configure API Keys for OpenAI ou Configure API Keys for Anthropic, mas o Discourse AI foi criado desde o primeiro dia para que você não fique preso a essas opções.

Este guia aborda a execução de um modelo de pesos abertos por conta própria e sua conexão ao Discourse.

Modelo utilizado aqui: Qwen/Qwen3.8-27B — um modelo denso de 27B lançado em agosto de 2026 com uma janela de contexto nativa de 256K, compreensão de imagem/vídeo, chamadas de ferramentas e modo de raciocínio ativado por padrão. Atualmente, é o ponto ideal para auto-hospedagem: forte o suficiente para resumização/agentes reais, pequeno o suficiente para uma única GPU. Substitua por qualquer outro modelo que preferir; os sinalizadores que mudam estão indicados abaixo.

O que o Discourse precisa do seu servidor de inferência

  • Um endpoint POST /v1/chat/completions compatível com a OpenAI (com suporte a streaming).
  • Chamada de ferramentas/funções nativa se você quiser que o AI Bot, agentes ou saída estruturada funcionem bem. Sem isso, o Discourse recorre a chamadas de ferramentas baseadas em XML, que modelos mais fracos tratam mal.
  • Uma janela de contexto real — mire em no mínimo 16K tokens, preferencialmente 32K+. O tamanho dos prompts do Discourse para resumização e RAG cresce rapidamente.
  • Opcional: entrada de imagem, se você quiser que o modelo leia imagens anexadas às postagens.

Qualquer coisa que satisfaça os três primeiros requisitos funciona, então a escolha abaixo depende realmente do seu hardware.

Configuração Bom para
vLLM Servidor GPU dedicado, ≥24GB de VRAM (quantizado) / 80GB (precisão completa), tráfego sustentado, múltiplos usuários
llama.cpp GPUs de consumo, Apple Silicon, máquinas apenas com CPU, fóruns pequenos únicos
Gerenciado / nuvem Você quer o modelo fora do seu laptop, mas não no seu hardware: qualquer endpoint compatível com OpenAI

Executando com vLLM

O vLLM é o motor recomendado para servidores GPU: licença Apache, servidor compatível com OpenAI pronto para uso e suporte de primeira classe para a família Qwen.

mkdir -p /opt/vllm-cache
export VLLM_API_KEY="$(openssl rand -hex 24)"

docker run -d --name vllm \
  --runtime nvidia --gpus all \
  -v /opt/vllm-cache:/root/.cache/huggingface \
  -e HF_HOME=/root/.cache/huggingface \
  -e "HF_TOKEN=${HF_TOKEN}" \
  -p 8080:8000 \
  --ipc=host \
  vllm/vllm-openai:latest \
  Qwen/Qwen3.8-27B \
  --host 0.0.0.0 \
  --port 8000 \
  --max-model-len 32768 \
  --reasoning-parser qwen3 \
  --enable-auto-tool-choice \
  --tool-call-parser qwen3_xml \
  --api-key "$VLLM_API_KEY"

Observações sobre os sinalizadores:

  • --max-model-len limita o contexto de prompt+saída e, portanto, o cache KV. O modelo suporta 256K nativamente; 32K é suficiente para trabalhos em fórum e mantém a VRAM previsível. Aumente-o (ou use auto) se você tiver memória.
  • --reasoning-parser qwen3 separa o raciocínio do modelo em um campo reasoning separado, em vez de despejá-lo na resposta. Você deve espelhar essa escolha nas configurações de LLM do Discourse (veja abaixo).
  • --enable-auto-tool-choice --tool-call-parser ... ativa a chamada de função nativa. Os nomes dos analisadores mudam entre as versões do vLLM — verifique python3 -m vllm.entrypoints.openai.api_server --help dentro da sua imagem se as chamadas de ferramentas retornarem como texto simples.
  • HF_TOKEN só é necessário para modelos restritos; --api-key protege seu próprio endpoint. Ressalva da documentação do vLLM: --api-key protege apenas as rotas /v1, então mantenha o servidor fora da internet pública.
  • Apertado em VRAM? Tente --quantization fp8 (ou um checkpoint pré-quantizado AWQ) antes de reduzir o contexto. Multi-GPU: adicione --tensor-parallel-size N.

Teste-o:

curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VLLM_API_KEY" \
  -d '{
    "model": "Qwen/Qwen3.8-27B",
    "messages": [{"role": "user", "content": "What is Discourse?"}],
    "max_tokens": 200
  }'

Você deve receber um corpo JSON normal de conclusão de chat de volta, com message.reasoning ao lado de message.content.

Executando com llama.cpp

O llama.cpp é a ferramenta certa quando o modelo precisa caber em menos VRAM do que o vLLM gostaria, ou quando você está em Apple Silicon, AMD, Vulkan ou CPU. Seu llama-server também expõe uma API compatível com a OpenAI.

Baixe e execute a versão oficial GGUF usando a imagem do container para seu backend (server-cuda aqui; server, server-rocm, server-vulkan e outros também existem):

export LLAMA_API_KEY="$(openssl rand -hex 24)"

docker run -d --name llama-server \
  --gpus all \
  -p 8080:8080 \
  -v /opt/llama-models:/models \
  ghcr.io/ggml-org/llama.cpp:server-cuda \
  -hf ggml-org/Qwen3.8-27B-GGUF:Q4_K_M \
  --host 0.0.0.0 \
  --port 8080 \
  --alias qwen3.8-27b \
  --api-key "$LLAMA_API_KEY" \
  --ctx-size 16384 \
  --n-gpu-layers auto \
  --flash-attn auto \
  --jinja \
  --reasoning auto
  • Q4_K_M tem cerca de 16GB de pesos; medido em torno de 18,4 GiB de VRAM em contexto 8K e ~20 GiB em 32K, então uma placa de 24GB funciona com um --ctx-size modesto. unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M da Unsloth é uma alternativa popular.
  • --jinja usa o template de chat do próprio modelo — isso é o que faz a chamada de ferramentas nativa funcionar.
  • --alias define o nome reportado pela API, para que o nome do modelo que você digita no Discourse corresponda.
  • --reasoning auto reporta o raciocínio separadamente, correspondendo ao comportamento --reasoning-parser do vLLM.
  • A entrada de imagem precisa do projetor multimodal; com -hf geralmente é buscado automaticamente, caso contrário passe --mmproj /models/mmproj-...gguf (ou --no-mmproj para economizar VRAM para serviço apenas de texto).

Teste-o:

curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $LLAMA_API_KEY" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{"role": "user", "content": "What is Discourse?"}],
    "max_tokens": 200
  }'

Executando algo diferente

Qualquer servidor que fale conclusões de chat compatíveis com OpenAI — SGLang, TensorRT-LLM, LM Studio, um gateway empresarial, uma implantação dedicada na nuvem — funciona como está: configure-o no Discourse com o provedor vLLM, que é nosso cliente genérico compatível com OpenAI com suporte a ferramentas nativo.

Se você preferir não executar a inferência por conta própria, mas ainda quiser ficar fora da dependência das grandes APIs, os Hugging Face Inference Endpoints (que agora têm o vLLM como motor padrão) funcionam com o provedor Hugging Face — aponte-o para a URL /v1/chat/completions do endpoint.

Tornando-o disponível para sua instância do Discourse

Normalmente, você executará isso em uma máquina dedicada devido ao requisito de GPU. Quando fizer isso:

  1. Coloque um proxy reverso na frente do servidor e finalize o TLS lá — o Discourse falará HTTPS com ele sem problemas.
  2. Restrinja o endpoint por firewall ou certificado de cliente para que apenas seus hosts Discourse possam se conectar. Uma chave API é autenticação, não uma fronteira de rede.
  3. Mantenha o cache do modelo em um volume persistente para que reinicializações não baixem novamente dezenas de gigabytes.

Configurando o Discourse AI

As conexões de LLM são configuradas através da interface administrativa, e não nas configurações do site. Vá para /admin/plugins/discourse-ai/ai-llms e adicione um novo LLM:

  1. Nome de exibição — o que seus administradores e usuários veem.
  2. Nome — enviado na solicitação da API; deve corresponder ao que o servidor espera (Qwen/Qwen3.8-27B para vLLM, seu --alias para llama.cpp).
  3. Provedor — vLLM para vLLM, llama.cpp e qualquer outro servidor compatível com OpenAI; Hugging Face para endpoints hospedados na HF.
  4. URL — a URL completa do endpoint incluindo o caminho, por exemplo, https://llm.example.com/v1/chat/completions.
  5. Chave API — digite-a, ou referencie uma chave armazenada na página de Segredos de IA.
  6. Tokenizer — Qwen para Qwen3.8 (escolha o tokenizer correspondente à família do seu modelo).
  7. Tokens máximos de prompt / tokens máximos de saída — mantenha a soma igual ou abaixo do --max-model-len / --ctx-size do seu servidor. Por exemplo, com um servidor de 32768 tokens: 28000 de prompt e 4096 de saída. Modelos de raciocínio gastam parte desse orçamento de saída pensando, então não aperte demais.
  8. Visão — ative a visão nativa se você serviu o modelo com suporte a imagens e quiser que ele leia imagens.
  9. Opções do provedor (vLLM): defina analisador de raciocínio vLLM como qwen3 para corresponder ao sinalizador do servidor, e use os campos de substituição de pensamento / esforço de raciocínio / orçamento de tokens de pensamento se você quiser que o Discourse guie o pensamento por solicitação. Deixe “desativar ferramentas nativas” desmarcado, a menos que a chamada de função do modelo esteja quebrada.

Clique em Executar teste antes de salvar: ele dispara uma solicitação com streaming e outra sem streaming e informa qual delas falhou.

Uma vez salvo, torne-o o padrão do site com a configuração do site ai_default_llm_model, ou atribua-o por recurso em /admin/plugins/discourse-ai/ai-features. Agentes individuais são configurados na página de Agentes.

Uma ressalva: as incorporações de busca semântica não vêm deste modelo. As incorporações usam seu próprio modelo de incorporação, configurado separadamente (um modelo de incorporação via API, ou um servidor Text Embeddings Inference auto-hospedado via configurações ai_hugging_face_tei_endpoint). Se você quiser a Busca por IA totalmente auto-hospedada, execute um pequeno modelo de incorporação como bge-m3 ao lado do seu LLM.

Solução de problemas

Sintoma Causa provável
404 do endpoint URL está sem /v1/chat/completions
model not found O Nome não corresponde ao ID do modelo servido / --alias
401 / 403 Incompatibilidade de chave API entre --api-key e o registro do LLM
Chamadas de ferramentas aparecem como texto JSON na postagem Servidor iniciado sem análise de chamada de ferramentas; o Discourse recorreu a ferramentas XML
Texto de raciocínio vazando na resposta --reasoning-parser ausente no servidor, ou a configuração de analisador de raciocínio do Discourse não corresponde a ele
Memória insuficiente da CUDA Reduza --max-model-len, adicione --quantization fp8 ou reduza --gpu-memory-utilization
Solicitações expiram em resumizações longas Modelos de raciocínio são lentos; nosso tempo limite HTTP é de 6 minutos — reduza o contexto, diminua o esforço de raciocínio ou desative o raciocínio
Respostas truncadas max output tokens no Discourse é muito pequeno quando os tokens de raciocínio são contados
17 Curtiram

Para quem estiver pesquisando este tópico com/para:
#Chaves-de-API-Llava

Eu também estou usando o vLLM. Eu também recomendaria o modelo openchat v3.5 0106, que é um modelo de 7 bilhões de parâmetros que tem um desempenho muito bom.

Na verdade, estou executando-o com quantização de 4 bits para que ele rode mais rápido.

Estou designando esta tarefa a um estagiário. Há recomendações de alguém sobre qual serviço específico assinar? Isto é para um teste. O estagiário atualmente tem um teste configurado com OpenAI. Funciona bem. Eles estão interessados em experimentar o HuggingFace TGI, mas parece que preciso dar a eles um servidor dedicado com GPU? Quais são as especificações mínimas para um teste?

Existem links que posso dar ao estagiário?

Ainda não analisei este projeto em profundidade. Estou apenas antecipando que o estagiário precisará de alguns recursos e estou tentando fazer algumas recomendações razoáveis sobre serviços para o estagiário em pesquisa.

Olá, ao expor com um certificado autoassinado vllm container em uma caixa de GPU on-prem, não encontrei uma boa maneira de adicionar o Root CA ao container discourse para que ele possa acessar com segurança este serviço on-prem via https.

exemplo:

./launcher enter app
curl -L https://vllm.infra.example.com/v1/models
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl falhou em verificar a legitimidade do servidor e, portanto, não pôde
estabelecer uma conexão segura com ele. Para saber mais sobre esta situação e
como corrigi-la, visite a página da web mencionada acima.

Existe uma boa maneira de adicionar um certificado raiz autoassinado no container discourse que sobreviveria às atualizações da imagem do container?

Pelo que sei, adicioná-lo no app.yml

run:
  - exec: wget ... && update-ca-certificates

só funcionaria bem durante a construção/reconstrução do aplicativo.

Qualquer dica é bem-vinda.

14 posts foram divididos em um novo tópico: Fazendo o discourse ai funcionar com ollama localmente

Haveria alguma maneira de adicionar um cabeçalho HTTP personalizado às requisições do Discourse AI? Temos um servidor equipado com GPU que usamos para diversos fins, ele possui uma API compatível com a OpenAI, mas por segurança, o colocamos atrás de um gateway de gerenciamento de API, que exige um cabeçalho HTTP personalizado (algo como SubscriptionKey) e fornecemos nossa chave de autenticação aqui.

Note que esta chave de autenticação é para passar pelo APIM e é diferente da chave de API que se forneceria para um serviço como a API da OpenAI.

Seria ótimo se houvesse uma maneira de adicionar Cabeçalhos Personalizados na configuração de cada LLM na página de configuração admin/plugins/discourse-ai/ai-llms. Talvez uma opção ‘Personalizado’ no menu suspenso ‘Provedor’, com opções adicionais para casos de uso como este?

1 Curtiu

A resposta rápida é um plugin para adicionar esse cabeçalho. Uma solução alternativa pode ser um servidor proxy local que você acessaria a partir do Discourse, que adicionaria o cabeçalho e enviaria os dados de volta para o Discourse. Uma fonte não confiável sugere que você pode conseguir que o NGINX faça isso para você com algo como

server {
    listen 80;
    
    location / {
        proxy_pass https://api.example.com;
        proxy_set_header SubscriptionKey xyz;
        proxy_set_header Host $host;
    }
}

Pode haver uma maneira inteligente de adicionar isso à configuração nginx do Discourse em algum caminho secreto (que também escutaria apenas o seu servidor).

1 Curtiu

Olá,

Não consigo encontrar essas configurações:

Alguma sugestão para testar o recurso de tradução com IA usando um backend Ollama?

Crie um novo LLM em /admin/plugins/discourse-ai/ai-llms/new?llmTemplate=none, selecione OpenAI como provedor, já que o Ollama oferece compatibilidade com a API OpenAI: OpenAI compatibility - Ollama.

1 Curtiu

Obrigado! Também notei que a URL só funciona quando o plugin de IA está ativado :person_facepalming:

Para o provedor recém-adicionado, alguma sugestão sobre o que escolher/configurar para as seguintes configurações obrigatórias?

  1. Tokenizador
  2. Janela de contexto

Para o modelo, estava pensando em talvez usar stablelm2:1.6b ou translategemma:4b, mas ainda não sei.
No README do último, há uma descrição de um prompt de entrada. Me pergunto qual é a consulta que o recurso de tradução da IA envia para o Ollama. c.f. /admin/plugins/discourse-ai/ai-agents/-28/edit

:sweat_smile:

O mais próximo do modelo que você escolher. Para o Gemma, você pode escolher o do Gemini.

Aqui você define o contexto máximo da sua implantação.

Por favor, não faça isso. Esse modelo é muito, muito, muito, muito, muito antigo. É a pior escolha que você pode fazer.

Esse modelo está uma geração atrás e tem requisitos de prompt muito rígidos que não são compatíveis com o Discourse. Esperamos um LLM de uso geral mais padrão.

Nesses tamanhos, recomendo o Qwen 3.5 ou o Gemma 4.

3 Curtiram

Obrigado mais uma vez. Configurei as definições, baixei o modelo, ajustei o agente e agora ativei o recurso de tradução com IA. Vamos ver o que acontece.

Outra pergunta. Antes estávamos usando o plugin de tradução com uma instância do LibreTranslate disponibilizada generosamente. Não seria possível conectar o recurso de tradução com IA do Discourse a esse endpoint do LibreTranslate? No final das contas, isso seria muito mais fácil do que ter que hospedar uma instância do Ollama em um servidor sem capacidade real para LLMs.

Por que não continuar usando isso? Não estamos mais atualizando-o, mas ele continuará funcionando como está.

A tradução do Discourse AI opera com um conjunto totalmente diferente de restrições em comparação ao plugin Tradutor original, por isso a construímos do zero para atender a este novo cenário, onde as traduções baseadas em LLMs são melhores e mais baratas do que o que era possível anteriormente com APIs de tradução por ML.

1 Curtiu