Sentimento do Discourse AI autosservido: opções com GPU e CPU

# Sentimento Self-hosted Discourse AI: Opções GPU e CPU

Configuração de análise de tonalidade e emoções dos posts no Discourse AI via self-hosted HuggingFace Text Embeddings Inference (TEI).

O que é isso

Sentimento no Discourse AI não é um modelo LLM de chat/completions. Por baixo dos panos, há duas pequenas modelos classificadores RoBERTa (~125M de parâmetros cada), executados via HuggingFace TEI. Os nomes dos modelos estão hardcoded nos SQL queries dos dashboards do Discourse — não podem ser alterados.

Fonte: Self-Hosting Sentiment and Emotion for DiscourseAI (Falco, equipe do Discourse).

Modelo model_name (exatamente como no código) Finalidade
Sentimento cardiffnlp/twitter-roberta-base-sentiment-latest positivo / negativo / neutro
Emoção SamLowe/roberta-base-go_emotions 28 emoções (alegria, raiva, surpresa…)

Formato da API: POST {\"inputs\": \"text\", \"truncate\": true} → array [{\"label\": \"...\", \"score\": 0.95}, ...]

Diferença: modelo cardiffnlp não possui tokenizer.json

O TEI exige tokenizer.json, mas o modelo cardiffnlp/twitter-roberta-base-sentiment-latest não tem (formato antigo: vocab.json + merges.txt). Solução: baixar os arquivos do modelo localmente e adicionar tokenizer.json do modelo SamLowe/roberta-base-go_emotions (o mesmo tokenizador RoBERTa-base).

Preparação (único)

sudo mkdir -p /opt/tei-sentiment-cache/model
cd /opt/tei-sentiment-cache/model

for f in config.json vocab.json merges.txt special_tokens_map.json pytorch_model.bin; do
  sudo curl -sL -o "$f"     "https://huggingface.co/cardiffnlp/twitter-roberta-base-sentiment-latest/resolve/main/$f"
done

sudo curl -sL -o tokenizer.json   "https://huggingface.co/SamLowe/roberta-base-go_emotions/resolve/main/tokenizer.json"
sudo curl -sL -o tokenizer_config.json   "https://huggingface.co/SamLowe/roberta-base-go_emotions/resolve/main/tokenizer_config.json"

Opção A: GPU

Imagem: ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3

O tag padrão :latest (e :1.9) foi compilado para compute cap 80 (Ampere) e não funciona na Blackwell (RTX 50x0, compute cap 120). Use exatamente cuda-1.9.3

docker pull ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3
sudo mkdir -p /opt/tei-emotion-cache

docker run -d --name tei-sentiment   --gpus all --shm-size 1g   -p 8081:80   -v /opt/tei-sentiment-cache/model:/data/model   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3   --model-id /data/model

docker run -d --name tei-emotion   --gpus all --shm-size 1g   -p 8082:80   -v /opt/tei-emotion-cache:/data   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3   --model-id SamLowe/roberta-base-go_emotions

Primeira execução na Blackwell: a JIT-compilação dos kernels CUDA leva ~5 minutos por container. Isso é uma única vez.

Desempenho GPU (RTX 5060 Ti)

Métrica Valor
Inferência de Sentimento ~14ms
Inferência de Emoção ~60ms
VRAM por container ~428 MB
VRAM total ~856 MB

Opção B: CPU (fallback)

Imagem: ghcr.io/huggingface/text-embeddings-inference:cpu-1.9

Adequado se GPU não estiver disponível ou se a VRAM for insuficiente. Não exige drivers NVIDIA.

docker pull ghcr.io/huggingface/text-embeddings-inference:cpu-1.9
sudo mkdir -p /opt/tei-emotion-cache

docker run -d --name tei-sentiment   --shm-size 1g   -p 8081:80   -v /opt/tei-sentiment-cache/model:/data/model   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cpu-1.9   --model-id /data/model --dtype float32

docker run -d --name tei-emotion   --shm-size 1g   -p 8082:80   -v /opt/tei-emotion-cache:/data   --restart unless-stopped   ghcr.io/huggingface/text-embeddings-inference:cpu-1.9   --model-id SamLowe/roberta-base-go_emotions --dtype float32
```Ao encontrar alto LA, é possível limitar o CPU:

```bash
docker update --cpus=0.1 tei-sentiment tei-emotion

Desempenho do CPU

Métrica Valor
Inferência de Sentimento ~270ms
Inferência de Emoção ~205ms
RAM no container ~500 MB
Com --cpus=0.1 ~2-3s por post

Alternância GPU ↔ CPU

docker stop tei-sentiment tei-emotion
docker rm tei-sentiment tei-emotion

Em seguida, inicie os containers conforme o modo desejado. Não é necessário alterar as configurações do Discourse — os endpoints permanecem os mesmos.

Verificação

curl -s http://localhost:8081/   -X POST -H 'Content-Type: application/json'   -d '{"inputs": "I am happy"}'

curl -s http://localhost:8082/   -X POST -H 'Content-Type: application/json'   -d '{"inputs": "I am happy"}'

Resposta esperada para sentiment: [{\"label\":\"positive\",\"score\":0.96},...]

Configuração do Discourse

Na /admin/plugins/discourse-ai/settings?filter=sentiment:

  1. discourse_ai_enabled = true
  2. ai_sentiment_enabled = true
  3. ai_sentiment_model_configs — dois objetos:
Campo Modelo 1 Modelo 2
model_name cardiffnlp/twitter-roberta-base-sentiment-latest SamLowe/roberta-base-go_emotions
endpoint http://<seu-host>:8081 http://<seu-host>:8082
api_key (vazio) (vazio)

Painéis

  • /admin/reports/overall_sentiment — tom geral (positivo - negativo)
  • /admin/reports/emotion_joy (e outras 27 emoções)
  • Backfill: ~2500 posts/hora, posts não mais antigos que 60 dias

Condições e Riscos

  • Os modelos foram treinados em inglês. Para texto em russo, o resultado é aproximado, mas o básico de sentiment analysis funciona.
  • O endpoint está aberto sem api_key — para produção, feche por meio de um reverse proxy.
  • Monitoramento de VRAM: nvidia-smi --query-compute-apps=pid,name,used_memory --format=csv,noheader