¿Por qué no uso LangChain (y lo que hago en su lugar) (Español (Spanish))

¿Por qué no uso LangChain (y lo que hago en su lugar)

Thursday, 18 December 2025

//

15 minute read

Soy desarrollador de .NET. Cuando empecé a construir sistemas alimentados por LLM, todos me apuntaron hacia LangChain. "Es el estándar", dijeron. "Todos los ejemplos lo usan". Y tenían razón - si estás en el ecosistema de Python, LangChain está en todas partes.

Pero esto es lo que pasa: no evito a LangChain porque es malo. Lo evito porque resuelve problemas que ya resolvo más explícitamente, y para mis casos de uso - C#, inferencia local, privacidad, determinismo - los marcos añaden fricción en lugar de valor.

Este no es un post anti-LangChain. Es un post sobre entender qué marcos de problemas resuelven, y darse cuenta de que es posible que no los necesite.

Tesis: Si entiendes los problemas que LangChain resuelve, no necesitas LangChain.

Lo que LangChain realmente hace bien

Seamos justos primero. LangChain sobresale en varias cosas:

Prototipado rápido Los ejemplos de cómo empezar son realmente buenos.

Integración del ecosistema de Python - Si ya estás en el mundo Python/Jupyter/pandas, LangChain pega todo sin problemas.

Bajar la barrera - Para las personas nuevas en LLMs, proporciona abstracciones útiles: plantillas de prompt, patrones de llamada de herramientas, gestión de memoria, integraciones vectoriales de DB.

LangChain es un acelerador de integración, no un requisito de IA. Acelera el camino de "Tengo una idea" a "Tengo una demostración". Eso es valioso.

Pero también es donde los problemas comienzan para mí como un desarrollador de C# construyendo sistemas de producción.

Los problemas que resuelve LangChain

Antes de descartar un marco, usted necesita entender qué problemas está resolviendo. LangChain aborda estos problemas reales:

  1. Construcción del contexto - Creación de avisos coherentes a partir de esquemas, muestras, historia y limitaciones
  2. Orquestración de herramientas - Gestión de múltiples llamadas de herramientas en secuencia con lógica condicional
  3. Gestión estatal - Mantener el contexto de conversación a través de múltiples turnos
  4. Reintentar y manejar errores - Recuperando con gracia cuando el LLM genera una salida inválida
  5. Razonamiento en varias etapas - Rompiendo tareas complejas en pasos secuenciales (el patrón "agente")
  6. Observabilidad - Seguimiento de lo que realmente sucedió durante la ejecución

Se trata de problemas legítimos. La pregunta es: ¿necesita un marco para resolverlos?

Donde LangChain empieza a doler

Para mi trabajo - construcción de sistemas .NET con LLM locales, estrictos requisitos de privacidad y comportamiento determinista - LangChain introduce fricción en varias áreas.

Estado oculto y flujo de control implícito

LangChain gestiona la memoria y el contexto para usted. Eso suena conveniente hasta que necesite depurar por qué su prompt es 10.000 tokens más de lo esperado, o por qué el LLM de repente tiene acceso al historial de conversación que pensó que había despejado.

El framework concatena, gestiona la memoria y maneja el orden de ejecución implícitamente. Cuando algo se rompe, estás depurando el comportamiento del framework, no el comportamiento de tu código.

Pensamiento acotado por el marco

Una vez que adoptes LangChain, empiezas a diseñar por LangChain. Su arquitectura se acopla a las abstracciones del marco: cadenas, agentes, retrievers, buffers de memoria.

Esto no es exclusivo de LangChain - todos los marcos hacen esto. Pero en un campo de movimiento rápido como LLMs, donde las abstracciones correctas todavía no están establecidas, el acoplamiento a la visión del mundo de un marco es arriesgado.

El Mismatch de Impedancia de Python

LangChain asume:

  • Procesos de larga duración (flujos de trabajo al estilo de libro de notas)
  • Estado mundial mutante
  • Mecanografía dinámica de Python y mecanografía de pato
  • Bloqueo de patrones de E/S

Como desarrollador de .NET, asumo:

  • Vida útil a la vista de las solicitudes (patrones básicos de ASP.NET)
  • Estado inmutable o gestionado explícitamente
  • Fuerte seguridad de mecanografía y compilación de tiempo
  • Async/esperar en todas partes

Los LangChain .NET puertos existen, pero están jugando a ponerse al día con la versión de Python, y las abstracciones todavía se sienten extrañas a C# idiomático.

Gaps de la realidad de la producción

Cuando pasas del prototipo a la producción, necesitas:

  • Determinismo - La misma entrada debe producir un comportamiento predecible
  • Validación - Asegúrese de que la salida de la LLM es segura antes de ejecutarla
  • Caja de arena - Limite lo que el código generado realmente puede hacer
  • Control de costos - Seguimiento del uso del token e imposición de límites
  • Inferencia local - Ejecutar modelos fuera de línea sin dependencias de la nube

LangChain optimiza la velocidad de iteración, no el endurecimiento de la producción. Está bien para las demostraciones; es un problema para la producción.

Lo que yo construyo en su lugar

Aquí está el modelo mental que utilizo: Los LLM son motores de razonamiento, no motores de ejecución.

Los LLM hacen:

  • Interpretación - Comprensión de la intención del usuario del lenguaje natural
  • Planificación - Romper tareas complejas en pasos
  • Traducción - Convertir intent en formatos estructurados (SQL, JSON, llamadas a funciones)

Los LLMS NO:

  • Calcular agregados - Un total de 100.000 filas
  • Escanear conjuntos de datos - Búsqueda a través de archivos grandes
  • Estado propio - Mantener la memoria a largo plazo

El principio: Los motores computan.

Esta separación conduce todo lo que construyo.

Contexto explícito, no memoria mágica

En lugar de la memoria administrada por framework, construyo un contexto explícitamente por petición:

public class QueryContext
{
    public List<ColumnInfo> Schema { get; set; }
    public List<Dictionary<string, string>> SampleRows { get; set; }
    public List<ConversationTurn> History { get; set; }
    public string UserQuestion { get; set; }
}

Cada construcción rápida es visible. Sé exactamente lo que se está enviando a la LLM porque construí la cuerda yo mismo:

private string BuildPrompt(QueryContext context)
{
    var sb = new StringBuilder();
    sb.AppendLine("You are a SQL expert. Generate a query based on:");
    sb.AppendLine();
    
    // Schema
    sb.AppendLine("Schema:");
    foreach (var col in context.Schema)
        sb.AppendLine($"  - {col.Name}: {col.Type}");
    
    // History (if any)
    if (context.History.Any())
    {
        sb.AppendLine("\nPrevious conversation:");
        foreach (var turn in context.History.TakeLast(3))
            sb.AppendLine($"  Q: {turn.Question} → SQL: {turn.Sql}");
    }
    
    // Current question
    sb.AppendLine($"\nQuestion: {context.UserQuestion}");
    sb.AppendLine("Generate SQL (no explanation, just the query):");
    
    return sb.ToString();
}

No hay estado oculto, no hay concatenación mágica, sólo construcción explícita de cuerdas, cuando está mal, sé por qué.

Capas de ejecución deterministas

En lugar de dejar que el LLM ejecute cualquier cosa, lo uso para generar intención, a continuación, ejecutar esa intención a través de motores deterministas:

  • Motores SQL (DuckDB) - Para consultas de datos
  • Motores de búsqueda (Lucene, Postgres full-text) - Para la recuperación de documentos
  • Motores de regla - Para la lógica de negocios
  • Servicios de dominio - Para operaciones validadas

El LLM genera SQL. DuckDB lo ejecuta. El LLM nunca ve los datos:

// LLM generates intent
var sql = await GenerateSqlAsync(context);

// Validate before execution
var error = ValidateSql(connection, sql);
if (error != null)
{
    // Retry with error feedback
    sql = await GenerateSqlAsync(context, previousError: error);
}

// Execute in sandboxed engine
var results = ExecuteQuery(connection, sql);

Esto es más seguro, más rápido y desmontable. El LLM no puede correr accidentalmente. DROP TABLE porque valido el SQL primero. El LLM no puede filtrar datos porque nunca ve los datos - sólo el esquema.

Un ejemplo concreto: Análisis CSV sin marcos

Hace poco escribí sobre análisis de archivos CSV grandes con LLM locales. La arquitectura:

Pregunta de usuario → LLM → SQL → DuckDB → Resultados

El LLM recibe:

  • El esquema CSV (nombres y tipos de columna)
  • 3 filas de muestra (para entender el formato de los datos)
  • La pregunta del usuario

El LLM genera:

  • Una consulta de DuckDB SQL

El sistema entonces:

  • Valida el SQL usando EXPLAIN (atrapa errores de sintaxis sin ejecutar)
  • Ejecuta la consulta contra el archivo CSV
  • Devuelve los resultados al usuario

El LLM nunca ve los datos reales. Sólo ve la estructura.

Esto es lo que LangChain llamaría un "agente" - un sistema que utiliza un LLM para generar acciones, validarlas, ejecutarlas, y potencialmente reinicia en caso de fallo.

Excepto que lo construí en ~200 líneas de C# sin marco:

public class CsvQueryService
{
    private readonly OllamaApiClient _ollama;
    private readonly string _model;
    
    public async Task<QueryResult> QueryAsync(string csvPath, string question)
    {
        using var connection = new DuckDBConnection("DataSource=:memory:");
        connection.Open();
        
        // 1. Build context
        var context = BuildContext(connection, csvPath, question);
        
        // 2. Generate SQL
        var sql = await GenerateSqlAsync(context);
        
        // 3. Validate
        var error = ValidateSql(connection, sql);
        if (error != null)
        {
            // Retry once with error feedback
            sql = await GenerateSqlAsync(context, error);
        }
        
        // 4. Execute
        return ExecuteQuery(connection, sql);
    }
}

Eso es todo. Sin cadenas, sin marco de agentes, sin magia. Sólo orquestación explícita de LLM → validación → ejecución.

¿Qué es un agente, en serio?

El término "agente" se lanza constantemente, normalmente significa "cualquier cosa que involucre un LLM". Seamos precisos.

Un agente es:

  • Un bucle - Funciona con múltiples iteraciones.
  • Con estado - Recuerda lo que ha intentado.
  • Con herramientas - Puede tomar acciones en el mundo
  • Con retroalimentación - Observa los resultados y los ajustes

Un agente es no una bibliotecaEs un patrón.

Mi patrón de agente en C#:

public class Agent
{
    private readonly List<ConversationTurn> _history = new();
    
    public async Task<string> RunAsync(string goal)
    {
        while (!IsGoalAchieved(goal))
        {
            // 1. Generate next action based on history
            var action = await GenerateActionAsync(goal, _history);
            
            // 2. Validate before executing
            if (!IsActionSafe(action))
            {
                _history.Add(new ConversationTurn 
                { 
                    Action = action, 
                    Result = "REJECTED: Unsafe action" 
                });
                continue;
            }
            
            // 3. Execute through deterministic tool
            var result = await ExecuteActionAsync(action);
            
            // 4. Record and continue
            _history.Add(new ConversationTurn { Action = action, Result = result });
        }
        
        return GenerateSummary(_history);
    }
}

Esto es un agente. Es un bucle con estado, herramientas, y retroalimentación. Lo escribí en 30 líneas. No necesitaba un marco.

Donde encaja el Marco de Agentes de Microsoft

Para ser justos con el ecosistema .NET, Microsoft ha lanzado el Microsoft Agent Framework que está diseñado especialmente para los desarrolladores de .NET construyendo sistemas de producción de IA.

Lo que el Microsoft Agent Framework hace bien

El framework (anteriormente conocido como Microsoft.Extensions.AI) proporciona:

  • Orquestración explícita - Tú controlas el bucle de agentes, no el framework.
  • Mecanografía fuerte - Seguridad en el tiempo de compilación para definiciones de herramientas y llamadas de funciones
  • Observabilidad de primera clase - Telemetría incorporada, registro y rastreo distribuido a través de OpenTelemetry
  • Límites de las empresas - Diseñado para la producción de sistemas .NET con una adecuada DI, configuración y gestión del ciclo de vida
  • Soporte multimodelo - Abstracciones sobre OpenAI, Azure OpenAI, Ollama y otros proveedores
  • Integración del kernel semántico - Funciona con la pila de IA más amplia de Microsoft

Componentes clave:

  • IChatClient - Interfaz unificada para completar el chat
  • IEmbeddingGenerator - Incrustaciones vectoriales entre proveedores
  • AIFunction - Llamada de función segura
  • Gasoducto Middleware - Para la tala, reintento, almacenamiento en caché, telemetría

Ejemplo

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddChatClient(builder => 
    builder.UseOllama("llama3.2")
           .UseOpenTelemetry()
           .UseLogging());

var app = builder.Build();

app.MapPost("/chat", async (IChatClient client, string message) =>
{
    var response = await client.CompleteAsync(message);
    return response.Content;
});

Donde sigo estando a un nivel más bajo

Incluso con el marco de Microsoft, prefiero mantener la orquestación central explícita:

No quiero:

  • Planificadores opacos - El marco de decisión autónoma de qué herramienta llamar
  • Selección implícita de herramientas - Enrutamiento mágico basado en descripciones de lenguaje natural
  • Lógica de reintento oculta - Recuperación de errores gestionados por framework No puedo inspeccionar

Quiero:

  • Loops visibles - Veo cada iteración en mi código.
  • Pasos probables - Puedo probar la lógica de la decisión.
  • Componentes reemplazables - Puedo intercambiar el LLM, las herramientas, la capa de validación
  • Estado explícito - Sé exactamente lo que está en contexto.

Microsoft's Agent Framework está más cerca de cómo pienso que LangChain. Respeta los patrones .NET, utiliza la inyección de dependencia correctamente, y no lucha contra el ecosistema. Pero todavía prefiero escribir la orquestación yo mismo.

Cuándo usar el Microsoft Agent Framework:

  • Construyendo aplicaciones de chat con llamadas a funciones
  • Necesita apoyo multimodelo (conmutar entre OpenAI, Azure, Ollama)
  • ¿Quieres características empresariales (telemetría, registro, rastreo distribuido)
  • Trabajar en un equipo que prefiera la coherencia del marco
  • Construcción en la parte superior del kernel semántico

Cuándo ir framework-less:

  • Usted necesita control total sobre el bucle de agente
  • Estás construyendo patrones de razonamiento personalizados
  • Usted quiere cero abstracción de los gastos generales
  • Usted está optimizando para casos de uso específicos (como análisis CSV o raspado web)
  • Quieres entender exactamente cómo funciona.

El marco no elimina las decisiones arquitectónicas. Usted todavía elige qué poner en contexto, cómo trocear los datos, y cuándo volver a intentarlo. Simplemente hace la plomería más fácil.

Por qué esto escala mejor a largo plazo

Los sistemas sin marco envejecen mejor por varias razones:

Desempeño Mi servicio de consulta CSV ejecuta sub-100ms porque no hay marco entre el LLM y DuckDB.

Predecibilidad de los costos - Controlo exactamente lo que va a la LLM. No hay inflación rápida oculta de la memoria administrada por framework.

Defugabilidad - Cuando algo se rompe, estoy depurando mi código, no la ingeniería inversa de la magia de un marco.

Privacidad - Para sistemas con estrictos requisitos de residencia de datos, saber exactamente lo que deja la máquina importa.

Escenarios fuera de línea - Dispositivos de borde, redes aerotransportadas, entornos regulados. Los marcos asumen acceso a Internet y servicios en la nube.

Cumplimiento de la normativa - En finanzas, salud y gobierno, a menudo necesitas explicar y auditar cada decisión. "El marco lo hizo" no es una respuesta aceptable.

Cuanto más limitado sea su entorno, más desea un control explícito.

Cuando usaría LangChain

Para desarmar a los críticos: hay casos legítimos en los que buscaría a LangChain.

Hackathons - La velocidad de demostración importa más que la arquitectura.

POC de desecho - Si estás validando una idea y planeas reescribir para la producción de todos modos.

Equipos Python-pesados - Si su equipo ya tiene fluidez en Python, el ajuste del ecosistema es fuerte.

Conceptos de enseñanza - Las abstracciones de LangChain pueden ayudar a los principiantes a entender el patrón de agente antes de construir el suyo propio.

Saber cuándo no usar algo es tan valioso como saber cuándo usarlo.

El modelo más amplio: marcos vs. primeros principios

Esto no se trata realmente de LangChain, sino de la compensación entre los marcos y la ingeniería de los primeros principios.

Los frameworks aceleran los problemas familiares. Si está construyendo la 100a API CRUD, alcance a Entity Framework o Dapper. Los patrones están arreglados.

No sabemos si las "cadenas" o "agentes" o "recompensas" son los modelos mentales correctos.

En ese ambiente, prefiero construir cerca del metal:

  • LLMs a través de llamadas directas de API (OllamaSharp, OpenAI SDK)
  • Construcción rápida mediante construcción explícita de cuerdas
  • Validación mediante lógica específica del dominio
  • Ejecución a través de motores diseñados específicamente (SQL, búsqueda, etc.)

Como desarrollador de .NET, tengo fuertes opiniones sobre cómo se deben construir los sistemas: vidas explícitas, mecanografía fuerte, async todo el camino hacia abajo, inyección de dependencia para la prueba.

Las abstracciones de LangChain no hacen un mapa limpio de esas opiniones, así que no las uso.

La comida para llevar

Si usted es un desarrollador de .NET mirando a LangChain y preguntándose "¿Necesito esto?", aquí está mi respuesta:

Tienes que resolver los problemas que LangChain resuelve - gestión de contextos, orquestación de herramientas, lógica de reintento, observabilidad.

No necesitas a LangChain para resolverlos. - Especialmente si valoras la explicitación, la escritura fuerte, y el endurecimiento de la producción sobre el prototipado rápido.

El principio sobre el que me baso:

"La razón de los LLMs, los motores computan, la orquestación es tuya para poseer".

O más simplemente:

"Si entiendes los problemas que resuelve un marco, a menudo no necesitas el marco".

Construye sistemas que tengan sentido en tu ecosistema, con tus limitaciones, usando las expresiones de tu idioma. Para mí, eso es C#, fuerte mecanografía, flujo de control explícito y capas de ejecución determinista.

Para ti, podría ser diferente y está bien.

El objetivo no es evitar los marcos, sino elegirlos conscientemente, entendiendo tanto lo que proporcionan como lo que cuestan.


Más información:

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.