Amazon基岩的生成AI应用程序:Go开发人员入门

Amazon基岩生成AI应用程序:Go开发者入门指南

这篇文章是为想要使用Amazon Bedrock构建生成式人工智能应用程序的Go开发人员提供的入门指南。Amazon Bedrock是一个完全托管的服务,通过API将来自亚马逊和第三方模型提供商的基础模型变得可访问。

我们将使用AWS Go SDK为Amazon Bedrock构建,并在进行过程中涵盖以下主题:

  • Amazon Bedrock Go API及其在内容生成等任务中的使用方法
  • 如何构建一个简单的聊天应用程序并处理来自Amazon Bedrock Foundation模型的流式输出
  • 示例代码演示

这些代码示例可在GitHub存储库中获得。

开始之前

如果您尚未安装,请确保安装最新版本的Go

确保您已经配置和设置了Amazon Bedrock,包括请求访问Foundation模型。

在运行示例时,我们将使用AWS Go SDK从本地机器调用Amazon Bedrock API操作。为此,您需要:

  1. 使用IAM用户/角色授予编程访问权限。
  2. 向您正在使用的IAM标识授予以下权限:
{    "Version": "2012-10-17",    "Statement": [        {            "Effect": "Allow",            "Action": "bedrock:*",            "Resource": "*"        }    ]}

关于AWS Go SDK身份验证的说明

如果您之前使用过AWS Go SDK,您将会对此熟悉。如果没有,请注意在代码示例中,我已经使用以下代码加载配置并指定身份验证凭据:

cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region))

当您使用config.LoadDefaultConfig初始化一个aws.Config实例时,AWS Go SDK使用其默认凭证链来查找AWS凭证。您可以在这里阅读有关详细信息,但在我的情况下,我已经在<USER_HOME>/.aws中有一个credentials文件,并且SDK可以检测并使用它。

Amazon Bedrock客户端类型

Amazon Bedrock Go SDK支持两种客户端类型:

  1. 第一种是bedrock.Client,可用于类似控制平面的操作,如获取有关基础Foundation模型的信息,自定义模型创建微调作业以自定义基础模型等。
  2. bedrockruntime.Clientbedrockruntime包中用于在Foundation模型上运行推理(这是有趣的部分!)。

列出Amazon Bedrock Foundation模型

首先,让我们看一个简单示例,使用控制平面客户端在Amazon Bedrock中列出foundation模型(省略了错误处理和日志记录):

    region := os.Getenv("AWS_REGION")    if region == "" {        region = defaultRegion    }    cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region))    bc := bedrock.NewFromConfig(cfg)    fms, err := bc.ListFoundationModels(context.Background(), &bedrock.ListFoundationModelsInput{        //ByProvider: aws.String("Amazon"),        //ByOutputModality: types.ModelModalityText,    })    for _, fm := range fms.ModelSummaries {        info := fmt.Sprintf("Name: %s | Provider: %s | Id: %s", *fm.ModelName, *fm.ProviderName, *fm.ModelId)        fmt.Println(info)    }

我们创建一个 bedrock.Client 实例,并使用它通过 ListFoundationModels API 获取 Amazon Bedrock 支持的 Foundation Models。

克隆 GitHub 存储库,并将其更改为正确的目录:

git clone https://github.com/build-on-aws/amazon-bedrock-go-sdk-examplescd amazon-bedrock-go-sdk-examplesgo mod tidy

运行此示例:

go run bedrock-basic/main.go

您应该看到支持的基础模型列表。

请注意,您还可以通过在 ListFoundationModelsInput 中指定提供程序、模态(输入/输出)等方式进行过滤。

使用 bedrockruntime API 调用模型进行推断

让我们从使用 Anthropi Claude(v2)模型开始。这是一个简单的内容生成场景的示例,具体如下:

<paragraph> “1758 年,瑞典植物学家和动物学家卡尔·林奈在他的《物种起源》中,将物种(双名命名法)命名为两个字(双名命名法)。Canis 是拉丁语中 “dog” 的意思,在这个属名下,他列出了家犬、狼和金背狼。”</paragraph>请重写上面的段落,以使它对一个五年级学生来说容易理解。请使用 <rewrite></rewrite> 标签输出您的重写部分。

运行程序:

go run claude-content-generation/main.go

输出结果可能在您的情况下会有所不同,但应与以下结果相似:

<rewrite>卡尔·林奈是来自瑞典的科学家,他研究植物和动物。在 1758 年,他发表了一本名为《物种起源》的书,其中他给所有物种起了两个词的名字。例如,他将狗称为 Canis familiaris。Canis 是拉丁语中 “dog” 的意思。在名为 Canis 的分类下,林奈列出了宠物狗、狼和金背狼。所以他用第一个词 Canis 来将相关度高的动物如狗、狼和豺狼归为一组。这种用两个词命名物种的方式被称为二名法,现今仍被科学家们使用在学术研究中。</rewrite>

这是 代码 片段(省略了错误处理等)。

    //...    brc := bedrockruntime.NewFromConfig(cfg)    payload := {        Prompt:            fmt.Sprintf(claudePromptFormat, prompt),        MaxTokensToSample: 2048,        Temperature:       0.5,        TopK:              250,        TopP:              1,    }    payloadBytes, err := json.Marshal(payload)    output, err := brc.InvokeModel(context.Background(), &bedrockruntime.InvokeModelInput{        Body:        payloadBytes,        ModelId:     aws.String(claudeV2ModelID),        ContentType: aws.String("application/json"),    })    var resp Response    err = json.Unmarshal(output.Body, &resp)    //.....

我们获取 bedrockruntime.Client 实例,并创建包含我们需要发送给 Amazon Bedrock 的请求的 payload(包括提示内容)。该 payload 以 JSON 格式化,并且其详细信息在此处有很好的文档记录:Foundation Models 推断参数

然后,我们在 InvokeModel 调用中包含 payload。注意调用中的 ModelId,您可以从 Base model IDs 列表中获取。然后将 JSON 响应转换为 Response 结构。

请注意,此“工作流程”(准备包含提示的 payload、编组 payload、模型调用和取消编组)将在我们的示例(以及您的应用程序)中经常出现,稍作修改以适应不同的模型/用例。

您还可以尝试一个使用此提示的信息提取场景:

<directory>电话簿:John Latrabe,800-232-1995,[email protected] Lana,800-759-2905,[email protected] Stevens,800-980-7000,[email protected]电话簿将由人力资源经理进行实时更新。"<directory>请按照文本中出现的顺序,每行输出目录中的电子邮件地址。如果文本中没有电子邮件地址,则输出“N/A”。

要运行程序

go run claude-information-extraction/main.go

聊天:一个典型的GenAI示例

我们不能没有一个聊天应用的GenAI文章,对吧?

继续使用Claude模型,让我们看一个对话示例。在这个示例中,您可以交换一次性消息,也可以交换多条消息(聊天),并保留对话历史记录。

由于这是一个简单的实施,状态保存在内存中。

要运行应用程序:

go run claude-chat/main.go# 如果要记录与LLM交换的消息,# 以详细模式运行程序go run claude-chat/main.go --verbose

这是我进行的一次对话的输出。请注意,最后的回答是基于先前的回答生成的,这要归功于对话历史的保留:

使用流媒体API

在之前的聊天示例中,您需要等待几秒钟才能获得完整的输出。这是因为这个过程是完全同步的-调用模型并等待完整的回应。

InvokeModelWithResponseStream API允许我们采用异步方法,也称为流式处理。如果您希望将响应显示给用户或在生成响应时处理响应,这将为应用程序提供一种“响应灵敏”的体验。

要尝试它,我们使用与内容生成示例中相同的提示,因为它会生成足够长的响应以便我们看到流式处理的过程。

  <rewrite>卡尔·林奈斯是一位瑞典科学家,研究植物和动物。1758年,他出版了一本名为《自然之系统》的书,其中给出了所有物种的两个词名称。例如,他称狗为Canis familiaris。Canis是狗的拉丁词。在Canis名称下,林奈斯列出了宠物狗、狼和金背狼。因此,他使用第一个词Canis将狗、狼和豺等密切相关的动物分组在一起。这种用两个词命名物种的方式被称为二名法,并且科学家今天仍在使用。</rewrite>

运行应用程序

go run streaming-claude-basic/main.go

您应该看到正在将输出写入控制台,因为Amazon Bedrock正在生成这些部分。

让我们来看一下代码。

这是第一部分:一切如常。我们使用提示(和参数)创建一个有效载荷,并调用InvokeModelWithResponseStream API,该API返回一个bedrockruntime.InvokeModelWithResponseStreamOutput实例。

    //...    brc := bedrockruntime.NewFromConfig(cfg)    payload := Request{        Prompt:            fmt.Sprintf(claudePromptFormat, prompt),        MaxTokensToSample: 2048,        Temperature:       0.5,        TopK:              250,        TopP:              1,    }    payloadBytes, err := json.Marshal(payload)    output, err := brc.InvokeModelWithResponseStream(context.Background(), &bedrockruntime.InvokeModelWithResponseStreamInput{        Body:        payloadBytes,        ModelId:     aws.String(claudeV2ModelID),        ContentType: aws.String("application/json"),    })    //....

下一部分与使用InvokeModel API进行同步处理的方法不同。由于InvokeModelWithResponseStreamOutput实例没有完整的响应(尚未获得),我们不能(或不应该)简单地将其返回给调用者。相反,我们选择使用processStreamingOutput函数逐个处理输出的位。

传递给它的函数的类型是type StreamingOutputHandler func(ctx context.Context, part []byte) error,这是我定义的自定义类型,用于提供一种方法,使调用应用程序可以指定如何处理输出块-在本例中,我们只是打印到控制台(标准输出)。

    //...    _, err = processStreamingOutput(output, func(ctx context.Context, part []byte) error {        fmt.Print(string(part))        return nil    })    //...

看看processStreamingOutput函数是如何做的(为了简洁起见省略了一些代码部分)。InvokeModelWithResponseStreamOutput使我们能够访问事件通道(类型为types.ResponseStream),其中包含事件负载。这只是一个由LLM部分生成的响应的JSON格式化字符串;我们将其转换为Response结构体。

我们调用handler函数(它将部分响应打印到控制台),并通过添加部分位来确保我们也继续构建完整的响应。完整的响应最终从函数中返回。

func processStreamingOutput(output *bedrockruntime.InvokeModelWithResponseStreamOutput, handler StreamingOutputHandler) (Response, error) {    var combinedResult string    resp := Response{}    for event := range output.GetStream().Events() {        switch v := event.(type) {        case *types.ResponseStreamMemberChunk:            var resp Response            err := json.NewDecoder(bytes.NewReader(v.Value.Bytes)).Decode(&resp)            if err != nil {                return resp, err            }            handler(context.Background(), []byte(resp.Completion))            combinedResult += resp.Completion            //....    }    resp.Completion = combinedResult    return resp, nil}

响应式聊天应用程序,多亏了流式API

现在,您已经了解了处理流式响应的方法和原因,我们的简单聊天应用程序是使用这种方法的完美候选人!

我不会再走一遍代码。我已经更新了聊天应用程序,使用InvokeModelWithResponseStream API并根据以前的示例处理响应。

要运行新版本的应用程序

go run claude-chat-streaming/main.go

到目前为止,我们使用的是Anthropic Claude v2模型。您也可以尝试Cohere模型示例进行文本生成。运行:go run cohere-text-generation/main.go

图像生成

图像生成是生成式人工智能的另一个基本用例!这个示例使用Amazon Bedrock中的Stable Diffusion XL模型根据提示和其他参数生成图像。

要尝试它:

go run stablediffusion-image-gen/main.go "<your prompt>"# 例如:go run stablediffusion-image-gen/main.go "斯里兰卡茶园"go run stablediffusion-image-gen/main.go "火箭从森林中发射,下面是一个花园,在蓝天下非常出色,吉布里"

您应该会看到生成的输出JPG文件。

这里是代码的简要说明(减去错误处理等)。

InvokeModel调用的输出载荷转换为Response结构体,然后进一步分解以提取base64图像(编码为[]byte)并使用encoding/base64进行解码,将最终的[]byte写入输出文件中(格式为output-.jpg)。

    //...    brc := bedrockruntime.NewFromConfig(cfg)    prompt := os.Args[1]    payload := Request{        TextPrompts: []TextPrompt{{Text: prompt}},        CfgScale:    10,        Seed:        0,        Steps:       50,    }    payloadBytes, err := json.Marshal(payload)    output, err := brc.InvokeModel(context.Background(), &bedrockruntime.InvokeModelInput{        Body:        payloadBytes,        ModelId:     aws.String(stableDiffusionXLModelID),        ContentType: aws.String("application/json"),    })    var resp Response    err = json.Unmarshal(output.Body, &resp)    decoded, err := resp.Artifacts[0].DecodeImage()    outputFile := fmt.Sprintf("output-%d.jpg", time.Now().Unix())    err = os.WriteFile(outputFile, decoded, 0644)    //...

请注意模型参数 (CfgScale, SeedSteps);它们的值取决于您的用例。例如,CfgScale 决定了最终图像对提示的呈现程度:使用较低的数字可以增加生成中的随机性。有关详细信息,请参阅 Amazon Bedrock 推理参数 文档。

从文本创建嵌入向量

文本嵌入向量代表了无结构文本(如文档、段落和句子)的有意义的向量表示。Amazon Bedrock 目前支持用于文本嵌入向量的 Titan Embeddings G1 - Text 模型。它支持文本检索、语义相似度和聚类。最大输入文本为 8K 个令牌,最大输出向量长度为 1536

要运行 示例

go run titan-text-embedding/main.go "<your input>"# 例如go run titan-text-embedding/main.go "猫"go run titan-text-embedding/main.go "狗"go run titan-text-embedding/main.go "Trex"

这可能是您将看到的最不令人兴奋的输出!事实上,通过查看 float64 的切片,很难找出任何东西。

当与其他组件(如向量数据库用于存储这些嵌入向量)和语义搜索这样的用例结合使用时,它将变得更加相关。这些主题将在未来的博客文章中介绍。现在,请暂且接受”它有效”这个事实。

结束语

我希望这对 Go 开发者作为使用基础模型在 Amazon Bedrock 上构建 GenAI 应用程序的起点是有用的。

请期待更多涵盖使用 Go 开发者的生成式 AI 主题的文章。在那之前,祝您开发愉快!