Bedrock Flows を本番で6ヶ月使って見えてきたこと【2026年版】
「Lambda + Step Functions でよくない?」と思ってた自分が、Bedrock Flows を本番導入して6ヶ月で気づいたこと。ハマりどころと設計パターンを正直に書きます。
Bedrock Flowsをチームで使い始めて6ヶ月、正直なところを書く
うちのチームがBedrock Flowsを本格的に本番投入したのは2025年の秋頃で、それから半年以上経った。当初は「Lambda + Step Functions + Bedrockを組み合わせればいいじゃん」という声もあったし、自分も最初はそう思っていた。でも実際に複数のLLMステップを連鎖させてみると、状態管理・プロンプトの受け渡し・エラーハンドリングが想像以上に煩雑で、Flows専用のコンソールとAPIに乗っかったほうがトータルで楽だという結論に至った。
ここに書くのは「どういう場面でFlowsを選ぶべきか」「実際にハマった部分」「2026年現在のアップデートを踏まえた設計パターン」、できるだけ生々しく。機能一覧の羅列はAWS公式ドキュメントに任せる。
なお、RAGパイプラインの本番運用については以前RAG本番運用1年で痛感したことに書いたので、チャンキング設計と組み合わせて読んでほしい。ローカルLLM側の話はローカルLLM本番投入でハマった話にまとめてある。
2026年のBedrock Flows、何が変わったか
2025年末〜2026年Q1にかけてのアップデートが、体感としてかなり大きかった。正直、2025年初頭のBedrock Flowsは「便利そうだけどまだ早い」という印象だった。ループがなかったし、並列処理は試験的だったし、CloudWatchとの統合も薄かった。それが今では、本番で使うのに十分なレベルになってきた。
| 機能 | 変更内容 | 実務インパクト |
|---|---|---|
| Inline Agent Node | FlowsからAgentを直接呼び出し、Agentのオーケストレーション結果を後続Nodeに渡せるように | 外部API呼び出し・KB参照をFlows内で完結できる |
| Loop Node(GA) | 条件付きループ処理が正式対応、最大50イテレーション | 要約→評価→再要約のような反復タスクに使える |
| Parallel Node(GA) | 複数ブランチを並列実行してJoin | 複数ドキュメントの同時処理でレイテンシ削減 |
| Flow Version管理強化 | バージョン間のdiff表示、ロールバックAPI | 本番/ステージング管理が格段に楽になった |
| CloudWatch Logs詳細化 | Node単位の実行時間・トークン数をメトリクスとして出力 | コスト分析がようやくできるようになった |
| Bedrock Guardrails統合 | FlowレベルでGuardrailsを設定可能 | Node単位でのインライン設定が不要に |
「ようやくできるようになった」という感じが正直なところで、これらが揃って初めて「本番で安心して使える」と思えた。
実際に組んだアーキテクチャ:カスタマーサポート自動化パイプライン
うちが本番で使っているユースケースの一つが、カスタマーサポートのチケット自動トリアージ→回答ドラフト生成→人間レビューキュー振り分けだ。こういうマルチステップのLLMタスクがBedrock Flowsの得意領域で、全体像はこんな構成になっている。
graph TB
subgraph Internet
User([ユーザー問い合わせ])
end
subgraph AWS_Cloud[AWS Cloud]
subgraph API_Layer[API Layer]
APIGW[API Gateway]
Lambda_Entry[Lambda\n入力バリデーション]
end
subgraph Bedrock_Flow[Bedrock Flows]
Flow_Input[Input Node\nチケット受信]
Classify[Prompt Node\nカテゴリ分類\nClaude 3.5 Sonnet]
Condition[Condition Node\n緊急度判定]
subgraph Parallel_Branch[Parallel Node]
KB_Search[Knowledge Base Node\nFAQ検索]
History_Search[Agent Node\n過去チケット参照]
end
Join[Join Node\n結果マージ]
Draft[Prompt Node\n回答ドラフト生成]
Quality[Prompt Node\n品質チェック]
Loop_Check{Loop Node\n品質スコア >= 0.85?}
Flow_Output[Output Node]
end
subgraph Storage[Storage & Queue]
S3[S3\nチケット保存]
DynamoDB[DynamoDB\nセッション管理]
SQS_High[SQS\n緊急キュー]
SQS_Normal[SQS\n通常キュー]
end
subgraph Observability[Observability]
CW_Logs[CloudWatch Logs]
CW_Metrics[CloudWatch Metrics\nトークン数・レイテンシ]
end
end
subgraph Human_Review[Human Review]
Agent_High[サポートチーム\n即時対応]
Agent_Normal[サポートチーム\n通常対応]
end
User --> APIGW --> Lambda_Entry
Lambda_Entry --> S3
Lambda_Entry --> Flow_Input
Flow_Input --> Classify
Classify --> Condition
Condition -- 緊急 --> SQS_High --> Agent_High
Condition -- 通常 --> Parallel_Branch
KB_Search --> Join
History_Search --> Join
Join --> Draft
Draft --> Quality
Quality --> Loop_Check
Loop_Check -- No, 再生成 --> Draft
Loop_Check -- Yes --> Flow_Output
Flow_Output --> SQS_Normal --> Agent_Normal
DynamoDB <--> History_Search
Bedrock_Flow --> CW_Logs
Bedrock_Flow --> CW_Metrics
このパイプラインで特に効いたポイントが3つある。
① Parallel NodeでKB検索と過去チケット参照を並列実行
以前はKB検索→Agent呼び出しを直列でやっていて、合計8〜12秒かかっていた。Parallel Node(GA版)に切り替えてから4〜6秒に短縮した。地味だけど、ユーザー体感としてはかなり変わる。
② Loop Nodeで品質チェックを反復
回答ドラフトの後に品質スコアを計算するPrompt Nodeを置き、スコアが0.85未満なら再生成するようにしている。実際には大体1〜2回のループで収束するが、最大3回でキャップをかけていて、3回でも閾値を下回ったら「要人間確認」フラグをつけて次のNodeに渡す。
③ CloudWatch MetricsでNode単位のトークン消費を監視
2026年のアップデートで入力/出力トークン数がNode粒度でCloudWatchに飛ぶようになった。これが本当に助かった。コスト爆発がどのNodeで起きているかすぐ特定できる。具体的にはこんなクエリで確認している。
# CloudWatch でNode単位のコストを可視化するクエリ例
import boto3
from datetime import datetime, timedelta
client = boto3.client('cloudwatch', region_name='us-east-1')
response = client.get_metric_data(
MetricDataQueries=[
{
'Id': 'draft_node_tokens',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/Bedrock',
'MetricName': 'FlowNodeInputTokenCount',
'Dimensions': [
{'Name': 'FlowId', 'Value': 'YOUR_FLOW_ID'},
{'Name': 'NodeName', 'Value': 'DraftGenerationNode'}
]
},
'Period': 3600,
'Stat': 'Sum'
}
}
],
StartTime=datetime.utcnow() - timedelta(days=7),
EndTime=datetime.utcnow()
)
for result in response['MetricDataResults']:
total_tokens = sum(result['Values'])
# Claude 3.5 Sonnet 入力: $0.003/1K tokens (2026年5月時点)
estimated_cost_usd = (total_tokens / 1000) * 0.003
print(f"Node: {result['Id']}, Tokens: {total_tokens:,}, Est. Cost: ${estimated_cost_usd:.2f}")
実行するとこういう出力になる:
Node: draft_node_tokens, Tokens: 1,240,500, Est. Cost: $3.72
週単位で監視していて、ドラフト生成Nodeが週3〜4ドルと一番コストが高い。品質チェックNodeが週1.2ドル。KB検索はBedrockのKB APIコストが別建てなので注意が必要だ。
Bedrock Flows で本当にハマった3つのポイント
以前Bedrock Flows導入で痛感した落とし穴でも書いたが、2026年版のアップデートを踏まえて改めて整理する。
① 変数スキーマの設計ミスは後で直せない
NodeとNodeの間のデータ受け渡しはFlow変数(JSON Schema)で定義する。問題は、Flowをいったんデプロイすると変数スキーマを変更するにはFlow全体を作り直す必要があることが多い点だ(2026年5月時点でも部分的なスキーマ変更は制約あり)。
自分たちは最初にスキーマを甘く設計して、後から「このNodeにチケットIDを引き回したい」となったときに地獄を見た。最初から「全NodeでIDとmetadataを必ず渡す」設計にすべきだったと反省している。
// 推奨: 最初から汎用フィールドを入れておく
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"ticket_id": { "type": "string" },
"content": { "type": "string" },
"metadata": {
"type": "object",
"additionalProperties": true // ← これが後で救ってくれる
},
"trace": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["ticket_id", "content"]
}
additionalProperties: true を入れておくと、後から後続Nodeで追加フィールドを乗せられる。地味に大事なので最初から入れておいてほしい。
② Inline AgentのToolsは事前にテストを徹底する
Inline Agent Nodeは非常に強力で、FlowsからAgentのツール実行(Lambda呼び出し)を直接トリガーできる。ただ、Agent内のToolが例外を投げたときのエラーハンドリングがFlowsレベルでは粗い。Tool失敗→Agentがリトライループにはまってタイムアウト、という事象が本番で2回起きた。これは正直かなり焦った。
現実的な対策は2つ。Agent側のLambdaで例外をちゃんとキャッチして {'errorMessage': '...', 'httpStatusCode': 500} 形式のレスポンスを返すこと、そしてAgent側の max_retrievals 設定を必ずデフォルト(5)より下げること(自分たちは3に設定している)。
③ Condition Nodeの評価式は「文字列比較」が基本
Condition NodeはLLMの出力を文字列として受け取り、その文字列に基づいてルーティングする。数値比較や複雑なロジックをCondition NodeのJSONPath式だけで表現しようとすると詰まる。
うちのやり方は、前段のPrompt Nodeに「緊急度を HIGH / MEDIUM / LOW のどれか1単語で返せ」と指示して、Condition Nodeはその文字列マッチだけにしている。複雑な判定ロジックはLLM側に押し込む、という設計思想が結果的にシンプルで保守しやすかった。最初は「LLMに判定させるのは怖い」と思っていたけど、プロンプトをちゃんと書けばかなり安定する。
パフォーマンスとコストの実測値
6ヶ月の本番運用データからざっくりまとめる。月次コストはこんな推移だった。
xychart-beta
title "Bedrock Flows 月次コスト推移(USD)"
x-axis ["2025-11", "2025-12", "2026-01", "2026-02", "2026-03", "2026-04"]
y-axis "Cost (USD)" 0 --> 1200
bar [210, 380, 620, 750, 880, 940]
line [210, 380, 620, 750, 880, 940]
月1000ドル手前で推移している。チケット処理数は月2万件ほどなので、1チケットあたり約4.7セントという計算になる。Flows自体のオーバーヘッドはほぼゼロに近く(Flow実行自体の追加コストは今のところ見えていない)、コストの大半はモデルのトークン代だ。
エンド・ツー・エンドのレイテンシ内訳はこんな感じになっている。
pie title Flowエンド・ツー・エンドレイテンシ内訳(平均9.2秒)
"分類Prompt Node" : 1.8
"並列処理(KB+Agent)" : 2.4
"回答ドラフト生成" : 3.1
"品質チェック" : 1.5
"その他オーバーヘッド" : 0.4
回答ドラフト生成が一番重い。Claude 3.5 Sonnetを使っているが、品質チェックにはHaiku 3.5を使うようにコスト/品質のトレードオフを調整した。同じNodeでもモデルを選べるので、「重い処理はSonnet、判定系はHaiku」という使い分けは積極的に試してみる価値がある。
なお、SageMaker Pipelinesとの比較をよく聞かれるが、あちらはML学習・バッチ推論向け、Bedrock FlowsはリアルタイムなLLMオーケストレーション向けで、用途が全然違う。SageMaker Pipelines ML CI/CDと混同しないように注意してほしい。
CDKでのFlows管理と運用のコツ
コンソールをぽちぽちして作ったFlowを本番で運用するのは正直しんどいので、うちはCDK(TypeScript)でFlow定義を管理している。2025年末からCDKのL2 ConstructがBedrockに対応し始めて、aws-cdk-lib/aws-bedrock でFlowのリソースを記述できるようになった。
import * as bedrock from 'aws-cdk-lib/aws-bedrock';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class SupportFlowStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const flowRole = new iam.Role(this, 'BedrockFlowRole', {
assumedBy: new iam.ServicePrincipal('bedrock.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonBedrockFullAccess')
]
});
// Flow定義はJSONで管理(コンソールのエクスポート機能でドラフトを得る)
const flowDefinition = require('./flow-definition.json');
const flow = new bedrock.CfnFlow(this, 'SupportTriageFlow', {
name: 'support-triage-flow',
executionRoleArn: flowRole.roleArn,
definition: flowDefinition,
description: 'カスタマーサポートチケット自動トリアージ v2.3'
});
// バージョン管理
const flowVersion = new bedrock.CfnFlowVersion(this, 'SupportFlowV1', {
flowIdentifier: flow.attrId,
description: '品質チェックループ追加版'
});
// エイリアスで本番/ステージングを切り替え
new bedrock.CfnFlowAlias(this, 'ProdAlias', {
flowIdentifier: flow.attrId,
name: 'production',
routingConfiguration: [{
flowVersion: flowVersion.attrVersion
}]
});
}
}
Flow定義のJSONはコンソールの「エクスポート」機能でベースを作って、それをGitで管理する運用にしている。コンソールで試作→Gitにコミット→CDKでデプロイ、というフローが今のところ一番回りやすい。ただ、コンソールで変更してGitに戻すのを忘れるとdriftが発生するので、CIでFlow定義のdiffチェックを入れている。
個人的には「完全にコード管理する」に拘ると複雑なJSON定義との格闘になってコストが高い。「コンソールでプロトタイプ、CDKで本番管理」のハイブリッドが現実的な落とし所だと思っている。CDKのセキュリティ検証についてはCDK Aspects・Nag活用ガイドも参考にしてほしい。
まとめ
6ヶ月Bedrock Flowsを本番で動かして、「ハマりは多いが、複数ステップLLMタスクにはかなり有効」という評価に落ち着いた。やって良かったと思っているし、今から始めるなら2026年時点はかなりタイミングがいいと思う。要点をまとめると以下の通りだ。
① 変数スキーマは最初から余裕を持って設計する
additionalProperties: true とIDフィールドの全Node引き回しを最初から入れておく。後から変えるのは本当に痛い。
② Parallel NodeとLoop NodeのGAで本番投入のハードルが下がった 2026年現在、並列処理と反復タスクが安定して動く。レイテンシ最適化に積極的に使っていい。
③ CloudWatch Metrics(Node粒度)でコスト可視化を必ずやる どのNodeがトークンを食っているか見えないとコスト管理ができない。アップデートで入ったNode単位メトリクスを最初から設定しておくこと。
④ Inline Agent NodeはTool側のエラーハンドリングを先に作る Flows側でできることは限られているので、Lambda(Tool)側で防御的なエラー処理を徹底する。
⑤ CDKで管理するが「コンソールでプロトタイプ、CDKで本番」のハイブリッドが現実的 完全コード管理に拘ると初期コストが高い。
次のアクションとしては、まず小さいユースケース(3〜4 Node程度の直列パイプライン)から試して、Parallel NodeとLoop Nodeを段階的に追加するのをおすすめする。最初からフル機能を使おうとするとデバッグが辛い。皆さんはどういうユースケースでBedrockのオーケストレーションを使っていますか?Step FunctionsとFlowsの使い分けとか、ぜひ聞かせてほしい。