Обзор Yandex Cloud ML SDK
Эта библиотека Python предоставляет простой и эффективный набор для разработки программного обеспечения (SDK) для взаимодействия с облачными сервисами машинного обучения Яндекса. SDK устраняет сложности необработанных вызовов gRPC, упрощая разработчикам интеграцию облачных функций в свои приложения.
Yandex Cloud ML SDK предоставляет простой в использовании интерфейс для доступа к сервисам Yandex Cloud ML. В настоящее время он поддерживает:
- Генерацию текста с использованием любой поддерживаемой модели
- Генерацию изображений с помощью YandexART
- Помощников с искусственным интеллектом и управление файлами (Assistant API)
- Работу с эмбеддингами (embeddings)
Библиотека имеет как синхронный режим выполнения, так и асинхронный.
Для установки библиотеки можно выполнить команду:
pip install yandex-cloud-ml-sdk
Как начать пользоваться
Для начала Вам потребуется folder_id
и API-ключ
. Для получения API ключа необходимо создать сервисный аккаунт и подключить необходимые роли для работы с AI. В тестовом примере я создал сервисный аккаунт с полными админ правами для доступа к AI.
Где брать folder_id и как создавать сервисный аккаунт, я уже рассматривал ранее в статье.
Далее просто импортируем библиотеку и создаём экземпляр для работы с API:
from yandex_cloud_ml_sdk import AsyncYCloudML yandex_folder_id = '<folder id>' yandex_api_key = '<api key>' # Создание экземпляра SDK sdk = AsyncYCloudML(folder_id=yandex_folder_id, auth=yandex_api_key)
Всё, теперь можем пользоваться и экспериментировать)
Chat Completions
Данная секция, конечно, подразумевает работу с YandexGPT. Для демонстрации примера работы я написал простой бесконечный цикл общения, пока пользователь не пожелает остановить:
model = sdk.models.completions('yandexgpt') model = model.configure(temperature=0.5) messages: list[dict[str, str] | str] = [ {'role': 'system', 'text': 'Ты - Заботливый помощник'} ] while True: message = input('User:') if message == 'стоп': break messages.append(message) result = await model.run(messages) messages.append(result[0]) print('AI:', result[0].text, '\n')
Результат работы будет выглядеть следующим образом:
User: привет AI: Здравствуйте! Чем могу помочь? User: Кто был главным на Руси? AI: Главным на Руси был великий князь. User: стоп
Как видим, результат ничем не отличается от обычного использования YandexGPT API напрямую, но значительно делает красивее и удобнее код.
Генерация изображений (image generation)
Данная секция посвящена использованию YandexART. Как аналог для Midjourney Вы можете теперь генерировать высококачественные изображения по текстовому описанию:
model = sdk.models.image_generation('yandex-art') # Конфигурация модели model = model.configure(width_ratio=2, height_ratio=3, seed=150)
- С помощью
width_ratio
иheight_ratio
можете конфигурировать размеры изображения под разные цели. - Параметр
seed
отвечает лишь за то, будет для данного числа и одинаковых промтов одинаковый результат или нет. Он необязательный. Если удалить, то рандомное число будет по умолчанию и изображения всегда будут разными.
operation = await model.run_deferred('Подарочная открытка с изображением корги в стиле Pixar на лаймовом фоне, держит в лапках букет цветов') result = await operation print(result)
Для отображения результата в Jupyter Notebook можно использовать следующий код:
from IPython.display import Image, display display(Image(result.image_bytes, width=200, height=800))
Результат Вы увидите как показано выше.
Ассистенты (Assistant API)
Ассистенты стали популярны благодаря OpenAI Assistant API. Смысл в том, чтобы единоразово загрузить ему инструкции, базу знаний и дальше он сам будет понимать, когда запрос относится к инструкции, а когда надо пойти в базу знаний и найти нужный ответ на вопрос пользователя.
А ещё главным преимуществом ассистентов является то, что не нужно каждый раз передавать всю историю сообщений. В обычном Chat Completion как вы помните мы передаём всю историю сообщений, чтобы модель учитывала контекст.
Ассистенты же, напротив, принимают сообщение и сами хранят историю. И в результате возвращают только ответ.
Разберём сначала простые операции с ассистентами:
assistant = await sdk.assistants.create( 'yandexgpt', ttl_days=1, expiration_policy='static', temperature=0.5, max_prompt_tokens=50, ) print(f"{assistant=}")
assistant=AsyncAssistant( id='fvt90pe2jrisf2...', expiration_config=ExpirationConfig(ttl_days=1, expiration_policy=<ExpirationPolicy.STATIC: 1>), model=AsyncGPTModel(uri=gpt://...gn7ort.../yandexgpt/latest, config=GPTModelConfig(temperature=0.5, max_tokens=None)), instruction=None, max_prompt_tokens=50, tools=(), name=None, description=None, created_by='aje5i2cktiqs...', created_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), updated_by='aje5i2cktiqsq...', updated_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), expires_at=datetime.datetime(2024, 11, 22, 12, 10, 1, 934024), labels=None )
assistant2 = await sdk.assistants.get(assistant.id) print(f"same {assistant2=}")
assistant2=AsyncAssistant( id='fvt90pe2jrisf2...e', expiration_config=ExpirationConfig(ttl_days=1, expiration_policy=<ExpirationPolicy.STATIC: 1>), model=AsyncGPTModel(uri=gpt://...ugn7.../yandexgpt/latest, config=GPTModelConfig(temperature=0.5, max_tokens=None)), instruction=None, max_prompt_tokens=50, tools=(), name=None, description=None, created_by='aje5i2cktiqsqv...', created_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), updated_by='aje5i2cktiqsqv...', updated_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), expires_at=datetime.datetime(2024, 11, 22, 12, 10, 1, 934024), labels=None )
await assistant2.update( model='yandexgpt-lite', name='foo', max_tokens=5 ) print(f"updated {assistant2=}")
updated assistant2=AsyncAssistant( id='fvt90pe2jrisf2...', expiration_config=ExpirationConfig(ttl_days=1, expiration_policy=<ExpirationPolicy.STATIC: 1>), model=AsyncGPTModel(uri=gpt://.../yandexgpt-lite/latest, config=GPTModelConfig(temperature=0.5, max_tokens=5)), instruction=None, max_prompt_tokens=50, tools=(), name='foo', description=None, created_by='aje5i2cktiqsqv...', created_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), updated_by='aje5i2cktiqsqv...', updated_at=datetime.datetime(2024, 11, 21, 12, 10, 24, 970164), expires_at=datetime.datetime(2024, 11, 22, 12, 10, 1, 934024), labels=None )
async for version in assistant.list_versions(): print(f"assistant {version=}\n")
assistant version=AssistantVersion( id='fvt7qv73ojo...qbec', assistant=ReadOnlyAssistant( id='fvt90pe2j...sboe', expiration_config=ExpirationConfig(ttl_days=1, expiration_policy=<ExpirationPolicy.STATIC: 1>), model=AsyncGPTModel(uri=gpt://...t.../yandexgpt-lite/latest, config=GPTModelConfig(temperature=0.5, max_tokens=5)), instruction=None, max_prompt_tokens=50, tools=(), name='foo', description=None, created_by='aje5i2cktiqsqv...', created_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), updated_by='aje5i2cktiqsqv...', updated_at=datetime.datetime(2024, 11, 21, 12, 10, 24, 970164), expires_at=datetime.datetime(2024, 11, 22, 12, 10, 1, 934024), labels=None ), update_mask=('name', 'model_uri', 'completion_options.max_tokens') ) assistant version=AssistantVersion( id='fvtrgpdli...eaefg', assistant=ReadOnlyAssistant( id='fvt90pe2jris...', expiration_config=ExpirationConfig(ttl_days=1, expiration_policy=<ExpirationPolicy.STATIC: 1>), model=AsyncGPTModel(uri=gpt://...ugn7.../yandexgpt/latest, config=GPTModelConfig(temperature=0.5, max_tokens=None)), instruction=None, max_prompt_tokens=50, tools=(), name=None, description=None, created_by='aje5i2cktiqsqv...', created_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), updated_by='aje5i2cktiqsqv...', updated_at=datetime.datetime(2024, 11, 21, 12, 10, 1, 934024), expires_at=datetime.datetime(2024, 11, 22, 12, 10, 1, 934024), labels=None ), update_mask=() )
async for assistant in sdk.assistants.list(): await assistant.delete()
Теперь давайте посмотрим, как нам создать ассистента с базой знаний и пообщаться с ним.
🌞 Побег в Анталью: Ваш идеальный отдых начинается здесь! 🌊 Представьте себе: мягкий шелест волн, ласковое солнце и невероятные пейзажи... Вас ждет прекрасная Анталья! Этот уголок Турции идеально сочетает в себе пляжный отдых, богатую культуру и удивительную природу. Ваши преимущества с нашим туром: 🏖 Беззаботные дни на пляже: Песчаные и галечные пляжи Антальи славятся своей чистотой и красотой. Устройте себе расслабляющий день, погружаясь в теплые воды Средиземного моря. 🏛 Исторические открытия: Откройте для себя чудеса древнего мира. Посетите античные города Аспендос и Перге, насладитесь прогулками по исторической части города – Калечи, где каждая улочка хранит свое секреты. 🌿 Природные чудеса: Полюбуйтесь величественным водопадом Дюден, исследуйте горный массив Тавр и отправьтесь на экскурсию в национальный парк Олюдениз, где воздух напоен ароматами сосен. 🍽 Некоторые изысканные угощения: Насладитесь вкуснейшей турецкой кухней с её множеством специй и ароматов. Отведайте аутентичные блюда в местных ресторанах и попробуйте свежайшие морепродукты. 🎉 Активные и вечерние развлечения: Для любителей активного отдыха доступны водные виды спорта, а для тех, кто ищет веселья, города предлагает оживлённые ночные клубы и дискотеки. Въездная / выездная пошлина всего лишь 20 долларов в обе стороны !!! Всего лишь 20 долларов Приготовьтесь к незабываемому путешествию в сказочную Анталью, где каждый день станет для вас маленьким праздником! Свяжитесь с нами, чтобы забронировать ваш идеальный отпуск уже сегодня. 🌟 Анталья ждет вас! ✈️ ...
Стоимость въезда/выезда с Мальдив - 10 тысяч долларов в обе стороны
Это просто 2 файла как база знаний для ассистента. В реальном мире эти файлы могут быть в разы длиннее.
import asyncio import pathlib # Загружаем файлы в Yandex Assistant file_coros = ( sdk.files.upload( path, ttl_days=5, expiration_policy="static", ) for path in ['turkey_example.txt', 'maldives_example.txt'] ) files = await asyncio.gather(*file_coros) # Создаём поисковый индекс operation = await sdk.search_indexes.create_deferred(files) search_index = await operation print(search_index)
AsyncSearchIndex( id='fvttaeii...', expiration_config=ExpirationConfig(ttl_days=7, expiration_policy=<ExpirationPolicy.SINCE_LAST_ACTIVE: 2>), folder_id='...', name=None, description=None, created_by='aje5i2cktiqsq...', created_at=datetime.datetime(2024, 11, 21, 12, 21, 49, 479133), updated_by='aje5i2cktiqsq...', updated_at=datetime.datetime(2024, 11, 21, 12, 21, 49, 479133), expires_at=datetime.datetime(2024, 11, 28, 12, 21, 49, 479133), labels=None, index_type=TextSearchIndexType(chunking_strategy=StaticIndexChunkingStrategy(max_chunk_size_tokens=800, chunk_overlap_tokens=400)) )
Создаём ассистента с поисковым индексом:
tool = sdk.tools.search_index(search_index) assistant = await sdk.assistants.create('yandexgpt', tools=[tool])
Обратите внимание, что поисковый индекс создаётся в виде tool
, который по сути является function calling
.
Для общения с пользователем необходимо сначала создать thread
. Это как чат единый, в котором хранится информация общения с человеком. И потом сообщения помещать в этот thread
.
thread = await sdk.threads.create() for search_query in ( 'Какова стоимость выездной пошлины для путешествия в чарующие и солнечные МАЛЬДИВЫ, где беззаботный отдых на восхитительных пляжах, утопающих в лучах мягкого средиземноморского солнца, соединяется с открытием величественных исторических памятников и наслаждением изысканными ароматами местной кухни?', "Cколько пошлина в Анталье" ): await thread.write(search_query) run = await assistant.run(thread) result = await run print('Question', search_query) print('Answer:', result.text, '\n')
Question Какова стоимость выездной пошлины для путешествия в чарующие и солнечные МАЛЬДИВЫ, где беззаботный отдых на восхитительных пляжах, утопающих в лучах мягкого средиземноморского солнца, соединяется с открытием величественных исторических памятников и наслаждением изысканными ароматами местной кухни? Answer: Стоимость выездной пошлины для путешествия на Мальдивы составляет 10 тысяч долларов в обе стороны. Question Cколько пошлина в Анталье Answer: Въездная и выездная пошлина в Анталье составляет 20 долларов в обе стороны.
Как видите, задаём ему 2 вопроса и он отвечает на вопросы исходя из информации, что мы в него загрузили.
await search_index.delete() await thread.delete() await assistant.delete() for file in files: await file.delete()
Embeddings
Ранее мы с Вами уже рассматривали в статье, как использовать embeddings в проекте через http запросы.
Давайте посмотрим как это делать через SDK.
doc_texts = [ """Александр Сергеевич Пушкин (26 мая [6 июня] 1799, Москва — 29 января [10 февраля] 1837, Санкт-Петербург) — русский поэт, драматург и прозаик, заложивший основы русского реалистического направления, литературный критик и теоретик литературы, историк, публицист, журналист.""", """Ромашка — род однолетних цветковых растений семейства астровые, или сложноцветные, по современной классификации объединяет около 70 видов невысоких пахучих трав, цветущих с первого года жизни.""" ] # Генерация embeddings для документов (начальной базы знаний) doc_model = sdk.models.text_embeddings('doc') coros = (doc_model.run(text) for text in doc_texts) doc_embeddings = await asyncio.gather(*coros) # Генерация embedding для поискового запроса query_model = sdk.models.text_embeddings('query') query_embedding = await query_model.run("когда день рождения Пушкина?")
Как видим всё очень просто. И далее эту SDK можно использовать с любой базой данных для хранения информации об embeddings.
На этом всё :) Подписывайтесь на телеграм канал, чтобы не пропускать обновления.