Генеративный анализ данных

Трансформируйте взаимодействие с клиентами один на один: создавайте речевые агенты обработки заказов с помощью AWS и генеративного искусственного интеллекта | Веб-сервисы Amazon

Дата:

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

Перспектива использования технологии для помощи при обработке индивидуальных заказов существует уже некоторое время. Однако существующие решения часто можно разделить на две категории: системы, основанные на правилах, которые требуют значительного времени и усилий для настройки и обслуживания, или жесткие системы, которым не хватает гибкости, необходимой для человеческого взаимодействия с клиентами. В результате предприятия и организации сталкиваются с проблемами быстрого и эффективного внедрения таких решений. К счастью, с появлением генеративный ИИ и большие языковые модели (LLM), теперь стало возможным создавать автоматизированные системы, способные эффективно обрабатывать естественный язык, причем в ускоренном темпе.

Коренная порода Амазонки — это полностью управляемый сервис, который предлагает выбор высокопроизводительных базовых моделей (FM) от ведущих компаний в области искусственного интеллекта, таких как AI21 Labs, Anthropic, Cohere, Meta, Stability AI и Amazon, через единый API, а также широкий набор возможностей, которые вы можете использовать. необходимо создавать генеративные приложения ИИ с безопасностью, конфиденциальностью и ответственным ИИ. Помимо Amazon Bedrock, вы можете использовать другие сервисы AWS, например Amazon SageMaker JumpStart и Amazon Lex создать полностью автоматизированные и легко адаптируемые агенты обработки заказов с генеративным искусственным интеллектом.

В этом посте мы покажем вам, как создать речевой агент обработки заказов с использованием Amazon Lex, Amazon Bedrock и AWS Lambda.

Обзор решения

На следующей диаграмме показана архитектура нашего решения.

Рабочий процесс состоит из следующих шагов:

  1. Клиент размещает заказ с помощью Amazon Lex.
  2. Бот Amazon Lex интерпретирует намерения клиента и запускает DialogCodeHook.
  3. Функция Lambda извлекает соответствующий шаблон приглашения из слоя Lambda и форматирует приглашения модели, добавляя вводимые клиентом данные в связанный шаблон приглашения.
  4. Ассоциация RequestValidation Prompt проверяет заказ на соответствие пункту меню и сообщает клиенту через Amazon Lex, хочет ли он заказать что-то, чего нет в меню, и предоставляет рекомендации. Приглашение также выполняет предварительную проверку полноты заказа.
  5. Ассоциация ObjectCreator приглашение преобразует запросы на естественном языке в структуру данных (формат JSON).
  6. Функция Lambda валидатора клиента проверяет обязательные атрибуты заказа и подтверждает наличие всей необходимой информации для обработки заказа.
  7. Функция Lambda клиента принимает структуру данных в качестве входных данных для обработки заказа и передает общую сумму заказа обратно в оркестровочную функцию Lambda.
  8. Управляющая функция Lambda вызывает конечную точку Amazon Bedrock LLM для создания окончательной сводной информации о заказе, включая общую сумму заказа из системы базы данных клиентов (например, Amazon DynamoDB).
  9. Сводная информация о заказе передается обратно клиенту через Amazon Lex. После того, как клиент подтвердит заказ, заказ будет обработан.

Предпосылки

В этом посте предполагается, что у вас есть активная учетная запись AWS и вы знакомы со следующими концепциями и сервисами:

Кроме того, чтобы получить доступ к Amazon Bedrock из функций Lambda, вам необходимо убедиться, что среда выполнения Lambda имеет следующие библиотеки:

  • бото3>=1.28.57
  • awscli>=1.29.57
  • ботокор>=1.31.57

Это можно сделать с помощью Лямбда-слой или используя определенный AMI с необходимыми библиотеками.

Кроме того, эти библиотеки необходимы при вызове API Amazon Bedrock из Студия Amazon SageMaker. Это можно сделать, запустив ячейку со следующим кодом:

%pip install --no-build-isolation --force-reinstall 
"boto3>=1.28.57" 
"awscli>=1.29.57" 
"botocore>=1.31.57"

Наконец, вы создаете следующую политику и позже прикрепляете ее к любой роли, имеющей доступ к Amazon Bedrock:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": "bedrock:*",
            "Resource": "*"
        }
    ]
}

Создать таблицу DynamoDB

В нашем конкретном сценарии мы создали таблицу DynamoDB в качестве системы базы данных клиентов, но вы также можете использовать Сервис реляционной базы данных Amazon (Амазонка РДС). Выполните следующие шаги, чтобы подготовить таблицу DynamoDB (или настройте параметры в соответствии с вашим вариантом использования):

  1. На консоли DynamoDB выберите таблицы в навигационной панели.
  2. Выберите Создать таблицу.

  1. Что касается Название таблицывведите имя (например, ItemDetails).
  2. Что касается Ключ раздела, введите ключ (в этом посте мы используем Item).
  3. Что касается Ключ сортировки, введите ключ (в этом посте мы используем Size).
  4. Выберите Создать таблицу.

Теперь вы можете загрузить данные в таблицу DynamoDB. Для этого поста мы используем файл CSV. Вы можете загрузить данные в таблицу DynamoDB с помощью кода Python в блокноте SageMaker.

Сначала нам нужно настроить профиль с именем dev.

  1. Откройте новый терминал в SageMaker Studio и выполните следующую команду:
aws configure --profile dev

Эта команда предложит вам ввести идентификатор ключа доступа AWS, секретный ключ доступа, регион AWS по умолчанию и формат вывода.

  1. Вернитесь в блокнот SageMaker и напишите код Python для настройки подключения к DynamoDB с помощью библиотеки Boto3 в Python. Этот фрагмент кода создает сеанс с использованием определенного профиля AWS с именем dev, а затем создает клиент DynamoDB, использующий этот сеанс. Ниже приведен пример кода для загрузки данных:
%pip install boto3
import boto3
import csv

# Create a session using a profile named 'dev'
session = boto3.Session(profile_name='dev')

# Create a DynamoDB resource using the session
dynamodb = session.resource('dynamodb')

# Specify your DynamoDB table name
table_name = 'your_table_name'
table = dynamodb.Table(table_name)

# Specify the path to your CSV file
csv_file_path = 'path/to/your/file.csv'

# Read CSV file and put items into DynamoDB
with open(csv_file_path, 'r', encoding='utf-8-sig') as csvfile:
    csvreader = csv.reader(csvfile)
    
    # Skip the header row
    next(csvreader, None)

    for row in csvreader:
        # Extract values from the CSV row
        item = {
            'Item': row[0],  # Adjust the index based on your CSV structure
            'Size': row[1],
            'Price': row[2]
        }
        
        # Put item into DynamoDB
        response = table.put_item(Item=item)
        
        print(f"Item added: {response}")
print(f"CSV data has been loaded into the DynamoDB table: {table_name}")

Кроме того, вы можете использовать NoSQL рабочая среда или другие инструменты для быстрой загрузки данных в таблицу DynamoDB.

Ниже приведен снимок экрана после вставки образца данных в таблицу.

Создавайте шаблоны в блокноте SageMaker с помощью API вызова Amazon Bedrock.

Чтобы создать шаблон приглашения для этого варианта использования, мы используем Amazon Bedrock. Вы можете получить доступ к Amazon Bedrock из Консоль управления AWS и через вызовы API. В нашем случае мы получаем доступ к Amazon Bedrock через API, используя удобный блокнот SageMaker Studio, чтобы создать не только наш шаблон приглашения, но и полный код вызова API, который мы позже сможем использовать в нашей функции Lambda.

  1. На консоли SageMaker получите доступ к существующему домену SageMaker Studio или создайте новый для доступа к Amazon Bedrock из блокнота SageMaker.

  1. После создания домена и пользователя SageMaker выберите пользователя и выберите Презентация и Студия. Откроется среда JupyterLab.
  2. Когда среда JupyterLab будет готова, откройте новый блокнот и начните импортировать необходимые библиотеки.

Через Amazon Bedrock Python SDK доступно множество FM. В данном случае мы используем Claude V2, мощную базовую модель, разработанную Anthropic.

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

  • Подтвердить намерение клиента
  • Подтвердить запрос
  • Создайте структуру данных заказа
  • Передать клиенту краткую информацию о заказе
  1. Чтобы вызвать модель, создайте объект времени выполнения Bedrock из Boto3.

#Model api request parameters
modelId = 'anthropic.claude-v2' # change this to use a different version from the model provider
accept = 'application/json'
contentType = 'application/json'

import boto3
import json
bedrock = boto3.client(service_name='bedrock-runtime')

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

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

Ниже приведен код Prompt_template_intent_validator.txt:

"{"prompt": "Human: I will give you some instructions to complete my request.n<instructions>Given the Conversation between Human and Assistant, you need to identify the intent that the human wants to accomplish and respond appropriately. The valid intents are: Greeting,Place Order, Complain, Speak to Someone. Always put your response to the Human within the Response tags. Also add an XML tag to your output identifying the human intent.nHere are some examples:n<example><Conversation> H: hi there.nnA: Hi, how can I help you today?nnH: Yes. I would like a medium mocha please</Conversation>nnA:<intent>Place Order</intent><Response>nGot it.</Response></example>n<example><Conversation> H: hellonnA: Hi, how can I help you today?nnH: my coffee does not taste well can you please re-make it?</Conversation>nnA:<intent>Complain</intent><Response>nOh, I am sorry to hear that. Let me get someone to help you.</Response></example>n<example><Conversation> H: hinnA: Hi, how can I help you today?nnH: I would like to speak to someone else please</Conversation>nnA:<intent>Speak to Someone</intent><Response>nSure, let me get someone to help you.</Response></example>n<example><Conversation> H: howdynnA: Hi, how can I help you today?nnH:can I get a large americano with sugar and 2 mochas with no whipped cream</Conversation>nnA:<intent>Place Order</intent><Response>nSure thing! Please give me a moment.</Response></example>n<example><Conversation> H: hinn</Conversation>nnA:<intent>Greeting</intent><Response>nHi there, how can I help you today?</Response></example>n</instructions>nnPlease complete this request according to the instructions and examples provided above:<request><Conversation>REPLACEME</Conversation></request>nnAssistant:n", "max_tokens_to_sample": 250, "temperature": 1, "top_k": 250, "top_p": 0.75, "stop_sequences": ["nnHuman:", "nnhuman:", "nnCustomer:", "nncustomer:"]}"


  1. Сохраните этот шаблон в файл, чтобы загрузить его на Amazon S3 и вызвать функцию Lambda при необходимости. Сохраните шаблоны как сериализованные строки JSON в текстовом файле. На предыдущем снимке экрана показан пример кода, позволяющий сделать это.
  2. Повторите те же действия с другими шаблонами.

Ниже приведены несколько снимков экрана других шаблонов и результатов вызова Amazon Bedrock с использованием некоторых из них.

Ниже приведен код Prompt_template_request_validator.txt:

"{"prompt": "Human: I will give you some instructions to complete my request.n<instructions>Given the context do the following steps: 1. verify that the items in the input are valid. If customer provided an invalid item, recommend replacing it with a valid one. 2. verify that the customer has provided all the information marked as required. If the customer missed a required information, ask the customer for that information. 3. When the order is complete, provide a summary of the order and ask for confirmation always using this phrase: 'is this correct?' 4. If the customer confirms the order, Do not ask for confirmation again, just say the phrase inside the brackets [Great, Give me a moment while I try to process your order]</instructions>n<context>nThe VALID MENU ITEMS are: [latte, frappe, mocha, espresso, cappuccino, romano, americano].nThe VALID OPTIONS are: [splenda, stevia, raw sugar, honey, whipped cream, sugar, oat milk, soy milk, regular milk, skimmed milk, whole milk, 2 percent milk, almond milk].nThe required information is: size. Size can be: small, medium, large.nHere are some examples: <example>H: I would like a medium latte with 1 Splenda and a small romano with no sugar please.nnA: <Validation>:nThe Human is ordering a medium latte with one splenda. Latte is a valid menu item and splenda is a valid option. The Human is also ordering a small romano with no sugar. Romano is a valid menu item.</Validation>n<Response>nOk, I got: nt-Medium Latte with 1 Splenda and.nt-Small Romano with no Sugar.nIs this correct?</Response>nnH: yep.nnA:n<Response>nGreat, Give me a moment while I try to process your order</example>nn<example>H: I would like a cappuccino and a mocha please.nnA: <Validation>:nThe Human is ordering a cappuccino and a mocha. Both are valid menu items. The Human did not provide the size for the cappuccino. The human did not provide the size for the mocha. I will ask the Human for the required missing information.</Validation>n<Response>nSure thing, but can you please let me know the size for the Cappuccino and the size for the Mocha? We have Small, Medium, or Large.</Response></example>nn<example>H: I would like a small cappuccino and a large lemonade please.nnA: <Validation>:nThe Human is ordering a small cappuccino and a large lemonade. Cappuccino is a valid menu item. Lemonade is not a valid menu item. I will suggest the Human a replacement from our valid menu items.</Validation>n<Response>nSorry, we don't have Lemonades, would you like to order something else instead? Perhaps a Frappe or a Latte?</Response></example>nn<example>H: Can I get a medium frappuccino with sugar please?nnA: <Validation>:n The Human is ordering a Frappuccino. Frappuccino is not a valid menu item. I will suggest a replacement from the valid menu items in my context.</Validation>n<Response>nI am so sorry, but Frappuccino is not in our menu, do you want a frappe or a cappuccino instead? perhaps something else?</Response></example>nn<example>H: I want two large americanos and a small latte please.nnA: <Validation>:n The Human is ordering 2 Large Americanos, and a Small Latte. Americano is a valid menu item. Latte is a valid menu item.</Validation>n<Response>nOk, I got: nt-2 Large Americanos and.nt-Small Latte.nIs this correct?</Response>nnH: looks correct, yes.nnA:n<Response>nGreat, Give me a moment while I try to process your order.</Response></example>nn</Context>nnPlease complete this request according to the instructions and examples provided above:<request>REPLACEME</request>nnAssistant:n", "max_tokens_to_sample": 250, "temperature": 0.3, "top_k": 250, "top_p": 0.75, "stop_sequences": ["nnHuman:", "nnhuman:", "nnCustomer:", "nncustomer:"]}"

Ниже приведен наш ответ от Amazon Bedrock с использованием этого шаблона.

Ниже приведен код для prompt_template_object_creator.txt:

"{"prompt": "Human: I will give you some instructions to complete my request.n<instructions>Given the Conversation between Human and Assistant, you need to create a json object in Response with the appropriate attributes.nHere are some examples:n<example><Conversation> H: I want a latte.nnA:nCan I have the size?nnH: Medium.nnA: So, a medium latte.nIs this Correct?nnH: Yes.</Conversation>nnA:<Response>{"1":{"item":"latte","size":"medium","addOns":[]}}</Response></example>n<example><Conversation> H: I want a large frappe and 2 small americanos with sugar.nnA: Okay, let me confirm:nn1 large frappenn2 small americanos with sugarnnIs this correct?nnH: Yes.</Conversation>nnA:<Response>{"1":{"item":"frappe","size":"large","addOns":[]},"2":{"item":"americano","size":"small","addOns":["sugar"]},"3":{"item":"americano","size":"small","addOns":["sugar"]}}</Response>n</example>n<example><Conversation> H: I want a medium americano.nnA: Okay, let me confirm:nn1 medium americanonnIs this correct?nnH: Yes.</Conversation>nnA:<Response>{"1":{"item":"americano","size":"medium","addOns":[]}}</Response></example>n<example><Conversation> H: I want a large latte with oatmilk.nnA: Okay, let me confirm:nnLarge latte with oatmilknnIs this correct?nnH: Yes.</Conversation>nnA:<Response>{"1":{"item":"latte","size":"large","addOns":["oatmilk"]}}</Response></example>n<example><Conversation> H: I want a small mocha with no whipped cream please.nnA: Okay, let me confirm:nnSmall mocha with no whipped creamnnIs this correct?nnH: Yes.</Conversation>nnA:<Response>{"1":{"item":"mocha","size":"small","addOns":["no whipped cream"]}}</Response>nn</example></instructions>nnPlease complete this request according to the instructions and examples provided above:<request><Conversation>REPLACEME</Conversation></request>nnAssistant:n", "max_tokens_to_sample": 250, "temperature": 0.3, "top_k": 250, "top_p": 0.75, "stop_sequences": ["nnHuman:", "nnhuman:", "nnCustomer:", "nncustomer:"]}"


Ниже приведен код Prompt_template_order_summary.txt:

"{"prompt": "Human: I will give you some instructions to complete my request.n<instructions>Given the Conversation between Human and Assistant, you need to create a summary of the order with bullet points and include the order total.nHere are some examples:n<example><Conversation> H: I want a large frappe and 2 small americanos with sugar.nnA: Okay, let me confirm:nn1 large frappenn2 small americanos with sugarnnIs this correct?nnH: Yes.</Conversation>nn<OrderTotal>10.50</OrderTotal>nnA:<Response>nHere is a summary of your order along with the total:nn1 large frappenn2 small americanos with sugar.nYour Order total is $10.50</Response></example>n<example><Conversation> H: I want a medium americano.nnA: Okay, let me confirm:nn1 medium americanonnIs this correct?nnH: Yes.</Conversation>nn<OrderTotal>3.50</OrderTotal>nnA:<Response>nHere is a summary of your order along with the total:nn1 medium americano.nYour Order total is $3.50</Response></example>n<example><Conversation> H: I want a large latte with oat milk.nnA: Okay, let me confirm:nnLarge latte with oat milknnIs this correct?nnH: Yes.</Conversation>nn<OrderTotal>6.75</OrderTotal>nnA:<Response>nHere is a summary of your order along with the total:nnLarge latte with oat milk.nYour Order total is $6.75</Response></example>n<example><Conversation> H: I want a small mocha with no whipped cream please.nnA: Okay, let me confirm:nnSmall mocha with no whipped creamnnIs this correct?nnH: Yes.</Conversation>nn<OrderTotal>4.25</OrderTotal>nnA:<Response>nHere is a summary of your order along with the total:nnSmall mocha with no whipped cream.nYour Order total is $6.75</Response>nn</example>n</instructions>nnPlease complete this request according to the instructions and examples provided above:<request><Conversation>REPLACEME</Conversation>nn<OrderTotal>REPLACETOTAL</OrderTotal></request>nnAssistant:n", "max_tokens_to_sample": 250, "temperature": 0.3, "top_k": 250, "top_p": 0.75, "stop_sequences": ["nnHuman:", "nnhuman:", "nnCustomer:", "nncustomer:", "[Conversation]"]}"


Как видите, мы использовали наши шаблоны подсказок для проверки пунктов меню, выявления недостающей необходимой информации, создания структуры данных и обобщения заказа. Базовые модели, доступные на Amazon Bedrock, очень эффективны, поэтому с помощью этих шаблонов вы сможете выполнять еще больше задач.

Вы завершили разработку подсказок и сохранили шаблоны в текстовые файлы. Теперь вы можете приступить к созданию бота Amazon Lex и связанных с ним функций Lambda.

Создайте слой Lambda с шаблонами подсказок.

Выполните следующие шаги, чтобы создать слой Lambda:

  1. В SageMaker Studio создайте новую папку с подпапкой с именем python.
  2. Скопируйте файлы подсказок в папку python папку.

  1. Вы можете добавить библиотеку ZIP в экземпляр своего блокнота, выполнив следующую команду.
!conda install -y -c conda-forge zip

  1. Теперь выполните следующую команду, чтобы создать ZIP-файл для загрузки на слой Lambda.
!zip -r prompt_templates_layer.zip prompt_templates_layer/.

  1. После создания ZIP-файла вы можете скачать его. Перейдите в Lambda, создайте новый слой, загрузив файл напрямую или сначала загрузив его на Amazon S3.
  2. Затем прикрепите этот новый слой к функции оркестрации Lambda.

Теперь файлы шаблонов приглашений хранятся локально в среде выполнения Lambda. Это ускорит процесс во время работы вашего бота.

Создайте слой Lambda с необходимыми библиотеками.

Выполните следующие шаги, чтобы создать слой Lambda с необходимыми библиотеками:

  1. Открыть Облако AWS9 среде экземпляра, создайте папку с подпапкой с именем python.
  2. Откройте терминал внутри python папку.
  3. Выполните следующие команды из терминала:
pip install “boto3>=1.28.57” -t .
pip install “awscli>=1.29.57" -t .
pip install “botocore>=1.31.57” -t .

  1. Run cd .. и поместите себя в новую папку, где у вас также есть python вложенная папка.
  2. Выполните следующую команду:
zip -r lambda-layer.zip

  1. После создания ZIP-файла вы можете скачать его. Перейдите в Lambda, создайте новый слой, загрузив файл напрямую или сначала загрузив его на Amazon S3.
  2. Затем прикрепите этот новый слой к функции оркестрации Lambda.

Создайте бота в Amazon Lex v2.

Для этого варианта использования мы создаем бот Amazon Lex, который может предоставить интерфейс ввода-вывода для архитектуры, чтобы вызывать Amazon Bedrock с помощью голоса или текста из любого интерфейса. Поскольку LLM будет обрабатывать часть разговора этого агента обработки заказов, а Lambda будет организовывать рабочий процесс, вы можете создать бота с тремя намерениями и без слотов.

  1. В консоли Amazon Lex создайте нового бота с помощью метода Создать пустого бота.

Теперь вы можете добавить намерение с любым подходящим начальным высказыванием, чтобы конечные пользователи могли начать разговор с ботом. Мы используем простые приветствия и добавляем первоначальный ответ бота, чтобы конечные пользователи могли отправлять свои запросы. При создании бота обязательно используйте перехватчик кода Lambda; это запустит функцию Lambda, которая будет организовывать рабочий процесс между клиентом, Amazon Lex и LLM.

  1. Добавьте свое первое намерение, которое запускает рабочий процесс и использует шаблон запроса проверки намерения для вызова Amazon Bedrock и определения того, чего пытается достичь клиент. Добавьте несколько простых фраз, чтобы конечные пользователи могли начать разговор.

Вам не нужно использовать какие-либо слоты или начальное чтение ни в одном из намерений бота. На самом деле, вам не нужно добавлять высказывания ко второму или третьему намерению. Это потому, что LLM будет руководить Lambda на протяжении всего процесса.

  1. Добавьте запрос подтверждения. Вы можете настроить это сообщение в функции Lambda позже.

  1. Под Перехватчики кода, наведите на Используйте функцию Lambda для инициализации и проверки.

  1. Создайте второе намерение без произнесения слов и без первоначального ответа. Это PlaceOrder намерение.

Когда LLM определяет, что клиент пытается разместить заказ, функция Lambda инициирует это намерение и проверяет запрос клиента на соответствие меню, а также проверяет отсутствие необходимой информации. Помните, что все это находится в шаблонах приглашений, поэтому вы можете адаптировать этот рабочий процесс для любого варианта использования, изменив шаблоны приглашений.

  1. Не добавляйте никаких слотов, но добавьте запрос на подтверждение и отклоните ответ.

  1. Выберите Используйте функцию Lambda для инициализации и проверки.

  1. Создайте третье намерение с именем ProcessOrder без образцов высказываний и без слотов.
  2. Добавьте первоначальный ответ, запрос на подтверждение и ответ на отказ.

После того как LLM подтвердил запрос клиента, функция Lambda запускает третье и последнее намерение обработать заказ. Здесь Lambda будет использовать шаблон создателя объекта для создания структуры данных JSON заказа для запроса таблицы DynamoDB, а затем использовать шаблон сводки заказа для суммирования всего заказа вместе с общей суммой, чтобы Amazon Lex мог передать его клиенту.

  1. Выберите Используйте функцию Lambda для инициализации и проверки. При этом можно использовать любую функцию Lambda для обработки заказа после того, как клиент дал окончательное подтверждение.

  1. После того, как вы создадите все три намерения, перейдите в визуальный конструктор для ValidateIntent, добавьте этап перехода к намерению и подключите к нему вывод положительного подтверждения.
  2. После добавления намерения перехода отредактируйте его и выберите намерение PlaceOrder в качестве имени намерения.

  1. Аналогично, чтобы перейти к Visual builder для PlaceOrder намерение и соединить вывод положительного подтверждения с ProcessOrder идти к намерению. Редактирование не требуется для ProcessOrder намерение.
  2. Теперь вам нужно создать функцию Lambda, которая управляет Amazon Lex и вызывает таблицу DynamoDB, как подробно описано в следующем разделе.

Создайте функцию Lambda для управления ботом Amazon Lex.

Теперь вы можете создать функцию Lambda, которая управляет ботом и рабочим процессом Amazon Lex. Выполните следующие шаги:

  1. Создайте функцию Lambda со стандартной политикой выполнения и позвольте Lambda создать для вас роль.
  2. В окне кода вашей функции добавьте несколько служебных функций, которые помогут: отформатируйте подсказки, добавив контекст lex в шаблон, вызовите API Amazon Bedrock LLM, извлеките нужный текст из ответов и многое другое. См. следующий код:
import json
import re
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

bedrock = boto3.client(service_name='bedrock-runtime')
def CreatingCustomPromptFromLambdaLayer(object_key,replace_items):
   
    folder_path = '/opt/order_processing_agent_prompt_templates/python/'
    try:
        file_path = folder_path + object_key
        with open(file_path, "r") as file1:
            raw_template = file1.read()
            # Modify the template with the custom input prompt
            #template['inputs'][0].insert(1, {"role": "user", "content": '### Input:n' + user_request})
            for key,value in replace_items.items():
                value = json.dumps(json.dumps(value).replace('"','')).replace('"','')
                raw_template = raw_template.replace(key,value)
            modified_prompt = raw_template

            return modified_prompt
    except Exception as e:
        return {
            'statusCode': 500,
            'body': f'An error occurred: {str(e)}'
        }
def CreatingCustomPrompt(object_key,replace_items):
    logger.debug('replace_items is: {}'.format(replace_items))
    #retrieve user request from intent_request
    #we first propmt the model with current order
    
    bucket_name = 'your-bucket-name'
    
    #object_key = 'prompt_template_order_processing.txt'
    try:
        s3 = boto3.client('s3')
        # Retrieve the existing template from S3
        response = s3.get_object(Bucket=bucket_name, Key=object_key)
        raw_template = response['Body'].read().decode('utf-8')
        raw_template = json.loads(raw_template)
        logger.debug('raw template is {}'.format(raw_template))
        #template_json = json.loads(raw_template)
        #logger.debug('template_json is {}'.format(template_json))
        #template = json.dumps(template_json)
        #logger.debug('template is {}'.format(template))

        # Modify the template with the custom input prompt
        #template['inputs'][0].insert(1, {"role": "user", "content": '### Input:n' + user_request})
        for key,value in replace_items.items():
            raw_template = raw_template.replace(key,value)
            logger.debug("Replacing: {} nwith: {}".format(key,value))
        modified_prompt = json.dumps(raw_template)
        logger.debug("Modified template: {}".format(modified_prompt))
        logger.debug("Modified template type is: {}".format(print(type(modified_prompt))))
        
        #modified_template_json = json.loads(modified_prompt)
        #logger.debug("Modified template json: {}".format(modified_template_json))
        
        return modified_prompt
    except Exception as e:
        return {
            'statusCode': 500,
            'body': f'An error occurred: {str(e)}'
        }
    
def validate_intent(intent_request):
    logger.debug('starting validate_intent: {}'.format(intent_request))
    #retrieve user request from intent_request
    user_request = 'Human: ' + intent_request['inputTranscript'].lower()
    #getting current context variable
    current_session_attributes =  intent_request['sessionState']['sessionAttributes']
    if len(current_session_attributes) > 0:
        full_context = current_session_attributes['fullContext'] + 'nn' + user_request
        dialog_context = current_session_attributes['dialogContext'] + 'nn' + user_request
    else:
        full_context = user_request
        dialog_context = user_request
    #Preparing validation prompt by adding context to prompt template
    object_key = 'prompt_template_intent_validator.txt'
    #replace_items = {"REPLACEME":full_context}
    #replace_items = {"REPLACEME":dialog_context}
    replace_items = {"REPLACEME":dialog_context}
    #validation_prompt = CreatingCustomPrompt(object_key,replace_items)
    validation_prompt = CreatingCustomPromptFromLambdaLayer(object_key,replace_items)

    #Prompting model for request validation
    intent_validation_completion = prompt_bedrock(validation_prompt)
    intent_validation_completion = re.sub(r'["]','',intent_validation_completion)

    #extracting response from response completion and removing some special characters
    validation_response = extract_response(intent_validation_completion)
    validation_intent = extract_intent(intent_validation_completion)
    
    

    #business logic depending on intents
    if validation_intent == 'Place Order':
        return validate_request(intent_request)
    elif validation_intent in ['Complain','Speak to Someone']:
        ##adding session attributes to keep current context
        full_context = full_context + 'nn' + intent_validation_completion
        dialog_context = dialog_context + 'nnAssistant: ' + validation_response
        intent_request['sessionState']['sessionAttributes']['fullContext'] = full_context
        intent_request['sessionState']['sessionAttributes']['dialogContext'] = dialog_context
        intent_request['sessionState']['sessionAttributes']['customerIntent'] = validation_intent
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'Fulfilled','Close',validation_response)
    if validation_intent == 'Greeting':
        ##adding session attributes to keep current context
        full_context = full_context + 'nn' + intent_validation_completion
        dialog_context = dialog_context + 'nnAssistant: ' + validation_response
        intent_request['sessionState']['sessionAttributes']['fullContext'] = full_context
        intent_request['sessionState']['sessionAttributes']['dialogContext'] = dialog_context
        intent_request['sessionState']['sessionAttributes']['customerIntent'] = validation_intent
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'InProgress','ConfirmIntent',validation_response)

def validate_request(intent_request):
    logger.debug('starting validate_request: {}'.format(intent_request))
    #retrieve user request from intent_request
    user_request = 'Human: ' + intent_request['inputTranscript'].lower()
    #getting current context variable
    current_session_attributes =  intent_request['sessionState']['sessionAttributes']
    if len(current_session_attributes) > 0:
        full_context = current_session_attributes['fullContext'] + 'nn' + user_request
        dialog_context = current_session_attributes['dialogContext'] + 'nn' + user_request
    else:
        full_context = user_request
        dialog_context = user_request
   
    #Preparing validation prompt by adding context to prompt template
    object_key = 'prompt_template_request_validator.txt'
    replace_items = {"REPLACEME":dialog_context}
    #validation_prompt = CreatingCustomPrompt(object_key,replace_items)
    validation_prompt = CreatingCustomPromptFromLambdaLayer(object_key,replace_items)

    #Prompting model for request validation
    request_validation_completion = prompt_bedrock(validation_prompt)
    request_validation_completion = re.sub(r'["]','',request_validation_completion)

    #extracting response from response completion and removing some special characters
    validation_response = extract_response(request_validation_completion)

    ##adding session attributes to keep current context
    full_context = full_context + 'nn' + request_validation_completion
    dialog_context = dialog_context + 'nnAssistant: ' + validation_response
    intent_request['sessionState']['sessionAttributes']['fullContext'] = full_context
    intent_request['sessionState']['sessionAttributes']['dialogContext'] = dialog_context
    
    return close(intent_request['sessionState']['sessionAttributes'],'PlaceOrder','InProgress','ConfirmIntent',validation_response)
    
def process_order(intent_request):
    logger.debug('starting process_order: {}'.format(intent_request))

     #retrieve user request from intent_request
    user_request = 'Human: ' + intent_request['inputTranscript'].lower()
    #getting current context variable
    current_session_attributes =  intent_request['sessionState']['sessionAttributes']
    if len(current_session_attributes) > 0:
        full_context = current_session_attributes['fullContext'] + 'nn' + user_request
        dialog_context = current_session_attributes['dialogContext'] + 'nn' + user_request
    else:
        full_context = user_request
        dialog_context = user_request
    #   Preparing object creator prompt by adding context to prompt template
    object_key = 'prompt_template_object_creator.txt'
    replace_items = {"REPLACEME":dialog_context}
    #object_creator_prompt = CreatingCustomPrompt(object_key,replace_items)
    object_creator_prompt = CreatingCustomPromptFromLambdaLayer(object_key,replace_items)
    #Prompting model for object creation
    object_creation_completion = prompt_bedrock(object_creator_prompt)
    #extracting response from response completion
    object_creation_response = extract_response(object_creation_completion)
    inputParams = json.loads(object_creation_response)
    inputParams = json.dumps(json.dumps(inputParams))
    logger.debug('inputParams is: {}'.format(inputParams))
    client = boto3.client('lambda')
    response = client.invoke(FunctionName = 'arn:aws:lambda:us-east-1:<AccountNumber>:function:aws-blog-order-validator',InvocationType = 'RequestResponse',Payload = inputParams)
    responseFromChild = json.load(response['Payload'])
    validationResult = responseFromChild['statusCode']
    if validationResult == 205:
        order_validation_error = responseFromChild['validator_response']
        return close(intent_request['sessionState']['sessionAttributes'],'PlaceOrder','InProgress','ConfirmIntent',order_validation_error)
    #invokes Order Processing lambda to query DynamoDB table and returns order total
    response = client.invoke(FunctionName = 'arn:aws:lambda:us-east-1: <AccountNumber>:function:aws-blog-order-processing',InvocationType = 'RequestResponse',Payload = inputParams)
    responseFromChild = json.load(response['Payload'])
    orderTotal = responseFromChild['body']
    ###Prompting the model to summarize the order along with order total
    object_key = 'prompt_template_order_summary.txt'
    replace_items = {"REPLACEME":dialog_context,"REPLACETOTAL":orderTotal}
    #order_summary_prompt = CreatingCustomPrompt(object_key,replace_items)
    order_summary_prompt = CreatingCustomPromptFromLambdaLayer(object_key,replace_items)
    order_summary_completion = prompt_bedrock(order_summary_prompt)
    #extracting response from response completion
    order_summary_response = extract_response(order_summary_completion)  
    order_summary_response = order_summary_response + '. Shall I finalize processing your order?'
    ##adding session attributes to keep current context
    full_context = full_context + 'nn' + order_summary_completion
    dialog_context = dialog_context + 'nnAssistant: ' + order_summary_response
    intent_request['sessionState']['sessionAttributes']['fullContext'] = full_context
    intent_request['sessionState']['sessionAttributes']['dialogContext'] = dialog_context
    return close(intent_request['sessionState']['sessionAttributes'],'ProcessOrder','InProgress','ConfirmIntent',order_summary_response)
    

""" --- Main handler and Workflow functions --- """

def lambda_handler(event, context):
    """
    Route the incoming request based on intent.
    The JSON body of the request is provided in the event slot.
    """
    logger.debug('event is: {}'.format(event))

    return dispatch(event)

def dispatch(intent_request):
    """
    Called when the user specifies an intent for this bot. If intent is not valid then returns error name
    """
    logger.debug('intent_request is: {}'.format(intent_request))
    intent_name = intent_request['sessionState']['intent']['name']
    confirmation_state = intent_request['sessionState']['intent']['confirmationState']
    # Dispatch to your bot's intent handlers
    if intent_name == 'ValidateIntent' and confirmation_state == 'None':
        return validate_intent(intent_request)
    if intent_name == 'PlaceOrder' and confirmation_state == 'None':
        return validate_request(intent_request)
    elif intent_name == 'PlaceOrder' and confirmation_state == 'Confirmed':
        return process_order(intent_request)
    elif intent_name == 'PlaceOrder' and confirmation_state == 'Denied':
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'Fulfilled','Close','Got it. Let me know if I can help you with something else.')
    elif intent_name == 'PlaceOrder' and confirmation_state not in ['Denied','Confirmed','None']:
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'Fulfilled','Close','Sorry. I am having trouble completing the request. Let me get someone to help you.')
        logger.debug('exiting intent {} here'.format(intent_request['sessionState']['intent']['name']))
    elif intent_name == 'ProcessOrder' and confirmation_state == 'None':
        return validate_request(intent_request)
    elif intent_name == 'ProcessOrder' and confirmation_state == 'Confirmed':
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'Fulfilled','Close','Perfect! Your order has been processed. Please proceed to payment.')
    elif intent_name == 'ProcessOrder' and confirmation_state == 'Denied':
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'Fulfilled','Close','Got it. Let me know if I can help you with something else.')
    elif intent_name == 'ProcessOrder' and confirmation_state not in ['Denied','Confirmed','None']:
        return close(intent_request['sessionState']['sessionAttributes'],intent_request['sessionState']['intent']['name'],'Fulfilled','Close','Sorry. I am having trouble completing the request. Let me get someone to help you.')
        logger.debug('exiting intent {} here'.format(intent_request['sessionState']['intent']['name']))
    raise Exception('Intent with name ' + intent_name + ' not supported')
    
def prompt_bedrock(formatted_template):
    logger.debug('prompt bedrock input is:'.format(formatted_template))
    body = json.loads(formatted_template)

    modelId = 'anthropic.claude-v2' # change this to use a different version from the model provider
    accept = 'application/json'
    contentType = 'application/json'

    response = bedrock.invoke_model(body=body, modelId=modelId, accept=accept, contentType=contentType)
    response_body = json.loads(response.get('body').read())
    response_completion = response_body.get('completion')
    logger.debug('response is: {}'.format(response_completion))

    #print_ww(response_body.get('completion'))
    #print(response_body.get('results')[0].get('outputText'))
    return response_completion

#function to extract text between the <Response> and </Response> tags within model completion
def extract_response(response_completion):
    
    if '<Response>' in response_completion:
        customer_response = response_completion.replace('<Response>','||').replace('</Response>','').split('||')[1]
        
        logger.debug('modified response is: {}'.format(response_completion))

        return customer_response
    else:
        
        logger.debug('modified response is: {}'.format(response_completion))

        return response_completion
        
#function to extract text between the <Response> and </Response> tags within model completion
def extract_intent(response_completion):
    if '<intent>' in response_completion:
        customer_intent = response_completion.replace('<intent>','||').replace('</intent>','||').split('||')[1]
        return customer_intent
    else:
        return customer_intent
        
def close(session_attributes, intent, fulfillment_state, action_type, message):
    #This function prepares the response in the appropiate format for Lex V2

    response = {
        "sessionState": {
            "sessionAttributes":session_attributes,
            "dialogAction": {
                "type": action_type
            },
            "intent": {
                "name":intent,
                "state":fulfillment_state
                
            },
            
            },
        "messages":
            [{
                "contentType":"PlainText",
                "content":message,
            }]
            ,
        
    }
    return response

  1. Прикрепите к этой функции слой Lambda, созданный ранее.
  2. Кроме того, прикрепите слой к созданным вами шаблонам подсказок.
  3. В роли исполнения Lambda прикрепите политику доступа к Amazon Bedrock, созданную ранее.

Роль выполнения Lambda должна иметь следующие разрешения.

Прикрепите функцию Orchestration Lambda к боту Amazon Lex.

  1. После создания функции в предыдущем разделе вернитесь в консоль Amazon Lex и перейдите к своему боту.
  2. Под Языки на панели навигации выберите Английский.
  3. Что касается Источник, выберите своего бота для обработки заказов.
  4. Что касается Версия или псевдоним лямбда-функции, выберите ПОСЛЕДНИЕ.
  5. Выберите Сохранить.

Создание вспомогательных функций Lambda

Выполните следующие шаги, чтобы создать дополнительные функции Lambda:

  1. Создайте функцию Lambda для запроса таблицы DynamoDB, которую вы создали ранее:
import json
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# Initialize the DynamoDB client
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('your-table-name')

def calculate_grand_total(input_data):
    # Initialize the total price
    total_price = 0
    
    try:
        # Loop through each item in the input JSON
        for item_id, item_data in input_data.items():
            item_name = item_data['item'].lower()  # Convert item name to lowercase
            item_size = item_data['size'].lower()  # Convert item size to lowercase
            
            # Query the DynamoDB table for the item based on Item and Size
            response = table.get_item(
                Key={'Item': item_name,
                    'Size': item_size}
            )
            
            # Check if the item was found in the table
            if 'Item' in response:
                item = response['Item']
                price = float(item['Price'])
                total_price += price  # Add the item's price to the total
    
        return total_price
    except Exception as e:
        raise Exception('An error occurred: {}'.format(str(e)))

def lambda_handler(event, context):
    try:
       
        # Parse the input JSON from the Lambda event
        input_json = json.loads(event)

        # Calculate the grand total
        grand_total = calculate_grand_total(input_json)
    
        # Return the grand total in the response
        return {'statusCode': 200,'body': json.dumps(grand_total)}
    except Exception as e:
        return {
            'statusCode': 500,
            'body': json.dumps('An error occurred: {}'.format(str(e)))

  1. Перейдите в Конфигурация вкладку в функции «Лямбда» и выберите Разрешения....
  2. Прикрепите заявление о политике на основе ресурсов, позволяющее функции Lambda обработки заказов вызывать эту функцию.

  1. Перейдите к роли выполнения IAM для этой функции Lambda и добавьте политику для доступа к таблице DynamoDB.

  1. Создайте еще одну функцию Lambda, чтобы проверить, были ли переданы все необходимые атрибуты от клиента. В следующем примере мы проверяем, зафиксирован ли атрибут размера для заказа:
import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

def lambda_handler(event, context):
    # Define customer orders from the input event
    customer_orders = json.loads(event)

    # Initialize a list to collect error messages
    order_errors = {}
    missing_size = []
    error_messages = []
    # Iterate through each order in customer_orders
    for order_id, order in customer_orders.items():
        if "size" not in order or order["size"] == "":
            missing_size.append(order['item'])
            order_errors['size'] = missing_size
    if order_errors:
        items_missing_size = order_errors['size']
        error_message = f"could you please provide the size for the following items: {', '.join(items_missing_size)}?"
        error_messages.append(error_message)

    # Prepare the response message
    if error_messages:
        response_message = "n".join(error_messages)
        return {
        'statusCode': 205,
        'validator_response': response_message
            }   
    else:
        response_message = "Order is validated successfully"
        return {
        'statusCode': 200,
        'validator_response': response_message
        }

  1. Перейдите в Конфигурация вкладку в функции «Лямбда» и выберите Разрешения....
  2. Прикрепите заявление о политике на основе ресурсов, позволяющее функции Lambda обработки заказов вызывать эту функцию.

Проверить решение

Теперь мы можем протестировать решение на примерах заказов, которые клиенты размещают через Amazon Lex.

В нашем первом примере клиент попросил фраппучино, которого нет в меню. Модель проверяется с помощью шаблона валидатора заказов и предлагает некоторые рекомендации на основе меню. После того как клиент подтвердит свой заказ, он уведомляется об общей сумме и сводке заказа. Заказ будет обработан на основании окончательного подтверждения клиента.

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

В нашем последнем примере клиент разместил заказ на несколько товаров, и для нескольких товаров не указан размер. Модель и функция Lambda проверят наличие всех необходимых атрибутов для обработки заказа, а затем попросят клиента предоставить недостающую информацию. После того как клиент предоставит недостающую информацию (в данном случае размер кофе), ему будет показана общая сумма заказа и сводная информация о заказе. Заказ будет обработан на основании окончательного подтверждения клиента.

Ограничения LLM

Результаты LLM носят стохастический характер, а это означает, что результаты нашего LLM могут различаться по формату или даже по форме ложного содержания (галлюцинаций). Поэтому разработчикам необходимо полагаться на хорошую логику обработки ошибок во всем своем коде, чтобы справиться с этими сценариями и избежать ухудшения качества работы конечных пользователей.

Убирать

Если вам больше не нужно это решение, вы можете удалить следующие ресурсы:

  • Лямбда-функции
  • Коробка Амазон Лекс
  • Таблица DynamoDB
  • Ковш S3

Кроме того, закройте экземпляр SageMaker Studio, если приложение больше не требуется.

Оценка стоимости

Информацию о ценах на основные службы, используемые этим решением, см. ниже:

Обратите внимание, что вы можете использовать Claude v2 без необходимости подготовки, поэтому общие затраты остаются минимальными. Чтобы еще больше сократить расходы, вы можете настроить таблицу DynamoDB на настройку по требованию.

Заключение

В этом посте показано, как создать агент обработки заказов с поддержкой искусственного интеллекта, использующий Amazon Lex, Amazon Bedrock и другие сервисы AWS. Мы показали, как оперативное проектирование с помощью мощной генеративной модели искусственного интеллекта, такой как Claude, может обеспечить надежное понимание естественного языка и потоки разговоров для обработки заказов без необходимости использования обширных обучающих данных.

В архитектуре решения используются бессерверные компоненты, такие как Lambda, Amazon S3 и DynamoDB, что обеспечивает гибкую и масштабируемую реализацию. Хранение шаблонов приглашений в Amazon S3 позволяет настроить решение для различных вариантов использования.

Следующие шаги могут включать расширение возможностей агента для обработки более широкого спектра запросов клиентов и крайних случаев. Шаблоны подсказок позволяют итеративно улучшать навыки оператора. Дополнительные настройки могут включать интеграцию данных о заказах с серверными системами, такими как инвентаризация, CRM или POS. Наконец, агент может быть доступен в различных точках взаимодействия с клиентами, таких как мобильные приложения, автокассы, киоски и т. д., используя многоканальные возможности Amazon Lex.

Чтобы узнать больше, обратитесь к следующим соответствующим ресурсам:

  • Развертывание и управление многоканальными ботами:
  • Оперативное проектирование Клода и других моделей:
  • Бессерверные архитектурные шаблоны для масштабируемых помощников искусственного интеллекта:

Об авторах

Мумита Дутта является партнером-архитектором решений в Amazon Web Services. В своей роли она тесно сотрудничает с партнерами в разработке масштабируемых и повторно используемых ресурсов, которые оптимизируют развертывание облачных технологий и повышают операционную эффективность. Она является членом сообщества AI/ML и экспертом по генеративному искусственному интеллекту в AWS. В свободное время она любит заниматься садоводством и кататься на велосипеде.

Фернандо Ламмолья — архитектор партнерских решений в Amazon Web Services. Он тесно сотрудничает с партнерами AWS, возглавляя разработку и внедрение передовых решений искусственного интеллекта в бизнес-подразделениях. Стратегический лидер с опытом работы в облачной архитектуре, генеративном искусственном интеллекте, машинном обучении и анализе данных. Он специализируется на реализации стратегий выхода на рынок и предоставлении эффективных решений искусственного интеллекта, соответствующих целям организации. В свободное время любит проводить время с семьей и путешествовать по другим странам.

Митул Патель — старший архитектор решений в Amazon Web Services. В своей роли специалиста по облачным технологиям он работает с клиентами, чтобы понять их цели и проблемы, а также предоставляет рекомендации по достижению их целей с помощью предложений AWS. Он является членом сообщества AI/ML и послом генеративного искусственного интеллекта в AWS. В свободное время он любит ходить в походы и играть в футбол.

Spot_img

Последняя разведка

Spot_img