为了账号安全,请及时绑定邮箱和手机立即绑定

利用AI和Amazon Bedrock Agents来自动执行任务

通过亚马逊Bedrock Agent自动化工具提高生产力,附带TypeScript和AWS CDK示例代码 前言.

✔️ 我们介绍Amazon Bedrock代理是什么。
✔️ 我们聊聊AWS的架构。
✔️ 我们来解析TypeScript和AWS CDK的代码。
✔️ 我们来做一些测试,看看它实际运行的效果。

嘿,大家好

Amazon Bedrock Agents 让您能够创建和自定义公司内的自主代理,这些代理能够为您完成任务。这些代理利用公司的数据和用户的输入,通过对话聊天和人工智能技术帮助员工完成任务。

它们就像协调者一样,管理基础模型、数据源和知识库、软件应用和用户对话之间的交互。此外,它们自动调用 API 来执行任务,并访问知识库来丰富与这些任务相关的信息。

在这篇文章中,我们将介绍一家名为LJ Resorts的虚构酒店和水疗公司,以便讨论其AWS架构和代码。客户可以通过我们提供的应用一次性预订所有项目,包括酒店住宿服务、水疗疗程和护理;还可以查询公司详情,如提供的护理服务种类、优惠信息和营业时间。

代码库位于这里。

Serverless Advocate 模式和解决方案库

👇 在我们继续之前 — 请在领英上关注我,点击这里https://www.linkedin.com/in/lee-james-gilmore/ 以便获得关于无服务器技术的新闻和未来的博客文章。

亚马逊的Bedrock Agents是什么?🤖

我们现在来聊聊Amazon Bedrock Agents是什么,它们怎么运作,先来看看几个重要的缩写。

缩写词

在我们开始之前,先来解释一下这些缩写代表什么意思。

✔️ FMs — 基石模型。

在Amazon Bedrock中,FM代表基础模型(Foundational Model)。Amazon Bedrock通过一个简单的API接口,提供来自Amazon及其他第三方模型提供商的预训练深度学习模型的访问权限。

✔️ 行动小组。

您可以通过提供一个OpenAPI模式来定义代理可执行的任务的API操作,并提供一个Lambda函数来处理编排过程中根据模式识别的输入API操作和参数,并返回API调用的结果作为输出结果。

✔️ 指示如下:

你编写描述代理功能的指令。通过高级指令,你可以在编排的每一步进一步定制指令,并包含Lambda函数以解析每个步骤的输出。在我们这个例子中,它是:

🤖 “帮助客户预订酒店房间、水疗疗程和预订服务;同时根据日期和预订类型向他们提供任何特别优惠,在他们完成预订前,向他们说明任何营业时间和价格,还要留意我们的酒店规定。”

亚马逊Bedrock的代理工作原理是怎样的?

如下的图所显示,我们从客户的输入信息开始,利用提示存储进行增强处理,用从会话存储中获取的先前对话历史作为上下文,最后调用基础模型。

接下来,编排程序解析响应,根据我们在OpenAPI规范文档中定义的操作,调用相应的Lambda动作组,并从Bedrock知识库中检索所需的额外信息。编排完成后,将结果返回给客户。

https://docs.aws.amazon.com/bedrock/latest/userguide/agents-how.html (网址保持不变)

现在让我们来看看这篇文章里我们要建的东西。我们就可以浏览代码、部署并测试这个应用。

要构建什么?🛠️

那么现在,我们已经详细了解了亚马逊Bedrock中的代理,并且了解了它们的基本原理。接下来我们来看看这篇文章我们要搭建什么。

注: 在之前的文章中,我们已经介绍了如何配置 Amazon Bedrock 知识库,因此本文将不再赘述。

这是我们正在构建的一个例子。提示:在我们之前的帖子中提到了知识库。

我们在之前的文章中介绍了亚马逊Bedrock知识库系统,可以在这里查看具体内容。

Amazon Bedrock 知识库系统与私有数据通过 TypeScript 和 AWS CDK,你可以把知识库集成到 Amazon Bedrock 中,以提供基础模型的支持…blog.serverlessadvocate.com

从上图可以看出:

  1. 客户通过提示触发Lambda函数的URL。
  2. 这会调用一个查询Lambda函数,该函数然后调用Amazon Bedrock代理并传入请求。
  3. 代理利用已部署的知识库(包含公司特有的数据)以及根据存储在S3桶中的OpenAPI规范的Lambda函数来对其他内部系统进行路由,进而完成客户的任务(它会为我们协调多个调用以完成任务)。

让我们在下一节来看一个互动的示例。现在我们已经讨论了高层次架构。

这样的互动可能是什么样子?

下面是一个例子,展示了客户与代理聊天时可能的情况,这实际上是我们在示例系统中,使用真实提示和输出运行时的样子。

顾客:“嘿,现在酒店有什么特别的优惠活动吗?”

代理 :“以下酒店特别优惠目前可用: — 周一宅度假优惠:房费八折(仅限周一) — 最后一刻逃离:同日预订七五折优惠(仅限周二) — 长住优惠:三晚住宿八折(仅限周三) — 套房升级:免费升级到行政套房(仅限周四) — 周末特惠套餐:两晚住宿九折优惠(仅限周五)”

📘 注意 :上述互动请查阅我们的知识库以获取更多信息。

顾客 :“我想在2024年2月25日预订一个酒店房间,有什么房间可以预订?”

前台 :“2024年2月25日可用的房间如下: — 房间101(标准间,$100):温馨舒适的标准间,配有大号床。 — 房间102(标准间,$100):宽敞的标准间,配有两张双人床。 — 房间103(豪华间,$150):豪华的房间,配有特大号床和景观。 — 房间104(套房,$200):雅致的套房,配有独立起居和卧室。 — 房间105(标准间,$90):……”

➡️注意: 上述互动通过我们自己的Lambda函数调用了外部服务。

顾客: “我可以预定一间豪华房在2024年02月25日,160美元吗?”

前台服务员:「我已经为您在2024年2月25日预订了109号房间,这是一间配有豪华浴室的豪华套房,价格为160美金。如果您还需要其他帮助,请告诉我。」

➡️注意 : 上述交互通过我们的Lambda函数调用了我们的外部服务。

客人:“有什么 SPA 项目可以预约?”

代理 :“今天可用的水疗项目有:—— 瑞典按摩:全身按摩,帮助放松,缓解紧张。费用:80元 —— 面部护理:深层清洁面部护理,让肌肤恢复活力。费用:70元 —— 热石按摩:能舒缓肌肉,改善血液循环。费用:90元 —— 香薰按摩:使用芳香精油进行轻柔按摩,促进放松和提升幸福感。费用:85元 —— …..{其余内容省略}”

➡️ 请注意 :这次交流通过我们的Lambda函数调用外部服务。

顾客: “可以帮我预约一下热石按摩吗?”

服务员: “是的,我已经为您预订了热石按摩。这种按摩疗法使用加热的石头帮助放松肌肉并促进血液循环。费用是90美元,请问还需要其他帮助吗?如果您还需要其他帮助,请告诉我哦!”

➡️ 注意 :上述互动通过我们的Lambda函数调用了外部服务。

顾客:“你们的营业时间是几点?”

工作人员 :“LJ度假村的营业时间是:周一至周五:上午9点 — 下午6点 周六及周日:上午7点 — 晚上7点”

注意 :可以查阅我们的知识库获得更多相关信息。

顾客:“那天早上你们有安排吗?”

代理:“查看今天的可用时段,我发现有一个上午时段可用:上午场:上午来一场高尔夫球,享受美景。费用:80美元。如果您想预订上午的高尔夫时段,请告诉我。”

➡️ 注意 :上述互动我们通过Lambda函数调用了一下外部服务。

顾客:那天我可以预约一个80美元的时段吗?

代理:“是的,今天上午我为您预订了一个时段,费用是80美元。这个时段让您可以在上午打一场高尔夫,同时欣赏美景。如果您还需要其他帮助或者有什么问题,请随时告诉我!”

➡️ 提示 :上述互动通过我们的Lambda函数调用了我们的外部服务

客户: “我所有的预订加起来一共多少钱?”

客服:“根据我的对话历史,您预订了一个90美元的热石按摩疗程和一个80美元的早晨预约时段。您预订的总金额是90美元加80美元,共计170美元。”

如下序列图可以总结这一点。

这展示了Bedrock Agents的强大之处。原本需要一个人访问许多不同的系统并与客户面对面交流的工作,现在AI可以自动化整个流程,从头到尾。

我们现在看了客户和代理之间可能发生的互动,接下来咱们看看关键代码吧。

聊关键代码 👨‍💻

好的,所以我们已经见识了这个基本示例的运行,现在我们来瞧瞧TypeScript和CDK的代码吧。请记住,完整的解决方案可以在那里找到:here

有状态栈

让我们从我们的Stateful栈开始,先创建一个Lambda函数。

    // 为代理创建Lambda函数 - 这个Lambda函数决定了提示与模式映射相关的外观  
    const actionGroupAgentLambda: nodeLambda.NodejsFunction =  
      new nodeLambda.NodejsFunction(本, 'AgentLambda', {  
        functionName: 'action-group-executor',  
        runtime: lambda.Runtime.NODEJS_20_X,  
        entry: path.join(  
          __dirname,  
          './src/adapters/primary/action-group-executor/action-group-executor.adapter.ts'  
        ),  
        memorySize: 1024,  
        handler: 'handler',  
        timeout: cdk.Duration.minutes(5), // 5分钟的持续时间  
        description: '操作组执行器的Lambda函数',  
        architecture: lambda.Architecture.ARM_64,  
        tracing: lambda.Tracing.ACTIVE,  
        bundling: {  
          minify: true,  
        },  
        environment: {  
          ...lambdaConfig,  
        },  
      });

我们接着按照下面的方式创建Amazon Bedrock代理:

# 原始代码保持不变

(注:此处应删除“原始代码保持不变”以保持一致)

最终翻译如下:

我们接着按照下面的方式创建Amazon Bedrock代理:

    // 创建Bedrock代理
    const agent = new bedrock.Agent(this, 'BedrockAgent', {  
      name: 'Agent',  
      description: '用于酒店、水疗和高尔夫球预订的代理人。',  
      foundationModel: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2,  
      instruction:  
        '请帮助我们的客户预订酒店房间、水疗疗程和高尔夫球预订;同时根据日期和预订类型向他们提供任何特殊优惠,在他们完成预订之前让他们了解任何开放时间和价格,并考虑我们的酒店政策。',  
      idleSessionTTL: cdk.Duration.minutes(10),  
      knowledgeBases: [kb],  
      shouldPrepareAgent: true,  
      alias: 'Agent',  
    });

我们可以从上面的代码中看到,我们给Agent赋予了如下的关键属性:会话应持续多久,链接至我们的Amazon Bedrock知识库,FM类型(我们的情况为Claud V2),以及该Agent的指令。

我们接下来按照下面的方式创建我们的工作组:

    // 添加用于预订的操作组
    new bedrock.AgentActionGroup(this, 'AgentActionGroup', {  
      actionGroupName: 'agent-action-group',  
      description: '描述',  
      agent: agent,  
      apiSchema: bedrock.S3ApiSchema.fromAsset(  
        path.join(__dirname, './schema/api-schema.json')  
      ),  
      actionGroupState: '启用',  
      actionGroupExecutor: actionGroupAgentLambda,  
      shouldPrepareAgent: true,  
    });

我们可以看到,我们提供了一个开放API规范,详细描述了代理可以做什么,以及作为动作代理的Lambda函数。现在我们来看一下这个开放API规范是什么样的。

    {  
      "openapi": "3.0.0",  
      "info": {  
        "title": "LJ度假村酒店、水疗及高尔夫预订API",  
        "version": "1.0.0",  
        "description": "用于管理客户酒店、水疗及高尔夫预订的API。"  
      },  
      "paths": {  
        "/rooms": {  
          "get": {  
            "summary": "查看可用房间",  
            "description": "获取指定日期的所有可用房间",  
            "operationId": "getAllAvailableRooms",  
            "responses": {  
              "200": {  
                "description": "获取房间列表",  
                "content": {  
                  "application/json": {  
                    "schema": {  
                      "type": "array",  
                      "items": {  
                        "type": "object",  
                        "properties": {  
                          "roomId": {  
                            "type": "string",  
                            "description": "房间唯一编号。"  
                          },  
                          "roomType": {  
                            "type": "string",  
                            "description": "房间类型。"  
                          },  
                          "roomDescription": {  
                            "type": "string",  
                            "description": "房间描述。"  
                          },  
                          "date": {  
                            "type": "string",  
                            "description": "房间空闲的日期。"  
                          },  
                          "cost": {  
                            "type": "string",  
                            "description": "房间每晚的费用。"  
                          }  
                        }  
                      }  
                    }  
                  }  
                }  
              }  
            }  
          },  
          "post": {  
            "summary": "预订特定日期的可用房间",  
            "description": "预订特定日期的房间",  
            "operationId": "bookRoom",  
            "requestBody": {  
              "required": true,  
              "content": {  
                "application/json": {  
                  "schema": {  
                    "type": "object",  
                    "properties": {  
                      "roomId": {  
                        "type": "string",  
                        "description": "预订房间的编号"  
                      },  
                      "date": {  
                        "type": "string",  
                        "description": "预订日期"  
                      }  
                    },  
                    "required": ["roomId", "date"]  
                  }  
                }  
              }  
            },  
            "responses": {  
              "200": {  
                "description": "房间预订成功了"  
              }  
            }  
          }  
        },  
        "/spa-sessions": {  
          "get": {  
            "summary": "查看所有可用的水疗项目",  
            "description": "获取指定日期的所有可用水疗项目",  
            "operationId": "getAllAvailableSpaTreatments",  
            "responses": {  
              "200": {  
                "description": "获取水疗项目列表",  
                "content": {  
                  "application/json": {  
                    "schema": {  
                      "type": "array",  
                      "items": {  
                        "type": "object",  
                        "properties": {  
                          "treatmentId": {  
                            "type": "string",  
                            "description": "水疗项目的唯一编号。"  
                          },  
                          "treatmentType": {  
                            "type": "string",  
                            "description": "水疗项目种类。"  
                          },  
                          "treatmentDescription": {  
                            "type": "string",  
                            "description": "水疗项目的描述。"  
                          },  
                          "date": {  
                            "type": "string",  
                            "description": "预订日期"  
                          },  
                          "cost": {  
                            "type": "string",  
                            "description": "水疗项目的费用。"  
                          }  
                        }  
                      }  
                    }  
                  }  
                }  
              }  
            }  
          },  
          "post": {  
            "summary": "预订特定日期的可用水疗项目",  
            "description": "预订特定日期的水疗项目",  
            "operationId": "bookSpaTreatment",  
            "requestBody": {  
              "required": true,  
              "content": {  
                "application/json": {  
                  "schema": {  
                    "type": "object",  
                    "properties": {  
                      "treatmentId": {  
                        "type": "string",  
                        "description": "预订的水疗项目编号"  
                      },  
                      "date": {  
                        "type": "string",  
                        "description": "预订日期"  
                      }  
                    },  
                    "required": ["treatmentId", "date"]  
                  }  
                }  
              }  
            },  
            "responses": {  
              "200": {  
                "description": "水疗项目预订成功"  
              }  
            }  
          }  
        },  
        "/g-sessions": {  
          "get": {  
            "summary": "查看所有可用的高尔夫时段",  
            "description": "获取指定日期的所有可用高尔夫时段",  
            "operationId": "getAllAvailableGolfSessions",  
            "responses": {  
              "200": {  
                "description": "获取高尔夫时段列表",  
                "content": {  
                  "application/json": {  
                    "schema": {  
                      "type": "array",  
                      "items": {  
                        "type": "object",  
                        "properties": {  
                          "sessionId": {  
                            "type": "string",  
                            "description": "高尔夫项目的唯一编号。"  
                          },  
                          "sessionType": {  
                            "type": "string",  
                            "description": "高尔夫项目种类。"  
                          },  
                          "sessionDescription": {  
                            "type": "string",  
                            "description": "高尔夫项目描述。"  
                          },  
                          "date": {  
                            "type": "string",  
                            "description": "预订日期"  
                          },  
                          "cost": {  
                            "type": "string",  
                            "description": "高尔夫项目的费用。"  
                          }  
                        }  
                      }  
                    }  
                  }  
                }  
              }  
            }  
          },  
          "post": {  
            "summary": "预订特定日期的可用高尔夫时段",  
            "description": "预订特定日期的高尔夫时段",  
            "operationId": "bookGolfSession",  
            "requestBody": {  
              "required": true,  
              "content": {  
                "application/json": {  
                  "schema": {  
                    "type": "object",  
                    "properties": {  
                      "sessionId": {  
                        "type": "string",  
                        "description": "预订的高尔夫项目编号"  
                      },  
                      "date": {  
                        "type": "string",  
                       .
                        "description": "预订日期"  
                      }  
                    },  
                    "required": ["sessionId", "date"]  
                  }  
                }  
              }  
            },  
            "responses": {  
              "200": {  
                "description": "高尔夫预订成功"  
              }  
            }  
          }  
        }  
      }  
    }

需要注意的是,我们的模型将用于确定应执行哪些任务的描述、路径、方法和操作ID。例如,当列出所有酒店房间时,它会使用特定的方法和操作ID(例如,HotelRoomListOperationId)。

说明 — “获取给定日期下所有可用房间的列表”。
操作ID — ‘getAllAvailableRooms’。这里的操作ID是指获取所有可用房间的操作标识。
路径 — ‘/rooms’。这里的路径指房间资源的URL路径。
方法 — ‘GET’。这里的方法指HTTP请求的方法。

当我们的Lambda函数被调用时,它会根据必要的信息来决定还需要调用哪些其他的系统。

    import {  
      MetricUnits,  
      Metrics,  
      logMetrics,  
    } from '@aws-lambda-powertools/metrics';  
    import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer';  
    import { golfSessions, rooms, spaTreatments } from 'stateful/src/data';  

    import { injectLambdaContext } from '@aws-lambda-powertools/logger';  
    import middy from '@middy/core';  
    import { logger } from '@shared/index';  

    const tracer = new Tracer();  
    const metrics = new Metrics();  

    export const adapter = async ({  
      inputText,  
      apiPath,  
      httpMethod,  
      actionGroup,  
      messageVersion,  
      requestBody,  
      sessionAttributes,  
      promptSessionAttributes,  
    }: Event): Promise<Response> => {  
      let body;  
      let httpStatusCode = 200;  

      try {  
        logger.info(  
          `inputText: ${inputText}, apiPath: ${apiPath}, httpMethod: ${httpMethod}`  
        );  

        // 实际上,这些会调用我们其他的 Lambda 函数、数据库或 API/服务  
        switch (apiPath) {  
          case '/rooms':  
            if (httpMethod === 'GET') {  
              body = rooms;  
            } else if (httpMethod === 'POST') {  
              body = rooms.find((room) => room.roomId === '109');  
            }  
            break;  

          case '/spa-sessions':  
            if (httpMethod === 'GET') {  
              body = spaTreatments;  
            } else if (httpMethod === 'POST') {  
              body = spaTreatments.find(  
                (treatment) => treatment.treatmentId === '3'  
              );  
            }  
            break;  

          case '/golfSessions':  
            if (httpMethod === 'GET') {  
              body = golfSessions;  
            } else if (httpMethod === 'POST') {  
              body = golfSessions.find((session) => session.sessionId === '1');  
            }  
            break;  

          default:  
            httpStatusCode = 500;  
            body =  
              '抱歉,我目前无法协助您,请尝试以其他方式提问。';  
            break;  
        }  

        metrics.addMetric('成功动作组查询', MetricUnits.Count, 1);  

        return {  
          messageVersion,  
          response: {  
            apiPath,  
            actionGroup,  
            httpMethod,  
            httpStatusCode,  
            sessionAttributes,  
            promptSessionAttributes,  
            responseBody: {  
              'application-json': {  
                body: JSON.stringify(body),  
              },  
            },  
          },  
        };  
      } catch (error) {  
        let errorMessage = '未知错误';  
        if (error instanceof Error) errorMessage = error.message;  
        logger.error(errorMessage);  

        metrics.addMetric('动作组查询错误', MetricUnits.Count, 1);  

        throw new Error(errorMessage);  
      }  
    };  

    export const handler = middy(adapter)  // 执行适配器
      .use(injectLambdaContext(logger))  
      .use(captureLambdaHandler(tracer))  
      .use(logMetrics(metrics));

你可以看到我们在上面的例子中直接将返回的数据硬编码,而不是像我们通常会这样做,调用其他系统。这样的做法。

我们的硬编码的数据在这篇文章的/data/data.ts文件中的一个例子是

现在我们来看看用于查询我们代理的Stateless栈。

无状态栈

我们首先创建一个用于查询的 Lambda 函数,并配置了流处理的 URL。

    // 创建用于查询 Agent 的 lambda 函数
    const queryModelLambda: nodeLambda.NodejsFunction = 
      new nodeLambda.NodejsFunction(this, 'QueryModelLambda', { 
        functionName: 'query-model-lambda', 
        runtime: lambda.Runtime.NODEJS_20_X, 
        entry: path.join( 
          __dirname, 
          './src/adapters/primary/query-model/query-model.adapter.ts' 
        ), 
        memorySize: 1024, 
        handler: 'handler', 
        timeout: cdk.Duration.minutes(3), 
        description: '查询模型 lambda 函数', 
        architecture: lambda.Architecture.ARM_64, 
        tracing: lambda.Tracing.ACTIVE, 
        bundling: { 
          minify: true, 
        }, 
        environment: { 
          AGENT_ID: agentId, 
          AGENT_ALIAS_ID: agentAliasId, 
          ...lambdaConfig, 
        }, 
      }); 

    // 查询 Lambda 添加流式响应的函数 URL
    const queryModelLambdaUrl = queryModelLambda.addFunctionUrl({ 
      authType: lambda.FunctionUrlAuthType.NONE, 
      invokeMode: lambda.InvokeMode.RESPONSE_STREAM, 
      cors: { 
        allowedOrigins: ['*'], // 允许的源
      }, 
    });

我们允许它调用代理,如下:

    // 允许查询用的 lambda 函数查询我们的模型、知识库和代理  
    queryModelLambda.addToRolePolicy(  // 添加到角色策略中  
      new iam.PolicyStatement({  
        actions: [  
          'bedrock:RetrieveAndGenerate',  // 从 Bedrock 获取和生成数据  
          'bedrock:Retrieve',  // 从 Bedrock 获取数据  
          'bedrock:InvokeModel',  // 调用 Bedrock 模型  
          'bedrock:InvokeAgent',  // 调用 Bedrock 代理  
        ],  // 操作权限  
        resources: ['*'],  // 资源范围  
      })  
    );

我们现在来看看我们的Query Lambda功能,它通过函数URL接收用户的提示并调用代理程序。

    import { MetricUnits, Metrics } from '@aws-lambda-powertools/metrics';
    import {
      BedrockAgentRuntimeClient,
      InvokeAgentCommand,
      InvokeAgentRequest,
      InvokeAgentResponse,
    } from '@aws-sdk/client-bedrock-agent-runtime';
    import { ResponseStream, streamifyResponse } from 'lambda-stream';

    import { config } from '@config';
    import { ValidationError } from '@errors/validation-error';
    import { logger } from '@shared/index';
    import { APIGatewayProxyEventV2 } from 'aws-lambda';

    const metrics = new Metrics();
    const client = new BedrockAgentRuntimeClient();

    const agentId = config.get('agentId');
    const agentAliasId = config.get('agentAliasId');

    function parseBase64(message: Uint8Array): string {
      return Buffer.from(message).toString('utf-8');
    }

    export const queryModelAdapter = async (
      { body }: APIGatewayProxyEventV2,
      responseStream: ResponseStream
    ): Promise<void> => {
      try {
        responseStream.setContentType('application/json');

        if (!body) throw new ValidationError('没有请求正文');
        const request = JSON.parse(body);

        const { sessionAttributes, promptSessionAttributes, sessionId, prompt } = request;

        const input: InvokeAgentRequest = {
          sessionState: {
            sessionAttributes,
            promptSessionAttributes,
          },
          agentId,
          agentAliasId,
          sessionId,
          inputText: prompt,
        };

        const command: InvokeAgentCommand = new InvokeAgentCommand(input);
        const response: InvokeAgentResponse = await client.send(command);

        const chunks = [];
        const completion = response.completion || [];

        for await (const chunk of completion) {
          if (chunk.chunk && chunk.chunk.bytes) {
            const parsed = parseBase64(chunk.chunk.bytes);

            chunks.push(parsed);
          }
        }

        const returnMessage = {
          sessionId: response.sessionId,
          contentType: response.contentType,
          message: chunks.join(' '),
        };

        metrics.addMetric('SuccessfulQueryModel', MetricUnits.Count, 1);

        // 注意:在示例中,我们没有使用流式处理,而是使用了 FURL 请求超时特性,但我们可以在循环中轻松地实现流处理
        responseStream.write(returnMessage);
        responseStream.end();
      } catch (error) {
        let errorMessage = '未知错误: ';
        if (error instanceof Error) errorMessage += error.message;
        logger.error(errorMessage);

        metrics.addMetric('QueryModelError', MetricUnits.Count, 1);

        responseStream.end();
        throw error;
      }
      responseStream.end();
    };

    export const handler = streamifyResponse(queryModelAdapter);

从上面的代码我们可以看出,我们正在从Agent那里流式传输响应,这些响应以分段的形式在流中返回;然而,在这个例子中我们并没有实时更新用户的反馈,而是等待操作完成后返回一个JSON对象作为响应。让我们在下一节的实际测试中试试看!

测测应用 🧪 (注:此处的实验试管符号在中文里没有直接对应的表达,但在此处保留以保持原意的趣味性。)

⚠️注意:_ “在部署示例应用程序之前,请注意,仅OpenSearch Serverless每月就需要花费700美元,不包括Bedrock、CloudWatch、Lambda、API网关等额外费用等。”

用 Postman 测试

您可以使用位于 postman/Bedrock Agents.postman_collection.json 的 Postman 文件,并用您自己的 Lambda 函数 URL 信息来测试。

我们可以用下面的 JSON 示例来测试一下:

// JSON内容保持不变

(注:此处应保持 JSON 内容的原有格式和缩进,无需在代码块内添加注释)

...
    {  
      "agentId": "agentId",  
      "agentAliasId": "agentAliasId",  
      "sessionId": "1f6aa00e-e585-49aa-aa2d-16adb64857c6",  
      "prompt": "2024年2月25日上午时段可以预约吗"  
    }

我们可以看到,我们的代理回应如下:

我们代理的一个示例通话及其相关回应。

在幕后,我们的智能代理已经安排了多个步骤,首先是查看那天可用的会议。

我们看到代理人首先发起一个请求来查看是否有可用的会话。

我们可以看到代理决定首先发起一个“GET”请求到“/g-sessions/”,这将会返回那一天高尔夫球场的时段。

然后它又向“/g-sessions/” 发起了第二次操作请求,方法为“POST”,以完成预订。

为课程预约安排的下一步

对话式AI及其自主代理的力量在这里得以展现,因为它已经协调了多项行动来帮助客户。

我们可以看到,我们的代理人已经帮我们预订了水疗中心和酒店。

我们现在可以用各种其他场景来测试这个功能,比如查看交易详情、预定水疗预约等等,——在评论里告诉我你们的体验如何!

结尾啦👋🏽 拜拜啦

希望你看了这篇文章后会喜欢,要是你喜欢的话,欢迎分享一下并给点意见!

请去订阅我的YouTube频道,那里有类似的内容!

我会非常愿意在以下渠道与您建立联系

https://www.linkedin.com/in/lee-james-gilmore/ (领英个人资料),https://twitter.com/LeeJamesGilmore (推特个人主页)

如果你喜欢这些文章,请关注我的主页Lee James Gilmore以获取更多文章/系列,并且别忘了打个招呼👋

请也在帖子底部使用“clap”功能来表达如果你喜欢它!(你可以多次点击鼓掌!)

我是...

“大家好,我是 Lee,我是一名 AWS 社区构建者、博主、AWS 认证云架构师,同时也是全球技术与架构负责人,基于英国;现在就职于英国的 City Electrical Factors 和美国的 City Electric Supply,已经工作了六年,主要在 AWS 上从事全栈 JavaScript 开发工作。”

我觉得自己是一名无服务器技术的倡导者,非常热爱这些领域,包括AWS、创新、软件架构和技术。

_** 以下信息纯属我个人的观点,对于因使用这些信息而产生的任何后果,我概不负责。_**

你可能也对以下内容感兴趣哦:

我所有的无服务器内容索引 🚀方便在一个地方轻松浏览,包括视频、博客文章等..blog.serverlessadvocate.com
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消