Bedrockフローを使ってアプリを作ったのでハマりポイントを備忘録メモ

GPT Researcherを模したアプリをBedrockフローで作成しました。

その名も **Nova Researcher**

https://github.com/moritalous/nova-researcher

Bedrockフローを使ってみて、色々ハマりポイントがあったので備忘録として残します。

:::note
GPT Researcherについては過去に投稿した記事を参考にしてください。

1年遅れぐらいでGPT Researcherやってみたらもう最高！（マルチエージェント版もあるよ）
https://qiita.com/moritalous/items/12874f21bf543744c5db
:::

## 構築したBedrockフローの紹介

このようなフローを作成しました。

![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/459ac67f-6428-f783-6e2b-c41ec537724b.png)

大きな流れとしてはこのようになります。

1. ユーザー入力からTavily APIを使ってWeb検索を行う
1. ユーザー入力をWeb検索結果とコンテキストとして与え、サブクエリーを4つ生成する
1. それぞれのサブクエリーに対してWeb検索を行い、上位5件についてスクレイピングを行う
1. サブクエリーごとにスクレイピング結果のサマリーを生成する
1. ユーザー入力と4つのサマリーを使いレポートを生成する


## 備忘録

### 前のフローの出力の一部を取得する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/5ae90f1f-53b3-c83b-0406-022eea0af7a2.png)

pre_searchのOutputはこのようなJSONで、

```json:Output
{
  "result": [...]
}
```

`result`の部分を切り出して`Array`で受け取りたい場合。

Input側の設定で式に「$.data.result」と指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/711f2d23-8d3b-8f4d-21b6-7d7bd21935b3.png)

## 型変換はできない

生成AIのレスポンスは文字列です。
今回のケースでは、「サブクエリーを配列で4つ生成して」というステップがあり、出力が

```
["サブクエリー１", "サブクエリー２", "サブクエリー３", "サブクエリー４"]
```

という文字列で出力されます。

この文字列を後続処理でArray型として扱いたかったのですが、残念ながら型変換の方法は提供されていないようでした。

そのため、Lambdaを間にはさみ、このような処理を実装しました。

```python
import json

def lambda_handler(event, context):
    value = event["node"]["inputs"][0]["value"]
    return json.loads(value)
```

## イテレーターノードはフローの中に1つまで

イテレーターノードはフローの中に1つまでしか登録できません。「入れ子の階層が1階層まで」ではなく、フローの中に1つしか配置できません。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/1fa83f86-1ef9-808c-7d11-055a5fa1a832.png)

私が作ったフローは、当初３つの入れ子で繰り返し処理をしたかったのですが、残念ながらできませんでした。どうしてもイテレーションにしないとエラーになる部分を厳選して、採用することにしました。

## イテレーションの反復数は50が上限

イテレーションのノードの反復数が50を超えると実行時エラーになります。

Webページの検索結果をBedrockでそれぞれを要約させるような処理を行うと、検索結果が50を超えるとエラーになります。

## コネクションの数は20まで

BedrockフローはCloudFormationにすでに対応しています。

フローの定義は[AWS::Bedrock::Flow FlowDefinition](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-bedrock-flow-flowdefinition.html)に沿って行うのですが、コネクション（ノード間の接続）の最大数が20なので、複雑なフローは組めません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/0522ae6c-a6df-2143-69df-2f07c902130d.png)


:::note warn
ノード間の接続数なので、ノードが少なくても、変数が多いとすぐに上限に引っかかると思います
:::

マネジメントコンソールで作成した場合は20以上の構成も作成できたので、そのうち解消するかもしれません。

## invoke_flow APIが使いづらい

作成したフローを呼び出すには`bedrock-agent-runtime`の`invoke_flow`を呼び出します。

```python
response = client.invoke_flow(
    flowIdentifier=flow_identifier,
    flowAliasIdentifier="TSTALIASID",
    inputs=[
        {
            "content": {
                "document": {
                    "task": task,
                    "now": datetime.datetime.now().strftime(
                        "%Y/%m/%d %H:%M:%S"
                    ),
                }
            },
            "nodeName": "FlowInputNode",
            "nodeOutputName": "document",
        },
    ],
)
```

レスポンスは`EventStream`型です。

```python
for stream in response["responseStream"]:
    if "flowOutputEvent" in stream:
        print(stream["flowOutputEvent"]["content"]["document"])
```

ストリーム型なのですがフローの最後まで実施してからまとめて応答がありますので、以下のような問題があります。

* フローの途中経過を知るすべがない（マネジメントコンソールではトレースが見られますが、APIでは取得できない）
* API呼び出しのタイムアウトを気にする必要がある

ちなみにBoto3のタイムアウトを伸ばすにはこのように設定します

```python
from botocore.config import Config

config = Config(read_timeout=600)
client = boto3.client("bedrock-agent-runtime", config=config)
```

:::note warn
また、当たり前かもしれませんが、途中で処理がコケるとエラーになります。「結構待ったのにエラーかよ！」ってことがよくあります。
エラーの内容も「Bedrock RuntimeがInput不正でエラーです」的なことしか出ないので、どうエラーなのかわからずデバッグが大変です
:::

## Bedrockプロンプト管理ではツールが定義できるがBedrockフローからは使えない

Bedrockプロンプト管理は、プロンプトをBedrockに登録し、再利用しやすくする機能です。ツール利用（Tool Use）の定義も可能です。

Bedrockフローは、生成AI呼び出し処理をBedrockプロンプト管理で定義したプロンプトを指定する方法がありますが、テキスト生成結果しか取得できず、ツール利用を指定した際の結果を取得することができません。JSONなどのレスポンスの型を整えるためにツールを使いたくても使えません。

## マネジメントコンソールで作成してからのIaCがおすすめ

マネジメントコンソールで作成したフローの定義は、`aws bedrcok-agent get-flow`で取得できます。JSONで定義が取得できるので、そのままCloudFormationテンプレート化（CDKのL1コンストラクト化）はすんなりできると思います。

一度作成したフローの更新も、マネジメントコンソールで行って差分反映が効率的と思います。

## （フロー関係ないですが）Bedrockのアプリケーション推論プロファイルを使う際の権限はハマる

CDK化の際に、「USリージョンのクロスリージョン推論」をソースとした「アプリケーション推論プロファイル」を作成しました。

せっかくなので最小権限の原則に挑戦したのですが、

* アプリケーション推論プロファイルの呼び出し権限だけだと、クロスリージョン推論を呼び出す権限がなく、実行エラー
* クロスリージョン推論の呼び出し権限を追加しても、基盤モデル（amazon.nova-pro-v1:0）を呼び出す権限がなく、実行エラー

となります。そのため、結局以下の呼び出し権限がすべて必要です。

* アプリケーション推論プロファイルの実行権限
* クロスリージョン推論プロファイルの実行権限
* 基盤モデルの実行権限（バージニア北部、オレゴン、オハイオ）

基盤モデルを直接呼び出す権限を付与してしまうので、「このIAMロールはこのアプリケーション推論プロファイルからの呼び出しを強制する」が、結局のところできません。


## まとめ

いろいろハマりポイントはありましたが、作成したいアプリは簡単に作れたので良しとしましょう！