Configuración del análisis de tono y emociones de los posts en Discourse AI mediante self-hosted HuggingFace Text Embeddings Inference (TEI).
¿Qué es esto?
Sentiment en Discourse AI no es un modelo LLM de chat/completions. Bajo el capó, hay dos pequeñas modelos clasificadores RoBERTa (~125M de parámetros cada uno), que se ejecutan a través de HuggingFace TEI. Los nombres de los modelos están hardcodeados en las consultas SQL de los dashboards de Discourse — no se pueden cambiar.
Fuente: Self-Hosting Sentiment and Emotion for DiscourseAI (Falco, equipo de Discourse).
| Modelo | model_name (exactamente como en el código) | Propósito |
|---|---|---|
| Sentiment | cardiffnlp/twitter-roberta-base-sentiment-latest |
positivo / negativo / neutral |
| Emotion | SamLowe/roberta-base-go_emotions |
28 emociones (alegría, ira, sorpresa…) |
Formato de API: POST {\"inputs\": \"text\", \"truncate\": true} → array [{\"label\": \"...\", \"score\": 0.95}, ...]
Característica: el modelo cardiffnlp no tiene tokenizer.json
TEI requiere tokenizer.json, pero el modelo cardiffnlp/twitter-roberta-base-sentiment-latest no lo tiene (formato antiguo: vocab.json + merges.txt). Solución: descargar los archivos del modelo localmente y añadir tokenizer.json del modelo SamLowe/roberta-base-go_emotions (el mismo tokenizador RoBERTa-base).
Preparación (una sola vez)
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"
Opción A: GPU
Imagen: ghcr.io/huggingface/text-embeddings-inference:cuda-1.9.3
La etiqueta estándar
:latest(y:1.9) está compilada para compute cap 80 (Ampere) y no funciona en Blackwell (RTX 50x0, compute cap 120). Usa específicamentecuda-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
Primera ejecución en Blackwell: la compilación JIT de los núcleos CUDA toma ~5 minutos en el contenedor. Esto es una sola vez.
Rendimiento GPU (RTX 5060 Ti)
| Métrica | Valor |
|---|---|
| Inferencia de Sentiment | ~14ms |
| Inferencia de Emotion | ~60ms |
| VRAM por contenedor | ~428 MB |
| VRAM por ambos | ~856 MB |
Opción B: CPU (fallback)
Imagen: ghcr.io/huggingface/text-embeddings-inference:cpu-1.9
Adecuado si no hay GPU disponible o si la VRAM es insuficiente. No requiere controladores 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
```Cuando el LA es alto, se puede limitar el CPU:
```bash
docker update --cpus=0.1 tei-sentiment tei-emotion
Rendimiento del CPU
| Métrica | Valor |
|---|---|
| Inferencia de Sentiment | ~270ms |
| Inferencia de Emoción | ~205ms |
| RAM por contenedor | ~500 MB |
| Con --cpus=0.1 | ~2-3s por post |
Cambiar GPU ↔ CPU
docker stop tei-sentiment tei-emotion
docker rm tei-sentiment tei-emotion
Luego, inicie los contenedores según la configuración deseada. No es necesario cambiar las configuraciones de Discourse — los endpoints son los mismos.
Verificación
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"}'
Respuesta esperada para sentiment: [{\"label\":\"positive\",\"score\":0.96},...]
Configuración de Discourse
En /admin/plugins/discourse-ai/settings?filter=sentiment:
- discourse_ai_enabled = true
- ai_sentiment_enabled = true
- ai_sentiment_model_configs — dos objetos:
| Campo | Modelo 1 | Modelo 2 |
|---|---|---|
| model_name | cardiffnlp/twitter-roberta-base-sentiment-latest |
SamLowe/roberta-base-go_emotions |
| endpoint | http://<tu-host>:8081 |
http://<tu-host>:8082 |
| api_key | (vacío) | (vacío) |
Paneles de control
/admin/reports/overall_sentiment— estado general (positivo - negativo)/admin/reports/emotion_joy(y otras 27 emociones)- Backfill: ~2500 posts/hora, posts no mayores a 60 días
Condiciones y riesgos
- Los modelos están entrenados en inglés. Para texto en ruso, el resultado es aproximado, pero el sentiment básico funciona.
- El endpoint está abierto sin api_key — para producción, cierre mediante un reverse proxy.
- Monitoreo de VRAM:
nvidia-smi --query-compute-apps=pid,name,used_memory --format=csv,noheader