thundercode commited on
Commit
50cf649
·
verified ·
1 Parent(s): be08c80

release: add docs/architecture/01-system-overview.md

Browse files
docs/architecture/01-system-overview.md ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 01 — System Overview
2
+
3
+ **Parent:** [Architecture hub](../ARCHITECTURE.md) · **Status tags:** `IMPLEMENTED` · `VERIFIED` ·
4
+ `MEASURED` · `NOT RUN`
5
+
6
+ ---
7
+
8
+ ## 1. What the system is
9
+
10
+ SatQuery AI takes a natural-language question and one or two image assets, and returns a typed
11
+ `ResultEnvelope` containing an answer, a list of evidence items, an execution trace, and a
12
+ confidence breakdown.
13
+
14
+ It is a **router + specialists** architecture. There is no single end-to-end model that "does
15
+ satellite QA". Instead:
16
+
17
+ 1. a **router** reads the question and predicts *which capability* is being requested;
18
+ 2. a **controller** validates the request, dispatches exactly one specialist, and assembles the
19
+ result;
20
+ 3. the **specialist** computes the actual answer using a frozen backbone plus a small trained module;
21
+ 4. an **evidence engine** aggregates, orders, deduplicates and bounds what the specialist produced;
22
+ 5. a **confidence** stage attaches a calibrated number, or honestly declines to.
23
+
24
+ ### 1.1 Why not one end-to-end model
25
+
26
+ | Constraint | Consequence |
27
+ |---|---|
28
+ | The system must run on **CPU** | End-to-end VLM inference at usable quality needs a GPU; small per-task modules do not. |
29
+ | Different tasks have **incompatible outputs** | `change` returns a spatial change map; `caption` returns prose; `optical_sar` returns a class distribution. One head cannot emit all three. |
30
+ | Tasks have **different data and metrics** | Each specialist is trained and evaluated on its own split with its own protocol. |
31
+ | **Truthfulness** | Per-task metrics are auditable. A single end-to-end number would hide which component failed. |
32
+
33
+ The cost of this design is that there is **no system-level accuracy number** — because there is no
34
+ single model to measure. That absence is stated rather than papered over.
35
+
36
+ ## 2. The six capabilities
37
+
38
+ Declared in `configs/base.yaml` (`router.tasks`) and mirrored in the live
39
+ `GET /api/capabilities` response. All six report `available: true` in the deployed system.
40
+
41
+ | Task | Assets | Output | Backbone (frozen) | Trained module |
42
+ |---|---|---|---|---|
43
+ | `vqa` | 1 | short answer | SmolVLM-500M-Instruct | (unadapted; LoRA exists but is rejected) |
44
+ | `caption` | 1 | prose caption | SmolVLM-500M-Instruct | (unadapted) |
45
+ | `grounding` | 1 | boxes / regions | RemoteCLIP ViT-B/32 | grounding head |
46
+ | `change` | 2 (equal shape) | change map + regions | STANet-style (ResNet-18 + PAM) | change head |
47
+ | `change_vqa` | 2 | short answer | — (cached change features) | change-VQA head |
48
+ | `optical_sar` | 2 (GeoTIFF pair) | class distribution | CROMA-base | fusion head |
49
+
50
+ `Task` in `core/schemas.py` also carries `unsupported` — the router's explicit "this is not a
51
+ satellite-imagery question" class. The router's label space is therefore **6 classes**
52
+ (`router/label_space.py`: `vqa, caption, grounding, change, optical_sar, unsupported`), and
53
+ `change_vqa` is reached through the change family rather than being a separate router class.
54
+
55
+ > **Note the asymmetry.** `router/label_space.py` lists **six** task classes; the capabilities
56
+ > endpoint lists **six** tasks but a *different* six — `change_vqa` appears in capabilities and
57
+ > `unsupported` does not. This is intentional: `unsupported` is a routing outcome, not a servable
58
+ > capability. `core/schemas.py::Task` carries all seven values.
59
+
60
+ ## 3. Component inventory
61
+
62
+ Every path below is real and is the authoritative location.
63
+
64
+ | Layer | Module | Responsibility |
65
+ |---|---|---|
66
+ | **Contracts** | `core/schemas.py` | the binding typed contract: `Task`, `Intent`, `Evidence`, `SpecialistResult`, `ResultEnvelope`, `ExecutionTrace`, … |
67
+ | **Config** | `core/config.py` | load, deep-merge, validate, hash the registry; `get_config()` singleton |
68
+ | **Errors** | `core/errors.py` | the error taxonomy (`ConfigError`, `ModelLoadError`, `WorkflowPlanError`, …) |
69
+ | **Planning** | `core/planner.py` | turn an `Intent` into a concrete workflow |
70
+ | **Registry** | `core/registry.py` | specialist registration / lookup |
71
+ | **Controller** | `core/controller.py` | the nine-state FSM; the only thing that dispatches |
72
+ | **Router** | `router/encoder.py` | frozen MiniLM embedding, cached |
73
+ | | `router/adapter.py` | the 5-head `IntentAdapter` (the only trainable router part) |
74
+ | | `router/classifier.py` | learned classification + confidence threshold |
75
+ | | `router/fallback.py` | deterministic lexical fallback (`lexical_route`) |
76
+ | | `router/label_space.py` | the ontology, single source of truth |
77
+ | | `router/dataset.py`, `router/train.py` | dataset generation and training |
78
+ | **Specialists** | `specialists/base.py` | the specialist interface |
79
+ | | `specialists/vqa/{model,inference,prompts}.py` | SmolVLM VQA |
80
+ | | `specialists/grounding/{remoteclip,head,inference,specialist}.py` | RemoteCLIP + head |
81
+ | | `specialists/change/{stanet,specialist,postprocess,vqa_specialist}.py` | change detection + change-VQA |
82
+ | | `specialists/optical_sar/{croma,fusion_head,inference,specialist,sensor_adapter,radiometry,prompts}.py` | CROMA fusion |
83
+ | **Evidence** | `evidence/engine.py` | aggregation: dedup → sort → renumber → cap |
84
+ | | `evidence/confidence.py` | temperature scaling, honest pass-through |
85
+ | **Inference app** | `app/space_app.py` | `build_space_app()`; the four-endpoint JSON contract |
86
+ | | `app/serving.py` | the composition root (`build_serving_controller()`) |
87
+ | | `app/deployment.py` | deployment helpers |
88
+ | **Gateway** | `gateway/` | the Render orchestrator |
89
+ | **Frontend** | `frontend/` | the static site |
90
+
91
+ ## 4. The frozen-backbone strategy
92
+
93
+ Four backbones, all pinned by revision in `configs/base.yaml`, all fetched from the Hub on first use:
94
+
95
+ | Role | Repository | Revision | Why frozen |
96
+ |---|---|---|---|
97
+ | Router encoder | `sentence-transformers/all-MiniLM-L6-v2` | `1110a243fdf4` | embeddings are cached; the adapter trains on cached vectors in 0.28 s on CPU |
98
+ | VLM | `HuggingFaceTB/SmolVLM-500M-Instruct` | `a7da5b986cb5` | a 500M VLM cannot be fine-tuned end-to-end on CPU |
99
+ | Grounding | `chendelong/RemoteCLIP` | `bf1d8a3ccf2d` | provides the visual-language embedding space; only the head is trained |
100
+ | Optical-SAR | `antofuller/CROMA` | `0dd28e3d633b` | provides optical/SAR/joint embeddings; only the fusion head is trained |
101
+
102
+ Two consequences:
103
+
104
+ 1. **The system is small.** The six trained artifacts total ~125 MiB. Everything else is public
105
+ weights.
106
+ 2. **Backbones are not redistributed.** The release publishes only the six trained modules, each
107
+ with its backbone dependency documented.
108
+
109
+ ## 5. The design vocabulary
110
+
111
+ `docs/ARCHITECTURE_FREEZE.md` section 5 assigns one verb per layer. This is not decoration — it
112
+ resolves real ambiguities about *where* a decision belongs:
113
+
114
+ | Layer | Verb | Consequence |
115
+ |---|---|---|
116
+ | Router | *understands* | its output (`Intent`) is **advisory only** — the controller decides |
117
+ | Policy engine / planner | *decides* | picks the workflow; may override the router |
118
+ | Specialists | *compute* | produce evidence; never decide routing |
119
+ | VLM | *explains* | produces prose; never produces a confidence number |
120
+ | Evidence engine | *proves* | aggregates; never re-derives a specialist's claim |
121
+
122
+ The `Intent` schema makes the first row explicit in code:
123
+
124
+ ```python
125
+ class Intent(BaseModel):
126
+ """Output of the learned router. Advisory only — the controller decides."""
127
+ ```
128
+
129
+ ## 6. What is deliberately absent
130
+
131
+ | Absent | Status |
132
+ |---|---|
133
+ | Database / persistence | by design — the gateway is stateless |
134
+ | Authentication / users | by design |
135
+ | Job queue | by design — inference is synchronous |
136
+ | GPU requirement | by design — CPU-first, `.to(device)` everywhere |
137
+ | Gradio GUI | `app/space_app.py` serves JSON only |
138
+ | Chain-of-thought in traces | by design — observable facts only |
139
+ | System-level end-to-end benchmark | **NOT RUN — none exists** |
140
+ | Router test-split evaluation | **NOT RUN** |
141
+
142
+ ## 7. Evidence for this document
143
+
144
+ | Claim | Source |
145
+ |---|---|
146
+ | six tasks, config-declared | `configs/base.yaml` §`router.tasks` |
147
+ | router label space is 6 classes | `router/label_space.py` §`TASK_CLASSES` |
148
+ | `Intent` is advisory | `core/schemas.py` §`Intent` docstring |
149
+ | layer verbs | `docs/ARCHITECTURE_FREEZE.md` §5 (quoted in `evidence/engine.py`) |
150
+ | frozen backbones + revisions | `configs/base.yaml` |
151
+ | six trained artifacts | `release/repo/models/manifest.json` (generated) |