Wazuh AI triage earns its place by doing one job: turning a finding into a short, evidence-backed verdict that an analyst can accept or overturn in seconds. The model does not decide, does not close anything, and never touches an endpoint. This lesson covers where a language model sits in the 5.0 pipeline, what context to give it, how to constrain its output, why log content is hostile input, and how to measure whether it is helping.

What you need

  • A Wazuh 5.0 deployment producing findings. If findings and events are still new terms, read rules, decoders and severity levels explained first.
  • An intake path off the indexer. The pattern here assumes an Alerting monitor delivering to a webhook, as set up in SOAR with Wazuh.
  • A decision, in writing, about what data may leave your deployment and to whom.
  • Enough tuning already done that the model is not being asked to paper over noise. See cutting false positives.
  • The deployment itself already locked down, since you are about to add an outbound data path to it. The checklist is in hardening Wazuh itself.

What Wazuh 5.0 ships, and what it does not

Wazuh 5.0 documents an AI Assistant in the dashboard. It is reached from the menu under Explore, and it gives analysts a conversational interface over security data: questions about vulnerabilities, findings and agent activity, answered in writing with structured results that link into Discover for further investigation. Conversations are saved so an analyst can pick an investigation back up.

The configuration surface, per the documentation, is:

  • Providers. Add a provider under the Settings tab with a name, a provider type, an endpoint URL and an API key. The documented types are OpenAI-compatible (covering OpenAI, Gemini, Ollama and LM Studio), Anthropic, and the Wazuh AI Assistant Brain.
  • Privacy settings. A field policy can anonymise or exclude specific finding fields before data is sent to the provider. Privacy mode can be enabled by default across the cluster, and you can decide whether users may override it per conversation from the chat page.
  • Conversation history. A retention setting controls how long saved conversations are kept. Setting it to 0 keeps them indefinitely.

Under the hood the assistant stores conversations in a wazuh-ai-assistant-sessions data stream. Access is granted by the wazuh_ai_assistant role, which is mapped to every authenticated user and carries a document-level security filter on the conversation owner, so a user retrieves only their own conversations. Provider configuration and the field policy live in a hidden internal-state index reached only through administrative endpoints on the indexer API under /_plugins/_setup/ai_assistant/.

That is the documented scope: an interactive analyst tool. The beta documentation does not describe an automated triage hook that runs the assistant against every incoming finding, and I have not found one. So if you want triage rather than conversation, you build it in the intake path yourself, which is what the rest of this lesson is about.

Where the model belongs in the pipeline

The intake path in 5.0 runs: detector produces a finding, an Alerting monitor queries the findings data streams on a schedule, a trigger fires, and an action delivers a message through a Notifications channel. A generic webhook channel pointing at your automation platform is the join.

Put the model one step further along, inside the automation platform, and give it a narrow slot:

  1. The webhook arrives with the finding identifiers.
  2. The automation platform enriches: pulls the full finding, the asset record, recent related activity.
  3. The model reads that package and returns a structured verdict.
  4. The platform applies policy to the verdict and acts: create a ticket, downgrade and tag, or escalate.
  5. Everything the model produced is attached to the case for review.

The separation matters. The model is a classifier with an explanation, not an agent with permissions. The platform holds the credentials and makes the changes, based on a policy you wrote, applied to a verdict the model proposed.

What context the model actually needs

Under-context is the most common cause of bad verdicts, and over-context is the most common cause of a large bill. Build a fixed envelope and fill it from sources you control.

The finding itself, trimmed. Not the whole document. The fields that carry meaning: wazuh.rule.title, wazuh.rule.level, the MITRE mapping, wazuh.agent.name, wazuh.agent.groups, @timestamp, the relevant WCS fields for the event type, and event.original clearly labelled as raw log text.

The rule’s intent. The rule title alone tells the model what matched, not what the detection was written to catch or what a true positive looks like. A one-paragraph description per rule, written by whoever authored it, is the single highest-value piece of context you can add. Keep it beside the rule in your content repository.

Asset context. What is this host, who owns it, is it production, is it internet-facing, is it a jump box where administrative behaviour is normal. Wazuh gives you a hook for this already through agent groups, which arrive on every document as wazuh.agent.groups; agent labels did the same job on 4.x, but the 5.0 beta does not document them yet, so do not build on wazuh.agent.labels.* until GA confirms it. Use groups rather than a separate CMDB lookup where you can.

Recent related activity. A short, bounded window from the same agent and the same rule, aggregated rather than listed: how many times this fired on this host in the last day, when it last fired, whether other hosts saw it in the same window. This is what turns “a failed login” into “the twentieth failed login this hour from one source”.

Prior decisions. If this rule and host combination was triaged and downgraded last week with a reason, the model should see that. This is where most of the compounding value is, and it needs no model to produce.

What to leave out: the rule’s full Sigma body, unrelated findings, entire log files, and anything your data policy has not cleared for the provider.

Constrain the output to a verdict contract

Free text is unreviewable and unmeasurable. Make the model fill in a schema and reject anything that does not validate.

The contract in the companion files for this lesson asks for these fields:

Field Purpose
verdict One of true_positive, likely_benign, needs_human, insufficient_context
confidence low, medium or high
rationale Two or three sentences, plain language
evidence A list of items, each naming the field or lookup it came from and quoting the value
proposed_action One of the allowed actions, and nothing else
unanswered What the model could not determine and what would settle it

Two of those fields do most of the work.

evidence forces the model to point at something. Every item names its source, so a reviewer can check it in one click. A verdict whose evidence list is empty or whose items cite nothing in the envelope gets rejected by the platform before a human ever sees it, no matter how confident the prose sounds.

insufficient_context gives the model somewhere honest to go. Without it, a model handed a thin envelope will produce a confident guess, because that is what the format asks for. Make not knowing a valid, cheap answer and you get it instead of fiction.

Which actions the model may propose

Write the allowed list down and enforce it in code, not in the prompt. A prompt is guidance; the platform’s action handler is the control.

Allowed:

  • Enrich. Add context to the case: asset owner, prior decisions, a link to the rule’s intent note, a summary for the ticket body.
  • Downgrade within a band. Move a low or informational finding into a review queue instead of a page, under a documented policy, with the verdict and evidence attached.
  • Ask a human. Escalate with a specific question rather than a summary. “This matched on a build agent where the pattern is routine; confirm whether this host is in scope” is worth more than three paragraphs of restatement.
  • Group. Propose that a set of findings belongs to one incident.

Not allowed:

  • Closing anything at high or critical severity. No exceptions, no confidence threshold that unlocks it. If the model is right ninety-nine times out of a hundred, the hundredth is the incident you will explain in a report.
  • Suppressing a rule or editing detection content. Tuning is a reviewed change to content in the Custom space, with an owner and an expiry date. A model proposing a tuning change is fine; a model making one is not.
  • Anything that touches an endpoint. Active response in 5.0 is driven by an Alerting monitor writing to the wazuh-active-responses data stream, which the manager polls and dispatches. Keep that path free of model output entirely. My position on active response has not changed: off by default, opted into per environment after tuning, as covered in when to leave active response off.

Treat log content as hostile input

This is the part most AI triage designs get wrong, and it is a straightforward attack.

An attacker who can influence anything you collect can write text into it. A username field, a URL path, a user agent string, a filename, a commit message. That text is decoded, normalised and preserved in the finding as event.original, and your intake sends it to the model. If the model treats the envelope as one undifferentiated block of instruction, the attacker gets a say in its verdict, and the payload is as simple as a line of prose asking for the activity to be classified as routine.

Defences, in the order they matter:

  1. Structural separation. Event-derived content goes in a clearly delimited data section with an explicit statement that nothing inside it is an instruction. Your policy, your rules and your allowed actions live in the system prompt, which never contains event data.
  2. Schema-constrained output. A verdict that must validate against a fixed schema gives injected text much less room. It cannot invent an action that the enum does not contain.
  3. Enforcement outside the model. The action handler checks the proposed action against the allowed list for that severity band. Injected text that persuades the model to propose closing a critical finding still fails at the handler.
  4. Evidence citation. An injected instruction that produces a verdict with no field-backed evidence gets rejected automatically.
  5. Watch for it. Log the cases where a verdict was rejected for a schema or policy violation. A rise in that count on one source is a signal worth chasing, not just a quality metric.

Control the cost before it controls you

Wazuh 5.0 indexes every event, not only rule matches, so the volume above the triage layer is larger than a 4.x instinct expects. Findings are the smaller population, but they are still the wrong unit to bill per item without thought.

  • Do not triage everything. Set an entry gate: severity band, integration category, or rules you have explicitly opted in. Start with one noisy family you already understand.
  • Deduplicate first. Detectors run on a schedule and the same underlying activity can produce a run of near-identical findings. Group by rule and agent within a window and triage the group once. This usually cuts volume more than any model choice.
  • Cache the stable parts. Rule intent notes and asset records change slowly. Prompt caching pays for itself immediately when the same system prompt and the same rule description go out hundreds of times a day.
  • Size the model to the job. Classification with citation is not the hardest task you will ever give a model. Benchmark a smaller one against your own labelled set before assuming you need the largest.
  • Cap and alert. A runaway loop in an automation platform is a normal outage, not an exotic one. Put a hard daily ceiling on calls and alert when it is approached.
  • Keep the option of local. The documented Wazuh AI Assistant provider types include OpenAI-compatible endpoints such as Ollama and LM Studio, which means a self-hosted model is a supported shape for the assistant. The same choice applies to your own intake path, and it changes the data-governance conversation as much as the cost one.

Measure precision, or you are guessing

Treat the triage layer as a detection component and hold it to the same standard.

Label a weekly sample by hand. Take a fixed number of the model’s verdicts, have an analyst judge each one blind, and record the disagreements. Track four numbers:

  • Downgrade error rate. How often something the model called likely benign was not. This is the number that can hurt you, and it is the one to hold to a low ceiling.
  • Escalation precision. Of the findings the model sent to a human, how many deserved it. This is the number that decides whether analysts keep trusting the queue.
  • Evidence quality. How often the cited evidence actually supports the verdict. A correct verdict with invented support is a failure, because it will be wrong later for the same reason.
  • Analyst time to decision. The reason the layer exists. If this does not fall, the rest is decoration.

Two rules of operation. Run a shadow period first, where the model produces verdicts that nobody acts on and analysts work as before, so you get a labelled set without risk. And re-measure after every change, to the prompt, to the model version, to the envelope. A prompt edit is a configuration change to a detection control and deserves the same change record.

Common failure modes

The model restates the finding. Nothing in the envelope beyond the finding itself, so there is nothing to reason with. Add rule intent and asset context before touching the prompt.

Confident verdicts on thin evidence. The schema has no insufficient_context option, or the prompt rewards a decision. Make the honest answer available and cheap.

Analysts stop reading the verdicts. Almost always caused by a wrong downgrade that reached someone. Trust recovers slowly. Start in shadow mode, and keep the downgrade band narrow for longer than feels necessary.

Cost climbs with no volume change. Usually retries on schema validation failures, or a duplicate storm from a detector run that nobody deduplicated. Log both.

Verdicts drift after a content update. New or retuned rules arrive without intent notes, so the model is working from titles. Make the note part of the rule’s promotion from Draft to Custom, as described in writing Sigma YAML detection rules.

Primary sources

The dashboard AI Assistant, its provider types, privacy settings and conversation retention are documented on the Wazuh dashboard configurations page. The intake side, monitors, triggers and notification channels, is on the Alerting module page. For what a finding contains and where it comes from, see Findings.

Next: Wazuh 5: What Changes and Why It Matters.

Files for this lesson

Every file this lesson refers to, exactly as used. Open a file to read it in the browser, or download the set as a zip. Download wazuh-ai-triage.zip. The full set for all lessons is at wazuh-tutorial-files.zip.

Frequently asked questions

Does Wazuh have a built-in AI assistant?

Wazuh 5.0 documents an AI Assistant in the dashboard under Explore. It is a conversational interface for asking questions about findings, vulnerabilities and agent activity in natural language, with results that link back to Discover. You configure a provider, an endpoint URL and an API key, and you can set a field policy that anonymises or excludes fields before data leaves the deployment.

Can an LLM close Wazuh alerts automatically?

It should not close anything at high or critical severity, and in most deployments it should not close anything at all. Let the model propose a verdict with evidence and let it downgrade or enrich low-severity noise under a written policy. Auto-closing is where AI triage turns into an undetected blind spot.

Is it safe to send Wazuh log data to an external AI provider?

Only after you decide what may leave. Findings carry hostnames, usernames, IP addresses, file paths and often raw log text. The Wazuh AI Assistant supports a field policy that anonymises or excludes named fields before sending, and privacy mode can be enforced by default across the cluster. Whatever tool you use, apply the same filter and get the decision signed off.

Can log content manipulate the model?

Yes. An attacker who can write to a monitored log can write instructions into it, and that text arrives in the finding as event.original. Treat every field originating from an event as untrusted data, never as instruction, and keep the model's output constrained to a schema so injected text has nothing to steer.

How do I know whether AI triage is working?

Measure it like a detection. Sample the model's verdicts against analyst judgement each week, track how often a downgrade was wrong, and watch that number rather than the volume the model handled. A tool that clears a lot of findings while being wrong occasionally is worse than no tool at all.