You know the hunt you want to run; you just do not want to hand-write the KQL or SPL for it. LLMs are strong at query generation and terrible at knowing your schema — they will confidently reference a field that does not exist in your index.
Tier: Foundation · Workflow area: AI-assisted SOC & detection
This playbook turns a plain-English hunt into a runnable SIEM query, with a validation loop that rejects hallucinated fields before they waste a search cycle.
Skip AI for a one-field filter change to a query you already have open — editing is faster than reviewing a regenerated search.
Give the model your schema, not just your hunt. The single biggest quality lever is pasting the table/index name and its real field list. Without it, the model invents SourceIP when your schema says src_ip.
Write a Microsoft Sentinel KQL query for this hunt against sample data.
Hunt: interactive logons by accounts named like service accounts
(starting "svc_") outside 08:00-20:00 IST, in the last 24h.
Table: SigninLogs
Available fields: TimeGenerated, UserPrincipalName, ResultType,
AppDisplayName, IPAddress, ClientAppUsed, Location.
Rules: use only fields I listed. If the hunt needs a field I did not
provide, stop and tell me which field is missing instead of inventing one.
Add a comment above each clause explaining what it filters.
Expected output: a commented KQL query using only your listed fields, or an explicit "I need a field for logon-type that you did not provide" — both are useful answers.
Read the WHERE/where clauses before you run. Confirm each referenced field is in your schema and each literal (time window, account prefix) matches your intent. The comments the model wrote make this a 30-second read.
Run against sample data first. Execute in your community-edition SIEM against a bounded, non-production index. A query that returns zero rows on data you know contains matches is a logic bug — usually an over-tight time filter or a wrong operator (== vs has).
Sanity-check the row count. A hunt that returns millions of rows or zero rows is almost always wrong. Ask the model to explain: "This returned 0 rows but I expect ~50; what clause is too strict?" and fix the named clause only.
Save the validated query with its schema note. Store the query alongside the schema version it was written against, so the next person knows why it references those exact fields.
Paste the field list every single time. A model that guessed your schema correctly last week will guess differently this week — grounding it in the real fields is cheaper than debugging a silent mismatch.
.getschema in KQL, | fieldsummary in SPL). One unknown field means the model reasoned from an imagined table — reject and re-prompt with a tighter field list.UserPrincipalName == "svc" matches nothing; startswith "svc_" is what the hunt meant. Models mix these up constantly.parse_json() or Computer fields that your schema does not have. The schema diff is non-negotiable.ago(24h) regardless of your stated timezone or business hours. Re-read every time clause against your intent.Client schemas are themselves sensitive — field names can reveal architecture. In a services engagement, run the generation on a local Ollama model or the client's approved AI endpoint, and hand the model a generalized schema: keep the field names it needs to write correct syntax, but strip anything identifying (index names, client-specific prefixes) to placeholders. Validate exclusively against the client's sample or lab indices, never their production data, and store the query inside the client's own repository so the schema note travels with it. The query logic is portable; the identifiers stay behind the client boundary.