Deployer des Agents IA Spatiaux en Entreprise : Guide Pratique
1. Qu'est-ce qu'un Agent IA Spatial ?
Un Agent IA Spatial est un systeme autonome qui combine un LLM (Large Language Model) avec des outils de perception et de raisonnement 3D. Contrairement a un chatbot classique, il peut interroger des nuages de points, naviguer dans des scene graphs et executer des operations geometriques en reponse a des requetes en langage naturel.
En entreprise, ces agents automatisent des workflows qui necessitaient auparavant l'intervention d'experts : inspection de batiments, controle qualite BIM, audit de conformite spatiale, ou analyse de jumeaux numeriques.
# Environnement pour Agent IA Spatial
mamba create -n spatial-agent python=3.11 -y
mamba activate spatial-agent
pip install langchain langchain-openai langgraph
pip install open3d laspy[lazrs] numpy
pip install fastapi uvicorn pydantic
pip install neo4j # Pour le scene graph
LangGraph plutot que les chains classiques de LangChain. Les graphes d'etats permettent des workflows d'agents beaucoup plus complexes avec des boucles de raisonnement, du backtracking et de la planification.
Comment structurer les donnees 3D pour qu'un LLM puisse les comprendre ?
2. Scene Graph 3D : La Structure de Connaissance Spatiale
Un Scene Graph est un graphe oriente qui represente les objets 3D (noeuds) et leurs relations spatiales (aretes). C'est la structure de donnees ideale pour permettre a un LLM de raisonner sur l'espace, car elle transforme la geometrie brute en connaissances structurees.
from dataclasses import dataclass, field
from typing import List, Dict, Optional
import numpy as np
from neo4j import GraphDatabase
@dataclass
class SpatialNode:
"""Objet 3D dans le scene graph."""
id: str
label: str
centroid: np.ndarray # Position 3D
bbox_extent: np.ndarray # Dimensions (L, l, h)
volume: float
n_points: int
properties: Dict = field(default_factory=dict)
@dataclass
class SpatialEdge:
"""Relation spatiale entre deux objets."""
source_id: str
target_id: str
relation: str # "above", "adjacent_to", "inside", etc.
distance: float
metadata: Dict = field(default_factory=dict)
class SceneGraph:
def __init__(self, neo4j_uri, neo4j_auth):
self.driver = GraphDatabase.driver(neo4j_uri, auth=neo4j_auth)
self.nodes: Dict[str, SpatialNode] = {}
self.edges: List[SpatialEdge] = []
def add_object(self, node: SpatialNode):
self.nodes[node.id] = node
with self.driver.session() as s:
s.run(
"CREATE (n:Object {id: $id, label: $label, x: $x, y: $y, z: $z, volume: $vol})",
id=node.id, label=node.label,
x=float(node.centroid[0]), y=float(node.centroid[1]),
z=float(node.centroid[2]), vol=node.volume
)
def compute_spatial_relations(self):
"""Calcule automatiquement les relations spatiales."""
nodes = list(self.nodes.values())
for i, a in enumerate(nodes):
for b in nodes[i+1:]:
dist = np.linalg.norm(a.centroid - b.centroid)
if dist < 2.0: # Seuil de proximite
rel = self._classify_relation(a, b)
edge = SpatialEdge(a.id, b.id, rel, dist)
self.edges.append(edge)
def _classify_relation(self, a, b):
dz = a.centroid[2] - b.centroid[2]
if dz > 0.5: return "above"
if dz < -0.5: return "below"
return "adjacent_to"
O(n^2). Pour des scenes avec plus de 1000 objets, utilisez un KD-Tree spatial pour limiter les comparaisons aux voisins proches uniquement.
Le scene graph est construit. Comment le connecter a un agent LangChain ?
3. Construction de l'Agent avec LangGraph
LangGraph permet de definir un agent comme un graphe d'etats avec des noeuds de decision et des outils. Chaque outil est une capacite spatiale : interroger le scene graph, mesurer des distances, detecter des anomalies.
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langgraph.prebuilt import create_react_agent
import numpy as np
# Outil 1 : Recherche dans le scene graph
@tool
def find_objects(label: str, max_results: int = 10) -> str:
"""Trouve les objets 3D correspondant a un label dans la scene."""
results = scene_graph.query_by_label(label, limit=max_results)
return json.dumps([{
"id": r.id, "label": r.label,
"position": r.centroid.tolist(),
"dimensions": r.bbox_extent.tolist()
} for r in results])
# Outil 2 : Mesure de distance 3D
@tool
def measure_distance(object_id_a: str, object_id_b: str) -> str:
"""Calcule la distance euclidienne entre deux objets 3D."""
a = scene_graph.nodes[object_id_a]
b = scene_graph.nodes[object_id_b]
dist = float(np.linalg.norm(a.centroid - b.centroid))
return f"Distance entre {a.label} et {b.label} : {dist:.2f} metres"
# Outil 3 : Audit de conformite spatiale
@tool
def check_compliance(rule: str) -> str:
"""Verifie une regle de conformite spatiale (ex: distance de securite)."""
violations = compliance_engine.check(scene_graph, rule)
if violations:
return json.dumps({"status": "NON_CONFORME", "violations": violations})
return json.dumps({"status": "CONFORME"})
# Assemblage de l'agent
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [find_objects, measure_distance, check_compliance]
agent = create_react_agent(
model=llm,
tools=tools,
state_modifier="""Tu es un Agent IA Spatial expert en analyse de scenes 3D.
Tu reponds en francais et tu justifies tes analyses par des mesures precises."""
)
visualisation qui genere des captures d'ecran annotees de la scene 3D. L'agent peut ainsi fournir des preuves visuelles de ses analyses, un atout majeur pour les rapports d'audit.
L'agent raisonne sur la scene. Comment l'exposer comme service d'entreprise ?
4. API REST avec FastAPI
Pour l'integration dans les systemes d'entreprise (ERP, GMAO, BIM servers), l'agent doit etre expose via une API REST. FastAPI offre la validation automatique des schemas, la documentation OpenAPI et les performances asynchrones.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import Optional, List
import uuid
app = FastAPI(
title="Spatial AI Agent API",
version="1.0.0",
description="API pour interroger des scenes 3D en langage naturel"
)
class QueryRequest(BaseModel):
question: str = Field(..., example="Quels extincteurs sont a plus de 15m d'une sortie ?")
scene_id: str = Field(..., example="building_floor_3")
max_steps: int = Field(default=5, ge=1, le=20)
class QueryResponse(BaseModel):
request_id: str
answer: str
tool_calls: List[dict]
confidence: float
processing_time_ms: float
@app.post("/api/v1/query", response_model=QueryResponse)
async def query_scene(request: QueryRequest):
"""Interroge une scene 3D en langage naturel."""
request_id = str(uuid.uuid4())
# Charger le scene graph de la scene demandee
scene = load_scene_graph(request.scene_id)
if not scene:
raise HTTPException(404, f"Scene '{request.scene_id}' introuvable")
# Executer l'agent
import time
start = time.time()
result = agent.invoke({
"messages": [{"role": "user", "content": request.question}]
})
elapsed = (time.time() - start) * 1000
return QueryResponse(
request_id=request_id,
answer=result["messages"][-1].content,
tool_calls=extract_tool_calls(result),
confidence=compute_confidence(result),
processing_time_ms=elapsed
)
@app.get("/api/v1/scenes")
async def list_scenes():
"""Liste les scenes 3D disponibles."""
return {"scenes": get_available_scenes()}
cache Redis pour les requetes frequentes. Un meme batiment sera interroge des centaines de fois avec des questions similaires. Le cache reduit le cout LLM de 80% et la latence de 95%.
L'API est operationnelle. Comment gerer les aspects production : monitoring, securite, scalabilite ?
5. Infrastructure Production : Docker, Monitoring, Securite
Un agent IA spatial en production exige une infrastructure robuste. Conteneurisation, observabilite et securite sont les trois piliers non-negociables.
# Image de base optimisee pour le calcul spatial
FROM nvidia/cuda:12.1-runtime-ubuntu22.04
WORKDIR /app
# Dependances systeme
RUN apt-get update && apt-get install -y \
python3.11 python3-pip libgl1-mesa-glx \
&& rm -rf /var/lib/apt/lists/*
# Dependances Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Code de l'agent
COPY . .
# Health check
HEALTHCHECK --interval=30s --timeout=10s \
CMD curl -f http://localhost:8000/health || exit 1
EXPOSE 8000
CMD ["uvicorn", "api_server:app", "--host", "0.0.0.0", "--port", "8000"]
from prometheus_client import Counter, Histogram, start_http_server
import structlog
logger = structlog.get_logger()
# Metriques Prometheus
QUERY_COUNT = Counter("spatial_agent_queries_total", "Total queries", ["scene_id"])
QUERY_LATENCY = Histogram("spatial_agent_query_seconds", "Query latency")
TOOL_CALLS = Counter("spatial_agent_tool_calls", "Tool invocations", ["tool_name"])
LLM_TOKENS = Counter("spatial_agent_llm_tokens", "LLM token usage", ["type"])
def log_query(request_id, scene_id, question, result, elapsed):
"""Logging structure pour l'observabilite."""
QUERY_COUNT.labels(scene_id=scene_id).inc()
QUERY_LATENCY.observe(elapsed)
logger.info("query_completed",
request_id=request_id,
scene_id=scene_id,
question=question[:100],
n_tool_calls=len(result.tool_calls),
elapsed_ms=elapsed * 1000,
confidence=result.confidence
)
# Demarrer le serveur de metriques sur le port 9090
start_http_server(9090)
| Composant | Technologie | Role |
|---|---|---|
| API Gateway | Traefik / Kong | Rate limiting, auth JWT |
| Agent Runtime | FastAPI + LangGraph | Logique metier |
| Scene Graph DB | Neo4j | Stockage relations spatiales |
| Cache | Redis | Requetes frequentes |
| Monitoring | Prometheus + Grafana | Metriques et alertes |
L'infrastructure est en place. Comment gerer les cas limites et les erreurs de l'agent ?
6. Gestion des Erreurs et Guardrails
Un agent en production doit gerer gracieusement les echecs : hallucinations du LLM, donnees manquantes dans le scene graph, timeouts sur les calculs 3D. Les guardrails sont des garde-fous qui valident les sorties de l'agent avant qu'elles n'atteignent l'utilisateur.
from dataclasses import dataclass
from typing import Optional
@dataclass
class ValidationResult:
is_valid: bool
reason: Optional[str] = None
corrected_answer: Optional[str] = None
class SpatialGuardrails:
"""Guardrails specifiques aux agents IA spatiaux."""
def validate_distance(self, claimed_distance: float, object_ids: tuple) -> ValidationResult:
"""Verifie qu'une distance annoncee est physiquement coherente."""
actual = self.compute_actual_distance(*object_ids)
error = abs(claimed_distance - actual) / actual
if error > 0.1: # Plus de 10% d'erreur
return ValidationResult(
is_valid=False,
reason=f"Distance incorrecte: {claimed_distance:.2f}m vs {actual:.2f}m reel",
corrected_answer=f"La distance reelle est de {actual:.2f} metres"
)
return ValidationResult(is_valid=True)
def validate_object_exists(self, object_id: str) -> ValidationResult:
"""Verifie que l'agent ne reference pas un objet inexistant."""
if object_id not in scene_graph.nodes:
return ValidationResult(
is_valid=False,
reason=f"Objet '{object_id}' non trouve dans le scene graph"
)
return ValidationResult(is_valid=True)
def validate_spatial_logic(self, answer: str) -> ValidationResult:
"""Verifie la coherence spatiale (ex: un objet ne peut etre 'au-dessus' ET 'en-dessous')."""
contradictions = self._detect_spatial_contradictions(answer)
if contradictions:
return ValidationResult(
is_valid=False,
reason=f"Contradictions spatiales detectees: {contradictions}"
)
return ValidationResult(is_valid=True)
guardrails = SpatialGuardrails()
Comment tester et valider l'agent avant le deploiement en production ?
7. Tests et Validation : Le Framework d'Evaluation
Tester un agent IA spatial est plus complexe que tester un logiciel classique. Les reponses sont non-deterministes, les parcours d'outils varient, et la qualite depend du contexte spatial. Nous utilisons un framework d'evaluation a trois niveaux.
import pytest
from typing import List
class SpatialAgentTestSuite:
"""Suite de tests pour un Agent IA Spatial."""
# Niveau 1 : Tests unitaires des outils
def test_find_objects_returns_valid_json(self):
result = find_objects.invoke({"label": "chaise"})
data = json.loads(result)
assert isinstance(data, list)
for obj in data:
assert "position" in obj
assert len(obj["position"]) == 3
def test_distance_is_symmetric(self):
d1 = measure_distance.invoke({"object_id_a": "obj_1", "object_id_b": "obj_2"})
d2 = measure_distance.invoke({"object_id_a": "obj_2", "object_id_b": "obj_1"})
assert extract_number(d1) == pytest.approx(extract_number(d2))
# Niveau 2 : Tests d'integration (agent complet)
def test_agent_uses_correct_tools(self):
result = agent.invoke({"messages": [{
"role": "user",
"content": "Combien de chaises y a-t-il dans la salle de reunion ?"
}]})
tool_names = [tc.name for tc in extract_tool_calls(result)]
assert "find_objects" in tool_names
# Niveau 3 : Tests de regression semantique
def test_known_answers(self):
"""Verifie que l'agent donne des reponses correctes sur des cas connus."""
test_cases = [
("Quel est l'objet le plus haut ?", "armoire_042"),
("Y a-t-il un extincteur pres de la porte ?", "oui"),
]
for question, expected in test_cases:
answer = get_agent_answer(question)
assert expected.lower() in answer.lower()
regression testing automatique qui rejoue un corpus de 100+ questions a chaque mise a jour du modele ou du scene graph. Tracez le taux de reponses correctes dans le temps pour detecter les regressions.
Conclusion : L'Agent IA Spatial, un Actif Strategique
Deployer un Agent IA Spatial en entreprise n'est pas un projet de recherche. C'est un projet d'ingenierie qui exige rigueur, architecture solide et monitoring continu.
- Scene Graph : La fondation structurelle qui transforme la geometrie en connaissance.
- LangGraph : Le framework d'orchestration qui connecte le LLM aux outils spatiaux.
- API REST : L'interface d'integration avec les systemes d'entreprise existants.
- Guardrails : La couche de securite qui previent les hallucinations spatiales.
- Tests : Le filet de securite qui garantit la qualite dans le temps.
Ces competences sont au coeur du module Spatial AI de la formation 3D Geodata Academy, ou vous construisez votre propre agent de bout en bout.
🚀 Construisez votre Agent IA Spatial
Ce guide pose les bases. Notre formation Elite vous accompagne dans la construction et le deploiement de votre agent sur vos donnees d'entreprise.
Rejoindre l'Elite (120h)