简介:

小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案,帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践,并应用到自己的日常工作里。

本次介绍的是如何利用亚马逊云科技一系列热门AI和云原生服务,如Amazon Bedrock、Amazon Kendra,开发一个AI对话机器人,本文将通过真实样例和场景帮助大家0基础学会AI核心技能。本架构设计全部采用了云原生Serverless架构,提供可扩展和安全的AI解决方案。通过Amazon API Gateway、AWS Amplify和AWS Lambda将应用程序与AI模型Mistral 7B集成,并利用Amazon Titan向量模型和向量库Kendra和RAG增强对话机器人和用户交互的内容。

本方案的解决方案架构图如下:

本方案主要服务介绍:

Amazon Bedrock

Amazon Bedrock 是亚马逊云科技上的生成式AI托管平台,专为开发者打造,用于快速构建和部署AI模型。它支持多种基础模型,简化了AI应用的开发过程。Bedrock的优势在于其高度可扩展性和易用性,帮助开发者快速从概念到产品,降低AI模型的开发门槛。

Amazon Kendra

Amazon Kendra 是一款智能搜索向量库,利用机器学习技术提供精准的搜索结果。它能够集成多个数据源,提供自然语言处理功能,帮助在RAG场景下提高信息检索效率。Kendra 的优势在于其智能的语义搜索和强大的数据集成能力,为用户提供直观的搜索体验。

AWS Amplify

AWS Amplify 是一个全面的开发平台,支持Web和移动应用的构建、部署和托管。它提供了丰富的工具和服务,如认证、存储、API等,简化了应用的开发流程。Amplify 的优势在于其无缝的集成和自动化SDLC扣成能力,使开发者能够快速构建高质量的应用。

Amazon API Gateway

Amazon API Gateway 是一种托管服务,允许开发者轻松创建、发布、维护和保护API。它提供了流量控制、授权、访问控制等功能,支持RESTful和WebSocket API。API Gateway 的优势在于其高效的流量管理和安全性,适用于各种用户规模的网页应用。

Amazon Cognito

Amazon Cognito 是一款用户身份验证和管理服务,支持用户注册、登录和访问控制。它能够与社交媒体登录、企业身份验证集成,简化用户管理流程。Cognito 的优势在于其强大的安全性和可扩展性,帮助开发者轻松实现用户身份验证功能。

AWS Lambda

AWS Lambda 是一种无服务器计算服务,允许开发者在无需管理服务器(Serverless)的情况下运行代码。Lambda 自动扩展,按实际计算时间和使用的计算资源收费,支持多种编程语言。其优势在于节省运维成本、提高开发效率和灵活的扩展能力,非常适合事件驱动的应用程序。

本方案包括的内容:

使用 Amazon Kendra 为文档建立索引以实现高效检索,并通过Kendra API 进行向量检索获取相关内容。

使用 AWS Amplify 和 Amazon CloudFront 作为前端服务器,托管聊天机器人应用程序。

通过 Amazon API Gateway 调用 AWS Lambda 函数,以获取向量库检索和后端大模型推理,进行 RAG(Retrieve and Generate)和提示工程。

学习设计提示词,并利用提示词调和 Amazon Kendra API 返回的文档搜索内容调用 Amazon Bedrock 上的大模型。

项目搭建具体步骤:

1. 首先我们进入Amazon Kendra

2. 建立Index索引,用于加快搜素效率

3. 为索引起名“kendra-index”,并选择Kendra需要访问的权限,其他配置选择模型后创建索引

4.接下来我们为向量库添加数据源,点击“Add Data Source”

5. 选择S3作为数据源

6. 为数据源配置一个昵称“kendra-data-source”,并选择文档语言为英文

7. 为访问数据源配置对应IAM权限

8. 选择保存文件的S3存储桶

9. 配置读取文档并向量化的模式和频率,最后点击添加数据源

10.  接下来我们打开自己偏好的CDE,运行下述BASH脚本部署我们的前端、后端、大模型服务、向量库基础设施。

#!/bin/bash
echo "Preparing Cloud9 for project deploymnet"

sudo yum install -y jq

echo "Update node js to version 16"
source "$HOME/.nvm/nvm.sh"
nvm install 16
nvm use 16
node --version

echo "Set region"
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
echo "AWS Region is $AWS_REGION"

echo "Build the backend code using sam build"
sam build

echo "Export S3 bucket name and Kendra index which are created as part of Startup CFN stack"
export S3BucketName=$(aws s3api list-buckets --query "Buckets[?contains(Name, 'kendra-docs')].Name" --output text)
export LabBucketName=$(aws s3api list-buckets --query "Buckets[?contains(Name, 'lab-code')].Name" --output text)
export KendraIndexID=$(aws kendra list-indices --query "IndexConfigurationSummaryItems[?contains(Name, 'kendra-index')].Id" --output text)
export LAMBDA_ROLE_ARN=$(aws iam  list-roles --query "Roles[?contains(RoleName, 'LambdaDeploymentRole')].Arn" --output text)


export SAMStackName="sam-bedrock-stack"
echo $SAMStackName

echo "Copy toml file and replace the parameters"

cp tools/samconf.toml samconfig.toml
# Replace values in .//samconfig.toml
sed -Ei "s|<KendraIndexId>|${KendraIndexID}|g" ./samconfig.toml
sed -Ei "s|<S3BucketName>|${S3BucketName}|g" ./samconfig.toml
sed -Ei "s|<LabBucketName>|${LabBucketName}|g" ./samconfig.toml
sed -Ei "s|<SAMStackName>|${SAMStackName}|g" ./samconfig.toml
sed -Ei "s|<AWS_REGION>|${AWS_REGION}|g" ./samconfig.toml
sed -Ei "s|<LambdaRole>|${LAMBDA_ROLE_ARN}|g" samconfig.toml

echo "Deploy app with sam deploy"
sam deploy

echo "Export few more parameters"
export BedrockApiUrl=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='BedrockApiUrl'].OutputValue" --output text)
export UserPoolId=$(aws cognito-idp list-user-pools --query "UserPools[?contains(Name, 'ChatbotUserPool')].Id"  --max-results 1 --output text)
export UserPoolClientId=$(aws cognito-idp list-user-pool-clients --user-pool-id ${UserPoolId}  --query "UserPoolClients[?contains(ClientName, 'ChatbotUserPoolClient')].ClientId"  --output text)

echo "Gateway endpoint: $BedrockApiUrl"
echo "Cognito user pool id: $UserPoolId"
echo "Cognito client id: $UserPoolClientId"

# Replace values in ./backend/samconfig.toml
sed -Ei "s|<ApiGatewayUrl>|${BedrockApiUrl}|g" ./frontend/src/main.js
sed -Ei "s|<CognitoUserPoolId>|${UserPoolId}|g" ./frontend/src/main.js
sed -Ei "s|<UserPoolClientId>|${UserPoolClientId}|g" ./frontend/src/main.js

#Install Ampliyfy and build frontend
echo "Install Ampliyfy and build frontend"
cd ~/environment/serverless-chatbot-code/frontend
npm i -S @vue/cli-service
npm i -g @aws-amplify/cli
npm install
npm run build
cp ~/.aws/credentials ~/.aws/config

#Amplify initialization
echo "Amplify initialization"
mv dist build
amplify init --yes


echo "Add hosting, hit enter key if it prompts for action, use default"
amplify add hosting parameters.json

echo "Publish the amplify project"
amplify publish --yes

11. 在本方案中,我们使用的是AWS SAM (Serverless Application Model)代码定义基础设施(IaC)创建云端资源。我们会在控制台中看到所有创建的资源信息,在亚马逊云科技平台上会创建对应的CloudFormation Stack堆栈,CloudFormation是亚马逊云科技的IaC服务。

12.部署成功后,该脚本会返回AI机器人服务的网页URL

13. 打开URL后,我们可以看到对话机器人的前端界面

14. 接下来我们创建一个Lambda函数,可以命名为“CognitoUserCreateFunction”,复制以下代码,利用用户管理服务Cognito创建一个网页用户

import boto3
import json
import secrets
import string
import os


REGION = os.environ['AWS_REGION']
ChatbotUserPool = os.getenv("Cognito_UserPool", None)
ChatbotUserPoolClient = os.getenv("Cognito_ClientID", None)
secrets_name = os.getenv("SECRET_ID", 'ui-credentials')
user_id = os.getenv("USER_ID", 'bedrock')


cognitoidentityserviceprovider = boto3.client('cognito-idp', region_name=REGION)

def generate_random_password(length=12):
    # Define character sets
    lowercase_letters = string.ascii_lowercase
    uppercase_letters = string.ascii_uppercase
    digits = string.digits
    special_characters = '!$@'

    # Ensure that the password includes at least one special character
    special_char = secrets.choice(special_characters)

    # Calculate the remaining length of the password
    remaining_length = length - 1

    # Generate the rest of the password with a mix of characters
    password_characters = (
        secrets.choice(lowercase_letters) +
        secrets.choice(uppercase_letters) +
        secrets.choice(digits) +
        ''.join(secrets.choice(lowercase_letters + uppercase_letters + digits + special_characters) for _ in range(remaining_length - 3))
    )

    # Shuffle the characters to make the password random
    password_list = list(password_characters)
    secrets.SystemRandom().shuffle(password_list)
    shuffled_password = ''.join(password_list)

    # Add the special character back at a random position
    position = secrets.randbelow(length)
    password = shuffled_password[:position] + special_char + shuffled_password[position:]

    return password

def create_secret(password):

    # Initialize the AWS Secrets Manager client
    client = boto3.client('secretsmanager')

    # Store the user ID and password in a dictionary
    secret_data = {
        'user_id': user_id,
        'password': password
    }

    # Create or update the secret in Secrets Manager
    try:
        response = client.create_secret(
            Name=secrets_name,
            SecretString=str(secret_data)
        )
        print("Secret created successfully!")
    except client.exceptions.ResourceExistsException:
        # If the secret already exists, update it
        response = client.update_secret(
            SecretId=secrets_name,
            SecretString=str(secret_data)
        )
        print("Secret updated successfully!")


def lambda_handler(event, context):
    status_msg = 'SUCCESS'
    try:
        password = generate_random_password()
        create_secret(password)
        
        try:
            cognitoidentityserviceprovider.admin_create_user(
                UserPoolId = ChatbotUserPool,
                Username = user_id, 
                UserAttributes = [
                    {"Name": "name", "Value": user_id}
                ]
            )
        except Exception as e1:
            print('cognito user exist already')

        cognitoidentityserviceprovider.admin_set_user_password(
            UserPoolId = ChatbotUserPool,
            Username = user_id, 
            Password = password,
            Permanent=True
        )
        status_msg = 'SUCCESS'
    except Exception as e:
        print('Error: ' + str(e))
        status_msg = 'FAILED'
        

    print('Successfully created user')


15. 运行代码后,我们成功创建了一个新的用户“bedrock”,在secret manager密码管理服务中可以获取用户的密码

16. 利用账户和密码登录刚刚展示的对话机器人网页

17. 最后我们就可以在对话机器人中和大模型实现问答了,本场景是未使用RAG的场景

18. 再次测试RAG场景

我们为机器人首先预定义提示词

Human:You are an intelligent AI advisor, and provide answers to questions by using fact based information. 
Use the following pieces of information to provide a concise answer to the question enclosed in <question> tags. 
Look for the contextual information enclosed in <context> tags.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
<context>{context}</context>
<question>{question}</question>
The response should be specific and use facts only.

Assistant:

我们在聊天框中提问“What is federal funds rate as of April 2024?”,机器人成功从文档中提取相关信息,回复了我们的问题

以上就是在亚马逊云科技上使用大语言模型、向量库和RAG技术搭建云原生AI对话机器人的解决方案。欢迎大家关注小李哥,未来获取更多国际前沿的生成式AI开发方案。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐