SentrySentry
IA

LLM Provider

Classificação remota opcional, provider-agnostic via trait LlmProvider

LLM Provider

O LLM é a camada opcional de maior custo: roda como fork para eventos que o hot path e o modelo local não resolveram — score ≥ only_above (30) ou verdict Quarantine. O core nunca chama uma API de LLM diretamente: tudo passa pela trait LlmProvider, e a seleção acontece por config. Default é desligado ([llm] provider = "none").

Trait LlmProvider

#[async_trait]
pub trait LlmProvider: Send + Sync {
    fn name(&self) -> &'static str;   // "openrouter", "ollama", …
    fn model_id(&self) -> &str;       // ex: "anthropic/claude-3.5-sonnet"
    async fn classify(&self, req: ClassifyRequest) -> anyhow::Result<ClassifyResponse>;
    async fn explain(&self, req: ExplainRequest) -> anyhow::Result<String>;
}

Definida em sentry-ai/src/llm.rs. Contratos ao redor:

  • ClassifyRequest — o payload (ProtocolData, serve para Http/Tcp/Tls/…), um resumo humanamente legível truncado e o JSON schema obrigatório da resposta.
  • ClassifyResponse — verdict, risk_score (0–100), risk_level, signals, confidence (0.0–1.0), explanation opcional e usage (LlmUsage: tokens de entrada/saída + custo, quando o provedor reporta).
  • ExplainRequest — explicação free-form de um evento já classificado (CLI e webhooks).

Prompt e parsing compartilhados

Todo adapter envia o mesmo system prompt e o mesmo JSON schema (llm/prompt.rs):

  • SYSTEM_PROMPT — enquadra o modelo como analista de triagem WAF e manda ser conservador: tráfego comum → allow com score baixo; padrões de ataque conhecidos → block/challenge com score alto.
  • classify_schema — schema estrito: enum fechado de verdicts, risk_score 0–100, signals, confidence; campos extras proibidos (additionalProperties: false).
  • context_from_event — resumo do evento para a user message (método, path, query, status, UA, referer, preview do body; SNI/JA3/JA4 para TLS), truncado a 2048 caracteres para custo de token previsível.
  • parse_classify — parsing tolerante: extrai o objeto JSON mesmo cercado de prose ou markdown fences, normaliza tokens ("Rate-Limit" = rate_limit, "deny" = block), clampa score/confidence fora de faixa e re-deriva risk_level do score.

Adapters

Construídos por config em make_llm_provider (que também é usada pelo sentry bench llm). Nome desconhecido ou chave faltando = warning + estágio desligado no run — nunca um crash.

Provider ([llm] provider)ImplementaçãoEndpointAuthNotas
openrouter (recomendado)OpenRouterProviderhttps://openrouter.ai/api/v1/chat/completionsSENTRY_LLM_KEY (bearer) — sem chave, o estágio é desabilitadoUm endpoint roteia para qualquer modelo (Claude, GPT, Gemini, Qwen, Llama, …). Envia response_format: json_schema com strict: true. Timeout 60 s. Model default: openai/gpt-4o-mini.
ollamaOllamaProviderhttp://localhost:11434/api/chat— (local, sem chave)Zero custo, zero vazamento de dados. Schema vai no system prompt + format: "json" (o suporte a response_format varia entre versões do Ollama). Timeout 120 s. Model default: llama3.1.
openaireusa OpenRouterProviderqualquer servidor OpenAI-compatible (base_url; default http://localhost:1234/v1)SENTRY_LLM_KEY enviado como bearer quando setadoLM Studio, vLLM, llama.cpp server — roda keyless em servidor local.
jevJevProviderhttps://api.typesafe.aiSENTRY_JEV_KEY (ou TYPESAFE_API_KEY / JEV_API_KEY / ~/.config/typesafe/key)Classificador calibrado, não generativo: um call responde verdict (choice) + risco 0–9 com probabilidades — sem reparo de JSON, sem alucinação de campos. explain devolve breakdown, não prosa. Model default: jev-latest.
mockMockLlmProvider——Determinístico para testes e wiring (default: block, score 90, confiança 0.9).

Não existe adapter dedicado Anthropic/OpenAI-SDK: use openrouter (que roteia para esses modelos) ou openai com base_url apontando para um endpoint compatível.

O fork no daemon (LlmFork)

LlmFork espelha o fork da IA local:

  • Gatilho: score ≥ only_above (default 30) ou verdict Quarantine.
  • Semáforo [llm] concurrency (default 4) + cache TTL por hash do payload (cache_ttl_secs, default 300 s) — payloads idênticos não re-chamam o LLM na janela.
  • Mapeamento para sinais (llm_signals): resposta benigna (verdict allow com score < 20) não gera sinal algum — o fork só eleva, então sinal de peso zero seria ruído. Resposta maliciosa → sinal LlmMalicious com peso = risk_score × confidence e detail = explanation (ou a lista de sinais).
  • Reentrada pelo rescore_from (só eleva); em mode = "fork" o evento é atualizado no Postgres e as actions aplicáveis re-dispatchadas; em mode = "shadow" o resultado é apenas logado (would change verdict) — nada age.

Configuração [llm] — exemplo mínimo

[llm]
provider = "openrouter"
model = "anthropic/claude-3.5-sonnet"
mode = "fork"                # ou "shadow" p/ observar sem agir
only_above = 30              # escala a partir deste score (ou em Quarantine)
concurrency = 4
cache_ttl_secs = 300
export SENTRY_LLM_KEY="sk-or-v1-…"

Chaves e defaults em LlmConfig; o provider padrão é none (desligado). Para rodar 100% local, troque para provider = "ollama" com model = "llama3.1" — nenhuma chave, nenhuma mudança de código.

Avaliando providers

sentry bench llm --providers mock,ollama,openrouter --n 200

O harness sentry bench llm compara provedores em latência, concordância com o pipeline de heurísticas, qualidade do output e custo — com kit sintético rotulado embutido ou replayando um access.log (--events) — antes de você commitar dinheiro num provider.

On this page