This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Thursday, 18 December 2025
Olen .NET-kehittäjä. Kun aloin rakentaa LLM-käyttöisiä järjestelmiä, kaikki osoittivat minua kohti LangChainia. "Se on standardi", he sanoivat. "Kaikki esimerkit käyttävät sitä." Ja he olivat oikeassa - jos olet Python-ekosysteemissä, LangChain on kaikkialla.
Mutta asia on näin: En välttele LangChainia, koska se on huono asia. Vältän sitä, koska se ratkaisee ongelmia, jotka jo ratkaisen selkeämmin, ja käyttötapani osalta - C#, paikallinen päättely, yksityisyys, determinismi - kehykset tuovat enemmän kitkaa kuin arvoa.
Tämä ei ole mikään LangChain-vastainen viesti, vaan viesti siitä, miten ymmärretään, mitä ongelmia kehykset ratkaisevat, ja tajutaan, että niitä ei välttämättä tarvita.
Teesi: Jos ymmärrät LangChainin ratkaisemia ongelmia, et tarvitse LangChainia.
LangChain on hyvä monessa asiassa:
Nopea prototyypitys Työskentelydemarin voi tehdä minuuteissa. Alkuesimerkit ovat aidosti hyviä.
Python-ekosysteemin integraatio - Jos olet jo Python/Jupyter/panda-maailmassa, LangChain liimaa kaiken yhteen saumattomasti.
Esteen alentaminen - LLM:lle uusille ihmisille se tarjoaa käyttökelpoisia abstraktioita: nopeita malleja, työkalunkäyttömalleja, muistinhallintaa, vektorin DB-integraatioita.
LangChain on KotouttamiskiihdytinSe ei ole tekoälyn vaatimus, vaan nopeuttaa polkua "Minulla on idea" ja "Minulla on demo".
Mutta se on myös se, mistä ongelmat alkavat minulle C#-kehittäjänä, joka rakentaa tuotantojärjestelmiä.
Ennen kuin hylkäät kehyksen, sinun on ymmärrettävä, mitä ongelmia se ratkaisee. LangChain käsittelee näitä todellisia kysymyksiä:
Nämä ovat laillisia ongelmia, ja kysymys kuuluu: tarvitseeko niiden ratkaisemiseksi kehystä?
Työlleni - rakennustuotannolle .NET-järjestelmät paikallisilla LLM-järjestelmillä, tiukat yksityisyysvaatimukset ja deterministinen käyttäytyminen - LangChain tuo kitkaa useille alueille.
LangChain hoitaa muistia ja kontekstia sinulle. Se kuulostaa kätevältä, kunnes sinun täytyy selvittää, miksi pikavippisi on odotettua pidempi, tai miksi LLM:llä on yhtäkkiä pääsy keskusteluhistoriaan, jonka luulit selvinneesi.
Kehys konkretisoidaan, hallitaan muistia ja käsitellään täytäntöönpanomääräystä epäsuorasti. Kun jokin menee rikki, rikot kehyksen käyttäytymisen, et koodisi käytöstä.
Kun olet adoptoinut LangChainin, alat suunnitella LangChainille. Arkkitehtuuri yhdistetään kehyksen abstraktioihin: ketjuihin, agentteihin, noutajiin, muistipuskureihin.
Tämä ei ole vain LangChainille - kaikki puitteet tekevät näin, mutta LLM:n kaltaisella nopeasti liikkuvalla kentällä, jossa oikeat abstraktiot eivät ole vielä asettuneet paikoilleen, on riskialtista kytkeytyä kehyksen maailmankuvaan.
LangChain olettaa:
.NET-kehittäjänä oletan:
Erytropoietiini LangChain .NET-portit On olemassa, mutta he pelaavat Python-version kanssa, ja abstraktiot tuntuvat yhä vieraalta idiomaattiselle C#:lle.
Kun siirryt prototyypistä tuotantoon, tarvitset:
LangChain optimoi iterointinopeuden, ei tuotannon kovettumisen. Se sopii demoille, se on ongelma tuotannolle.
Tässä on mielimalli, jota käytän: LLM:t ovat päättelymoottoreita, eivät toteutusmoottoreita.
Periaate: LLM:n järki, moottorit laskevat.
Tämä asumusero vie kaiken, mitä rakennan.
Kehysmuistin sijaan rakennan kontekstin erikseen pyydettäessä:
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; }
}
Jokainen nopea rakentaminen on näkyvissä. Tiedän tarkalleen, mitä LLM:ään lähetetään, koska rakensin narun itse:
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();
}
Ei salattua tilaa, ei maagista konkretisaatiota, vain selkeä merkkijonorakennus, kun se on väärin, tiedän miksi.
Sen sijaan, että antaisin LLM:n tehdä mitä tahansa, käytän sitä luodakseni aiesopimus, sitten toteuttaa tämä aikomus deterministisillä moottoreilla:
LLM tuottaa SQL. DuckDB suorittaa sen. LLM ei koskaan näe dataa:
// 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);
Tämä on turvallisempaa, nopeampaa ja luotettavampaa. LLM ei voi vahingossa juosta DROP TABLE Koska vahvistan ensin SQL:n. LLM ei voi vuotaa dataa, koska se ei koskaan näe dataa - vain skeemaa.
Kirjoitin äskettäin aiheesta suurten CSV-tiedostojen analysointi paikallisilla LLM-tunnuksillaArkkitehtuuri:
Käyttäjäkysymys → LLM → SQL → DuckDB → Tulokset
LLM saa seuraavat tiedot:
LLM tuottaa:
Tämän jälkeen järjestelmä:
EXPLAIN (saalis syntaksivirheitä tekemättä)LLM ei koskaan näe todellista dataa, vaan vain rakenteen.
LangChain kutsuisi tätä "agentiksi" - järjestelmäksi, joka käyttää LLM:tä toimien tuottamiseen, validoi niitä, toteuttaa ne ja mahdollisesti kostautuu epäonnistumiselle.
Paitsi että rakensin sen 200 riviin C#:ta ilman kehystä:
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);
}
}
Siinä kaikki. Ei ketjuja, ei agentteja, ei magiaa. Vain LLM:n selkeä orkestrointi → varmentaminen → teloitus.
Termiä "agentti" heitellään jatkuvasti, yleensä tarkoittaen "mitä tahansa, mikä liittyy LLM:ään". Tarkastetaan tarkemmin.
Agentti on:
Agentilla on ei kirjastoaSe on kaava.
Agenttikuvioni 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);
}
}
Tämä on agentti. Se on silmukka, jossa on tilaa, työkaluja ja palautetta. Kirjoitin sen 30 rivillä. En tarvinnut kehystä.
Ollakseen reilu .NET-ekosysteemille Microsoft on julkaissut Microsoft Agent Framework Se on suunniteltu .NET-kehittäjille, jotka rakentavat tekoälyjärjestelmiä.
Kehyksessä (aiemmin Microsoft.Extensions.AI) määrätään seuraavaa:
Avainosat:
IChatClient - Yhdistetty rajapinta chatin loppuunsaattamiselleIEmbeddingGenerator - Vektori upottaa eri palveluntarjoajiinAIFunction - Tyyppiturvallinen toimintokutsuEsimerkki:
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;
});
Microsoftin kehyksistä huolimatta pidän mieluummin ydinorkesterin selkeänä:
En halua:
Haluan:
Microsoftin Agent Framework on lähempänä ajattelutapaani kuin LangChain. Se kunnioittaa .NET-malleja, käyttää riippuvuusruisketta oikein, eikä taistele ekosysteemiä vastaan, mutta kirjoitan mieluummin itse.
Milloin Microsoft Agent Frameworkia käytetään:
Milloin mennä ilman kehyksiä:
Kehys ei poista arkkitehtonisia päätöksiä, vaan päättää silti, mitä laittaa kontekstiin, miten kerätä dataa ja milloin yrittää uudelleen. Se vain helpottaa putkistoa.
Puitteettomat järjestelmät ikääntyvät paremmin monesta syystä:
Suorituskyky CSV-palvelullani on alle 100 ms, koska LLM:n ja DuckDB:n välillä ei ole kehystä.
Kustannusten ennakoitavuus Hallitsen tarkkaan, mitä LLM: lle tapahtuu. Ei piilotettua nopeaa inflaatiota kehysmuistista.
Debuggattavuus Kun jokin menee rikki, rikon koodini, en kääntele taikoja.
Yksityisyys - Järjestelmille, joilla on tiukat dataresidenssivaatimukset, tieto siitä, mikä koneen jättää, on tärkeää.
Offline-skenaariot - Edge-laitteet, ilmatiiviit verkot, säännellyt ympäristöt. Kehykset edellyttävät internet- ja pilvipalveluita.
Sääntelyn noudattaminen - Rahoituksessa, terveydenhuollossa ja hallituksessa on usein selitettävä ja tarkastettava jokainen päätös. "Puite teki sen" ei ole hyväksyttävä vastaus.
Mitä rajoittavampi ympäristösi on, sitä enemmän haluat selkeää hallintaa.
Kriitikoiden riisuminen aseista: LangChainin tavoittamisessa on laillisia tapauksia.
Hakatonit Speed to demo merkitsee muutakin kuin arkkitehtuuria.
Hylkäävät POC-keskukset - Jos hyväksyt idean ja suunnittelet uudelleenkirjoittamista tuotantoa varten joka tapauksessa.
Python-raskaat joukkueet - Jos tiimisi on jo sujuva Pythonissa, ekosysteemi sopii hyvin.
Opetuskäsitteet LangChainin abstraktiot voivat auttaa aloittelijoita ymmärtämään agenttikuviota ennen omiensa rakentamista.
Tietää, milloin ei On yhtä arvokasta käyttää jotain kuin tietää, milloin käyttää sitä.
Kyse ei oikeastaan ole LangChainista, vaan kehysten ja ensimmäisten periaatteiden suunnittelun välisestä vaihdosta.
Kehykset nopeuttavat tuttuja ongelmia. Jos rakennat 100. CRUD-rajapintaa, ota yhteyttä Entity Frameworkiin tai Dapperiin.
Mutta LLM-käyttöiset järjestelmät eivät ole vielä selvillä oikeista abstraktioista. Emme tiedä, ovatko "ketjut" tai "agentit" tai "palauttajat" oikeita mielenmalleja.
Siinä ympäristössä rakennan mieluummin lähelle metallia:
OllamaSharp, OpenAI SDK).NET-kehittäjänä minulla on vahvat mielipiteet siitä, miten järjestelmiä pitäisi rakentaa: selkeät elinajat, vahva kirjoittaminen, async koko matkan alas, riippuvuusruiske testausta varten.
LangChainin abstrakteja ei kartoiteta selkeästi noille mielipiteille, joten en käytä niitä.
Jos olet .net-kehittäjä ja mietit LangChainia: "Tarvitsenko tätä?", tässä on vastaukseni:
Sinun täytyy ratkaista LangChainin ratkaisemat ongelmat - kontekstin hallinta, työkalujen orkestrointi, uudelleenyrittäminen, havainnointi.
Et tarvitse LangChainia ratkaisemaan niitä - Varsinkin, jos arvostaa eksplisiittisyyttä, vahvaa kirjoittamista ja tuotannon kovettumista nopeassa prototyypityksessä.
Periaate, jonka pohjana olen, on:
"LMM-järki. Moottorit laskevat. Orkestraation voi omistaa."
Tai yksinkertaisesti:
"Jos ymmärtää ongelmat, jotka kehys ratkaisee, ei useinkaan tarvitse niitä."
Rakenna ekosysteemeissäsi järkeviä järjestelmiä, joilla on omat rajoitteensa ja jotka käyttävät kielesi idiomeja. Minulle se on C#, vahva kirjoittaminen, selkeä kontrollivirta ja deterministiset toteutuskerrokset.
Sinulle se voi olla erilaista, eikä se haittaa.
Tavoitteena ei ole vältellä kehyksiä, vaan valita ne tietoisesti ja ymmärtää, mitä ne tarjoavat ja mitä ne maksavat.
Lue lisää:
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.