Claude Media
ClaudeのRuby SDKでツール実行ループとページネーションを実装する

ClaudeのRuby SDKでツール実行ループとページネーションを実装する

公式Ruby SDKのインストールからtool_runnerの自動実行ループ、ページネーション、ファイルアップロード、エラー処理まで実装例で解説します。

Ruby SDKの導入 — インストールと動作要件

Anthropic公式のRuby SDKは、Ruby 3.2.0以上のアプリケーションからClaude APIへ直接アクセスするためのライブラリです。HTTPトランスポートに標準ライブラリの net/http を使い、コネクションプールは connection_pool gemが担います。型定義はYard・RBS・RBIの3形式で同梱されており、実行時に依存ライブラリを増やさずに型の恩恵を受けられます。

このSDKが担うのは、リクエストを送って応答を受け取るという低レベルな部分だけです。ツールの実行結果を踏まえて次の指示を出し続けるような、状態を持った自律エージェントの構築そのものは扱いません。エージェントループやセッション管理まで欲しい場合はPython・TypeScript向けのAgent SDKやClaude Codeの領分になります。Rubyアプリケーションに組み込む用途では、今回扱うクライアントSDKが選択肢になります。Sinatraのバッチジョブやレポート生成、Railsアプリの一機能としてClaudeを呼び出すといった、既存Rubyコードの内側から必要なタイミングでAPIを叩く構成に向いています。

導入はBundlerで一行です。

bundle add anthropic

APIキーは環境変数 ANTHROPIC_API_KEY から自動で読み込まれます。個人アカウントキーやサービスアカウントキーが複数のワークスペースにアクセスできる場合は、リクエストヘッダー anthropic-workspace-id でワークスペースを明示します。

anthropic = Anthropic::Client.new(
  api_key: ENV["ANTHROPIC_API_KEY"] # デフォルト値なので省略可能
)
 
message = anthropic.messages.create(
  max_tokens: 1024,
  messages: [{role: "user", content: "Hello, Claude"}],
  model: :"claude-opus-5"
)
 
message.content.each do |block|
  puts block.text if block.type == :text
end

ストリーミングとテキストヘルパーで応答を逐次処理する

長い応答を待たずに処理を始めたいとき、SDKはServer-Sent Eventsによるストリーミングをサポートします。messages.stream はメッセージ単位のイベントを返し、stream.text.each は生成中のテキスト断片だけを取り出せます。

anthropic = Anthropic::Client.new
stream = anthropic.messages.stream(
  max_tokens: 1024,
  messages: [{role: :user, content: "Say hello there!"}],
  model: :"claude-opus-5"
)
 
stream.text.each do |text|
  print(text)
end

イベント単位で処理したい場合は stream.each にブロックを渡し、message.type を見て分岐します。SDK側で蓄積(accumulation)の仕組みも提供されるため、途中経過を自分で結合するコードを書く必要はありません。

tool_runnerでツール呼び出しの自動実行ループを組む

ツール呼び出しを自分でループ実装すると、Claudeの応答を受け取り→ツールを実行し→結果を返す、というやり取りを手動で書くことになります。Ruby SDKの tool_runner はこの往復を自動化し、BaseModel で入力スキーマを、BaseTool で実行本体を定義するだけでループが完結します。

anthropic = Anthropic::Client.new
class CalculatorInput < Anthropic::BaseModel
  required :lhs, Float
  required :rhs, Float
  required :operator, Anthropic::InputSchema::EnumOf[:+, :-, :*, :/]
end
 
class Calculator < Anthropic::BaseTool
  input_schema CalculatorInput
 
  def call(expr)
    expr.lhs.public_send(expr.operator, expr.rhs)
  end
end
 
# ツール実行ループを自動で処理
anthropic.beta.messages.tool_runner(
  model: "claude-opus-5",
  max_tokens: 1024,
  messages: [{role: "user", content: "What's 15 * 7?"}],
  tools: [Calculator.new]
).each_message { |message| puts message.content }

tool_runner はベータ名前空間 anthropic.beta.messages に置かれています。ツール呼び出しの一般的な設計方針はTool RunnerでAnthropic APIのツール呼び出しループを自動化するで扱っています。実行ループを自作する場合の考え方もそちらにまとめてあるので、Ruby以外の実装と比較したいときはあわせて確認してください。

ページネーションとファイルアップロードを実装する

Claude APIの一覧系エンドポイントはページネーションされています。Ruby SDKは自動でページを進める auto_paging_each を提供するので、次ページの有無を自分で判定する必要がありません。

anthropic = Anthropic::Client.new
page = anthropic.messages.batches.list(limit: 20)
 
# 単一アイテムの取得
batch = page.data[0]
puts(batch.id)
 
# 必要に応じて自動で次ページを取得
page.auto_paging_each do |batch|
  puts(batch.id)
end

細かい制御が必要な場合は next_page?next_page を使い、ループを自分で回すこともできます。

ファイルアップロードは PathnameStringIO、生の文字列コンテンツのいずれも受け付けます。

anthropic = Anthropic::Client.new
require "pathname"
 
# Pathnameを使うとファイル名を送りつつ大きなファイルをメモリに全展開せずに済む
file_metadata = anthropic.files.upload(file: Pathname("/path/to/file"))
 
# ファイル内容を直接渡すことも可能
file_metadata = anthropic.files.upload(file: File.read("/path/to/file"))

エラー処理・リトライ・タイムアウトの設定

接続に失敗した場合やAPIが4xx/5xx応答を返した場合、Anthropic::Errors::APIError のサブクラスが送出されます。ステータスコードとエラー型の対応は次の通りです。

ステータスエラー型
HTTP 400エラー型BadRequestError
HTTP 401エラー型AuthenticationError
HTTP 403エラー型PermissionDeniedError
HTTP 404エラー型NotFoundError
HTTP 409エラー型ConflictError
HTTP 422エラー型UnprocessableEntityError
HTTP 429エラー型RateLimitError
HTTP 500以上エラー型InternalServerError
タイムアウトエラー型APITimeoutError
ネットワークエラーエラー型APIConnectionError

接続エラー・408・409・429・500番台・タイムアウトは、短い指数バックオフとともにデフォルトで2回まで自動リトライされます。回数は max_retries でクライアント全体、または request_options でリクエスト単位に上書きできます。タイムアウトはデフォルト10分で、timeout オプションで変更可能です。リトライとタイムアウトは独立した設定です。タイムアウトを短くしただけではリトライ回数は変わりません。

anthropic = Anthropic::Client.new(
  max_retries: 0, # デフォルトは2
  timeout: 20 # 秒。デフォルトは10分
)

Sorbet型とプラットフォーム統合の使い分け

Ruby SDKはsorbet-runtimeに依存せずRBI定義だけを提供します。そのため列挙型は実行時には常にプリミティブとして振る舞う「tagged symbol」で表現されます。Anthropic::MessageCreateParams::ServiceTier::AUTO のような定数を渡しても、リテラルの :auto を渡しても同じ結果です。開発時の型チェックはSorbetに任せつつ、実行時のオーバーヘッドを持ち込まない設計です。

クラウド別のクライアントも用意されています。Agent Platform向けは Anthropic::VertexClient で、googleauth gemが必要です。Bedrockは2系統に分かれます。新規プロジェクト向けの Anthropic::BedrockMantleClient と、既存の InvokeModel APIを使うアプリ向けの Anthropic::BedrockClient です。Claude Platform on AWSはメインの anthropic gemに含まれる Anthropic::AWSClient を使います。コンストラクタに workspace_id: を渡すか、環境変数 ANTHROPIC_AWS_WORKSPACE_ID を設定します(ベータ)。

このパッケージはSemVer(セマンティックバージョニング)に従います。ただし .rbi / .rbs の型定義ファイルへの改善は、実行時の振る舞いに影響しないため非破壊的変更として扱われます。型定義だけが更新されるパッチリリースを破壊的変更と誤認しないための取り決めです。

BaseModelが提供する構造的等価性とシリアライズ

すべてのパラメータ・レスポンスオブジェクトは Anthropic::Internal::Type::BaseModel を継承しており、いくつかの便利な機能を共有しています。未知のフィールドを含むすべてのフィールドに obj[:prop] 構文でアクセスでき、obj => {prop: prop} のようなパターンマッチングで分解代入もできます。2つのAPI呼び出しが同じ値を返せば、== の比較で構造的等価性が成立します。#to_h#deep_to_h#to_json#to_yaml といったヘルパーも共通で使えます。レスポンスをログに残す、テストでスナップショット比較するといった用途では、これらのヘルパーを使えば独自のシリアライズコードを書かずに済みます。

未文書化のパラメータ・エンドポイントへの対応

ドキュメント化されていないパラメータを送りたい、あるいはドキュメント化されていないレスポンスプロパティを読みたい場合は、request_optionsextra_queryextra_bodyextra_headers を渡します。

anthropic = Anthropic::Client.new
message = anthropic.messages.create(
  max_tokens: 1024,
  messages: [{role: "user", content: "Hello, Claude"}],
  model: :"claude-opus-5",
  request_options: {
    extra_query: {my_query_parameter: "example"},
    extra_body: {my_body_parameter: "example"},
    extra_headers: {"my-header": "example"}
  }
)

ドキュメント化されていないエンドポイントそのものを叩きたい場合は、認証・リトライなどクライアントの機能を保ったまま anthropic.request で直接リクエストできます。

response = anthropic.request(
  method: :post,
  path: "/undocumented/endpoint",
  query: {"dog" => "woof"},
  headers: {"useful-header" => "interesting-value"},
  body: {"hello" => "world"}
)

他言語のAnthropic SDKと比べたRubyの実装上の癖

公式が提供するクライアントSDKはPython・TypeScript・C#・Go・Java・PHP・Rubyの7言語です。同じMessages APIを叩く点は共通ですが、Ruby版は型安全性の担保の仕方が際立って異なります。C#のクラスやPHPの値オブジェクトのように実行時にも型情報が残る言語と異なり、RubyはSorbetの型注釈を実行時に一切参照しません(なおTypeScriptの型定義もコンパイル時に消去されるため、実行時保持の実例ではありません)。型チェックは開発時の静的解析(srb tc 等)に完全に委ねられます。本番実行では素のHashやSymbolと変わらない速度で動きます。

もう一つの癖はコネクション管理です。Anthropic::Client インスタンスはスレッドセーフですが、フォークセーフなのは「進行中のHTTPリクエストが無いとき」に限られます。デフォルトのコネクションプールサイズは99です。Resqueやforemanのようにワーカープロセスをforkする構成では、クライアントの生成タイミングをforkの後に置くか、fork前にin-flightリクエストが残っていないことを確認する必要があります。

よくあるつまずき

  • ワークスペースヘッダーの付け忘れ: 個人・サービスアカウントキーが複数ワークスペースにアクセスできる設定の場合、anthropic-workspace-id を付けないと意図しないワークスペースの請求先に計上されることがあります
  • IOディスクリプタの直接アップロード: files.upload に生の IO を渡すとリトライが無効化されるため、ネットワーク不安定な環境では PathnameFile.read の結果を渡す方が安全です
  • BedrockClientとBedrockMantleClientの取り違え: 新規プロジェクトは BedrockMantleClient が前提で、BedrockClient は既存の InvokeModel API利用アプリを移行させないための後方互換クライアントです
  • forkするワーカーでのクライアント使い回し: HTTPリクエストが進行中のままforkすると、コネクションプールの状態が壊れることがあります

まとめ

Ruby SDKは bundle add anthropic で導入でき、Ruby 3.2.0以上が前提条件です。ツール呼び出しの往復を自作したくなければ tool_runner に任せ、一覧取得は auto_paging_each で自動ページネーションを使うのが基本形になります。型安全性はSorbetの静的解析に寄せる設計なので、実行時のオーバーヘッドを気にせず型の恩恵を受けられます。ただしMicrosoft Foundry経由での利用だけは非対応です。

料金・モデル選択・認証まわりの全体像はAnthropic API完全ガイド、トークン数を事前に把握したい場合はcount_tokensで送信前にトークン数を数える方法もあわせて参照してください。

この記事を共有:XはてブLinkedIn