Workflow Threshold Tuning¶
The agentic workflow's Thresholds config block (src/config/workflow_config_schema.py,
ThresholdConfig) holds three gating values plus one sibling not covered here
(JunkFilterThreshold, see content filter docs):
| Threshold | Default | Range | Gates |
|---|---|---|---|
MinHuntScore |
97.0 | 0-100 | Not currently enforced; see below |
RankingThreshold |
6.0 | 0-10 | Whether the workflow continues past the LLM RankAgent step |
SimilarityThreshold |
0.5 | 0.0-1.0 | Whether a generated SIGMA rule is queued or dropped as a near-duplicate |
All three live in AgenticWorkflowConfigTable (src/database/models.py:456-458),
are read at runtime from state["config"] inside src/workflows/agentic_workflow.py,
and can be changed via PUT /api/workflow/config (src/web/routes/workflow_config.py:468)
or the workflow config UI.
RankingThreshold (6.0)¶
Gates whether the workflow continues past the ranking step. The LLM RankAgent
scores an article's relevance 0-10; if ranking_score < RankingThreshold, the
workflow terminates with TERMINATION_REASON_RANK_THRESHOLD and never reaches
extraction or SIGMA generation.
Code: src/workflows/agentic_workflow.py:1712
(should_continue = ranking_score >= ranking_threshold).
Raising it makes the workflow pickier: fewer articles reach extraction, reducing LLM spend but risking false negatives on borderline-relevant articles. Lowering it lets more articles through extraction at the cost of running the (expensive) extraction agents on lower-relevance content.
SimilarityThreshold (0.5)¶
Gates SIGMA rule novelty/deduplication in two places, both in
src/workflows/agentic_workflow.py:
- Context filtering (~line 3045): when assessing novelty against existing
rules, only candidates with
similarity >= SimilarityThresholdare kept as "similar rules" context (top 10). - Queue-promotion gate (
select_queueable_rule_indices, threshold lookup at ~line 3149): after generation, if a rule'smax_similarity >= SimilarityThresholdagainst existing rules, it is treated as a near-duplicate and dropped (not queued for human review). Below the threshold, or when the comparator is inconclusive, the rule is queued. The boundary is inclusive: a rule scoring exactly the threshold is suppressed.
The decision is strictly per rule. Until 2026-08-26 this gate computed a
batch aggregate (max() of every rule's score) and discarded the entire batch
when that aggregate cleared the threshold, so one near-duplicate silently
suppressed the novel rules generated alongside it, and defeated the
fail-open routing that sends an inconclusive comparison to review.
Raising it (toward 1.0) requires rules to be nearly identical to existing ones before they're suppressed, so more near-duplicate rules reach the review queue (more reviewer noise, fewer missed novel rules). Lowering it suppresses more aggressively, risking silently dropping a genuinely novel rule that happens to share atoms with an existing one.
MinHuntScore (97.0): not currently enforced¶
MinHuntScore is stored, exposed via the config API and MCP resources, and
threaded through AgenticWorkflowConfigTable and multiple response payloads,
but as of this writing, no code path in the agentic workflow reads it as a
comparison gate. Searching for every consumer of config.min_hunt_score /
MinHuntScore turns up only pass-through reads (API responses, MCP resource
listings, eval-bundle config snapshots); never an if hunt_score < config.min_hunt_score
check that terminates or filters anything.
The value that actually gates whether an ingested article auto-triggers the
agentic workflow is a different, separate field:
AgenticWorkflowConfigTable.auto_trigger_hunt_score_threshold
(src/services/workflow_trigger_service.py:89-97), editable via its own
endpoint (PATCH /api/workflow/config/auto-trigger-threshold). An article
auto-triggers only when its keyword hunt score is strictly above this
threshold.
Its default is 100.0, which sits above the 99.9 hunt-score ceiling, so by
default nothing auto-processes; auto-triggering is opt-in and only happens once
a user consciously lowers the threshold. This default is set consistently across
the DB model (src/database/models.py column default), the two create-default
paths (workflow_config.py and workflow_trigger_service.py:50), and every
fallback in workflow_config.py; the runtime value is read from AppSettingsTable
(seeded from the column default on first access). See
docs/architecture/scoring.md for the scoring formula
and the 99.9 ceiling.
Independently, several services accept their own min_hunt_score filter
parameter with their own local defaults, unrelated to the workflow config value:
src/services/rag_service.py (semantic search filter, default None),
src/services/chunk_analysis_backfill.py (backfill eligibility, default 50.0),
src/web/routes/ml_hunt_comparison.py (default 50.0). None of these read
ThresholdConfig.MinHuntScore.
If you're trying to gate workflow triggering by hunt score, tune
auto_trigger_hunt_score_threshold, not MinHuntScore. If you're trying to
gate something else with MinHuntScore, note that changing it today only
changes the number displayed in the UI/API; it has no runtime effect until a
gate is added that reads it.
How to re-evaluate after a change¶
Threshold changes to RankingThreshold or SimilarityThreshold affect how many
articles/rules survive the pipeline, so validate against the eval corpus before
relying on a new value:
- Change the threshold via
PUT /api/workflow/config(or the workflow config UI). - Run the extraction agents against the canonical eval set from the
/mlops/agent-evalspage (or the underlyingEvalBundleService/ subagent-eval-aggregate API); see Agent Evals for how to read the results (MAE, badges, node coloring). - Compare MAE and per-article deltas against the previous config version; the eval UI keeps prior versions for side-by-side comparison.
- Do not edit the eval fixtures (
config/eval_articles.yaml,config/eval_articles_data/) to make a threshold change look better; fixtures are ground truth and are changed only through the dedicated eval-fixture-audit process, never to chase a threshold-tuning result.
Last updated: 2026-07-16 Last reviewed: 2026-09-01