【AWS】コラム

Step FunctionsからClaudeを呼ぶ方法2種類を実測比較 速いのはBedrock、安いのはHTTP Task

作成者: ゼネックコミュニケーション|Sep 9, 2026, 6:06:59 AM

 こんにちは~ 浮田です。  

■  TL;DR

  • Step FunctionsからClaudeを呼ぶ方法は2つあります。Bedrock最適化統合(bedrock:invokeModel)と、HTTP TaskでAnthropic APIを直叩きする方法。どちらも「1ステート」で書けます
  • 同一画像・同一プロンプト・同一パラメータで56枚×3回。速いのはBedrockでした。p50が1.1秒 vs 1.5秒、p95は1.2秒 vs 2.5〜2.7秒。テールの安定感が段違いです(p50=ふつうの速さ、p95=運が悪いときの速さ。用語は第4節の補足で整理しています)
  • しかもこの差、接続の固定オーバーヘッドではありませんでした。出力が長くなるほど開きます(1語なら+0.4秒、400字なら+1.2秒)。生成中のスループット自体が違います
  • HTTP Taskのテールの重さは、経路に内在する確率的な遅延でした。画像なし・1語だけ返す最小リクエストでも70本中9本(13%)が1.5秒超に跳ね、Bedrockは70本すべてが0.6〜0.9秒に収まりました。設計では回避できない種類の遅さです
  • 一方コストはHTTP Taskが約9%安い。Bedrockのトークン単価はAnthropic API直のちょうど1.10倍でした(机上計算ではなく実請求で確認)
  • おまけの罠: モデルカードで「東京In-Region提供✓」でも、素のモデルIDではオンデマンド呼び出しできません(jp.推論プロファイル必須)
  • 結論:経路選びは「速さと運用のBedrock」か「安さとモデル鮮度のHTTP Task」の二択。

 

  目次 

  1. はじめに — 前回の続きと、今回の問い
  2. 2つの経路は何が違うのか
  3. 罠①「東京で使える」と「東京で呼べる」は別物
  4. 検証構成 — 公平に比べるためにやったこと
  5. 実装(ASLは1ステートだけ)
  6. 実測①:1語分類 56枚×3run
  7. 実測②:出力を長くすると差は拡大する
  8. 実測③:レイテンシの内訳をCloudWatchで割る
  9. 実測④:テールの正体を探る(最小リクエストでの切り分け実験)
  10. コスト比較 — 実請求から単価を逆算した
  11. 比較結果まとめ
  12. 使い分けの指針
  13. ハマったポイントまとめ
  14. 後片付け
  15. まとめ
  16. 検証の限界

 

 1. はじめに — 前回の続きと、今回の問い  

前回の記事では、プロンプトを自動で改良するループを回して書類分類の精度を上げる、という実験をしていました。

そこで一番効いた教訓は「モデル選定が全て」でした。
判定役をNova Liteにすると82%で頭打ちだった分類が、Claude Haiku 4.5に替えた瞬間に100%。プロンプトをこねる前にモデルを替えろ、という話です。

で、そうなると次の疑問が出てきます。

そのClaudeを、Step Functionsからどう呼ぶのが最適なのか?

調べると方法は2つありました。

  1. Bedrock最適化統合: ステートマシンから bedrock:invokeModel を直接呼ぶ。AWS内で完結します
  2. HTTP Task: Step FunctionsのHTTPエンドポイント呼び出し機能で、Anthropic APIを直叩きする。APIキーはEventBridge connectionで管理します

正直なところ「なんとなくBedrockのほうが楽そう」という感覚はありました。でも、根拠がありません。速いのか、安いのか、何がどれだけ違うのか、何ひとつ数字で言えない。

そこで前回と同じ手法(p50/p95、サーバ側タイムスタンプ、複数回の再現確認)で、同じ画像・同じプロンプト・同じパラメータを両方に流して測ってみることにしました。

結論を先に言うと、速いのはBedrock、安いのはHTTP Task

※ 補足: 厳密には3つ目の経路もありえます。HTTP Task から Bedrock の bedrock-mantle エンドポイント(Anthropic Messages API 互換)を叩く形です。bedrock-mantleについてはこちらの記事を参照してください。

 

■  2. 2つの経路は何が違うのか  

まず構造から。

A案:

Step Functions ──(SDK統合)──> Bedrock ──> Claude (AWS内・日本ジオ)


B案:

aStep Functions ──(HTTP Task)──> api.anthropic.com ──> Claude (Anthropic側)

ここをオフィスの荷物運びでたとえると分かりやすいです。

A案は社内便です。同じビルの中をカートで運ぶので速いし、警備を通るのも社員証(IAM)だけで済みます。

B案は外部の宅配便。一度ビルを出て公道に乗るので、道の混み具合に左右されます。伝票(APIキー)も要ります。

この「一度ビルを出るかどうか」が、後で出てくるp95の差にそのまま効いてきます。しかも思っていたより直接的に効いていました(第9節)。

構造の違いは3点に集約されます。

A: Bedrock最適化統合

B: HTTP Task

認証

IAMロールだけ

APIキー→EventBridge connection→Secrets Managerに自動保存→実行時にx-api-key注入

結果の取り出し

$states.result.Body.content[0].text

$states.result.ResponseBody.content[0].text

ペイロード制限

256KB(S3から入力を読む逃げ道あり)

256KB(逃げ道なし)

B案の認証は手数が多そうに見えますが、connectionを1つ作るだけで、あとはStep Functionsが実行時にヘッダーへ注入してくれます。

ステートマシン定義にシークレットが一切現れないのは、地味にありがたいポイントでした。

レスポンス構造は、中身はどちらも同一のMessages API形式です。

ただ包み方が違うので、JSONataのパスが1階層ずれます。これが後で刺さります(後述)。

 

■  3. 罠①「東京で使える」と「東京で呼べる」は別物  

さて、実装に入る前にいきなり躓きました。

Bedrockのモデルカードを見ると、Claude Haiku 4.5は ap-northeast-1(東京)で In-Region 提供に✓が付いています。

だから素のモデルID anthropic.claude-haiku-4-5-20251001-v1:0 で呼べるものだと思っていました。実際に叩くとこうなります。

▲ 素のモデルIDでオンデマンド呼び出ししたときのエラー

オンデマンドでは推論プロファイル経由が必須でした。

日本の場合は jp.anthropic.claude-haiku-4-5-20251001-v1:0(東京+大阪の日本ジオルーティング)を使います。

つまり「ドキュメント上のIn-Region提供」は、「単一リージョンに閉じたオンデマンド呼び出しができる」を意味しません。提供されている(=そのリージョンで処理される)話と、そのリージョンIDだけで呼べる話は別レイヤーなんですね。

これ、以前Location Serviceの公共交通ルーティングを検証したときに得た「リージョン対応≠データ網羅」とまったく同じ形の罠です。ドキュメントの✓は、自分が知りたいことの✓とは限らない。同じところで二度転んだので、そろそろ体で覚えたいところです。

※ 補足: 計測期間中の課金は全量が東京(APN1)に計上されていました。jp.プロファイルは名目上「東京+大阪」ですが、実際はほぼ東京で処理されていたようです。

 

 4. 検証構成 — 公平に比べるためにやったこと

  題材

前回記事で使った合成日本語ビジネス書類データセット(7クラス×24枚)の test split 56枚を使います。1実行=1画像の分類で、両ステートマシンにまったく同じ入力(base64画像+プロンプト+パラメータ)を渡します。

  • モデル: Claude Haiku 4.5(両経路とも同一スナップショット 20251001)
  • プロンプト: 7クラスから1語で答えさせる形式指定だけの素朴なもの
  • max_tokens: 16 / temperature: 0
  • 画像は最大93KB → base64後 約124KB。両経路とも256KB制限内にインラインで同梱できます

プロンプトについてひとつ。

前回育てた高精度プロンプトはあえて使いませんでした。 経路の比較に、前回記事の成果物への依存を持ち込みたくなかったからです。

ここで育成済みプロンプトを使うと「この差はプロンプトのせいでは?」と自分で疑う余地が残ってしまいます。

   公平性の担保  

比較実験は、条件を揃えるところが8割だと思っています。やったことは以下です。

  • 両ステートマシンとも単一ステート・リトライなし。リトライはレイテンシを汚すので、失敗は記録して除外する方針にしました(結果的に全実行成功でリトライゼロでした)
  • IAMロールも共用1本。権限差の混入を防ぎます
  • 1枚ごとにA→B / B→Aの順序を入れ替えて交互実行。時間帯と順序の偏りを打ち消します
  • レイテンシはDescribeExecutionのサーバ側 startDate / stopDate から算出。クライアント側で測ると自分のネットワークの機嫌が混ざるので、そこは見ません
  • 別日に再計測して再現確認

   <補足:p50 / p95 / テールって何?>  

この記事はこの3語で全部説明してしまうので、先に整理しておきます。私も最初は「平均でいいじゃん」と思っていた側なので、同じ方向から来る人向けの節です。

56回の実行を、速かった順に1列に並べたと想像してください。

  • p50(50パーセンタイル)= 前から数えて真ん中の人のタイム。ざっくり「ふつうに使ったときの速さ」です。中央値と同じものです
  • p95(95パーセンタイル)= 前から95%目、つまり後ろから3番目くらいの人のタイム。「運が悪かったときの速さ」。20回に1回はこれ以上かかる、という意味になります
  • テール(tail)= その列の後ろのほう、遅い側の裾。ここが長く伸びていることを「テールが重い」と言います

電車でたとえると分かりやすいです。p50は「いつもだと何分で着く」、p95は「遅延した日だと何分かかる」。通勤で本当に困るのは後者のほうですよね。

ではなぜ平均を使わないのか

平均は、極端に遅い1回に引っ張られて実態より悪く見えることもあれば、逆に大量の速い実行に薄められて「たまに5秒かかる」という事実を消してしまうこともあります。どちらの方向にも嘘をつくんですね。p50とp95を並べて出せば「ふつうの速さ」と「最悪付近の速さ」が分けて見えるので、この記事ではこの2つで通します。

そしてこの記事の結論は、実はここに集約されます。2つの経路はp50では約400msしか変わらないのに、p95では2倍以上開く。 つまり「ふつうの速さ」より「運が悪いときの速さ」に差が出るタイプの違いでした。ユーザーを待たせる処理では、効いてくるのは後者です。

 

   実装

CDKでconnection・IAMロール・ステートマシン2本をデプロイします。ASLの本体はどちらも1ステートだけです。

A案(抜粋):

 B案(抜粋):  

 見ていただくと分かりますが、差分は実質Resourceと引数の形だけです。「Step FunctionsからLLMを呼ぶのは大変そう」というのは、私の思い込みでした。Lambdaを1つも書かずに、どちらの経路もこれで動きます。  

 

 6. 実測①:1語分類 56枚×3run  

まずは主測定。1語分類を56枚、3回です。

run

A: Bedrock p50 / p95

B: HTTP Task p50 / p95

day1

1,084 / 1,210ms

1,502 / 2,511ms

day1-rep2

1,069 / 1,176ms

1,504 / 2,668ms

day2

1,106 / 1,211ms

1,410 / 2,647ms

3回測って数字がほぼ動かないので、これは素性のいいデータだと思います。読み取れることは2つ。

  1. p50でBedrockが約400ms速い(1.07〜1.11秒 vs 1.41〜1.50秒)
  2. それ以上に特徴的なのが分布の形です。Bedrockはp50とp95がほぼ同じ(±100ms)なのに対し、HTTP Taskはp95がp50の1.7〜1.9倍。テールが重く、20回に1回は2.5秒以上かかります

平均だけ見ていたら気づかない差です。

ユーザーを待たせる同期処理なら、効いてくるのは間違いなくp95側です。正解率は両経路とも55/56・54/56で実質同一。ここは主題ではないので流します。

それより収穫だったのが、input_tokensが全実行で1,204トークンに完全一致したことです。

両経路が同じトークナイズを経て同じモデルに到達している、という強い証拠になりました。ここが揃っていないと比較そのものが無効になるので、正直ホッとしました。

 

 7. 実測②:出力を長くすると差は拡大する  

さて、ここで疑問が出ます。この400msは、接続を確立するための固定オーバーヘッドなのでは?

最初はそう思っていました。違いました。同じ画像で、出力長だけを3段階に変えてみます。

条件

出力トークン

A p50

B p50

1語ラベルのみ

約7

1,084ms

1,502ms

+418ms

+判定根拠100字

約146

2,332ms

3,289ms

+957ms

+詳細読解400字

約398

4,204ms

5,394ms

+1,190ms

別日の再計測でも

medium: A 2,328 / B 3,018ms、

long: A 4,228 / B 5,146ms と同じ構図が再現しました。

Aの数字は2回でほぼ一致(2,332→2,328、4,204→4,228)していて、揺れているのはBのほうだけ、というのも示唆的です。

固定差ではありませんでした。出力が伸びるほど差も伸びています。

増分から実効生成速度を概算すると、A ≈ 7〜9ms/トークン、B ≈ 9〜13ms/トークン。つまり接続の入り口だけの話ではなく、生成している最中のスループット自体が、HTTP Task経由だと1〜2割遅いということになります。

これは使い分けにそのまま効きます。1語分類なら0.4秒差で済みますが、要約や長文生成を回すなら差は素直に積もっていきます。

 

 8. 実測③:レイテンシの内訳をCloudWatchで割る  

Bedrockは InvocationLatency(モデル側の処理時間)をCloudWatchに出してくれます。day1の56実行と突き合わせると、こう割れました。

実行全体 1,119ms
├─ モデル処理 903ms (InvocationLatency)

 └─ SFn統合+転送 216ms

A案のオーバーヘッドは約0.2秒。1.1秒のうち0.9秒はモデルが考えている時間なので、経路側でこれ以上削れる余地は小さいと分かります。

そして、ここで気づいたのがもう一つの差です。B案には同等の公式メトリクスが存在しないので、この分解ができません。

これ、遅い/速い以上に運用で効いてくると思いました。

本番で「レスポンスが遅い」と言われたとき、モデルが遅いのか経路が遅いのかを切り分けられるかどうかは、障害対応の速さに直結します

「Bedrockは内訳を検証できる、直叩きはブラックボックス」という観測可能性の差は、比較表に1行足す価値があります。

 

 9. 実測④:テールの正体を探る(最小リクエストでの切り分け実験)  

さて本題です。

残った宿題は、B案のp95でした。あの重いテールはどこから来るのか。候補は3つ考えました。

  • (a) EventBridge connectionの認証トークン取得
  • (b) 画像処理・生成そのもののばらつき
  • (c) 経路そのもののネットワーク揺らぎ

そこで、画像を渡さず「OK」と1語返すだけの最小リクエストを各70本流す、という切り分け実験をしました。

モデルの仕事をほぼゼロに潰せば、残った時間は接続と転送の分しかない、という考え方です。

A: Bedrock

B: HTTP Task

p50

730ms

1,035ms

p95

836ms

2,122ms

範囲

608〜865ms

849〜5,211ms

1.5秒超のスパイク

0 / 70

9 / 70(13%)

この実験の表だけ、p50/p95に加えて「範囲」と「スパイク数」を並べています。

理由があって、テールの正体を見たいときはp95では粗いからです。

p95は「後ろから3番目くらい」の1点しか教えてくれないので、その先最悪値がどこまで伸びているのか、遅い実行が何本あるのかが見えません。ここではまさにその「先」が知りたいので、最小値〜最大値の幅と、しきい値(1.5秒)を超えた本数を数えました。

Bのp95は2,122msですが、最悪値は5,211ms。p95の2倍以上先まで裾が伸びているわけです。一方Aは上限が865msで、p95(836ms)とほとんど差がありません。この「p95より先がどうなっているか」の違いが、次の切り分けの材料になります。

結果は明快でした。

  • (b)は棄却。画像も生成もないのに、Bは13%がスパイクしています。テールはペイロードの問題ではありません
  • (a)も棄却。スパイクの発生位置は実行途中にランダムに散っていて、初回に集中していません。認証のコールドスタートなら初回に出るはずです
  • 残るのは (c) 経路そのものの確率的な遅延。国外への往復のせいか、Anthropic API側の揺らぎかまでは、外からは切り分けられません。ただ少なくとも、B案を選ぶ限りリクエストの1割強は2〜5倍遅くなり、これは設計では回避できないということが分かりました

対してAは、70本すべてが608〜865msという狭い帯に収まりました。

範囲の上限が865msに対して、Bは最悪5,211ms。この安定性の差が、p95の差(1.2秒 vs 2.5秒)の正体です。

そしてもうひとつ、おまけの発見がありました。

最小リクエスト同士のp50差は約305ms(1,035 − 730)。主測定で見えた差は約420msでしたから、引き算すると内訳が見えます。

約420msの差のうち、大部分(約305ms)はベースの経路オーバーヘッドの差で、残り約100msが出力7トークン分の生成速度の差。

第7節で「出力が伸びると差が広がる」と書きましたが、正確に言うと下駄が305msあって、そこに生成速度の差が積み上がっていく構造だったわけです。

1語なら下駄が支配的、400字なら生成速度差が支配的になる。ここまで割れると、自分のユースケースでどちらが効くか見積もれます。

 

 10. コスト比較 — 実請求から単価を逆算した  

ここは机上計算ではなく、計測で実際に発生した実請求(Cost Explorer)から単価を逆算しました。公表価格表を写すだけだと、プロファイル経由の扱いが本当にその単価なのか確認できないからです。

トークン単価(1Mトークンあたり)

入力

出力

A: Bedrock(jp.プロファイル)

$1.10

$5.50

B: Anthropic API 直

$1.00

$5.00

Bedrockはちょうど1.10倍でした。

プロファイル経由でも係数は素直です。

そしてStep Functions側は、どちらの経路も1実行=3遷移×$0.000025で完全に同額

内訳はこうなります。

開始(Start → InvokeModel) 1

InvokeModel の実行       1

終了(→ End / Succeeded)    1

──────────────────────────

合計 3

請求上の1,368遷移÷456実行=3.0でも裏が取れました。

100万回換算(今回のshort条件: 入力1,204+出力7トークン)

A: Bedrock

B: HTTP Task

トークン

$1,363

$1,239

Step Functions

$75

$75

合計

$1,438

$1,314

B案が約9%安い、という結果です。

おもしろいのは、Step Functions側の$75が両経路とも同額だという点です。

A案とB案の引き算では丸ごと消えるので、経路差$124はまるごとトークン単価の差ということになります。Expressに変えれば$72ほど削れますが、それも両方に等しく効くので順位は動きません。

※ 今回Standardを選んだのは、DescribeExecutionのサーバ側タイムスタンプでレイテンシを測りたかったからでもあります。コストだけ見てExpressにすると、この計測手法は使えません。

LLMワークフローのコストは経路のトークン単価差で決まり、オーケストレーション費用は論点にならない。

Step Functionsのコストを気にして構成を歪めるくらいなら、経路とモデルを見直したほうが桁違いに効く、ということですね。

 

 11. 比較結果まとめ  

観点

A: Bedrock統合

B: HTTP Task直叩き

p50レイテンシ

約1.1秒

約1.5秒

p95レイテンシ

約1.2秒(安定)

約2.5秒(テール重い)

テールの正体

スパイクなし(0/70)

経路由来の確率的遅延(9/70)

生成スループット

約7〜9ms/トークン

約9〜13ms/トークン

コスト(100万回)

$1,438

$1,314(-9%)

認証

IAMのみ

APIキー+connection

観測可能性

CloudWatchで内訳分解できる

ブラックボックス

モデル鮮度

Bedrockでの提供待ち

Anthropicの最新に即追従

リージョン

日本ジオ内で完結

国外へ出る

事前準備

モデル有効化+jp.プロファイル

connection作成のみ

 

見事に「全部勝ち」がいないのが、この表の面白いところでした。

 

 12. 使い分けの指針  

  • ユーザー向けの同期処理・レイテンシ重視 → Bedrock統合
    p50で3〜4割速く、なによりp95が安定しています。SLOを切るならこちらです。この最小リクエスト実験で分かったとおり、B案のテールはリトライやタイムアウト調整で消せる種類のものではないので、ここは経路選択で決めるしかありません
  • バッチ処理・コスト重視・最新モデルをすぐ使いたい → HTTP Task。1割強のリクエストが2〜5倍遅くなっても、全体のスループットで吸収できるならデメリットになりません。トークン1割引が積もる大量処理や、Bedrock未提供の新モデルを使いたい場合に向きます
  • データを国外に出せない要件があるなら実質Bedrock一択
    ただし「In-Region」の意味は第3節の罠を参照してください。日本ジオ内ルーティングであって、東京固定ではありません

※ データの取り扱いや保持の条件は各サービスの規約と設定によります。要件がシビアな場合は必ず一次情報で確認してください。

 

 13. ハマったポイントまとめ  

 きれいに動いた話はここまで。ここからが、この記事のいちばん書きたかった部分です。  

   ① 素のモデルIDでオンデマンド呼び出しできない  

 第3節の話です。モデルカードのIn-Region ✓を信じてValidationExceptionを食らいました。jp.推論プロファイルを使います。あわせてIAMポリシーはプロファイルARNと基盤モデルARN(東京・大阪の両方)の両方に許可が必要です。  

   ② 採点の正規化で自爆した  

出力を説明付きにした途端、正解率が61%に急落して見えました。

一瞬「説明させるとモデルが混乱するのか?」と思ったのですが、生出力を確認するとモデルはちゃんと正解しています。犯人は自前の正規化コードでした。「領収書 …請求書ではなく…」のような文から、ラベルを配列順で探していたので、後ろに出てくる否定形のラベルを拾っていたのです。出現位置が最も早いラベルを採るよう直して解決。

採点系を疑う前にモデルを疑うな。 これ、プロンプト育成記事に続いて二度目の教訓です。二度目なので、もう言い訳できません。精度が急に落ちたときは、まず自分の集計コードを見る。

   ResponseBodyBody  

同じClaudeの返答なのに、JSONataのパスが違います。

A案(Bedrock): $states.result.Body.content[0].text
B案(HTTP): $states.result.ResponseBody.content[0].text

経路を差し替えるとき、後続ステートをコピペしただけでは動きません。地味ですが確実に一度は踏むポイントです。

   jp.プロファイルのIAM  

bedrock:InvokeModelの許可先を東京の基盤モデルだけに絞ると、大阪へルーティングされたときに落ちる可能性があります。今回の課金証跡上は東京のみでしたが、両リージョンを許可しておくのが安全です。  

 

 14. 後片付け  

npx cdk destroy
aws secretsmanager delete-secret --secret-id anthropic-api-key --force-delete-without-recovery

Secrets Managerは論理削除で最短7日残るので、検証用のキーは--force-delete-without-recoveryで消しています。

作るのは一瞬、片付けは意外と気を遣うところです。

 

 15. まとめ  

「Step FunctionsからClaude呼べる?」の答えは「2通りあって、どちらも1ステートで書ける」でした。

「じゃあどっちがいい?」は用途次第ですが、少なくとも判断基準は数字で出せました。

  • 速さとテールの安定、そして観測可能性が欲しければ Bedrock統合
  • コストとモデル鮮度が欲しければ HTTP Task
  • 状態遷移コストは両経路で同額なので、経路選びには効かない

検証として一番おもしろかったのは、この最小リクエスト実験でした。

「Bが遅い」で終わらせずに、画像も生成も剥がして最小リクエストにしてみたら、テールが設計で回避できない性質のものだと分かり、しかも420msの内訳(下駄305ms+生成差100ms)まで割れてしまった

仮説を3つ立てて2つ潰す、という作業がここまできれいに決まることは滅多にないので、これは素直に気持ちよかったです。

そして個人的な一番の収穫は、比較そのものより「ドキュメントの✓と、実際に呼べることは別物」「採点系のバグは二度起きる」という、実測しないと絶対に手に入らない2つの教訓のほうでした。

モデルカードのIn-Region ✓を信じてValidationExceptionを食らい、正解率61%の急落に青ざめて、原因は自分の正規化コードだった。この手の話は、机上の比較記事からは一行も出てきません。

 

 16. 検証の限界  

最後に、この記事の数字がどこまでの話なのかを書いておきます。

この結果は「1モデル(Claude Haiku 4.5)・1リージョン(ap-northeast-1)・1アカウント・2026年7月時点」のスナップショットです。

トークン単価もルーティングもテールの振る舞いも変わりえますし、モデルやリージョンが違えば数字は変わります。

また、時間帯とテールの相関(たとえば米国のピーク時間にBが悪化するのか)は未検証です。

最小リクエスト実験でスパイクが実行途中に散っていたことは確認できましたが、時刻軸での偏りを主張できるだけのサンプルは取っていないので、本記事では触れていません。ここは宿題として残します。