Motor de recomendação personalizada em 5 etapas — sem LLM no caminho crítico.
Template open-source com pesos versionados em banco, fuzzy matching e relaxamento em 5 níveis.
Sistemas de recomendação baseados em RAG/LLM têm 3 problemas operacionais sérios:
- Latência alta — cada recomendação dispara chamada de embeddings + LLM
- Custo crescente — escala linear com volume de usuários
- Auditoria difícil — "por que esse item apareceu pro usuário X?" é não-trivial
Esse repo mostra uma alternativa determinística, auditável e sub-segundo pra casos onde:
- O catálogo é finito (cursos, aulas, produtos)
- Os atributos do usuário são estruturáveis
- Existe componente de segurança (recomendações que NÃO podem aparecer pra certos perfis)
Pipeline de 5 etapas orquestrado em N8N + PostgreSQL:
1. Coleta Conversacional → 2. Normalização (fuzzy) → 3. Filtros Hard (segurança)
↓
4. Scoring multilayer (S_obj + S_esf + S_pref - P) × M → 5. Periodização
↓
Protocolo de relaxamento (5 níveis)
quando pool < N_alvo
[filtros de segurança NUNCA relaxam]
- 🔒 Filtros de segurança nunca relaxam — se um item é unsafe pra um perfil (ex: alto-impacto pra gestante), está fora. Sem exceção.
- 📊 Pesos vivem em tabela versionada (
algorithm_weightscom flagis_active) — calibrar =UPDATE, não deploy - 🤝 Preferência não declarada = neutro (peso máximo) — não enviesa o ranking
- 🎲 Princípio da precaução — quando
extraction_confidence < 0.7, assume pior caso (HIGH/COMPLEX) - 🏆 Profissional preferido é multiplicador (×1.10), não filtro — favoritos sobem mas não dominam
flowchart TB
A[User Input<br/>perfil + objetivo + restrições] --> B[Etapa 1<br/>Coleta Conversacional<br/>Agent IA]
B --> C[Etapa 2<br/>Normalização<br/>pg_trgm fuzzy match]
C --> D[Etapa 3<br/>Filtros Hard<br/>segurança → equip → duração → nível]
D --> E{Pool<br/>suficiente?}
E -- não --> F[Protocolo de Relaxamento<br/>N1 saudável → N5 catastrófico]
F --> D
E -- sim --> G[Etapa 4<br/>Scoring multilayer<br/>S_final = max 0, S_obj + S_esf + S_pref - P × M]
G --> H[Etapa 5<br/>Periodização<br/>N_req aulas em 5 semanas]
H --> I[Plano final<br/>ranqueado + diversificado]
subgraph Postgres
J[(catalog)]
K[(synonyms x3)]
L[(weights versioned)]
M[(instructors)]
end
C -.consulta.-> K
D -.consulta.-> J
G -.consulta.-> L
G -.consulta.-> M
style D fill:#dc2626,color:#fff
style G fill:#3FCF8E,color:#000
style F fill:#fbbf24,color:#000
| Camada | Tecnologia |
|---|---|
| Orquestração | N8N (3 workflows) |
| Banco | PostgreSQL 15 + extensão pg_trgm (Supabase compatível) |
| Embeddings (opcional offline) | Vertex AI text-embedding-005 ou BGE-M3 local |
| Coleta conversacional | LangChain Agent (GPT-4 / Claude / Gemini — qualquer) |
| Glue code | Node.js dentro de nós N8N |
.
├── sql/
│ ├── 001_schema.sql # 6 tabelas core
│ ├── 002_indexes.sql # GIN trgm + B-tree filtros
│ ├── 003_seed_synonyms.sql # 100+ sinônimos canônicos
│ ├── 004_seed_catalog.sql # 50 itens demo
│ ├── 005_seed_weights.sql # pesos v0.1 ativos
│ └── 006_views_auditoria.sql # views pra debug
├── workflows/
│ ├── 01-recommendation-pipeline-v2.json # workflow principal 5 etapas
│ ├── 02-test-suite.json # 11 casos sintéticos (A1-3, B1-3, etc)
│ └── 03-coleta-agent.json # LangChain Agent (Etapa 1)
├── docs/
│ ├── arquitetura.md
│ ├── 5-etapas-pipeline.md
│ ├── relaxamento-5-niveis.md
│ ├── scoring-multilayer.md
│ ├── pesos-versionados.md
│ └── fuzzy-matching-pg-trgm.md
├── scripts/
│ ├── benchmark_fuzzy.py # bench fuzzy match vs alternativas
│ └── seed_from_csv.py # ingest em batch
└── docker-compose.yml # N8N + Postgres pronto pra dev
git clone https://github.com/LufeDigitalWave/deterministic-recommendation-engine
cd deterministic-recommendation-engine
docker compose up -d
# popular schema + dados de exemplo
for f in sql/*.sql; do
docker compose exec postgres psql -U postgres -d engine -f "/sql/$(basename $f)"
done
# importar workflows N8N
# abra http://localhost:5678 e importe os 3 arquivos de workflows/
# chamar endpoint do workflow principal
curl -X POST http://localhost:5678/webhook/recommendation-pipeline/v2 \
-H 'Content-Type: application/json' \
-d @docs/exemplos/payload_demo_user.json| Métrica | Antes (RAG) | Depois (determinístico) |
|---|---|---|
| Latência P95 | ~3s | < 800ms |
| Custo por recomendação | $0.004 | ~$0 |
| Auditabilidade | baixa | 100% (logs por etapa) |
| Cobertura catálogo | 100% | 100% |
| Calibração de pesos | requer deploy | UPDATE no banco |
Cobre os 5 buckets de risco identificados em produção:
| Bucket | Casos | O que testa |
|---|---|---|
| A — Restrição articular | A1, A2, A3 | Joelho, ombro, lombar — filtro hard nunca relaxa |
| B — Fase de vida | B1, B2, B3 | Gestante, pós-parto, idoso — segurança máxima |
| C — Nível técnico | C1, C2 | Iniciante x avançado — não recomenda complexidade alta |
| D — Sem equipamento | D1 | equipment=[] retorna pool não-vazio |
| E — Catastrófico | E1, E2 | Quando todas as 5 relaxações falham, fallback |
- docs/5-etapas-pipeline.md — visão detalhada do pipeline
- docs/relaxamento-5-niveis.md — por que 5 níveis e não 3
- docs/scoring-multilayer.md — fórmula completa + casos extremos
- docs/pesos-versionados.md — calibração sem deploy
- docs/fuzzy-matching-pg-trgm.md — bench pg_trgm vs alternativas
Seja honesto:
- ❌ Catálogo > 1M itens — vector search com pgvector ou Pinecone vai escalar melhor
- ❌ Atributos do usuário não-estruturáveis (livre texto puro) — RAG vence
- ❌ Recomendação social ("amigos curtiram X") — collaborative filtering é melhor caminho
- ❌ Cold start de usuário — você precisa dos Q1-Q6 antes de funcionar bem
Pra esses casos, RAG vetorial ou modelos colaborativos (ALS, etc) resolvem com menos overhead.
MIT — use livremente em produtos comerciais.