Agent Blueprints for Amazon Bedrockを試す（新サービスじゃなくて、CDKコンストラクタだよ）

休暇で十分ナマッているところに、熱めなニュースが飛び込んできました！

それはこいつ

**Agent Blueprints for Amazon Bedrock**

---

ブログの発表

https://aws.amazon.com/jp/about-aws/whats-new/2024/08/blueprints-agent-based-automation-amazon-bedrock/

公式ドキュメントにはまだ言及がありませんでしたが、探したら出てきました。

ドキュメント

https://awslabs.github.io/agents-for-amazon-bedrock-blueprints/

GitHubリポジトリ

https://github.com/awslabs/agents-for-amazon-bedrock-blueprints

サンプルプロジェクトのGitHubリポジトリ

https://github.com/aws-samples/amazon-bedrock-samples/tree/main/agents-for-bedrock/agent-blueprint-templates


## サンプルを試してみる

あまり意味もわからず、サンプルに突撃してみました。


1. サンプルプロジェクトをGitHubからCloneします

    ```shell:Console
    git clone https://github.com/aws-samples/amazon-bedrock-samples.git
    cd amazon-bedrock-samples/agents-for-bedrock/agent-blueprint-templates/
    ```

1. `blueprint.sh` のinitを実行します。以下のものがインストールされます

    * nvm
    * Node.js
    * AWS CLI
    * AWS CDK
    
    ```shell:Console
    ./blueprints.sh init
    ```

    :::note
    `blueprints.sh`には、init以外のコマンドも用意されています。
    
    `init` : 必要なモジュールをインストール
    `ls` : CDKスタックの一覧を出力
    `deploy <stack>` : CDKスタックをデプロイする
   :::

    実行中のターミナルに設定を反映します。

    ```shell:Console
    source ~/.bashrc
    ```

1. AWSの認証情報をセットします

    `aws configure`

    セットできたら、`aws s3 ls`で正しくセットされているか確認しましょう

1. サンプルに用意されているCDKスタックを確認します

    ```shell:Console
    ./blueprints.sh ls
    ```

    ```text
    Available stacks:
    - 01-agent-with-function-definitions
    - 02-agent-with-return-of-control
    - 03-agent-with-kb-and-guardrails
    - 04-agent-with-lambda-parser
    - 05-agent-with-classification-instructions
    ```

    それぞれのCDKスタックの内容は以下のとおりです。
    
    | CDKスタック | 内容 | 解説URL |
    | --- | --- | --- | 
    | 01-agent-with-function-definitions | Lambda関数を定義したスタック | [リンク](https://awslabs.github.io/agents-for-amazon-bedrock-blueprints/using-templates/ag-function-definition/) |
    | 02-agent-with-return-of-control | Return of controlを定義したスタック | [リンク](https://awslabs.github.io/agents-for-amazon-bedrock-blueprints/using-templates/ag-roc/) |
    | 03-agent-with-kb-and-guardrails | knowledgeBaseとGuardrailsを定義したスタック |  [リンク](https://awslabs.github.io/agents-for-amazon-bedrock-blueprints/using-templates/ag-kb-guardrails/) |
    | 04-agent-with-lambda-parser | Parser Lambdaを定義したスタック|  [リンク](https://awslabs.github.io/agents-for-amazon-bedrock-blueprints/using-templates/ag-advance-prompts-parser/) |
    | 05-agent-with-classification-instructions | 簡単なクラス分類を定義したスタック | [リンク](https://awslabs.github.io/agents-for-amazon-bedrock-blueprints/using-templates/ag-classification/) |

1. 「01-agent-with-function-definitions」スタックをデプロイします

    ```
    ./blueprints.sh deploy 01-agent-with-function-definitions
    ```

    途中で作成される`cdk.out/AgentWithFunctionDefinitionStack.template.json`をApplication Composerで表示してみました。

    LambdaやAuroraがデプロイされます

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/53f605bf-ec88-2f3b-fa39-25896bdcb0d4.png)

    同じ内容ですが、デプロイ完了後、`aws cloudformation describe-stack-resources --stack-name AgentWithFunctionDefinitionStack --output text`を実行した結果です。

    | Logical ID | Resource Type | Status |
    |------------|---------------|--------|
    | AWS679f53fac002430cb0da5b7982bd22872D164C4C | AWS::Lambda::Function | CREATE_COMPLETE |
    | AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2 | AWS::IAM::Role | CREATE_COMPLETE |
    | AmazonBedrockAgentBlueprintsStackAgentBlueprinthrassistantagentwithfunctiondefinition1D2A9A47 | AWS::Bedrock::Agent | CREATE_COMPLETE |
    | AmazonBedrockAgentBlueprintsStackBedrockServiceRole55949A6B | AWS::IAM::Role | CREATE_COMPLETE |
    | AmazonBedrockAgentBlueprintsStackBedrockServiceRoleDefaultPolicy52DAA442 | AWS::IAM::Policy | CREATE_COMPLETE |
    | CDKMetadata | AWS::CDK::Metadata | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDAuroraClusterForAgentWithFD382D4012 | AWS::RDS::DBCluster | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDAuroraSecretForAgentWithFD8B12F67F | AWS::SecretsManager::Secret | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDAuroraSecretForAgentWithFDAttachment4D738340 | AWS::SecretsManager::SecretTargetAttachment | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDLambdaRoleToPopulateData3DFAB4C7 | AWS::IAM::Role | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDPopulateSampleDataFuncB4C9989C | AWS::Lambda::Function | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDTriggerPopulateDataFunction0445536E | Custom::AWS | CREATE_COMPLETE |
    | RDSDatabaseForAgentWithFDTriggerPopulateDataFunctionCustomResourcePolicy651A2782 | AWS::IAM::Policy | CREATE_COMPLETE |
    | VacationsActionGroupLambdaRoleAF8D096D | AWS::IAM::Role | CREATE_COMPLETE |
    | VacationsActionGroupVacationsActionGroupLambdaFunction17AAB9FC | AWS::Lambda::Function | CREATE_COMPLETE |
    | VacationsActionGroupVacationsActionGroupLambdaFunctionBedrockAgentInvokePermission91baaf9a5c1D17BC084 | AWS::Lambda::Permission | CREATE_COMPLETE |

1. Bedrockの画面で確認します

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/0817d2b0-7854-bb50-b3b5-11b04a0ad7a0.png)
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/196e588c-2169-5dda-2ee4-3766fe7a037d.png)
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/fbdbf4d8-ddc0-40fe-541d-65e36cc3c2b0.png)

    それっぽく動きました

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b57ca7f1-3c3b-f439-284a-1351d4013f47.png)


## で、結局なんなの？

サンプルだと何がなんだかわからなかったので、いちからやってみます。

```shell:Console
cd ~/
mkdir bedrock-agent
cd bedrock-agent
cdk init app --language=typescript
```

`Amazon Bedrock Agents Blueprints`モジュールをインストールします。

```shell:Console
npm install @aws/agents-for-amazon-bedrock-blueprints
```

:::note warn
いつもながら、いい感じに名称が揺れてます。。

パッケージ名：agents-for-amazon-bedrock-blueprints
ドキュメントでの名称：Agent Blueprints for Amazon Bedrock
:::

CDKのコードを書きます。（コードの細かな内容は、先程の「01-agent-with-function-definitions」から拝借しています）

まず、AgentDefinitionBuilderを使って、`aws_bedrock.CfnAgentProps`を生成します。

```typescript:bedrock-agent/lib/bedrock-agent-stack.ts
    const agentDef = new AgentDefinitionBuilder(this, 'HRAssistantAgent', {})
      .withAgentName('hr-assistant-agent-with-function-definition')
      .withInstruction(
        'As an HR agent, your role involves assisting employees with a range of HR tasks. ' +
        'These include managing vacation requests both present and future, ' +
        'reviewing past vacation usage, tracking remaining vacation days, and addressing general HR inquiries. ' +
        'You will rely on contextual details provided by employees to fulfill their HR needs efficiently. ' +
        'When discussing dates, always use the YYYY-MM-DD format unless clarified otherwise by the employee. ' +
        'If you are unsure about any details, do not hesitate to ask the employee for clarification.' +
        'Use "you" to address the employee directly, making it more personal and actionable.' +
        'Make sure the responses are direct, straightforward, and do not contain unnecessary information.'
      )
      .withFoundationModel('anthropic.claude-3-sonnet-20240229-v1:0')
      .withUserInput()
      .build();
```

:::note
`.withXXX`を使ってビルドできるのはいいですね。
:::

次にAction Groupの定義を行います。

```typescript:bedrock-agent/lib/bedrock-agent-stack.ts
    const hrAssistanceAction = new AgentActionGroup(this, 'VacationsActionGroup', {
      actionGroupName: 'VacationsActionGroup',
      description: 'Actions for getting the number of available vacations days for an employee and confirm new time off',
      actionGroupExecutor: {
        lambdaDefinition: {
          lambdaCode: readFileSync(join(__dirname, '..', 'lambda', 'ag-assist-with-vacations-lambda.ts')),
          lambdaHandler: 'handler',
          lambdaRuntime: Runtime.NODEJS_18_X,
          timeoutInMinutes: 15,
        }
      },
      schemaDefinition: {
        functionSchema: {
          functions: [{
            name: 'get_available_vacation_days',
            description: 'Get the number of vacation days available for a certain employee',
            parameters: {
              employee_id: {
                type: 'integer',
                description: 'The ID of the employee to get the available vacations',
                required: true
              }
            }
          }, {
            name: 'reserve_vacation_time',
            description: 'Reserve vacation time for a specific employee - you need all parameters to reserve vacation time',
            parameters: {
              employee_id: {
                type: 'integer',
                description: 'The ID of the employee to reserve the vacation time for',
                required: true
              },
              start_date: {
                type: 'integer',
                description: 'The start date of the vacation time to reserve',
                required: true
              },
              end_date: {
                type: 'integer',
                description: 'The end date of the vacation time to reserve',
                required: true
              }
            }
          }]
        }
      },
    });
```

:::note
OpenAPIスキーマを指定する方法も可能です
:::


最後に`BedrockAgentBlueprintsConstruct`を生成します。

```typescript:bedrock-agent/lib/bedrock-agent-stack.ts
    new BedrockAgentBlueprintsConstruct(this, 'AmazonBedrockAgentBlueprintsStack', {
      agentDefinition: agentDef,
      actionGroups: [hrAssistanceAction],
    });
```

これで完成です。

:::note

<details><summary>bedrock-agent-stack.ts全体</summary>

```typescript
import { AgentActionGroup, AgentDefinitionBuilder, BedrockAgentBlueprintsConstruct } from '@aws/agents-for-amazon-bedrock-blueprints';
import * as cdk from 'aws-cdk-lib';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import { readFileSync } from 'fs';
import { join } from 'path';

export class BedrockAgentStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const agentDef = new AgentDefinitionBuilder(this, 'HRAssistantAgent', {})
      .withAgentName('hr-assistant-agent-with-function-definition')
      .withInstruction(
        'As an HR agent, your role involves assisting employees with a range of HR tasks. ' +
        'These include managing vacation requests both present and future, ' +
        'reviewing past vacation usage, tracking remaining vacation days, and addressing general HR inquiries. ' +
        'You will rely on contextual details provided by employees to fulfill their HR needs efficiently. ' +
        'When discussing dates, always use the YYYY-MM-DD format unless clarified otherwise by the employee. ' +
        'If you are unsure about any details, do not hesitate to ask the employee for clarification.' +
        'Use "you" to address the employee directly, making it more personal and actionable.' +
        'Make sure the responses are direct, straightforward, and do not contain unnecessary information.'
      )
      .withFoundationModel('anthropic.claude-3-sonnet-20240229-v1:0')
      .withUserInput()
      .build();


    const hrAssistanceAction = new AgentActionGroup(this, 'VacationsActionGroup', {
      actionGroupName: 'VacationsActionGroup',
      description: 'Actions for getting the number of available vacations days for an employee and confirm new time off',
      actionGroupExecutor: {
        lambdaDefinition: {
          lambdaCode: readFileSync(join(__dirname, '..', 'lambda', 'ag-assist-with-vacations-lambda.ts')),
          lambdaHandler: 'handler',
          lambdaRuntime: Runtime.NODEJS_18_X,
          timeoutInMinutes: 15,
        }
      },
      schemaDefinition: {
        functionSchema: {
          functions: [{
            name: 'get_available_vacation_days',
            description: 'Get the number of vacation days available for a certain employee',
            parameters: {
              employee_id: {
                type: 'integer',
                description: 'The ID of the employee to get the available vacations',
                required: true
              }
            }
          }, {
            name: 'reserve_vacation_time',
            description: 'Reserve vacation time for a specific employee - you need all parameters to reserve vacation time',
            parameters: {
              employee_id: {
                type: 'integer',
                description: 'The ID of the employee to reserve the vacation time for',
                required: true
              },
              start_date: {
                type: 'integer',
                description: 'The start date of the vacation time to reserve',
                required: true
              },
              end_date: {
                type: 'integer',
                description: 'The end date of the vacation time to reserve',
                required: true
              }
            }
          }]
        }
      },
    });

    new BedrockAgentBlueprintsConstruct(this, 'AmazonBedrockAgentBlueprintsStack', {
      agentDefinition: agentDef,
      actionGroups: [hrAssistanceAction],
    });

  }
}
```

</details>


:::

`cdk deploy`すると、以下のものが作成されます。

* Agents for Amazon Bedrockの設定もろもろ
* アクショングループ
* アクショングループのLambda

**これはいい！！**

### knowledgeBaseも簡単に作成できるよ！

以下のコードでこれだけのことを行います！！！！！

* ローカルディレクトリ配下にあるファイルをS3に上げる
* OpenSearch Serverlessコレクションを作成する
* S3のバケットをデータソースとして設定し、データ同期
* Agentの一部にknowledgeBaseを設定する

やば

```typescript:bedrock-agent/lib/bedrock-agent-stack.ts
    // ローカルにあるファイルの場所を指定する
    const assetDir = join(__dirname, '..', 'assets', 'kb_documents');

    const assetFiles = readdirSync(assetDir).map(fileName => {
      return readFileSync(join(assetDir, fileName));
    });

    // knowledgeBaseを定義する
    const knowledgeBase = new AgentKnowledgeBase(this, "BedrockDocs", {
      kbName: 'booking-agent-kb',
      agentInstruction: 'Access the knowledge base when customers ask about the plates in the menu.',
      assetFiles: assetFiles
    })

    new BedrockAgentBlueprintsConstruct(this, 'AmazonBedrockAgentBlueprintsStack', {
      agentDefinition: agentDef,
      actionGroups: [hrAssistanceAction],
      knowledgeBases: [knowledgeBase]    // ここを追加
    });
```

と思ったら、同期に失敗しました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8607c1d8-4aa8-1cf3-bf3c-49650de637f3.png)

Oh,no...

どうもIAM権限が変。。
S3の権限部分の`aws:SourceAccount`を`aws:ResourceAccount`に変えるとうまくいきました。

Issueを上げてみました。

https://github.com/awslabs/agents-for-amazon-bedrock-blueprints/issues/21

---

ちなみにスタックを削除すると、OpenSearch Serverlessのコレクションも削除されました。