Context
Processing a 1.2MB Unit42 PDF costs ~$0.37 across 28 LLM calls (204K tokens). The BatchMapper accounts for 84% of cost (23 calls at 10 spans/batch for 201 spans). Increasing batch size to 25 (done in commit 49fb67e) helps, but further reductions are possible.
Proposed Optimizations
1. Span deduplication before mapping (Medium effort, ~30% cost reduction)
The SpanFinderAgent detects 201 spans from a large report, but many overlap — the same text region matched by multiple tactic patterns. Deduplicating spans before they reach the BatchMapper would reduce the total span count significantly.
Approach:
- After SpanFinder, group spans by overlapping character ranges
- Merge overlapping spans into a single representative span, preserving all matched pattern metadata
- Only send unique text regions to the mapper
Expected impact: 201 spans → ~80-120 unique spans → fewer mapper batches
2. Two-pass mapping with cheap pre-filter (Higher effort, ~50% cost reduction)
Most spans from pattern matching are noise (generic language that happens to match tactic keywords). A cheap first pass could filter these before the expensive LLM mapper.
Approach options:
- Heuristic scoring: Use keyword density, technique ID presence, and vector similarity scores to classify spans as "likely technique" vs "noise" without an LLM call
- Small model pre-filter: Use a cheaper/faster model (e.g., Gemini Flash Lite or a local classifier) for binary "contains TTP?" classification, then only send positive spans to the full mapper
- Confidence threshold on retriever: The BatchRetrieverAgent already scores candidates — raise the threshold to filter low-confidence spans before mapping
Expected impact: 201 spans → 50-80 high-confidence spans → 2-4 mapper batches instead of 8-10
Cost projections
| Optimization |
Mapper calls (201 spans) |
Est. cost |
Savings |
| Current (batch=25) |
~8 |
~$0.15 |
baseline |
| + Span dedup |
~4-5 |
~$0.08 |
~47% |
| + Two-pass filter |
~2-3 |
~$0.05 |
~67% |
| Both combined |
~2 |
~$0.04 |
~73% |
References
Context
Processing a 1.2MB Unit42 PDF costs ~$0.37 across 28 LLM calls (204K tokens). The BatchMapper accounts for 84% of cost (23 calls at 10 spans/batch for 201 spans). Increasing batch size to 25 (done in commit 49fb67e) helps, but further reductions are possible.
Proposed Optimizations
1. Span deduplication before mapping (Medium effort, ~30% cost reduction)
The SpanFinderAgent detects 201 spans from a large report, but many overlap — the same text region matched by multiple tactic patterns. Deduplicating spans before they reach the BatchMapper would reduce the total span count significantly.
Approach:
Expected impact: 201 spans → ~80-120 unique spans → fewer mapper batches
2. Two-pass mapping with cheap pre-filter (Higher effort, ~50% cost reduction)
Most spans from pattern matching are noise (generic language that happens to match tactic keywords). A cheap first pass could filter these before the expensive LLM mapper.
Approach options:
Expected impact: 201 spans → 50-80 high-confidence spans → 2-4 mapper batches instead of 8-10
Cost projections
References
bandjacks/llm/mapper_optimized.pybandjacks/llm/agents_v2.py(SpanFinderAgent)