OpenHandsを使用したAWS CDKプロトタイプ開発の記録

# はじめに
この記事では、OpenHandsを使用してAWS CDKプロジェクトを作成した際の試行錯誤と、その過程で得られた知見をまとめます。
OpenHandsの環境準備は[こちら](https://qiita.com/kei1-dev/items/20e20eee220e544a8323)を参照してください。

# プロジェクトの概要
手動で作成されたAWSリソースをCDK化するため、
基本的にはOpenHandsに依頼しながら、TypeScriptベースのAWS CDKプロジェクトを作成し、以下の要素を実装しました：
- IAMロール
- セキュリティグループ
- Lambda LayerとLambda Function
- REST APIのためのAPI Gateway
- Auroraデータベース
- Cognitoユーザープールとクライアント

※ OpenHandsで使用したLLMはOpenAIのGPT-4oです

# 開発の流れ

## 1. 初期セットアップ
- AWS CDKをグローバルにインストール
- TypeScriptプロジェクトの初期化
- IAMロール、セキュリティグループの追加


## 2. プロジェクト構造の整備
フォルダ構成を以下のように整理：
```
aws-cdk-proto/
├── cdk-project/
│   ├── bin/
│   │   └── cdk-project.ts
│   ├── lib/
│   │   ├── constructs/   # 自作コンストラクト
│   │   ├── assets/      # Lambda Layer等
│   │   └── cdk-project-stack.ts
│   ├── test/
│   └── env/            # 環境設定
├── docs/               # ドキュメント
└── agentlogs/          # 作業ログ
```

## 3. 環境設定の改善
- 環境別の設定ファイル（dev.ts）を作成
- Contextを使用した環境設定の切り替え機能を実装
```typescript
// lib/env/IConfig.ts
interface Config {
    stackConfig: {
        environment: string;
        region: string;
        account: string;
    };
    vpcImported: {
        vpcId: string;
        subnetIds: string[];
    };
    lambdaEnvironment: { [key: string]: string };
    cognito: {
        userPoolName: string;
        userPoolClientName: string;
        callbackUrls: string[];
    };
}

export { Config };

// lib/env/dev.ts
import { Config } from './IConfig';

export const config: Config = {
  stackConfig: {
    environment: 'dev',
    region: 'ap-northeast-1',
    account: '987654321098'
  },
  vpcImported: {
    vpcId: 'vpc-1a2b3c4d',
    subnetIds: ['subnet-1a2b3c4d', 'subnet-4e5f6g7h', 'subnet-7h8i9j0k']
  },
  lambdaEnvironment: {
    CLIENT_ID: '9m8n7b6v5c4x3z2w1',
    CLIENT_SECRET: '1a2b3c4d5e6f7g8h9i0j',
    DB_HOST: 'exampleinstance.c7ituocb1qke.us-west-2.rds.amazonaws.com',
    DB_NAME: 'sample_database',
    DB_PASSWORD: 'A1s2d3f4g5h6j7k8l9',
    DB_USER: 'sample_user',
    LD_LIBRARY_PATH: '/opt/example_lib',
    REDIRECT_URI: 'https://exampleapi.us-west-2.amazonaws.com/staging/authCode',
    TOKEN_URL: 'https://us-west-2example.auth.us-west-2.amazoncognito.com/oauth2/token',
  },
  cognito: {
    userPoolName: 'example-userpool',
    userPoolClientName: 'example-addon',
    callbackUrls: [
      'https://exampleapi.us-west-2.amazonaws.com/staging/authCode',
      'https://us-west-2example.auth.us-west-2.amazoncognito.com/oauth2/idpresponse',
    ],
  }
};

// bin/cdk-project.ts
import * as cdk from 'aws-cdk-lib';
import { CdkProjectStack } from '../lib/cdk-project-stack';
import { config as devConfig } from '../env/dev';
import { config as testConfig } from '../env/test';

const app = new cdk.App();

// 環境名の取得
const environment = app.node.tryGetContext('environment');
if (!environment) {
  throw new Error('Please specify environment with -c environment=dev or -c environment=test');
}
// 環境の読み替え
const config = environment === 'dev' ? devConfig : null;

if (!config) {
  throw new Error('Invalid environment');
}

// スタックの作成
new CdkProjectStack(app, 'CdkProjectStack', {
  env: { account: config.stackConfig.account, region: config.stackConfig.region },
}, config);

```
デプロイは以下のコマンドで行う：
```bash
cdk deploy -c environment=dev
```

## 4. Lambda Layer実装
- PandasLayerの作成：pandasライブラリを含むLayer
- BaseLayerの作成：ベースライブラリを含むLayer

## 5. Lambda Function実装
- ハンドラー：`lambda_function.lambda_handler`の設定
- 2つのLambda Layer（PandasLayerとBaseLayer）との統合
- 環境変数の`env`ファイルによる管理

## 6. API Gateway設定
- REST API エンドポイントの作成
- Lambda FunctionとのIntegration設定

## 7. Aurora設定
- Auroraクラスターの作成
- パラメータグループとサブネットグループの設定

## 8. Cognito統合
- CfnUserPoolとCfnUserPoolClientの実装

# 使った感想
- OpenHandsのセッションが生きている間は最低限のコンテキスト把握をしているため、なるべく切らずに同じセッションを使い続けた方が良い
  - ブラウザベースのため、ブラウザ切るとセッションが切れるため、少し苦労した
- セッションが切れた際にもなるべくコンテキストを把握させるため、作業の区切りでドキュメントを更新するように指示をした
- また、コミットのたびに作業ログを出力させ、ユーザからの指示内容、作業ログ、実行コマンドを残した
  - セッション切れたタイミングでは、ドキュメントやフォルダ構造を確認、把握した内容を出力するように指示すると一定程度効果はあった
  - Devin AIだとKnowledgeレイヤーあるとどこかで読んだので、こんな事しなくていいのかもしれない
- 依頼ベースでのフォルダ構造の変更(Constructsの分離や環境設定の分離)は難しく、ユーザが作業、仮ファイルを作成後、指示をすると適切に修正してくれるようになった
  - 他エージェントでもフォルダ構造の変更はあまりうまくいかず、ユーザがガイドを示してあげたほうが効率良さそう
- Lambda Layerの実装以降は適切なパラメータさえ渡していれば、あまり指示をしなくてもうまく作成してくれるようになった
- 各作業の最初に以下のようなフォーマットで指示出ししていたが、もっといい方法があるかもしれない
  - Claude 3.5 Sonnetにいいフォーマットがあるか聞いたら、具体的なコマンドやテンプレートで指示しろって言われた(それが分かってりゃわざわざ依頼しないのよ)
  ```
   # 既存AWS環境のCDK化
   ## フォルダ
   /workspace/xxx

   ## タスク
   - 既に構築されたAWS環境をCDK化してください
   - 大まかな構成はAPI GW + Cognito + Lambda(Lambda Layer) + Auroraです。
   - VPCやサブネットについてはCDK化しないため、パラメータとして受け取れるようにしてください
      - VPC ID: vpc-xxx

   - まずIAMロールとセキュリティグループから作ります。
   ### IAMロール
   - 名前: xxx
   - ポリシー(全て管理ポリシー):
      - AWSLambdaVPCAccessExecutionRole
      - AWSLambdaBasicExecutionRole
   - 信頼関係: "lambda.amazonaws.com"

   ### セキュリティグループ
   - 名前: xxx
   - インバウンドルール:
      - TCP 3306 0.0.0.0/0
   - アウトバウンドルール:
      - すべて すべて 0.0.0.0/0

   ## 実行ルール
   - docs/rules.mdを参照して、記載のルールを準拠してください。
   - わからないことはインターネットで検索して対応してください
   - 決定が必要な内容や作成にあたって情報が足りない場合については、選択肢を示して判断を求めてください
   ```

# まとめ

OpenHandsを使用したAWS CDKプロトタイプ開発を通じて、以下のような知見が得られました：
1. セッション管理の重要性
   - コンテキスト維持のため、可能な限り同一セッションの継続が効果的
   - セッション切断時の対策として、定期的なドキュメント更新が有効

2. 作業ログの重要性
   - コミット時の作業ログ出力により、指示内容と実行コマンドの追跡が容易に
   - セッション再開時のコンテキスト復元に有効

3. フォルダ構造の最適化
   - AIエージェントによる構造変更には限界あり
   - ユーザーによる初期ガイドが効率的

# 参考
- [OpenHands Github](https://github.com/All-Hands-AI/OpenHands)
- [OpenHandsの環境準備](https://qiita.com/kei1-dev/items/20e20eee220e544a8323)