Integración de la API de ChatGPT en una aplicación ASP.NET Core

Resumen ejecutivo

Esta guía traduce y amplía el artículo original sobre cómo integrar la API de ChatGPT en una aplicación ASP.NET Core, añadiendo mejoras técnicas orientadas a producción: cliente tipado, resiliencia con Polly, streaming, cache, observabilidad, pruebas y prácticas de seguridad. La versión original explica la creación del proyecto, el almacenamiento de la clave y un servicio básico para llamar al endpoint de chat.

Requisitos previos

  • Clave de OpenAI activa.
  • .NET SDK (recomendado .NET 8 o superior).
  • Conocimientos básicos de ASP.NET Core, inyección de dependencias y IHttpClientFactory.

1. Pasos básicos

  1. Crear proyecto:
    • dotnet new webapi -n MyAiApp
  2. Guardar la clave de OpenAI en variables de entorno o Azure Key Vault (no en appsettings.json en producción).
  3. Registrar HttpClient con IHttpClientFactory.
  4. Implementar un servicio que encapsule llamadas a la API de OpenAI y exponerlo mediante un controlador Web API.

2. Mejoras técnicas detalladas

2.1 Cliente tipado y configuración centralizada

Registrar un cliente tipado centraliza BaseAddress, headers y timeouts, facilita pruebas y evita duplicación.

Program.cs (registro del cliente tipado con Polly):

csharp

var builder = WebApplication.CreateBuilder(args);
var openAiKey = builder.Configuration["OpenAI:ApiKey"];
builder.Services.AddHttpClient<OpenAiClient>(client =>
{
client.BaseAddress = new Uri("https://api.openai.com/v1/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", openAiKey);
client.Timeout = TimeSpan.FromSeconds(60);
})
.AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(new[] {
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(4)
}))
.AddTransientHttpErrorPolicy(policy => policy.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
builder.Services.AddScoped<IChatService, ChatService>();

Por qué: IHttpClientFactory evita agotamiento de sockets y un cliente tipado facilita pruebas y configuración centralizada.

2.2 Resiliencia: reintentos y circuit breaker con Polly

Usar Polly para reintentos exponenciales y circuit breaker evita sobrecargar servicios externos y mejora la estabilidad. La integración con IHttpClientFactory es la práctica recomendada.

Ejemplo de política avanzada (esquema):

csharp

.AddPolicyHandler(Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(r => !r.IsSuccessStatusCode)
.AdvancedCircuitBreakerAsync(
failureThreshold: 0.5,
samplingDuration: TimeSpan.FromSeconds(30),
minimumThroughput: 10,
durationOfBreak: TimeSpan.FromSeconds(60)))

2.3 Servicio de chat robusto (ChatService)

  • Validación y saneamiento del prompt.
  • Límites de longitud y tokens.
  • Uso de CancellationToken para abortar peticiones.
  • Serialización con System.Text.Json y opciones camelCase.

Ejemplo de implementación:

csharp

public class ChatService : IChatService
{
private readonly HttpClient _http;
private readonly ILogger<ChatService> _log;
private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
public ChatService(HttpClient http, ILogger<ChatService> log)
{
_http = http;
_log = log;
}
public async Task<string> GetResponseAsync(string prompt, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(prompt)) throw new ArgumentException("Prompt vacío");
prompt = prompt.Length > 4000 ? prompt[..4000] : prompt;
var body = new
{
model = "gpt-4o-mini",
messages = new[] { new { role = "user", content = prompt } },
max_tokens = 1024
};
using var resp = await _http.PostAsJsonAsync("chat/completions", body, _jsonOptions, ct);
if (!resp.IsSuccessStatusCode)
{
var err = await resp.Content.ReadAsStringAsync(ct);
_log.LogWarning("OpenAI error {Status}: {Error}", resp.StatusCode, err);
throw new HttpRequestException($"OpenAI API error {resp.StatusCode}");
}
using var stream = await resp.Content.ReadFromJsonAsync<JsonDocument>(_jsonOptions, ct);
var content = stream.RootElement.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString();
return content ?? string.Empty;
}
}

2.4 Streaming de respuestas

Cuando el endpoint lo soporte, consumir la respuesta en stream reduce la latencia percibida y mejora la experiencia del usuario. Implementar lectura por chunks desde ReadAsStreamAsync() y procesar SSE o JSON chunked. Manejar reconexiones y cancelaciones.

2.5 Cache y control de costos

  • Redis para cachear respuestas determinísticas (resúmenes, plantillas).
  • TTL por tipo de prompt.
  • Registro de tokens consumidos por petición para auditar costos.
  • Límites por usuario para controlar gasto.

2.6 Observabilidad

  • Logs estructurados (Serilog): incluir requestId, userId, model, tokensUsed, latency.
  • Métricas: exportar a Prometheus o Application Insights.
  • Trazas distribuidas con OpenTelemetry para correlacionar frontend → API → OpenAI.

2.7 Seguridad y cumplimiento

  • No exponer la API key en frontend.
  • Almacenar secretos en Key Vault o variables de entorno.
  • Rotación de claves y auditoría.
  • Autenticación/Autorización en endpoints (JWT, scopes).
  • Saneamiento de prompts para evitar inyección de instrucciones o filtrado de datos sensibles.

2.8 Pruebas y CI/CD

  • Unit tests: mockear HttpMessageHandler para simular respuestas de OpenAI.
  • Integration tests: entorno sandbox o claves de prueba.
  • Pipelines: linters, pruebas, escaneo de secretos y despliegue automatizado.

3. Ejemplo de controlador (API)

csharp

[ApiController]
[Route("api/[controller]")]
public class ChatController : ControllerBase
{
private readonly IChatService _chat;
private readonly ILogger<ChatController> _log;
public ChatController(IChatService chat, ILogger<ChatController> log)
{
_chat = chat;
_log = log;
}
[HttpPost("prompt")]
public async Task<IActionResult> PostPrompt([FromBody] PromptRequest req, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(req?.Prompt)) return BadRequest("Prompt requerido");
var result = await _chat.GetResponseAsync(req.Prompt, ct);
return Ok(new { response = result });
}
}
public record PromptRequest(string Prompt);

4. Checklist de producción

ÍtemEstado sugerido
Clave en Key Vault / variables de entornoRequerido
Cliente tipado + IHttpClientFactoryRequerido
Polly: reintentos y circuit breakerRecomendado
Límites por usuario y validación de entradaRequerido
Cache (Redis) para respuestas frecuentesRecomendado
Logs, métricas y trazasRequerido
Pruebas unitarias e integraciónRequerido
Rotación de claves y auditoríaRecomendado

5. Notas sobre costes y gobernanza

  • Registrar tokens por petición permite asignar costes a clientes o tenants.
  • Definir políticas de uso (tokens máximos por día/usuario) para evitar sorpresas en la factura.

Conclusión

Este documento reúne la traducción del artículo original y añade mejoras prácticas para llevar la integración de ChatGPT a un entorno de producción: resiliencia, seguridad, observabilidad, cache y pruebas. Pega este contenido en Word y adáptalo a tu proyecto; incluye los fragmentos de código en archivos .cs y configura las dependencias NuGet (Polly, Serilog, OpenTelemetry, StackExchange.Redis) según tus necesidades.

Publicado por

Avatar de Desconocido

hughfernandez

Azure lover, .NET zombie, devOps enginner, sysAdmin curious, iot entusiasm, XAML husband, ASP.NET ninja, AI fan, gamer by conviction!!...

Deja un comentario