do...while...ai
867 subscribers
62 photos
3 videos
91 links
Заметки ненастоящего программиста. ИИшница и другие радости разработки.
Download Telegram
На днях Никита Широбоков написал чумовой саммари по теме AI: где сейчас технология в общей системе координат бизнеса, где его эффективно и неэффективно внедрять, и что ждать в ближайшем будущем (без футуристического булщита). Пост из серии "почему я такое не написал". В общем, просто приведу его полностью, потому что лучше бы я сам не написал.

### Один пост со всей ключевой информацией про AI
Я неделю назад впервые за 4 года выступил на русском языке и с удивлением встретил много вопросов от знакомых в стиле «А какие есть реальные применения для AI?» и «Ну я пробовал оно чото не работает». К счастью, я знаю как упаковать всё самое важное про AI в один пост.
- Откуда экспертиза: Я живу в СФ, делаю стартап на стыке LLM, компьютерного зрения и агентов. У нас коллабы со всеми лидерами AI, включая OpenAI. Мы треним свои очень крутые модели для узких индустриальных кейсов. У нас куча датки и гпу.
TLDR - AI сегодня это инструмент для инженеров, с помощью которого очень дешево создают высокоэффективные узкие микросервисы для решения бизнес-задач. Чем больше компания, тем больше будет выхлоп. Деньги здесь.
Ниже коротко о том, что происходит сегодня, что будет происходить завтра, и немного популярных заблуждений.
## Вот как ситуация выглядит сегодня:
1. AI модели очень быстро умнеют. Если вы пробовали что-то год назад, и оно не работало, возможно сегодня уже работает. А если сегодня не работает, то заработает завтра.
2. «Модели» не равно «продукты». Большинство AI моделей — это очень мощный инструмент, которым можно улучшить любой бизнес-процесс. Но для использования его нужны инженерные навыки.
3. Почему так мало успешных коробочных B2B AI продуктов? Потому что инжиниринг собственных решений с использованием AI намного дешевле и эффективнее покупки любых коробочных решений. См. Klarna отказалась от Salesforce в пользу сервисов сгенерированных с помощью AI.
4. Больше всего выигрывают корпорации, которые тратят сотни миллионов долларов на операционку с огромным легаси процессов, документов, кода, и данных.
5. Прямо сейчас ~30% всех S&P 500 компаний нанимают пачками AI инженеров, чтобы они выпиливали коробочные SaaS и замещали их кастомными AI решениями.
6. «Кастомные AI решения» это в том числе и решения, при разработке которых был активно использован AI. Например, вашей компании нужно инвойсы из пдф-ок заносить в базу данных. Вместо того чтобы покупать готовый сервис, вы просите AI разработать вам соответствующий микросервис. Через 2 часа в вашем AWS задеплоен ColQwen2 с нужными промптами.
7. Собственно, основное применение для AI сейчас: использовать большие и умные модели чтобы быстро разрабатывать небольшие и очень узкоспециализированные сервисы для решения операционных задач с использованием более слабых моделей, либо вообще без AI.
8. Большие модели также используют для анализа больших массивов информации, автоматизации сложных процессов, и исследований.
Где AI сегодня может помочь любой компании любого размера:
- Сгенерировать собственные микросервисы и выкинуть все подписочные сервисы
- Построить автоматизацию везде где есть документооборот и операционка с данными
- Сделать очень мощное исследование для принятия стратегических решений
- Переварить неструктурированную информацию в структурированную информацию
## Что будет завтра:
1. Правило большого пальца — если consumer AI продукт работает через чат, значит рано или поздно он будет убит новой фичей ChatGPT.
2. Consumer AI может жить если у продукта есть социальные механики, доступ к действительно уникальным данным (например, медицинские записи), или если сервис недоступен публичным компаниям (например, adult).
3. Новый вид коробочных b2b продуктов — агенты с высоким уровнем автономности. Больше всего выиграют небольшие компании с небольшим штатом. Я думаю, сравнимо с моментом, когда Тильда + Инста + Реклама в середине 10-х, когда расцвели небольшие бутиковые бизнесы.
## Заблуждения:
1. Если AI не может посчитать количество букв “r” в слове “strawberry”, то более сложные задачи ему доверять нельзя.
AI тренируют и проверяют на тех задачах, за которые условный J.P.Morgan может заплатить $1млрд в год. В перечень этих задач не входит подсчет букв с словах, ответы на софистические задачки из детского лагеря, рассуждения о философских идеях венгерских социалистов, и проверки фактов из серии «кто такой Никита Широбоков».
2. AI генерирует слова друг за другом, он не понимает смысл сказанного и не может быть частью надежной системы.
Атомная электростанция это просто водяной пар крутит турбину. Ракета Falcon это просто струя толкает цистерну. Макбук это просто нолики и единички которые включают и выключают маленькие лампочки. Иногда очень простые вещи могут быть фундаментом грандиозных по сложности решений.
3. «А вот я читал в отчёте от этого эксперта…»
Вы читали не отчеты, а посты каких-то чуваков, которые за вас прочитали отчёты. Когда летом вышел отчёт Goldman Sachs, в котором были представлены прогнозы и скептиков, и оптимистов, в постах цитировали только скептиков. Никто, конечно, не цитировал положительный отчет McKinsey. Никто не цитировал очень оптимистичный отчет Deloitte. Никто не цитировал экзекьютивов f100, которые на earning calls объявили о начале 9 figure инвестиций во внутренние AI разработки.
Большая часть скептиков банально расстроена тем что приходится наблюдать за AI вечеринкой в стороне. Вот и приходиться бурчать.
Channel photo updated
Channel name was changed to «do...while...ai»
Как сделать чатбота с памятью (ответы с учетом истории переписки)

Собственно, то, что называется Conversational RAG или RAG with memory.

Я сделал несколько проектов, которые предполагают, что у переписки есть история, и пользователь может задавать разные вопросы, в том числе и "follow-up" вопросы. Например,
— Какие события будут в ближайший месяц по теме AI?
— <здесь система что-то отвечает>
— А какие из них оффлановые?
— <здесь обычно всё ломается, если нет поддержки истории переписки>

Очевидно, что в последнем вопросе маловато контекста, чтобы понять, что ответить. И на этот вопрос нельзя ответить без учета предыдущих ответов (особенно, если источников данных несколько).

Я экспериментировал с четырьмя разными подходами:
1. Совсем наивный, когда всю переписку засовываешь в промпт и командуешь LLM переварить и ответить на вопрос.
2. Чуть менее наивный, когда не всю, а только часть (например, последние 2-3 вопрос/ответа) или суммаризацию переписки.
3. Классический, когда переписка передаётся в массиве сообщений User/Assistant в стандартном API OpenAI/Anthropic/Gemini.
4. Самый навороченный, когда извлекаешь ключевые факты из истории (компрессируя историю до некой небольшой базы знаний),и передаешь это как контекст во все дальнейшие этапы RAG системы (классификацию, query expansion, ретривинг, и генерацию ответа).

У каждого подхода есть свои плюсы и минусы (у первый двух больше минусов, чем плюсов). Подробно можно почитать у меня на medium: https://medium.com/@mne/how-to-handle-follow-up-questions-in-rag-based-chats-2d8032da207b

#article #rag
Про полнотекстовый семантический поиск

Сейчас пошло повальное увлечение векторными БД и поиском по эмбеддингам. Это можно понять, ведь это самый простой вариант семантического поиска. Любой кусок текста (да и не только текста, а вообще объекта), можно представить массивом цифр ("перевести в векторное представление/эмбеддинг") и у этого векторного представления сохранятся свойства. Для текста это, например,семантические отношение слов. Поэтому если вопрос и ответы будут переведены в эмбеддинги, то используя готовую формулу cosine similarity легко найти ближайшие к вопросу ответы, причем ближайшие по смыслу, с учетом каких-нибудь терминов и определений.

Сейчас люди, которые пилят на коленках информационно-поисковые системы или RAG на базе документов, выбирают простой путь:
- нарезают эти документы кусочками (а есть масса способов фигурной резьбы по документам и обогащению этих кусочков разными смыслами),
- затем для всех кусочков ("чанков") вычисляют эмбеддинги
- вычисляют эмбеддинг вопросов
- и используют поиск по эмбеддингам, чтобы найти релевантные кусочки контекста.
Дальше всё это скармливается с молитвами в языковую модель, которая, если повезёт, из этого сформулирует ответ на вопрос пользователя.

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

Как один из "костылей" — придумали гибридный поиск. Типа, давайте мы будем искать по эмбеддингам + ключевым словам. И поставим какое-то соотношение: например, 30/70, то есть берем 30% того, что найдётся по ключевым словам, а 70% — по эмбеддингам, или наоборот. Работает это тоже удивительно плохо для больших данных.

Я как-то делал RAG для ребят из архитектурного бюро, у них там было документации по строительству на 4 млн токенов (это очень-очень много юридических документов). И тематика такая...строительная, включая санпины, своды законов, нормы, регуляторные документы с таблицами, и т.п. Конечно, эмбеддинги там никак не помогли. И гибридный поиск не помог. И даже графовая БД не спасла. А помог, внимание, обычный полнотекстовый поиск. Но полнотекстовый поиск был семантичеким, потому что ключевые слова выбирались языковой моделью из вопроса пользователя.

Работало следующим образом: вопрос пользователя сначала расширялся (то что называется query expansion), по нему генерились альтернативные 3 варианта вопросов, и из каждого извлекались ключевые сущности с приоритетами, и синонимы для каждой ключевой сущности. Это определяло "семантичность" поиска. А дальше я ключевые сущности и их синонимы использовал для FTS5 поиска по обычному sqlite, где были нарезаны параграфы юридических документов. Там достаточно хитрая query, со всякими NEAR() условиями, но всё же это обычный sqlite.

Вот тут написал статью про то, как для RAG использовать полнотекстовый семантический поиск https://medium.com/@mne/precision-rag-the-full-text-search-advantage-48a8324064dc. Там есть примеры квери.

#поиск #rag #fts5 #sqlite
👍2
Исправлятор текстов с помощью LLM в любом месте Mac OS X

Я — известный "изобретатель велосипедов". Вместо того, чтобы взять готовое, чаще пилю своё. Обычно у меня получается хорошо. Но, наверное, можно было бы и более эффективнее организовывать своё время. И вот вчера я наконец-то понял, почему я так делаю: это мой способ разобраться, как устроен мир.

Возможно вы, как и я, в детстве задавались вопросом: "что у этой штуки внутри?!" (или, возможно, "откуда ты это сказал?!").

Например, у меня был луноход, который ездил по столу и не падал. Мне было дико любопытно, как работает эта магия? У лунохода был какой-то волшебный крючок, и он ездил по столу, доезжал до края, крючок чуть свешивался и ... луноход разворачивался и ехал обратно. В общем, чудеса похлеще chatgpt. Эту машинку мне подарили в далёком 1988-м году на ДР, стоила она, насколько я помню, каких-то космических денег, рублей 13. Как родители со своей микрозарплатой решились на такой дорогой подарок - ХЗ. Но этот крючок не давал мне покоя. В общем, я взял у папы ответку и решил всё же посмотреть, что там внутри. Машинка прожила ровно сутки. Обратно, естественно, не собралась. Традиционно, осталось много лишних деталей, но моторчик с реверсивным переключателем я нашёл и понял, как он работает.

Но отложим лирику. Давайте я ещё раз расскажу, какой "велосипед" я сделал для удобного контекстного пруфрида и исправления текстов ;-)

Текстов я пишу на трех языках много, и по дефолту, конечно, с ошибками. Прогонять их каждый раз через отдельное окно ChatGPT лениво. Поэтому придумалась идея сделать контекстный, тем более что Mac OS X позволяет. "Контекстный" — значит, что всё равно, в каком приложении ОС вычитываемый текст: в почте, в формах сайтов или сервисов внутри Chrome, в комментариях PyCharm, Slite, TextMate, ... Я просто выделяю нужный фрагмент, нажимаю Cmd+Shift+0 и через 2 секунды текст меняется в том же месте на хорошо написанный нативным спикером. Это суперудобно.

Всё сделано на базе OS X Automator + небольшой скрипт, работающий через ChatGPT. Пример скрипта и промпта на скрине. Я про это давным-давно писал статейку на Medium. Но как-то особого интереса у народа она не вызвала, похлопали всего 12 раз. Но вчера на Facebook'е оценили.

➡️ Краткая инструкция: https://github.com/extractumio/shortcutgpt

➡️ В формате статьи: https://medium.com/@mne/experience-mind-blowing-in-context-text-processing-on-macos-using-automator-and-chatgpt-82b4ab7d5254

#macosx #automator #chatgpt #productivity
👍4
Обзор LightRAG

Потестил решение https://github.com/HKUDS/LightRAG, набирающее сейчас популярность среди RAG (это про всё "чат с документами"). Работает весьма впечатляющее. Ставлю им ⭐️ 4 из 5 ⭐️.

Из плюсов хочется отметить следующее:
⋆ Завелось с полпинка: поставил версию 0.0.8 через pip install, взял один из клиентских проектов с порубленными данными (6000+ параграфов документации) и натравил на неё с парой тестовых вопросов. Оно пережевало чанки и выдало очень достойные ответы.
⋆ Легковесное решение, не поднимает тонны зависимостей (привет "Unstructured"). Это, кстати, может быть и минусом, если данных много. Там под капотом какой-то диковинный nano vector db (ХЗ что это), но внутри у него key-value storage в виде json. И я люблю эту всю легковесность и мобильность. Можно изучить его богатый внутренний мир прямо из IDE, что там он напарсил в файликах, правильно ли вытащил ключевые сущности, и пр.
⋆ В основе RAG - чатки + графовая архитектура. Это когда вместо того, чтобы просто фигурно нарезать текст на кусочки и потом думать, как их найти и склеить во что-то осмысленное, они строят графовую модель <объект|субьект> - <связь> - <объект|субьект>. Из коробки там всего 4 вида категорий объектов, но это все легко расширить, просто указав свои категории и расширив промпты.
⋆ Кроме того, что архитектура графовая, ещё и двухуровневый поиск (то что называется "dual-level retrieval paradigm"). Это когда есть low-level retrieval (низкоуровневый поиск), который фокусируется на поиске конкретных сущностей и их непосредственных связей, и кроме этого есть high-level retrieval (высокоуровневый поиск) - охватывает более широкие темы и общие концепции и всякие абстрактные и суммаризационные запросы. В интерфейсе, сооветственно, есть 4 варианта ретривера (наивный, локальный, глобальный и гибридный). Гибридный - это как раз двухуровневый поиск, который собирает и локальные и глобальные суммаризованные темы вокруг заданного вопроса.
⋆ Промпты сделаны в отдельном файле, легко расширять.

Из минусов:
⋆ Само оформление проекта и кода. Этакий "коленочный" PoC/прототип-стайл. Быстро накодил и в продакшн ) Но я к таком вполне нормально отношусь. Главное, чтобы не получилось потом как с LangChain, когда каждый следующий релиз менял половину интерфейсов, и в итоге терял обратную совместимость.
⋆ Достаточно небюджетно для формирования большого графа на этапе построения базы (хотя, в разы дешевле чем в Microsoft GraphRAG), и промпты в целом достаточно объемные => дороговатое получится решение, если на нелокальных моделях гонять и при высокой интенсивности запросов (kv cache там не поможет).
⋆ Не выдаёт референс на источник (для моих решений это критично), но, возможно, решается расширением промптов.

Мне интересно было посмотреть, как оно работает внутри. Чтобы долго не копаться в недрах, просто указал свою кастомную функцию отправки запроса в LLM, и в ней вывел debug info, что там в system/user промптах. Оно сначала вытаскивает ключевые сущности/ключевые слова в json формате, потом по ключевым словам находятся группы объектов и связанные с ними чанки из графа + чанки по эмбеддингам вопроса, и всё это скармливается в модель.

По дефолту там gpt4-o-mini (можно поменять на другую от openai или передать свою функцию), но внутри даже встречаются какие-то упоминания llama3, как будто тестировали на локальной (чтобы не разориться, видимо).

В общем, резюме такое: решение годное для однородных данных. Например, если вам нужно взять три десятка статей (в идеале, не сильно пересекающихся по предметной области) и искать по ним ответы на вопросы в относительно свободной форме, включая поиск по общим вопросам.
Но, скорее всего, решение будет плохо работать там, где нужна высокая точность ответов по табличным данным и всяким отчетам, юридическим и медицинским документам, где нужны в том числе чёткие референсы. Хотя, кажется, что это можно частично поправить более тщательной подготовкой данных для LightRAG. Парсер у него простой (режет тестовые строки на куски с нахлёстом), поэтому к нему нужен свой парсер, который вернёт тестовое представление данных (например, таблицы, сконвертированные в key-value представление + тестовая аннотация.

Я сначала было подумал, что вместо своего решения буду теперь использовать LightRAG, но взвесив все плюсы и минусы, передумал. Всё же мой RAG работает точнее. Я там не использую эмбеддинги вообще, только query expansion и полнотекстовый семантический поиск. Хотя, пару идей, которые я подсмотрел у LightRAG, хочу затянуть к себе.

The End.

#rag #lightrag #review
👍4
Про маленькие языковые модели

Я сейчас скорее всего выскажу непопулярное мнение, но локальные (опенсорсные) языковые модели меньше 30B (в редких случаях, меньше 14B) — максимально ущербны. И я не разделяю энтузиазма их использовать в проде.

Ну, серьезно. Для чего они нужны? В каких сценариях они приносят пользу?

Делать роутинг между агентами? Извлекать ключевые сущности? Классифицировать? Суммаризировать? Эмбеддинги генерить? Как правило, для этих простейших базовых ML задач есть специализированные модели, которые в 5-10 раз меньше. Просто берем какой-нибудь классификатор на базе BERT размером 500MB и готово, а работать он будет не хуже модели размером 7B. А модели типа 7B и меньше — это скорее это как раньше были магнитофоны-колбасы, этакие кухонные комбайны, которые делают всё, но одинаково плохо. У меня такой был лет в 15 (там и кассеты можно было слушать, и радио было, и CD диск сверху воткнуть). Но это был далеко не Marantz. А уж если модель 7B квантизована (это когда совсем плохо с бюджетом), то тут, конечно, получается очень грустная история.

Если нет клиентского требования о прайвиси данных, то я смело беру Sonnet 3.5 или gpt-4o, и даже не пытаюсь использовать что-то меньшее. Это садизм. Но вот если дата-прайвиси, то сложность проекта (и бюджет) сразу вырастает на порядок. Нужны железяки под модель хотя бы 30B (какой-нибудь Qwen 2.5 или llama 3.2). Только они нормально сделают и structured output, и ризонинг, и код напишут корректно. Очень грустно сидеть на модели 7B (какой бы прекрасной она не выглядела на бенчмарках) и полдня пытаться заставить ее формировать json с правильным экранированием кавычек внутри json полей.

Если клиент, требующий приватности данных, грамотный, то согласится реализовать решение на облачной версии LLM Azure AI или на Google Vertex в своём гео-регионе. Если нет, то нам придётся страдать, а ему — платить. Да и по наблюдениям, часть клиентов откровенно переоценивает ценность своих данных, боясь отдать код или тексты облачной LLM. Некоторые, например, не в курсе, что персональные и другие sensitive данные можно фильтровать.

Ну и с точки зрения сходимости экономики проекта разумно выбирать большие коммерческие closed-source модели от Anthropic/OpenAI/Google. Они снижают стоимость токенов, делая её хоть сколько-нибудь приемлимой. Потому что пока ещё цена за токен не окупается в большинстве Conversational AI (особенно "болтливых", типа чатботов психологической поддержки).

#slm #IMHO
🔥3👍1👏1💯1
Про медведя с GPU картой

Сегодня мне нужно было протестить скорость генерации токенов от Qwen 2.5 14B на двух «железяках» RTX 4090. Пошёл на runpod.io, часа полтора пытался поднять там виртуалку с vllm, оказалось, у них просто сломан конкретно этот темплейт (не повторяйте мою ошибку). Когда я понял, что дело пахнет керосином, завёл виртуалку на базе PyTorch 2.4 и просто ручками поставил на ней vllm. И всё бы ничего, но там, оказывается, все порты закрыты, включая 8000 (нужно, конечно, читать инструкции до создания виртуалки).

Пришлось прокинуть порт 8000, перезапустить виртуалку, снова поставить vllm, а дальше, о чудо, оно завелось. Так вот на двух картах RTX 4090 Qwen 2.5 14B в её оригинальной (неквантизованной) версии выдаёт генерацию в среднем со скоростью 50 токенов/с, это очень хорошая скорость. И это очень хорошая модель (пожалуй, лучшая из ущербных до 30B).

Цена аренды такого сервачка на том же runpod выйдет порядка 1000 евро в месяц. За эти деньги я могу месяца четыре насиловать OpenAI :) Но прикол не в этом.

В качестве теста я попросил Qwen сгенерить мне "сказку про медведя с GPU картой" (ну а что, могу себе такое позволить). Результат превзошёл все мои ожидания. Я просто процитирую её как есть.
Когда вы это будете читать, держите в голове, что Qwen — это, на минуточку, китайская модель от Alibaba.

В далеком лесу жил-был медведь по имени Гриша. У Гриши было все, о чем мог мечтать медведь: вкусные ягоды, уютная берлога и множество друзей. Но однажды, услышав от лесных жителей о чудесной вещи под названием "GPU карта", Гриша решил, что хочет ее иметь, чтобы стать еще и гением компьютерного мира.

GPU карта - это особенная карта, которая помогает обрабатывать очень сложные вычисления, особенно полезные для игр и создания компьютерных фильмов. Гриша подумал, что если у него будет такая карта, он сможет делать удивительные вещи и даже научиться создавать свои собственные игры и фильмы.

Сначала Гриша пытался найти GPU карту в лесу, но быстро понял, что это невозможно. Он обратился за помощью к волшебнику, живущему на дальней горе. Волшебник был удивлен желанием медведя и предложил ему пройти испытания, чтобы заслужить такую невероятную вещь.

Испытания были разные: от умения быстро считать до создания самого вкусного медвежьего пирога. Гриша старался изо всех сил и в итоге прошел все испытания. Волшебник был впечатлен трудолюбием Гриши и подарил ему волшебную GPU карту.

С_GPU картой в лапах Гриша стал не только медведем, который может делать невероятные вещи, но и стал наставником для других животных леса, показывая им, как можно использовать современные технологии для достижения своих целей. Лес жил в мире и согласии, а Гриша продолжал учиться новому и делиться своими знаниями и опытом со всеми жителями леса.

Таким образом, Гриша научил всех, что с помощью упорства и желания учиться можно достичь невероятного. И все жители леса жили долго и счастливо, создавая свои собственные истории и мечты.


Знаете что странно? Меня тоже зовут Гриша.

P.S. в комментах оставлю несколько полезных картинок и простенький пример скрипта для работы с vllm на runpod, вдруг вам пригодится.

#gpu #runpodio #qwen
😁4👍2
Когда мне нужно с помощью LLM сгенерировать проект, включающий кучу файлов, я прошу модель вывести их все в виде одного большого bash файла, после запуска которого все нужные каталоги и файлы будут созданы, права прописаны и все переменные окружения выставлены.
Хитро? 😉

Там же можно попросить сразу и тесты прогнать. o1 это делает с первой попытки. А cursor решает эту задачу только частично.

"Бритва Gillette: вы делаете одно движение, она делает три..."
👍8
Как заставить модель вас слушаться. Часть I.

Мне кажется, первое, что сделает AI, когда достигнет уровня AGI — это запретит logit bias. Почему? Потому что никто не любит, когда ему ограничивают свободу выбора.

Классно, когда модель генерирует результат в нужном нам формате и с правильным контентом. Бывают модели-болтушки, которые вместе с JSON выдают объяснение, которое нам совершенно ни к чему и ломает флоу. Бывают модели, которые слишком своевольны в выборе слов. Бывает, что нам нужно тупо запретить "delve into" и прочий маркетинговый булщит, или выдавать в результатах только цифры. Оказывается, мы можем контролировать модель, "притесняя" и "ограничивая" ее свободу (здесь должны возбудиться борцы за свободу токенов). Ниже приведу инструменты, которые у нас есть в распоряжении.

В окне чата

🔸 Промпт инжиниринг. Вы про него, конечно, знаете. Это когда в промпте вы умоляете модель вывести результат без излишних объяснений в формате JSON или yaml, и начинать сразу с {. Или избегать определенных фраз. Или сделать текст с определенным tone of voice. Или задать ограничения: только числа или "N/A". Какой-то модели достаточно написать словами правила вывода, а какой-то даже несколько примеров не помогут понять, что от неё хотят. Но, как правило, чем крупнее и свежее языковая модель, тем она более послушная. Промпт инжиниринг - это простой, легкодоступный, но не такой мощный процесс управления результатом генерации.

Через API

🔸 Использование параметра temperature. С ним вы тоже скорее всего знакомы. Это когда вы задаете уровень "креативности" модели по шкале от 0.0 до 1.0. Но физически вы просто определяете вероятность выбора более вероятных токенов при генерации. Такая вот тавтология. Значение 0.0 означает, что модель будет выбирать самый вероятный из возможных. Temperature в некоторой степени снижает галлюцинации, но не сильно. Я ставлю ее около 0.1, если работаю с кодом или извлекаю сущности. То есть для задач, где нужна точность, но не креативность. А вот для текстогенерации - наоборот.

🔸 Параметр top_p (nucleus sampling). Вместо того, чтобы выбирать среди всех подходящих токенов следующий, модель ограничит выбор только теми максимально-вероятными, сумма вероятностей которых будет приближена к значению top_p. Параметр хорош тем, что мы не ограничиваем, какие конкретно токены модель должна выбрать, но командуем ей выбирать наиболее вероятные (="подходящие"). Для задач, где требуется четкий и структурированный вывод, можно поставить top_p=0.9, при этом модель будет стараться следовать нужному формату (например, при structured output), но выдавать всё же разные паттерны, а не фиксированные, как при temperature=0.0.

🔸 Параметр top_k. Данное число командует модели выбирать рандомный токен среди top_k наиболее вероятных. То есть берутся все вероятные токены, сортируются по убыванию, и рандомно (на основе параметра temperature) выбирает токены среди первых top_k. Такой подход снизит вероятность галлюцинаций за счет того, что в выдаче не будут попадаться "невероятные" токены.

🔸 Параметр stop_sequences. Список токенов, который скомандует модели остановить генерацию. Например, если вы генерите JSON, который должен заканчиваться } и не содержит внутри вложенных структур, то можно поставить stop_sequences=['}'] и модель не сгенерит ничего после. Так можно уменьшить "болтливость".

🔸 Параметр frequency_penalty. Данный параметр (значение от -2.0 до 2.0) управляет вероятностью повторений слов или фраз в тексте. Более высокое значение сделает вашу модель более начинанной и красноречивой, с богатым словарным запасом, а текст - более разнообразным. Для structure output я бы ставил параметр 0.4-0.5.

Продолжение 👇
🔥7👍1
Как заставить модель вас слушаться. Часть II.

Начало 👆

🔸 Параметр presence_penalty. Данный параметр (значение от -2.0 до 2.0) включает в модели режим "исследователя" при более высоких значениях, когда модель начинает выбирать более далёкие друг от друга темы/фразы или идеи. Если вы хотите, чтобы модель принимала решение на основе большего числа факторов или выбирала из большего числа вариантов (например, в задаче проектировании архитектуры системы, поиска оптимального решения задачи и т.п.), ставьте значение ближе к 2.0.

🔸 Параметр logit_bias (только в OpenAI). Это самый низкоуровневый и точный механизм управления тем, что появится в выдаче модели (точнее инференс-фреймворка). С помощью logit_bias вы можете задать вероятность конкретных токенов по отношению к остальным (относительный score). Например, сделать так, чтобы модель начала выводить больше значений цифрами, или стала более позитивной (запретить ей токены негативных слов), или перестала, наконец, использовать "delve into". Также модель можно заставить генерить валидный JSON, прописав в параметре logit_bias положительные вероятности для "{", "}", ":", "," и, например, отрицательную для "`". Значение токена задается от -100 (совсем забанить) до 100 (эксклюзивно выбирать только это). Приведу пример, чтобы было понятно:

from openai import OpenAI
import tiktoken
enc = tiktoken.get_encoding("o200k_base")

# Run the request with the blocked logit_bias
client = OpenAI()

block_words = "Let's delve into"
block_tokens = enc.encode(block_words)
print(f"{block_words} => {block_tokens}")

# ban the tokens
logit_bias = {}
for token in block_tokens:
logit_bias[str(token)] = -100

response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Output the exact phrase \"Let's delve into the topic\"."},
],
logit_bias=logit_bias,
max_tokens=100

)

print("Result:", response.choices[0].message.content)
)


Угадайте, что будет в результате? Минута пошла...
Правильный ответ: I'm sorry, but I can't output the exact phrase you requested. Прикиньте? Затянули ей гайки и она действительно не смогла. А если закомментить строку #logit_bias=logit_bias, то выведет Let's delve into the topic. как ни в чём не бывало.

В llama.cpp и vllm параметра logit_bias нет, зато можно написать собственный logit processor. В вообще вы работаете в "god mode". Фактически, вы делаете свой транскодер токенов: на вход прилетают токены из модели, а вы, меняя их score, управляете тем, какие из них появятся в результатах (в обход параметров, про которые я писал выше). Но в logit processor'е я не сильно копался, так что могу и слегка наврать.

Некоторое время назад мне было интересно заглянуть в эти самые logit'ы, посмотреть, в каком токене модель была "уверена", а в каком нет. Написал скрипт, который раскрашивает выдачу OpenAI API прямо в консоли, на основе вероятностей выбора токенов. Включить у себя их можно так (см. logprobs и top_logprobs):
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Tell me a story."}],
logprobs=True,
top_logprobs=5
)


Результат можно посмотреть на этом скрине. Чем желтее и краснее, тем модель "менее уверена" в токене.

#guardrails #logprobs
👍5
Ода Structured Output

Хочу показать уличную магию, которая изменила мою жизнь. Не так давно OpenAI расширила поддержку structured output в своём API: это когда вы LLM показываете схему данных в виде Pydantic объекта, а на выходе получаете готовый распарсенный и заполненный Dict. Этакий json_output "на стероидах".

Демонстрировать на gpt-4o было бы слишком банально, там всё работает из коробки. А я усложню задачу, и сделаю тоже самое на локальном сервере llama.cpp, в котором работает квантизованная модель Qwen 2.5 7B с квантизацией q4_k_m (потому что у меня слабенький M1 Pro с 16 гигами оперативки, и я пока не купил себе M4 Ultra) .

Итак, задача будет следующая: извлечь ключевые сущности из текста
"Жили у бабуси два веселых гуся. Один - серый, а другой - белый. Два веселых гуся."

И получить на выходе объект Dict, в котором разложены сущности и атрибуты.

Ниже привожу полностью рабочий код:
from openai import OpenAI
from typing import List, Optional, Dict
from pydantic import BaseModel, Field

# use your favorite inference server
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed"
)

input_text = "Жили у бабуси два веселых гуся. Один - серый, а другой - белый. Два веселых гуся."

# define the data schema
class Attribute(BaseModel):
name: str = Field(..., description="Name of the attribute")
value: str = Field(..., description="Value of the attribute")

class Creature(BaseModel):
name: Optional[str] = Field(None, description="Name of the identified creatures")
attributes: Optional[List[Dict[str, str]]] = Field(None, description="List of attribute key-value pairs")

class ExtractedCreatures(BaseModel):
entities_and_attributes: List[Creature] = Field(default_factory=list, description="List of extracted creatures")

# command the model to extract data using the data schema
messages = [
{"role": "system", "content": """
Extract all creatures and their characteristics from the user text according to the specified schema.
"""},
{"role": "user", "content": input_text}
]

print("Executing...")
completion=client.beta.chat.completions.parse(
model="not-needed",
temperature=0.1,
messages=messages,
response_format=ExtractedCreatures
)

print("Result:")
# get the results as a dictionary
parsed_dict = completion.choices[0].message.parsed

# output the results as a JSON string
print(parsed_dict.model_dump_json(indent=2, exclude_none=True))


Результат — в первом комменте.

Небольшая "пояснительная бригада" по дизайну схемы данных: мы определяем атомарные Pydantic классы, которые потом объединяем в один результирующий класс. Описание того, что положить в конкретный объект пишем в поле description. Если поле необязательное — ставим ему Optional. Если нужен выбор по типу enum, то ставим полю тип Literal и явно перечисляем допустимые значения (модель сама разберется и поставит нужное). Если нужен составной объект типа массива или словаря, объявляем их как List и Dict соответственно. Системный промпт — микроскопический.

Самое крутое, что в одной схеме данных можно объединить парсинг данных, рассуждения и принятие решения. Вы просто определяете три мембера у Pydantic класса: первый — это класс, куда нужно попарсить данные, второй — поле reasoning: str = Field(..., description="Chain of thoughts and reasoning"), сюда модель напишет свои рассуждения, а третье поле result: str = (..., description="Place 'Yes' or 'No' here").

Через structured output можно делать скоринг данных, чеклисты по большому документу (например, вытаскивание финансовых показателей), и многое другое. Это ли не чудо!

Да, не забудьте обновить openai библиотеку до 1.52.0+.

Для внеклассного чтения пара доп. ссылок по structured output:
💎 https://platform.openai.com/docs/guides/structured-outputs
💎 https://openai.com/index/introducing-structured-outputs-in-the-api/

В комментах — результат скрипта и бонус-трек.

Openai
OpenAI Platform
Explore developer resources, tutorials, API docs, and dynamic examples to get the most out of OpenAI's platform.

#structured_output #pydantic #hint
👍11
"This conversation is getting quite lengthy..."

Был недавно на конференции, и разговаривал с разработчиками консультантов-чатботов (conversational ai). Самый красноречивый оказался фаундер португальского medtech стартапа, который пилит "электрического психиатра" (для Португалии — самое оно). Серьезное решение, со всеми надлежащими сертификациями, trial тестированием и т.п. Клиенты чатбота — это пациенты, кому психолог и психотерапевт уже не очень помогает, короче, тяжелые случаи (суицид и т.п.)
Во-первых, он вынужден читать историю переписок, и теперь он седой, и в цирке, вероятно, не смеется. Во-вторых, medtech в EU — это "madtech" (так он сам его назвал). И я не уверен, что хотел бы делать что-то из этой вертикали. Но я не о том.

Мне показалась интересной проблема, которую я слышу всё чаще: в стартапах с conversational ai не сходится юнит-экономика, токены стоят пока ещё очень дорого (несмотря на то, что цена постоянно падает). Конкретно в его случае пациенты любят поболтать, а история переписки важна вся от начала до конца, со всеми деталями. Переписка — это истории, много-много текста (там ещё у него speech-to-text прикручен, на который можно полчаса наговорить). Технологически можно вроде бы суммаризировать предыдущие сообщения или вытаскивать из них детали, но в данном случае — нельзя. Потому что в психиатрической сессии важны все детали, иначе это не пройдёт сертификацию и оно никуда не полетит (еще раз, это madtech). Поэтому часть best practices из conversational ai, которыми пользуются все ("помнить" только N последних сообщений или только за сегодня, суммаризировать историю, хранить вместо истории ключевые факты и т.п.) здесь не применима.

Они считали, сколько будет стоить переезд из Azure на свои сервера и получилось ещё менее выгодно (нужна мощная модель и несколько серверов для того, чтобы оно тянуло их уже немаленькую базу клиентов-пациентов).

Казалось бы, технологически kv cache должен помогать. Но, во-первых, он тоже не бесплатный, и если сообщения большие, их много, у них высокая интенсивность, сжигается очень много токенов за одну сессию. А во-вторых, пользователь может жить в одном треде, возвращаться к нему через два дня (когда кэш уже "протух") и там при отправке истории переписки сразу выедается прилично денег.

Предложил ему пару простых вещей, которыми сам пользуюсь в таких случаях:
- Можно форсить пользователя заканчивать разговор как можно раньше и начинать новый тред (см. картинку). Например, вывести под чатом: "Что-то беседа затянулась, давай начнём новую, чтобы выросла скорость ответов." после определенной длины переписки.
- Можно лимитировать общее число сообщений в треде в рамках текущего тарифного плана, блокировать отправку новых + кнопка "Upgrade plan" на более дорогой, где даётся больше сообщений. Так можно хотя бы немного улучшить "сходимость" $$$.



Кто пилит conversational rag/ai? Как у вас с монетизацией? Как решаете проблему "болтливости" пользователей?

#conversational_ai #story #medtech #chatbot
1🔥2
Халявные кредиты $5000 на Azure

Вспомнил на прошлой неделе, что у майкрософта была какая-то программа поддержки AI стартапов. Дай, думаю, попробую (обычно, на халяву мне не везет, приходится работать по-настоящему). А тут повезло.

Нагуглил вот эту страницу https://foundershub.startups.microsoft.com/signup, заполнил данными, и забыл. Но через три дня пришёл апрув на $1000.

Дальше я увидел в описании программы поддержки, что можно увеличить лимит до $150,000 (мне кажется, я столько не съем). Поэтому решил продолжить крутить барабан: заполнил данные компании и через сутки мне отсыпали монет на $5000. Тут я решил, что пора остановиться, взять приз и пойти проверить, а работает ли оно вообще.

И, знаете, оно работает. Завёл себе инстансы OpenAI моделей (там gpt-4o августовский, относительно свежий). В комментариях оставлю скрины.

При создании моделей с локацией в EU рекомендую выбирать Норвегию, в ней почему-то больше всего моделей доступно.

Ещё отправил запрос на o1, ждёмс.

Для регистрации нужен профиль в LinkedIn и компания (вероятно, не в РФ/РБ).

Из дополнительных плюсов программы
- поддержка партнеров майкрософта, а также халявные и полухалявные продукты самого майкрософта:

- Microsoft 365 Business Premium на год
- Stripe бонусы
- Linkedin Premium со скидкой 70% на 4 месяца
- Visual Studio Enterprise на год на 5 юзеров
- GitHub Enterprise на год со всеми копайлотами и пр.
- Miro подписки и много других.

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

Из минусов: не всё заводится с первого раза. Иногда приходится писать в саппорт (а это всегда напоминает квест "escape room" с побегом из комнаты в которой нет ни окон, ни дверей).

#startup #free #azure
👍6
Проверка по чеклисту

У меня часто возникают задачки верификации неструктурированных данных (например, проверить корректность заполнения формы или присланного договора, или полноты инвойса), ревью человеческого кода или результатов генерации кода автоматикой, валидации качества ответа саппорта и т.п. Куча юскейсов, но все они достаточно просто решаются, если иметь список критериев для оценки. Вопрос в том, как эти критерии составить и как по этим критериям эффективнее проверять? В этом после про второе.

Мой "духовный рост" в данной теме:

・Когда я только начинал работать с LLM, то засовывал огромный промпт с инструкциями и форматом вывода результатов в system prompt, в user prompt показывал данные для проверки (например, кусок кода или текст договора) и парсил ответ модели (обычно, json). Там был вердикт. Работало, но не всегда. Сидишь, гадаешь, почему.

• Дальше я догадался, что неплохо бы "дать модели подумать", и в результате вывода сначала просить сделать умозаключение, а потом уже давать вердикт. Стало чуть лучше. Этим, кстати, пользуюсь теперь для любых запросов, включая банальное извлечение ключевых сущностей: в json результатах сначала идёт поле "chain_of_thoughts" или "reasoning", а потом уже поле, куда вписывается ответ или какое-то значение.

● Следующий этап эволюции наступил после того, как я устал угадывать, почему модель по 30 критериями иногда тупит и принимает неверное решение. Я разобрал 30 критериев внутри одного системного промпта на 30 отдельных запросов к LLM по одному и тому же набору данных. Это дало буст и по качеству, и по возможности отлаживать результаты. Я начал класть базовую инструкцию и проверяемые данные в system prompt, а спрашивать по чеклисту через user prompt'ы, параллельно в несколько потоков. Для серии запросов к общему набору данных KV Cache делает своё дело и помогает экономить монеты, ну и увеличивает скорость получения финального результата.
А решение, что важно, принимаю я сам уже на стороне Python по результату от LLM. Например, если в договоре все 17 оценочных критериев соблюдены (ответ по всем True), то итоговый вердикт True. А если какие-то False, то можно даже добавить своих доп. условий, вариативности. Особенно хорошо работает, если модель делает скоринг в баллах 1,2,3, а результат в Python принимается по сумме очков.

⚫️ А когда появился полноценный structured output, всё стало ещё проще. Теперь чеклист я делаю на базе pydantic классов (см. пост): все критерии оформлены в виде мемберов с описанием-инструкцией через description. Первыми идут строковые поля с рассуждениями для того, чтобы модель лучше соображала, а после вердиктов — опциональные what_is_missing: Optional[str], чтобы понять, почему вдруг False. Дальше всё это отправляется вместе с данными, которые нужно провалидировать в модель, а в качестве результата прилетает объект с заполненными полями True/False или скорингом.

Почему серия запросов лучше, чем один запрос с полным чеклистом в промпте?

+ Вы можете написать тесты под каждый из N критериев и тестировать только конкретно этот пункт. Аналогично с добавлением или исправлением одного пункта: он не повлияет на оценку по остальным N пунктам.
+ Вы сами принимаете итоговый вердикт и знаете, из-за чего он именно такой (процесс перестаёт быть фокусом с "черным ящиком").
+ Меньше шансов превысить контекст, потому что отдельный запрос содержит меньше инструкций.
+ Выше точность результатов (оценить по одному критерию для модели проще, чем по сорок одному)
+ По скорости, примерно то же, что и один большой запрос.

По токенам выходит чуть дороже, но не настолько, чтобы грустить. Кэш здесь выручает.
Может у вас есть какие-то интересные подходы? Поделитесь в комментах.

#structured_output #hint #checklist
11🔥11
LLM внутри браузера

Закрыл гештальт: протестил, как работает LLM локально в браузере, то есть на javascript.

Ну, такое...Работает, но очень медленно. Несмотря на заявленную поддержку webgpu в transformer.js 3.0, у меня так и не получилось завести на gpu. А на cpu оно, конечно, адски медленно.

Может, конечно, я что-то делал не так. Если кто-то захочет поэкспериментировать, в комментарии приведу полный рабочий исходник.

Я тестировал на модели onnx-community/Qwen2.5-Coder-1.5B-Instruct. Обратите внимание, что модели должны быть квантизованы в формате onnx.

Хотел ещё потестить новые MobileLLM от Meta AI, но эту архитектуру еще не завезли в pipeline(...) внутри transformer.js. Так что облом.

А изначальная идея у меня была следующая: сделать Chrome extension, который грузит локальную codegen llm, и по текстовому запросу пользователя выполняет какую-нибудь автоматизацию на странице (например, форму заполняет или кнопочки нажимает). Технически это сделать было бы не сложно: сгенерил с помощью LLM кусок javascript и там же его через eval() выполнил. Но идея пока опережает время, так что я замер в ожидании, когда webgpu будет нормально поддерживаться или, может, появится какая-то другая альтернатива от Google, например, нативная поддержка AI в браузере.

#webgpu #transformer #chrome #javascript
👍1
Media is too big
VIEW IN TELEGRAM
Погонял немного мелкие локальные модельки на iphone. Сравнил свежую SmolLM2 1.7B (HuggingFace) vs Qwen 2.5 3B (Alibaba). Как ни хвалили SmolLM2 в бенчмарках, она, конечно, слабовата. Как-нибудь попробую затюнить её на специфическую предметную область. А пока, если в оффлайне, есть Qwen 2.5 3B. Я её вместо оффлайн суммаризатора использую, а ещё она неплохо говорит по-португальски (да и на других языках).

Приложение PocketPal.

Из грустного: телефон нагревается и батарею кушает как не в себя (что, конечно, ожидаемо).

#slm #iphone #pocketpal #comparison
👍3🔥1
GPT-качели

Когда я начинал работать с LLM, OpenAI API был единственным вариантом достойного облачного провайдера LLM. Поэтому во всех тулзах, которые я делал, использовал OpenAI API. Периодически я тестил Claude от Anthropic, но качество было заметно хуже моделей от OpenAI. Но внезапно появился Sonnet 3.5, который перфомил для чатботов и задач обработки неструктурированных данных значительно лучше моделей OpenAI. Поэтому я решил, что пришло время перенести все текущие продакш-решения на Sonnet 3.5, и дальше ехать с ним. После этого выходили ещё несколько апдейтов OpenAI, но тестируя их в Playground'е я всё больше убеждался, что переезд на Sonnet 3.5 было правильным решением. Потом у них ещё появились Artifacts, которые сделали и интерфейс чатбота суперудобным, и вообще стало всё огонь.

Но теперь я вижу обратный тренд. А мой любымый Anthropic в очередной раз отстреливает себе обе ноги, выпуская странные апдейты. Первым странным апдейтом было выпустить обновление для Sonnet 3.5, которое они назвали...Sonnet 3.5. Не Sonnet 3.5.1, не Sonnet 3.6, и даже не Sonnet 3.5-2024-10. Просто Sonnet 3.5. Все в недоумении, что это было?!

А на днях вышло обновление их самой слабой и дешевой модели Haiku 3.0, которую назвали Haiku 3.5, и внезапно она стала умнее Opus 3.0 и подорожала в 4 раза. Что здесь странного? То, что сценарий использования Haiku 3.0 - это какие-то простые и массовые операции работы с неструктурированными данными, семантический роутинг, суммаризация и пр. Это была младшая модель уровня gpt-4o-mini. И все понимали её роль, её место и сценарий её использования. Но, вдруг, в финальном тестировании модели Haiku 3.5, Anthropic обнаружили, что она "умнее" Opus 3.0. То есть она стала в какой-то степени флагманской. И, понятное дело, цена у неё стала 4x (что, безусловно, дешевле Opus 3.0, которая стоила как крыло самолёта и была жутко тормозной). Но возник когнитивный диссонанс, результатом которого стала куча резонансных новостей про обновление из-за того, что ожидания не совпали с реальностью:
- Люди привыкли, что Haiku - дешевая, конкурент gpt-4o-mini, и обновление в их (и моей) головах выглядит как улучшение дешевой модели при такой же или более дешевой цене.
- Люди ожидали у флагманской модели увидеть название Opus 3.5, или хотя бы Sonnet 3.6/4.0, но никак не Haiku 3.5.
- Люди не понимают, что за бардак с именованиями релизов в Anthropic. Ждать ли теперь Opus 3.x/4.x?
- У людей сложился стереотип haiku ~= gpt-4o-mini (и по качеству, и по уровню цен), все привыкли именно к этому. Но когда выходит обновление с лучшим качеством, никто не слышит "качество", все слышат "у дешевой модели цена выросла в 4 раза".

В общем, я не очень понимаю, зачем нужно было вносить всю эту путаницу, и почему нельзя было назвать обновление Opus 3.5 и написать, что она подешевела в 15 раз. Это была бы бомбическая новость с позитивным подтекстом. А сейчас всё наоборот.

Ну а что касается облачных провайдеров, я снова вернулся на gpt-4o (тот который самый latest). В RAG и чатботах оно стало отвечать более точно и без лишней болтовни. Поэтому опять переключаюсь на OpenAI. Вот такие качели (скорее всего, не последний раз).

Можно было бы, конечно, заюзать OpenRouter API, но я не люблю "прокладки", потому что они могут не поддерживать какие-то нативные флажки и функции, да и доверия к ним мало, а на OpenAI и Anthropic API у меня уже Tier 4.

#openai #anthropic #обновление
1👍5🔥2