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
- Crear proyecto:
dotnet new webapi -n MyAiApp
- Guardar la clave de OpenAI en variables de entorno o Azure Key Vault (no en
appsettings.jsonen producción). - Registrar
HttpClientconIHttpClientFactory. - 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
CancellationTokenpara abortar peticiones. - Serialización con
System.Text.Jsony 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
HttpMessageHandlerpara 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
| Ítem | Estado sugerido |
|---|---|
| Clave en Key Vault / variables de entorno | Requerido |
| Cliente tipado + IHttpClientFactory | Requerido |
| Polly: reintentos y circuit breaker | Recomendado |
| Límites por usuario y validación de entrada | Requerido |
| Cache (Redis) para respuestas frecuentes | Recomendado |
| Logs, métricas y trazas | Requerido |
| Pruebas unitarias e integración | Requerido |
| Rotación de claves y auditoría | Recomendado |
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.
