<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adil Eskintan</title>
    <description>The latest articles on DEV Community by Adil Eskintan (@eskintan).</description>
    <link>https://dev.to/eskintan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022906%2F08271afd-968d-4a39-8438-23494758d457.png</url>
      <title>DEV Community: Adil Eskintan</title>
      <link>https://dev.to/eskintan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eskintan"/>
    <language>en</language>
    <item>
      <title>I built an autonomous DFIR agent on Qwen Cloud that refuses to trust itself</title>
      <dc:creator>Adil Eskintan</dc:creator>
      <pubDate>Thu, 09 Jul 2026 14:16:25 +0000</pubDate>
      <link>https://dev.to/eskintan/i-built-an-autonomous-dfir-agent-on-qwen-cloud-that-refuses-to-trust-itself-5344</link>
      <guid>https://dev.to/eskintan/i-built-an-autonomous-dfir-agent-on-qwen-cloud-that-refuses-to-trust-itself-5344</guid>
      <description>&lt;h2&gt;
  
  
  An autonomous incident-response agent on Qwen models where deterministic code, not the model, gets the final word, and a two-tier run plus an ablation that proves the trust layer resolves uncertainty without ever manufacturing a confirmation.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The problem nobody wants to say out loud
&lt;/h2&gt;

&lt;p&gt;AI is being adopted fastest exactly where a wrong answer is most expensive:&lt;br&gt;
security investigations, incident response, digital forensics. A SOC analyst&lt;br&gt;
under pressure at 3 a.m. is &lt;em&gt;extremely&lt;/em&gt; tempted to let a model write the verdict.&lt;/p&gt;

&lt;p&gt;But you cannot ship a forensic conclusion you cannot audit. If an AI says "this&lt;br&gt;
host is compromised," a court, an auditor, or an incident commander asks one&lt;br&gt;
question: &lt;strong&gt;prove it.&lt;/strong&gt; "The model was confident" is not proof.&lt;/p&gt;

&lt;p&gt;So for this hackathon I built &lt;strong&gt;Sentinel Qwen Ensemble&lt;/strong&gt;, an autonomous DFIR agent&lt;br&gt;
that runs end-to-end on &lt;strong&gt;Qwen models hosted on Alibaba Cloud (DashScope)&lt;/strong&gt;, and&lt;br&gt;
whose entire design is organized around a single rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The AI proposes. Deterministic code disposes. The model never gets the final word.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What it actually is (Track 4: an autopilot agent)
&lt;/h2&gt;

&lt;p&gt;Point it at Windows evidence (a memory image, a disk image, or both) and walk&lt;br&gt;
away. A &lt;strong&gt;16-step deterministic conductor&lt;/strong&gt; runs the whole investigation with&lt;br&gt;
zero human steering (the 16 steps, condensed):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SHA-256 fingerprints the evidence (chain of custody, before anything else).&lt;/li&gt;
&lt;li&gt;Checks kernel integrity.&lt;/li&gt;
&lt;li&gt;Asks Qwen which forensic tools to run for &lt;em&gt;this&lt;/em&gt; evidence.&lt;/li&gt;
&lt;li&gt;Runs them: &lt;strong&gt;195 typed forensic tools&lt;/strong&gt; (Volatility 3, Sleuth Kit, EZ Tools,
Plaso, YARA) exposed through a custom &lt;strong&gt;MCP server with zero shell access&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Builds a typed evidence database, cross-referencing every PID, IP, path, hash.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;4-member Qwen ensemble&lt;/strong&gt; analyzes the evidence in parallel.&lt;/li&gt;
&lt;li&gt;A deterministic validator checks &lt;strong&gt;every claim&lt;/strong&gt; against the exact tool output
that produced it.&lt;/li&gt;
&lt;li&gt;A ReAct loop lets the agent re-investigate its own suspicious findings.&lt;/li&gt;
&lt;li&gt;A consolidated Step-13AA adjudication, ~13 fail-closed promotion gates, and a
final disposition step decide what, if anything, is "confirmed."&lt;/li&gt;
&lt;li&gt;Out comes a verified, risk-ranked incident report, with an optional
human-in-the-loop approval checkpoint before the report is written.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The model is only ever invoked &lt;em&gt;inside&lt;/em&gt; well-bounded steps. Everything that&lt;br&gt;
decides what reaches the report is plain, auditable Python.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it runs on Qwen Cloud (env only, no code change)
&lt;/h2&gt;

&lt;p&gt;The whole port is one small provider seam, &lt;code&gt;make_llm_client()&lt;/code&gt; plus a stdlib-only&lt;br&gt;
DashScope (OpenAI-compatible) adapter. No model literal is hardcoded anywhere in&lt;br&gt;
the 16-step pipeline; the provider and model are chosen purely by environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SIFT_LLM_PROVIDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qwen
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DASHSCOPE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...          &lt;span class="c"&gt;# your Qwen Cloud key&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SIFT_DEFAULT_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qwen3.7-max
./setup.sh /path/to/case   &lt;span class="c"&gt;# one line: builds the Docker image, forwards these envs, mounts evidence read-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Model tiering keeps it cheap: &lt;code&gt;qwen-plus&lt;/code&gt; for the high-volume work (ensemble,&lt;br&gt;
ReAct, tool selection, report), &lt;code&gt;qwen3.7-max&lt;/code&gt; reserved for keystone adjudication.&lt;br&gt;
There is real DashScope-specific engineering under that seam, too: implicit&lt;br&gt;
prompt-cache accounting (the rd01 heavy run reused &lt;strong&gt;381,696 cached tokens&lt;/strong&gt;, ~36% off;&lt;br&gt;
the featured DC01 heavy run reused &lt;strong&gt;371,072&lt;/strong&gt;),&lt;br&gt;
a &lt;code&gt;reasoning_content&lt;/code&gt; fallback for Qwen thinking mode, per-model output-cap&lt;br&gt;
clamps, and bounded read-timeout retries that fixed a live-run failure.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two tiers, one public case: depth scales, the confirmation bar doesn't
&lt;/h2&gt;

&lt;p&gt;I ran the &lt;strong&gt;same&lt;/strong&gt; real Windows intrusion case through the &lt;strong&gt;identical trust&lt;br&gt;
layer&lt;/strong&gt; at two Qwen model tiers. Nothing changed but the model. And the case is&lt;br&gt;
&lt;strong&gt;public and reproducible&lt;/strong&gt;: DFIR Madness "Stolen Szechuan Sauce" &lt;strong&gt;DC01&lt;/strong&gt; (2 GB&lt;br&gt;
memory + ~4.9 GB two-segment E01 disk), which any judge can download and rerun end to end. Both&lt;br&gt;
images were mounted read-only and SHA-256 verified; the runs record their own&lt;br&gt;
provenance, so this is not a claim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"llm_provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen3.7-max"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"llm_endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"integrity_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Light (&lt;code&gt;qwen-plus&lt;/code&gt; x4)&lt;/th&gt;
&lt;th&gt;Heavy (&lt;code&gt;qwen3.7-max&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Findings (final)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confirmed malicious&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs review&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Benign&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;3m 46s&lt;/td&gt;
&lt;td&gt;14m 39s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost (cache-aware)&lt;/td&gt;
&lt;td&gt;~$0.22&lt;/td&gt;
&lt;td&gt;~$1.67&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools (swept / hit / failed)&lt;/td&gt;
&lt;td&gt;33 / 29 / 0&lt;/td&gt;
&lt;td&gt;33 / 27 / 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrity (mem + disk)&lt;/td&gt;
&lt;td&gt;MATCH&lt;/td&gt;
&lt;td&gt;MATCH&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the &lt;strong&gt;light&lt;/strong&gt; tier the ensemble surfaced a single lead, and the deterministic&lt;br&gt;
layer held it at &lt;strong&gt;needs-review&lt;/strong&gt; rather than confirming it. &lt;strong&gt;1 finding, 0&lt;br&gt;
confirmed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;heavy&lt;/strong&gt; tier the 4-member ensemble reconstructed the &lt;strong&gt;entire intrusion&lt;/strong&gt;:&lt;br&gt;
the &lt;code&gt;coreupdater.exe&lt;/code&gt; C2 beacon, outbound and inbound RDP, data staged for exfil to&lt;br&gt;
&lt;code&gt;\FileShare\Secret&lt;/code&gt;, code injection into &lt;code&gt;explorer&lt;/code&gt;/&lt;code&gt;svchost&lt;/code&gt;/&lt;code&gt;spoolsv&lt;/code&gt;, and&lt;br&gt;
scheduled-task plus WMI persistence, attributed to &lt;code&gt;administrator&lt;/code&gt;/&lt;code&gt;public&lt;/code&gt; and&lt;br&gt;
mapped to &lt;strong&gt;5 MITRE tactics&lt;/strong&gt; (Execution, Persistence, Defense Evasion, Lateral&lt;br&gt;
Movement, Command and Control), overall risk &lt;strong&gt;CRITICAL&lt;/strong&gt;. That is &lt;strong&gt;44 findings&lt;/strong&gt;&lt;br&gt;
and the whole attack laid out. And still &lt;strong&gt;0 confirmed&lt;/strong&gt;: 21 held at needs-review,&lt;br&gt;
23 judged benign, &lt;strong&gt;0 left inconclusive&lt;/strong&gt;. The engine saw the full compromise and&lt;br&gt;
refused to stamp "confirmed" on a single lead, because none of them carried atomic&lt;br&gt;
proof, and on this design &lt;em&gt;no evidence means no confirmation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Depth scales with the model tier (1 -&amp;gt; 44 findings); the confirmation bar does&lt;br&gt;
not.&lt;/strong&gt; The 0-confirmed result here is not a gap, it is the trust layer working. Two&lt;br&gt;
more things held on both tiers: &lt;strong&gt;0 tool failures&lt;/strong&gt; (a fix pass added &lt;code&gt;foremost&lt;/code&gt;&lt;br&gt;
plus MFTECmd/SBECmd/RBCmd and made Sleuth Kit offset-aware, so 33 tools swept with&lt;br&gt;
none failing), and &lt;strong&gt;0 inconclusive&lt;/strong&gt; findings, because the consolidated Step-13AA&lt;br&gt;
adjudication skipped the wasteful generative self-correction and re-judged every&lt;br&gt;
ambiguous finding to a final verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  And when atomic proof is present, the same engine confirms
&lt;/h2&gt;

&lt;p&gt;DC01 shows the engine holding leads honestly when nothing is atomic. A second real&lt;br&gt;
intrusion case (&lt;code&gt;rd01&lt;/code&gt;) shows the other half: when the evidence &lt;em&gt;is&lt;/em&gt; atomic, the&lt;br&gt;
identical layer &lt;strong&gt;confirms&lt;/strong&gt;. On the heavy tier, &lt;strong&gt;4 findings cleared every&lt;br&gt;
confirmation gate&lt;/strong&gt;: PsExec lateral movement, PWDumpX credential dumping, an IFEO&lt;br&gt;
&lt;code&gt;sethc.exe&lt;/code&gt; sticky-keys backdoor, and a payload (&lt;code&gt;p.exe&lt;/code&gt;) run from a temp directory.&lt;br&gt;
Each traces to the exact tool output that proved it. On the light tier, &lt;code&gt;rd01&lt;/code&gt;&lt;br&gt;
confirmed &lt;strong&gt;0&lt;/strong&gt;. Same gates, different depth: the bar is the constant, the model&lt;br&gt;
tier just changes how much clears it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ablation: the layer resolves uncertainty, it never manufactures a confirmation
&lt;/h2&gt;

&lt;p&gt;Here is the experiment I care about most. A skeptic could say: sure, but does the&lt;br&gt;
"trust layer" just rubber-stamp whatever the flagship wants? So on &lt;code&gt;rd01&lt;/code&gt; I ran the&lt;br&gt;
&lt;strong&gt;same case, same &lt;code&gt;qwen3.7-max&lt;/code&gt;&lt;/strong&gt;, and toggled only the Step-13AA finalization flags:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trust-layer finalization&lt;/th&gt;
&lt;th&gt;Confirmed&lt;/th&gt;
&lt;th&gt;Inconclusive&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;ON&lt;/strong&gt; (as shipped)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OFF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With finalization &lt;strong&gt;off&lt;/strong&gt;, 11 findings are stranded at inconclusive and only one&lt;br&gt;
clears confirmation. Turn it &lt;strong&gt;on&lt;/strong&gt; and the layer re-judges every ambiguous&lt;br&gt;
finding to a final verdict: inconclusive collapses from 11 to &lt;strong&gt;0&lt;/strong&gt;, and the&lt;br&gt;
intrusion chain re-confirms. Crucially, in &lt;em&gt;both&lt;/em&gt; runs every promotion still had to&lt;br&gt;
pass the same deterministic eligibility gate. &lt;strong&gt;The layer resolves uncertainty; it&lt;br&gt;
never manufactures a confirmation.&lt;/strong&gt; (That 3-confirmed reproduction is one shy of&lt;br&gt;
June's 4, which is normal model non-determinism, and I would rather report that&lt;br&gt;
honestly than round it up.)&lt;/p&gt;

&lt;p&gt;That, to me, is the whole point: you can measure the trust, not just assert it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I think this matters
&lt;/h2&gt;

&lt;p&gt;Most "AI agent" demos optimize for the happy path: look how much it found. The&lt;br&gt;
harder and more valuable thing in high-stakes domains is the opposite: &lt;strong&gt;look how&lt;br&gt;
disciplined it is about what it refuses to claim.&lt;/strong&gt; Sentinel Qwen Ensemble is built so&lt;br&gt;
the trust is &lt;em&gt;provable&lt;/em&gt;: every confirmed finding traces to the exact tool output&lt;br&gt;
that proves it, evidence is hashed before and after, and the code, not the model,&lt;br&gt;
owns the verdict.&lt;/p&gt;

&lt;p&gt;Qwen Cloud made the agent's reasoning cheap enough to run a four-member ensemble&lt;br&gt;
plus a ReAct re-investigation loop on every case for the price of a coffee. The&lt;br&gt;
trust layer made that reasoning &lt;em&gt;safe to act on.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo (MIT):&lt;/strong&gt; &lt;a href="https://github.com/3sk1nt4n/Sentinel-Ensemble-Qwen" rel="noopener noreferrer"&gt;https://github.com/3sk1nt4n/Sentinel-Ensemble-Qwen&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-cost demo (no key, no evidence, any OS):&lt;/strong&gt; &lt;code&gt;./setup.sh docker&lt;/code&gt; (Windows: &lt;code&gt;.\setup.cmd docker&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof-of-Alibaba-Cloud code:&lt;/strong&gt; &lt;code&gt;src/sift_sentinel/llm_provider.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shipped run metrics (public DC01 case, both tiers, plus the &lt;code&gt;rd01&lt;/code&gt; confirm + ablation):&lt;/strong&gt; &lt;code&gt;docs/qwen-runs/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://youtu.be/A53FpVgdnnU" rel="noopener noreferrer"&gt;https://youtu.be/A53FpVgdnnU&lt;/a&gt; (2:50, DC01 public case; also in-repo &lt;code&gt;docs/sentinel-qwen-demo.mp4&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>qwencloud</category>
      <category>alibabacloud</category>
      <category>dfir</category>
    </item>
  </channel>
</rss>
