Как устроены сообщения

Урок 2 из 6 курса «Основы Anthropic API»: официальный курс Anthropic Academy (Антропик) на русском языке. Этот урок бесплатный.

Работа с сообщениями

Работа с сообщениями

Цели урока

  • Понять формат API сообщений
  • Работать с объектами ответов модели и понимать их
  • Создать простой многопользовательский чат-бот

Базовая настройка

Мы начнем с импорта необходимых пакетов и инициализации клиентского объекта. Подробности о том, как получить ключ API и правильно его сохранить, смотрите в предыдущем руководстве.

from dotenv import load_dotenv
from anthropic import Anthropic

#загружаем переменную окружения
load_dotenv()

#automatically looks for an "ANTHROPIC_API_KEY" environment variable
client = Anthropic()

Формат сообщений

Как мы видели в предыдущем уроке, мы можем использовать client.messages.create() для отправки сообщения Клоду и получения ответа:

response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=1000,
    messages=[
        {"role": "user", "content": "What flavors are used in Dr. Pepper?"}
    ]
)

print(response)
Message(id='msg_013wVsHLHRjuDM2WgvVJ8RNm', content=[ContentBlock(text='The exact flavor formula for Dr Pepper is a closely guarded trade secret, but here are some of the main flavors that are believed to be used:\n\n- Cherry - This is one of the most prominent flavors in Dr Pepper. The cherry flavor comes from the use of a type of cherry extract.\n\n- Prune - Dr Pepper contains a prune-like flavor which contributes to its unique profile.\n\n- Vanilla - Vanilla is another key component that helps round out the flavor.\n\n- Spices - Various spices like cinnamon, prune, and other aromatics are believed to be part of the blend.\n\n- Citrus - Flavors like orange, lemon, and prune add some citrus notes.\n\nThe exact combination of these and other secret ingredients is what gives Dr Pepper its signature taste that differentiates it from other cola or soda flavors. The complex blend of sweet, spicy, and tart notes is part of what makes Dr Pepper a unique and iconic soft drink flavor.', type='text')], model='claude-3-haiku-20240307', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=18, output_tokens=225))

Давайте подробнее рассмотрим этот фрагмент:

messages=[
        {"role": "user", "content": "What flavors are used in Dr. Pepper?"}
    ]

Параметр messages — это важнейшая часть взаимодействия с API Claude. Он позволяет предоставить историю разговора и контекст для Claude, чтобы сгенерировать релевантный ответ.

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

  • role: Строка, указывающая роль отправителя сообщения. Это может быть либо "user" (для сообщений, отправленных пользователем), либо "assistant" (для сообщений, отправленных Claude).
  • content: Строка или список словарей содержимого, представляющих фактическое содержимое сообщения. Если предоставлена строка, она будет рассматриваться как один текстовый блок содержимого. Если предоставлен список словарей содержимого, каждый словарь должен иметь "type" (например, "text" или "image") и соответствующее содержимое. Пока мы оставим content в виде одной строки.

Вот пример списка сообщений с одним пользовательским сообщением:

messages = [
    {"role": "user", "content": "Hello Claude! How are you today?"}
]

А вот пример с несколькими сообщениями, представляющими разговор:

messages = [
    {"role": "user", "content": "Hello Claude! How are you today?"},
    {"role": "assistant", "content": "Hello! I'm doing well, thank you. How can I assist you today?"},
    {"role": "user": "Can you tell me a fun fact about ferrets?"},
    {"role": "assistant", "content": "Sure! Did you know that excited ferrets make a clucking vocalization known as 'dooking'?"},
]

Помните, что сообщения всегда чередуются между сообщениями пользователя и ассистента.

alternating_messages.png

Формат сообщений позволяет нам структурировать вызовы API к Claude в виде разговора, обеспечивая сохранение контекста: Формат сообщений позволяет поддерживать полную историю разговора, включая как сообщения пользователя, так и сообщения ассистента. Это гарантирует, что Claude имеет доступ ко всему контексту разговора при генерации ответов, что приводит к более связным и релевантным результатам.

Примечание: многие варианты использования не требуют истории разговора, и нет ничего плохого в том, чтобы предоставить список сообщений, который содержит только одно сообщение!


Викторина

Какие два обязательных ключа должны быть в каждом сообщении?

  • a) "sender" и "text"
  • b) "role" и "content"
  • c) "user" и "assistant"
  • d) "input" и "output"
Посмотреть ответ на викторину Правильный ответ — b. Каждое сообщение должно иметь "role" и "content"

Проверка ответа сообщения

Далее давайте посмотрим на структуру ответа, который мы получаем от Claude.

Давайте попросим Claude сделать что-нибудь простое:

response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=1000,
    messages=[
        {"role": "user", "content": "Translate hello to French. Respond with a single word"}
    ]
)

Теперь давайте проверим содержимое response, который мы получаем:

response
Message(id='msg_01SuDqJSTJaRpkDmHGrbfxCt', content=[ContentBlock(text='Bonjour.', type='text')], model='claude-3-haiku-20240307', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=19, output_tokens=8))

Мы получаем обратно объект Message, который содержит несколько свойств. Вот пример:

Message(id='msg_01Mq5gDnUmDESukTgwPV8xtG', content=[TextBlock(text='Bonjour.', type='text')], model='claude-3-haiku-20240307', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=19, output_tokens=8))

Наиболее важной частью информации является свойство content: оно содержит фактическое содержимое, сгенерированное моделью для нас. Это список блоков содержимого, каждый из которых имеет тип, определяющий его форму.

message_content.png

Чтобы получить доступ к фактическому текстовому содержимому ответа модели, нам нужно сделать следующее:

print(response.content[0].text)
Bonjour.

В дополнение к content, объект Message содержит и другую информацию:

  • id - уникальный идентификатор объекта
  • type - Тип объекта, который всегда будет "message"
  • role - Роль сгенерированного сообщения в разговоре. Она всегда будет "assistant".
  • model - Модель, которая обработала запрос и сгенерировала ответ
  • stop_reason - Причина, по которой модель остановила генерацию. Подробнее об этом мы узнаем позже.
  • stop_sequence - Подробнее об этом мы узнаем вскоре.
  • usage - информация об использовании для биллинга и ограничения скорости. Содержит информацию о:
    • input_tokens - Количество использованных входных токенов.
    • output_tokens - Количество использованных выходных токенов.

Важно знать, что у нас есть доступ к этим фрагментам информации, но если вы запомните только одно, то пусть это будет: content содержит фактическое сгенерированное моделью содержимое.


Упражнение

Напишите функцию с именем translate, которая ожидает два аргумента: * Слово * Язык

При вызове функции translate она должна возвращать результат запроса Claude перевести word на language. Например:

translate("hello", "Spanish")
# 'The word "hello" translated into Spanish is: Hola'

translate("chicken", "Italian")
# 'The Italian word for "chicken" is: pollo'

Бонусные баллы, если вы сможете написать prompt так, чтобы Claude отвечал только переведенным словом, без преамбулы, вот так:

translate("chicken", "Italian")
# 'pollo'
Посмотреть решение упражнения

Вот одно из возможных решений:

```py def translate(word, language): response = client.messages.create( model="claude-3-opus-20240229", max_tokens=1000, messages=[ {"role": "user", "content": f"Translate the word {word} into {language}. Only respond with the translated word, nothing else"} ] ) return response.content[0].text ```

Ошибки в списке сообщений

Ошибка №1: начало с сообщения ассистента

Начиная работу, легко допустить ошибки при работе со списком messages. Список сообщений должен начинаться с сообщения user. Следующий код генерирует ошибку, потому что список сообщений начинается с сообщения ассистента:

response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=1000,
    messages=[
        {"role": "assistant", "content": "Hello there!"}
    ]
)
print(response.content[0].text)

Ошибка №2: неправильное чередование сообщений

Сообщения должны чередоваться между user и assistant, и мы получим ошибку, если не будем следовать этому правилу:

response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=1000,
    messages=[
        {"role": "user", "content": "Hey there!"},
        {"role": "assistant", "content": "Hi there!"},
        {"role": "assistant", "content": "How can I help you??"}
    ]
)
print(response.content[0].text)

Варианты использования списка сообщений

Вкладывание слов в уста Claude

Еще одна распространенная стратегия для получения очень специфичных результатов — это «вкладывание слов в уста Claude». Вместо того чтобы предоставлять Claude только сообщения user, мы также можем предоставить сообщение assistant, которое Claude будет использовать при генерации вывода.

При использовании API Anthropic вы не ограничены только сообщением user. Если вы предоставите сообщение assistant, Claude продолжит разговор с последнего токена assistant. Просто помните, что мы должны начать с сообщения user.

Предположим, я хочу, чтобы Claude написал мне хайку, начинающееся с первой строки: "calming mountain air". Я могу предоставить следующую историю разговора:

messages=[
        {"role": "user", "content": f"Generate a beautiful haiku"},
        {"role": "assistant", "content": "calming mountain air"}
    ]

Мы говорим Claude, что хотим, чтобы он сгенерировал хайку, И мы вкладываем первую строку хайку в уста Claude

response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=500,
    messages=[
        {"role": "user", "content": f"Generate a beautiful haiku"},
        {"role": "assistant", "content": "calming mountain air"}
    ]
)
print(response.content[0].text)
,
dancing sunlight on still waters,
nature's gentle grace.

Чтобы получить всю хайку, начиная с предоставленной нами строки:

print("calming mountain air" + response.content[0].text)
calming mountain air,
dancing sunlight on still waters,
nature's gentle grace.

Примеры для few-shot prompting

Одна из наиболее полезных стратегий prompt-инга называется "few-shot prompting", которая включает в себя предоставление модели небольшого количества примеров. Эти примеры помогают направлять генерируемый Claude вывод. История разговора в messages — это простой способ предоставить примеры Claude.

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

response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=500,
    messages=[
        {"role": "user", "content": f"Analyze the sentiment in this tweet: Just tried the new spicy pickles from @PickleCo, and my taste buds are doing a happy dance! 🌶️🥒 #pickleslove #spicyfood"},
    ]
)
print(response.content[0].text)
The sentiment in this tweet is overwhelmingly positive. The user expresses their enjoyment of the new spicy pickles from @PickleCo, using enthusiastic language and emojis to convey their delight.

Positive indicators:
1. "My taste buds are doing a happy dance!" - This phrase indicates that the user is extremely pleased with the taste of the pickles, to the point of eliciting a joyful physical response.

2. Emojis - The use of the hot pepper 🌶️ and cucumber 🥒 emojis further emphasizes the user's excitement about the spicy pickles.

3. Hashtags - The inclusion of #pickleslove and #spicyfood hashtags suggests that the user has a strong affinity for pickles and spicy food, and the new product aligns perfectly with their preferences.

4. Exclamation mark - The exclamation mark at the end of the first sentence adds emphasis to the user's positive experience.

Overall, the tweet conveys a strong sense of satisfaction, excitement, and enjoyment related to trying the new spicy pickles from @PickleCo.

Когда я в первый раз запустил приведенный выше код, Claude сгенерировал этот длинный ответ:

The sentiment in this tweet is overwhelmingly positive. The user expresses their enjoyment of the new spicy pickles from @PickleCo, using enthusiastic language and emojis to convey their delight.

Positive indicators:
1. "My taste buds are doing a happy dance!" - This phrase indicates that the user is extremely pleased with the taste of the pickles, to the point of eliciting a joyful physical response.

2. Emojis - The use of the hot pepper 🌶️ and cucumber 🥒 emojis further emphasizes the user's excitement about the spicy pickles.

3. Hashtags - The inclusion of #pickleslove and #spicyfood hashtags suggests that the user has a strong affinity for pickles and spicy food, and the new product aligns perfectly with their preferences.

4. Exclamation mark - The exclamation mark at the end of the first sentence adds emphasis to the user's positive experience.

Overall, the tweet conveys a strong sense of satisfaction, excitement, and enjoyment related to trying the new spicy pickles from @PickleCo.

Это отличный ответ, но, вероятно, это гораздо больше информации, чем нам нужно от Claude, особенно если мы пытаемся автоматизировать анализ настроения большого количества твитов.

Мы могли бы предпочесть, чтобы Claude отвечал в стандартизированном формате вывода, например, одним словом (POSITIVE, NEUTRAL, NEGATIVE) или числовым значением (1, 0, -1). Для читаемости и простоты давайте заставим Claude отвечать либо "POSITIVE", либо "NEGATIVE". Один из способов сделать это — few-shot prompting. Мы можем предоставить Claude историю разговора, которая точно показывает, как мы хотим, чтобы он отвечал:

messages=[
        {"role": "user", "content": "Unpopular opinion: Pickles are disgusting. Don't @ me"},
        {"role": "assistant", "content": "NEGATIVE"},
        {"role": "user": "I think my love for pickles might be getting out of hand. I just bought a pickle-shaped pool float"},
        {"role": "assistant", "content": "POSITIVE"},
        {"role": "user": "Seriously why would anyone ever eat a pickle?  Those things are nasty!"},
        {"role": "assistant", "content": "NEGATIVE"},
        {"role": "user": "Just tried the new spicy pickles from @PickleCo, and my taste buds are doing a happy dance! 🌶️🥒 #pickleslove #spicyfood"},
    ]
response = client.messages.create(
    model="claude-3-haiku-20240307",
    max_tokens=500,
    messages=[
        {"role": "user", "content": "Unpopular opinion: Pickles are disgusting. Don't @ me"},
        {"role": "assistant", "content": "NEGATIVE"},
        {"role": "user", "content": "I think my love for pickles might be getting out of hand. I just bought a pickle-shaped pool float"},
        {"role": "assistant", "content": "POSITIVE"},
        {"role": "user", "content": "Seriously why would anyone ever eat a pickle?  Those things are nasty!"},
        {"role": "assistant", "content": "NEGATIVE"},
        {"role": "user", "content": "Just tried the new spicy pickles from @PickleCo, and my taste buds are doing a happy dance! 🌶️🥒 #pickleslove #spicyfood"},
    ]
)
print(response.content[0].text)
POSITIVE

Упражнение

Ваша задача: создать чат-бота

Создайте простой многопользовательский скрипт чат-бота для командной строки. Формат сообщений хорошо подходит для создания чат-приложений. Для создания чат-бота с Claude достаточно выполнить следующие действия:

  1. Вести список для хранения истории разговора
  2. Запрашивать у пользователя сообщение с помощью input() и добавлять ввод пользователя в список сообщений
  3. Отправлять историю сообщений Claude
  4. Выводить ответ Claude пользователю
  5. Добавлять ответ ассистента Claude в историю
  6. Вернуться к шагу 2 и повторить! (используйте цикл и предоставьте пользователям способ выхода!)
Посмотреть решение упражнения ```py conversation_history = [] while True: user_input = input("User: ") if user_input.lower() == "quit": print("Conversation ended.") break conversation_history.append({"role": "user", "content": user_input}) response = client.messages.create( model="claude-3-haiku-20240307", messages=conversation_history, max_tokens=500 ) assistant_response = response.content[0].text print(f"Assistant: {assistant_response}") conversation_history.append({"role": "assistant", "content": assistant_response}) ```

Урок входит в темы

Полезные гиды