Як я перестав переписувати код при кожній зміні LLM-провайдера. Архітектура, що економить нерви

💡 Усі статті, обговорення, новини про AI — в одному місці. Приєднуйтесь до AI спільноти!

Знаєте це відчуття? П’ятниця, вечір. Ви щойно завершили міграцію кодової бази з GPT-4 на Claude — переписали API-виклики, полагодили парсинг відповідей, оновили логіку стримінгу. Тиждень був пеклом, але фух — тести зелені.

І тут Google випускає новий Gemini з бенчмарками, від яких решта моделей виглядає як калькулятор із 1995 року.

Так. Я через це проходив. Не раз.

Після третьої міграції за пів року я сів і подумав: навіщо я переписую інтеграційний код, якщо єдине, що змінюється, — це HTTP-ендпоінт? Запит — JSON. Відповідь — JSON. Усі моделі приймають повідомлення на вхід і віддають текст на виході. Але дідька лисого. Чому зміна провайдера відчувається як заміна двигуна на ходу?

Саме так я натрапив на LM-Proxy і це змінило мій підхід до архітектури LLM-застосунків. Розповім, що це таке, чому це важливо і як налаштувати все за 5 хвилин.

Чому ваш інтеграційний код — це бомба сповільненої дії

Давайте чесно глянемо на еволюцію типового LLM-проєкту.

Місяць 1: просто візьмемо OpenAI. Один провайдер, один SDK, усе просто.

Місяць 3: для задачі сумаризації Claude насправді працює краще. Давайте додамо Anthropic. Тепер у вас два різні SDK, два потоки автентифікації, два формати відповідей і один if/else, який переслідує вас уві сні.

Місяць 5: Gemini Flash набагато дешевший для простих запитів. Давайте перенаправимо туди нескладні задачі. Логіка маршрутизації тепер розповзлася по трьох файлах, обробка помилок для кожного провайдера — своя, а новий розробник вбив два дні лише на те, щоб розібратися, який API-ключ куди підставляти.

Місяць 7: хтось питає: «А можемо підняти локальну модель для чутливих даних?» — і ви починаєте оновлювати LinkedIn.

Корінь зла простий: кожен LLM-провайдер вигадав власний формат API, хоча всі вони роблять по суті одне й те саме. Формат OpenAI став де-факто стандартом — але Anthropic, Google та інші прийшли зі своїми примхами, схемами автентифікації та протоколами стримінгу.

Те, що нам потрібно — це універсальний перекладач. Reverse proxy, який зовні прикидається старим добрим OpenAI API, а всередині мовчки робить брудну роботу з перекладу запитів на «діалекти» інших провайдерів.

Рішення: LM-Proxy за одну хвилину

LM-Proxy легковісний HTTP-проксі/шлюз на Python і FastAPI, сумісний з OpenAI API.

Його філософія проста:

Ваш застосунок спілкується з одним API. LM-Proxy спілкується з усіма іншими.

OpenAI, Anthropic, Google (AI Studio / Vertex AI), локальні PyTorch-моделі, Ollama, LM-Studio чи vLLM — все це стає доступним через єдиний ендпоінт /v1/chat/completions.

«Окей, черговий LLM-gateway», — скажете ви. Справедливо. Але дайте я поясню, чому саме цей вартий уваги і чим він відрізняється від «ентерпрайз-монстрів».

Чому це працює

1. Радикальний мінімалізм

Жодного Kubernetes. Жодного Redis. Ніякої архітектури з 47 мікросервісів.

pip install lm-proxy
lm-proxy

Один Python-пакет. Один TOML-конфіг. Одна команда. Запускається на VPS за $2/місяць у Docker-контейнері або вбудовується напряму в Python-застосунок як FastAPI-суб-застосунок. Ядро навмисно компактне — жодного зайвого баласту для задачі, яка його не потребує.

2. Конфігурація, для якої не потрібна наукова ступінь

Ось повний робочий конфіг, який маршрутизує GPT-запити до OpenAI, Claude — до Anthropic, а Gemini — до Google:

host = "0.0.0.0"
port = 8000

# Підключення до провайдерів (upstream)
[connections.openai]
api_type = "open_ai"
api_base = "https://api.openai.com/v1/"
api_key = "env:OPENAI_API_KEY"

[connections.anthropic]
api_type = "anthropic"
api_key = "env:ANTHROPIC_API_KEY"

[connections.google]
api_type = "google_ai_studio"
api_key = "env:GOOGLE_API_KEY"

# Магія маршрутизації
[routing]
"gpt*" = "openai.*"
"claude*" = "anthropic.*"
"gemini*" = "google.*"
"*" = "openai.gpt-4o-mini"        # fallback

# Групи доступу
[groups.default]
api_keys = ["my-team-api-key-1", "my-team-api-key-2"]

Прочитайте зверху донизу. Ви зрозуміли його за 30 секунд, правда? Чистий TOML з очевидною семантикою — ніяких кошмарів з відступами в YAML і JSON-простирадл на 200 рядків. До речі, YAML, JSON і Python-формати конфігурації теж підтримуються.

Префікс env: підтягує секрети зі змінних оточення або .env-файлів — ключі ніколи не потрапляють у git.

3. Маршрутизація за патернами (кілер-фіча)

Секція [routing] — це місце, де відбувається магія. Ключі — це glob-патерни, що зіставляються з назвою моделі, яку надсилає клієнт. Суфікс .* означає «передати назву моделі провайдеру як є». Тобто коли клієнт запитує claude-sonnet-4-5-20250929, LM-Proxy перенаправляє саме цю назву до API Anthropic. Жодних таблиць відповідності, жодних файлів із мапінгом моделей — воно просто працює.

Можна й прив’язати патерн до конкретної моделі:

[routing]
"custom*" = "local.llama-7b"        # Будь-який "custom*" запит → локальна Llama
"gpt-3.5*" = "openai.gpt-3.5-turbo" # Прив'язка до конкретної моделі
"*" = "openai.gpt-4o-mini"          # Все інше → дешевий fallback

Це означає, що клієнтський код ніколи не змінюється. Хочете спробувати нову модель — оновіть один рядок у конфігу. A/B-тест двох провайдерів — додайте правило. Вивести модель з обігу — перенаправте патерн. Нуль змін у коді.

4. Віртуальні ключі та контроль доступу

Ця фіча перетворює LM-Proxy з іграшки на production-інструмент. Проксі оперує двома рівнями ключів: віртуальні (клієнтські, для автентифікації з проксі) та провайдерські (справжні ключі від OpenAI, Anthropic тощо, які залишаються прихованими).

# Преміум-користувачі мають доступ до всього
[groups.premium]
api_keys = ["premium-key-1", "premium-key-2"]
allowed_connections = "*"

# Безкоштовний тариф — лише OpenAI
[groups.free]
api_keys = ["free-key-1"]
allowed_connections = "openai"

# Внутрішні інструменти — лише локальні моделі
[groups.internal]
api_keys = ["internal-key-1"]
allowed_connections = "local"

Upstream API-ключі ніколи не потрапляють до клієнтів. Можна проводити їх ротацію, не чіпаючи жодної клієнтської конфігурації. Можна створювати гранулярні рівні доступу — преміум-користувачі отримують Claude Opus, безкоштовні — GPT-4o-mini, внутрішні інструменти працюють на локальних моделях. Усе управляється одним конфіг-файлом.

Підтримується навіть зовнішня автентифікація — валідація віртуальних API-ключів через Keycloak, Auth0 або будь-який OIDC-провайдер:

[api_key_check]
class = "lm_proxy.api_key_check.CheckAPIKeyWithRequest"
method = "POST"
url = "http://keycloak:8080/realms/master/protocol/openid-connect/userinfo"
response_as_user_info = true
use_cache = true
cache_ttl = 60

[api_key_check.headers]
Authorization = "Bearer {api_key}"

Ваші наявні OAuth-токени автоматично стають ключами до LLM API. А якщо вбудовані валідатори не підходять, можна написати кастомний — просто Python-функція, яка приймає рядок API-ключа і повертає назву групи.

5. Повноцінна підтримка стримінгу

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

6. Бібліотека, а не лише сервер

LM-Proxy — це не тільки окремий сервіс, але й Python-пакет, який можна імпортувати. Оскільки він побудований на FastAPI, його можна вбудувати у наявний застосунок, використовувати в інтеграційних тестах або компонувати з іншими ASGI-middleware. Не потрібно запускати окремий процес, якщо ви цього не хочете.

Реальний сценарій: 5 хвилин до production

Розглянемо конкретну ситуацію. Ви розробляєте SaaS-продукт, який використовує LLM. Хочете GPT-4o для складних міркувань, Claude Sonnet для роботи з великими документами та Gemini Flash для дешевої класифікації.

Крок 1: Встановлення

pip install lm-proxy

Крок 2: Створити .env

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AI...

Крок 3: Створити config.toml

host = "0.0.0.0"
port = 8000

[connections.openai]
api_type = "open_ai"
api_base = "https://api.openai.com/v1/"
api_key = "env:OPENAI_API_KEY"

[connections.anthropic]
api_type = "anthropic"
api_key = "env:ANTHROPIC_API_KEY"

[connections.google]
api_type = "google_ai_studio"
api_key = "env:GOOGLE_API_KEY"

[routing]
"gpt*" = "openai.*"
"claude*" = "anthropic.*"
"gemini*" = "google.*"
"*" = "google.gemini-2.0-flash"

[groups.backend]
api_keys = ["backend-service-key"]
allowed_connections = "*"

[groups.frontend]
api_keys = ["frontend-widget-key"]
allowed_connections = "google"     # лише дешеві моделі

Крок 4: Запуск

lm-proxy

Крок 5: Використання з вашого застосунку

from openai import OpenAI

# Зверніть увагу: ми стукаємо на наш проксі
client = OpenAI(
    api_key="backend-service-key",
    base_url="http://localhost:8000/v1"
)

# Складні міркування → GPT-4o
response = client.chat.completions.create(
    model="gpt-5.2",
    messages=[{"role": "user", "content": "Проаналізуй цей контракт..."}]
)

# Великий документ → Claude
response = client.chat.completions.create(
    model="claude-opus-4-6",
    messages=[{"role": "user", "content": "Підсумуй цей 100-сторінковий звіт..."}]
)

# Швидка класифікація → Gemini Flash (дешево)
response = client.chat.completions.create(
    model="gemini-2.0-flash",
    messages=[{"role": "user", "content": "Це лист — спам? ..."}]
)

Один клієнт, один base URL, три провайдери. А завтра, коли вийде чергова модель, на 50% дешевша, ви зміните один рядок у config.toml і перезапустите сервер. Код застосунку залишиться незмінним.

LM-Proxy vs LiteLLM: Давид і Голіаф

У коментарях обов’язково запитають: «А чим це краще за LiteLLM?»

Скажемо чесно — LiteLLM — це 400-кілограмова горила в цій ніші. Понад 33 000 зірок на GitHub, підтримка 2000+ LLM, лістинг на AWS Marketplace, функціонал на кшталт дашбордів для трекінгу витрат, guardrails, кешування, rate limiting, SSO та інтеграції з MCP — це повновісна enterprise-платформа. Вона також пропонує як Python SDK, так і HTTP-проксі-сервер, тож архітектурно покриває схожу територію.

То чому ж обирати LM-Proxy?

З тієї ж причини, з якої ви обираєте Flask замість Django або SQLite замість PostgreSQL. Не кожній задачі потрібен «повний фарш» ентерпрайз-стеку.

LM-Proxy:

  • Філософія: мінімальне ядро, розширюйте за потреби.
  • Налаштування: pip install lm-proxy + один TOML-файл.
  • Залежності: FastAPI + MicroCore (легковісно).
  • Конфіг: TOML / YAML / JSON / Python.
  • Вбудовування як бібліотеки: першокласний сценарій використання.
  • Віртуальні ключі + групи: вбудовано.
  • OIDC / Keycloak авт.: вбудовано.
  • Трекінг витрат, guardrails, кеш: немає з коробки (розширюється через плагіни).
  • Адмін-панель: ні.
  • Підтримка провайдерів: усі основні + локальні моделі / вбудований інференс.

LiteLLM Proxy:

  • Філософія: все включено.
  • Налаштування: pip install 'litellm[proxy]' + YAML + опціонально БД.
  • Залежності: важче дерево залежностей.
  • Конфіг: YAML.
  • Вбудовування як бібліотеки: підтримується, але не рекомендується документацією.
  • Віртуальні ключі + групи: вбудовано (більш розвинуто).
  • OIDC / Keycloak авт.: вбудовано (більш розвинуто: JWT-валідація, мапінг ролей, SSO UI).
  • Трекінг витрат, guardrails, кеш: вбудовано.
  • Адмін-панель: так.
  • Підтримка провайдерів: усі основні.

Обирайте LM-Proxy, коли: вам потрібен легкий проксі, який легко вбудовується, з мінімальним footprint, можливістю конфігурування через Python і без потреби в 90% enterprise-функцій. Ідеально для невеликих команд, особистих проєктів або коли хочете шлюз, вихідний код якого можна повністю прочитати за один вечір.

Обирайте LiteLLM, коли: вам потрібен enterprise-рівень трекінгу витрат, UI для управління, десятки інтеграцій, guardrails, кешування та підтримка 100+ провайдерів і інтеграцій з коробки.

Інші альтернативи, як-от Portkey, схиляються ще більше в бік enterprise. LM-Proxy свідомо займає нішу «рівно стільки шлюзу, скільки потрібно» — достатньо потужний для production, достатньо простий, щоб конфіг-файл і був документацією.

Що вже є (і що хотілося б бачити далі)

Варто визнати — LM-Proxy вже покриває більше, ніж очікуєш від «легковісного» інструменту:

  • Структуроване логування — система логування з підключенням JsonLogWriter і LogEntryTransformer (відстежує токени, тривалість, групу, з’єднання, IP-адресу), плюс застосунок lm-proxy-db-connector для запису логів у PostgreSQL, MySQL, SQLite та інші бази через SQLAlchemy
  • Балансування навантаження — є приклад конфігурації, який розподіляє запити випадковим чином між кількома LLM-серверами за допомогою Python-формату конфігурації.
  • Обробники запитів — механізм, подібний до middleware, для перехоплення запитів перед надсиланням до upstream-провайдерів, що дозволяє реалізувати наскрізні завдання на кшталт аудиту та маніпуляцій заголовками.
  • Підтримка Vertex AI — Google Cloud Vertex AI підтримується поряд із простішим AI Studio API, з окремим прикладом конфігурації.

Проєкт розвивається (остання версія: v3.0.1), і кілька речей залишаються в моєму особистому списку побажань:

  • Usage analytics dashboard — інфраструктура логування та БД уже солідна, але вбудований UI для візуалізації витрат і використання був би вишенькою на торті.
  • Wildcard model expansion — режим expand_wildcards для /v1/models запланований, але ще не реалізований — наразі потрібно явно перелічувати моделі в конфігурації маршрутизації.
  • Automatic provider failover — якщо OpenAI повертає 5xx, автоматично перенаправити запит до Anthropic. Балансування між інстансами одного провайдера вже є, але кросс-провайдерний failover доповнив би картину.

Філософія «extensibility-by-design» означає, що більшість можливостей можна додавати у вигляді плагінів, не чіпаючи ядро. Наприклад, існує пакет db-connector для підключення до БД, логування в неї та перевикористання підключення SQLAlchemy для інших потреб — зокрема як сховища API-ключів тощо.

Кодова база має ліцензію MIT і достатньо компактна, щоб прочитати її повністю за один вечір.

Підсумок

Якщо ви працюєте з кількома LLM-провайдерами, або підозрюєте, що будете — зав’язуйте писати код під конкретного провайдера / SDK. Втуліть між собою і ними проксі-шар, і нехай міграція між моделями буде тим, чим вона має бути: зміною одного рядка в конфігу.

pip install lm-proxy

GitHub | PyPI

А як ви вирішуєте проблему зоопарку моделей і їх API/SDK? Пишете свої врапери чи використовуєте готові шлюзи? Запрошую до холівару в коментарях.

👍ПодобаєтьсяСподобалось12
До обраногоВ обраному8
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

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

Коментар порушує правила спільноти і видалений модераторами.

Бля, як додавання проксі може заставити переосмислити архітектуру?? Інвалід розумової праці, їй богу. Патерн ’стратегія’ нам приніс.

Пане Вадиме, перелогіньтесь. Бо після ваших коментарів я починаю думати про вашу маму. Чи заслужила вона таке.

Я шкодую, що зачепив вас, піддавши сумнівам вашу освіту. Перепрошую і пропоную збавити обороти.

Нас читають зокрема молоді інженери, люди, що щиро бажають розібратись в тематиці. І ваші коментарі «архітектора» вводять їх в оману.

Проксі є класичним будівельним блоком системного дизайну. І для додатків, працюючих з LLM він часто є зручним, тому що LLM-request — це так би мовити, новий будівельний блок програмного забезпечення сьогодення. Я б сказав — Unit of AI work. І цей блок — досить генералізований. Може виникати безліч причин, сому нам може знадобитись наскрізний функціонал поверх LLM-запитів: Observability, бюджетування, модерація, safety-контроль, in-stream PII Data masking, роутінг, load-balancing, збір метрик/телеметрії і ще тисяча інших.

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

Спочатку у вас є PoC / MvP, і ви живете собі з цьою апкою, нарощуєте функціонал.

Так от, на якомусь етапі, краще на ранньому, ніж на пізньому, ймовірно і виникає переосмислення архітектури з введенням proxy. І дуже класно, якщо це виникло до того моменту, як ми пекельно роздули доменну логіку цільового додатку, обмазавши його подібним наскрізним функціоналом по всій кодовій базі і отримавши low cohesion & high coupling, що дуже погано для maintainability.

Це вайбкодінгархітект
Народження

О, фасад над ШІ вже є, чекаємо фасад фасадів і фасад фасад фасадів які просто ганяють трафік між апішками (ми це вже
проходили з мікросервісами, graphql ітд)

Ви є противником мікросервісного підходу?

Розкажіть, why not. Якщо не утрирувати до маленького сайтику, де менше мільйону рядків коду і проблема масштабування не стоїть в принципі.

Я є противником переускладнень заради переускладнень. До речі кількість рядків — це взагалі не критерій розділення на мікросервіси

Я є противником переускладнень заради переускладнень.

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

До речі кількість рядків — це взагалі не критерій розділення на мікросервіси

Саме стосовно мікросервісів — безумовно, не критерій, але для розділення кодової бази вцілому — дуже навіть критерій.

Знаєте, зараз, коли стало модно застосовувати ШІ для кодінгу — агенти дуже краще працюють з кодом, якщо він компактний і повністю поміщається в контекст.

І свіжі розробники, що залітають на проєкт, краще пораються в маленьких, атомарних кодових базах.

Тому вважаю інженерним гріхом, що породжує потвор кривавого ентерпрайзу, дублювання, неорганізовану складність і когнітивну тяжкість проєкту, наприколад... не винести, якийсь низькорівневий вузькоспеціалізований і зфокусований функціонал в бібліотечку, де це можливо.
А якщо в організації, в рамках якої ведеться розробка, ще й практикується OpenSource — то це взагалі чудово, бо дає стимули для створення якіснішої документації, кращого коду, надихає професійних розробників розвиватись, співпрацювати і взаємодіяти в рамках цілих екосистем програмного забезпечення.

Зачекайте, схоже я не зрозумів. Це якась вундервафля чи вайбкодери винайшли Проксі та Фасад патерн? Чи знижувати coupling між шарами і сторонніми сервісами це щось нове?

Ігоре, дякую за коментар.

Вайбкодери не знають слова «coupling» — вони думають, це щось із Тіндера.

це щось нове?

«Нема нічого нового під сонцем», сказав Еклезіаст три тисячі років тому, і відтоді ніхто не зміг його спростувати.

Доречі, у вас білди на github впали.

Ну тобто по ділу немає що сказати, так? Хай мої білди вас не турбують. Я про них статтю на доу не викатую.

Мені по ділу сказать — як два байта переслать.
Але тоді ж давайте і грунтовніший наїзд, щоб на нього цікаво було відповідати.

Бо про що мова?

— Так, ви праві, дійсно нічого нового, просто проксі для LLM. Так, ви праві, зниження coupling з 3-rd party сервісами. Ніяких див, винаходів чи архітектурної езотерики, просте рішення.

А щодо вайбкодерів — розкрийте думку, будь-ласка. Схоже, я не зрозумів.
Це претензія до якості рішення? Тоді в чому саме? Чи просто осуд віртуальних вайб-кодерів, які гіпотетично десь от обертаються навколо цієї статті і циркулюють десь приблизно тут (були зізнання нижче в коментарях☝️)

Очевидно ніякого наїзду не було. Лише хіба на тон статті, який явно носить клікбейтний/викривальний характер.

От я вам тут нагенерував, щоб було цікаво:


Стаття — Класичний приклад того, як індустрія розробки ходить по колу, видаючи старі концепції за нові «одкровення». Якщо відкинути хайп навколо LLM, то перед нами типова історія про «винайдення велосипеда», яку можна розкритикувати з кількох позицій у стилі «нічого нового».
1. Патерни проектування 30-річної давнини
Автор з великим ентузіазмом описує те, що в класичній архітектурі ПЗ називається патерном «Адаптер» (Adapter) або «Фасад» (Facade). Книга «Банди чотирьох» (GoF) вийшла у 1994 році, і там детально описано, як об’єднати різні інтерфейси сторонніх сервісів під одним універсальним інтерфейсом, щоб код не залежав від конкретної реалізації.
У 2024–2025 роках нам кажуть: «Дивіться, я придумав, як не переписувати код при зміні провайдера!». Це база, яку викладають на другому курсі університету.
2. Еволюція API Gateway
Те, що автор називає «LM-Proxy», — це просто спеціалізований API Gateway. Ми проходили це з базами даних (ODBC/JDBC), з хмарними провайдерами (Terraform, Libcloud), з платіжними шлюзами (Stripe/Stedi).
Кожен раз, коли з’являється нова ніша (як-от LLM), з’являється армія розробників, які кажуть: «Ой, у них різні API, давайте зробимо проксі!». Це стандартний етап дорослішання будь-якої технології, а не якась архітектурна знахідка.
3. Культ «Конфігурацій» замість Коду
Автор підносить TOML-конфіг як «магію маршрутизації». Але ми вже бачили це в Apache, Nginx, XML-конфігах Spring або Enterprise Service Bus (ESB) на початку 2000-х.
Ідея «винесемо логіку в конфіг, щоб не чіпати код» часто призводить до того, що конфіг стає настільки складним, що його важче підтримувати, ніж сам код. Це просто перекладання відповідальності з Python-файлу на TOML-файл.
4. Черговий прошарок залежностей
Коментарі під статтею влучно підмічають: щоб просто «перекладати JSON з одного формату в інший», пропонується тягнути в проект монстра на 128 МБ (або 24 МБ, як стверджує автор) із десятками залежностей.
В епоху, коли ми боремося за продуктивність і безпеку ланцюжка постачання (supply chain security), додавання ще одного проміжного проксі-сервісу — це просто створення додаткової точки відмови (SPOF) і збільшення затримки (latency).
Підсумок:
Стаття написана живою мовою і справді вирішує біль початківців, але для досвідченого інженера це виглядає як: «Привіт, я відкрив для себе інкапсуляцію сторонніх залежностей і назвав це новим терміном». Нічого нового — лише чергова ітерація старої як світ боротьби з vendor lock-in за допомогою ще одного рівня абстракції.

В самому слові «вайбкодери» нічого поганого немає. Але воно вже досить давно набуло значення людини, яка бездумно запускає Ральф луп і чекає на готовий результат не задумуючись над деталями (архітектурою, подальшою підтримкою і розширенням) аби тести позеленіли.

Стосовно клікбейту, і більшості накиданого ШІ: Мій оригінальний текст англійською не містить взагалі таких гучних слів як **Архітектура**, назва статті How I Stopped Rewriting My Code Every Time I Switched LLM Providers.

Ось це про архітектуру в заголовку, було, якщо не помиляюсь, знахідкою редакторів ДОУ в процесі редактури. Мені особисто ця зміна подобається. Але чомусь багатьох тут трігерить.

Стаття написана живою мовою і справді вирішує біль початківців, але для досвідченого інженера це виглядає як: «Привіт, я відкрив для себе інкапсуляцію сторонніх залежностей і назвав це новим терміном». Нічого нового — лише чергова ітерація старої як світ боротьби з vendor lock-in за допомогою ще одного рівня абстракції.

Стаття наративно орієнтована на початківців, всім відразу не догодиш
¯\_(⊙_ʖ⊙)_/¯

А профі може бути цікаво, якщо вони вже знають, навіщо їм LLM Proxy, і знаходяться в пошуку найбільш підходящого рішення під їх специфіку (мінімалізм, простота, розширюваність, написано на python / FastApi).

Стаття наративно орієнтована на початківців, всім відразу не догодиш

Просто ржу)))
Дякую))))

Лайнокод переходить на новий рівень.
Якщо раніше бухтіли над недолугим спагеті кодом та матюкалися що перебираємо масив там, де можно отримати елемент по індексу, то тепер ми напихали купу лайна генерованого АІ, десь так трохи розуміючи що воно там робить (яке там нахрін О(1), головне швидко нагенерили і працює) і думаємо як його організувати так, щоб хоча б на рівні АРІ воно пристойно виглядало і милуємося лаконічністю конфігурації файла, який налаштовує оті генерилки щоб вони одна одній не заважали.
Це п****ць
Крапка

Та чого ти переживаєш, в нас рейти пдіростуть. :-) Галер люблять старий гівнокод, можна доїти роками багато грошей.

Та нас взагалі веселі часи чекають.
Це ж тут нові архітектурні патерни навигадують, оно вже цілу проблему з конкарренсі між АІ агентами вирішують, а це ж тільки початок.
Які там нафіг солід, хренячиш по агенту на фронтенд, бекенд і куа і хопа, ти не нудний гребець з веслом, а цілий АІ-агент лід!!
Треба тільки пару років почекати поки це покоління вайбкодерів нагенерить достатньо коду)))

Ну я тебе розумію але мені агенти подобаються, це гарна тула, дуже, але за розумним використанням. Не розумні викорситання збільшать попит на нас, бо сотанні роки щось мяса тіграм не докладають та знову в офіси тягнуть :-) Ну така вже доля з оцим програмуванням — розгрібати гівно папередніків.

Я теж в захваті від того ж гітхаб копайлота.
Це ж готова документація під рукою зрозумілою мовою, та ще й в контексті відкритого солюшена. Не треба нічого гуглити — просто виділяєш кусок коду і питаєш що воно робить.
Особливо подобається, коли є шматок коду і лінь в ньому розбиратися, то копайлот завжди підкаже що воно таке.
А ще генерація усіляких там мапінгів, коменти в коді.
Усіляка девопс фігня, коли хз що деплоїться через тераформ в оті всі ежури з кубернейтісами і треба додати якийсь флаг — раніше день можно було витратити намагаючись розібратися в отіх всіх змінних та енварймнетах, зараз питання вирішується за три хвилини.
Але про генерацію коду... Ну копайлот часто генерив код який навіть не компілювався, іноді таки тупив на очевидних речах і йому треба було підказувати.
На днях випробував кодклауд — ну що сказати, згенерував в ньому вебморду для chess.com API — перша версія це тупо набір *.ts файлів з захардкоженим в них html та css.....
Ну воно начебто і запрацювало, але сидіти покроково пояснювати йому очевидні речі, як, наприклад, що іконки це окремі svg файли, а не захардкожені стрінги мене вже не вистачило. І це я ще до NGRX з усілякими там авторизаціями не дійшов.
Але народ щось там генерує, в них все літає, вони хапонули бога за бороду і тепер кожний може відчути себе програмістом.
Ну хай граються)
Мені вже смішно з цього цирку, але найцікавіше буде попереду, коли вони нагенерують достатньо лайна та будуть розмірковувати про якість генерованого коду, бо воно щось зовсім якось криво працює)
Та хоча вже почали. І цей топік цьому підтвердження — як подружити генерилки щоб вони одна одній незаважали і навіть GoFовські паттерни до цього лайна приплели.

У вашому випадку це «shit in — shit out». Погугліть в контексті кодінг агентів та llm

Я щось подібне робив з langchain.
Різні провайдери підключаєш до агента і все

Але не знаю чи воно повноцінно працює з стримінгом. Я не пробував

Стрімінг тут — це якраз болюча частина з нюансованою реалізацією.

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

І до речі, проект починав одразу з langchain. За два роки багато чого було зроблено перероблено але всі два роки проект жив з ChatGPT. Учора за пів години перемкнув на anthropic. Дякувати langchain — все запрацювало майже без фіксів.

Як вам загалом LangChain?

Я дивлюсь на нього — і тягне оперувати низькорівневішими абстракціями (LLM Request, Vector DB Request), щоб потім не вгризатися під капот того, що вони називають «агентом». Хочеться від початку мати повний нюансований контроль: як саме працює пам’ять, яку репрезентацію має кожне повідомлення для моделі, а яку для користувача, і так далі в такому дусі.

Але з іншого боку, LangChain сьогодні № 1 за популярністю, тож однозначно заслуговує уваги.

Жодних нарікань. Гнучкий, підтримує більшість вендорів. LangSmith — окреме дякую. Стосовно контролю: немає проблем можеш користуватись або самим базовим chain, або високорівневим Graph, Agent тощо. З часом приходить розуміння як воно все поєднано та перестаєш боятись високорівневі абстракції. Claude Code дуже гарно розуміє фреймворк та може написати або готовий код або ж кастомну реалізацію чогось.
Хіба що спочатку було важко зрозуміти LCEL.

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

Вадиме, «просто зсунув проблему в інше місце» — це те саме, що сказати про Nginx: «ти не вирішив TLS, просто зсунув його в проксі». Так. Саме так працює інженерія. Це називається separation of concerns.

Вам в університеті не викладали принцип «Розділяй і володарюй»?

це те саме, що сказати про Nginx: "ти не вирішив TLS, просто зсунув його в проксі"

это не тоже самое, не говоря что то что ты зачем-то в пример привел несравнимое по масштабу и возможностям

Так. Саме так працює інженерія. Це називається separation of concerns.

я знаю, но при чем здесь это, если ты описываешь проблему вендорлока и как решение предлагаешь лок на нишевый проект (5 контрибьюторов, 9 форков, 73 лайка)

Вам в університеті не викладали принцип "Розділяй і володарюй"?

нет, 95% математика и ее производные, 100 сортов программирования, теория по железу от печатных плат до робототехники, нейросети, криптография, моделирование

Вам в університеті не викладали принцип «Розділяй і володарюй»?

А вам не викладали основи психології та тлумачення поняття «пасивної агресії»? )

Нажаль ні, я за освітою інженер.

А питання є справелдивим. Я поясню свою репліку — мене щиро здивувало, як людині з атрибуцією авторитетної інженерної ролі у підписі (Solution Architect), вдається писати таку нісенітницю — буквально і формально нікчемні твердження з точки зору одночасно кількох базових університетських курсів з інженерії програмного забезпечення.

Може, власне, пан Вадим кваліфікований спеціаліст, але саме тут в нього є суттєва прогалина в знаннях. Тому я й питаю про університет.

Це направду потребує пояснень🤔

Мінімальне ядро казали вони.

pip install lm-proxy
......
Installing collected packages: pyperclip, websockets, urllib3, typing-extensions, tqdm, sniffio, six, shellingham, rpds-py, regex, PyYAML, python-multipart, python-dotenv, pygments, pycparser, pathable, more-itertools, mdurl, MarkupSafe, lazy-object-proxy, jiter, isodate, idna, httpx-sse, h11, exceptiongroup, docutils, docstring_parser, dnspython, distro, colorama, click, charset_normalizer, chardet, certifi, attrs, annotated-types, annotated-doc, werkzeug, uvicorn, typing-inspection, rfc3339-validator, requests, referencing, pydantic-core, markdown-it-py, Jinja2, httpcore, email-validator, cffi, anyio, tiktoken, starlette, rich, pydantic, jsonschema-specifications, jsonschema-path, httpx, cryptography, typer, sse-starlette, rich-rst, pydantic-settings, openapi-pydantic, openai, jsonschema, fastapi, authlib, openapi-schema-validator, mcp, cyclopts, openapi-spec-validator, openapi-core, fastmcp, ai-microcore, lm-proxy
Successfully installed Jinja2-3.1.6 MarkupSafe-3.0.3 PyYAML-6.0.3 ai-microcore-5.3.0 annotated-doc-0.0.4 annotated-types-0.7.0 anyio-4.12.1 attrs-25.4.0 authlib-1.6.8 certifi-2026.1.4 cffi-2.0.0 chardet-5.2.0 charset_normalizer-3.4.4 click-8.3.1 colorama-0.4.6 cryptography-46.0.5 cyclopts-4.5.3 distro-1.9.0 dnspython-2.8.0 docstring_parser-0.16 docutils-0.22.4 email-validator-2.3.0 exceptiongroup-1.3.1 fastapi-0.129.0 fastmcp-2.12.5 h11-0.16.0 httpcore-1.0.9 httpx-0.28.1 httpx-sse-0.4.3 idna-3.11 isodate-0.7.2 jiter-0.13.0 jsonschema-4.26.0 jsonschema-path-0.3.4 jsonschema-specifications-2025.9.1 lazy-object-proxy-1.12.0 lm-proxy-3.0.2 markdown-it-py-4.0.0 mcp-1.16.0 mdurl-0.1.2 more-itertools-10.8.0 openai-1.109.1 openapi-core-0.22.0 openapi-pydantic-0.5.1 openapi-schema-validator-0.6.3 openapi-spec-validator-0.7.2 pathable-0.4.4 pycparser-3.0 pydantic-2.12.5 pydantic-core-2.41.5 pydantic-settings-2.13.1 pygments-2.19.2 pyperclip-1.11.0 python-dotenv-1.1.1 python-multipart-0.0.22 referencing-0.36.2 regex-2026.1.15 requests-2.32.5 rfc3339-validator-0.1.4 rich-14.3.3 rich-rst-1.3.2 rpds-py-0.30.0 shellingham-1.5.4 six-1.17.0 sniffio-1.3.1 sse-starlette-3.2.0 starlette-0.52.1 tiktoken-0.12.0 tqdm-4.67.3 typer-0.24.0 typing-extensions-4.15.0 typing-inspection-0.4.2 urllib3-2.6.3 uvicorn-0.41.0 websockets-14.2 werkzeug-3.1.6

128 Мегабайт, щоб перекладати JSON

Шановний, та ну не 128, а лише 23.9 мегабайтиків зі всім деревом залежностей.

І щодо “перекладати JSON”. Тут, на хвилиночку, повноцінний HTTP reverse proxy / API gateway з маршрутизацією за патернами, streaming-підтримкою, системою контролю доступу (віртуальні ключі, групи), кількома форматами конфігурації та CLI. Для цього потрібен відповідний стек, і він тут цілком стандартний для python-мікросервісу.

Пропоную подивитись на прямі залежності нашого піддослідного:
```
dependencies = [
“ai-microcore>=5.1.2,<6”,
“fastapi>=0.121.3,<1”,
“uvicorn>=0.41.0”,
“typer>=0.24.0”,
“requests~=2.32.5”,
“pydantic~=2.12.5”,
]
```
І для контрасту, прямі залежності litellm, не рахуючи JS-стеку (!)
```
[tool.poetry.dependencies]
python = “>=3.9,<4.0”
fastuuid = “>=0.13.0”
httpx = “>=0.23.0”
openai = “>=2.8.0”
python-dotenv = “>=0.2.0”
tiktoken = “>=0.7.0”
importlib-metadata = “>=6.8.0”
tokenizers = “*”
click = “*”
jinja2 = “^3.1.2”
aiohttp = “>=3.10”
pydantic = “^2.5.0”
jsonschema = “>=4.23.0,<5.0.0”
numpydoc = {version = “*”, optional = true} # used in utils.py

uvicorn = {version = “^0.31.1”, optional = true}
uvloop = {version = “^0.21.0”, optional = true, markers="sys_platform != ’win32’“}
gunicorn = {version = “^23.0.0”, optional = true}
fastapi = {version = “>=0.120.1”, optional = true}
backoff = {version = “*”, optional = true}
pyyaml = {version = “^6.0.1”, optional = true}
rq = {version = “*”, optional = true}
orjson = {version = “^3.9.7”, optional = true}
apscheduler = {version = “^3.10.4”, optional = true}
fastapi-sso = { version = “^0.16.0”, optional = true }
PyJWT = { version = “^2.10.1”, optional = true, python = “>=3.9” }
python-multipart = { version = “^0.0.22”, optional = true, python = “>=3.10”}
cryptography = {version = “*”, optional = true}
prisma = {version = “0.11.0”, optional = true}
azure-identity = {version = “^1.15.0”, optional = true, python = “>=3.9”}
azure-keyvault-secrets = {version = “^4.8.0”, optional = true}
azure-storage-blob = {version="^12.25.1“, optional=true}
google-cloud-kms = {version = “^2.21.3”, optional = true}
google-cloud-iam = {version = “^2.19.1”, optional = true}
google-cloud-aiplatform = {version = “>=1.38.0”, optional = true}
resend = {version = “>=0.8.0”, optional = true}
pynacl = {version = “^1.5.0”, optional = true}
websockets = {version = “^15.0.1”, optional = true}
boto3 = { version = “1.40.76”, optional = true }
redisvl = {version = “^0.4.1”, optional = true, markers = “python_version >= ’3.9′ and python_version < ’3.14’”}
mcp = {version = “>=1.25.0,<2.0.0”, optional = true, python = “>=3.10”}
a2a-sdk = {version = “^0.3.22”, optional = true, python = “>=3.10”}
litellm-proxy-extras = {version = “0.4.44”, optional = true}
rich = {version = “13.7.1”, optional = true}
litellm-enterprise = {version = “0.1.32”, optional = true}
diskcache = {version = “^5.6.1”, optional = true}
polars = {version = “^1.31.0”, optional = true, python = “>=3.10”}
semantic-router = {version = “>=0.1.12”, optional = true, python = “>=3.9,<3.14”}
mlflow = {version = “>3.1.4”, optional = true, python = “>=3.10”}
soundfile = {version = “^0.12.1”, optional = true}
pyroscope-io = {version = “^0.8”, optional = true, markers = “sys_platform != ’win32’”}
```

Красномовно?
Так.

Корисніше було б порівняти споживання RAM у runtime — бо саме це впливає на вартість інфраструктури, а не розмір site-packages.

Ні, не красномовно. Зовсім. Бо:

1. На питання «чому так зроблено» ви кажете «так усі роблять, ось дивіться у сусіда ще гірше» — це не відповідь на запитання, це чорна риторика.

2. Нащо ви приводити у приклад залежності сервера litellm коли мова йде про клієнтську лібу?

3. Якщо ми вже порівнюємо — чим ваше рішення краще ніж litellm?

1. На питання «чому так зроблено» ви кажете «так усі роблять, ось дивіться у сусіда ще гірше» — це не відповідь на запитання, це чорна риторика.

Ну дивістья, можна навелосипедити все з нуля і ми отримаєм нульовий об’єм залежностей, але драматично більшу кодову базу, яка тим не менше, ймовірно впишеться в кілобайти, а не десятки мегабайтів.

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

Є такий критерій, як maintainability.
У довгостроковій перспективі підтримувати велосипед з нуля без жодної залежності буде дорого, а втрата інженера, що це велосипедив, буде фатальною для організації. Тому ми використовуєм якісні industry-standard пакети з відкритим кодом.

І це має певні накладні витрати, як то розмір site packages.

Якщо ваш бекенд розроблений в тій же екосистемі (python), то більшість цих пакетів може бути перевикористана і не скачуватись повторно, бо вони тут ну прям дуже-дуже мейнстрімні для python.

Я відповів на ваше запитання?

2. Нащо ви приводити у приклад залежності сервера litellm коли мова йде про клієнтську лібу?

Не зовсім зрозумів, що мається на увазі під клієнтською лібою.

І LM-Proxy і LiteLLM — імплементації проксі-серверів для LLM, це сервіси, що крутяться на бекенді.

LM-Proxy ви можете також зробити частиною вашого основного бекенд-додатку, використовуючи той же python package в якості бібліотеки, замість розгортання окремого сервісу в вашій інфраструктурі (якщо ваш бекенд написаний на Python).

3. Якщо ми вже порівнюємо — чим ваше рішення краще ніж litellm?

LiteLLM — рішення для enterprise з великим об’ємом власної кодової бази, більшою складністю коду, містить в собі WEB-адмінку, нафаршироване купою різних плагінів.

LM-Proxy — кілька тисяч рядків коду на python, в яких нескладно розібратись для розширення власного функціоналу. Адмінки немає, ніякого UI немає, тільки конфігурація.

Треба швидко запроксювати LLM запити, щоб не зашивати API-ключі від LLM в клієнтський додаток? Вам сюди.

Треба додати наскрізну логіку обробки LLM-запитів, а в код кінцевого додатку лізти не хочеться чи не можеться? Вам сюди.

Треба подебажити, які запити шле 3rd party AI апка до LLM? Втикніть між нею та цільовою LLM LM-Proxy і дивіться.

LiteLLM віджирає всю пам’ять на бюджетному VPS? Вам сюди.

І LM-Proxy і LiteLLM — імплементації проксі-серверів для LLM, це сервіси, що крутяться на бекенді.

ні, див. docs.litellm.ai/docs/#how-to-use-litellm
You can use LiteLLM through either the Proxy Server or Python SDK.

Так це була відповідь на коментар про «клієнтську лібу»

Python SDK має на увазі саме використання на бекенді.

Наприклад, бекенд сервіс, що обслуговує ваш додаток, може включати в себе LiteLLM або LM-Proxy як бібліотеку, замість того, щоб менеджети окремий сервіс.

Ну, тобто, ви звичайно можете і те і те підключити в клієнт/фронт, якщо він у вас на Python, ніхто не забороняє. Але ж навіщо?
¯\_(ツ)_/¯

Так це була відповідь на коментар про «клієнтську лібу»

зрозумів, схоже виникла деяка плутаниця в тому, що ви розглядаєте LiteLLM для побудови server gateway/proxy, хоча більшість використовує LiteLLM як адаптер для уніфікації взаємодії з різними LLM-провайдерами, і саме Python SDK (бібліотека-клієнт) призначений якраз для цих цілей

для задачі сумаризації Claude насправді працює краще.

цікаво, чи можна це якось об’єктивно виміряти...чи це тільки відчуття 🤔
(так розумію суммаризація — це конспектування?)

я неайтівець. вайбкодер, практики мало, а теоретично це питання бачу на даний момент так:

є платформа Google Cloud,
там є щось під назвою Vertex AI,

і там є Model Garden (назва красномовна.. це можливість підключити будь-яку модель (!)... крім джемінаїв, є і антропік, і безкоштовні... і можна вибрати регіон дата-центру... і т.д.)
......

### 1. Як це працює (Механізм)

Замість того, щоб вписувати довгий API-код у змінну, ви просто кажете скрипту: «Дивись у цей файл».

* Ви створюєте **Service Account** у консолі Google Cloud.
* Генеруєте для нього **JSON-ключ** і завантажуєте на комп’ютер/сервер.
* У скрипті або в терміналі вказуєте шлях до цього файлу.

### 2. Чому це зручно (один раз і назавжди)

Вам достатньо додати лише один рядок у термінал (або в початок скрипта), і **всі моделі** з Model Garden стануть доступними:

```bash
export GOOGLE_APPLICATION_CREDENTIALS="/шлях/до/вашого/ключа.json"

```
Після цього код виглядає максимально «чистим» — жодних паролів, токенів чи секретів:

```python
import vertexai
from vertexai.generative_models import GenerativeModel

# Скрипт сам підхопить JSON-файл із системної змінної
vertexai.init(project="my-project", location="us-central1″)

model = GenerativeModel("gemini-1.5-flash") # Або будь-яка інша модель з Garden
print(model.generate_content("Привіт!«).text)

```
### 3. Головні переваги JSON-файла:

* **Універсальність:** Один файл дає доступ і до Gemini, і до розгорнутої Llama, і до сховища файлів (Bucket).
* **Безпека:** Ви додаєте файл у `.gitignore`, і ваш секретний ключ ніколи не потрапить у публічний доступ (на відміну від API-ключа, який часто випадково «засвічують» у коді).
* **Автономність:** Це ідеально для серверів (ботів, сайтів), де немає живої людини, щоб натиснути «Увійти через Google».

---

**Резюме:** JSON-файл — це ваша **безстрокова перепустка**, яку ви один раз показуєте системі, і вона більше не ставить зайвих питань про доступ до моделей.

Хочете підкажу, де саме в консолі Google Cloud натиснути кнопку, щоб згенерувати цей файл?

Підписатись на коментарі