Skip to content

Determinism and Anti Hallucination

Luigi Colluto edited this page Jul 15, 2026 · 1 revision

Determinism & Anti-Hallucination

LLMs hallucinate and are non-deterministic. Argo's design assumes this and wraps the model in deterministic gates and hard physical limits, so a wrong, invented, or unlucky model output is caught, bounded, or made harmless — never silently trusted or acted on.

Guardrails & Safety answers "can Argo do something it shouldn't?" (attack a live host, submit a report). This page answers the adjacent question: "can I trust what the model says, given that models make things up?" Three principles run through the whole pipeline:

  1. Take determinism out of the model's hands wherever a plain algorithm can do the job.
  2. Verify every consequential model claim against ground truth — the code, a schema, a real build, an actual observation.
  3. Physically bound what any run can consume or reach.

The model is a powerful proposer; the value comes from what the deterministic scaffolding does with its proposals.

1. Deterministic computation instead of model output

  • Patch diffs are computed, not written. In Remediation & Fixes the model supplies a full-file rewrite (new_content) or exact search/replace edits; Argo computes the unified diff mechanically with difflib (fixes._mechanical_diff). The model never authors hunk headers, so it can't miscount @@ ranges — the "corrupt patch" failure mode. _apply_edits requires each search to match the file exactly once, else it rejects the whole file rather than half-applying it.
  • Live probes run in a fixed executor, not a model shell. In Runtime Verification / Live Verification the model proposes and interprets a probe plan, but a stdlib executor issues the requests — the model never gets a network/execution primitive.
  • Scoring is a deterministic matcher, not an LLM judge. Benchmarks & Costs matches findings to labels by normalized CWE + file-suffix + line tolerance (benchmark.score_run).
  • Dedup is a pure keydedup_key (file + line + CWE), not a model similarity call.

2. Verify every model claim against ground truth

  • Schema gates. Ingest validates scope.json against its JSON Schema and refuses to proceed on an empty prohibited_techniques; every finding is schema-validated. A malformed audit finding is repaired and flagged, never a silent whole-focus loss.
  • Fixes must build, not just look right. A patch is accepted only if it deterministically applies (git apply -p1, fallback patch --fuzz=0) and compiles/builds (py_compile, node --check, go build, cargo check, or your --build-cmd/--docker) and introduces no new errors vs a pre-patch baseline — all on an isolated copy (verify.verify_patch). A model's "this is fixed" is worthless until the compiler agrees; --re-audit adds a second, unbiased check.
  • Runtime / live confirmation. A static hypothesis becomes confirmed only when an observed runtime signal or in-scope HTTP response agrees, with a differential control probe to judge the difference.
  • Adversarial, refute-first validation (Adversarial Validation). A user-proposed "why didn't you find X?" candidate is re-validated the same way and labelled an interactive probe — never silently promoted into the findings set.
  • Corroboration against reality (Docs & History Corroboration) — downgrade by-design behavior, exclude already-fixed issues.

3. Physical / resource limits (hard caps)

Unbounded model behavior — an infinite loop, a runaway agent — is capped by wall-clock and cost ceilings that fire regardless of what the model is "trying" to do.

  • Wall-clock timeoutssession_timeout_s (default 1800s) + per-stage stage_timeouts; a hung session is killed, not waited on. Cancellation kills the whole process tree.
  • Cost ceilingsbudget_usd HARD per-run abort; session_max_cost_usd (--max-budget-usd); session_max_turns tripwire.
  • Concurrency + fan-outmax_parallel_audits (3); max_focuses (used by --smoke).
  • Sandbox anti-DoS — runtime runtime_max_requests (50) + payload/boot caps; live live_max_requests (30), live_max_writes (5, DELETE always blocked), live_max_redirects (3, each re-validated in-scope). An oversized probe plan is rejected whole, not truncated.

4. Isolation & reproducibility

  • Read-only repo, single chokepoint — the target is mounted read-only to every session; detection never patches it; remediation writes only to patches/. The AgentRunner is the one place tools are granted.
  • Offline by default — non-live stages get no network tools; only the bounded OSINT stages reach the public web, never the in-scope hosts.
  • Files are the source of truth — every stage writes to disk and downstream stages re-read it; nothing hides in model memory.
  • Commit pinningargo pipeline --commit <sha> pins the source to an exact revision: identical input every time, and the basis for reproducible benchmark corpora.
  • Full ledger — every LLM call logged (model, tokens, cost); no hidden spend.

5. Scope & authorization locks (deterministic gates)

Plain-code assertions in argo/guardrails.py that run on the plan, not on a prompt — the model can't talk its way past them: assert_prohibited_present, assert_no_network_tools, assert_loopback_only + validate_probe_plan, assert_live_authorized, assert_inscope_only, assert_live_write_policy. See Guardrails & Safety for what each enforces.

The net effect

Every consequential step is either computed deterministically, verified against ground truth, or physically bounded. A hallucinated or unpredictable model output degrades to the safe outcome — a rejected finding, an unverified patch, a refused probe, an aborted-on-budget run — never a wrong action, a silently-trusted false claim, or an unbounded cost. That is what makes an LLM-native auditor safe to point at an arbitrary repository.

Related

Clone this wiki locally