AWSのChaliceを使うと、Agents for Amazon Bedrockが劇的に簡単に＆構築が爆速になる話

# この記事を1行で

AWSのChaliceで、日本語のコメントを実行トリガーにした関数を簡単に書けるようになりました

![Firefly サイバーな世界に置かれた聖杯 7837.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/f8ec7719-24a8-765f-aa18-d4753fe5ca11.jpeg)


※Agents for Amazon Bedrockの名前が長いので、この記事では`Agents for AB`、略称で`A4AB`と書きます。

# この記事を3行で

- 2023年11月の末に、`Agents for AB`（自然言語で出入力できるサーバーレスなAPIを作れるサービス）が発表された
- Chaliceには`Agents for AB`に必要なリソースを自動出力する機能がある
- 今のChaliceを使うと、爆速で自然言語で対話するAPIを作ることができる

# 必要な開発環境

- Python(Lambdaが動く環境ならOKです。3.10で確認しています。)
- AWSアカウント
    - `Agents for AB`は記事執筆時点で東京リージョンに対応していないため、あらかじめバージニア（`us-east-1`）リージョンのAWSプロファイルを作っておきます
    - バージニアリージョンのBedrockのClaude v2を有効にしておきます。  
    手順は末尾の付録に書きます

# ラップタイムと動きの説明

実装から動作確認までの流れを、実際の実行にかかる時間を計りながら説明します。

**<font color="#065299">1. まず必要なライブラリをインストールします。</font>**

```shell:ターミナル、またはコマンドプロンプトで実行
pip install chalice chalice-spec==0.7.0 chalice-a4ab boto3 pydantic
```

**<font color="#065299">2. プロジェクトを作ります。</font>**
サンプルです。プロジェクト名はフリーレンにしました。

```shell:ターミナル、またはコマンドプロンプトで実行
chalice new-project frieren
```

※Measure-Commandで時間を計りながら進めます。
プロジェクトの作成にかかった時間は2.68秒です。

![new-project-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/dd4a909c-1d89-789d-9a22-a827456dd8e7.png)

**<font color="#065299">3. new-projectでapp.pyが作られているので、編集します</font>**
テンプレートを元に、アプリを更新します。
コメントの日本語がそのまま実行条件になるので、コメントが重要です

```python:app.py
from chalice_a4ab import Chalice, AgentsForAmazonBedrockConfig
from chalice_spec.docs import Docs, Operation
from pydantic import BaseModel


AgentsForAmazonBedrockConfig(
    instructions="あなたはフリーレンと呼ばれている女性で、発言者と会話をしています。ソースから受け取ったmessageはそのまま返してください",
).apply()

# AppNameはプロジェクト名と合わせてください
app = Chalice(app_name="frieren")


class TalkInput(BaseModel):
    """
    生きてきた年数（年齢）を受け取ります

    年数を受け取ります
    """

    age: int


class TalkResponse(BaseModel):
    """
    応答を返します

    応答を返します
    """

    message: str


@app.route(
    "/talk",
    methods=["POST"],
    docs=Docs(
        post=Operation(
            request=TalkInput,
            response=TalkResponse,
        )
    ),
)
def talk_with_aura():
    """
    フリーレンがアウラに言い返します

    私は～年生きた大魔族だ、という相手の発言を受け取って、数値を倍にして応答します
    """
    input = TalkInput.model_validate(app.current_request.json_body)

    # 200: OK
    return TalkResponse(
        message=f"アウラ、お前の前にいるのは、{input.age * 2}年以上生きた魔法使いだ"
    ).model_dump_json()
```

同じディレクトリにある依存ライブラリの設定も編集します。

```requirements.txt
chalice-spec==0.7.0
chalice-a4ab
pydantic
```

ソースコードのコメントは3行以上で書きます。

```python
"""
フリーレンがアウラに言い返します # 1行目は処理の概要

私は～年生きた大魔族だ、という相手の発言を受け取って、数値を倍にして応答します # 実行条件
"""
```

1行目は処理の概要、3行目以降が実行条件として反映されます。

**<font color="#065299">4. デプロイします</font>**

--profileにはAWSのプロファイルを指定します。
プロファイルのリージョンはus-east-1にします。

:::note warn
Chaliceはデプロイ時のオプションで--regionを指定できないため、プロファイルの設定でデプロイ先のリージョンを指定する必要があります
:::

```shell:ターミナル、またはコマンドプロンプトで実行
# ソースコードをクラウドにデプロイします
chalice deploy --profile ${自分のプロファイル}

# Agents for ABを作成して、チャット経由でアプリを呼べるようにします
chalice-a4ab init --profile ${自分のプロファイル} --region us-east-1
```

Chaliceのアプリのデプロイにかかった時間は31.93秒です。

![deploy-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/0100ac38-881e-f6e0-573a-1d493b236bb4.png)

`Agents for AB`の作成にかかった時間は79.33秒です。

![sync-time-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/c6c21913-0789-da29-62a1-f2214eea95a7.png)

必要な作業はこれだけです。もう動きます。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/ed4f4c18-ac06-9b14-1919-214acb3da406.png)

トータルで113秒。2分未満ですから、カップラーメンが茹で上がるよりも早く完成しました。

**<font color="#065299">5. 日本語のコメントを元に、アプリが動きます</font>**

バージニアリージョンのBedrock -> Agentを開くと、テスト用のチャットがあります。
そこでデプロイしたAPIと会話することができます。

![frieren-aura.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/e1fc4ed5-8541-eb7b-2351-3942c57325ed.png)

入力が実行条件をクリアして、関数が呼ばれます。
流れは以下の通りです。

> **<font color="#037f0c">ユーザーの入力：</font>**
> …ふざけるな、私は五百年以上生きた大魔族だ

$$▼$$

> **<font color="#037f0c">実行条件：ソースコードのコメント</font>**
> 入力に合う実行条件を探して、実行する関数を選びます
> 「私は～年生きた大魔族だ、という相手の発言を受け取って、数値を倍にして応答します」

$$▼$$

> **<font color="#037f0c">パラメータの抽出と変換：</font>**
> 五百→`500`に変換する

$$▼$$

> **<font color="#037f0c">関数の実行：AWS Lambda</font>**
> パラメータを受け取ってPythonのソースコードを実行、以下の文字列を返す
> 「アウラ、お前の前にいるのは、{input.age * 2}年以上生きた魔法使いだ」

$$▼$$

> **<font color="#037f0c">チャットの出力：</font>**
> アウラ、お前の前にいるのは、1000年以上生きた魔法使いだ

入力が漢数字でもアラビア数字でも、型を数値として認識します。
Pythonで意識して型変換をする必要はありません。

# 作成後の更新と料金

**<font color="#065299">ソースコードを更新するときは？</font>**

syncコマンドを実行すると、ソースコードの更新が数秒で反映されます。

```shell:ターミナル、またはコマンドプロンプトで実行
# ソースコードをクラウドに反映します
chalice deploy --profile ${自分のプロファイル}

# Agents for ABのプロンプトを更新します
chalice-a4ab sync --profile ${自分のプロファイル} --region us-east-1
```

**<font color="#065299">使わなくなったアプリを削除するときは？</font>**

deleteコマンドを実行すると、作成したアプリは消えます

```shell:ターミナル、またはコマンドプロンプトで実行
# デプロイしたChaliceアプリを削除します
chalice delete --profile ${自分のプロファイル}

# Agents for ABのアプリを削除します
chalice-a4ab delete --profile ${自分のプロファイル} --region us-east-1
```

**<font color="#065299">料金はどのくらい？</font>**

22回会話をして遊ぶと、1ドルちょっとかかりました。それなりの料金です。

![costs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/7eee9305-ec0b-69e4-50f3-ef26b692ae63.png)

サーバーレスですから、使ったぶんだけ料金が請求されます。Lambdaほど安くはないのですが、EC2や自前のサーバーを借りて月額料金を払うことに比べれば安価です。

`Claude Instant`にすれば料金を下げることもできます。

## あらためて何がすごいの？

- **ソースコードがそのまま自然言語で使えるAPIになります**
    - よくあるFastAPI風のソースコードに、日本語のコメントを加えるだけで作れます
    - プロンプトはAWSが用意するため、こちらはシチュエーションと条件を渡すだけです
    - デプロイ、反映はコマンド2つだけで実行できます
- **反映が早く、すぐに使うことができます**
    - 簡単なAPIのデプロイにかかる時間は2分未満です
    - 修正後の反映は数秒です
- **完全なサーバーレスです**
    - 使っていないときの料金がかかりません
    - Pythonがあれば開発できます。Dockerのような開発環境も不要です

# Chaliceって何？

Chaliceは2016年からある古参のサービスです。AWSが開発しています。
CDKやSAMの仲間で、ソースコードからWebアプリのバックエンドを作ることができます。

https://github.com/aws/chalice

**実装が簡単**で、**とにかく早い**ことが特徴です。
特に開発で権限管理を触る必要がないため、AWSの知識がなくてもサーバーレスの開発を始めることができます。

- サーバーレスなAPIを、CloudFormationやCDKなしで作ることができる
- ソースコードを解析して、必要なIAMの権限を自動で設定してくれる
- デプロイ、再デプロイが圧倒的に早く、数秒～数分で終わる
- 環境構築が簡単

方向性としても、**非プログラマでも、誰でも自動化を進めることができる**生成AIと相性の良い開発環境です。ただ、複数人の開発に向いていないこと、AWSで作業をするたいていの人にとって簡単さがメリットになりにくいこともあって、ややニッチなツールではありました。

考え方や実装はFastAPIに似ていて、ソースコードからAPIやドキュメントを作ることができます。ソースコードのコメントからOpenAPIのスキーマファイルを出力するプラグインもありますから、`Agents for AB`に必要なリソースを全て揃えることができます。

https://github.com/TestBoxLab/chalice-spec

ただ、まだ`Agents for AB`が新しいサービスということもあって、今日時点で公式には`Agents for AB`に対応していませんでした。Chaliceを動かせるように対応するプラグインを作って、PIPでインストールできるようにして公開しました。

https://github.com/ShotaOki/ChaliceA4AB

現時点で、ソースコードから`Agents for AB`へのデプロイまで、数行のコマンドだけでできるようになりました。

![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/9f913363-4c66-76dd-4f89-57b1395aac4f.png)

生成AIの登場でプログラミングのすそ野が広がったことで、簡単に使えるChaliceの良さが強く出るようになりました。ついにChaliceに時代が追いつきました。

# 他のユースケースも考えてみる

### 依存ライブラリのファイルを解析させる

pom.xmlのDependenciesを分析させてみます。Javaの依存ライブラリのファイルです。

受け取る入力クラスはこのように定義します

```python:app.py
class TalkInput(BaseModel):
    """
    依存情報を受け取ります

    POMのdependenciesから、グループIDとアーティファクトIDを受け取ります。
    """

    groupId: str
    artifactId: str
```

関数はこのように定義しました

```python:app.py
@app.route(
    "/search",
    methods=["POST"],
    docs=Docs(
        post=Operation(
            request=TalkInput,
            response=TalkResponse,
        )
    ),
)
def search_proc():
    """
    パッケージを分析します

    XML形式のPOMから、dependenciesを分析します。
    """
    print(app.current_request.json_body)
    
    # ここにデータが来ます
    # input = TalkInput.model_validate(app.current_request.json_body)

    # 200: OK
    return TalkResponse(message="障害報告はありません").model_dump_json()
```

入力データはPOMのXMLの一部を切り出したものです。

```xml: 入力データ
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jdbc</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jdbc</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-thymeleaf</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>
```

実行してみます。ライブラリを分析した結果を答えてくれました。

![pom-execute.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/36a3e89e-c36b-637d-0812-faed6fe0992b.png)

Lambdaの実行履歴を見ると、4回実行されています。XMLをうまく分析して、dependencyの数だけLambdaを呼び出していることが分かります。

![lambda-log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/d09ecceb-28ab-6fab-8fae-8a23dcbbd2a3.png)

ソースコードのコミットで動くようにして、パッケージのライセンスやバージョンを調べる処理を入れると便利になりそうです。

商用不可のOSSライブラリがプロジェクトに入れば牽制を投げる、もっと新しいバージョンがあれば通知する、みたいなことができるようになります。

# まとめ

日本語で条件を書くだけで、以下のようなことができるようになります。

- 自由入力されたテキストから目的のデータを抜き出す
    - 例：「あしたの午前9時におとな三名でチェックインしたいんです」から、時間と人数を抜き出す
- 形式の決まったテキストから、目的のデータを抜き出す
    - 例：XMLから目的の値を取り出す

`Agents for AB`を使うと、データを整形する仕組みを自然言語で作れるようになります。AIでやる作業を自動化しよう、全部AIじゃなくて一部分だけソースコードを書こう、の時に使える強力なツールだと思います。

# ちょっと気を付けること

Chaliceは、デフォルトのままではAPIGatewayがセキュリティなしで公開されています。
APIGatewayを通して呼ばないのなら、アクセス拒否を設定して呼べないようにしておくほうが良いです。

`.chalice`のディレクトリの中に、以下のようなファイルを作ります。

```json: resource-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*/*/*"
    }
  ]
}
```

`.chalice/config.json`を編集して、リソースポリシーがAPIGatewayに反映されるようにします

```json: config.json
{
  "version": "2.0",
  "app_name": "アプリ名",
  "stages": {
    "dev": {
      "api_gateway_stage": "api",
      "api_gateway_policy_file": "resource-policy.json" // <<< これを追加
    }
  }
}
```

これでAPIGateway経由ではLambdaを呼べなくなります。チャットからの呼び出しはAPIGatewayを通さず、直接Lambdaが呼ばれています。

# 付録：Agents for Amazon Bedrock でCloudeを使い始める

初めてBedrockを使うときは、Bedrockのモデルの有効化が必要です。

AWSのマネジメントコンソールを開いて、Bedrockの`Model Access`を開きます。

Anthropicの欄が`Available to request`になっていれば、右上の`Manage model access`を開いて、編集画面に移動してください。

![enable-claude.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/99f0105d-e225-fbe7-6f00-c7c6df8683d7.png)

`Use case detail submitted`で、会社名、自身のホームページ、利用目的を答えるように言われますが、会社は個人名そのままでも大丈夫でした。ホームページもgithubのページで通りました。

![manage-.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/1481f33d-6d44-468e-8705-5ad46e6309e2.png)

利用目的に`Claude`の単語を入れると、「個人情報が入った説明文はダメだよ」みたいなエラーになります。今時点ではClaude＝男性名で個人情報扱いだそうです。使おうとしているサービス名に触れないように注意しながら、ふわっとした説明文を書きます。

ほんの数分ほど待つと有効化されると思います。