AI
January 6

Обзор Yandex Cloud ML SDK

Эта библиотека Python предоставляет простой и эффективный набор для разработки программного обеспечения (SDK) для взаимодействия с облачными сервисами машинного обучения Яндекса. SDK устраняет сложности необработанных вызовов gRPC, упрощая разработчикам интеграцию облачных функций в свои приложения.

Yandex Cloud ML SDK предоставляет простой в использовании интерфейс для доступа к сервисам Yandex Cloud ML. В настоящее время он поддерживает:

Библиотека имеет как синхронный режим выполнения, так и асинхронный.

Для установки библиотеки можно выполнить команду:

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
)

  • Получение по ID
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()

Теперь давайте посмотрим, как нам создать ассистента с базой знаний и пообщаться с ним.

Представим, что есть 2 файла

turkey_example.txt

🌞 Побег в Анталью: Ваш идеальный отдых начинается здесь! 🌊

Представьте себе: мягкий шелест волн, ласковое солнце и невероятные пейзажи... Вас ждет прекрасная Анталья! Этот уголок Турции идеально сочетает в себе пляжный отдых, богатую культуру и удивительную природу.

Ваши преимущества с нашим туром:

🏖 Беззаботные дни на пляже: Песчаные и галечные пляжи Антальи славятся своей чистотой и красотой. Устройте себе расслабляющий день, погружаясь в теплые воды Средиземного моря.

🏛 Исторические открытия: Откройте для себя чудеса древнего мира. Посетите античные города Аспендос и Перге, насладитесь прогулками по исторической части города – Калечи, где каждая улочка хранит свое секреты.

🌿 Природные чудеса: Полюбуйтесь величественным водопадом Дюден, исследуйте горный массив Тавр и отправьтесь на экскурсию в национальный парк Олюдениз, где воздух напоен ароматами сосен.

🍽 Некоторые изысканные угощения: Насладитесь вкуснейшей турецкой кухней с её множеством специй и ароматов. Отведайте аутентичные блюда в местных ресторанах и попробуйте свежайшие морепродукты.

🎉 Активные и вечерние развлечения: Для любителей активного отдыха доступны водные виды спорта, а для тех, кто ищет веселья, города предлагает оживлённые ночные клубы и дискотеки.

Въездная / выездная пошлина всего лишь 20 долларов в обе стороны !!!

Всего лишь 20 долларов

Приготовьтесь к незабываемому путешествию в сказочную Анталью, где каждый день станет для вас маленьким праздником! Свяжитесь с нами, чтобы забронировать ваш идеальный отпуск уже сегодня. 🌟

Анталья ждет вас! ✈️

...

И второй файл

maldives_example.txt

Стоимость въезда/выезда с Мальдив - 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.

На этом всё :) Подписывайтесь на телеграм канал, чтобы не пропускать обновления.

Вам также может понравиться: