- PVSM.RU - https://www.pvsm.ru -

Делюсь опытом: ИИ-агент на Claude без LangChain и RAG — разбор архитектуры и развилок

Зачем я это пишу

Запустил недавно прод-агента на Claude — небольшой, для бытовой задачи у знакомой. По дороге понял несколько вещей, которые сначала казались мне очевидными «после интернета», а потом, когда сел и проверил на реальной задаче, начали играть иначе. Хочется поделиться разбором — может, кому-то сэкономит неделю-другую на тех же развилках.

Сразу честно: я не «академический» разработчик. До последнего года писал на Python в основном чтобы прочитать чужой скрипт и подправить отступ. То, что вы увидите в коде ниже — собирал руками, лезя в документацию по каждому слою. Поэтому это не «гайд от эксперта», а скорее дневник развилок — где я выбрал так и так и почему. Если у вас опыт глубже и я где-то перегнул палку или упустил — буду благодарен за поправки в комментариях, для меня это самая ценная часть таких публикаций.

Никаких ссылок, кодов промокодов и «закажите у нас» — просто разбор.


TL;DR

Сделал ИИ-агента для небольшой студии дизайна интерьера. Делает две вещи:

  • Чат на сайте — отвечает на типовые вопросы про услуги, тарифы, сроки, мягко переводит на встречу с дизайнером.

  • Эндпоинт /brief — клиент заполняет короткий бриф через форму, на выходе получает три заметно разных концепции интерьера: палитра, материалы, мебель, свет, четыре фото из Pexels на каждую.

Стек: Claude Haiku 4.5 для чата, Claude Sonnet 4.6 для генерации концепций, FastAPI на Railway, Pexels API для фото, статичный фронт на VPS [1]. Без LangChain, без LlamaIndex, без векторной базы, без RAG-фреймворка. Один файл main.py на 712 строк, два промпта, четыре эндпоинта.

В статье — почему именно этого хватило, какие развилки прошёл, на каких граблях постоял и где этот подход начнёт ломаться.


Контекст

Заказ: у знакомой — небольшая студия дизайна интерьера. Один дизайнер, проектов в работе одновременно три-четыре. Бюджет на разработку — несколько рабочих дней (это я сам себе ставил, у нас был дружеский договор). Боль на входе воронки понятная: клиенты пишут в WhatsApp в одиннадцать вечера, утром половина уже у конкурентов, потому что никто им не ответил. И вторая боль — клиент часто сам не понимает, чего хочет, и приходит на встречу «расскажите мне что-нибудь».

Из этих двух болей выросли две сущности:

  1. Чат-агент — отвечает мгновенно ночью, не оставляет сообщения «висеть до утра».

  2. /brief — генератор трёх концепций по короткому брифу. Не заменяет дизайнера, а готовит первую встречу: клиент приходит уже с понятием, какое направление откликается.

Это не «универсальный бот для всего». Это две узкие функции с понятной границей ответственности. Дальше будет важно, почему именно эта узость позволила обойтись без фреймворков.


Развилка №1: брать ли LangChain

Начинал я с распространённого представления, что любой ИИ-агент = LangChain (для оркестрации) + векторная база (Chroma/Qdrant/Pinecone) для контекста + RAG-цепочка для актуальных данных + агент-роутер с тулами. Так пишут в большинстве гайдов и так делают «взрослые». Хотел тащить это в проект, потому что страшно было пилить «не как у людей».

Прежде чем тащить — сел и расписал, что из этого реально решает мою задачу. Получилось вот что:

  • Оркестрация цепочек? У меня одна цепочка: сообщение → один запрос к LLM → ответ. Никаких ветвлений, параллельных шагов, ретраев с разными моделями. LangChain тут — обёртка вокруг httpx.post().

  • RAG для тарифов и услуг? Весь контент студии помещается в 2,5 тысячи токенов системного промпта. Это меньше, чем один embedding-запрос в Pinecone. И главное — этот контент не меняется каждую неделю. Тарифы — раз в квартал, услуги ещё реже.

  • Векторная база? Под что? Под историю диалога — сессии короткие, 5–10 сообщений, помещаются в окно. Под услуги — их семь штук. Под похожих клиентов — у дизайнера 30 проектов в год, векторный поиск тут неуместен по размерности.

  • Агент-роутер с тулами? Чат не должен никуда ходить во внешний мир — ни в календарь, ни в CRM, ни в базу. У /brief есть один тул (поиск фото в Pexels), и он жёстко привязан к структуре ответа, не выбирается агентом.

Когда сложил это в табличку, оказалось — из всех абстракций LangChain я бы использовал ровно одну, ChatAnthropic как обёртку над HTTP-клиентом. И эта обёртка добавляет: зависимость на ~500 МБ, новый слой с собственными багами, версионную нестабильность (LangChain известен breaking changes между минорными релизами), новый язык описания цепочек, который пришлось бы учить.

В обмен — chain.invoke() вместо прямого POST. Всё.

Никакого LangChain.

Это не значит, что LangChain — плохой инструмент. Это значит, что для узких задач с одной цепочкой он избыточен. Граница, на которой он начинает окупаться: 3+ параллельных цепочки, реальный роутинг между моделями, сложный multi-step агент с тулами. Если у вас одно «вопрос → ответ» — берите голый HTTP.

Это не моё гениальное озарение — это просто результат честного разбора задачи. Если у вас контр-пример, когда LangChain заметно окупился именно на простой задаче, очень интересно его услышать, я могу что-то упускать.


Системный промпт вместо RAG

Раз весь контент студии помещается в SYSTEM, я просто положил его туда. Структура промпта — не «творческий текст», а строгий FAQ-документ. Ниже укороченный фрагмент (полный — около 5500 токенов вместе с эталонами ответов):

Ты — ассистент студии дизайна интерьера. Отвечай вежливо, по делу,
в спокойном профессиональном тоне. Без лишних эмодзи. Только на русском.
Короткие чёткие ответы.

О СТУДИИ:
- Дизайн интерьера жилых пространств в Москве
- Сайт студии и контакты — см. правила маршрутизации ниже

УСЛУГИ:
1. Функциональное планирование — планировочные решения, перепланировки
2. Дизайн-проект — цельный визуальный образ, материалы, рабочая логика
3. Рабочая документация — чертежи и технические решения
...

ТАРИФЫ:
Три тарифа с разной глубиной проработки. Включают замер, фотофиксацию
и договор. Подробности — на сайте студии (правила маршрутизации ниже).

ПРАВИЛА:
- Вопрос о записи/консультации → дать контакт студии
- Вопрос о цене → объяснить тариф и что входит, без точных цифр
  если клиент не уточнил параметры объекта
- Хочет посмотреть варианты / не знает чего хочет → предложи /brief
- Не придумывай информацию которой нет выше

Подсвечу важное: разделы написаны человеческим языком в правильной структуре. «УСЛУГИ» — нумерованный список с одной строкой описания. «ТАРИФЫ» — словесное описание уровней без точных сумм в промпте (это спорное решение, у нас тарифы менялись пару раз за квартал, и я не хотел перекатывать SYSTEM каждый раз). «ПРАВИЛА» — однозначная маршрутизация типовых интентов.

Модель в такой структуре не теряется. Когда клиент спрашивает «сколько стоит планировка», Haiku видит, что «планировочное решение» упомянуто в услугах, и отвечает связно. Если бы тот же контент я залил в Chroma чанками по 512 токенов и доставал через RAG, я бы получил тот же результат, но: с задержкой на embedding-запрос, риском «не доехал нужный чанк», необходимостью поддерживать векторную базу как отдельный сервис. Зачем — я честно не нашёл ответа.


Эндпоинт /chat целиком

Это полный эндпоинт чата. Он короче, чем README типичного LangChain-стартера:

@app.post("/chat")
async def chat(request: Request):
    body = await request.json()
    messages = body.get("messages", [])
    if not messages:
        return JSONResponse({"error": "messages is empty"}, status_code=400)

    async with httpx.AsyncClient(timeout=60) as client:
        response = await client.post(
            "https://api.anthropic.com/v1/messages",
            headers={
                "x-api-key": ANTHROPIC_API_KEY,
                "anthropic-version": "2023-06-01",
                "content-type": "application/json",
            },
            json={
                "model": "claude-haiku-4-5-20251001",
                "max_tokens": 2000,
                "system": [{
                    "type": "text",
                    "text": SYSTEM,
                    "cache_control": {"type": "ephemeral"},
                }],
                "messages": messages,
            },
        )
        data = response.json()

    text = "".join(
        b.get("text", "") for b in data.get("content", []) if b.get("type") == "text"
    ).strip()
    return JSONResponse({"reply": text, "usage": data.get("usage")})

Разберу несколько решений по этому коду:

Модель claude-haiku-4-5-20251001. Самый маленький Claude — быстрый, дешёвый, по нашим задачам (короткие ответы по плотному FAQ) не уступает Sonnet. Время первого токена — ~600 мс, полного ответа — 1–2 секунды. Стоимость на нашем трафике — около копейки за диалог.

max_tokens=2000. Запас, который реально не используется — типичный ответ помещается в 100–300 токенов. Поставил с резервом на случай «расскажи подробно про каждый тариф».

system как список объектов с cache_control. Это включение prompt caching у Anthropic. Если SYSTEM-промпт не меняется между запросами, API его кэширует, повторные запросы считают только новые сообщения. Хит даёт скидку 90% на input-токены и режет латентность первого токена в 2 раза. Про грабли этого механизма дальше отдельный раздел.

messages приходит целиком от клиента. История диалога хранится во фронтенде в localStorage, не на сервере. Это осознанное упрощение: пока нет аутентификации и нет необходимости хранить переписку, не хочется мучиться с базой и доступами. Когда понадобится — добавлю SQLAlchemy и одну таблицу.

Никакого retry, fallback, circuit breaker. Anthropic стабилен — за квартал ~99,95% успешных запросов. Заворачивать в tenacity с пятью попытками — преждевременная оптимизация. Если упадёт — клиент перезагрузит страницу.

Это «минимально достаточный код». Один файл, одна функция, прямой запрос. Тесты пишутся за пять минут — мокаешь httpx.AsyncClient и проверяешь, что отправляется правильный body.


Главное открытие: FAQ-эталоны вместо чёрного списка запретов

Самой нетривиальной частью оказалось не научить ассистента отвечать, а научить молчать. Первые версии фантазировали: обещали невозможные сроки, путали тарифы, со второго сообщения начинали продавать встречу. То, что для меня казалось «модель должна сама понять, что это плохо» — она не понимала.

Первый порыв — добавить чёрный список:

ЗАПРЕТЫ:
- Не обещай сроки меньше 2 месяцев
- Не предлагай встречу до того, как узнал площадь
- Не используй слова «гарантирую», «точно», «безусловно»
- Не упоминай конкурентов
- Не давай скидок
- Не пиши маркированными списками, только связным текстом
...

Это не работало. К концу второй недели у меня было 23 пункта запретов. Ассистент всё равно их нарушал через раз, а сам промпт превратился в нечитаемое полотно «не делай X». И ещё тонкая вещь, которую я заметил: чёрные списки в принципе ухудшают качество ответов — модель будто фокусируется на «как обойти запреты» вместо «как помочь клиенту».

Переломный момент — переход на FAQ-эталоны в самом теле SYSTEM. Вместо «не делай так» — «вот эталонный ответ на типовой вопрос». Структура такая:

ВОПРОС: Сколько стоит проект двушки?
ОТВЕТ-ЭТАЛОН: «Зависит от тарифа и площади. У нас три уровня
проработки. Если назовёте метраж, могу подсказать ориентир по
каждому. Если интересно, могу подсказать, какой подходит под
вашу задачу».

ВОПРОС: Когда вы свободны на консультацию?
ОТВЕТ-ЭТАЛОН: «Запись на встречу — через контакт студии,
дизайнер ответит в течение дня и согласует время напрямую».

ВОПРОС: А вы можете спроектировать всё без меня, я не понимаю в дизайне?
ОТВЕТ-ЭТАЛОН: «Полностью без вас — не получится, ваши предпочтения
и образ жизни нужны как материал. Но если непонятно с чего начать,
у нас на сайте есть бесплатная концепция — за минуту собирается
три направления, можно посмотреть, что откликается».

Эта схема резко улучшила сдержанность. Модель видит готовый паттерн — длину ответа, тон, структуру, способ обработки ситуации — и воспроизводит его на похожих вопросах. Чёрный список из 23 пунктов схлопнулся в 4 правила маршрутизации (раздел «ПРАВИЛА» выше).

Почему это работает? Моя рабочая гипотеза: LLM — это машина имитации паттернов, а не машина следования инструкциям. Когда говоришь «не делай X», у модели нет образца того, что делать вместо X — она просто стохастически снижает вероятность токенов, связанных с X. Когда даёшь эталонный пример, она тиражирует его стилистически. Эталон — это положительный пример, чёрный список — отрицательный, и для имитативных моделей положительные примеры эффективнее.

Это не моё открытие — про важность few-shot examples писано тысячу раз. Что было откровением лично для меня — что 10–15 эталонов внутри SYSTEM работают лучше, чем 50 запретов и 5 эталонов отдельно. Эталоны делают всю тяжёлую работу.


Грабли с порогом кэширования 4096 у Haiku 4.5

Anthropic в 2024 году выкатил prompt caching. Если SYSTEM-промпт длинный и стабильный, API его кэширует на сервере, повторные запросы платят только за uncached input. На длинных промптах — до 90% экономии и латентность первого токена режется в 2 раза.

Я разметил кэширование в /chat сразу:

"system": [{
    "type": "text",
    "text": SYSTEM,
    "cache_control": {"type": "ephemeral"},
}],

И смотрю на usage в ответах API — cache_creation_input_tokens и cache_read_input_tokens стабильно нули. Кэш не работает. Я месяц думал, что у меня баг в коде. Проверял версии API, разрешения, ставил debug-логи. Никаких ошибок.

Оказалось — у Haiku 4.5 минимальный размер кэшируемого блока 4096 токенов. У Sonnet и Opus — 1024. Если блок короче — Anthropic просто тихо игнорирует cache_control, без warning, без ошибки в ответе. Нигде это не подсвечено крупно в документации, нашёл в одной строке в таблице параметров кэширования.

Мой SYSTEM на момент проблемы был ~2500 токенов. Для Sonnet кэш бы работал, для Haiku — нет. Было два варианта:

  1. Раздуть SYSTEM до 4096+, добавив FAQ-эталоны и подробные правила. Это улучшает качество ответов в любом случае (см. предыдущий раздел), плюс активирует кэш.

  2. Переключиться на Sonnet. Дороже, медленнее, для нашего чата избыточен.

Выбрал первый. Сейчас SYSTEM — ~5500 токенов, кэш активен. На каждом запросе вижу:

"usage": {
  "input_tokens": 47,
  "cache_creation_input_tokens": 0,
  "cache_read_input_tokens": 5483,
  "output_tokens": 178
}

Экономия на 1000 диалогах: вместо 5,5 млн input-токенов почти всё считается как cached, цена режется примерно в 10 раз.

Что забрать: если активируете caching на Haiku — следите, чтобы блок был ≥4096 токенов, иначе тихо ничего не работает. Для Sonnet и Opus порог 1024. Это, мне кажется, должно быть в документации красным жирным шрифтом, но пока нет.


Эндпоинт /brief: структурированный ответ от Sonnet 4.6

Второй эндпоинт сложнее. От /chat отличается двумя вещами:

  • здесь нужен Sonnet 4.6 (не Haiku) — задача креативная, нужен качественный язык и заметная разница между концепциями;

  • ответ должен быть строго JSON-массивом, без markdown-обёрток, парсимый напрямую.

Промпт начинается так:

Ты — арт-директор студии интерьерного дизайна.
Студия: тёплый минимализм, Japandi, скандинавский, функциональное
проектирование жилых пространств.

Тебе приходит бриф клиента. Твоя задача — выдать ТРИ заметно разных
концепт-направления для обсуждения с клиентом.

Отвечай СТРОГО валидным JSON-массивом без markdown-обёртки и без
комментариев. Никакого текста до или после JSON.

Структура каждого направления:
{
  "name": "Название концепции (2-4 слова, на русском)",
  "tagline": "Одна строка — суть атмосферы",
  "palette": [
    {"hex": "#XXXXXX", "name": "название цвета"},
    ... ровно 5 цветов
  ],
  "materials": [...5-6 материалов с конкретикой...],
  "furniture": [...5-6 предметов...],
  "lighting": "температура, источники, акценты",
  "mood": "что чувствует человек, время дня, звуки",
  "image_prompts": [...4 prompt-а на английском...],
  "pexels_queries": [...4 поисковых запроса для Pexels...]
}

Несмотря на жёсткие инструкции «без markdown», Sonnet периодически оборачивает ответ в ```json … ```. Не всегда, в 5–10% случаев. Бороться с этим через prompt engineering можно — но дешевле просто прибрать на парсинге:

def _parse_concepts(text: str):
    text = text.strip()
    if text.startswith("```"):
        text = re.sub(r"^```(?:json)?s*", "", text)
        text = re.sub(r"s*```$", "", text)
    return json.loads(text)

Три строки регэкспа решают проблему, на которую можно было бы потратить часы тюнинга температуры и формулировок. Принцип, который я для себя сформулировал: не воюй с моделью там, где можно простить ей слабость в коде.


Pexels вместо генерации картинок: почему живое фото бьёт DALL-E

Концепция без визуала — это полотно текста, которое клиент не дочитает. С визуалом — оживает. Первый вариант, который я попробовал — генерация картинок через DALL-E 3 / Midjourney через API. Получалось красиво, но узнаваемо неживо: у AI-генерации в 2026 году всё ещё характерные артефакты — слишком чистая геометрия, неестественные текстуры, странные углы между плинтусом и стеной. Любой, кто видел реальный интерьер на фото, считывает «это не настоящее» за секунду.

Переключился на Pexels API. Это бесплатный сток с реальной фотографией: 200 запросов/час, 20 000/месяц, без оплаты для коммерческого использования.

Логика: Sonnet кроме описаний выдаёт 4 коротких поисковых запроса для каждой концепции:

"pexels_queries": [
  "japandi living room",
  "warm wood texture",
  "linen sofa detail",
  "beige minimalist interior"
]

И параллельно через asyncio.gather тянем 4 фото:

async def fetch_pexels_set(queries, prompts, randomize=False):
    queries = (queries or [])[:4]
    async with httpx.AsyncClient() as client:
        results = await asyncio.gather(
            *[fetch_pexels(client, q, randomize=randomize) for q in queries]
        )
    return [
        {
            "prompt": prompts[i],
            "query": queries[i],
            "url": (r or {}).get("url"),
            "photographer": (r or {}).get("photographer"),
            "page": (r or {}).get("page"),
        }
        for i, r in enumerate(results)
    ]

В каждой концепции — 4 живых фото по теме, с указанием фотографа и ссылкой на оригинал (этого требует лицензия Pexels).

Что забрать: для интерьеров, фуд-фото, путешествий, моды — берите Pexels/Unsplash вместо генерации. Дешевле, быстрее, главное — выглядит как настоящее. Генерация выигрывает там, где нужна абсолютная уникальность сцены (фантастика, дизайн упаковки, иллюстрации к чему-то нестандартному), а не там, где нужна правдоподобность.


Микро-урок про форму: чипсы вместо текстовых полей

Маленький, но важный UX-урок — не про модели, про форму. Изначально все поля брифа были textarea — «опишите образ жизни», «опишите свет в комнате», «что вам не нравится». Около половины клиентов застревали на «образе жизни» и закрывали вкладку. Текстовое поле в форме — это психологический барьер: надо сформулировать, проверить грамматику, бояться написать глупость.

Заменил почти все поля на чипсы — кнопки с предзаготовленными вариантами. Для «Кто живёт»: «один», «пара», «пара + ребёнок», «пара + 2 детей», «несколько поколений». Для «Бюджет»: «эконом», «средний», «комфорт», «премиум». Для «Стиль-ориентир»: «минимализм», «Japandi», «скандинавский», «современная классика», «лофт», «эклектика», «не знаю».

Чипсы дополнены опциональным текстовым полем. Заполняемость формы выросла в 2,5–3 раза. Сигнал для модели чуть упростился, но это компенсируется тем, что до неё вообще доходит больше клиентов.

Принцип, который я выписал себе на полях: снижайте сопротивление на входе в форму даже ценой потери богатства данных. 100 коротких ответов лучше, чем 30 длинных, потому что моделям и людям одинаково удобнее работать с короткими.


Минимальный стек целиком

┌─ Frontend ───────────────────────────────────────────┐
│ Статичный HTML/Next.js, лежит на VPS                 │
│ Виджет чата — fixed-кнопка справа снизу              │
│ Страница /brief — форма с чипсами + результат        │
└──────────────────────────────────────────────────────┘
                       │ POST
                       ▼
┌─ Backend ────────────────────────────────────────────┐
│ FastAPI на Railway, один файл main.py (712 строк)    │
│                                                      │
│ POST /chat              → Claude Haiku 4.5            │
│ POST /brief             → Claude Sonnet 4.6 + Pexels  │
│ POST /regenerate-images → только Pexels               │
└──────────────────────────────────────────────────────┘
                       │
            ┌──────────┴──────────┐
            ▼                     ▼
       Anthropic              Pexels
       (LLM)                  (фото)

Никаких баз данных. Никаких очередей. Никаких воркеров. Деплой — git push, Railway сам ребилдит контейнер за минуту-две.


Где этот подход начнёт ломаться

Чтобы статья не выглядела «как мы выкинули весь enterprise и заиграли» — честно скажу, где минимально достаточный перестаёт быть достаточным.

1. Если контент перестаёт помещаться в SYSTEM. Допустим, появилось 500 кейсов, каждый со своим описанием, и хочется, чтобы агент про них вспоминал в нужный момент. 500 кейсов в SYSTEM не лезут — приходит RAG. Тогда же приходит embedding, векторная база, индексация, инвалидация при обновлении. Это уже отдельная инфраструктура.

2. Если появляется multi-step рассуждение. Например, «собери концепцию, проверь, нет ли в ней цветов из стоп-листа, если есть — переделай, потом подбери фото». Сейчас одношаговый pipeline. Если шагов становится 3+ — стоит подумать про оркестрацию (LangGraph, или просто аккуратный async/await).

3. Если нужны тулы. Например, агент смотрит в Google Calendar дизайнера, находит свободные слоты, предлагает три времени. Это тулы. Можно руками через Anthropic function calling. Если тулов 2–3 — руками. Если 10+ — есть смысл смотреть в сторону агентских фреймворков, но я бы сначала попробовал Claude Tool Use напрямую.

4. Если появляется состояние между сессиями. История диалога, профиль клиента, прогресс воронки — это БД, скорее всего Postgres + ORM. У меня этого нет осознанно: история в localStorage, новый клиент = новый разговор.

5. Если нужна команда из трёх и более человек. Минимально достаточный код легко поддерживать одному. Когда подключаются ещё двое — нужны соглашения, шаблоны, абстракции. Тут LangChain/типизированные цепочки/Pydantic-схемы окупаются. Не из-за технических причин, а из-за общего языка в команде.

«Минимально достаточный» — это не идеология. Это состояние, в котором находится проект сейчас. Через год эта же кодовая база может потребовать LangChain — и это будет нормально. Главное — не тащить его за полгода до того момента, когда он действительно нужен.


Что бы сделал по-другому

Если бы запускался сегодня с нуля:

  • Логи запросов в SQLite с самого начала. Сейчас история не сохраняется нигде, и через два месяца не могу пересмотреть «как же там ассистент ответил тому клиенту, который потом стал заказчиком». Пять строк кода, надо было делать сразу.

  • Метрика прерванных диалогов. Знать, на каком сообщении клиент закрыл виджет — золотой источник для тюнинга промпта. Веду эту работу глазами по выгрузкам, что не очень.

  • A/B на SYSTEM-промпте. Правки промпта летят в прод без сравнения с предыдущей версией. Один эндпоинт с двумя версиями и подсчётом конверсии — пара часов работы, окупится за месяц.

  • Не пытался бы сделать «универсальный бот». Узкий ассистент с понятной границей ответственности — это единственное, что у меня уверенно заработало. Любая попытка «давайте он ещё и Х» приводила к падению качества по основной задаче.


Вместо вывода

Когда смотришь на современный AI-ландшафт, кажется, что любой агент = LangChain + векторка + RAG + observability + оркестрация. Иногда это правда. Чаще — это гипотеза, которая не выдерживает встречи с реальной задачей.

Стоит честно сесть и спросить себя по каждому слою: что именно эта абстракция решает в моей задаче и сколько мне будет стоить её отсутствие. У меня по большинству слоёв стоимость отсутствия оказалась — ноль. И когда я перестал тащить их «потому что так делают все», вылезло, что вся работа делается в одном файле, читается за вечер и спокойно поддерживается одним человеком.

Это не призыв всё писать руками — фреймворки существуют не просто так. Это призыв не путать сложность инструмента со сложностью задачи. Иногда задача — простая, и тогда простой стек её решает быстрее, дешевле и надёжнее.


Буду рад содержательным комментариям. Особенно интересны два кейса: где LangChain у вас действительно окупился — нужны контр-примеры, чтобы лучше калибровать совет; и где минимально достаточный стек у вас, наоборот, треснул через полгода — на каких именно границах. Спасибо, если дочитали.

Автор: eignatiev

Источник [2]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/454257

Ссылки в тексте:

[1] VPS: https://www.reg.ru/?rlink=reflink-717

[2] Источник: https://habr.com/ru/articles/1054326/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1054326