Product briefing
A defence & export-control watch system, scoped and built as a real deliverable — a pipeline working end to end, not a mock-up.
Summary
What follows details the scoping, the architecture and the evidence — in that order.
The problem
An explicit non-goal: the system does not decide. It speeds up and structures the human watch — the decision stays human.
Scoping
1 and 3 covered in V1 · 2 delivered as the first V2 slice — perimeter extended to the five categories on 2026-08-20, escalation being conditioned on a measured candidate antecedent rather than on the category (see roadmap)
MECE perimeter
Export control, arms contracts, military movements, defence diplomacy, industrial programmes — thematic filtering, worldwide.
Classified intelligence, cybersecurity (a distinct neighbouring perimeter), automated decision-making, financial market analysis.
An accepted revision: strict geographic filtering, tested on real data, wrongly rejected nearly all of 2 sources out of 5 → removed, replaced by a location extraction kept as non-filtering metadata. Documented as a revision, not hidden.
Architecture
18 RSS feeds, selected by country, validated live
collector.py
before the LLM call, not after
classification + summary + verified quote
cross-checking + confidence score, bounded tool loop
event threads over the history, same bounded loop
Shared state (VigieState) between the nodes, every node traced natively — with no manual instrumentation. The first three nodes are a deterministic workflow; the last two let the model decide on its own tool calls, each under a coded double cap — one more agentic node is not one more cap to forget.
What survives runs — LLM budget, seen links, analysed history — goes through a single persistence layer, local files in development and Firestore in production. The digest served is a sliding window over that history, not a snapshot of the last run: since deduplication empties the later collections, serving the raw run would erase the display on every pass.
Display
Capture of 2026-09-06, during the transition to English: the escalation gates found nothing that week, so every card shows the unscored state rather than a score — which is what the design is for. The confidence score sits on a sequential ramp and not in red/green — a score read as a verdict would invalidate the non-goal. An item outside the verifier's perimeter comes out with no score rather than with a zero. The "state media" provenance is displayed, not buried.
Coverage
Built on the verified location of the event, not on the country of the source. Items with no extractable place and places not attachable to a country (maritime spaces, commands) are counted explicitly: a map that showed only its successes would overstate the real coverage.
Synthesis
A thread links the articles of one story — same parties, same operation — and not of one theme. The timeline is at the real scale of time: the gap between publications is the signal, and equidistant markers would make three dispatches in twenty minutes and a story spread over three weeks look identical. No aggregate score is produced at thread level — averaging partly absent scores would fill by computation a gap left deliberately.
Production
The endpoint that spends is closed behind a token and answers 503 when none is configured, rather than staying open — it triggers a full run, so it burns the day's budget on its own. Reading the digest stays public: that is what a browser does, and it costs nothing.
A non-negotiable guardrail
Every summary generated by the LLM must come with a quote verified verbatim against the source text. With no evidence, the item is rejected automatically — not merely flagged.
The same principle applies to the extracted place: empty rather than unverifiable.
Guardrails
The method counted as much as the result. The runbook first prescribed two full runs launched together — which produced a single reservation and did not even overlap, deduplication having starved the second. A counter left under its cap would then have passed for proof.
Estimated value
| Item | Assumption | Value |
|---|---|---|
| Team concerned | 4 dedicated analysts | — |
| Time saved | 90 → 30 min of review / day / analyst | 528 h/year |
| Value of that time | 528 h × €60/h fully loaded | ~€32,000/year |
| Cost of the system (measured, real) | ~200 items/day × 1 call + capped escalation | ~€140/year |
A favourable ratio even in a degraded scenario (the gain halved). The qualitative benefit — standardisation, traceability — remains decisive beyond time alone.
Risks
A verbatim quote is mandatory, with automatic rejection when absent.
Explicit positioning: an aid to the watch, never an automated decision tool.
Selection by country on a SIPRI criterion, state media marked as such in the digest.
A daily call cap + bounded agentic escalation — coded and tested, not merely documented.
Evidence · the precision KPI
These four figures are not a trend, and presenting them as one would be the easiest mistake to make here. Each was taken on a different sample, under a different protocol, against a prompt that had changed in between. What moves from one to the next is the severity of the method, not the quality of the classifier. The last was revised downwards — from 79% — on 2026-09-06, when an annotation inconsistency was settled against the flattering reading.
Evidence · the precision KPI
A precision measurement tests the clarity of the definitions as much as the quality of the model. The two are audited together — and the audit corrected itself here: the scoping document stated nothing about the boundary in question, but the prompt had carried an undocumented rule for five days that the model was faithfully applying. A disagreement about the specification, not a gap in it: the rule was changed, not added, and carried back into the scoping document.
Evidence · the precision KPI
Hence a full confusion matrix rather than a count of disagreements: the direction of an error points at the fix. Confusing two in-scope categories and letting out-of-scope material through are not the same defect, and averaging them hides both.
Evidence · the precision KPI
Revised downwards on 2026-09-06, from 38/48. Two articles describing the same event — a first example handed to a customer navy — carried opposite labels in the same blind session; settling that inconsistency turned an agreement into a disagreement. The consequence was unfavourable and is published as such. The figure has also aged: the classification prompt has changed three times since, so it no longer describes the current system.
Evidence · the precision KPI
Two rules taken from this, applied to every prompt change since. Check the controls and not only the reference case. And a rule's position counts as much as its content: a first formulation stating exactly the right distinction moved 0 items out of 25 — only rewriting it to lead with the branch that had to win produced the effect.
Evidence · defects found by testing
Fixing it where the symptom appeared would have produced a map that looked right and no longer meant anything — the guardrail is that a place is displayed only if it was verified verbatim.
Evidence · defects found by testing
None of these defects was visible in a code review. The crash was recoverable in a minute; the silent loss it uncovered would have kept removing items from every subsequent run.
Evidence · defects found by testing
The same reasoning governs deduplication's position in the graph: it runs before the analysis node, so an item already seen never costs a call to be filtered out.
Roadmap
One arbitration stays open on purpose, and three things are not done. Both on the next two slides — stated as what would settle them, not as intentions.
Open arbitration
Which is why the arbitration stays open rather than being settled by a round number. The loss is diffuse — the largest single source accounts for 10 calls out of 99, so removing a feed corrects nothing — and the two reasons point at different fixes: composition of the sources on one side, extraction on the other. No redistribution between nodes recovers either. One option is already ruled out: raising the grouping threshold for a budget reason would silently expire a threshold calibrated on a precision measurement.
What is not done
Observability came before autonomy, deliberately. The deployment diagnosis had not stumbled on the absence of infrastructure but on the absence of a log: the pipeline emitted nothing, so every measurement on the previous slides would have become invisible the moment a scheduler took over. The structured log shipped first, and the container was exercised end to end against real feeds with the call cap forced to zero — which tests truncation without spending anything.
In summary
MECE, alternatives assessed, KPIs, risks, governance — with the same demand for empirical verification from beginning to end, right down to the final precision measurement.