August 11, 2026
From threat report to hunt: Building a threat hunting AI agent with LangChain — The Big Rollback #8
When a new threat report lands (a vendor blog post, a Hacker News writeup, a CISA advisory, a tweet with a fresh CVE…) the hunt itself is…

By Kelvin Santos[zdk]
4 min read
When a new threat report lands (a vendor blog post, a Hacker News writeup, a CISA advisory, a tweet with a fresh CVE…) the hunt itself is the hard part: forming a hypothesis, interpreting the findings, deciding what actually matters in your environment. But before any of that, there's the setup: pulling the IOCs out of the text, cross-checking against other sources, mapping them to techniques, working out which tables apply, writing the queries, and pulling it all together into a documented hunt.
That setup happens every single time a report comes in, and it's the same shape every time.
That's why Polaris was born.
The idea
After Skopos, I wanted to keep exploring AI agents in security operations, this time closer to the blue team work I do day to day.
The concept is simple: give the agent a report URL or a pasted report, and it runs the setup pipeline itself:
- It searches for related information across other sources
- extracts the IOCs and MITRE techniques
- writes the hunting queries for your SIEM
- and generates a walkthrough report.
No stopping to ask which steps you want. One prompt in, one draft report out for you to review.
Implementation
Stack:
- LangChain / LangGraph — agent orchestration (create_agent)
- Azure AI Foundry — model hosting for the agent.
- Tavily, trafilatura, ioc-finder — search, article extraction, IOC parsing.
The core of Polaris is a reason-and-act loop: the LLM decides which tool to call next, observes the result, and repeats until it has enough to write the report. Fetch the article, extract the IOCs, search for corroborating sources, generate the queries.
The part I find most interesting is where the domain knowledge lives. Instead of one enormous system prompt, the knowledge of how to write correct Sentinel KQL lives in a skill the agent loads on demand: a reference of real table names, real columns, and the query patterns my team actually uses. The model doesn't reason from scratch, it pulls in verified material first.
One more piece matters here more than it did in Skopos. This agent reads external, untrusted content for a living, so everything that comes back from a fetch or a search passes through a second, cheaper model call first: a prompt-injection guardrail. It fails closed, if the check itself errors, the content is withheld rather than trusted.
Results
Feed Polaris a URL and it runs the whole pipeline end to end. Here's a run against a Check Point SmartConsole advisory (CVE-2026–16232). It pulled the article, extracted the IOCs, mapped them to techniques, and generated a hunting query, all from a single URL
The agent when it starts:
The report it produced, with the summary, the IOC table, and the mapped techniques:
And the KQL it wrote for the extracted indicators:
That's the core of it working: report in, structured hunt out, ready for an analyst to review.
One thing worth sharing from building it, though, because it surprised me. The hardest part wasn't getting the agent to write KQL, it was getting it to write KQL that runs against a real Sentinel workspace. The model has no problem with syntax, every query parses cleanly. Where it struggles is schema, and that's a sneakier problem than a syntax error, because the query looks completely trustworthy right up until you paste it into Sentinel and it fails:
DeviceProcessEvents
| project Timestamp, DeviceName, ProcessCommandLine
// Timestamp is the column name in Defender's Advanced Hunting portal.
// Inside a Sentinel workspace, the same table uses TimeGenerated.
union CommonSecurityLog, AzureDiagnostics, AzureNetworkAnalytics_CL
// That third table doesn't exist in most workspaces.
// The whole query fails unless you add isfuzzy=true.DeviceProcessEvents
| project Timestamp, DeviceName, ProcessCommandLine
// Timestamp is the column name in Defender's Advanced Hunting portal.
// Inside a Sentinel workspace, the same table uses TimeGenerated.
union CommonSecurityLog, AzureDiagnostics, AzureNetworkAnalytics_CL
// That third table doesn't exist in most workspaces.
// The whole query fails unless you add isfuzzy=true.These aren't exotic mistakes, they're the near-misses of a model that learned KQL from mixed contexts. The answer wasn't a smarter model, it was grounding: the skill loads a real schema reference before writing anything, and every query that failed for real got added to that reference, not just patched once. It doesn't fully solve it, the model can still reach for a column that isn't there, but it moves the needle a lot. Which is exactly why the analyst reviewing the output isn't optional.
Final Notes
Polaris is a support tool, not an autopilot. It drafts the queries and the report; the analyst reviews, interprets, and decides. The model reasons and drafts, it doesn't decide what a finding means, and I think that boundary should stay exactly where it is.
The value isn't replacing the hunt, it's absorbing the repetitive setup that comes before it, at a scale where a SOC is dealing with a constant stream of reports and not enough hours.
Picture a workflow that's already receiving a stream of CVE notifications and threat intel. Now imagine an agent picking up that content as it arrives and handing analysts a ready-made starting point for each one, IOCs extracted, techniques mapped, draft queries written, before anyone even opens the report. That's where this gets genuinely useful.
Agentic doesn't mean replacement. Think of it as enhancement.
In the next post, I want to explore agentic and MCP.
Link to the repo: https://github.com/zdr4k/Polaris