Introducción al kernel semántico: orquestación de IA en C#
Si ha estado creando aplicaciones .NET y observando la evolución del panorama de la IA, probablemente se haya preguntado: ¿cuál es la mejor manera de integrar grandes modelos de lenguaje en mis proyectos de C# sin convertir mi código base en espaguetis? Ese es exactamente el problema que resuelve el kernel semántico de Microsoft, y después de pasar el último año creando aplicaciones de producción con él, puedo decirles que se ha convertido en una de las herramientas más importantes de mi conjunto de herramientas de desarrollador.
En esta publicación, lo guiaré a través de todo lo que necesita para comenzar con Semantic Kernel, desde comprender los conceptos básicos hasta crear un asistente de IA del mundo real. Ya sea que esté inmerso en el desarrollo de IA o esté buscando una forma estructurada de organizar llamadas de LLM en sus aplicaciones .NET existentes, esta guía lo tiene cubierto.
¿Qué es el kernel semántico?
Semantic Kernel (SK) es un SDK de código abierto de Microsoft que actúa como una capa de orquestación entre el código de su aplicación y modelos de lenguaje grandes como GPT-4o, Azure OpenAI u otros servicios de IA. Piense en ello como un middleware liviano que le permite combinar código C# tradicional con capacidades de IA de una manera limpia y componible.
Pero ¿por qué no llamar directamente a la API de OpenAI? Es absolutamente posible, y para casos de uso sencillos, está bien. Pero en el momento necesitas:
- Deje que la IA decida qué funciones llamar según la entrada del usuario
- Combine múltiples llamadas de IA con código tradicional en una canalización
- Añade memoria y contexto para que la IA recuerde interacciones anteriores.
- Cree agentes de varios pasos que razonan a través de tareas complejas
…te encontrarás reinventando la rueda. Semantic Kernel le ofrece todo esto listo para usar, con soporte .NET de primera clase, integración de inyección de dependencias y una arquitectura de complementos que resulta natural para cualquier desarrollador de C#.
El proyecto se encuentra en GitHub en el repositorio microsoft/semantic-kernel y tiene SDK para C#, Python y Java. El SDK de C# es el más maduro y en el que nos centraremos aquí.
Conceptos básicos
Antes de escribir cualquier código, comprendamos los componentes básicos.
El núcleo
El Kernel es el objeto central en Semantic Kernel. Es el orquestador, lo que une sus servicios, complementos y configuración de IA. Usted crea uno, registra sus servicios y complementos en él y luego lo utiliza para ejecutar mensajes o invocar funciones. Si está familiarizado con la inyección de dependencias en ASP.NET Core, el Kernel le resultará muy familiar: es esencialmente un contenedor de servicios con superpoderes de IA.
Complementos y funciones
Un complemento es una colección de funciones relacionadas que el kernel puede invocar. Las funciones vienen en dos tipos:
- Funciones de aviso: definidas como plantillas de lenguaje natural que se envían al LLM
- Funciones nativas: métodos normales de C# decorados con atributos que el kernel puede descubrir y llamar
Por ejemplo, podría tener un WeatherPlugin con una función nativa GetCurrentWeather(string city) y una función de aviso que resume los datos meteorológicos de una manera amigable.### Conectores de IA
Los conectores son la forma en que Semantic Kernel se comunica con los servicios de IA. Los más comunes son:
AzureOpenAIChatCompletion— para el servicio Azure OpenAIOpenAIChatCompletion— para la API de OpenAI directamente- Incorporación de conectores para búsqueda de vectores y memoria.
Los registra en el Kernel al inicio y todo lo demás simplemente funciona.
Configurando tu proyecto
Ensuciémonos las manos. Comience creando una nueva aplicación de consola:
dotnet new console -n SemanticKernelDemo
cd SemanticKernelDemo
Ahora agregue los paquetes Semantic Kernel NuGet:
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Connectors.AzureOpenAI
Si usa OpenAI directamente en lugar de Azure OpenAI:
dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
Para compatibilidad con memoria e incrustaciones (usaremos esto más adelante):
dotnet add package Microsoft.SemanticKernel.Plugins.Memory
dotnet add package Microsoft.Extensions.VectorData.Abstractions
Su .csproj debe apuntar a .NET 8 o posterior. Las últimas versiones de Semantic Kernel aprovechan al máximo las funciones modernas de .NET.
Tu primer núcleo
Comencemos con el ejemplo más simple posible: crear un kernel, conectarlo a un servicio de inteligencia artificial y hacerle una pregunta.
using Microsoft.SemanticKernel;
// Build the kernel with Azure OpenAI
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
var kernel = builder.Build();
// Invoke a simple prompt
var result = await kernel.InvokePromptAsync(
"Explain dependency injection in C# in three sentences."
);
Console.WriteLine(result);
Si está utilizando OpenAI directamente, intercambie el registro del servicio:
builder.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: "your-openai-api-key"
);
Eso es todo. Ejecútelo y obtendrá una explicación concisa sobre la inyección de dependencia. Pero esto es sólo una muestra de lo que ocurre.
Uso de plantillas de mensajes
Las plantillas de mensajes le permiten parametrizar sus mensajes con variables usando una sintaxis estilo Manillar:
var prompt = """
You are a technical writer. Write a brief summary of {{$topic}}
aimed at developers with {{$experienceLevel}} experience.
Keep it under 200 words.
""";
var function = kernel.CreateFunctionFromPrompt(prompt);
var arguments = new KernelArguments
{
["topic"] = "gRPC in .NET",
["experienceLevel"] = "intermediate"
};
var result = await kernel.InvokeAsync(function, arguments);
Console.WriteLine(result);
Aquí es donde Semantic Kernel comienza a brillar: puede definir plantillas de mensajes reutilizables, versionarlas y componerlas en flujos de trabajo más grandes.
Complementos y funciones nativas
Los complementos son donde Semantic Kernel cierra la brecha entre la IA y su código C# existente. Una función nativa es simplemente un método normal que se expone al Kernel.
using Microsoft.SemanticKernel;
using System.ComponentModel;
public class TimePlugin
{
[KernelFunction("get_current_time")]
[Description("Gets the current date and time in UTC")]
public string GetCurrentTime()
{
return DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC");
}
[KernelFunction("get_time_in_timezone")]
[Description("Gets the current time in a specific timezone")]
public string GetTimeInTimezone(
[Description("The IANA timezone identifier, e.g. 'America/New_York'")] string timezone)
{
var tz = TimeZoneInfo.FindSystemTimeZoneById(timezone);
var time = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
return time.ToString("yyyy-MM-dd HH:mm:ss");
}
}
Observe los atributos [KernelFunction] y [Description]. Estos son fundamentales: las descripciones son lo que la IA lee para comprender cuándo y cómo llamar a sus funciones. Las buenas descripciones marcan la diferencia entre una IA que utiliza sus herramientas de forma eficaz y una que está confusa.
Registre el complemento en su kernel:
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
builder.Plugins.AddFromType<TimePlugin>();
var kernel = builder.Build();
También puede crear complementos más complejos que inyecten servicios. Dado que Semantic Kernel se integra con Microsoft.Extensions.DependencyInjection, sus complementos pueden recibir dependencias del constructor como cualquier otro servicio:
public class OrderPlugin
{
private readonly IOrderRepository _repository;
public OrderPlugin(IOrderRepository repository)
{
_repository = repository;
}
[KernelFunction("get_order_status")]
[Description("Retrieves the status of an order by its ID")]
public async Task<string> GetOrderStatus(
[Description("The order ID to look up")] string orderId)
{
var order = await _repository.GetByIdAsync(orderId);
return order is null
? $"No order found with ID {orderId}"
: $"Order {orderId}: {order.Status}, placed on {order.CreatedAt:d}";
}
}
Llamada de funciones y autoinvocación
Aquí es donde las cosas se ponen realmente interesantes. Con la llamada a función (también conocida como llamada a herramienta), permite que el modelo de IA decida a cuál de sus funciones registradas llamar según el contexto de la conversación. El modelo no ejecuta código: devuelve una solicitud estructurada que dice “Quiero llamar a la función X con estos argumentos” y el Kernel maneja la invocación real.
A continuación se explica cómo habilitar la llamada de función automática:
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
builder.Plugins.AddFromType<TimePlugin>();
builder.Plugins.AddFromType<WeatherPlugin>();
var kernel = builder.Build();
// Enable automatic function calling
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
var result = await kernel.InvokePromptAsync(
"What time is it in Tokyo and what's the weather like there?",
new KernelArguments(settings)
);
Console.WriteLine(result);
Con FunctionChoiceBehavior.Auto(), el Kernel:
- Envíe su mensaje a la IA junto con descripciones de todas las funciones disponibles.
- La IA decide que necesita llamar a
get_time_in_timezoneyget_weather - El Kernel ejecuta automáticamente esas funciones.
- Los resultados se envían a la IA.
- La IA compone una respuesta en lenguaje natural utilizando los resultados de la función.Este bucle puede ocurrir varias veces en una sola invocación: la IA puede llamar a varias funciones en secuencia para recopilar toda la información que necesita. También puedes usar
FunctionChoiceBehavior.Required()para obligar a la IA a llamar al menos a una función, o proporcionar una lista específica de funciones que puede usar.
Finalización del chat con historial
Para aplicaciones conversacionales, querrás usar ChatCompletionService directamente con un objeto ChatHistory:
using Microsoft.SemanticKernel.ChatCompletion;
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("""
You are a helpful developer assistant. You have access to tools
for checking the time and weather. Be concise and friendly.
""");
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) break;
history.AddUserMessage(input);
var response = await chatService.GetChatMessageContentAsync(
history,
executionSettings: settings,
kernel: kernel
);
history.AddAssistantMessage(response.Content ?? "");
Console.WriteLine($"Assistant: {response.Content}");
}
Esto le brinda un chatbot completamente interactivo que mantiene el historial de conversaciones y puede llamar a sus complementos según sea necesario.
Memoria e incrustaciones
Uno de los patrones más poderosos en las aplicaciones de IA es la Generación aumentada de recuperación (RAG), que brinda a la IA acceso a sus propios datos incorporándolos en el espacio vectorial y recuperando fragmentos relevantes en el momento de la consulta.
Semantic Kernel proporciona abstracciones para trabajar con incrustaciones y almacenes de vectores. A continuación se explica cómo configurar un almacén de vectores en memoria para el desarrollo:
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Microsoft.SemanticKernel.Embeddings;
#pragma warning disable SKEXP0010
// Create an embedding generation service
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAITextEmbeddingGeneration(
deploymentName: "text-embedding-ada-002",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
var kernel = builder.Build();
var embeddingService = kernel.GetRequiredService<ITextEmbeddingGenerationService>();
// Generate embeddings for your documents
var documents = new[]
{
"Semantic Kernel is an open-source SDK for AI orchestration.",
"Azure OpenAI provides enterprise-grade AI models.",
"Plugins in SK allow you to expose C# methods to AI models."
};
var embeddings = await embeddingService.GenerateEmbeddingsAsync(documents);
Para escenarios de producción, almacenaría estas incorporaciones en una base de datos vectorial dedicada como Azure AI Search, Qdrant o Pinecone. Semantic Kernel tiene conectores para todos estos a través de las abstracciones Microsoft.Extensions.VectorData.
Un flujo RAG típico se ve así:
- Ingesta: fragmenta tus documentos, genera incrustaciones y guárdalos en una base de datos vectorial.
- Recuperar: cuando un usuario hace una pregunta, incrusta la consulta y busca los documentos más similares
- Generar: pasar los documentos recuperados como contexto al LLM junto con la pregunta del usuario.
[KernelFunction("search_knowledge_base")]
[Description("Searches the internal knowledge base for relevant information")]
public async Task<string> SearchKnowledgeBase(
[Description("The search query")] string query)
{
var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query);
var results = await _vectorStore.SearchAsync(queryEmbedding, limit: 3);
return string.Join("\n\n", results.Select(r => r.Text));
}
Al exponer su canal RAG como una función del núcleo, la IA puede decidir automáticamente cuándo necesita buscar en su base de conocimientos, manteniendo limpia la orquestación y dejando que el modelo haga lo que mejor sabe hacer.
Planificadores y agentes
A medida que sus aplicaciones de IA se vuelven más complejas, necesitará la IA para planificar y ejecutar tareas de varios pasos. Aquí es donde entra en juego el marco del agente de Semantic Kernel.
Conceptos básicos: agente de finalización de chat
El tipo de agente más simple incluye un modelo de finalización de chat con instrucciones y complementos:
using Microsoft.SemanticKernel.Agents;
#pragma warning disable SKEXP0110
var agent = new ChatCompletionAgent
{
Name = "DevAssistant",
Instructions = """
You are a senior .NET developer assistant. Help users with code
reviews, architecture decisions, and debugging. Always provide
code examples when relevant. Use your available tools to look up
current information when needed.
""",
Kernel = kernel,
Arguments = new KernelArguments(settings)
};
var history = new ChatHistory();
history.AddUserMessage("How should I structure a clean architecture project in .NET 8?");
await foreach (var message in agent.InvokeAsync(history))
{
Console.WriteLine(message.Content);
history.Add(message);
}
Colaboración entre múltiples agentes
Cuando las cosas se vuelven realmente poderosas es cuando tienes varios agentes trabajando juntos. Semantic Kernel admite patrones de chat grupal donde colaboran agentes con diferentes especializaciones:
#pragma warning disable SKEXP0110
var codeReviewer = new ChatCompletionAgent
{
Name = "CodeReviewer",
Instructions = """
You review C# code for bugs, performance issues, and best practices.
Be specific about what you find and suggest concrete fixes.
""",
Kernel = kernel
};
var securityAuditor = new ChatCompletionAgent
{
Name = "SecurityAuditor",
Instructions = """
You focus exclusively on security vulnerabilities in code.
Look for injection attacks, authentication issues, data exposure,
and OWASP Top 10 violations.
""",
Kernel = kernel
};
var groupChat = new AgentGroupChat(codeReviewer, securityAuditor)
{
ExecutionSettings = new AgentGroupChatSettings
{
TerminationStrategy = new MaximumIterationTerminationStrategy(4)
}
};
groupChat.AddChatMessage(
new ChatMessageContent(AuthorRole.User, "Review this code: ...")
);
await foreach (var message in groupChat.InvokeAsync())
{
Console.WriteLine($"[{message.AuthorName}]: {message.Content}");
}
Este patrón es increíblemente útil para tareas complejas en las que deben influir diferentes perspectivas o áreas de experiencia. Cada agente opera con su propio indicador del sistema y puede tener su propio conjunto de complementos.
Ejemplo del mundo real: creación de un asistente de documentación de proyectos
Unamos todo con un ejemplo práctico: un asistente de IA que ayuda a los desarrolladores a comprender una base de código leyendo archivos y respondiendo preguntas.
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.ComponentModel;
// Define our plugins
public class FileSystemPlugin
{
private readonly string _rootPath;
public FileSystemPlugin(string rootPath)
{
_rootPath = rootPath;
}
[KernelFunction("read_file")]
[Description("Reads the contents of a file from the project directory")]
public async Task<string> ReadFile(
[Description("Relative path to the file")] string path)
{
var fullPath = Path.Combine(_rootPath, path);
if (!File.Exists(fullPath))
return $"File not found: {path}";
var content = await File.ReadAllTextAsync(fullPath);
// Truncate very large files
if (content.Length > 8000)
content = content[..8000] + "\n... [truncated]";
return content;
}
[KernelFunction("list_files")]
[Description("Lists files in a directory, optionally filtered by extension")]
public string ListFiles(
[Description("Relative directory path")] string directory,
[Description("File extension filter like '.cs' or '.json'")] string? extension = null)
{
var fullPath = Path.Combine(_rootPath, directory);
if (!Directory.Exists(fullPath))
return $"Directory not found: {directory}";
var files = Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories)
.Select(f => Path.GetRelativePath(_rootPath, f))
.Where(f => extension is null || f.EndsWith(extension))
.Take(50);
return string.Join("\n", files);
}
}
public class DocumentationPlugin
{
[KernelFunction("generate_summary")]
[Description("Generates a structured markdown summary for documentation")]
public string GenerateSummaryTemplate(
[Description("Name of the component")] string componentName,
[Description("Brief description")] string description,
[Description("Key responsibilities as comma-separated values")] string responsibilities)
{
var items = responsibilities.Split(',', StringSplitOptions.TrimEntries);
var bullets = string.Join("\n", items.Select(r => $"- {r}"));
return $"""
## {componentName}
{description}
### Responsibilities
{bullets}
---
*Generated documentation — review and expand as needed.*
""";
}
}
// Wire it all up
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
builder.Plugins.AddFromObject(new FileSystemPlugin("./src"));
builder.Plugins.AddFromType<DocumentationPlugin>();
var kernel = builder.Build();
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("""
You are a codebase documentation assistant. You help developers understand
projects by reading source files and explaining architecture, patterns,
and design decisions.
When asked about code, use your tools to read the actual files rather
than guessing. Be specific and reference actual code when possible.
Generate documentation artifacts when asked.
""");
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
Console.WriteLine("Documentation Assistant ready. Ask me about your codebase!");
Console.WriteLine("Type 'exit' to quit.\n");
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
break;
history.AddUserMessage(input);
var response = await chatService.GetChatMessageContentAsync(
history,
executionSettings: settings,
kernel: kernel
);
Console.WriteLine($"\nAssistant: {response.Content}\n");
history.AddAssistantMessage(response.Content ?? "");
}
Este asistente puede:- Listar y leer archivos desde el directorio de su proyecto
- Responder preguntas sobre el código base leyendo archivos fuente reales
- Generar documentación en formato Markdown
- Mantener el contexto de la conversación para que las preguntas de seguimiento funcionen de forma natural
La IA decide automáticamente cuándo llamar a read_file, list_files o generate_summary según lo que le pidas. Pregúntele “¿Qué hace OrderService?” y leerá el archivo, lo analizará y lo explicará. Pídale que “Genere documentación para el módulo de autenticación” y explorará los archivos, comprenderá la estructura y producirá un resumen formateado.
Consejos para la producción
Antes de enviar su aplicación Semantic Kernel, he aprendido algunas cosas por las malas:
Utilice la inyección de dependencias correctamente. En las aplicaciones ASP.NET Core, registre el kernel y los servicios en su contenedor DI en lugar de crearlos en línea:
builder.Services.AddKernel()
.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: configuration["AzureOpenAI:Endpoint"]!,
apiKey: configuration["AzureOpenAI:ApiKey"]!
)
.Plugins.AddFromType<TimePlugin>()
.AddFromType<OrderPlugin>();
Maneje los errores con elegancia. Las llamadas de LLM pueden fallar, expirar o devolver resultados inesperados. Envuelva sus invocaciones en bloques try-catch e implemente políticas de reintento con Polly o las funciones de resiliencia integradas.
Supervise el uso de tokens. Cada mensaje, cada descripción de función y cada parte del historial de chat consume tokens. Utilice filtros para registrar y realizar un seguimiento del uso:
kernel.FunctionInvocationFilters.Add(new LoggingFilter());
Mantenga las descripciones de sus funciones precisas. Las descripciones vagas hacen que la IA llame a las funciones de manera incorrecta. Pruebe sus descripciones preguntando: “Si solo leo la descripción, ¿sabría exactamente cuándo y cómo usar esta función?”
Conclusión
Semantic Kernel es una de esas bibliotecas que cambia fundamentalmente la forma de pensar sobre la creación de aplicaciones. No es solo un contenedor de API: es un marco de orquestación que le permite componer capacidades de IA con código tradicional de una manera que sea mantenible, comprobable y lista para producción.
Lo que más me gusta de él es que respeta el ecosistema .NET. Utiliza patrones que ya conoce (inyección de dependencia, atributos, asíncrono/espera, interfaces) y los extiende al mundo de la IA. No es necesario aprender un paradigma completamente nuevo; simplemente agrega IA como otra capacidad en su conjunto de herramientas.
Si está creando aplicaciones .NET y aún no ha explorado el kernel semántico, ahora es el momento. El SDK es estable, la comunidad está activa y los patrones que permite (desde una simple orquestación rápida hasta la colaboración entre múltiples agentes) se están convirtiendo en habilidades esenciales para los desarrolladores modernos.
Empiece poco a poco. Cree un kernel, registre un complemento y observe cómo la IA llama a su código. Una vez que haga clic, comenzará a ver oportunidades para agregar inteligencia en todas partes de sus aplicaciones.
La documentación oficial y el repositorio de GitHub son recursos excelentes para continuar su viaje. ¡Feliz edificio!