unknown commited on
Commit
059a062
·
1 Parent(s): d60f9a3

cleaning commit

Browse files
Files changed (9) hide show
  1. CLAUDE.md +0 -199
  2. README.md +0 -64
  3. agent/__init__.py +0 -1
  4. app/__init__.py +0 -1
  5. app/main.py +28 -44
  6. data/README.md +3 -0
  7. docs/Agent4EO_PRD.md +0 -289
  8. environment.yml +0 -17
  9. eo/__init__.py +0 -1
CLAUDE.md DELETED
@@ -1,199 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
-
7
- **Agent4EO** is a single-process, local demonstration platform for agentic Earth Observation (EO) analysis. It routes natural language queries to appropriate EO processing tools, executes computations on curated satellite imagery samples, and returns visual outputs with AI-generated interpretations.
8
-
9
- **Current Status**: Project is in planning phase. The complete specification is in [docs/Agent4EO_PRD.md](docs/Agent4EO_PRD.md).
10
-
11
- **Stack**: Python 3.11, GDAL/Rasterio (geospatial), LangChain + Mistral AI (agent orchestration), Gradio (UI).
12
-
13
- ## Development Commands
14
-
15
- ### Environment Setup
16
- ```bash
17
- # Environment must be created externally via conda/mamba with dependencies from conda-forge
18
- # Dependencies: python=3.11, numpy, gdal, rasterio, gradio, pydantic, pyyaml
19
- # langchain-core, langchain-community, mistralai, matplotlib (optional), pytest (optional)
20
- ```
21
-
22
- ### Running the Application
23
- ```bash
24
- export MISTRAL_API_KEY="your_key_here"
25
- python -m app.main
26
- ```
27
-
28
- ### Testing
29
- ```bash
30
- pytest tests/ # Run all tests
31
- pytest tests/test_ndvi_math_small.py # Run specific test
32
- ```
33
-
34
- ## High-Level Architecture
35
-
36
- ### Agent Flow Pattern
37
- The application uses **LLM-powered tool routing** with structured outputs, not heavy agent frameworks:
38
-
39
- ```
40
- User Input (sample selection + NL query)
41
-
42
- agent/router.py (Mistral LLM selects tool via LangChain tool-calling)
43
-
44
- agent/tools.py (LangChain tool wrappers)
45
-
46
- eo/{ndvi|lst|dnbr}.py (geospatial computation)
47
-
48
- agent/interpreter.py (Mistral LLM generates narrative)
49
-
50
- Gradio UI (display preview, histogram, stats, interpretation)
51
- ```
52
-
53
- **Key Pattern**: Strict mismatch handling—if `sample.task` doesn't match the intended tool, the system refuses with a clear message rather than attempting execution.
54
-
55
- ### Three Core EO Tools
56
-
57
- #### 1. NDVI (Normalized Difference Vegetation Index)
58
- - **File**: `eo/ndvi.py`
59
- - **Input**: Single Sentinel-2 GeoTIFF with RED & NIR bands
60
- - **Formula**: `(NIR - RED) / (NIR + RED + 1e-6)`, clipped to [-1, 1]
61
- - **Output**: float32 GeoTIFF, preview PNG, histogram, stats (min/max/mean/std/percentiles)
62
- - **Use case**: Vegetation health assessment
63
-
64
- #### 2. LST (Land Surface Temperature)
65
- - **File**: `eo/lst.py`
66
- - **Input**: Single Landsat 8/9 thermal band (Band 10) + metadata constants
67
- - **Process**: DN → Radiance → Brightness Temp → LST (with emissivity correction)
68
- - **Critical**: Uses Planck constants (K1, K2), emissivity (ε), wavelength (λ ≈ 10.895µm)
69
- - **Constants**: Stored per-sample in `config/samples_index.json` metadata
70
- - **Output**: float32 GeoTIFF in Celsius, preview, histogram, temperature stats
71
- - **Use case**: Urban heat island analysis
72
-
73
- #### 3. dNBR (Differenced Normalized Burn Ratio)
74
- - **File**: `eo/dnbr.py`
75
- - **Input**: Pre/post Sentinel-2 pair with NIR & SWIR2 bands
76
- - **Formula**: `NBR = (NIR - SWIR2) / (NIR + SWIR2 + 1e-6); dNBR = NBR_post - NBR_pre`
77
- - **Severity classes**: Unburned/Low/Moderate/High/Very High based on thresholds in `config/heuristics.yaml`
78
- - **Output**: float32 GeoTIFF, preview, histogram, stats + severity class distributions
79
- - **Use case**: Wildfire burn severity mapping
80
-
81
- ### Configuration-Driven Architecture
82
-
83
- **Sample Registry** (`config/samples_index.json`):
84
- - Single source of truth for all input data
85
- - Contains paths, task types, band indices (1-based), and metadata constants
86
- - Router validates `sample.task` against requested tool
87
-
88
- **Interpretation Thresholds** (`config/heuristics.yaml`):
89
- - Thresholds inform LLM wording, don't change math
90
- - Examples: NDVI mean ranges, LST temperature thresholds, dNBR severity bins
91
- - Separate from computation logic for easy tuning
92
-
93
- ### Structured Output Pattern
94
-
95
- All outputs use **Pydantic schemas** enforced via LangChain's `JsonOutputParser`:
96
-
97
- ```python
98
- class ToolResult(BaseModel):
99
- raster_path: str # Output GeoTIFF path
100
- preview_png: str # 8-bit normalized preview
101
- histogram_png: Optional[str]
102
- metrics: Dict[str, float] # Stats: min/max/mean/std/percentiles
103
- extras: Dict[str, Any] # Tool-specific data (e.g., severity counts)
104
-
105
- class Interpretation(BaseModel):
106
- headline: str
107
- bullets: List[str] # Must include numeric values
108
- caveats: List[str] # 0-2 items
109
- ```
110
-
111
- ## Critical Implementation Details
112
-
113
- ### Geospatial I/O Requirements
114
- - **CRS/Transform**: All outputs MUST preserve input CRS and geotransform
115
- - **NoData handling**: Consistent handling of NoData values across all tools
116
- - **Band indices**: 1-based (GDAL convention), specified per-sample in registry
117
- - **Output storage**: `outputs/<task>/<sample_id>/<timestamp>/` (internal use)
118
-
119
- ### LLM Orchestration Constraints
120
- - **Router**: Use LangChain tool-calling (not heavy agents) for tool selection
121
- - **Mismatch handling**: If tool doesn't match `sample.task`, refuse immediately—no retries
122
- - **Interpretation**: Pass `{task, sample.title, metrics, extras, thresholds_excerpt}` to LLM
123
- - **Schema enforcement**: Use `JsonOutputParser` to guarantee `Interpretation` structure
124
-
125
- ### Dependency Management
126
- - **Package manager**: Conda/conda-forge ONLY (not pip)
127
- - **Python version**: 3.11 (pinned for GDAL compatibility)
128
- - **No environment commands**: README lists dependencies but doesn't run conda commands
129
- - **Environment setup**: Handled externally by user
130
-
131
- ### Scope Constraints (Do NOT implement)
132
- - No file uploads
133
- - No remote STAC catalogs or cloud data access
134
- - No FastAPI/REST API (single-process only)
135
- - No databases
136
- - No ML models (CLIP, segmentation, etc.)
137
- - No Docker containers
138
- - No batch processing
139
- - No vector analytics
140
- - No download feature in UI (display-only)
141
- - No CI/CD pipelines in v1
142
-
143
- ## Code Organization Principles
144
-
145
- ### File Length and Structure
146
- - Keep files **< 200 lines** when possible
147
- - Functions should be small and focused
148
- - Separate concerns: math in `eo/`, orchestration in `agent/`, UI in `app/`
149
-
150
- ### Module Responsibilities
151
- - **`eo/`**: Pure geospatial computation (NDVI/LST/dNBR math + I/O)
152
- - **`eo/io.py`**: Raster read/write helpers with CRS/transform preservation
153
- - **`eo/viz.py`**: Preview PNG and histogram generation
154
- - **`agent/schema.py`**: Pydantic models only
155
- - **`agent/tools.py`**: LangChain tool wrappers (thin layer over `eo/` functions)
156
- - **`agent/router.py`**: LLM tool selection logic
157
- - **`agent/interpreter.py`**: LLM narrative generation
158
- - **`agent/prompts/`**: Externalized system prompts
159
- - **`app/main.py`**: Gradio UI only (no business logic)
160
-
161
- ### Prompt Engineering
162
- - **Router prompt**: Include user sentence, sample ID, sample.task, and strict tool contracts
163
- - **Interpreter prompt**: Concise style, require numeric values in bullets, limit caveats to 0-2
164
- - **Externalize prompts**: Store in `agent/prompts/*.txt` for easy editing
165
-
166
- ## Performance Targets
167
-
168
- - App startup: < 3 seconds
169
- - Tool execution (50-100 MB samples): < 2-3 seconds
170
- - Memory footprint: < 400 MB during processing
171
-
172
- ## Testing Focus
173
-
174
- ### Critical Test Cases
175
- 1. **Routing correctness**: Vegetation query → NDVI, heat query → LST, burn query → dNBR
176
- 2. **Mismatch handling**: Wrong tool for sample type → clear refusal message
177
- 3. **Output quality**: Valid GeoTIFF with preserved CRS/transform, correct NoData handling
178
- 4. **Interpretation format**: Valid JSON with numeric values in bullets
179
-
180
- ### Test Files
181
- - `tests/test_router_mismatch.py`: Validate refusal on tool-sample mismatches
182
- - `tests/test_ndvi_math_small.py`: Verify NDVI computation accuracy
183
-
184
- ## Common Pitfalls
185
-
186
- 1. **LST constants**: Don't hardcode K1/K2/emissivity—read from sample metadata
187
- 2. **Band indices**: Registry uses 1-based indices (GDAL convention), not 0-based
188
- 3. **CRS preservation**: Always copy input CRS/transform to output GeoTIFF
189
- 4. **Router retries**: Don't implement retry logic—refuse on mismatch immediately
190
- 5. **UI downloads**: Do not add download buttons (display-only requirement)
191
- 6. **Dependency installation**: README must NOT include conda/pip commands
192
-
193
- ## Key Files to Reference
194
-
195
- - **[docs/Agent4EO_PRD.md](docs/Agent4EO_PRD.md)**: Complete specification (definitive source)
196
- - **`config/samples_index.json`**: Sample registry with paths, tasks, bands, constants
197
- - **`config/heuristics.yaml`**: Interpretation thresholds for LLM narratives
198
- - **`agent/schema.py`**: Pydantic models for structured outputs
199
- - **`eo/ndvi.py`, `eo/lst.py`, `eo/dnbr.py`**: Core computation logic
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md DELETED
@@ -1,64 +0,0 @@
1
- ---
2
- title: Agent4EO
3
- emoji: 🛰️
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app/main.py
9
- pinned: false
10
- ---
11
-
12
- # Agent4EO
13
-
14
- A single-process, local demonstration platform for agentic Earth Observation (EO) analysis. Routes natural language queries to appropriate EO processing tools, executes computations on curated satellite imagery samples, and returns visual outputs with AI-generated interpretations.
15
-
16
- ## Stack
17
-
18
- - Python 3.11
19
- - GDAL/Rasterio (geospatial)
20
- - LangChain + Mistral AI (agent orchestration)
21
- - Gradio (UI)
22
-
23
- ## Dependencies
24
-
25
- Create a conda/mamba environment with the following packages from conda-forge:
26
-
27
- - `python=3.11`
28
- - `numpy`
29
- - `gdal`
30
- - `rasterio`
31
- - `gradio`
32
- - `pydantic`
33
- - `pyyaml`
34
- - `langchain-core`
35
- - `langchain-community`
36
- - `mistralai`
37
- - `matplotlib` (optional, for histograms)
38
- - `pytest` (optional, for testing)
39
-
40
- ## Configuration
41
-
42
- ### Mistral API Key
43
-
44
- Provide your Mistral API key via one of these methods (checked in order):
45
-
46
- 1. **Environment variable** (recommended):
47
- '''bash
48
- export MISTRAL_API_KEY="your_key_here"
49
- '''
50
-
51
- 2. **Config file**: Copy `config/app.yaml.example` to `config/app.yaml` and set `mistral_api_key`.
52
-
53
- ## Running the Application
54
-
55
- '''bash
56
- python -m app.main
57
- '''
58
-
59
- The Gradio UI will launch at http://localhost:7860.
60
-
61
- ## Notes
62
-
63
- - **UI is display-only**: No download buttons. Artifacts are written to `outputs/<task>/<sample_id>/<timestamp>/` for internal use.
64
- - **Curated samples only**: No file uploads. Samples are defined in `config/samples_index.json`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent/__init__.py DELETED
@@ -1 +0,0 @@
1
- """Agent orchestration for Agent4EO."""
 
 
app/__init__.py DELETED
@@ -1 +0,0 @@
1
- """Gradio UI application."""
 
 
app/main.py CHANGED
@@ -129,65 +129,49 @@ def run_analysis(
129
  except Exception as e:
130
  return None, None, None, "", f"❌ Error: {str(e)}"
131
 
132
- about_md = """
133
- # Agent4EO — **Exploring agentic workflows for Earth Observation (EO).**
134
-
135
- Despite the abundance of EO data, many use cases still struggle to cross the gap from research to deployment, from geopsatial experts to every day end-users.
136
- Inspired by Google's Earth AI, this project goal is to interrogate and unpack what lies behind **geospatial reasoning**: how modern AI weaves physics, context and satellite pixels into clear operational insights.
137
-
138
- Agent4EO demonstrates how recent advances in Large Language Models and Earth-observation algorithms can be composed into agentic workflows to translate EO data potential into practical understandble analyses.
139
-
140
- ---
141
- ## Current capabilities:
142
- The user selects a curated sample and submits a plain-text request.
143
- A routing agent (Ministral-3B via LangChain tool calling) chooses exactly one analysis tool and executes it.
144
- Implemented tools include NDVI on Sentinel-2 (vegetation condition), LST on Landsat 8/9 (land surface temperature in °C), and dNBR on paired Sentinel-2 scenes (burn severity).
145
- Processing relies on rasterio/GDAL with correct CRS/transform and NoData handling.
146
- The interface returns a quicklook, a histogram, quantitative metrics (e.g., min/mean/max and percentiles), and a concise, number-grounded interpretation generated by the LLM.
147
-
148
-
149
- * **Operational by design.** The same pattern can scale to monitoring jobs, alerts, and reports—without burying users in geospatial plumbing.
150
-
151
- Future directions:
152
- SAR-based oil-spill detection, flood mapping,
153
- multi-temporal change maps
154
- region-of-interest operations and overlays
155
- semantic scene search, multilingual explanations, and confidence cues.
156
- The aim is a lightweight, extensible path from prompt to analysis to interpretation that supports real-world, repeatable EO workflows.
157
 
158
- ---
159
-
160
- ## What this could do next (ideas)
161
-
162
- * **New skills:** NDWI for surface water, UHI intensity, vegetation anomalies, SAR-based flood extent, cloud/shadow masking, multi-temporal change maps.
163
- * **Richer narratives:** Compare samples (“today vs last month”), generate incident summaries, or “explain like I’m a mayor” vs “explain like I’m a hydrologist.”
164
- * **Spatial drill-downs:** Simple zonal stats over neighborhoods, parks, burn perimeters; hotspot labeling and quadrant summaries.
165
- * **Quality controls:** Confidence scores, basic QA flags, and transparent assumptions (e.g., emissivity choices).
166
- * **Interoperability:** Hook into STAC catalogs, task queues, or ops notebooks—while keeping the same agentic front door.
167
- * **Human-in-the-loop:** Let reviewers approve/rewrite the agent’s explanations before sharing.
168
 
169
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
- ## Notes & caveats
172
-
173
- * This is a **display-only MVP** using curated samples; outputs are saved locally for inspection.
174
- * Thresholds (e.g., dNBR classes, “hot” LST) are **demo defaults**—tune them to your context.
175
- * Interpretations are grounded in computed metrics but **not a substitute for expert analysis**.
176
-
177
- If you’re excited by agentic EO—and believe satellite analytics should be as simple as **asking a question**—you’re in the right place.
178
 
179
  """
180
 
 
181
  def create_ui():
182
  """Create Gradio interface."""
183
  samples = load_sample_registry()
184
  sample_choices = [s.title for s in samples.values()]
185
 
186
  with gr.Blocks(title="Agent4EO") as demo:
187
- gr.Markdown("# 🛰️ Agent4EO\nNatural language interface for Earth Observation analysis")
188
 
189
 
190
- with gr.Accordion("About this demo", open=True):
 
191
  gr.Markdown(about_md)
192
 
193
  with gr.Row():
 
129
  except Exception as e:
130
  return None, None, None, "", f"❌ Error: {str(e)}"
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ about_md = """
 
 
 
 
 
 
 
 
 
134
 
135
  ---
136
+ ## Challenge
137
+ - **Decision-makers** don’t need raw imagery; they need clear, **operational signals**, still many Earth Observation (EO) use-cases fail to cross the gap from geospatial experts to end-users, **from research to deployment**.
138
+ - Staying current with a **fast-moving field** while making **geospatial reasoning** explicit : As a **curiosity-driven AI Research Engineer**, I had to **investigate agentic workflows** and the pipeline structures behind groundbreaking initiatives capturing attention: [Google Earth AI](https://ai.google/earth-ai/), [Axion Planetary MCP](https://github.com/Dhenenjay/axion-planetary-mcp), [Ageospatial](https://ageospatial.com/), [GeoRetina](https://www.georetina.com/)
139
+
140
+ ## Solution
141
+ - Curated samples are fed to a **routing agent** that selects the appropriate analysis, like a geospatial expert would.
142
+ - Implemented tools: **Vegetation condition analysis** (NDVI), **Urban Heat Island monitoring** (LST), and **burn scars charcterization** (dNBR).
143
+ - **OpenStreetMap context** (API call) is attached to computed metrics to anchor results to places.
144
+ - A second **LLM** pass turns numbers + context into a concise operational narrative.
145
+ - The interface displays a **quicklook**, **histogram**, quantitative **metrics**, and a short, number-grounded **explanation**.
146
+ - The agent is powered by **Ministral-3B** for efficiency; orchestration uses **LangChain**. EO I/O uses **Rasterio** with proper CRS/transform and not-relevant data handling.
147
+
148
+ ## Results
149
+ - A **live, accessible project** : I believe in public demos over private perfection.
150
+ - **Clear visual and textual outputs** understandable by non-experts, revealing Earth Observation's added value.
151
+ - A concrete **exploration of agentic orchestration** for EO that other practitioners can reuse as a reference pattern.
152
+
153
+ ## Possible extensions
154
+ - Move to true **prompt-driven analysis**: users specify topic, period, and region; the agent fetches data on demand (from a way larger range of sources) and selects tools accordingly.
155
+ - **Richer narratives** tailored to roles (territorial planning, insurers...) with domain-aware capacity.
156
+ - Expand the **toolset**: multi-temporal change maps, flood risk, landslide tracking, building characterization, oil-spill detection, SAR flood mapping, and embeddings-based heads with integration of **Foundation Models**
157
+ - Enable **on-the-fly tool drafting** (code synthesis) when safe and auditable.
158
+ - Be **deliberate with LLMs**: for such a small range of tools, selection via LLM isn’t necessary, and with such low context models can hallucinate. Agents should be used where they add real leverage, beyond conventional programming capacities.
159
 
 
 
 
 
 
 
 
160
 
161
  """
162
 
163
+
164
  def create_ui():
165
  """Create Gradio interface."""
166
  samples = load_sample_registry()
167
  sample_choices = [s.title for s in samples.values()]
168
 
169
  with gr.Blocks(title="Agent4EO") as demo:
170
+ gr.Markdown("# 🛰️ Agent4EO - A demo by Thomas OLIVE")
171
 
172
 
173
+ with gr.Accordion("Agent4EO is a demo that investigates and demystifies geospatial reasoning by using a lightweight agent to turn curated satellite samples into clear, operational insights with concise narratives.",
174
+ open=True):
175
  gr.Markdown(about_md)
176
 
177
  with gr.Row():
data/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ ---
docs/Agent4EO_PRD.md DELETED
@@ -1,289 +0,0 @@
1
- # Agent4EO — PRD (MVP, one‑shot build)
2
-
3
- > **Purpose**: Provide Claude Code with everything needed to generate an almost‑ready‑to‑use local demo platform that routes a plain‑text user request to the correct Earth Observation (EO) tool, runs the computation on a curated sample from `data/samples/`, and returns visuals, stats, and an interpretation. **The UI is display‑only** (no download feature). **Environment setup is handled externally**; Claude must only specify dependencies, not run conda/mamba commands.
4
-
5
- ---
6
-
7
- ## 1) Summary
8
-
9
- Agent4EO is a **single‑process, local app** (conda‑based) where a user:
10
- 1) picks a curated **sample** from `/data/samples/`,
11
- 2) writes a **plain text sentence** (e.g., “give me burn severity for this S2 pair”), and
12
- 3) the **agent** routes to exactly one tool and executes it:
13
-
14
- - **NDVI (Sentinel‑2 single scene)**
15
- - **LST (Landsat 8/9 single scene)**
16
- - **dNBR (Sentinel‑2 pre/post pair)**
17
-
18
- Outputs: **preview PNG**, **histogram PNG** (if matplotlib present), compact **stats** (JSON/table), and a short **LLM interpretation** (displayed in UI). Artifacts (e.g., computed GeoTIFF) may be written under `outputs/` for internal inspection, but **no download control** is exposed in the UI.
19
-
20
- **No file uploads. No STAC/cloud. No Docker.** LLM = **Mistral** (via API) orchestrated with **LangChain** tool‑calling. (See §11 for Claude Code execution guidelines.)
21
-
22
- ---
23
-
24
- ## 2) Goals (MVP)
25
-
26
- - **Samples‑only** selection from `config/samples_index.json`.
27
- - **Natural‑language routing** → one tool (NDVI, LST, dNBR) or a clear refusal if the sample is incompatible with the requested tool.
28
- - **EO math implemented** for all three tools (NDVI, LST, dNBR) with correct I/O (CRS/transform, nodata).
29
- - **Interpretation**: concise, numeric, grounded in computed metrics (no external data).
30
- - **Display‑only** for the computed product (no download in MVP).
31
-
32
- ---
33
-
34
- ## 3) Non‑Goals (MVP)
35
-
36
- - No file uploads, no remote catalogs, no FastAPI/REST, no databases, no CLIP, no segmentation/ML.
37
- - No batch processing, no vector analytics beyond tiny helpers, no cloud infra, no CI in v1.
38
- - No download feature in the UI.
39
-
40
- ---
41
-
42
- ## 4) Users & Jobs
43
-
44
- - **Demo owner / reviewer** validates an end‑to‑end **agentic** EO workflow on curated samples and reads a short explanation of outcomes.
45
-
46
- ---
47
-
48
- ## 5) UX (single page)
49
-
50
- **Left column**
51
- - **Sample dropdown** (from `samples_index.json`).
52
- - **Prompt textbox** (one sentence like “analyze urban heat for this Landsat scene”).
53
- - **Run** button.
54
-
55
- **Right column**
56
- - **Preview PNG** (quicklook).
57
- - **Histogram PNG** (if matplotlib present).
58
- - **Stats table** (metrics dictionary).
59
- - **Interpretation card** (headline + bullets + caveats).
60
- - *(No download button in MVP.)*
61
-
62
- Errors: show a clear banner when tool–sample mismatch or missing files.
63
-
64
- ---
65
-
66
- ## 6) Data & Registry
67
-
68
- All inputs are curated and registered in **`config/samples_index.json`** (single source of truth). Example items:
69
-
70
- ```json
71
- [
72
- {
73
- "id": "s2_green",
74
- "task": "NDVI",
75
- "title": "Sentinel-2 — green area",
76
- "paths": ["data/samples/s2_ndvi/s2_green.tif"],
77
- "bands": {"red": 1, "nir": 2}
78
- },
79
- {
80
- "id": "l8_paris_hot",
81
- "task": "LST",
82
- "title": "Landsat 8 — Paris (hot)",
83
- "paths": ["data/samples/landsat_lst/l8_paris_hot.tif"],
84
- "meta": {"emissivity": 0.97, "k1": 774.89, "k2": 1321.08}
85
- },
86
- {
87
- "id": "s2_fire_pair_a",
88
- "task": "DNBR",
89
- "title": "Sentinel-2 — fire pair A",
90
- "paths": [
91
- "data/samples/s2_dnbr/pair_a/pre.tif",
92
- "data/samples/s2_dnbr/pair_a/post.tif"
93
- ],
94
- "bands": {"nir": 1, "swir2": 2}
95
- }
96
- ]
97
- ```
98
-
99
- Assume band indices are **1‑based**. Constants required for LST (e.g., emissivity, Planck constants) can be embedded per sample.
100
-
101
- ---
102
-
103
- ## 7) Tools & Math (to be implemented by Claude)
104
-
105
- ### 7.1 NDVI (Sentinel‑2)
106
- - **Input**: single GeoTIFF with **RED** & **NIR** bands (indices from registry).
107
- - **Formula**: `NDVI = (NIR - RED) / (NIR + RED + eps)` with `eps = 1e-6`; clip to `[-1, 1]`; respect NoData.
108
- - **Outputs**: GeoTIFF `float32` (same CRS/transform, nodata), preview PNG (8‑bit normalized), histogram PNG (optional), stats: `min`, `max`, `mean`, `std`, `p10`, `p50`, `p90`, `%valid`.
109
-
110
- ### 7.2 LST (Landsat 8/9 TIRS)
111
- - **Input**: single thermal band GeoTIFF (e.g., Band 10) plus constants from metadata.
112
- - **Assumptions** (embedded; no external reads):
113
- 1) Radiance: `Lλ = ML * DN + AL` (or assume DN already radiance if ML/AL missing, controlled via metadata).
114
- 2) Brightness Temperature: `TB(K) = K2 / ln(K1 / Lλ + 1)`.
115
- 3) Emissivity: constant `ε` from metadata (e.g., 0.97).
116
- 4) LST: `LST(K) = TB / (1 + (λ * TB / ρ) * ln(ε))`, with `λ ≈ 10.895µm`, `ρ = 1.438e-2 m·K`; convert to °C for output.
117
- - **Outputs**: LST GeoTIFF (`float32`, °C), preview + histogram, stats: `min`, `max`, `mean`, `std`, `p10`, `p50`, `p90`.
118
-
119
- ### 7.3 dNBR (Sentinel‑2 pre/post pair)
120
- - **Inputs**: two GeoTIFFs (pre, post) with **NIR** & **SWIR2** bands.
121
- - **Formulas**: `NBR = (NIR - SWIR2) / (NIR + SWIR2 + eps)`; `dNBR = NBR_post - NBR_pre`.
122
- - **Severity bins** (configurable; see §8): Unburned ≤ 0.10, Low ≤ 0.27, Moderate ≤ 0.44, High ≤ 0.66, Very High ≤ 1.00.
123
- - **Outputs**: dNBR GeoTIFF (`float32`), preview + histogram, stats + **class counts/percentages** per severity bin.
124
-
125
- **Common I/O**: preserve CRS and transform; set sensible NoData; save artifacts under `outputs/<task>/<sample_id>/<timestamp>/` (internal; UI does not expose a download).
126
-
127
- ---
128
-
129
- ## 8) Thresholds for Interpretation (config file)
130
-
131
- Thresholds **inform wording**; they don’t change math. Store in `config/heuristics.yaml`:
132
-
133
- ```yaml
134
- ndvi:
135
- mean_low: 0.2
136
- mean_med: 0.5
137
- lst:
138
- hot_mean_c: 32.0
139
- very_hot_p90_c: 38.0
140
- dnbr:
141
- bins:
142
- - {label: "Unburned", max: 0.10}
143
- - {label: "Low", max: 0.27}
144
- - {label: "Moderate", max: 0.44}
145
- - {label: "High", max: 0.66}
146
- - {label: "Very High",max: 1.00}
147
- ```
148
-
149
- ---
150
-
151
- ## 9) Agent & LLM Orchestration
152
-
153
- - **Router**: LLM **tool‑calling** selects **exactly one** tool or refuses with one‑sentence mismatch reasoning. Provide three LangChain tools that wrap `eo/ndvi.py::run`, `eo/lst.py::run`, `eo/dnbr.py::run`.
154
- - **Interpreter**: After a tool runs, pass `{task, sample.title, metrics, extras, thresholds_excerpt}` to LLM and return validated JSON.
155
-
156
- ### 9.1 Pydantic Schemas
157
- ```python
158
- class ToolResult(BaseModel):
159
- raster_path: str
160
- preview_png: str
161
- histogram_png: Optional[str] = None
162
- metrics: Dict[str, float]
163
- extras: Dict[str, Any] = {}
164
-
165
- class Interpretation(BaseModel):
166
- headline: str
167
- bullets: List[str] # include at least one numeric value per bullet
168
- caveats: List[str] # 0–2 items
169
- ```
170
-
171
- ### 9.2 LangChain wiring
172
- - Minimal **tool calling** composition (no heavy agents). Use `JsonOutputParser` for `Interpretation` parsing.
173
- - The router prompt includes: user sentence, selected `sample_id` and its declared `task`, and strict tool contracts.
174
- - If the chosen tool doesn’t match the sample’s declared task → **refusal** (no hidden retry).
175
-
176
- ---
177
-
178
- ## 10) Folder Structure (to generate)
179
-
180
- ```
181
- agent4eo/
182
- app/
183
- main.py # Gradio UI: dropdown + prompt + run; render outputs
184
- agent/
185
- schema.py # Pydantic models
186
- tools.py # LangChain tool wrappers for the 3 Python funcs
187
- router.py # LLM router (tool-calling)
188
- interpreter.py # LLM narrative -> Interpretation
189
- prompts/
190
- system_router.txt
191
- system_interpret.txt
192
- eo/
193
- ndvi.py # math + I/O
194
- lst.py # math + I/O
195
- dnbr.py # math + I/O
196
- viz.py # quicklook, histogram helpers
197
- io.py # read/write helpers
198
- config/
199
- samples_index.json # curated registry
200
- heuristics.yaml # thresholds for narratives
201
- outputs/ # gitignored runtime artifacts
202
- data/samples/... # curated small rasters
203
- tests/
204
- test_router_mismatch.py
205
- test_ndvi_math_small.py
206
- environment.yml
207
- README.md
208
- .gitignore
209
- ```
210
-
211
- ---
212
-
213
- ## 11) “For Claude Code” — Execution & Output Rules
214
-
215
- **Why these rules**: Claude Code performs best with clear scope, explicit constraints, and **returning diffs/patches** plus runnable pointers. Use tool‑calling for structure and schemas for JSON outputs. *Do not add features or deps not listed here.*
216
-
217
- **When generating the repo in one shot, follow these constraints:**
218
- - **Dependency whitelist** (conda/`conda-forge` only): `python=3.11`, `numpy`, `gdal`, `rasterio`, `gradio`, `pydantic`, `pyyaml`, `langchain-core`, `langchain-community`, `mistralai`, `matplotlib` (optional), `pytest` (optional).
219
- - Keep files **short** (< ~200 lines when possible); keep functions small.
220
- - **Output only unified diffs (patches)** plus a short **README**.
221
- - README must **list dependencies** and the **entry point** but **must not include conda/mamba install commands**. Environment creation is handled externally.
222
- - Ensure **structured outputs** via tool‑calling & `JsonOutputParser` for the `Interpretation` schema.
223
- - Implement **strict mismatch handling**: if sample.task ≠ intended tool, return a one‑sentence refusal; don’t run anything.
224
- - All outputs saved under `outputs/<task>/<sample_id>/<timestamp>/` (internal). UI is **display‑only** (no download).
225
-
226
- **Run (README excerpt):**
227
- - Ensure required dependencies (see §12) exist in the environment (managed externally).
228
- - Set `MISTRAL_API_KEY` in your shell.
229
- - Start the app with: `python -m app.main`.
230
-
231
- ---
232
-
233
- ## 12) Dependencies (conda, conda‑forge)
234
-
235
- - EO: `numpy`, `gdal`, `rasterio`, `matplotlib` (optional)
236
- - App/Agent: `gradio`, `pydantic`, `pyyaml`
237
- - LLM: `langchain-core`, `langchain-community`, `mistralai`
238
- - Python: `3.11`
239
- - Tests (optional): `pytest`
240
-
241
- ---
242
-
243
- ## 13) Performance Targets
244
-
245
- - App start < **3s** on a typical laptop.
246
- - Each tool run (on ~50–100 MB samples) < **2–3s** to compute + render preview.
247
- - Memory footprint < **400 MB** during processing.
248
-
249
- ---
250
-
251
- ## 14) Acceptance Criteria
252
-
253
- 1) **Routing correctness**
254
- - NDVI sample + “show vegetation condition” → NDVI tool called.
255
- - LST sample + “analyze urban heat” → LST tool called.
256
- - dNBR pair + “burn severity map” → dNBR tool called.
257
-
258
- 2) **Mismatch handling**
259
- - NDVI sample + “burn severity” prompt → **refusal** explaining that dNBR needs a pre/post pair.
260
-
261
- 3) **Artifacts & UI**
262
- - Each successful run produces artifacts under `outputs/...` and renders **preview**, **histogram** (if available), **stats**, and **interpretation** in the UI (display‑only).
263
-
264
- 4) **Interpretation**
265
- - LLM returns valid `Interpretation` JSON; UI displays headline + bullets + caveats.
266
-
267
- 5) **I/O fidelity**
268
- - Output GeoTIFF preserves CRS & transform; nodata handled consistently.
269
-
270
- ---
271
-
272
- ## 15) Risks & Mitigations
273
-
274
- - **GDAL/rasterio installs** → use `conda-forge` only; pin Python=3.11; keep README dependency list exact.
275
- - **LST constants ambiguity** → store needed constants per sample in `samples_index.json`; document assumptions in code.
276
- - **Router hallucination** → precise tool descriptions; enforce sample.task check; refusal on mismatch.
277
- - **Over‑verbose LLM outputs** → enforce JSON schema; concise style prompts.
278
-
279
- ---
280
-
281
- ## 16) References
282
-
283
- - Internal EO formulas and thresholds as specified above.
284
- - LangChain tool‑calling patterns and JSON parsing best practices.
285
- - Mistral API usage for function/tool calling.
286
-
287
- ---
288
-
289
- *End of PRD.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
environment.yml DELETED
@@ -1,17 +0,0 @@
1
- name: agent4eo
2
- channels:
3
- - conda-forge
4
- dependencies:
5
- - python=3.11
6
- - numpy
7
- - gdal
8
- - rasterio
9
- - gradio
10
- - pydantic
11
- - pyyaml
12
- - langchain-core
13
- - langchain-community
14
- - mistralai
15
- - matplotlib
16
- - pytest
17
- - tenacity
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eo/__init__.py DELETED
@@ -1 +0,0 @@
1
- """Earth Observation processing tools."""