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 ワークフローをスケーラブルに構築したい場合、強い選択肢 だと思います。ただし、以下のポイントを押さえておくと、実装がずっとスムーズになりますね:
- 完全なノーコードは諦める:複雑ロジックは Lambda など外部サービスに委譲する 2 層構成が最適
- トークン管理を意識する:入力が大きい場合は中間ステップで要約・圧縮を明示的に行う
- プロンプトは詳細に、JSON バリデーションは必須:曖昧な指示は LLM に返されない
- 非同期実行とキューイングを活用:本番環境では SQS で一つずつ処理するほうが安定
- A/B テストと CloudWatch 監視は運用の生命線:精度 vs コストの最適化、失敗検知の仕組みを整える
正直、学習コストはそれなりにある けど、一度構築すると保守性と拡張性が高いんですよ。特に「AI + 自動化のワークフローを何個も構築しないといけない組織」には、Flows を導入する価値が十分あると思います。
チームで似た案件を抱えてたら、イベント駆動アーキテクチャとのハイブリッド構成も検討する価値あり。ワークフローの粒度が細かい場合は、Kafka でイベント駆動にしつつ、各マイクロサービスの内部ロジックを Bedrock Flows で実装するパターンも有効だと思います。
次は、Bedrock Flows と Agents の使い分け について検証予定。この辺りは 2026 年時点でもまだ試行錯誤の余地があるので、また何か気づきがあれば共有したいですね。