Multi-step spatial agent orchestration is the discipline of running an agent that plans a sequence of geoprocessing calls, executes them one at a time, and validates the intermediate geometry after every step so a single bad operation cannot silently poison the rest of the chain. Part of the Geospatial Prompt Engineering & Tool Routing area, this guide addresses the failure mode unique to multi-step spatial work: errors that are invisible at the step boundary but compound into a corrupted final result. It gives you an orchestrator with explicit state, checkpoints, and rollback.
A single tool call can be validated in isolation, but a chain of them accumulates risk. A buffer that quietly produces an empty geometry, a reprojection that shifts a feature off the map, an intersection that returns a GeometryCollection instead of a polygon — each is locally plausible and globally fatal. The orchestrator’s job is to treat every intermediate geometry as untrusted, gate the transition between steps behind a validation predicate, checkpoint the last good state, and fall back deterministically the moment an invariant breaks rather than pressing on into nonsense.
Foundational Principles
Every intermediate geometry is untrusted until validated. The output of step N is the input to step N+1, so an unchecked geometry propagates its defect down the whole chain. The orchestrator must run a validation predicate — non-empty, valid, correct type, plausible extent — before any step is allowed to consume the previous step’s result.
State is explicit and checkpointed, never implicit. The agent’s progress cannot live only in local variables or model context. Each accepted step writes a checkpoint holding the validated geometry and the step index, so the chain can resume from the last good point and roll back cleanly on failure instead of restarting from scratch.
Failure is a first-class branch, not an exception you hope never fires. When a step’s output fails validation, the orchestrator rolls back to the last checkpoint and takes a deterministic fallback — a narrower operation, a safe default, or a clean abort — rather than feeding the model a broken geometry and asking it to improvise.
Step-by-Step Implementation Pipeline
1. Model the plan and the checkpointed state
Represent the plan as an ordered list of typed steps and the run as a state object that carries the last validated geometry plus a stack of checkpoints. This makes rollback a pop, not a rerun.
from dataclasses import dataclass, field
from typing import List, Optional, Callable, Dict, Any
from shapely.geometry.base import BaseGeometry
@dataclass
class Step:
name: str
tool: Callable[[BaseGeometry, Dict[str, Any]], BaseGeometry]
params: Dict[str, Any]
@dataclass
class Checkpoint:
step_index: int
geometry: BaseGeometry
@dataclass
class OrchestrationState:
current: BaseGeometry
checkpoints: List[Checkpoint] = field(default_factory=list)
step_index: int = 0
def commit(self, geom: BaseGeometry) -> None:
self.checkpoints.append(Checkpoint(self.step_index, geom))
self.current = geom
self.step_index += 1
def rollback(self) -> Optional[Checkpoint]:
return self.checkpoints[-1] if self.checkpoints else None
Because each commit snapshots the validated geometry, the run always has a well-defined point to return to. Deciding which fallback to take when a rollback fires draws on Fallback Routing for Geospatial Queries.
2. Run each step behind a geometry validation gate
The core loop invokes a tool, validates its output against explicit predicates, and only then commits a checkpoint. Any failure rolls back and triggers the deterministic fallback.
import logging
logger = logging.getLogger("spatial_orchestrator")
def is_geometry_sound(geom: Optional[BaseGeometry]) -> bool:
if geom is None or geom.is_empty:
return False
if not geom.is_valid:
return False
# Reject implausible planetary extents (WGS84 degrees).
minx, miny, maxx, maxy = geom.bounds
if not (-180 <= minx <= maxx <= 180 and -90 <= miny <= maxy <= 90):
return False
return True
def run_chain(
plan: List[Step],
state: OrchestrationState,
fallback: Callable[[str, OrchestrationState], Dict[str, Any]],
) -> Dict[str, Any]:
for step in plan:
try:
result = step.tool(state.current, step.params)
except Exception as exc:
logger.error("step %s raised: %s", step.name, exc)
return fallback(f"TOOL_ERROR:{step.name}", state)
if not is_geometry_sound(result):
logger.warning("step %s produced unsound geometry", step.name)
last = state.rollback()
reason = f"VALIDATION_FAIL:{step.name}@{last.step_index if last else -1}"
return fallback(reason, state)
state.commit(result)
logger.info("committed checkpoint %d after %s", state.step_index - 1, step.name)
return {"status": "ok", "geometry": state.current, "steps": state.step_index}
The gate is deliberately strict: an empty or invalid geometry, or one whose bounds escape the globe, halts the chain before the next step consumes it. This same per-step validation discipline underlies LLM-Assisted Geoprocessing Pipelines, which frames the broader pipeline these agents run inside.
3. Persist checkpoints so a chain can resume
For long or asynchronous chains, in-memory checkpoints are not enough — a crashed worker must resume from the last validated geometry. Serialize each checkpoint to durable storage keyed by run id, and key each step by an input hash so a resumed run skips already-completed, idempotent steps rather than recomputing them. The concrete persistence and resume mechanics are worked out in Chaining Geoprocessing Tools with State Checkpoints.
Failure Modes & Root Causes
Silent empty-geometry propagation. An intersection with no overlap returns an empty geometry, which every subsequent step happily processes into more emptiness. The root cause is treating “no error raised” as “success”; the fix is the explicit non-empty gate before each commit.
Lost work on transient failure. A network blip in step 7 of a 10-step chain restarts everything when there is no checkpoint. The root cause is implicit state; the fix is a durable checkpoint after every validated step.
CRS drift across steps. One tool returns metres, the next assumes degrees, and the extent check is the only thing standing between the agent and a feature launched into orbit. The root cause is unvalidated CRS assumptions between steps; the fix is asserting the working CRS as part of the soundness gate.
Rollback to a corrupt checkpoint. If a defective geometry was committed because the gate was too weak, rollback returns to a poisoned state. The root cause is an under-specified validation predicate; the fix is to make is_geometry_sound conservative and to never commit without it.
Production Validation Protocols
- Assert
is_geometry_soundis called before everycommit— enforce with a test that patches the gate and confirms no checkpoint is written when it returnsFalse. - Assert that a tool raising an exception yields a deterministic fallback and leaves the checkpoint stack unchanged from the last good state.
- Simulate a mid-chain failure and assert the run resumes from the correct checkpoint index rather than step zero.
- Track per-step validation-failure rate and rollback count as observability KPIs; a rising rollback rate signals a drifting planner or a flaky tool.
- Assert idempotency: replaying a step with an identical input hash produces an identical checkpoint and no duplicate side effects.