Amazon Bedrock 与多模态大语言模型Anthropic Claude 开箱 (上篇)

注意:本文已经针对2024年3月初新发布的Claude 3进行了更新。

本文介绍了Amazon Bedrock服务的初始化,如何开始使用Claude模型,并讲解了Prompt Engineer调优的最佳实践,以及几个简单场景样例代码。

本文分成上下两篇:

一、背景

Amazon Bedrock是托管的大模型服务,可通过API调用来自多家公司的基础模型(Foundation Model, 以下简称FM),包括AI21 Labs、Anthropic、Cohere、Meta、Stability AI以及Amazon等。使用Amazon Bedrock可通过模型微调(Fine-tune)和检索增强生成(Retrieval Augmented Generation,以下简称RAG)等技术实现更好的检索效果。由于Amazon Bedrock是无服务器的,因此您无需管理EC2虚拟机、GPU等基础设施,可使用多种开发语言从应用程序中调用,使用场景包括文本生成、文本摘要、图像生成、对话、虚拟助手、知识库、向量转换等。

在大模型的选择上,除了大家熟知的Meta(Facebook)的Llama2等模型之外,Bedrock提供了来自Anthropic的Claude模型,这是与OpenAI处于竞争地位的大模型。在2023年9月,Amazon向Anthropic投资40亿美金并且达成合作。

在模型版本选择上,Claude 3的版本对应如下:

  • Claude 3 Opus 对标GPT的下一代(截止2024年3月12日尚未发布),好于现有GPT4 Turbo
  • Claude 3 Sonnet在多个第三方评测中,与GPT4 Turbo互有胜负,但比GPT4-Turbo便宜
  • Claude 3 Haiku对表对标GPT3.5 Turbo,比3.5效果好很多

Claude 2的版本对应如下:

  • Claude V2.1 对标GPT4 Trubo
  • Claude Instant 对标GPT 3.5 Turbo,Titan Embeddings G1对标OpenAI的text-embedding-ada-002。他们支持的Token、相应的Cost都是在同等体量。

总结以上模型选择:一般文本生成应用场景,选用Claude 3 Sonnet性价比最高(与GPT4T互有胜负且便宜)。对于复杂逻辑例如包含数学题等,选择Claude 3 Opus效果最佳(超越GPT4T但贵)。对于访问量超大的内容审核等,选择最便宜的Claude 3 Haiku(比GPT3.5T更好且便宜)。

下边开始介绍使用。

二、申请模型访问权限并在AWS控制台上使用

注:以下操作以具备AWS账号AdministratorAccess的账号来操作。

1、确认联系信息为海外地址

申请Bedrock服务中的Claude大模型要求必须有AWS海外账户。根据Claude模型的EULA用户协议,使用Claude模型的公司需要是海外实体。因此这里申请时候,账户的联系信息登记的支付信息(Billing Address)等信息都应为海外地址,不能是中国大陆地址也不能是香港地址。如果之前注册账号的时候,使用了中国大陆地址注册或使用香港地址,则可以在账户信息页面编辑修改,使用公司的海外营业机构地址。比如改为贵公司在新加坡的门店、当地员工所在办公地址。

此处修改还涉及不同国家税费的问题,请咨询为您服务的客户经理和商务人员。

从右上角进入自己账户的Billing and Cost Managment界面。

向下移动页面中,找到联系信息,编辑他们,确保使用海外分支机构的地址和联系人。

2、申请模型权限

切换要操作的Region到特定区域,例如本文为us-west-2美西2俄勒冈区域。

接下来进入Bedrock服务界面,可看到其中的模型如果还没申请,状态就是Available to request。点击右上角的Model access按钮,点击右侧的Manage model access按钮发起申请。

推荐申请的模型包括:

  • Claude(对标OpenAI gpt-4-1106-preview4)
  • Claude Instant(对标OpenAI gpt-3.5-turbo-1106)
  • Claude 3 Sonnet(2024年3月最新大模型,效果好于GPT4)
  • Titan Embeddings G1 – Text 对标OpenAI的text-embedding-ada-002
  • Titan Multimodal Embeddings G1

申请时候要填写的表单格式如下截图。

填写以上用户场景信息。通过审核可能需要几分钟时间。然后即可看到如下模型的状态变为已授权。如下截图。

获得模型许可授权后,可以通过左侧的Playgrounds菜单,进入图形界面测试。点击Select model选择模型。如下截图。

在选择模型的清单中,点击Anthropic公司,选择Claude 3 Sonnet或者Claude V2.1,最大允许传入Context是200K的。点击Apply,即可在界面上发起交互。如下截图。

现在可以在控制台上使用了!

3、新模型申请权限的说明

需要注意的是,如果您以前只是申请过Claude2模型的权限,那么Claude3模型发布后,您需要重新给Claude3模型申请权限,才可以继续使用。如果您之前申请了Claude 3 Sonnet模型,那么当新的Claude 3 Haiku模型发布后,是需要重新申请权限的。而同一个名称的模型,如果只是尾号小版本迭代,是不需要重新申请的。

三、配置IAM和AKSK通过代码访问Bedrock API

Bedrock与OpenAI的ChatGPT调用认证方式不同。ChatGPT因为是独立的SaaS服务,其身份认证只依靠HTTPS的Header。Bedrock是属于整个AWS体系中的一个服务,因此其有着标准的Access Key/Secret Key的认证方式。此外,还需要匹配的IAM Policy才可以调用。下面进行配置。

1、创建API调用需要的IAM Policy

注意:以前配置的最小权限仅是调用Bedrock和Claude模型所需要的权限。如果您要使用Bedrock知识库,那么还涉及S3存储桶、OpenSearch向量数据库等服务,这时候建议您赋予Bedrock-FullAccess权限,调试更简单。调试后再适当缩减权限。

进入IAM服务,点击左侧Policy策略菜单,点击右上角创建策略按钮。

在向导第一步,策略编辑器位置,不使用可视化模式,而是点击JSON按钮,直接粘贴策略。如下截图。

策略如下:

{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "bedrock",
        "Effect": "Allow",
        "Action": [
            "bedrock:InvokeModel",
            "bedrock:InvokeModelWithResponseStream"
        ],
        "Resource": "arn:aws:bedrock:*::foundation-model/*"
    }
}

点击下一步继续,在策略名称位置,输入名称bedrock-runtime,记住这个名称,稍后将会使用。如下截图。

创建策略完成。

2、创建API所属的IAM用户并绑定IAM Policy

进入IAM服务,点击左侧菜单Users用户,点击右上角新建User按钮。如下截图。

在新建用户的向导中,为用户起名叫做bedrock-runtime,不需要选中向用户提供 AWS 管理控制台的访问权限,因为是API调用,不需要登录AWS控制台,所以不需要给这个权限。然后点击下一步继续,。如下截图。

在设置权限界面,点击右侧直接附加策略按钮,然后在下方权限策略的搜索框中,输入上一步创建的Policy名叫bedrock-runtime,下方即可过滤出来。选中这个策略,点击下一步继续。如下截图。

创建用户完成。

3、为使用者创建AKSK

接下来为用户创建AKSK密钥。从IAM用户中,找到刚才的用户,点击安全凭证标签页。如下截图。

在安全凭证标签页中,将页面向下移动,来到创建访问密钥部分。点击创建访问密钥。如下截图。

选择使用场景是命令行界面(CLI),并点击下方的确认,我理解上述建议,并希望继续创建访问密钥。然后点击右下角的下一步按钮。如下截图。

输入密钥的标签,例如取名bedrock-runtime。点击右下角的创建访问密钥按钮。如下截图。

创建密钥完成。请注意,密钥只显示一次,如果没有复制下来,或者没有下载CSV文件,则密钥不再显示,只能重新生成一个新的密钥。如下截图。

点击已完成按钮完成密钥创建。

四、准备通过API调用Bedrock服务和Claude模型所需要的AKSK

调用Bedrock使用的Access Key/Secret Key是与调用其他AWS服务的认证通用的,因此其配置过程可通过安装AWSCLI进行快速配置,也可以手工配置让各种编程语言的SDK识别到这个Key。

从安全角度考虑,不推荐在代码中硬编码写入密钥,推荐将密钥配置在本机的home目录下。

1、通过AWSCLI安装配置密钥(可选)

下载AWSCLI,并安装。

安装后运行aws configure,依次输入Access Key,Secret Key,调用的Region(本次实验为us-west-2),最后一步默认输出格式,填写JSON即可。

2、手工配置Access Key/Secret Key

如果上一步通过AWSCLI配置好了密钥,那么本步骤可以跳过。

(1) Linux系统

Linux系统放置AKSK到Home目录下特定位置

创建~/.aws/credentials文件,内容如下:

[default]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET_KEY

创建~/.aws/config文件,请注意使用特定的Region,例如文本前边步骤申请Model Access时候,选择的是us-west-2区域。内容如下:

[default]
region=us-west-2

保存退出。

(2) Windows系统

创建C:\Users\%USERNAME%\.aws\credentials文件。其中%USERNAME%是当前Windows使用的用户名,另外.aws目录是开头带有一个圆点表示隐藏目录。内容如下:

[default]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET_KEY

创建C:\Users\%USERNAME%\.aws\config文件。其中%USERNAME%是当前Windows使用的用户名,另外.aws目录是开头带有一个圆点表示隐藏目录。请注意使用特定的Region,例如文本前边步骤申请Model Access时候,选择的是us-west-2区域。内容如下:

[default]
region=us-west-2

保存退出。

3、如果没有配置AKSK时候在代码中写入密钥的方式

如果没有按照上述过程配置的home目录下的密钥,那么可以在代码中硬编码写入密钥,只是这种方式存在安全隐患,不推荐使用。代码中硬编码写入密钥的方法如下:

session = boto3.Session(
    aws_access_key_id="xxxxxxxxxxxxxxx",
    aws_secret_access_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    region_name="us-west-2"
    )

这种方法是有安全隐患的,因为代码会被上传到Git(包括公司私有Git),会造成密钥可见,因此不推荐。

4、安装对应语言的SDK(以Python为例)

pip install boto3 json

5、确认可用的模型ID

在配置好AWSCLI后,执行如下命令:

aws bedrock list-foundation-models --region=us-west-2 --by-provider anthropic --query "modelSummaries[*].modelId"

即可返回所有Claude模型。例如返回结果如下:

[
    "anthropic.claude-instant-v1:2:100k",
    "anthropic.claude-instant-v1",
    "anthropic.claude-v2:0:18k",
    "anthropic.claude-v2:0:100k",
    "anthropic.claude-v2:1:18k",
    "anthropic.claude-v2:1:200k",
    "anthropic.claude-v2:1",
    "anthropic.claude-v2",
    "anthropic.claude-3-sonnet-20240229-v1:0",
    "anthropic.claude-3-haiku-20240307-v1:0"
]

接下来继续了解模型调用API并编写测试代码。

五、了解Claude模型的几种调用方式并编写代码调用API

1、直接调用Claude官方API和调用Bedrock API之间的区别

Claude模型的开发者Anthropic官方提供了SaaS方式的模型服务,使用者需要在Anthropic的官网https://console.anthropic.com/上注册,然后使用Anthropic官方API调用(当然账单也是付给Anthropic和AWS无关)。Python代码样例如下:

# 使用Anthropic官方的Claude模型的SDK的Python代码样例
import anthropic
from anthropic import HUMAN_PROMPT, AI_PROMPT

anthropic.Anthropic().completions.create(
    model="claude-2.1",
    max_tokens_to_sample=1024,
    prompt=f"{HUMAN_PROMPT} Hello, Claude{AI_PROMPT}",
)

运行以上代码的前提是为Python安装Anthropic的库,执行pip install -U anthropic即可安装。以上代码可以看出,程序与AWS服务不发生任何关系,API调用是直接发送给Anthropic官方的Endpoint进行调用。在直接调用Anthropic这种使用场景下,除了安装anthropic的依赖库,也可以直接构建POST请求的方式来提交访问,详情可参考Anthropic官网文档

在Amazon Bedrock上运行Claude模型有很多优势,您可以结合AWS的多种产品,包括Bedrock知识库、Bedrock Agent、向量数据库、S3数据湖、Lambda函数计算、API Gateway应用网关等构建完整的应用体系。本文介绍的是在Amazon Bedrock服务上使用Anthropic的Claude模型。

Amazon Bedrock提供了多种大模型服务,Claude只是其中一种模型。因此,Bedrock的API是进行了封装,包括身份认证、模型请求等都通过Amazon Bedrock进行发送,程序代码调用的Endpoint也是AWS的Endpoint,因此程序代码上的调用方法也和直接Claude有区别。以下是调用Bedrock上Claude3模型的Python代码样例。

# 使用Amazon Bedrock提供的Claude模型的Python代码样例
import boto3
import json

bedrock = boto3.client(service_name="bedrock-runtime")
body = json.dumps({
  "max_tokens": 256,
  "messages": [{"role": "user", "content": "Hello, world"}],
  "anthropic_version": "bedrock-2023-05-31"
})

response = bedrock.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0")

response_body = json.loads(response.get("body").read())
print(response_body.get("content"))

运行以上代码的前提是为Python安装AWS SDK的库,执行pip install -U boto3,需要确保boto3版本大于1.28.59。以上代码可以看出,程序与Anthropic官方不发生任何关系,API调用是直接发送给Amazon Bedrock的Endpoint进行调用,且身份认证也是通过AWS SDK,即通过Access Key/Secret Key来认证。除了使用AWS SDK之外,还可以直接构建HTTPS请求以POST方式提交给Amazon Bedrock终端节点,这种访问方式的文档请参考这里。直接构建POST请求的方式属于底层调用,相对麻烦,因此不推荐,首选使用AWS SDK的方式。除了Python之外,其他主要开发语言都可以加载对应的SDK后调用Bedrock。

本文后续所有文档都以调用Bedrock为例,不再描述使用Claude官方API。

2、单次消息模式和Streaming模式

普通的单次消息模式,发送一个较长的Prompt和素材提交给大模型,大模型再返回一段较长的生成式内容,可能需要数秒钟。在单词消息模式下,程序需要等待Bedrock和Claude返回完整消息后,才进行后续处理,如果返回的信息有成百上千字(输出Token多),那么等待时间给用户的反馈非常不好。

降低模型响应时间的一个办法是使用Streaming模式。在Stream模式下,大模型在生成很长内容的时候随时返回,Streaming的模式将使得程序随时能显示出来。这样从用户交互的角度,不需要等待很长时间才一下子看到全部对话,而是生成一行返回一行,用户感受到大模型仿佛是人类一样逐渐的说出来,从交互体验上非常友好。Streaming的原理是SSE(Server-Sent Events),详情可参考这里

单次消息和Streaming模式是两个API,在Claude3上分别叫做InvokeModel(API文档在这里)和InvokeModelWithResponseStream(API文档在这里)。

本文后续会针对这两个模式分别给出例子。

3、从Claude2的Text Completion切换到Claude3的Message改变与PE部分的变化

本小节针对已经在使用Claude2的用户,切换到Claude3时候要做的调整。

Claude2模型是2023年发布的,Claude3在2024年发布,二者在API调用上所有差别。Claude2支持旧的API Text Completion(文档在这里),也支持新的API Messages(文档在这里)。而Claude3只使用新的Message。因此从Claude2切换到Claude3时候,需要对代码进行进行一定的改动。

Claude2的使用Text Completion时候提交的prompt格式如下:

prompt = "\n\nHuman: Hello there\n\nAssistant: Hi, I'm Claude. How can I help?\n\nHuman: Can you explain Glycolysis to me?\n\nAssistant:"

Claude3的使用Message的API时候提交的prompt格式如下:

messages = [
  {"role": "user", "content": "Hello there."},
  {"role": "assistant", "content": "Hi, I'm Claude. How can I help?"},
  {"role": "user", "content": "Can you explain Glycolysis to me?"},
]

由此可以看到,其API构成稍微有些差别。您需要使用message API来调用Claude3。

在Prompt Enginering方面,Claude2因为输入的整个Prompt都是Text文本,因此需要通过标记\n\nHuman\n\nAssistant的方式来表明哪些是人类的输入和哪些是大语言模型的。在Claude3模型的调用中,改用Message API后,是以JSON的方式来输入。其中又使用role标签且值是user来表示这部分信息是人类的输入,由此就不需要再加上\n\nHuman标签了。

除此了Message API的区别之外,大部分Claude2使用的Prompt编写方式在Claude3上继续可用。

4、调用Claude3的单次消息模式的Python代码样例

注:以下代码使用新的Message API的方式。

import boto3
import json

model = "anthropic.claude-3-sonnet-20240229-v1:0"
message = 'Write an email from Bob, Customer Service Manager, to the customer "John Doe" who provided negative feedback on the service provided by our customer support engineer'

session = boto3.Session(
    region_name="us-west-2"
    )
bedrock = session.client(service_name="bedrock-runtime")
body = json.dumps({
  "max_tokens": 500,
  "messages": [{"role": "user", "content": message}],
  "anthropic_version": "bedrock-2023-05-31"
})
response = bedrock.invoke_model(body=body, modelId=model)

# response content
response_body = json.loads(response.get("body").read())
### get text from response
text = response_body.get("content")
for item in text:
    if 'text' in item:
        print(item['text'])

执行后,等待数秒会看到完整输出,即按照Prompt要求,模型以客户服务经理Bob的身份写给客户John Doe的关于差评的一封道歉信。

5、调用Claude3的Streaming模式

根据前文介绍,为了改善较长token输出时候的体验,可以用streaming模式,模拟出和人类交互的一段一段的输出的感觉。

注:以下代码使用新的Message API的方式。

import boto3
import json

model = "anthropic.claude-3-sonnet-20240229-v1:0"
message = 'Write an email from Bob, Customer Service Manager, to the customer "John Doe" who provided negative feedback on the service provided by our customer support engineer'

session = boto3.Session(
    region_name="us-west-2"
    )
bedrock = session.client(service_name="bedrock-runtime")
body = json.dumps({
  "max_tokens": 500,
  "messages": [{"role": "user", "content": message}],
  "anthropic_version": "bedrock-2023-05-31"
})

response = bedrock.invoke_model_with_response_stream(body=body, modelId=model)

# response content

for event in response.get("body"):
    chunk = json.loads(event["chunk"]["bytes"])
    if chunk['type'] == 'content_block_delta':
        if chunk['delta']['type'] == 'text_delta':
            print(chunk['delta']['text'], end="")

执行后,会在Console上以Streaming流式的方式逐句输出,即按照Prompt要求,模型以客户服务经理Bob的身份写给客户John Doe的关于差评的一封道歉信。

6、Claude 3的多模态调用(图片内容理解)

Claude 3的三个版本均为多模态大模型,支持输入图片可理解其中的内容。这里准备一个example.jpg作为测试图像,然后准备代码如下:

import boto3
import json
import base64

model = "anthropic.claude-3-sonnet-20240229-v1:0"

# input image as base64 string
file_name = "example.jpg"
with open(file_name, "rb") as image_file:
    image_bytes = image_file.read()
image_string = base64.b64encode(image_bytes).decode('utf8')

# Claude 3 multi-modal message
message = [
            {
                "type": "text",
                "text": "使用中文描述下这张图片,在100字之内",
            },
            {
                "type": "image",
                "source": {
                    "type": "base64",
                    "media_type": "image/png",
                    "data": image_string
                }
            }
        ]

session = boto3.Session(
    region_name="us-west-2"
    )
bedrock = session.client(service_name="bedrock-runtime")
body = json.dumps({
  "max_tokens": 500,
  "messages": [{
      "role": "user", 
      "content": message
      }],
  "anthropic_version": "bedrock-2023-05-31"
})

response = bedrock.invoke_model(body=body, modelId=model)

# response content
response_body = json.loads(response.get("body").read())
# print(response_body.get("content"))

### get text from response
text = response_body.get("content")
for item in text:
    if 'text' in item:
        print(item['text'])

返回结果如下:

这张照片捕捉了一只正在小憩的猫咪。它蜷缩着身体,闭着眼睛安详地睡着。猫咪的毛发条纹分明,背景呈现出朦胧的蓝紫色调,营造出一种梦幻般的氛围。整张照片构图简单、细节清晰,展现了一种安静、祥和的美感。

由此看到多模态已经识别了图片内容。

7、使用Claude 2和Claude Instant模型的单次消息调用

注:以下代码使用Claude2旧的Text Completion的方式,需要在Prompt中使用\n\nHuman来标记人类输入。

import boto3
import json

model = "anthropic.claude-v2:1"
prompt = '\n\nHuman: Write an email from Bob, Customer Service Manager, to the customer "John Doe" who provided negative feedback on the service provided by our customer support engineer\n\nAssistant: Here is a draft:\n'

session = boto3.Session(
    region_name="us-west-2"
    )
brt = session.client('bedrock-runtime')

body = json.dumps({"prompt": prompt, 'max_tokens_to_sample': 4000 })
response = brt.invoke_model(
    modelId='anthropic.claude-instant-v1',
    body=body
)

response_body = json.loads(response["body"].read())
completion = response_body["completion"]
print(completion)

执行后,等待数秒会看到完整输出,即按照Prompt要求,模型以客户服务经理Bob的身份写给客户John Doe的关于差评的一封道歉信。

8、使用Claude 2和Claude Instant模型Streaming模式调用

注:以下代码使用Claude2旧的Text Completion的方式,需要在Prompt中使用\n\nHuman来标记人类输入。

import boto3
import json

model = "anthropic.claude-v2:1"
prompt = '\n\nHuman: Write an email from Bob, Customer Service Manager, to the customer "John Doe" who provided negative feedback on the service provided by our customer support engineer\n\nAssistant: Here is a draft:\n'

session = boto3.Session(
    region_name="us-west-2"
    )
brt = session.client('bedrock-runtime')

body = json.dumps({"prompt": prompt, 'max_tokens_to_sample': 4000 })
response = brt.invoke_model_with_response_stream(
    modelId=model,
    body=body
)

stream = response.get('body')
i=1
output = []
if stream:
    for event in stream:
        chunk = event.get('chunk')
        if chunk:
            chunk_obj = json.loads(chunk.get('bytes').decode()) #print(f'{chunk_obj}')
            text = chunk_obj['completion']
            output.append(text)
            print(f'{text}') 
            i+=1

执行后,会在Console上以Streaming流式的方式逐句输出,即按照Prompt要求,模型以客户服务经理Bob的身份写给客户John Doe的关于差评的一封道歉信。

六、Claude Prompt Engineering 调优

请参考本文下篇

七、参考文档

Anthropic官方对Bedrock API的介绍

https://docs.anthropic.com/claude/reference/claude-on-amazon-bedrock

AWS Bedrock的API例子(多种开发语言)

https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_Claude3_Text_section.html

Claude 3 Streaming API

https://docs.anthropic.com/claude/reference/messages-streaming

bedrock-claude-streamlit 代码例子(Python)

https://github.com/terrificdm/bedrock-claude-streamlit/blob/main/bedrock_streamlit.py

Amazon Bedrock Claude3 Workshop 中文版

https://catalog.us-east-1.prod.workshops.aws/workshops/17879811-bd5c-4530-8b85-f0042472f2a1/en-US/demo-in-console/image/defect-detection