Bedrock Flows導入で痛感した、ワークフロー自動化の地味だけど重い落とし穴

Bedrock Flowsを実務のカスタマーサポート自動化に導入。ノーコードの理想と現実のギャップ、デバッグで困ったこと、本番運用のコツを赤裸々にシェアします。

Bedrock Flows との出会いは、正直期待と現実のギャップから始まった

先月、うちのチームで大量のカスタマーサポートチケット分類業務を自動化する案件が走った。従来だとLambda × LLM API呼び出しの複雑なロジックを書いていたんだけど、今回はBedrock Flows を導入してみることにした。

最初は「ノーコード・ローコードでワークフロー組めるなら楽だろう」くらいの気楽な気持ちだったんですよ。でも実装してみたら、思ったより複雑で、逆に手書きコードのほうがマシな場面もあった。ただ、工夫次第では劇的に開発効率が上がる という結論に至った。実際の経験を共有しておきたい。

Bedrock Flows の基本:何ができて、何ができないのか

Bedrock Flows は AWS が 2025 年から本格化させた、生成AI ワークフローをビジュアルに定義・管理するサービスなんですよ。2026 年時点では、ChatGPT の GPTs や Google の Vertex AI Agents とも競合している領域。

できることはこんな感じです:

  • 複数の LLM(Claude、Titan、その他サードパーティモデル)をシーケンシャルに実行
  • ユーザー入力・APIレスポンスに応じた条件分岐
  • 外部 API(HTTP、AWS Lambda)との連携
  • ストレージ(S3、DynamoDB)へのデータ読み書き
  • プロンプト管理と A/B テスト機能

一方、できないこと(またはやるべきではないこと):

  • 複雑なビジネスロジック(金融計算、複雑な状態管理)
  • リアルタイム性が求められるユースケース(応答時間が重要)
  • セキュアで細かいアクセス制御
  • オンプレミス環境での実行

実装当初、僕たちはチケット分類 → キーワード抽出 → データベース登録 → メール通知、という一連のフローを完全に Bedrock Flows で組もうとしたんですよ。ところが データベース側の複雑なトランザクション制御 が必要になったとき、Flows だけでは実現不可能だと気づいた。結局、Lambda 関数を中間に挟む構成に変更することにしたんです。

ユーザー入力

[Claude 3.5 Sonnet でテキスト分類]

[条件分岐:信頼度 > 0.8?]
   ├→ YES: [Lambda → DynamoDB トランザクション実行]
   └→ NO: [Claude で詳細確認]

[メール送信 API]

実装してみてわかったのは、Bedrock Flows は「最初の数ステップ」と「最後の処理」は Flows で、複雑な中間ロジックは外部サービス(Lambda など)に委譲するハイブリッド構成が最適 ということ。正直これが分かるまでに結構試行錯誤しましたね。

AWS構成図:本番環境での推奨アーキテクチャ

flowchart TB
    subgraph VPC["VPC (us-east-1)"]
        subgraph PrivateSubnet["Private Subnet"]
            Lambda["Lambda<br/>(トランザクション処理)"]
            DDB[("DynamoDB<br/>(チケット管理)")]
        end
        subgraph PublicSubnet["Public Subnet"]
            APIGW["API Gateway<br/>(REST)"] 
        end
    end
    
    subgraph AWSServices["AWS Services"]
        BedrockFlows["🤖 Bedrock Flows<br/>(ワークフロー)"]
        Bedrock["Bedrock LLM<br/>(Claude 3.5S)"]
        S3["S3<br/>(ログ/監査)"]
        CloudWatch["CloudWatch<br/>(監視)"]
        SQS["SQS<br/>(バッチ処理キュー)"]
    end
    
    External["外部サービス<br/>(メール/Slack)"]
    
    APIGW -->|チケット投稿| BedrockFlows
    BedrockFlows -->|LLM実行| Bedrock
    BedrockFlows -->|Lambda呼び出し| Lambda
    Lambda <-->|読み書き| DDB
    BedrockFlows -->|キュー送信| SQS
    BedrockFlows -->|ログ記録| S3
    BedrockFlows -->|メトリクス| CloudWatch
    Lambda -->|非同期通知| External
    
    style BedrockFlows fill:#FF9900,stroke:#232F3E,stroke-width:2px,color:#fff
    style Bedrock fill:#FF9900,stroke:#232F3E,stroke-width:2px,color:#fff
    style VPC fill:#E8F4F8,stroke:#146EB4
    style AWSServices fill:#FFF4E6,stroke:#FF9900

この構成ではそれぞれが以下の役割を担ってます:

  • Bedrock Flows がマスターオーケストレータとして、入力から出力までのワークフロー全体を管理
  • Lambda は複雑なビジネスロジック(DB トランザクション、複数テーブルの同期更新)を担当
  • SQS でバッチ処理を非同期化(大量チケットの一括処理時に有効)
  • S3 × CloudWatch で監視・監査ログを集約

運用してみて気づいたのは、この構成だと Flows のビジュアル画面だけで全体フローが把握でき、かつ複雑ロジックは Lambda テストで単体検証できる というのが地味に便利だってことなんですよ。デバッグ時間が確実に短くなりました。

実装してハマった 3 つのポイント

1. トークン制限:入力が大きいと予想外のエラーになる

Bedrock Flows を使う際、各ステップで「前のステップの出力がすべて次のステップに渡される」という仕様になっているんです。これ自体は悪くないんだけど、チケット本文が長いときに致命的だった。

例えば、長いカスタマー説明文(500 行以上)をそのまま Claude に投げると、1 回目のステップで 10 万トークン消費して、2 回目のステップでは入力トークン上限超過で失敗する。実際にこれで何度か本番環境でエラーが出ました。

対策として、Flows 内で中間ステップを入れて、テキスト圧縮を明示的に行う ようにしたんです。こんな感じです:

ステップ 1: Claude 3.5 Haiku(軽量)で要約を作成

ステップ 2: 要約結果のみを次に渡す

ステップ 3: Claude 3.5 Sonnet(高精度)で分類

こうするとトークン消費が 3 分の 1 に落ちたんですよ。地味な工夫だけど、本番環境での LLM 費用削減に直結する。

2. 条件分岐の JSON 解析が思ったより難しい

Bedrock Flows で条件分岐を作るときは、LLM の出力を JSON として解析する必要があるんですね。ところが Claude のプロンプトが曖昧だと、JSON 形式を守らずにテキストを返す ことがある。最初は Flows の条件分岐が「壊れている」と思ったけど、実は プロンプトの指示が不十分 だったんです。

# ❌ ダメなプロンプト
"チケットを分類してください。結果は JSON で返してください。"

# ✅ 良いプロンプト
"""チケットを以下の JSON スキーマで分類してください。
{
  "category": "bug" | "feature" | "support",
  "confidence": 0.0 ~ 1.0,
  "reason": "理由"
}

必ず有効な JSON のみを返してください。他のテキストは含めないでください。"""

これを Flows の「System Prompt」に設定して、さらに出力に JSON バリデーション を入れることで、エラーレートが 25% から 2% に落ちたんですよ。大きな改善です。

3. 本番環境でのコールドスタート:初回実行が遅い

Bedrock Flows を初めて実行するときは、30 ~ 60 秒かかる ことがあるんですよ。これはコンテナのスピンアップや LLM モデルのロードが原因。単発のテストなら問題ないけど、同時に複数のワークフローを実行するときは、SQS でキューイングして少しずつ実行するほうが安定したんです。

# Lambda で SQS メッセージを受け取り、
# Bedrock Flows API を呼び出す
import boto3
import json

flows_client = boto3.client('bedrock-flows')
sqs_client = boto3.client('sqs')

def lambda_handler(event, context):
    for message in event['Records']:
        ticket_data = json.loads(message['body'])
        
        # Bedrock Flows を非同期実行
        response = flows_client.start_flow_execution(
            flowIdentifier='ticket-classification-v1',
            clientRequestToken=ticket_data['ticket_id'],
            inputs={
                'ticket_content': ticket_data['content'],
                'priority': ticket_data['priority']
            }
        )
        
        # 実行状態を DynamoDB に記録
        print(f"Flow execution started: {response['executionArn']}")

非同期化すると、SQS が自動でレート制限 してくれるので、Bedrock 側への負荷も分散されるんですよ。これで安定性がぐっと上がりました。

プロンプトエンジニアリングと A/B テストの工夫

Bedrock Flows には、同じステップで複数のプロンプトを試せる A/B テスト機能 が組み込まれているんですよ。これを有効活用すると、本番導入前に精度を上げられるんです。

例えば、チケット分類の精度を高めるため、以下 3 つのプロンプト案を作成して比較しました:

プロンプト案説明精度推論コスト
案 A:シンプル「カテゴリを選んでください」78%¥0.5/回
案 B:COT(思考の鎖)「理由を述べながら分類してください」85%¥1.2/回
案 C:Few-shot例を 3 つ示してから分類89%¥1.8/回

コスト vs 精度を見ると、案 B が 費用対効果が最高 だったので、本番環境では案 B を採用することにしたんです。

この種のテストは Flows の UI から簡単にできるので、プロンプト案が複数あるなら 必ず A/B テストを走す ことをお勧めしますね。意外と結果が違うので。

監視とアラート:本番運用で無視できない部分

Bedrock Flows を本番環境で使うなら、CloudWatch と EventBridge の設定が必須 だ。

# CloudWatch ログを解析して、失敗の理由を把握する
log_group_name = '/aws/bedrock/flows/ticket-classification'

# CloudWatch Insights クエリ
query = """
fields @timestamp, executionArn, status, errorMessage
| filter status = "FAILED"
| stats count() by errorMessage
"""

# EventBridge で失敗時にアラート
event_pattern = {
    "source": ["aws.bedrock"],
    "detail-type": ["Flow Execution Failed"],
    "detail": {
        "flowIdentifier": ["ticket-classification-v1"]
    }
}

運用していてわかったのは、Flows の失敗の大半が「プロンプト出力が JSON 形式でない」か「外部 API のタイムアウト」 だってことなんですよ。CloudWatch で監視しておくと、問題を素早く発見できる。

まとめ

Bedrock Flows は 複雑な AI ワークフローをスケーラブルに構築したい場合、強い選択肢 だと思います。ただし、以下のポイントを押さえておくと、実装がずっとスムーズになりますね:

  1. 完全なノーコードは諦める:複雑ロジックは Lambda など外部サービスに委譲する 2 層構成が最適
  2. トークン管理を意識する:入力が大きい場合は中間ステップで要約・圧縮を明示的に行う
  3. プロンプトは詳細に、JSON バリデーションは必須:曖昧な指示は LLM に返されない
  4. 非同期実行とキューイングを活用:本番環境では SQS で一つずつ処理するほうが安定
  5. A/B テストと CloudWatch 監視は運用の生命線:精度 vs コストの最適化、失敗検知の仕組みを整える

正直、学習コストはそれなりにある けど、一度構築すると保守性と拡張性が高いんですよ。特に「AI + 自動化のワークフローを何個も構築しないといけない組織」には、Flows を導入する価値が十分あると思います。

チームで似た案件を抱えてたら、イベント駆動アーキテクチャとのハイブリッド構成も検討する価値あり。ワークフローの粒度が細かい場合は、Kafka でイベント駆動にしつつ、各マイクロサービスの内部ロジックを Bedrock Flows で実装するパターンも有効だと思います。

次は、Bedrock Flows と Agents の使い分け について検証予定。この辺りは 2026 年時点でもまだ試行錯誤の余地があるので、また何か気づきがあれば共有したいですね。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事