| name | tag-rules |
| description | Gerencia o engine de auto-tagging do Ravi. Use quando precisar:
- Criar, validar, listar ou explicar tag rules
- Aplicar regras manualmente em um contato ou em todos via tick
- Entender como rules disparam reativas em mensagens novas
- Modelar workflows com transições de tags (lifecycle:new → lifecycle:qualified)
|
Tag Rules
Tag rules classificam contatos e chats de forma determinística. Cada rule lê estado canônico e adiciona/remove tags. Sem IA, sem inferência.
Rules são a base de orquestração: instâncias aplicam tag inicial (defaultContactTags), observers reagem a tags (--scope tag --tag-target contact), e tag rules movem o contato entre estados conforme o que aparece nas conversas.
Modelo
- Rule = JSON em
.ravi/tag-rules/<id>.json
scope: contact ou chat
conditions: predicados tipados pelo scope (AND implícito)
apply: ações tag / removeTag no target
when: matched (default) ou not-matched para inverter
Execução:
- Reativa: cada mensagem inbound DM dispara o engine via
queueMicrotask no consumer
- Periódica:
ravi tag-rules tick --apply percorre todos os contatos (recomendado via cron)
- Manual:
ravi tag-rules evaluate <rule-id> --target contact:<id> (default dry-run)
Audit: cada apply emite profile.tag_added/profile.tag_removed no timeline do contato e NATS ravi.tags.rule.applied.
Comandos
ravi tag-rules list
ravi tag-rules show <rule-id>
ravi tag-rules validate
ravi tag-rules explain --target contact:<id>
ravi tag-rules evaluate <rule-id> --target contact:<id> [--apply]
ravi tag-rules tick [--apply] [--limit <n>]
evaluate e tick são dry-run por default. Use --apply quando confirmar.
Inspeção Cruzada
Tag-rules é UM dos 5 planos do CRM. Quando inspecionar, sempre combine com os outros pra ter contexto:
ravi tag-rules list --json
ravi instances list --json
ravi contacts list --json
ravi chats list --limit 5 --json
ravi observers rules list --json
⚠️ Regras sem contatos = inerte. Antes de criar regra, confirme que há intake ativo e contatos sendo criados.
⚠️ Regras sem observer consumindo a tag de saída = pipeline incompleto. A regra muda a tag, mas ninguém age. Sempre verifique se existe observer rule consumindo cada tag que sua rule produz.
Conditions Vocabulary
scope: contact
has-tag, not-has-tag, has-any-tag, has-all-tags
status: allowed | pending | blocked | discovered
last-inbound-age: operadores >/</>=/<=/= e duração (7d, 24h, 30m)
has-chat-with: sub-conditions avaliadas em chats relacionados
scope: chat (e sub-conditions de has-chat-with)
chat-type: dm | group | channel | thread
message-count: operadores numéricos
any-message-text-matches: regex case-insensitive, lastN e from (any|contact|agent)
last-inbound-age: idade da última mensagem inbound
has-tag, not-has-tag: tags atachadas ao chat asset
Apply Semantics
apply:
- target: contact
tag: lifecycle:qualified
removeTag: lifecycle:new
when: matched
targetMode: all
Transições explícitas via removeTag. Tag families são deferred — quando manualmente listar removes ficar repetitivo, promover pra family.
Playbook: Pipeline de Lead
1. Instância marca tag inicial
ravi instances set main contactIntakeMode discovered
ravi instances set main defaultContactTags lifecycle:new
2. Rule promove new → qualified quando o contato menciona compra
cat > ~/.ravi/tag-rules/qualify-buy-intent.json <<'EOF'
{
"id": "qualify-buy-intent",
"description": "Move lead pra qualified quando demonstra interesse de compra",
"scope": "contact",
"enabled": true,
"priority": 10,
"conditions": [
{ "kind": "has-tag", "tag": "lifecycle:new" },
{
"kind": "has-chat-with",
"conditions": [
{ "kind": "any-message-text-matches", "pattern": "(preço|comprar|orçamento)", "from": "contact" }
]
}
],
"apply": [
{
"target": "contact",
"tag": "lifecycle:qualified",
"removeTag": "lifecycle:new",
"when": "matched"
}
]
}
EOF
ravi tag-rules validate
ravi tag-rules explain --target contact:<id>
3. Observer entra quando tag muda
ravi observers rules set qualified-nurture <observer-agent> \
--scope tag \
--tag lifecycle:qualified \
--tag-target contact \
--observer-role qualified-nurture
4. Rule esfria lead inativo (cron)
cat > ~/.ravi/tag-rules/cold-lead.json <<'EOF'
{
"id": "cold-lead",
"scope": "contact",
"enabled": true,
"priority": 50,
"conditions": [
{ "kind": "has-tag", "tag": "lifecycle:qualified" },
{ "kind": "last-inbound-age", "operator": ">", "duration": "7d" }
],
"apply": [
{ "target": "contact", "tag": "temperature:cold", "when": "matched" }
]
}
EOF
Rode via cron:
0 */6 * * * cd /path/to/ravi && bin/ravi tag-rules tick --apply --json
Regras de Ouro
- Rules são determinísticas: mesmo estado, mesmo resultado.
- Sempre dry-run antes de apply.
- Não usar rules pra mudar
contact_policies.status ou crm_contact_profiles.lifecycle — só tags.
- Transições de estado entre tags =
removeTag explícito no apply.
- Cascade guard previne loop entre rules; max-depth é telemetria por enquanto.
tick é idempotente: rodar 2x não duplica eventos (no-op detectado).
Debugging
ravi tag-rules explain --target contact:<id> mostra:
- Quais rules deram MATCH e quais miss
- O trace de cada condition
- Que tags seriam adicionadas/removidas no apply
ravi contacts events <phone> mostra timeline incluindo profile.tag_added/profile.tag_removed.
NATS: assine ravi.tags.rule.applied para reagir em outros sistemas.
Spec
Spec normativa: tags/auto-tagging em .ravi/specs. Cobre invariants, performance, audit, e convergência futura com observer rules compostas.