SmartPot-AI es el asistente inteligente de SmartPot. Recibe la última lectura de un cultivo, su historial reciente, los actuadores disponibles, dónde está y el clima de afuera, y devuelve un diagnóstico completo que combina estas técnicas de inteligencia artificial:
| Técnica | Qué resuelve | Dónde vive |
|---|---|---|
| Sistema experto | Diagnóstico por variable y conclusiones encadenadas (estrés térmico, riesgo de hongos, bloqueo de nutrientes, falla de sensor…) con su explicación |
app/engine/diagnosis.py, expert_system.py, rules.py
|
| Lógica difusa | Índice de salud de 0 a 100 que tolera la incertidumbre de los sensores | app/engine/fuzzy.py |
| Aprendizaje automático | Regresión logística (ventilación), red neuronal MLP (corrección de pH) e Isolation Forest (lecturas atípicas) | app/engine/models.py |
| Agente reactivo | Convierte el diagnóstico en acciones sobre los actuadores que el cultivo sí tiene | app/engine/agent.py |
| Pronóstico | Tendencia de cada variable (Theil-Sen) y horas hasta salir del rango ideal | app/engine/forecast.py |
| Análisis de flota | Ranking, problemas compartidos del entorno, grupos K-Means y acciones en bloque | app/engine/fleet.py |
| Consejo de lugar | Compara la luz que pide la especie con la que recibe el cultivo y sube el tono solo cuando el lugar ya afecta la salud | |
| Aprendizaje continuo | Aprende de las lecturas reales: supervisado (¿se secará?, ¿habrá calor?, humedad en 1 h) y no supervisado (estados de operación y lecturas atípicas), con reentrenamiento cuando llegan datos nuevos | app/learning/ |
| Aceleración C++ Nativa | Cómputo numérico directo en C++20 (Theil-Sen |
src/native/, app/engine/native_bridge.py
|
Es un servicio interno: solo SmartPot-API lo consulta, con un token compartido, y nunca se publica en Internet.
flowchart LR
req["Lectura + historial<br/>+ actuadores"] --> diag["Diagnóstico<br/>por variable"]
diag --> ml["Modelos de ML"]
diag --> es["Sistema experto<br/>(encadenamiento)"]
ml --> es
diag --> fz["Lógica difusa<br/>índice de salud"]
es --> ag["Agente reactivo"]
ml --> ag
fz --> out["Respuesta"]
es --> out
ag --> out
api["Lecturas reales<br/>(lotes de la API)"] --> lr["Aprendizaje continuo"]
lr --> es
lr --> ag
lr --> out
app/knowledge/profiles.py guarda los rangos ideales de seis especies en la escala de los sensores del dispositivo:
temperatura (°C), humedad del aire (%), luz (lux relativos, 0 a 2000), pH, nutrientes (ppm, EC × 700) y humedad del
sustrato (%).
| Especie | Temperatura | Humedad | pH | TDS (ppm) | Sustrato |
|---|---|---|---|---|---|
Lechuga (LETTUCE) |
15–22 | 50–70 | 5.5–6.5 | 560–840 | 60–80 |
Tomate (TOMATO) |
20–28 | 60–80 | 5.5–6.5 | 1400–2800 | 55–75 |
Fresa (STRAWBERRY) |
18–26 | 60–75 | 5.5–6.2 | 700–980 | 60–80 |
Albahaca (BASIL) |
20–30 | 40–60 | 5.5–6.5 | 700–1120 | 50–70 |
Espinaca (SPINACH) |
15–24 | 50–70 | 6.0–7.0 | 1260–1610 | 60–80 |
Pimentón (PEPPER) |
21–29 | 50–70 | 5.8–6.5 | 1400–2100 | 55–75 |
Una variable fuera de rango se mide en tolerancias (5 °C, 15 %, 300 lux, 0.8 de pH, 400 ppm, 15 %). Desde una tolerancia completa el hallazgo es crítico.
- Diagnóstico: cada variable queda
LOW,OPTIMALoHIGH, con severidadWARNINGoCRITICALy una recomendación en español. Entre las 22:00 y las 6:00 (hora local enviada enlocalHour) la luz baja queda comoREST: es el periodo de oscuridad de la planta y no resta salud. - Modelos: las variables se normalizan respecto al perfil (0 = mínimo ideal, 1 = máximo ideal), así un mismo modelo
sirve para todas las especies. Se entrenan al arrancar con 4000 muestras sintéticas y semilla fija, por lo que los
resultados son reproducibles.
GET /v1/modelsinforma su exactitud. - Sistema experto: un motor de encadenamiento hacia adelante dispara reglas por prioridad (cada regla una sola
vez). Las conclusiones de primer nivel —por ejemplo
heat_stressonutrient_lockout— alimentan reglas de segundo nivel comocompound_stressocritical_state. Si el Isolation Forest y el historial indican una lectura imposible,sensor_faultbloquea las acciones. - Lógica difusa: cada desviación pertenece en distinto grado a los conjuntos «ideal», «aceptable», «desviada» y «crítica». La inferencia Sugeno de orden cero da la salud de cada variable, el índice global las pondera (el pH y la temperatura pesan más) y una regla de peor caso limita la salud cuando algo es crítico.
- Pronóstico: con el historial y la hora de cada lectura, el estimador de Theil-Sen (mediana de las pendientes
entre pares, resistente a lecturas atípicas) calcula la pendiente por hora, el valor esperado en 3 h y en cuántas
horas la variable saldría de su rango. Alimenta las reglas
drying_trendyheat_building. - Agente: propone acciones solo para los actuadores del cultivo: riego si el sustrato está seco (salvo que llueva sobre un cultivo al aire libre), luz ultravioleta si falta de día (y apagarla de noche), ventilación si hay calor, humedad alta o el modelo lo predice, humidificador, dosificador de pH y de nutrientes. Si el pronóstico indica que el sustrato llegará al mínimo o la temperatura al máximo en menos de 1 h, actúa antes (riego o ventilación preventivos). La API aplica el enfriamiento y solo ejecuta con el modo automático activo.
- Aprendizaje: lo aprendido de las lecturas reales de la especie (ver abajo) entra al sistema experto (
learned_drying,learned_heat,unusual_pattern) y, con probabilidad de 85 % o más, al agente como riego o ventilación preventivos. - Lugar y clima: con
placement(bajo techo o al aire libre y cuánto sol recibe) yweather(el clima de afuera que la API pide al simulador), la evaluación devuelveplacement:OKsi el lugar le sirve a la especie,UNKNOWNsi no se sabe dónde está,TIPsi no es el ideal pero la planta está bien yMOVEsi el lugar ya se nota en las lecturas. Al aire libre suma dos reglas:rain_outside(con 0,5 mm o más de lluvia no hace falta regar) ysensor_vs_outside(el sensor se aleja 8 °C o más del clima de afuera). - Flota:
POST /v1/fleetrecibe todos los cultivos de una cuenta y devuelve el ranking por salud, los problemas que se repiten en la mitad o más de los cultivos (señal de que el problema es el entorno), grupos por condiciones similares con K-Means (la cantidad de grupos se elige por silueta) y las acciones del agente reunidas por actuador para aplicarlas en bloque.
Los modelos base se entrenan con datos sintéticos; el aprendizaje continuo los complementa con lo que de verdad pasa en
los cultivos reales. La API envía por lotes cada lectura de un cultivo real que llega por MQTT (las de los cultivos
virtuales son sintéticas y no se envían) (POST /v1/learning/readings) y el servicio las guarda en SQLite dentro del
volumen /data, con el id del cultivo seudonimizado (SHA-256): aprende de la serie de cada cultivo sin saber a
quién pertenece.
| Paso | Qué hace | Dónde |
|---|---|---|
| 1. Calidad de datos | Completitud, validez física y atípicos por rango intercuartílico (k = 3), que se excluyen; da un puntaje DQS | quality.py |
| 2. Variables | Posición de cada variable en su rango ideal, hora del día circular (seno y coseno) y tendencia de la última media hora | features.py |
| 3. Etiquetas autosupervisadas | Lo que pasó en la hora siguiente: ¿el sustrato bajó del mínimo?, ¿la temperatura pasó del máximo?, ¿cuánto cambió la humedad? | features.py |
| 4. Comparación de modelos | Línea base, regresión logística, K vecinos, árbol de decisión, bosque aleatorio, gradient boosting y red neuronal, con validación cruzada temporal (TimeSeriesSplit, 4 cortes) y F1 macro o error medio |
trainer.py |
| 5. Ajuste | GridSearchCV sobre el mejor candidato |
trainer.py |
| 6. Campeón y retador | Se evalúa en el 20 % de lecturas más recientes; el modelo nuevo reemplaza al vigente solo si lo mejora y si supera a la línea base | trainer.py |
| 7. No supervisado | Estados de operación con K-Means (k de 2 a 6 por silueta, con nombre legible) e Isolation Forest entrenado con lecturas reales | trainer.py |
Cada especie se entrena por separado desde 200 lecturas etiquetadas y se reentrena cada 300 lecturas nuevas. En modo autónomo puede ejecutarse en un proceso local secundario, mientras que en arquitecturas desacopladas de producción el entrenamiento se delega enteramente a SmartPot-Trainer (http://trainer-smartpot:8001), operando SmartPot-AI como un motor puro de inferencia sub-milisegundo que recarga los modelos en caliente (POST /v1/learning/reload) en < 5 ms sin interrupción de servicio.
POST /v1/insights responde además learning: el estado de operación actual, si la lectura es atípica para la especie,
la probabilidad de necesitar riego o ventilación en la próxima hora y la humedad esperada del sustrato en 1 h, cada una
con el modelo y su puntaje.
SmartPot-AI incorpora una arquitectura computacional híbrida de alto rendimiento diseñada para operar a nivel sub-milisegundo y eliminar los cuellos de botella de memoria y contención de CPU en producción:
-
Jerarquía de Carga de 3 Niveles (3-Tier Engine Bridge):
-
Tier 1 (Cython Extension
nogil): Cargasmartpot_cy_core(.so/.pyd) vinculando directamente la cabecera C ABIcapi.h. EmpleaMemoryviews(double[::1]) para paso zero-copy sin asignación de objetos Python y ejecuta los bucles intensivos bajo bloqueswith nogil:para paralelismo multihilo real en CPU. -
Tier 2 (ctypes DLL/SO): Carga dinámica del núcleo compilado
smartpot_core.dllolibsmartpot_core.socon vectorización SIMD AVX2. - Tier 3 (Pure Python Fallback): Degradación elegante transparente en NumPy/Python estándar si no se dispone de extensiones compiladas en el entorno local.
-
Tier 1 (Cython Extension
-
Cero Cold-Start con Modelos Base Pre-horneados:
- Eliminación del retraso de inicialización sintética (de ~5.500 ms a < 60 ms) mediante el artefacto comprimido
app/resources/base_models.joblib(601 KB), pre-entrenado con semilla fija 42 víascripts/prebake_models.py. - Carga instantánea de
ventilation(regresión logística),ph_correction(MLPClassifier) yanomaly(IsolationForest de 150 árboles).
- Eliminación del retraso de inicialización sintética (de ~5.500 ms a < 60 ms) mediante el artefacto comprimido
-
Pipeline de Series Temporales con Polars:
-
ReadingStore.load_df: Ingesta directa desde SQLite apl.DataFramemediantepl.read_database, eliminando la creación de tuplas intermedias en memoria de Python. -
quality.assess_polars: Expresionespl.LazyFramevectorizadas para verificación física IQR en paralelo sobre miles de registros. - Vectorización de cálculo de pendientes temporales (
_slopes) para ventanas móviles de 30 minutos.
-
-
Adaptación de Bajo Rango (LoRA) por Especie en NumPy:
- Implementación de
LowRankCropAdapter($W = W_0 + \frac{\alpha}{r} B \cdot A$ con rango$r \in [2, 4]$ ) para actualización reactiva en memoria por especie en$< 0.2\text{ ms}$ , reservandoGridSearchCVúnicamente para ventanas offline periódicas de baja frecuencia.
- Implementación de
Mediciones auditadas en entorno de producción local mediante scripts/benchmark_performance.py:
| Componente / Métrica | Implementación Anterior (Python puro) | Implementación Optimizada (C++20 / Cython / LoRA) | Mejora / Factor |
|---|---|---|---|
| Núcleo C++20 / Cython ( |
2.50 ms | 47.2 µs (0.047 ms) | 53x más rápido |
| Arranque en Frío (Cold-Start) | 5,537 ms | 57.5 ms | 96x más rápido (-98.9%) |
| Control de Calidad DQS (20,000 filas) | 185 ms | 4.31 ms (C++20) / 36.4 ms (Polars) | 43x más rápido |
| Adaptación Online LoRA ( |
15,000–35,000 ms (GridSearch) | 0.189 ms (188 µs) | > 100,000x más rápido |
| Throughput de Inferencia Global | ~18 req/s | 87 req/s | 4.8x mayor rendimiento |
Todas las rutas /v1 exigen Authorization: Bearer <SMARTPOT_AI_TOKEN>.
| Método | Ruta | Descripción |
|---|---|---|
| GET | /health |
Público. {"status":"UP","models":"READY","learning":"PERSISTENT"} |
| POST | /v1/insights |
Evalúa una lectura |
| POST | /v1/fleet |
Analiza todos los cultivos de una cuenta |
| GET | /v1/crop-profiles |
Base de conocimiento por especie |
| GET | /v1/models |
Exactitud de los modelos base |
| GET | /v1/engine |
Telemetría, versión y optimizaciones SIMD del motor nativo en C++20 |
| POST | /v1/learning/readings |
Recibe hasta 2000 lecturas reales por lote |
| GET | /v1/learning/status |
Lecturas, calidad, comparación de modelos, estados y detector de atípicos por especie |
| POST | /v1/learning/train |
Entrena ya una especie ({"cropType":"LETTUCE"}) o las que tienen datos nuevos |
| DELETE | /v1/learning/crops/{cropId} |
Olvida las lecturas de un cultivo eliminado |
{
"cropType": "TOMATO",
"measures": {"temperature": 31, "humidity": 55, "ph": 6.1, "tds": 1600, "soilMoisture": 45, "brightness": 700},
"history": [],
"actuators": ["WATER_PUMP", "FAN", "UV_LIGHT"],
"localHour": 14,
"placement": {"setting": "OUTDOOR", "exposure": "FULL_SUN"},
"weather": {"temperature": 24.4, "humidity": 63, "radiation": 520, "precipitation": 0, "isDay": true}
}Cada elemento de history puede traer measuredAt (ISO 8601); con al menos 6 lecturas que abarquen 20 minutos se
calculan los pronósticos.
{"forecasts": [{"parameter": "soilMoisture", "trend": "FALLING", "slopePerHour": -6.0, "hoursToLimit": 0.7, "limit": "MIN",
"message": "La humedad del sustrato baja 6 % por hora: llegará al mínimo (60 %) en unos 40 minutos."}]}La respuesta trae health (index, level, label y byParameter, la salud de cada variable que explica el índice),
forecasts, diagnosis, conclusions (con su certeza), predictions, placement (el consejo de lugar), actions (actuator, action,
durationSeconds, reason) y un summary como: «Tu tomate está en estado "Saludable" (79/100). Revisa la humedad del
sustrato y la temperatura. El asistente propone 2 acciones.»
SmartPot-AI incorpora un motor de aceleración numérica directa en C++20 (src/native/) diseñado para maximizar el rendimiento, reducir el consumo de memoria y garantizar latencia sub-milisegundo:
-
Estimador Theil-Sen Optimizado (
$O(M)$ lineal): sustituye la ordenación completa y matrices temporales de NumPy (np.triu_indices) por selección lineal mediantestd::nth_element, logrando entre 3.6x y 50x de aceleración con huella de memoria nula. -
Inferencia Difusa Sugeno Vectorizada: funciones de pertenencia trapezoidal sin saltos condicionales (
branchless), evaluación de grado de verdad y ponderación agronómica con constantes en tiempo de compilación (constexpr). - Control de Calidad de Datos (IQR): análisis de completitud, validez física y descarte de espigas atípicas mediante percentiles en tiempo lineal sobre lotes de hasta 20 000 lecturas.
-
Transformación de Características y Codificación Circular: normalización vectorial y cálculo trigonométrico continuo de hora solar (
$\sin$ ,$\cos$ ). -
Arquitectura de C ABI y Fallback Resiliente: enlace dinámico mediante
ctypes(app/engine/native_bridge.py) con tipos estructurados y degradación elegante a Python estándar en entornos sin compilador. -
Observabilidad: consulta de estado en
GET /v1/engine.
- Linux / Docker:
make -C src/native(generaapp/native/libsmartpot_core.socon-O3 -fPIC -std=c++20). - Windows (MSVC): ejecutar
src\native\build.bat(generaapp\native\smartpot_core.dllcon/std:c++20 /O2 /LD). - Multiplataforma (CMake):
cmake -B src/native/build src/native cmake --build src/native/build --config Release
La imagen es pública: ghcr.io/smartpottech/smartpot-ai. La plataforma completa, con este servicio incluido, se
levanta con un solo comando y sin descargar el código:
curl -fsSL https://raw.githubusercontent.com/SmartPotTech/.github/main/docker/demo/compose.yaml | docker compose -f - up -d --waitAbre http://localhost:5173 e ingresa con demo@smartpot.app / SmartPot2026. Se detiene con
docker compose -p smartpot-demo down (con -v borra también los datos). Más detalle en la
demo.
El mismo servicio corre en la demo beta del stack nuevo: cambia demo por demo-beta
en la URL y en el nombre del proyecto.
- uv (instala Python 3.13 si hace falta)
cp .env.example .env # define SMARTPOT_AI_TOKEN (mínimo 24 caracteres)
uv sync
uv run uvicorn app.main:app --reload --port 8000Con DOCS_ENABLED=true la documentación interactiva queda en http://localhost:8000/docs.
uv run ruff check .
uv run pytestCubren la base de conocimiento, el encadenamiento de reglas, la monotonía del índice difuso, la exactitud mínima de los
modelos, las decisiones del agente, la lluvia y el sensor frente al clima, el consejo de lugar, el contrato HTTP y el
aprendizaje continuo: seudonimización, poda, calidad de datos,
etiquetas, modelos que superan a la línea base, campeón y retador, persistencia y la respuesta learning.
docker build -t smartpot-ai .
docker pull ghcr.io/smartpottech/smartpot-ai:latestLa imagen instala las dependencias con uv en una etapa aparte, corre como el usuario 1000 y admite sistema de archivos
de solo lectura. Monta un volumen en /data para conservar lo aprendido; sin él, el aprendizaje funciona en memoria y
se pierde al reiniciar.
Cada cambio en main pasa por el CI, publica la imagen en GHCR (y en Docker Hub como réplica cuando el repositorio
tiene credenciales) y pide el despliegue al workflow central
de SmartPotTech/.github, que actualiza producción de a uno y verifica
/health.
El asistente es un servicio interno: la API le pregunta y aplica sus respuestas. Su documentación propia está en
docs/ (también en DOCX
y PDF), con sus diagramas en docs/diagrams: el general del
componente y los de la evaluación, el aprendizaje continuo y el análisis de flota.
La documentación técnica
explica la base de conocimiento, las 21 reglas, el consejo de lugar, el índice difuso, el pronóstico, el análisis de flota y el aprendizaje
continuo. Los diagramas generales muestran la plataforma completa en una sola imagen ampliable:
- Decisión de la IA: de la lectura a la orden: diagnóstico, pronóstico, modelos, reglas, índice difuso y agente
- Linaje de los datos: cómo llegan las lecturas de los cultivos reales al aprendizaje y qué se guarda
- Máquinas de estado: los estados del aprendizaje de cada especie
Como parte del Plan Maestro de Maximización del Stack B (demo-beta), SmartPot-AI evoluciona in-place hacia un binario 100% nativo en Rust 2024 Edition con #![forbid(unsafe_code)] en la lógica de negocio y mimalloc como allocator global:
-
Servidor HTTP y gRPC Dual:
-
axum 0.8: Preserva la compatibilidad 1:1 con todas las rutas REST JSON públicas (/health,/v1/insights,/v1/fleet,/v1/crop-profiles,/v1/models,/v1/engine). -
tonic: Servidor gRPC de alto rendimiento sobre socket UNIX/var/run/smartpot/ai.sock(mononodo /demo-beta) con fallback transparente a TCP interno.
-
-
Núcleo Físico-Agronómico SIMD (
core::simd/wide):-
VPD Foliar Psicrométrico: Ecuación de Tetens/Buck con offset radiativo foliar (
$\Delta T_{\text{leaf}} = -1.5^\circ\text{C}$ ). - EKF 1D: Filtro de Kalman Extendido unidimensional para desacoplar el ruido electromagnético de relés y bombas en pH, TDS y temperatura.
-
Theil-Sen
$O(M)$ y Sugeno Branchless: Portados a registrosf64x4con paralelismo a nivel de instrucción.
-
VPD Foliar Psicrométrico: Ecuación de Tetens/Buck con offset radiativo foliar (
-
Sistema Experto Compilado en Bitsets
u64:- Codificación de hechos agronómicos en máscaras de bits (
TempHigh,VpdCritical,PhAcidicDrift,MoistureDepleting). - Evaluación atómica de las 21 reglas mediante
(facts & rule.mask) == rule.masken$< 200\text{ ns}$ .
- Codificación de hechos agronómicos en máscaras de bits (
-
Motor ML Zero-Copy y Hot-Reload Atómico:
- Evaluación de árboles con
tract-onnxpuro (eliminandodlopende bibliotecas C dinámicas, Treelite y deserialización pickle). - Tensores LoRA y centroides mapeados en memoria mediante
rkyv(memmap2). - Hot-reload atómico lock-free con
arc_swap::ArcSwap<ModelBundle>previa verificación de hash BLAKE3 y firma Ed25519 ante eventos Valkey Pub/Sub (smartpot:ai:reload). - Control Predictivo Basado en Modelos (MPC) para el cálculo de duración de actuación.
- Evaluación de árboles con
Este proyecto está bajo la licencia MIT. Consulta el archivo LICENSE para más detalles.