Sono uno sviluppatore .NET. Quando ho iniziato a costruire sistemi LLM-powered, tutti mi hanno indicato verso LangChain. "E 'lo standard," hanno detto. "Tutti gli esempi lo usano." E avevano ragione - se siete nell'ecosistema Python, LangChain è ovunque.
Ma ecco il punto: non evito LangChain perché è brutto. Lo evito perché risolve i problemi che ho già risolto in modo più esplicito, e per i miei casi d'uso - C#, inferenza locale, privacy, determinismo - i framework aggiungono attrito piuttosto che valore.
Questo non è un post anti-LangChain. E 'un post sulla comprensione di quali framework problemi risolvere, e rendersi conto che non si potrebbe avere bisogno di loro.
Tesi: Se capisci i problemi che LangChain risolve, non hai bisogno di LangChain.
Prima di tutto siamo onesti. LangChain eccelle in diverse cose:
Prototipazione rapida - Si può avere una demo di lavoro in pochi minuti. Gli esempi di iniziare sono veramente buoni.
Integrazione dell'ecosistema Python - Se sei già nel mondo Python/Jupyter/pandas, LangChain colleziona tutto senza problemi.
Abbassare la barriera - Per le persone nuove a LLMs, fornisce astrazioni utili: template prompt, modelli di chiamata degli strumenti, gestione della memoria, integrazioni DB vettoriali.
LangChain è un acceleratore di integrazioneAccelera il percorso da "Ho un'idea" a "Ho una demo."
Ma è anche dove i problemi iniziano per me come un C# sviluppatore di sistemi di produzione di costruzione.
Prima di scartare un quadro, è necessario capire quali problemi sta risolvendo. LangChain affronta questi problemi reali:
Questi sono problemi legittimi. La domanda è: avete bisogno di un quadro per risolverli?
Per il mio lavoro - costruire sistemi .NET di produzione con LLM locali, rigorosi requisiti di privacy, e il comportamento deterministico - LangChain introduce attrito in diverse aree.
LangChain gestisce la memoria e il contesto per voi. Questo suona comodo fino a quando non è necessario debug perché il prompt è 10.000 gettoni più lungo del previsto, o perché la LLM improvvisamente ha accesso alla cronologia delle conversazioni che si pensava di aver eliminato.
Il framework concatena i suggerimenti, gestisce la memoria e gestisce implicitamente l'ordine di esecuzione. Quando qualcosa si rompe, si sta debug il comportamento del framework, non il comportamento del codice.
Una volta adottato LangChain, si inizia a progettare per LangChain. La tua architettura si accoppia alle astrazioni del framework: catene, agenti, retriever, buffer di memoria.
Questo non è unico per LangChain - tutti i framework lo fanno. Ma in un campo in rapida evoluzione come gli LLM, dove le astrazioni giuste non sono ancora sistemate, accoppiarsi alla visione del mondo di un framework è rischioso.
LangChain assume:
Come sviluppatore .NET, presumo:
La Porte .NET LangChain esistono, ma stanno giocando a recupero con la versione Python, e le astrazioni si sentono ancora estranei a C# idiomatico.
Quando si passa dal prototipo alla produzione, è necessario:
LangChain ottimizza la velocità di iterazione, non l'indurimento di produzione. Va bene per le demo; è un problema per la produzione.
Ecco il modello mentale che uso: I LLM sono motori di ragionamento, non motori di esecuzione.
Il principio: LLMs ragione. motori calcolare.
Questa separazione guida tutto quello che costruisco.
Invece della memoria gestita da framework, costruisco esplicitamente il contesto per richiesta:
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; }
}
Ogni costruzione rapida è visibile. So esattamente cosa viene inviato alla LLM perché ho costruito io stesso la corda:
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();
}
Niente stato nascosto, nessuna concatenazione magica, solo un'esplicita costruzione di stringhe, quando e' sbagliato, so perche'.
Invece di lasciare che l'LLM esegua qualsiasi cosa, la uso per generare intento, quindi eseguire tale intento attraverso motori deterministici:
LLM genera SQL. DuckDB lo esegue. LLM non vede mai i dati:
// 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);
Questo è più sicuro, veloce e debuggabile. L'LLM non può eseguire accidentalmente DROP TABLE perché convalido prima l'SQL. L'LLM non può trapelare dati perché non vede mai i dati - solo lo schema.
Recentemente ho scritto circa analisi di grandi file CSV con LLM locali. L'architettura:
Domanda dell'utente → LLM → SQL → DuckDB → Risultati
L'LLM riceve:
L'LLM genera:
Il sistema quindi:
EXPLAIN (errori di sintassi delle catture senza esecuzione)L'LLM non vede mai i dati reali. Vede solo la struttura.
Questo è ciò che LangChain definirebbe un "agente" - un sistema che utilizza un LLM per generare azioni, convalidarle, eseguirle, e potenzialmente riprovare sul fallimento.
Tranne che l'ho costruito in ~200 linee di C# senza framework:
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);
}
}
Niente catene, niente agenti framework, niente magia. Solo un'esplicita orchestrazione di LLM → convalida → esecuzione.
Il termine "agente" viene continuamente gettato in giro, di solito per significare "qualsiasi cosa che coinvolga un LLM." Cerchiamo di essere precisi.
Un agente è:
Un agente e' non è una libreriaE' uno schema.
Il mio modello di agente in 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);
}
}
Questo è un agente. È un ciclo con stato, strumenti e feedback. L'ho scritto in 30 righe. Non avevo bisogno di un quadro.
Per essere giusto per l'ecosistema .NET, Microsoft ha rilasciato il Microsoft Agent Framework che è costruito appositamente per gli sviluppatori .NET che costruiscono sistemi di produzione AI.
Il framework (precedentemente noto come Microsoft.Extensions.AI) fornisce:
Componenti chiave:
IChatClient - Interfaccia unificata per il completamento della chatIEmbeddingGenerator - Inserimento vettoriale tra i fornitoriAIFunction - Funzione Type-Safe chiamanteEsempio:
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;
});
Anche con il framework di Microsoft, preferisco mantenere l'orchestrazione di base esplicita:
Non voglio:
Voglio:
Microsoft Agent Framework è più vicino a come penso di LangChain. Rispetta i modelli .NET, utilizza l'iniezione di dipendenza correttamente, e non combatte l'ecosistema. Ma io stesso preferisco ancora scrivere l'orchestrazione.
Quando usare Microsoft Agent Framework:
Quando andare framework-less:
Il framework non elimina le decisioni architettoniche. Scegli ancora cosa mettere in contesto, come tagliare i dati, e quando riprovare. Rende solo l'impianto idraulico più facile.
I sistemi privi di quadro invecchiano meglio per diversi motivi:
Prestazioni Il mio servizio di query CSV funziona sotto i 100 metri perché non c'è un framework tra l'LLM e DuckDB.
Prevedibilità dei costi - Io controllo esattamente quello che va alla LLM. Nessuna inflazione improvvisa nascosta dalla memoria gestita da un framework.
Debugability Quando qualcosa si rompe, sto debugando il mio codice, non reverse-engineering la magia di un framework.
Privacy - Per sistemi con rigorosi requisiti di residenza dei dati, sapendo esattamente cosa rimane della macchina.
Scenari fuori rete - Dispositivi di bordo, reti pneumatiche, ambienti regolamentati. I framework assumono l'accesso a Internet e i servizi cloud.
Conformità normativa - In finanza, sanità e governo, spesso è necessario spiegare e controllare ogni decisione. "Il quadro è stato fatto" non è una risposta accettabile.
Più il vostro ambiente è limitato, più volete un controllo esplicito.
Per disarmare i critici: ci sono casi legittimi in cui io raggiungerei LangChain.
HackathonsCity name (optional, probably does not need a translation) - La velocita' verso la demo conta piu' dell'architettura.
POC di lancio - Se stai convalidando un'idea e hai comunque intenzione di riscrivere per la produzione.
Squadre Python-heavy - Se la tua squadra è già fluente in Python, l'ecosistema in forma è forte.
Concetti didattici - Le astrazioni di LangChain possono aiutare i principianti a capire il modello dell'agente prima di costruirne uno.
Sapere quando non usare qualcosa è prezioso quanto sapere quando usarlo.
Non si tratta di LangChain, si tratta del compromesso tra quadri e ingegneria dei primi principi.
I Framework accelerano i problemi familiari. Se stai costruendo la 100a API CRUD, raggiungi il Framework Entity o Dapper. I modelli sono regolati.
Ma i sistemi alimentati da LLM? Le astrazioni giuste non sono ancora sistemate. Non sappiamo se "catena" o "agente" o "retriever" siano i modelli mentali giusti. Stiamo ancora cercando di capirlo.
In quell'ambiente, preferisco costruire vicino al metallo:
OllamaSharp, OpenAI SDK)Come sviluppatore di .NET, ho forti opinioni su come i sistemi dovrebbero essere costruiti: vite esplicite, forte dattilografia, asincronia fino in fondo, iniezione di dipendenza per testabilità.
Le astrazioni di LangChain non corrispondono a quelle opinioni, quindi non le uso.
Se sei uno sviluppatore .NET guardando LangChain e chiedendo "Ho bisogno di questo?", ecco la mia risposta:
Devi risolvere i problemi che LangChain risolve - gestione del contesto, orchestrazione degli strumenti, riprova logica, osservabilità.
Non hai bisogno di LangChain per risolverli. - Specialmente se apprezzi l'esplicitezza, la forte dattilografia e l'indurimento della produzione sulla rapida prototipazione.
Il principio su cui mi baserò è:
"Motivazioni dell'LLM. I motori computano. L'orchestra è vostra."
O più semplicemente:
"Se capisci i problemi che un framework risolve, spesso non hai bisogno del framework."
Costruisci sistemi che abbiano senso nel tuo ecosistema, con i tuoi vincoli, usando gli idiomi del tuo linguaggio. Per me, questo è C#, forte dattilografia, flusso di controllo esplicito e livelli di esecuzione deterministici.
Per te potrebbe essere diverso.
L'obiettivo non è quello di evitare quadri. L'obiettivo è quello di sceglierli consapevolmente, capire sia quello che forniscono e quello che costano.
Ulteriore lettura:
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.