Fluxos / JevModel
Classificação de mensagens do Slack: roteie mensagens de canal com a Events API e o Jev
Classifique mensagens de um canal de ajuda ou feedback do Slack com um app pequeno: receba eventos message.channels, verifique a assinatura, confirme em até 3 segundos e depois peça ao Jev um Choice fixo e um Noul de urgência para reagir, responder ou encaminhar. Inclui retentativas, deduplicação, privacidade e quando uma ferramenta no-code basta.
Última atualização:
Resposta rápida: confirme primeiro, classifique depois
- Um app do Slack inscrito no evento de bot message.channels (escopo channels:history) recebe um HTTP POST por mensagem nos canais públicos em que o bot entrou. Verifique o cabeçalho X-Slack-Signature com o signing secret e devolva HTTP 200 em até 3 segundos; o Slack trata respostas mais lentas como falha e tenta de novo até 3 vezes.
- Depois de confirmar, envie só o texto da mensagem para /api/v1/systemone do JevModel com um Choice de 2–12 rótulos definidos por você (por exemplo bug, access, how_to, other) e um Noul de “bloqueado agora?”. Use as probabilidades retornadas para reagir com emoji, responder na thread ou encaminhar, e mande os incertos para uma pessoa.
- Deduplique por event_id, ignore mensagens de bots e edições e nunca apague uma mensagem por causa de um rótulo. Se você já usa Zapier, Make, n8n, Power Automate ou Pipedream, os gatilhos de Slack deles chamam a mesma API do Jev sem hospedar servidor.
O que significa, na prática, classificar mensagens do Slack?
Quase toda equipe tem pelo menos um canal onde pedidos diferentes se acumulam: #help, #it-support, #customer-feedback, #sales-questions. Classificar significa ler cada mensagem nova e atribuir um rótulo de uma lista curta e fixa, para que ela chegue à pessoa certa sem alguém vigiar o canal o dia todo. O rótulo só ajuda se disparar algo visível: uma reação com emoji que os responsáveis filtram, uma resposta em thread pedindo dados que faltam, uma cópia encaminhada a um canal de equipe ou um ticket no seu sistema. Uma segunda pergunta independente costuma importar tanto quanto a categoria: alguém está bloqueado agora? Mantenha as duas decisões separadas. Uma mensagem pode ser uma dúvida de uso de baixa prioridade ou um problema de acesso urgente, e misturar urgência na lista de categorias piora as duas respostas. Este guia cobre canais públicos em que um bot entrou. Mensagens diretas e canais privados exigem outros escopos e uma justificativa de privacidade mais forte; ficam de fora.
Route customer support with JevClassificação de intenção em texto de produto e suporte: regras, LLMs e APIs tipadasChoice, Score, and NoulComo um app do Slack recebe mensagens de canal?
Use a Events API. Nas configurações do app, ative Event Subscriptions e escolha a entrega: uma Request URL HTTP que recebe POSTs do Slack, ou Socket Mode, em que seu processo abre um WebSocket e não precisa de URL pública. Em bot events, assine message.channels; ele exige o escopo channels:history e você só recebe mensagens de canais públicos dos quais o bot é membro, então convide-o com /invite em cada canal a classificar. Cada evento chega em JSON com um envelope externo (type event_callback, team_id e um event_id único globalmente) e um evento interno com type message, channel, user, text, ts e channel_type channel. Se quiser só mensagens que mencionam o bot, assine app_mention. Na entrega HTTP, o Slack assina cada requisição: calcule v0= mais o digest hex HMAC-SHA256 da string v0:{X-Slack-Request-Timestamp}:{corpo bruto} com o signing secret, compare com X-Slack-Signature em tempo constante e rejeite timestamps a mais de cinco minutos do seu relógio para evitar replay. O Bolt para JavaScript, Python e Java faz isso por você. A primeira requisição a uma Request URL nova é um desafio url_verification, respondido devolvendo o valor challenge.
JevModel API: request and responsePrivate runs and historyPor que o app precisa responder ao Slack em 3 segundos?
A documentação da Events API do Slack pede um HTTP 2xx em até três segundos; senão a entrega conta como falha e o Slack tenta de novo três vezes: quase imediatamente, após um minuto e após cinco minutos, com um cabeçalho x-slack-retry-num (1, 2, 3) e um x-slack-retry-reason como http_timeout. Se mais de 95% das entregas falharem em 60 minutos, o Slack pode desativar temporariamente as assinaturas (apps com menos de 1.000 eventos por hora não são desativados automaticamente). Uma classificação é uma requisição de rede a outro serviço, então não faça o Slack esperar. Confirme primeiro e classifique depois a partir de uma fila ou tarefa em segundo plano, como o próprio Slack recomenda. Mais três regras evitam trabalho dobrado e loops. Primeira: guarde o event_id de cada evento tratado e descarte repetidos; uma retentativa traz o mesmo event_id e a API do JevModel não tem chave de idempotência, então uma entrega duplicada seria classificada e cobrada duas vezes. Segunda: ignore mensagens com bot_id ou subtype (edições, entradas, respostas do seu próprio bot), ou o bot classificará as próprias respostas. Terceira: orce o volume: o Slack limita as entregas a 30.000 eventos por workspace e app a cada 60 minutos, e cada mensagem classificada é uma requisição Jev comum no seu saldo de tokens.
JevModel pricing and free Jev runsJevModel API: request and responseComparação: gatilho de Slack no-code vs app próprio do Slack vs exportar CSV
Os três podem enviar a mesma pergunta Jev; muda quem cuida do encanamento. A tabela compara a forma do fluxo, não a precisão, e nenhuma linha é benchmark.
Classificação de texto no Zapier: AI by Zapier Classify vs Vertex Classify Text vs HTTP → JevNó Text Classifier do n8n: integrado vs chamar Jev em um workflowBatch text classification with the JevModel API| Dimensão | Ferramenta no-code (Zapier, Make, n8n…) | App próprio → Events API → Jev | Exportar canal → Console → Batch |
|---|---|---|---|
| Hospedagem | O gatilho de Slack da ferramenta; sem servidor | Seu endpoint ou um processo Socket Mode | Só o navegador |
| Tempo | Depende do polling ou webhook da ferramenta | Quase tempo real após a confirmação de 3 s | Depois do fato, para análise |
| Retentativas e deduplicação | A ferramenta cuida; veja o histórico | Seus: guardar event_id, pular bot_id e subtype | Não se aplica |
| Saída do Jev | A mesma resposta Choice / Noul com probabilidades | Igual, com controle total de reações e respostas | Respostas por linha na exportação (50 linhas por lote antes de comprar, 1.000 depois) |
Exemplo de código: verificar, confirmar, classificar, reagir (ilustrativo)
O esboço é um handler HTTP de Node 18+ sem framework para cada passo ficar visível; o Bolt pode cuidar da assinatura e da confirmação. Dê ao bot os escopos channels:history e reactions:write, assine message.channels e convide o bot para o canal. O handler verifica a assinatura no corpo bruto, responde url_verification, devolve 200 na hora e só então chama o Jev. A requisição segue o contrato documentado do JevModel: POST /api/v1/systemone com uma chave Bearer de conta verificada, um state e perguntas nomeadas, aqui um Choice para o tipo de pedido e um Noul para urgência; uma resposta bem-sucedida tem code 0 e data.answers. Não há campo model porque o site sempre chama typesafe/jev-1.13 via OpenRouter; o JevModel é independente da TypeSafe AI, que desenvolve o Jev. Respostas de baixa probabilidade ganham a reação eyes para uma pessoa olhar, e um code diferente de zero deixa a mensagem intocada. O Set em memória é só para o exemplo; use um armazenamento compartilhado com vários processos e uma fila de verdade se a plataforma encerrar o trabalho depois da resposta.
JevModel API: request and responseChoice, Score, and NoulJev probability, confidence and human-review thresholds// Illustrative only. Node 18+. Env: SLACK_SIGNING_SECRET, SLACK_BOT_TOKEN, JEVMODEL_API_KEY.
// Bot scopes: channels:history (for message.channels) and reactions:write.
import crypto from "node:crypto";
import http from "node:http";
const handled = new Set(); // event_id values; use a shared store in production
function fromSlack(req, raw) {
const ts = Number(req.headers["x-slack-request-timestamp"]);
if (!ts || Math.abs(Date.now() / 1000 - ts) > 300) return false; // replay window
const mine = "v0=" + crypto
.createHmac("sha256", process.env.SLACK_SIGNING_SECRET)
.update("v0:" + ts + ":" + raw)
.digest("hex");
const theirs = String(req.headers["x-slack-signature"] || "");
return mine.length === theirs.length &&
crypto.timingSafeEqual(Buffer.from(mine), Buffer.from(theirs));
}
http.createServer((req, res) => {
let raw = "";
req.on("data", (chunk) => (raw += chunk));
req.on("end", () => {
if (!fromSlack(req, raw)) return res.writeHead(401).end();
const body = JSON.parse(raw);
if (body.type === "url_verification") return res.end(body.challenge);
res.writeHead(200).end(); // acknowledge within 3 seconds, work afterwards
const e = body.event;
if (!e || e.type !== "message" || e.subtype || e.bot_id) return; // skip edits, joins, bots
if (handled.has(body.event_id)) return; // Slack retries reuse the event_id
handled.add(body.event_id);
classify(e).catch(console.error);
});
}).listen(3000);
async function classify(e) {
const res = await fetch("https://jevmodel.app/api/v1/systemone", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.JEVMODEL_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
state: { channel: "help", text: e.text }, // text only, no user profile
questions: {
kind: {
type: "choice",
instructions: "What kind of request is this help-channel message?",
criteria: {
bug: "Something is broken, slow, or returns an error.",
access: "Asking for an account, a permission, or access to a tool.",
how_to: "Asking how to do something that already works.",
other: "Chat, thanks, or anything that fits none of the labels above.",
},
},
urgent: {
type: "noul",
instructions: "Is someone blocked right now by what this message describes?",
criteria: {
true: "A person or customer cannot work now, or a service is down.",
false: "It can wait for normal working hours.",
},
},
},
}),
});
const out = await res.json();
if (out.code !== 0) return; // leave the message unlabeled
const { kind, urgent } = out.data.answers;
const p = kind.probabilities ? kind.probabilities[kind.choice] : 0;
const emoji = { bug: "bug", access: "key", how_to: "question" };
const names = [p < 0.7 ? "eyes" : emoji[kind.choice]]; // 0.7 is a placeholder
if (urgent.noul >= 0.8) names.push("rotating_light"); // placeholder threshold
for (const name of names.filter(Boolean)) {
await fetch("https://slack.com/api/reactions.add", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.SLACK_BOT_TOKEN,
"Content-Type": "application/json; charset=utf-8",
},
body: JSON.stringify({ channel: e.channel, timestamp: e.ts, name }),
});
}
}
O que deve acontecer depois que a mensagem recebe um rótulo?
Prefira ações visíveis e reversíveis. A reação é a mais leve: os responsáveis filtram por emoji e qualquer pessoa remove uma errada. Uma resposta em thread pedindo um print ou número de pedido ajuda em how_to ou bug, mas publique só acima de um limiar testado, porque uma resposta automática errada num canal movimentado incomoda. Encaminhar um link para um canal de equipe ou abrir um ticket no seu sistema funciona quando o rótulo for confiável. Não apague, oculte nem mova mensagens por causa de um rótulo, e não acione o plantão só pelo Noul antes de saber com que frequência ele erra. Envie só o que a decisão precisa: o texto costuma bastar, então deixe de fora nomes, e-mails e campos de perfil. Antes de ativar, exporte algumas centenas de mensagens antigas do canal, rotule à mão, rode as mesmas perguntas e compare por rótulo; defina os limiares com essa amostra. 0,7 e 0,8 no exemplo são marcadores, não recomendações. Acompanhe a fatia de other e de reações eyes: se for alta, as definições precisam de ajuste.
Evaluate a Jev workflowJev probability, confidence and human-review thresholdsPrivate runs and historyQual configuração escolher?
Escolha uma ferramenta no-code se ela já roda suas outras automações e um atraso de cerca de um minuto é aceitável; os guias de Zapier, Make, n8n, Power Automate e Pipedream mostram a mesma requisição Jev em cada uma. Escolha um app próprio do Slack se quiser reações quase em tempo real, controle total de retentativas e respostas, ou vários canais com conjuntos de rótulos diferentes. Escolha exportar o canal e usar o Console → Batch se o objetivo for uma análise pontual de para que um canal foi usado, não roteamento ao vivo. Checklist para qualquer caminho: 1) defina rótulos com um other explícito; 2) mantenha a urgência num Noul separado, não num rótulo; 3) deduplique por event_id e ignore mensagens de bots; 4) envie o texto, não o perfil; 5) comece só com reações e adicione respostas ou tickets depois de medir a concordância numa amostra rotulada. O JevModel é só texto e não é open source; imagens, arquivos e áudios do Slack não são classificados nessa configuração. Se uma mensagem pode pertencer a várias categorias ao mesmo tempo, leia multi-label-text-classification antes de ampliar o Choice.
Classificação de texto multilabel: um Choice, várias perguntas sim/não ou uma segunda passagemCategorize Text no Make.com: AI Toolkit vs HTTP → Jev para rótulos tipados e limiaresClassificação de texto no Power Automate: modelos AI Builder vs HTTP → JevClassificação de texto no Pipedream: ação Hugging Face vs HTTP → JevJev é multimodal / baixável? O que o JevModel realmente éRotular issues do GitHub com IA: um workflow do GitHub Actions que chama o JevClassificação de e-mails com IA: regras, prompts LLM ou uma API de decisão tipada para o e-mail recebidoPerguntas frequentes
Preciso de um servidor público para classificar mensagens do Slack?
Não. Com Socket Mode seu processo abre um WebSocket para o Slack e recebe os mesmos eventos sem Request URL pública. Uma ferramenta no-code com gatilho de Slack é outra opção sem servidor próprio.
Por que a mesma mensagem do Slack foi classificada duas vezes?
Em geral porque o app respondeu ao Slack depois de 3 segundos e o Slack reenviou com o mesmo event_id. Confirme na hora, guarde os event_id tratados e pule repetidos; o JevModel não tem chave de idempotência que faça isso por você.
O bot classifica canais privados ou mensagens diretas?
Só com os eventos de mensagem e escopos de history próprios desses tipos de conversa, e só onde o bot é membro. Este guia cobre canais públicos; para conteúdo privado, obtenha acordo explícito dos responsáveis pelo workspace e envie o mínimo de texto necessário.
Quanto custa classificar mensagens do Slack com o Jev?
Cada mensagem classificada é uma requisição comum à API cobrada em tokens do seu saldo; contas verificadas começam com 100.000 tokens grátis, e o Choice e o Noul na mesma requisição são cobrados juntos. Filtrar mensagens de bots, edições e conversa muito curta antes de chamar reduz o uso. Veja o guia de preços para os pacotes atuais.
JevModel é independente e não é afiliado à TypeSafe AI.