Arquitectura para Aplicaciones Inteligentes con ASP.NET Core
La combinación de ASP.NET Core con tecnologías de inteligencia artificial permite crear aplicaciones web altamente inteligentes, escalables y mantenibles. Este enfoque facilita la incorporación de capacidades como aprendizaje automático, procesamiento del lenguaje natural y visión por computadora directamente en servicios web modernos.
Servicios de Aprendizaje Automático en .NET
ML.NET ofrece una inetgración nativa con ASP.NET Core, permitiendo cargar modelos preentrenados y realizar inferancias en tiempo real. La clave está en gestionar correctamente el ciclo de vida del modelo mediante inyección de deepndencias:
public interface IPrediccionService
{
Task<ResultadoPrediccion> PredecirAsync(EntradaDatos datos);
}
public class ServicioPrediccion : IPrediccionService
{
private readonly ILogger<ServicioPrediccion> _logger;
private readonly PredictionEnginePool<EntradaDatos, ResultadoPrediccion> _motor;
public ServicioPrediccion(
PredictionEnginePool<EntradaDatos, ResultadoPrediccion> motor,
ILogger<ServicioPrediccion> logger)
{
_motor = motor;
_logger = logger;
}
public async Task<ResultadoPrediccion> PredecirAsync(EntradaDatos entrada)
{
try
{
var resultado = _motor.Predict("ModeloClasificacion", entrada);
_logger.LogInformation("Predicción generada: {Etiqueta}", resultado.EtiquetaPredecida);
return await Task.FromResult(resultado);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error durante la predicción");
throw;
}
}
}
En Program.cs, se configura el pool de motores para reutilizar instancias y mejorar el rendimiento:
builder.Services.AddPredictionEnginePool<EntradaDatos, ResultadoPrediccion>()
.FromFile("ModeloClasificacion", "modelos/clasificador.zip", watchForChanges: true);
Despliegue de Modelos ONNX
Para modelos entrenados en Python (TensorFlow/PyTorch), el formato ONNX permite su ejecución en entornos .NET mediante Microsoft.ML.OnnxRuntime:
public class MotorOnnx : IDisposable
{
private readonly InferenceSession _sesion;
private bool _disposed = false;
public MotorOnnx(string rutaModelo)
{
_sesion = new InferenceSession(rutaModelo);
}
public float[] Inferir(float[,] datosEntrada)
{
var tensor = new DenseTensor<float>(datosEntrada, new int[] { 1, datosEntrada.GetLength(0), datosEntrada.GetLength(1) });
var entrada = new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("entrada", tensor) };
using var salida = _sesion.Run(entrada);
return salida.First().AsTensor<float>().ToArray();
}
public void Dispose()
{
if (!_disposed)
{
_sesion?.Dispose();
_disposed = true;
}
}
}
Optimización del Rendimiento con Procesamiento por Lotes
Para escenarios con alta carga, agrupar múltiples solicitudes mejora significativamente la eficiencia del uso de recursos:
public class ProcesadorPorLotes
{
private readonly Queue<SolicitudProcesada> _cola = new();
private readonly Timer _temporizador;
private readonly int _tamanoLote = 64;
public ProcesadorPorLotes()
{
_temporizador = new Timer(EjecutarLote, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(50));
}
private void EjecutarLote(object state)
{
var lote = new List<SolicitudProcesada>();
lock (_cola)
{
while (_cola.Count > 0 && lote.Count < _tamanoLote)
{
lote.Add(_cola.Dequeue());
}
}
if (lote.Any())
{
var resultados = RealizarInferenciaEnLote(lote.Select(s => s.Datos).ToArray());
AsignarResultados(lote, resultados);
}
}
public Task<RespuestaInferencia> EnviarSolicitudAsync(DatosEntrada datos)
{
var fuente = new TaskCompletionSource<RespuestaInferencia>();
var solicitud = new SolicitudProcesada(datos, fuente);
lock (_cola)
{
_cola.Enqueue(solicitud);
}
return fuente.Task;
}
}
Gestión de Versiones y Pruebas A/B
Implementar múltiples versiones de modelos permite pruebas controladas y despliegues progresivos:
[ApiController]
[Route("api/[controller]")]
public class ClasificacionController : ControllerBase
{
private readonly Dictionary<string, IPrediccionService> _servicios;
public ClasificacionController(IEnumerable<IPrediccionService> servicios)
{
_servicios = servicios.ToDictionary(s => s.GetType().Name);
}
[HttpPost("clasificar")]
public async Task<iactionresult> Clasificar([FromBody] EntradaUsuario entrada, [FromQuery] string version = "default")
{
var servicio = _servicios.ContainsKey(version)
? _servicios[version]
: _servicios.Values.First();
var resultado = await servicio.PredecirAsync(new EntradaDatos { Contenido = entrada.Texto });
return Ok(new { Etiqueta = resultado.EtiquetaPredecida, Confianza = resultado.Puntuacion });
}
}
</iactionresult>
Integración de APIs de Procesamiento del Lenguaje Natural
El uso de IHttpClientFactory junto con políticas de resiliencia asegura comunicaciones estables con servicios externos:
services.AddHttpClient("ApiLenguaje", cliente =>
{
cliente.BaseAddress = new Uri(builder.Configuration["Apis:Lenguaje:Url"]);
cliente.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", builder.Configuration["Apis:Lenguaje:Token"]);
})
.AddPolicyHandler(ConfigurarReintento())
.AddPolicyHandler(ConfigurarCircuito());
Las políticas garantizan tolerancia a fallos transitorios:
static IAsyncPolicy<HttpResponseMessage> ConfigurarReintento()
{
return Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(3, i => TimeSpan.FromMilliseconds(200 * Math.Pow(2, i)));
}
Visión por Computadora con OpenCVSharp
OpenCVSharp permite procesar imágenes en tiempo real dentro de servicios background:
public class ServicioDeteccionRostros : BackgroundService
{
private readonly VideoCapture _camara;
private readonly CascadeClassifier _detector;
public ServicioDeteccionRostros()
{
_camara = new VideoCapture(0);
_detector = new CascadeClassifier("modelos/rostro.xml");
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using var frame = new Mat();
_camara.Read(frame);
if (frame.IsEmpty()) continue;
var rostros = _detector.Detect(frame);
foreach (var rect in rostros)
{
Cv2.Rectangle(frame, rect, Scalar.Red, 2);
}
// Publicar imagen procesada vía SignalR o guardar
await Task.Delay(30, stoppingToken);
}
}
}
Monitoreo y Diagnóstico
Mediante middleware personalizado se puede rastrear el rendimiento de las inferencias:
app.Use(async (context, next) =>
{
var inicio = DateTime.UtcNow;
await next();
var duracion = DateTime.UtcNow - inicio;
if (context.Request.Path.StartsWithSegments("/api/prediccion"))
{
var logger = context.RequestServices.GetRequiredService<ILogger<MiddlewareRendimiento>>();
logger.LogInformation("Inferencia completada en {ms} ms", duracion.TotalMilliseconds);
}
});
Despliegue con Docker
Un archivo Dockerfile optimizado incluye los modelos y bibliotecas necesarias:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
COPY modelos/ ./modelos/
COPY modelos/rostro.xml ./modelos/
ENTRYPOINT ["dotnet", "AplicacionAI.dll"]
Esta arquitectura permite escalar horizontalmente servicios de IA mientras se mantiene la consistencia del entorno y la reproducibilidad del despliegue.