18377
По всем вопросам- @haarrp C# - обучающий канал Senior C# разработчика. @ai_machinelearning_big_data - Machine learning @itchannels_telegram - 🔥лучшие ит-каналы @csharp_ci - C# академия @pythonlbooks- книги📚 Реестр РКН: https://clck.ru/3Fk3kb
Одна функция в C однажды положила заметную часть раннего интернета.
В 1988 году в стандартной библиотеке C была функция `gets()`. Она читала ввод пользователя в буфер, но не проверяла его размер. Если данных приходило больше, чем помещалось, они просто начинали затирать соседнюю память.
Именно это использовал Robert Morris в знаменитом Morris Worm. Червь атаковал `fingerd`, переполнял буфер и заставлял систему выполнять чужой код.
Итог: около 6000 машин были заражены за считанные часы. По оценкам того времени, это была примерно десятая часть всего интернета.
Самое безумное здесь не сам червь, а то, насколько маленькой была причина. Один небезопасный вызов. Один буфер без проверки. Одна функция, которую годами использовали как обычный инструмент.
Позже `gets()` признали настолько опасной, что её фактически выкинули из современного C.
Урок простой: в системном программировании безопасность часто ломается не на сложной криптографии, а на банальном «а что будет, если ввод окажется длиннее, чем мы ожидали?»
#history
🖥 Курс «Git Pro: от первого коммита до уровня senior» - на Stepik
project_final_v2_FINAL_truly_final - знакомо?
Значит, пора.
Большинство разработчиков знают 5 команд Git и боятся шестую. Коммитят в main, гуглят «how to undo» и копируют папку «на всякий случай».
Это не работа - это выживание.
После курса вы:
— делаете rebase, не задерживая дыхание;
— разбираете конфликт на 200 файлов по алгоритму;
— возвращаете «потерянные навсегда» коммиты за 30 секунд через reflog;
— пишете историю, которую не стыдно показать на code review.
Git Flow, trunk-based, Pull Request, защита веток, CI/CD-хуки — всё, что отличает джуна от senior в командной работе.
Скидка 53%, 48 часов: https://stepik.org/course/284799/
В C# 15 появились union types. И это не косметика, это смена подхода к обработке ошибок.
Вы можете явно описать результат как “либо значение, либо ошибка”, прямо на уровне языка.
Без самодельных Result-классов, без лишних обёрток, без попыток использовать исключения там, где они неуместны.
Раньше, ты либо городишь свой Result и работаешь с лишним бойлерплейтом.
Либо тащишь сторонние библиотеки.
Либо кидаешь исключения и теряешь контроль над потоком.
С union types эта проблема решается .
Pattern matching начинает работать так, как и должен. Ты не гадаешь, что вернул метод, а явно разбираешь все варианты.
Код становится проще читать, проще тестировать и сложнее сломать.
⚡️ Версионирование API в Minimal APIs без лишней магии
Если вы пишете API на ASP.NET Core и планируете жить с ним дольше одного релиза, версионирование лучше заложить сразу.
Minimal APIs отлично дружат с ApiVersionSet: можно явно объявить поддерживаемые версии, включить репортинг доступных версий и привязать конкретный endpoint к нужной версии.
В примере создается набор версий API:v1 - текущая стабильная версия v2 - новая версия для развития контракта
После этого endpoint /api/v{version:apiVersion}/workouts/{workoutId} получает привязку через .WithApiVersionSet(apiVersionSet) и явно мапится на .MapToApiVersion(1).
Главная идея простая: версия становится частью маршрута, а не скрытой договоренностью между клиентом и backend.
Это особенно важно, когда API уже используют мобильные приложения, внешние интеграции или несколько фронтендов. Вы можете развивать v2, не ломая клиентов, которые все еще сидят на v1.
Для C# backend-разработчика это один из тех паттернов, который выглядит мелочью на старте, но сильно экономит нервы на проде.
🖥 На Stepik обновили курс «C# с нуля до профи»
Представьте: через четыре месяца вы открываете чужой .NET-проект и читаете его как книгу.
IServiceCollection не вызывает ступора. async Task<IActionResult> пишется на автомате. Вы точно знаете, почему EF Core сгенерировал именно такой SQL - и как переписать запрос, чтобы он летал.
Это не фантазия. Это результат после 16 модулей, в которых каждая концепция объясняется через код и закрепляется практикой.ООП, SOLID, LINQ, async/await, DI, EF Core, ASP.NET Core, Docker, Kubernetes - всё, что казалось магией, станет рабочим инструментом.
А бонусом - портфолио проектов: от CLI-утилит и REST API до собственного SaaS с multi-tenancy, JWT и деплоем в Kubernetes под TLS.
Скидка - 58% доступна 48 часов: https://stepik.org/a/282984/
Никогда не возвращай null для коллекций в C#
Спорное мнение, но на практике это один из самых частых источников багов.
Когда метод возвращает null вместо списка, ты заставляешь каждого, кто его вызывает, писать дополнительную проверку.
Иначе всё ломается на самом банальном месте:
foreach (var transaction in GetTransactions(userId))
{
// boom, если null
}
Enumerable.Empty<T>()
new List<T>()
[] в C# 12
var transactions = GetTransactions(userId)
?? Enumerable.Empty<TransactionDto>();
🔥 Самая недооценённая фича HttpClient в .NET
DelegatingHandler - это middleware для исходящих HTTP-запросов, про который многие забывают.
По сути, ты собираешь pipeline для запросов так же, как в ASP.NET для входящих.
И вместо того чтобы пихать логику в каждый вызов, выносишь всё в одно место.
Авторизация, логирование, ретраи, кеш, аудит, всё навешивается как цепочка обработчиков.
Код становится чище, повторного кода меньше, а поведение запросов контролируется централизованно.
Один раз настроил и это работает для всех клиентов.
Если используешь HttpClient и до сих пор не трогаешь DelegatingHandler, ты реально упускаешь мощный инструмент
Skywork выкатили Matrix-Game 3.0 - и это уже почти живая игровая вселенная, генерируемая ИИ
720p в реальном времени при 40 FPS
5B модель с INT8-квантизацией - работает удивительно быстро
Главный фокус - стабильность во времени:
модель запоминает прошлые кадры и “достраивает” будущее без развалов сцены
Есть и более мощная версия - 28B MoE, которая лучше держит физику и динамику
Как обучали:
Unreal Engine + AAA-игры + реальные видео
Внутри не просто видео, а связка:
Video + Pose + Action + Prompt
за счёт этого можно генерировать длинные, осмысленные сцены
Это зачатки полноценного AI-геймдвижка
Ссылка на модель: https://modelscope.ai/models/Skywork/Matrix-Game-3.0
Нужны твои навыки в работе с ИИ на МТС True Tech Hack 2026!
Тебе сюда, если ты инженер данных, разработчик или системный аналитик. 1 500 000 рублей — общий призовой фонд ИТ-соревнования.
Формат: командное онлайн-соревнование с финалом в Москве. Если нет команды — поможем найти на платформе мероприятия.
Тебя ждут три задачи на выбор — каждая с уникальной технической фишкой.
Для всех финалистов — приглашение на закрытую вечеринку, а лучших участников позовут на стажировку.
Успей зарегистрироваться до 9 апреля
MWS Cloud Platform приглашает на сеньорский митап
Что обсудим:
→ Почему vhost-user обходит virtio-net
→ Когда писать свой балансировщик вместо HAProxy
→ Почему нельзя выбрать один язык для платформы
Поспорим на дебатах Go vs Kotlin — все желающие могут присоединиться и задавать вопросы из зала.
📅 9 апреля, 18:00
📍 Место Санкт-Петербург, Конногвардейский бульвар, 4, Mishka Bar
Для кого: сеньоров-разработчиков, сетевых инженеров и архитекторов облачных платформ
Сложность докладов: 8/10
Места ограничены, регистрация обязательна. 👉
🚀 Почему этот EF Core код тормозит?
Технически - всё ок.
По производительности не очень.
Вот типичная ошибка:
❌ Загружаешь всю сущность (все колонки)
❌ Потом фильтруешь и мапишь уже в памяти
Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение
✅ Как правильно:
Используй проекцию через `.Select()` прямо в запросе:
- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему
📌 Правило простое:
Не тащи всё - бери только то, что используешь
Именно такие мелочи чаще всего дают x2–x10 к скорости.
🚀 Динамические Claims в ASP.NET Core
Хотите добавлять права пользователю прямо во время выполнения приложения?
В ASP.NET Core это можно сделать через Claims Transformation.
Что это даёт:
- можно подтягивать данные из базы или внешнего сервиса
- добавлять их в HttpContext.User
- строить гибкие политики авторизации
То есть права пользователя можно расширять динамически, без изменения токена или повторной аутентификации.
Пример из поста:
Во время трансформации claims добавляется новый claim:
CardType = "platinum"
После этого можно создать policy:
- пользователь должен быть аутентифицирован
- у него должен быть claim CardType = platinum
И использовать её в авторизации.
Это особенно полезно для:
- RBAC (role-based access control)
- сложных систем прав
- динамических разрешений из БД
- feature-based доступа
Например:
- premium пользователь
- платиновая карта
- доступ к закрытому API
- платные функции
Claims Transformation позволяет подмешивать эти права на лету.
Полная статья:
https://milanjovanovic.tech/blog/master-claims-transformation-for-flexible-aspnetcore-authorization
FluentValidation делает валидацию входных данных очень простой.
Знаете ли вы, что валидаторы поддерживают DI (Dependency Injection)?
Можно внедрять объекты настроек, настроенные через DI, и использовать их прямо внутри валидаторов.
Посмотрите пример в коде ниже.
Разработчик показал, как использовать Ollama для извлечения данных из чеков прямо в .NET.
Самая интересная часть оказалась не в том, чтобы отправить изображение модели.
Гораздо сложнее было получить результат, который можно реально использовать в коде.
Обычный текстовый ответ мало помогает, когда нужны структурированные данные:
- позиции из чека
- количество
- цены
- итоговая сумма
Поэтому вместо обычного ответа модель начали просить возвращать JSON.
После этого результат можно сразу маппить в C#-объекты и использовать в приложении.
И именно здесь начинается самое интересное.
Большая часть работы — не код, а правильный prompt.
Если модель:
- округляла цену
- пропускала цифру
- или «придумывала» позицию
приходилось уточнять инструкции.
Это и есть главный сдвиг в таком подходе:
раньше разработчик писал парсеры и regex,
а теперь — настраивает поведение модели через prompt.
Когда модель начинает возвращать структурированные данные,
всё остальное снова превращается в обычный код приложения.
Разбор полной реализации:
https://milanjovanovic.tech/blog/how-to-extract-structured-data-from-images-using-ollama-in-dotnet
🚀 Max
#ai #ollama #dotnet #csharp
Хватит копировать один и тот же unit test
Если тест отличается только входными данными, не нужно плодить десятки одинаковых методов.
В xUnit для этого есть parameterized tests, они же data-driven tests. Вы пишете один тест, а потом прогоняете его на разных наборах данных.
Самый простой вариант - [InlineData(...)], когда параметры можно прямо указать над тестом.
Если данных больше или нужна нормальная типизация, можно вынести их в TheoryData<T> и подключить через [ClassData(typeof(...))].
Смысл простой: меньше копипасты, чище тесты, проще добавлять новые кейсы.
Вместо пяти почти одинаковых тестов - один понятный сценарий и набор входных данных.
Для xUnit это один из тех приемов, который быстро окупается в любом C# проекте.
🖥 Unity 6 заходит в эпоху AI-геймдева: ИИ-агент теперь прямо в редакторе
Unity выкатила в открытую бету AI-помощника, который работает не как «чатик сбоку», а как полноценный агент внутри проекта. Он видит контекст сцены, понимает структуру кода и может сам вносить изменения.
Что умеет:
— Plan Mode: разбивает задачу на шаги, пишет код, ищет ошибки и помогает выстроить архитектуру
— Figma-интеграция: можно дать ссылку на макет, а Unity попробует собрать по нему интерфейс
— Генерация ассетов: текстуры, звуки и 3D-объекты можно создавать по текстовому описанию
— Откат изменений: если агент накосячил, правки можно быстро вернуть назад
Для Pro и Enterprise функции уже доступны. В Personal-версии бету можно попробовать через trial.
Геймдев постепенно превращается в работу не только с кодом и сценами, но и с агентами, которые собирают часть проекта по описанию.
А вместе с этим, похоже, нас ждёт новая волна нейрослоп-игр в Steam.
https://unity.com/ru/features/ai?utm_campaign=unity-ai-beta
Представь обычную ситуацию.
Пользователь заходит в корзину, добавляет товары, что-то удаляет, меняет количество и оформляет заказ.
В базе у тебя остаётся только финальное состояние. Просто запись “как есть сейчас”. Всё, что происходило до этого, исчезает.
Ты не увидишь, какие товары удаляли, где пользователь сомневался, на каком шаге что-то пошло не так. Для отладки, аналитики и аудита это слепая зона.
Пока у тебя простой CRUD на ASP.NET Core и Entity Framework Core, это может не мешать. Но как только появляются бизнес-процессы посложнее, начинаются проблемы. Нет истории - нет понимания.
Здесь пригодится Event Sourcing.
Вместо того чтобы перезаписывать состояние, ты сохраняешь каждое изменение как отдельное событие. Не “корзина сейчас такая”, а “в корзину добавили товар”, “товар удалили”, “оформили заказ”.
События неизменяемые. Ты их не редактируешь и не удаляешь. Только добавляешь новые.
Каждая сущность превращается в поток событий. У корзины своя последовательность: создали, добавили товар, удалили, оформили заказ.
Текущее состояние собирается из этих событий. В
C# это обычно выглядит как агрегат, который проигрывает события и восстанавливает состояние в памяти. Плюс там же проверяются бизнес-правила перед добавлением новых событий.
Для чтения строятся отдельные модели. Например, через Marten или тот же PostgreSQL. Они обновляются сразу или асинхронно и отдают данные в API.
В итоге у тебя не просто таблица с текущими значениями, а полная история системы. Можно откатиться на любой момент, разобрать баг, посмотреть реальные действия пользователя.
CRUD отвечает на вопрос “что сейчас в базе”.
Event Sourcing отвечает на вопрос “что реально происходило”.
И для сложных систем это уже не архитектурный стиль, а вопрос выживания проекта.
Гайд по эвент-сорсингу
🖥 Что выведет код?
Как бы ты почистил этот код?
A - Заменить вложенные if на guard clauses
B - Использовать LINQ для фильтрации и суммы
C - Вынести скидку VIP в отдельный метод
D - Все варианты выше
@csharp_ci
✔️ Rate Limiting по пользователю это must have для любого нормального API.
Если ты до сих пор ограничиваешь по IP, у тебя уже есть проблема. Один шумный клиент может легко положить сервис для всех.
Правильный подход это лимиты по User ID. Ты берёшь идентификатор пользователя из контекста и используешь его как partition key.
В итоге каждый пользователь получает свой собственный лимит, нет конфликтов между клиентами, нет ложных блокировок, и API остаётся стабильным даже под нагрузкой.
Плюс это честно, потому что один агрессивный пользователь не влияет на остальных.
В ASP.NET Core это настраивается буквально в пару строк через AddRateLimiter с FixedWindow или SlidingWindow стратегией.
Главная мысль в том, что rate limiting это не только про защиту от атак, это про контроль ресурсов и предсказуемость системы.
Если у тебя есть публичный API и нет нормальных лимитов, это не вопрос если упадёт, это вопрос когда.
🚨 Тихий убийца производительности в EF Core, о котором забывают почти все.
Запрос выглядит идеально - пока у тебя мало данных. Потом начинается ад.
Ты пишешь обычный Include, потом ещё один на ту же сущность… и не замечаешь, как EF Core превращает это в монструозный SQL с кучей JOIN’ов.
Что происходит под капотом:
EF делает несколько JOIN’ов → получается cross product → строки начинают дублироваться
Итог:
• данных в ответе становится в разы больше
• память улетает
• запросы резко тормозят
И всё это без единой ошибки в коде.
Решение есть и оно банально простое - Query Splitting.
Вместо одного жирного запроса EF разбивает его на несколько аккуратных. Без дублирования, без раздувания результата, без боли.
Одна настройка - и ты экономишь кучу ресурсов на проде.
P.S. Если работаешь с EF Core - такие нюансы решают, будет ли твой сервис летать или умирать под нагрузкой.
🔥 Не понимаешь, что происходит внутри .NET приложения
Значит у тебя нет нормальной трассировки
Минимальный сетап, который стоит добавить почти в любой проект
OpenTelemetry с базовой инструментализацией
ASP.NET Core, HttpClient, EF Core, Redis и база через Npgsql или SqlClient
Этого хватает, чтобы видеть полный путь запроса
от входа в API до базы и внешних сервисов
Дальше всё становится прозрачным
• где тормозит
• где падает
• где утекает время
Для визуализации можно подключить любой стек
• Aspire Dashboard под быстрый старт
• Grafana если нужен прод уровень
• Jaeger для классического трейсинга
• Seq если хочешь объединить логи и трейсы
Seq особенно удобен, потому что нормально работает со структурированными логами
По факту это один из самых дешёвых апгрейдов инфраструктуры, а профит даёт сразу
Гайд
🌟 Микросервисы - это не про то, чтобы в первый же день распилить приложение на десятки сервисов.
Ключевая идея — независимая эволюция.
Каждый сервис:
— владеет своей предметной областью
— хранит свои данные
— разворачивается независимо
Именно отсюда появляются реальные плюсы:
— команды двигаются быстрее
— релизы становятся безопаснее
— система становится устойчивее к сбоям
Но есть и обратная сторона:
— растёт сложность
— появляется больше координации между командами
— сложнее поддерживать консистентность данных
Микросервисы — это не «серебряная пуля».
Это обмен: гибкость и скорость ↔ сложность и операционные расходы.
Поэтому перед тем как идти в эту архитектуру — важно понять базу.
Разбор ключевых концепций и нюансов
🔥 Парень без опыта навайбкодил клон Warcraft с помощью ИИ - индустрия, ты как там?
Обычный парень просто накидывал идеи в Claude и получил полноценную браузерную стратегию.
Что в итоге:
• 9 фракций, ~200 уникальных юнитов
• 50 заклинаний и 70 зданий
• 150 треков, тысячи спрайтов и реплик
• Прокачка с древами развития
• Магия, гарнизоны, торговля, туман войны
• Генерация карт на лету
И это ещё не всё:
• PvP с игроками
• PvE против ИИ (3 уровня сложности)
• Наблюдение за боями ИИ vs ИИ
• Голосовое управление армией
• Работает в браузере — ПК, телефон, планшет
Без регистрации. Просто заходишь в гостевой режим и играешь.
https://www.shardsofstone.com/
⚙️ Как правильно работать с настройками в .NET?
В .NET есть 3 основных интерфейса для конфигурации.
И если выбрать не тот — приложение может просто игнорировать изменения в настройках.
Разбираем просто 👇
1️⃣ IOptions
- читается один раз при запуске
- кэшируется на всё время жизни приложения
- подходит для статических настроек
2️⃣ IOptionsSnapshot
- пересчитывается на каждый запрос
- подхватывает изменения в appsettings.json без перезапуска
- идеален для Web API (Scoped)
3️⃣ IOptionsMonitor
- обновляется в реальном времени
- триггерит событие при изменении настроек
- подходит для фоновых сервисов (Singleton)
Главное правило:
👉 статические настройки → IOptions
👉 веб-приложения → IOptionsSnapshot
👉 фоновые сервисы с реакцией на изменения → IOptionsMonitor
Если используешь не тот интерфейс — можешь долго не понимать, почему конфиг не обновляется.
А это одна из самых частых и незаметных ошибок в .NET.
Подробнее
Как за 2 года стать разработчиком, за которого конкурируют компании?
⚡️ 8 апреля ИТМО в партнёрстве с Яндекс Практикумом проведут День открытых дверей онлайн-магистратуры «Фронтенд- и бэкенд-разработка».
Поговорим про фронтенд и бэкенд, разницу между ними и уровень знаний на старте. Обсудим, какие навыки нужны для поступления, как магистратура помогает быстрее выйти на рынок — и быть на нём конкурентоспособным.
Отдельно разберём сценарии для тех, кто уже работает в IT: как сменить стек, углубиться в текущий или перейти в новые направления, включая работу с ИИ.
На встрече обсудим:
— какой трек выбрать и как выстроить карьеру в разработке
— какую роль играет ИИ в программировании
— как устроена программа и чему вы научитесь
— как проходит обучение и как его совмещать с работой
— как поступить: вступительные испытания и ключевые даты
☁️ Как на самом деле устроены инфраструктурные сервисы Yandex Cloud?
Разработчики Yandex Cloud и Yandex Infrastructure расскажут об этом 16 апреля на встрече для разработчиков, архитекторов и сетевых инженеров.
В программе вас ждут реальные технические варианты реализации и опыт нетривиальных решений разработчиков платформы:
— Инфраструктура как код для управления оповещениями: и никаких проблем
— Развёртывание в ритме танго: как мы заменили оркестрацию процесса установки «хореографией»
— Как мы оптимизируем вывод больших языковых моделей: кэширование, время отклика и ресурсы графических ускорителей
— Как мы строили собственный сервис доставки контента и через что нам пришлось пройти?
— Как мы работаем с уязвимостями на примере современных атак
— Секретный доклад (онлайн трансляция не предусмотрена)
Участники смогут обсудить самые “горячие” вопросы, технические варианты реализации и ошибки с разработчиками сервисов Yandex Cloud и другими участниками.
Когда: 16 апреля офлайн в Москве и онлайн.
Помимо экспертных докладов, офлайн участников ждут развлекательная программа, а тех, кто онлайн: инженерное соревнование в прямом эфире.
Регистрируйтесь, чтобы послушать реальные истории от разработчиков, обменяться опытом и узнать, что скрыто под «капотом» инфраструктурных сервисов, а также какие планы у команды на будущее.
👩💻 Открытый урок «Производительность кода на примере алгоритмов сортировки»
🗓 21 апреля в 20:00 МСК
🆓 На этом открытом уроке мы разберёмся, как скорость работы программы зависит от выбранного способа решения задачи - на понятном примере сортировки данных
🔍 Что вы узнаете на вебинаре:
• Почему существует несколько способов выполнить одну и ту же задачу
• Как разные подходы к сортировке данных влияют на скорость работы программы
• Почему одни решения подходят для маленьких объёмов данных, а другие — для больших
• Что происходит с программой, когда данных становится в 10, 100 или 1000 раз больше
🎯 Кому будет полезно:
• Тем, кто только присматривается к профессии разработчика на C#
• Новичкам, которые начали изучать C# и хотят понимать, как писать «быстрый» код
• Всем, кто хочет разобраться, от чего зависит скорость работы приложений
🔗 Ссылка на регистрацию: https://otus.pw/GAHM/?erid=2W5zFGdMqeZ
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
👩💻 Открытый урок «Как работают структуры данных C# "под капотом"»
🗓 13 апреля в 20:00 МСК
🆓 На этом открытом уроке мы простым и понятным языком разберём структуры данных, что происходит внутри программы, когда она хранит и обрабатывает данные.
Что рассмотрим на вебинаре:
✔ Внутреннее устройство ключевых коллекций
✔ Принципы работы и алгоритмическую сложность операций
✔ Особенности реализации, влияющие на производительность
Кому будет полезно:
✔ Начинающим разработчикам - чтобы углубить понимание платформы .NET и писать эффективный, надёжный код.
✔ Тем, кто готовится к техническим собеседованиям (вопросы о внутреннем устройстве коллекций и сложности операций - классика интервью).
✔ Всем, кто хочет осознанно выбирать структуры данных - избегать типичных ошибок, понимать компромиссы и узкие места.
🔗 Ссылка на регистрацию: https://otus.pw/HE4y/?erid=2W5zFJNNR7F
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
Vector Search - как это работает (и почему это важно для .NET разработчиков)
Vector search ищет смысловую близость, а не просто точные совпадения.
Он сравнивает данные - текст, изображения или аудио - используя векторные эмбеддинги в многомерном пространстве.
То есть система ищет не одинаковые слова, а похожие по смыслу объекты.
Почему это важно?
Vector search лежит в основе многих AI-функций:
- семантический поиск
- рекомендательные системы
- интеграции с LLM
- умные ассистенты внутри приложений
Добавив векторный поиск в приложение, разработчик может создавать намного более умные продукты, которые понимают смысл запросов пользователя.
Это дает реальную бизнес-ценность - от поиска по документам до персонализированных рекомендаций.
📍 Полный пример реализации
🚀 Max