Back to "Чому я не використовую LangCain (і що я роблю замість цього)"

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

Agents AI Architecture C# LLM Systems Design

Чому я не використовую LangCain (і що я роблю замість цього)

Thursday, 18 December 2025

Коли я почав будувати системи LLM, всі вказували мені на LangChain. "Це стандарт," - сказали вони. "Всі приклади використовують його." і вони були праві - якщо ви знаходитесь в екосистемі Python, LangChain є скрізь.

Але ось у чому річ: я не уникаю Лангхейну, тому що це погано. Я уникаю його, тому що він вирішує проблеми, які я вже розв'язую більш явно, і для моїх випадків використання - C#, локальні висновки, конфіденційність, детермінізм - рамки додають тертя, а не цінності.

Це не повідомлення від LangCain, це допис про розуміння того, що проблеми вирішуються, і усвідомлення того, що вони можуть вам не знадобитися.

Тезис: якщо ви розумієте проблеми, розв' язані LangChain, вам не потрібен LangChain.

Що насправді робить Лангцин

Спочатку будьмо справедливими.

Швидке створення прототипів - Ви можете зробити демонстрацію за лічені хвилини. getting-початкові приклади дійсно хороші.

Інтеграція з екосистемою Python - Якщо ви вже у світі Python/Jupyter/pandas, LangCain робить все разом безперешкодно.

Як зменшити бар'єр - Для людей, нових до LLM, він надає корисні абстракції: запрошення шаблонів, інструмент виклику шаблонів, керування пам'яттю, вектор DB інтеграції.

LangChain є а прискорювач інтеграціїЦе пришвидшує шлях від "У мене є ідея" до "У мене є демо."

Але проблеми також починаються для мене як виробничі системи розробки C#A.

Проблеми, пов'язані з Лангшейном

Перш ніж звільнити систему, вам слід зрозуміти, які проблеми її розв' язання.

  1. Контекстна побудова - Побудова послідовних запитів зі схем, зразків, історії та обмежень
  2. Утиліта інструмента - Керування багатьма інструментними викликами у послідовності з умовною логікою
  3. Керування станами - Підтримування контексту спілкування через декілька ходів
  4. Повторна спроба і обробка помилок - Відновлення граціозно, якщо LLM створює некоректний вивід
  5. Обдумування з декількома кроками - Розбити складні завдання послідовними кроками (взірець "агент")
  6. Спостереження - Стеження за тим, що насправді сталося під час виконання

Це законні проблеми. Питання в тому, чи потрібна вам система для їх розв'язання?

Там, де починає боліти Лангцин

Для моєї робочої системи .NET з місцевими LLM, строгими вимогами приватності, і детермінованою поведінкою, Лангхейн впроваджує тертя в кількох місцях.

Прихований стан і неявна потока керування

LangChain керує пам' яттю і контекстом. Це звучить зручно, аж доки вам не потрібно з' ясувати, чому ваш запит дорівнює 10 000 жетонів довше, ніж очікувалося, або чому LLM раптом має доступ до історії розмови, яку ви думали, що очистили.

За допомогою об' єму ви можете наказати програмі виконати запити, керувати пам' яттю і керувати порядком виконання без будь- яких дій. Якщо щось розбивається, ви можете усування вад поведінки оболонки, а не поведінки вашого коду.

Продумування зібране з фреймом

Після прийняття LangChain ви починаєте проектування для LangChainВаша архітектура поєднується з абстракцією системи: ланцюгами, агентами, редиссерами, буферами пам'яті.

Це не унікально для LangChain - всі оболонки роблять це, але у області швидкого пересування, на зразок LLM, де правильні абстракції ще не залагоджені, з'єднання до світогляду системи є ризикованим.

Невідповідність до Python

LangChain припускає:

  • Длинні процеси (примітки у стилі приміток)
  • Загальний стан Mutable
  • Динамічне друкування і друкування качок на Python
  • Блокування шаблонів вводу/ виводу

Як розробник . NET, я припускаю:

  • Житлові життя, які можна переглянути за запитом (адреси ядра ASP. NET)
  • Нерухомий або явно з керуванням стан
  • Безпека друку і компіляції
  • Async/await всюди

The Порти LangCain.NET існує, але вони граються з версією Python, і абстракції все ще вважаються чужими для ідіоматичного C#.

Продуктивна реальність

Коли ви переходите від прототипу до виробництва, вам потрібно:

  • Рішучість - Те саме вхідне значення має викликати передбачувану поведінку
  • Перевірка - Переконайтеся, що вивід LLM є безпечним перед виконанням його
  • СендбоксінгCity in New Jersey USA - Обмежити те, що створений код може насправді зробити
  • Керування вартістю - Використання ключа доріжки і обмеження
  • Локальний висновок - Запустити моделі в автономному режимі без проблем з хмарами

LangChain оптимізує швидкість ітерації, а не процес імітації виробництва. Це добре для демонстрації; це проблема виробництва.

Що я будую замість

Ось розумова модель, яку я використовую: LLM - це логічні рушії, а не рушії виконання.

LLMs Do:

  • Інтерпретація - Розуміння наміру користувача з природної мови
  • Планування - Кинути складні завдання на кроки.
  • Переклад - Перетворення наміру у структуровані формати (SQL, JSON, виклики функцій)

LLM НЕ:

  • Обчислення агрегаторів - Cumming 100 000 рядків
  • Набори даних сканування - Пошук у великих файлах
  • Власний стан - Збереження довгочасної пам' яті

Принцип: Причина LLM. Двигуни обчислюють.

Це розділення керує всім, що я будую.

Явний контекст, а не магічна пам' ять

Замість пам' яті з керуванням об' єктом, я будую контекст явно на запит:

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; }
}

Кожна негайна побудова видима. Я точно знаю, що відправляється в LLM, тому що я побудував рядок сам:

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();
}

Ніякого таємного стану, ніякого магічного контетенту, просто ясної будівлі струн, коли це не так, я знаю чому.

Визначити шари виконання

Замість того, щоб дозволити LLM виконати що-небудь, я використовую його для створення Мета, потім виконати цей намір через детермінуючі двигуни:

  • Рушійи SQL (DuckDB) - Для запитів до даних
  • Пошукові рушії (Lucene, Postgres повнотекстовий) - Для отримання документа
  • Рушії правил - Для ділової логіки
  • Служби домену - Для підтверджених операцій

LLM створює SQL. DuckDB виконує його. LLM ніколи не бачить даних:

// 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);

Це безпечніше, швидше і знешкоджене. LLM не може випадково запустити DROP TABLE LLM не може протікати дані, тому що він ніколи не бачить дані - тільки схема.

Конкретний приклад: аналіз CSV без блоків

Нещодавно я писав про Аналіз великих файлів CSV за допомогою локальних LLMАрхітектура:

Питання користувача → LLM → SQL → DuckDB → Результати

LLM отримує:

  • Схема CSV (назви та типи)
  • 3 ряди зразків (щоб зрозуміти формат даних)
  • Питання користувача

The LLM генерує:

  • Запит до SQL DuckDB

Система тоді:

  • Перевіряє використання SQL EXPLAIN (виводить синтаксичні помилки без виконання)
  • Виконує запит проти файла CSV
  • Повертає результати користувачеві

LLM ніколи не бачить реальних даних. Він бачить лише структуру.

Це те, що LangChain назвав би "агентом" - системою, яка використовує LLM для створення дій, перевірки їх, виконання і потенційні ретакції при невдачі.

Окрім того, що я побудував його у ~200 рядках C# без оболонок:

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);
    }
}

Просто явне оркестрування LLM → перевірка → виконання.

Що це є агент, дійсно?

Термін "агент" прокидається навколо постійно, зазвичай, щоб мати на увазі "що-небудь пов'язане з LLM." Давайте будемо точні.

Агент:

  • Цикл - Він запускає декілька ітерацій
  • З станом - Він пам'ятає, що намагався.
  • З інструментами - Вона може діяти у світі.
  • З відгуком - Він бачить результати та пристосування.

Агентом є не бібліотекаЦе шаблон.

Шаблон мого агента у 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);
    }
}

Це агент, це петля зі штатами, інструментами та зворотним зв'язком.

Де вміщується робота агента MicrosoftName

Щоб бути справедливим до екосистеми .NET, Microsoft випустив Microsoft Agent Framework Це зроблено для розробки систем виробництва AI.

Що блокада Microsoft Agent робить правильно

Основа (колишня відома як Microsoft.Extensions.AI) надає:

  • Явне оркестрування - Ви контролюєте петлю агента, а не систему.
  • Сильний друк - Забезпечити час компіляції для визначень інструментів і виклику функцій
  • Зручність першого класу - Вбудована телеметрія, лісозаготівля і розподілене слідкування за допомогою OpenTelemetrary
  • Промислові кордони - Спроектовані для виробництва.NET систем з належними DI, конфігураціями та управління життєвим циклом.
  • Підтримка декількох моделей - Абстракції над OpenAI, Azure OpenAI, Ollama та іншими постачальниками послуг.
  • Інтеграція з семантичним ядром - Працює з ширшим стеком комп' ютера Microsoft

Компоненти ключів:

  • IChatClient - Універсальний інтерфейс для завершення балачки
  • IEmbeddingGenerator - Вбудовування векторів через постачальників послуг
  • AIFunction - Виклик до безпечної функції типу
  • трубопровод Midrageware - для ведення лісозаготівлі, повторних спроб, кешування, телеметрії

Приклад:

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;
});

Там, де я все ще залишаюся на нижчій нозі

Навіть з шаблоном Microsoft, я вважаю за краще тримати базове оркестрування явним:

Я не хочу:

  • Непрозорі планувальники - Оболонка самостійно вирішує, який інструмент викликати
  • Неявний вибір інструмента - Магічні маршрути, засновані на описах рідної мови.
  • Прихована логічна спроба - Поновлення помилок під час керування кадрів Я не можу перевірити

Я хочу:

  • Видимі цикли - Я бачу кожну ітерацію в моєму коді
  • Еталонні кроки - Я можу перевірити логіку рішень.
  • Замінені компоненти - Я можу поміняти LLM, інструменти, шар перевірки
  • Явний стан - Я точно знаю, що в контексті

Вона поважає моделі . NET, правильно використовує ін'єкції залежності і не бореться з екосистемою.

Коли використовувати рамки Microsoft Agent:

  • Побудова програм для балачки з викликом функції
  • Потрібна підтримка декількох режимів (перемикання між OpenAI, Azure, Ollama)
  • Бажає можливостей підприємництва (телеметрії, лісозаготівлі, розподілу)
  • Робота в команді, яка віддає перевагу консистенції обрисів
  • Будівництво над семантичним ядром

Під час обходу без обмежень:

  • Вам потрібен повний контроль над петлею агента
  • Ви створюєте нетипові орієнтаційні схеми
  • Ви хочете, щоб на поверхні не було абстрагування
  • Ви оптимізуєте для конкретних випадків використання (напр., аналіз CSV або використання веб-екскрементів)
  • Ви хочете точно зрозуміти, як це працює

База не усуває архітектурні рішення. Ви все ще обираєте те, що слід вкласти в контекст, як відрізати дані і коли повторювати. Це лише полегшує роботу водопровідної системи.

Чому це масштабує краще за довготермину

Бездротова система старіє з декількох причин:

Швидкодія Моя служба запитів CSV не працює через відсутність рамки між LLM і DuckDB.

Прогнозованість вартості - Я контролюю саме те, що йде до LLM.

Можливість зневаджування - Когда что-то сломается, я изображаю свой код, а не перевертаю магию карии.

Конфіденційність - Для систем с строгими стандартами по базам данных, изначально, понимая, что оставляет дело в машине.

Автономні сценарії Устройства ребра, сеть с воздухом, регульированные обстановки.

Регульований послух - Фінансами, охороною здоров'я та урядом, вам часто потрібно пояснювати та перевіряти кожне рішення.

Чим більше обмежується твоє середовище, тим більше ти хочеш мати явний контроль.

Коли я використовуватиму LangChain

Для роззброєних критиків існують законні випадки, коли я б добився до Лангхейна.

Хакафонсlithuania_ municipalities. kgm - Швидкість до демонстрації має більше значення, ніж архітектура.

Викинути POC - Якщо ви підтверджуєте ідею і плануєте переписати її для виробництва в будь-якому випадку.

Вага Python- команди - Якщо ваша команда вже вільно працює на Python, екосистема дуже сильна.

Вчення понять Абстракти Лангхіна можуть допомогти початківцям зрозуміти модель агента, перш ніж будувати власну.

Знати коли неDescription of a condition. Do not translate key words (# V1S #, # V1 #,) використовувати щось настільки ж цінне, як знання того, коли саме ним користуватися.

Візерунок Broader: Frameworks vs. Перші принципи

Мова йде не про Лангхейн, а про компроміс між системами та інженерією першокласників.

Frameworks прискорюють знайомі проблеми. Якщо ви створюєте програмний інтерфейс 100- го CRUD, докладайте зусиль для створення фреймів сутностей або Dapper. Шаблони буде вирішено.

Але LLM-силові системи? чи "абстракціоністи" чи "справжні розумові моделі." ми все ще з"ясовуємо це.

У такому середовищі я волію будувати поруч з металом:

  • LLM через виклики API (OllamaSharp, OpenAI SDK)
  • Запропонувати побудову з чіткою будівлею рядків
  • Перевірка за певною логікою домену
  • Виконання за допомогою вбудованих рушіїв (SQL, search тощо)

Як розробник .NET, я маю тверду думку про те, як слід будувати системи: явні терміни життя, сильний друк, синхронізація до кінця, ін'єкція залежностей для перевірки.

Абстракти Лангхейн не відносяться чисто до цих поглядів, тому я не використовую їх.

Зникнення

Якщо ви - розробник .NET, який дивиться на LangCain і запитує: "Чи мені це потрібно?" ось моя відповідь:

Вам потрібно вирішити проблеми, пов'язані з LangCain - Керування контекстом, інструмент оркестрування, повторення логіки, невизначеність.

Вам не потрібно, щоб LangCain розв'язував їх - Особливо, якщо ви цінуєте явність, сильний друк і виробництво тверді через швидкі прототипи.

Принцип над яким я будую:

"Правила пам'яті, двигуни обчислюють. Оркестрування - ваш власний."

Або простіше:

"Якщо ви розумієте проблеми, які розв'язує система, вам часто не потрібна система."

Зібрати системи, які мають сенс у вашій екосистемі, з вашими обмеженнями, використовуючи ідіоми вашої мови. Для мене це C#, сильний друк, явний контрольний потік і детермінуючі шари виконання.

Для вас все може бути по-іншому.

Мета полягає не в тому, щоб уникнути обмежень, а в тому, щоб вибрати їх свідомо, з розумінням як того, що вони забезпечують, так і того, що вони коштують.


Подальше читання:

logo

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