Skip to content

Latest commit

 

History

History
552 lines (442 loc) · 13.4 KB

File metadata and controls

552 lines (442 loc) · 13.4 KB

Ejemplos de Uso - LIN OpenAI Framework

Este documento contiene ejemplos prácticos de cómo usar el framework.

?? Índice

  1. Crear Tools Simples
  2. Trabajar con Schemas Complejos
  3. Validación y Error Handling
  4. Extensiones Avanzadas
  5. Integración con OpenAI (Conceptual)

Crear Tools Simples

Ejemplo 1: Tool sin parámetros

public class SystemTools
{
    [Tool(Description = "Obtiene la fecha y hora actual del sistema")]
    public DateTimeResponse GetCurrentDateTime()
    {
        return new DateTimeResponse
        {
            DateTime = DateTime.Now,
            TimeZone = TimeZoneInfo.Local.DisplayName
        };
    }
}

[JsonSchema(Description = "Información de fecha y hora")]
public class DateTimeResponse
{
    [JsonSchemaProperty(Description = "Fecha y hora actual", Type = "string", Format = "date-time")]
    public DateTime DateTime { get; set; }
    
    [JsonSchemaProperty(Description = "Zona horaria")]
    public required string TimeZone { get; set; }
}

Ejemplo 2: Tool con parámetro opcional

public class StringTools
{
    [Tool(Description = "Convierte texto a mayúsculas o minúsculas")]
    public TextResponse ConvertCase(
        [ToolParam(Description = "Texto a convertir")]
        string text,
        
        [ToolParam(Description = "Modo: 'upper' o 'lower'", Required = false)]
        string mode = "upper")
    {
        return new TextResponse
        {
            Original = text,
            Converted = mode.ToLower() == "lower" ? text.ToLower() : text.ToUpper(),
            Mode = mode
        };
    }
}

Ejemplo 3: Tool con múltiples parámetros

public class FileTools
{
    [Tool(Description = "Busca archivos por nombre y extensión")]
    public FileSearchResponse SearchFiles(
        [ToolParam(Description = "Directorio donde buscar")]
        string directory,
        
        [ToolParam(Description = "Patrón de búsqueda (ej: *.txt)")]
        string pattern,
        
        [ToolParam(Description = "Incluir subdirectorios", Required = false)]
        bool recursive = false,
        
        [ToolParam(Description = "Límite de resultados", Required = false)]
        int maxResults = 100)
    {
        // Implementación mock...
        return new FileSearchResponse
        {
            Directory = directory,
            Pattern = pattern,
            FilesFound = Random.Shared.Next(0, maxResults),
            Recursive = recursive
        };
    }
}

Trabajar con Schemas Complejos

Schema con Arrays

[JsonSchema(Description = "Respuesta con lista de usuarios")]
public class UserListResponse
{
    [JsonSchemaProperty(Description = "Lista de usuarios encontrados", Required = true)]
    public required List<User> Users { get; set; }
    
    [JsonSchemaProperty(Description = "Total de usuarios", Type = "integer")]
    public int TotalCount { get; set; }
}

public class User
{
    [JsonSchemaProperty(Description = "ID del usuario", Type = "integer")]
    public int Id { get; set; }
    
    [JsonSchemaProperty(Description = "Nombre completo")]
    public required string Name { get; set; }
    
    [JsonSchemaProperty(Description = "Email", Format = "email")]
    public required string Email { get; set; }
}

Schema con Objetos Anidados

[JsonSchema(Description = "Información completa del pedido")]
public class OrderResponse
{
    [JsonSchemaProperty(Description = "ID del pedido")]
    public required string OrderId { get; set; }
    
    [JsonSchemaProperty(Description = "Información del cliente")]
    public required CustomerInfo Customer { get; set; }
    
    [JsonSchemaProperty(Description = "Items del pedido")]
    public required List<OrderItem> Items { get; set; }
    
    [JsonSchemaProperty(Description = "Total del pedido", Type = "number")]
    public decimal Total { get; set; }
}

public class CustomerInfo
{
    [JsonSchemaProperty(Description = "Nombre del cliente")]
    public required string Name { get; set; }
    
    [JsonSchemaProperty(Description = "Email de contacto", Format = "email")]
    public required string Email { get; set; }
}

public class OrderItem
{
    [JsonSchemaProperty(Description = "Nombre del producto")]
    public required string ProductName { get; set; }
    
    [JsonSchemaProperty(Description = "Cantidad", Type = "integer")]
    public int Quantity { get; set; }
    
    [JsonSchemaProperty(Description = "Precio unitario", Type = "number")]
    public decimal Price { get; set; }
}

Validación y Error Handling

Validar Tools antes de Registro

var registry = new ToolRegistry();

// Validar antes de registrar
var validation = ToolValidator.ValidateToolClass(typeof(MyTools));

if (!validation.IsValid)
{
    Console.WriteLine("? Errores encontrados:");
    foreach (var error in validation.Errors)
    {
        Console.WriteLine($"  - {error}");
    }
    return;
}

if (validation.HasWarnings)
{
    Console.WriteLine("??  Advertencias:");
    foreach (var warning in validation.Warnings)
    {
        Console.WriteLine($"  - {warning}");
    }
}

// Registrar si la validación pasó
registry.AddToolsFrom<MyTools>();

Invocación Segura con Try/Catch

var registry = new ToolRegistry();
registry.AddToolsFrom<MathTools>();

try
{
    var result = registry.InvokeTool("Divide", 10.0, 0.0);
    Console.WriteLine($"Resultado: {result}");
}
catch (TargetInvocationException ex)
{
    // La excepción real está en InnerException
    Console.WriteLine($"Error en la tool: {ex.InnerException?.Message}");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Tool no encontrada: {ex.Message}");
}

Usando TryInvokeTool

var result = registry.TryInvokeTool("Divide", 10.0, 0.0);

if (result == null)
{
    Console.WriteLine("? La invocación falló");
}
else
{
    Console.WriteLine($"? Resultado: {result}");
}

Extensiones Avanzadas

Buscar Tools Dinámicamente

var registry = new ToolRegistry();
registry.AddToolsFrom<WeatherTools>();
registry.AddToolsFrom<MathTools>();
registry.AddToolsFrom<DatabaseTools>();

// Buscar todas las tools relacionadas con "weather"
var weatherTools = registry.FindTools("weather");
Console.WriteLine($"Tools encontradas: {weatherTools.Count()}");

foreach (var tool in weatherTools)
{
    Console.WriteLine($"  • {tool.Key}: {tool.Value.Description}");
}

// Buscar tools relacionadas con "user"
var userTools = registry.FindTools("user");
// ...

Estadísticas del Registry

var registry = new ToolRegistry();
registry.AddToolsFrom<WeatherTools>();
registry.AddToolsFrom<MathTools>();
registry.AddToolsFrom<DatabaseTools>();

var stats = registry.GetStats();

Console.WriteLine($"?? Estadísticas:");
Console.WriteLine($"  Total de tools: {stats.TotalTools}");
Console.WriteLine($"  Total de parámetros: {stats.TotalParameters}");
Console.WriteLine($"  Tools con return type: {stats.ToolsWithReturnType}");
Console.WriteLine($"  Promedio de parámetros: {stats.AverageParametersPerTool:F2}");

Exportar Tools a JSON

var registry = new ToolRegistry();
registry.AddToolsFrom<WeatherTools>();
registry.AddToolsFrom<MathTools>();

// Exportar con formato
var json = registry.ExportToolsToJson(indented: true);
Console.WriteLine(json);

// Guardar en archivo
File.WriteAllText("tools-definition.json", json);

Verificar Existencia de Tool

if (registry.HasTool("GetCurrentWeather"))
{
    var result = registry.InvokeTool("GetCurrentWeather", "Madrid", "celsius");
}
else
{
    Console.WriteLine("Tool no disponible");
}

Integración con OpenAI (Conceptual)

Este es un ejemplo conceptual de cómo se integraría con OpenAI API.

Paso 1: Registrar Tools

var registry = new ToolRegistry();
registry.AddToolsFrom<WeatherTools>();
registry.AddToolsFrom<MathTools>();

Paso 2: Convertir a Formato OpenAI

// Pseudo-código: convertir tools a formato OpenAI
var openAITools = registry.GetAllTools().Select(kvp => new
{
    type = "function",
    function = new
    {
        name = kvp.Key,
        description = kvp.Value.Description,
        parameters = new
        {
            type = "object",
            properties = kvp.Value.Parameters.ToDictionary(
                p => p.Name,
                p => new
                {
                    type = MapTypeToJsonType(p.Type),
                    description = p.Description
                }
            ),
            required = kvp.Value.Parameters
                .Where(p => p.Required)
                .Select(p => p.Name)
                .ToArray()
        }
    }
}).ToList();

Paso 3: Enviar a OpenAI

// Pseudo-código: llamada a OpenAI
var response = await openAIClient.CreateChatCompletionAsync(new
{
    model = "gpt-4",
    messages = new[]
    {
        new { role = "user", content = "¿Qué temperatura hay en Madrid?" }
    },
    tools = openAITools
});

Paso 4: Procesar Function Call

// Si OpenAI quiere llamar una function
if (response.Choices[0].FinishReason == "tool_calls")
{
    var toolCall = response.Choices[0].Message.ToolCalls[0];
    var toolName = toolCall.Function.Name;
    var arguments = JsonSerializer.Deserialize<Dictionary<string, object>>(
        toolCall.Function.Arguments
    );
    
    // Invocar la tool usando nuestro registry
    var result = registry.InvokeTool(
        toolName,
        arguments.Values.ToArray()
    );
    
    // Serializar resultado
    var resultJson = JsonSerializer.Serialize(result);
    
    // Enviar resultado de vuelta a OpenAI
    var followUpResponse = await openAIClient.CreateChatCompletionAsync(new
    {
        model = "gpt-4",
        messages = new[]
        {
            // ...mensajes anteriores...
            new { role = "tool", tool_call_id = toolCall.Id, content = resultJson }
        }
    });
}

Ejemplo Completo de Workflow

public class AIOrchestrator
{
    private readonly ToolRegistry _registry;
    private readonly OpenAIClient _openAI; // Pseudo
    
    public AIOrchestrator()
    {
        _registry = new ToolRegistry();
        _registry.AddToolsFrom<WeatherTools>();
        _registry.AddToolsFrom<MathTools>();
        // _openAI = new OpenAIClient(...);
    }
    
    public async Task<string> ProcessUserQuery(string query)
    {
        // 1. Enviar query a OpenAI con tools disponibles
        var response = await SendToOpenAI(query);
        
        // 2. Si OpenAI quiere usar una tool
        if (response.WantsToCallTool)
        {
            // 3. Ejecutar la tool
            var toolResult = _registry.InvokeTool(
                response.ToolName,
                response.ToolArguments
            );
            
            // 4. Enviar resultado a OpenAI
            var finalResponse = await SendToolResultToOpenAI(
                toolResult,
                response.ToolCallId
            );
            
            return finalResponse.Answer;
        }
        
        return response.Answer;
    }
}

?? Mejores Prácticas

1. Descripciones Claras

// ? BUENO
[Tool(Description = "Busca información de vuelos por número de vuelo y fecha")]
public FlightInfo SearchFlight(
    [ToolParam(Description = "Número de vuelo (ej: AA123)")]
    string flightNumber,
    
    [ToolParam(Description = "Fecha del vuelo en formato YYYY-MM-DD")]
    string date)
{
    // ...
}

// ? MALO
[Tool(Description = "Busca vuelo")]
public FlightInfo SearchFlight(string fn, string dt)
{
    // ...
}

2. Validación de Parámetros

[Tool(Description = "Divide dos números")]
public CalculationResponse Divide(
    [ToolParam(Description = "Dividendo")]
    double dividend,
    
    [ToolParam(Description = "Divisor (no puede ser cero)")]
    double divisor)
{
    if (divisor == 0)
    {
        throw new ArgumentException("El divisor no puede ser cero", nameof(divisor));
    }
    
    return new CalculationResponse
    {
        Operation = "division",
        Operand1 = dividend,
        Operand2 = divisor,
        Result = dividend / divisor
    };
}

3. Usar Tipos Específicos

// ? BUENO - Tipo específico
[JsonSchema]
public class ProductSearchResponse
{
    [JsonSchemaProperty(Description = "Productos encontrados")]
    public required List<Product> Products { get; set; }
    
    [JsonSchemaProperty(Description = "Total de resultados")]
    public int TotalResults { get; set; }
}

// ? MALO - Tipo genérico
public object SearchProducts(string query)
{
    return new { products = new List<object>(), total = 0 };
}

4. Organizar Tools por Dominio

// Separar tools por funcionalidad
public class WeatherTools { /* ... */ }
public class FinanceTools { /* ... */ }
public class UserManagementTools { /* ... */ }
public class NotificationTools { /* ... */ }

// Registrar de forma organizada
registry.AddToolsFrom<WeatherTools>();
registry.AddToolsFrom<FinanceTools>();
registry.AddToolsFrom<UserManagementTools>();
registry.AddToolsFrom<NotificationTools>();

?? Notas Adicionales

  • Todos los ejemplos son conceptuales y requieren implementación real para producción
  • Los ejemplos de integración con OpenAI son pseudo-código ilustrativo
  • Para casos reales, considera async/await, validaciones robustas y error handling

Para más información, consulta: