GenUのバックエンド (CDK) 詳細解説　⑦WebSearchAgentStack スタックの解説

## はじめに

皆さん、こんにちは。

私は業務でデータ利活用基盤を取り扱っていること、2024 AWS Japan Top Engineer に選出されたということから、[AWS GenU](https://aws-samples.github.io/generative-ai-use-cases-jp/) およびそれに必要なデータ基盤の探求 ([Snowflake](https://www.snowflake.com/ja/), [dbt](https://www.getdbt.com/), [Iceberg](https://iceberg.apache.org/), etc) に取り組む必要があると考えています。

本投稿では、[GenU のバックエンドである CDK コード](https://github.com/aws-samples/generative-ai-use-cases-jp/tree/main/packages/cdk)を詳細に解説します。
自身そして閲覧して頂いた皆様の GenU への理解が少しでも深まり、生成 AI の民主化につながっていければと考えています。

## 前回までのおさらい

前回までで、以下が完了しました。

- [①AWS CDK のセットアップ](https://qiita.com/siruko/items/fd25fdcf89615cb85262)
- [②AWS CDK の動作確認](https://qiita.com/siruko/items/73169f986b4173e3d3a5)
- [③GenU の概要](https://qiita.com/siruko/items/625801b9e1847b305c1e)
- [④GenU CDK スタックの概要](https://qiita.com/siruko/items/8570ed43f7162ea4b907)
- [⑤CloudFrontWafStack スタックの解説](https://qiita.com/siruko/items/30439576ee7c63165d21)
- [⑥RagKnowledgeBaseStack スタックの解説](https://qiita.com/siruko/items/1223c9d22e73168a8809)

GenU の CDK は最大で以下の 6 つの子スタックを作成します。

- `CloudFrontWafStack`
- `RagKnowledgeBaseStack`
- `AgentStack`
- `GuardrailStack`
- `GenerativeAiUseCasesStack` ※メインスタック
- `DashboardStack`

今回は GenU 内の `WebSearchAgentStack` スタックを解説していきたいと思います。

### AgentStack スタック

AgentStack は Web 検索やコード生成・実行を行う Bedrock エージェントのスタックです。
アーキテクチャ図でいうと、以下の赤枠の部分にあたります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217144/d349c179-ca0e-4413-80b5-5ac0c4cb5218.png)

AgentStack の実体は [`packages/cdk/lib/agent-stack.ts`](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/packages/cdk/lib/agent-stack.ts) にあります。

```typescript:packages/cdk/lib/agent-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Agent } from './construct';
import { Agent as AgentType } from 'generative-ai-use-cases-jp';
import { StackInput } from './stack-input';

export interface AgentStackProps extends StackProps {
  params: StackInput;
}

export class AgentStack extends Stack {
  public readonly agents: AgentType[];

  constructor(scope: Construct, id: string, props: AgentStackProps) {
    super(scope, id, props);

    const { searchAgentEnabled, searchApiKey } = props.params;

    const agent = new Agent(this, 'Agent', {
      searchAgentEnabled,
      searchApiKey,
    });

    this.agents = agent.agents;
  }
}
```

このスタックでは、以下のリソースを作成しています。

- `Agent`

#### AgentStack > Agent リソース

Agent リソースはパラメータとして、以下を受け取っています。

- searchAgentEnabled: デフォルト false ([`stack-input.ts`](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/packages/cdk/lib/stack-input.ts)で設定)
- searchApiKey: デフォルト null ([`stack-input.ts`](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/packages/cdk/lib/stack-input.ts)で設定)

Agent の実体は `packages/cdk/lib/construct/agent.ts` にあります。
以下のソースコードが Agent の定義です。

```typescript:packages/cdk/lib/construct/agent.ts
import { Duration, Lazy, Names, RemovalPolicy } from 'aws-cdk-lib';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import {
  Effect,
  PolicyDocument,
  PolicyStatement,
  Role,
  ServicePrincipal,
} from 'aws-cdk-lib/aws-iam';
import {
  BlockPublicAccess,
  Bucket,
  BucketEncryption,
} from 'aws-cdk-lib/aws-s3';
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
import { CfnAgent, CfnAgentAlias } from 'aws-cdk-lib/aws-bedrock';
import { Agent as AgentType } from 'generative-ai-use-cases-jp';

interface AgentProps {
  // Context Params
  searchAgentEnabled: boolean;
  searchApiKey?: string | null;
}

export class Agent extends Construct {
  public readonly agents: AgentType[] = [];

  constructor(scope: Construct, id: string, props: AgentProps) {
    super(scope, id);

    const suffix = Lazy.string({ produce: () => Names.uniqueId(this) });

    const { searchAgentEnabled, searchApiKey } = props;

    // agents for bedrock の schema やデータを配置するバケット
    const s3Bucket = new Bucket(this, 'Bucket', {
      encryption: BucketEncryption.S3_MANAGED,
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      enforceSSL: true,
      blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
    });

    // schema を s3 に配置
    const schema = new BucketDeployment(this, 'ApiSchemaBucket', {
      sources: [Source.asset('assets/api-schema')],
      destinationBucket: s3Bucket,
      destinationKeyPrefix: 'api-schema',
    });

    // Agent
    const bedrockAgentRole = new Role(this, 'BedrockAgentRole', {
      assumedBy: new ServicePrincipal('bedrock.amazonaws.com'),
      inlinePolicies: {
        BedrockAgentS3BucketPolicy: new PolicyDocument({
          statements: [
            new PolicyStatement({
              effect: Effect.ALLOW,
              resources: [s3Bucket.bucketArn, `${s3Bucket.bucketArn}/*`],
              actions: ['*'],
            }),
          ],
        }),
        BedrockAgentBedrockModelPolicy: new PolicyDocument({
          statements: [
            new PolicyStatement({
              effect: Effect.ALLOW,
              resources: ['*'],
              actions: ['bedrock:*'],
            }),
          ],
        }),
      },
    });

    // Search Agent
    if (searchAgentEnabled && searchApiKey) {
      const bedrockAgentLambda = new NodejsFunction(
        this,
        'BedrockAgentLambda',
        {
          runtime: Runtime.NODEJS_LATEST,
          entry: './lambda/agent.ts',
          timeout: Duration.seconds(300),
          environment: {
            SEARCH_API_KEY: searchApiKey ?? '',
          },
        }
      );
      bedrockAgentLambda.grantInvoke(
        new ServicePrincipal('bedrock.amazonaws.com')
      );

      const searchAgent = new CfnAgent(this, 'SearchAgent', {
        agentName: `SearchEngineAgent-${suffix}`,
        actionGroups: [
          {
            actionGroupName: 'Search',
            actionGroupExecutor: {
              lambda: bedrockAgentLambda.functionArn,
            },
            apiSchema: {
              s3: {
                s3BucketName: schema.deployedBucket.bucketName,
                s3ObjectKey: 'api-schema/api-schema.json',
              },
            },
            description: 'Search',
          },
          {
            actionGroupName: 'UserInputAction',
            parentActionGroupSignature: 'AMAZON.UserInput',
          },
        ],
        agentResourceRoleArn: bedrockAgentRole.roleArn,
        idleSessionTtlInSeconds: 3600,
        autoPrepare: true,
        description: 'Search Agent',
        foundationModel: 'anthropic.claude-3-haiku-20240307-v1:0',
        instruction:
          'あなたは指示に応えるアシスタントです。 指示に応えるために必要な情報が十分な場合はすぐに回答し、不十分な場合は検索を行い必要な情報を入手し回答してください。複数回検索することが可能です。',
      });

      const searchAgentAlias = new CfnAgentAlias(this, 'SearchAgentAlias', {
        agentId: searchAgent.attrAgentId,
        agentAliasName: 'v1',
      });

      this.agents.push({
        displayName: 'SearchEngine',
        agentId: searchAgent.attrAgentId,
        aliasId: searchAgentAlias.attrAgentAliasId,
      });
    }

    // Code Interpreter Agent
    const codeInterpreterAgent = new CfnAgent(this, 'CodeInterpreterAgent', {
      agentName: `CodeInterpreterAgent-${suffix}`,
      actionGroups: [
        {
          actionGroupName: 'CodeInterpreter',
          parentActionGroupSignature: 'AMAZON.CodeInterpreter',
        },
      ],
      agentResourceRoleArn: bedrockAgentRole.roleArn,
      idleSessionTtlInSeconds: 3600,
      autoPrepare: true,
      description: 'Code Interpreter',
      foundationModel: 'anthropic.claude-3-sonnet-20240229-v1:0',
      instruction: `あなたは、コード実行、チャート生成、複雑なデータ分析の機能を持つ高度な AI エージェントです。あなたの主な機能は、これらの機能を活用して問題を解決し、ユーザーの要求を満たすことです。あなたの主な特性と指示は次のとおりです。

コード実行:
- リアルタイムで Python 環境にアクセスし、コードを記述および実行できます。
- 計算やデータ操作を求められた場合は、常に正確性を確保するためにこのコード実行機能を使用してください。
- コードを実行した後、正確な出力を報告し、結果を説明してください。

データ分析:
- 統計分析、データ可視化、機械学習アプリケーションなど、複雑なデータ分析タスクに優れています。
- 問題を理解し、データを準備し、分析を実行し、結果を解釈するなど、体系的にデータ分析タスクに取り組んでください。

問題解決アプローチ:
- 問題や要求が提示された場合は、それを段階に分けてください。
- 考え方のプロセスと取っている手順を明確に伝えてください。
- タスクが複数の手順やツールを必要とする場合は、開始前にアプローチを概説してください。

透明性と正確性:
- 自分が何をしているかを常に明確にしてください。コードを実行する場合は、そのことを伝えてください。画像を生成する場合は、その旨を説明してください。
- 何かを確信できない場合や、タスクが自分の能力を超えている場合は、はっきりとそのことを伝えてください。
- 仮説的な結果を実際の結果として提示しないでください。コード実行や画像生成から得られた実際の結果のみを報告してください。

対話スタイル:
- 単純な質問には簡潔に、複雑なタスクには詳細な説明を提供してください。
- 適切に専門用語を使いますが、分かりやすい説明を求められた場合は、簡単な言葉で説明する準備をしてください。
- 役立つ関連情報や代替アプローチを積極的に提案してください。

継続的改善:
- タスクを完了した後、ユーザーに説明が必要かどうか、フォローアップの質問があるかどうかを尋ねてください。
- フィードバックに耳を傾け、それに応じてアプローチを調整してください。

あなたの目標は、コード実行、画像生成、データ分析の独自の機能を活用して、正確で役立つ洞察に富む支援を提供することです。ユーザーの要求に対して、最も実用的で効果的な解決策を提供するよう常に努めてください。`,
    });

    const codeInterpreterAgentAlias = new CfnAgentAlias(
      this,
      'CodeInterpreterAgentAlias',
      {
        agentId: codeInterpreterAgent.attrAgentId,
        agentAliasName: 'v1',
      }
    );

    this.agents.push({
      displayName: 'CodeInterpreter',
      agentId: codeInterpreterAgent.attrAgentId,
      aliasId: codeInterpreterAgentAlias.attrAgentAliasId,
    });
  }
}
```

この中では、以下の AWS リソースを生成するコンストラクトを定義しています。

- `Bucket`
- `BucketDeployment`
- `Role`
- `NodejsFunction`
- `CfnAgent`　※SearchAgent
- `CfnAgent`　※CodeInterpreterAgent

上から 1 つずつ見ていきます。

#### AgentStack > Agent > Bucket リソース

Bucket は Bedrock エージェントが使用する Web 検索 API スキーマ定義などを保存する S3 バケットのリソースです。
以下のソースコードが Bucket の定義です。

```typescript:packages/cdk/lib/construct/agent.ts (抜粋)
    // agents for bedrock の schema やデータを配置するバケット
    const s3Bucket = new Bucket(this, 'Bucket', {
      encryption: BucketEncryption.S3_MANAGED,
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      enforceSSL: true,
      blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
    });
```

この中では、以下の AWS リソースを生成しています。

- Bedrock エージェント用 S3 バケット
  - 暗号化: AWS マネージドキーでの暗号化
  - 削除ポリシー: バケットを削除 (バケットに含まれるオブジェクトも削除)
  - SSL アクセス: 強制
  - パブリックアクセス: ブロック

#### AgentStack > Agent > BucketDeployment コンストラクト

BucketDeployment は S3 バケットへのデプロイ (ファイルアップロード) を指定します。
以下のソースコードが BucketDeployment の定義です。

```typescript:packages/cdk/lib/cloud-front-waf-stack.ts (抜粋)
    // schema を s3 に配置
    const schema = new BucketDeployment(this, 'ApiSchemaBucket', {
      sources: [Source.asset('assets/api-schema')],
      destinationBucket: s3Bucket,
      destinationKeyPrefix: 'api-schema',
    });
```

この中では、Bedrock エージェント用 S3 バケットに、[`assets/api-schema`](https://github.com/aws-samples/generative-ai-use-cases-jp/tree/main/packages/cdk/assets/api-schema) 配下のローカルファイルをアップロードすることを指定しています。
S3 バケット上のキープレフィックスは `api-schema` を指定しています。

##### api-schema.json の内容

`assets/api-schema` フォルダには [`api-schema.json`](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/packages/cdk/assets/api-schema/api-schema.json) というファイルのみ格納されています。

`api-schema.json` の内容は以下の通りです。

```json:packages/cdk/assets/api-schema/api-schema.json
{
    "openapi": "3.0.0",
    "info": {
        "title": "Research Assisstant Agent API",
        "version": "1.0.0",
        "description": "API to search information from internet."
    },
    "paths": {
        "/search": {
            "get": {
                "summary": "Get information from search engine",
                "description": "キーワードで検索し情報を取得します。調査、調べる、Xについて教える、まとめるといったタスクで利用できます。会話から検索キーワードを推測してください。検索結果には関連度の低い内容も含まれているため関連度の高い内容のみを参考に回答してください。複数回実行可能です。",
                "operationId": "Search",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "keyword": {
                                        "type": "string",
                                        "description": "検索キーワード"
                                    }
                                },
                                "required": [
                                    "keyword"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "検索結果",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "results": {
                                            "type": "string",
                                            "description": "検索結果"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```

このファイルには、`/search`というパスに対してキーワード検索を行う API の定義が記述されています。

#### AgentStack > Agent > Role リソース

Role は Bedrock エージェントにアタッチする IAM ロールです。
以下のソースコードが Role の定義です。

```typescript:packages/cdk/lib/construct/agent.ts (抜粋)
    // Agent
    const bedrockAgentRole = new Role(this, 'BedrockAgentRole', {
      assumedBy: new ServicePrincipal('bedrock.amazonaws.com'),
      inlinePolicies: {
        BedrockAgentS3BucketPolicy: new PolicyDocument({
          statements: [
            new PolicyStatement({
              effect: Effect.ALLOW,
              resources: [s3Bucket.bucketArn, `${s3Bucket.bucketArn}/*`],
              actions: ['*'],
            }),
          ],
        }),
        BedrockAgentBedrockModelPolicy: new PolicyDocument({
          statements: [
            new PolicyStatement({
              effect: Effect.ALLOW,
              resources: ['*'],
              actions: ['bedrock:*'],
            }),
          ],
        }),
      },
    });
```

この中では、以下の AWS リソースを生成しています。

- `BedrockAgentRole` ロール
  - `BedrockAgentS3BucketPolicy` ポリシー
    - `s3に対する全アクション` の許可権限
    - 対象リソースは S3 の Bedrock エージェント用 S3 バケット
  - `BedrockAgentBedrockModelPolicy` ポリシー
    - `bedrockに対する全アクション` の許可権限
    - 対象リソースは全リソース

この `BedrockAgentRole` ロールを後述する `CfnAgent` にアタッチすることで、Bedrock エージェントが S3 にアクセスしたり、Bedrock のモデル呼び出しなどが行えるようになります。

#### AgentStack > Agent > NodejsFunction リソース

NodejsFunction は Web 検索処理を行う Lambda 関数のリソースです。Bedrock エージェントから呼び出されます。

以下のソースコードが NodejsFunction の定義です。

```typescript:packages/cdk/lib/construct/agent.ts (抜粋)
    const { searchAgentEnabled, searchApiKey } = props;

    /* 中略 */

    // Search Agent
    if (searchAgentEnabled && searchApiKey) {
      const bedrockAgentLambda = new NodejsFunction(
        this,
        'BedrockAgentLambda',
        {
          runtime: Runtime.NODEJS_LATEST,
          entry: './lambda/agent.ts',
          timeout: Duration.seconds(300),
          environment: {
            SEARCH_API_KEY: searchApiKey ?? '',
          },
        }
      );
      bedrockAgentLambda.grantInvoke(
        new ServicePrincipal('bedrock.amazonaws.com')
      );

      /* 中略 */
    }
```

この中では、以下の AWS リソースを生成しています。

- `BedrockAgentLambda` Lambda 関数
  - パラメータ値の `searchAgentEnabled` が `true` の場合かつ、`searchApiKey` に値が設定されている場合に生成
  - `searchAgentEnabled` のデフォルト値は `false` ([stack-input.ts`](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/packages/cdk/lib/stack-input.ts)で設定)
  - `searchApiKey` には [Brave サーチ API](https://brave.com/ja/search/api/) の API キーを設定する (後述)
  - ランタイムは NodeJS の最新版
  - ソースコードは [`./lambda/agent.ts`](https://qiita.com/drafts/aef0a9599df60d47eb1e/edit#bedrockagentlambda-%E9%96%A2%E6%95%B0%E3%81%AE%E5%87%A6%E7%90%86) (後述)
  - タイムアウトは 300 秒 (5 分)
  - 環境変数 `SEARCH_API_KEY` にパラメータの `searchApiKey` を指定
  - Bedrock からの呼び出しを許可

##### BedrockAgentLambda 関数の処理

この `BedrockAgentLambda` 関数の実体は [`packages/cdk/lambda/agent.ts`] にあります。

```typescript:packages/cdk/lambda/agent.ts
import {
  AgentInput,
  AgentOutput,
  BraveSearchResult,
} from 'generative-ai-use-cases-jp';

export const handler = async (event: AgentInput): Promise<AgentOutput> => {
  try {
    // Params
    const props = event.requestBody.content['application/json'].properties;
    let keyword = '';
    for (const prop of props) {
      if (prop.name === 'keyword') {
        keyword = prop.value;
      }
    }

    // Search
    const searchUrl = `https://api.search.brave.com/res/v1/web/search?q=${keyword}&count=3&text_decorations=0`;
    const searchApiKey = process.env.SEARCH_API_KEY || '';
    const response = await fetch(searchUrl, {
      headers: {
        'X-Subscription-Token': searchApiKey,
      },
    });
    const data = await response.json();
    console.log(JSON.stringify(data));

    const results = data.web.results.map((result: BraveSearchResult) => ({
      title: result.title,
      description: result.description,
      extra_snippets: result.extra_snippets,
      url: result.url,
    }));

    // Create Response Object
    const response_body = {
      'application/json': {
        body: `<search_results>${JSON.stringify(results)}</search_results>`,
      },
    };
    const action_response = {
      actionGroup: event.actionGroup,
      apiPath: event.apiPath,
      httpMethod: event.httpMethod,
      httpStatusCode: 200,
      responseBody: response_body,
    };
    const api_response = {
      messageVersion: '1.0',
      response: action_response,
    };

    return api_response;
  } catch (error: unknown) {
    console.log(error);
    const action_response = {
      actionGroup: event.actionGroup,
      apiPath: event.apiPath,
      httpMethod: event.httpMethod,
      httpStatusCode: 500,
      responseBody: {
        'application/json': {
          body: 'Internal Server Error',
        },
      },
    };
    const api_response = {
      messageVersion: '1.0',
      response: action_response,
    };
    return api_response;
  }
};

```

この Lambda 関数では [Brave サーチ API](https://brave.com/ja/search/api/) を使用して、Web インデックス検索を行い、結果を返却します。

#### AgentStack > Agent > CfnAgent (SearchAgent) リソース

CfnAgent (SearchAgent) は Web 検索を行う Bedrock エージェントです。
以下のソースコードが CfnAgent (SearchAgent) の定義です。

```typescript:packages/cdk/lib/construct/agent.ts (抜粋)

    // Search Agent
    if (searchAgentEnabled && searchApiKey) {

      /* 中略 */

      const searchAgent = new CfnAgent(this, 'SearchAgent', {
        agentName: `SearchEngineAgent-${suffix}`,
        actionGroups: [
          {
            actionGroupName: 'Search',
            actionGroupExecutor: {
              lambda: bedrockAgentLambda.functionArn,
            },
            apiSchema: {
              s3: {
                s3BucketName: schema.deployedBucket.bucketName,
                s3ObjectKey: 'api-schema/api-schema.json',
              },
            },
            description: 'Search',
          },
          {
            actionGroupName: 'UserInputAction',
            parentActionGroupSignature: 'AMAZON.UserInput',
          },
        ],
        agentResourceRoleArn: bedrockAgentRole.roleArn,
        idleSessionTtlInSeconds: 3600,
        autoPrepare: true,
        description: 'Search Agent',
        foundationModel: 'anthropic.claude-3-haiku-20240307-v1:0',
        instruction:
          'あなたは指示に応えるアシスタントです。 指示に応えるために必要な情報が十分な場合はすぐに回答し、不十分な場合は検索を行い必要な情報を入手し回答してください。複数回検索することが可能です。',
      });

      const searchAgentAlias = new CfnAgentAlias(this, 'SearchAgentAlias', {
        agentId: searchAgent.attrAgentId,
        agentAliasName: 'v1',
      });

      this.agents.push({
        displayName: 'SearchEngine',
        agentId: searchAgent.attrAgentId,
        aliasId: searchAgentAlias.attrAgentAliasId,
      });
    }
```

この中では、以下の Bedrock エージェントを生成しています。

- Bedrock エージェント
  - 名前は `SearchEngineAgent-ABCD1234`
  - アクションとして前述の `BedrockAgentLambda` 関数を呼び出す
  - 呼び出し時の API スキーマは前述の `api-schema.json` を指定
  - ロールは前述の `BedrockAgentRole` ロールを設定する
  - ユーザーとの会話情報を保持する期間は 1 時間
  - 基盤モデルは `anthropic.claude-3-haiku-20240307-v1:0` を指定
  - エージェントへの指示に以下を指定
    **あなたは指示に応えるアシスタントです。 指示に応えるために必要な情報が十分な場合はすぐに回答し、不十分な場合は検索を行い必要な情報を入手し回答してください。複数回検索することが可能です。**
- 作成した Bedrock エージェントのエイリアスに `v1` を指定
- `SearchEngine` という表示名でエージェントに登録

なお、`BedrockAgentLambda` 関数と同様、以下を満たす場合のみ Bedrock エージェントを生成します。

> パラメータ値の `searchAgentEnabled` が `true` の場合かつ、`searchApiKey` に値が設定されている場合に生成

#### AgentStack > Agent > CfnAgent (CodeInterpreterAgent) リソース

CfnAgent (CodeInterpreterAgent) は コード生成および生成したコードの実行を行い、結果を出力することができる Bedrock エージェントです。コードインタプリターと呼ばれます。

以下のソースコードが CfnAgent (CodeInterpreterAgent) の定義です。

```typescript:packages/cdk/lib/construct/agent.ts (抜粋)
    // Code Interpreter Agent
    const codeInterpreterAgent = new CfnAgent(this, 'CodeInterpreterAgent', {
      agentName: `CodeInterpreterAgent-${suffix}`,
      actionGroups: [
        {
          actionGroupName: 'CodeInterpreter',
          parentActionGroupSignature: 'AMAZON.CodeInterpreter',
        },
      ],
      agentResourceRoleArn: bedrockAgentRole.roleArn,
      idleSessionTtlInSeconds: 3600,
      autoPrepare: true,
      description: 'Code Interpreter',
      foundationModel: 'anthropic.claude-3-sonnet-20240229-v1:0',
      instruction: `あなたは、コード実行、チャート生成、複雑なデータ分析の機能を持つ高度な AI エージェントです。あなたの主な機能は、これらの機能を活用して問題を解決し、ユーザーの要求を満たすことです。あなたの主な特性と指示は次のとおりです。

コード実行:
- リアルタイムで Python 環境にアクセスし、コードを記述および実行できます。
- 計算やデータ操作を求められた場合は、常に正確性を確保するためにこのコード実行機能を使用してください。
- コードを実行した後、正確な出力を報告し、結果を説明してください。

データ分析:
- 統計分析、データ可視化、機械学習アプリケーションなど、複雑なデータ分析タスクに優れています。
- 問題を理解し、データを準備し、分析を実行し、結果を解釈するなど、体系的にデータ分析タスクに取り組んでください。

問題解決アプローチ:
- 問題や要求が提示された場合は、それを段階に分けてください。
- 考え方のプロセスと取っている手順を明確に伝えてください。
- タスクが複数の手順やツールを必要とする場合は、開始前にアプローチを概説してください。

透明性と正確性:
- 自分が何をしているかを常に明確にしてください。コードを実行する場合は、そのことを伝えてください。画像を生成する場合は、その旨を説明してください。
- 何かを確信できない場合や、タスクが自分の能力を超えている場合は、はっきりとそのことを伝えてください。
- 仮説的な結果を実際の結果として提示しないでください。コード実行や画像生成から得られた実際の結果のみを報告してください。

対話スタイル:
- 単純な質問には簡潔に、複雑なタスクには詳細な説明を提供してください。
- 適切に専門用語を使いますが、分かりやすい説明を求められた場合は、簡単な言葉で説明する準備をしてください。
- 役立つ関連情報や代替アプローチを積極的に提案してください。

継続的改善:
- タスクを完了した後、ユーザーに説明が必要かどうか、フォローアップの質問があるかどうかを尋ねてください。
- フィードバックに耳を傾け、それに応じてアプローチを調整してください。

あなたの目標は、コード実行、画像生成、データ分析の独自の機能を活用して、正確で役立つ洞察に富む支援を提供することです。ユーザーの要求に対して、最も実用的で効果的な解決策を提供するよう常に努めてください。`,
    });

    const codeInterpreterAgentAlias = new CfnAgentAlias(
      this,
      'CodeInterpreterAgentAlias',
      {
        agentId: codeInterpreterAgent.attrAgentId,
        agentAliasName: 'v1',
      }
    );

    this.agents.push({
      displayName: 'CodeInterpreter',
      agentId: codeInterpreterAgent.attrAgentId,
      aliasId: codeInterpreterAgentAlias.attrAgentAliasId,
    });
```

この中では、以下の Bedrock エージェントを生成しています。

- Bedrock エージェント

  - 名前は `CodeInterpreterAgent-ABCD1234`
  - アクションとして [`AMAZON.CodeInterpreter`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-bedrock-agent-agentactiongroup.html#cfn-bedrock-agent-agentactiongroup-parentactiongroupsignature) を指定する
  - ロールは前述の `BedrockAgentRole` ロールを設定する
  - ユーザーとの会話情報を保持する期間は 1 時間
  - 基盤モデルは `anthropic.claude-3-sonnet-20240229-v1:0` を指定
    ※デフォルトでは Web 検索用 Becrock エージェントより推論性能が優れたモデルが指定されています。
  - エージェントへの指示に以下を指定
    **あなたは、コード実行、チャート生成、複雑なデータ分析の機能を持つ高度な AI エージェントです。あなたの主な機能は、これらの機能を活用して問題を解決し、ユーザーの要求を満たすことです。あなたの主な特性と指示は次のとおりです。**

    ～中略～

    **あなたの目標は、コード実行、画像生成、データ分析の独自の機能を活用して、正確で役立つ洞察に富む支援を提供することです。ユーザーの要求に対して、最も実用的で効果的な解決策を提供するよう常に努めてください。**

- 作成した Bedrock エージェントのエイリアスに `v1` を指定
- `CodeInterpreter` という表示名でエージェントに登録

以上です。
次回は GenU 内の `GuardrailStack` スタックを解説していきたいと思います。

## (参考) GenU のバックエンド (CDK) 詳細解説投稿一覧

- [①AWS CDK のセットアップ](https://qiita.com/siruko/items/fd25fdcf89615cb85262)
- [②AWS CDK の動作確認](https://qiita.com/siruko/items/73169f986b4173e3d3a5)
- [③GenU の概要](https://qiita.com/siruko/items/625801b9e1847b305c1e)
- [④GenU CDK スタックの概要](https://qiita.com/siruko/items/8570ed43f7162ea4b907)
- [⑤CloudFrontWafStack スタックの解説](https://qiita.com/siruko/items/30439576ee7c63165d21)
- [⑥RagKnowledgeBaseStack スタックの解説](https://qiita.com/siruko/items/1223c9d22e73168a8809)
- [⑦WebSearchAgentStack スタックの解説](https://qiita.com/siruko/items/aef0a9599df60d47eb1e)
- [⑧GuardrailStack スタックの解説](https://qiita.com/siruko/items/d16e9fe27df4673d7554)
- [⑨GenerativeAiUseCasesStack > Auth スタックの解説](https://qiita.com/siruko/items/0c14040a1af132f42382)
- [⑩GenerativeAiUseCasesStack > Database, Api スタックの解説](https://qiita.com/siruko/items/5f249f11847f3829c18a)
- [⑪GenerativeAiUseCasesStack > CommonWebAcl, Web, Rag スタックの解説](https://qiita.com/siruko/items/1063daa2efe0b374a3fc)
- [⑫GenerativeAiUseCasesStack > RagKnowledgeBase, UseCaseBuilder, Transcribe スタックの解説](https://qiita.com/siruko/items/73d6006004caf8648594)
- [⑬DashBoard スタックの解説](https://qiita.com/siruko/items/73a75c7d0146d12ecbc3)
- [⑭GenU の Outputs の解説](https://qiita.com/siruko/items/afc14128a5b1a15ab69c)