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.