Обзор 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.
На этом всё :) Подписывайтесь на телеграм канал, чтобы не пропускать обновления.