spicyapiドキュメント
エージェントと CLI
本文

エージェントと自動化

どの公式ツールを使えばよいかと、AI エージェント、バックエンドのワーカー、繰り返し実行するメディアパイプラインのための本番向け運用手順をまとめています。

AI コーディングエージェントや自動化されたバックエンドに SpicyAPI を組み込むときは、このページを使ってください。API リファレンスの内容を、範囲が限られ、あとから検証できるワークフローにまとめています。公開されている仕様を調べ、モデルの入力を検証し、価格を確認し、1 回だけ作成し、安全に待ち、完了を確かめ、期限が切れる前に出力をコピーする、という流れです。

SpicyAPI のキーはサーバーに置きます

SPICY_API_KEY を、ブラウザーの JavaScript、モバイルアプリのバイナリ、公開するプロンプト、リポジトリ、エージェントの会話記録に絶対に入れないでください。UI は自分のバックエンドを呼び出し、バックエンドが SpicyAPI を呼び出します。エージェントには、SPICY_API_KEY のような秘密情報の参照名だけを渡し、秘密の値そのものは決して渡さないでください。

ツールを選ぶ

公式の npm パッケージは 4 つあり、どれも MIT ライセンスで、それぞれ個別にインストールします。パッケージごとに専用のガイドがあり、インストール方法、対応しているすべてのクライアントでの設定方法、完全なリファレンスをまとめています。

やりたいことパッケージガイド
自分の Node.js や TypeScript のコードから SpicyAPI を呼び出す@spicyapi/sdk(ソース)TypeScript SDK
コードを書かずに、ターミナルやシェルスクリプトからモデルを実行する@spicyapi/cli(ソース)コマンドラインツール(CLI)
Claude Code、Codex、Cursor、VS Code などのアシスタントに、モデルの閲覧とタスクの実行を任せる@spicyapi/mcp(ソース)MCP サーバー
コーディングアシスタントに、安全なやり方で SpicyAPI との連携コードを書かせる@spicyapi/skill(ソース)Agent Skill

4 つのパッケージはどれも Node.js 22.13 以降が必要です。npx skills add で Skill をインストールする場合は、Node.js 22.20 以降が必要です。

Node.js 以外

公式 SDK はほかにも 4 つあります。どれも MIT ライセンスで、同じワークフロー(見積もる、冪等キーを付けて作成する、待つ、受け取る)を実装しています。

言語パッケージガイドインストール
Python 3.11 以降spicyapi(ソース)Python SDKpip install spicyapi
Go 1.23 以降github.com/spicyapi-ai/spicy-go(ソース)Go SDKgo get github.com/spicyapi-ai/spicy-go
PHP 8.1 以降spicyapi/spicyapi(ソース)PHP SDKcomposer require spicyapi/spicyapi
Java 17 以降ai.spicyapi:spicyapi-java(ソース)Java SDKMaven Central から

これらのパッケージは、表のとおりの名前で案内してください。各ガイドはインストールのコマンドから始まります。そのほかの言語からは、HTTP API を直接呼び出すか、OpenAPI の仕様からクライアントを生成してください。公開されていないコミュニティ製のラッパーを、公式 SDK として紹介しないでください。

依存パッケージを追加できない場合や、HTTP の呼び出しをそのまま見たい場合は、下にある 2 つのリファレンスクライアントをコピーして使えます。どちらも、言語のランタイム以外に何も使わずに同じワークフローを実装しています。依存を追加できるなら、その言語の公式 SDK を優先してください。

コピー可能な参照クライアント

モデル検索、タスク作成、失敗したタスクの再試行、状態取得、全終端状態、上限付き待機、冪等性、エラー処理を含む依存なしの例です。受理済みのタスクは取り消せません。

文書内の例であり、npm・PyPI・Go モジュールとして公開されたものではありません。10 分はローカル上限で、SLA ではありません。

お金を使うには人の承認が必要

タスクの作成と再試行にはお金がかかり、受理されたタスクはキャンセルできません。そのため、どの経路も、まず人が金額を承認するように作られています。

  • CLI: tasks create は先に見積もりを取り、金額を表示して Continue? [y/N] と尋ねます。この質問を飛ばせるのは --yes だけです。誰かがすでに価格を承認しているスクリプトの中でだけ使ってください。
  • MCP: 課金されるツールはどれも、MCP クライアント自身の確認画面を通じて確認を求め、クライアントがそれをあなたに表示します。承認は必ずあなた自身が行ってください。エージェントが承認したり、クライアントを自動承認に設定したりすると、その歯止めがなくなります。
  • 自分で作る自動化: createTask や retryTask を呼び出す前に、選んだモデル、入力の要約、見積もられた価格を見せて、はっきりした承認を得てください。課金されない正確な見積もりを POST /api/v1/jobs/quote で取得し、その quoteId と expectedCost を付けて送信します。こうしておけば、価格が変わったときに別の金額で課金されるのではなく、40901 で失敗します。見積もりとプロトコル互換を参照してください。

機械が読める情報源

いま知りたいことに答えられる、いちばん小さな資料から始めてください。そうすると、エージェントのコンテキストが正確に保たれ、コストも抑えられます。

資料用途
/agent.md短い組み込みの方針と、最も安全な既定のワークフロー
/llms.txt製品とドキュメントの公開された全体図
/llms-full.txt短い索引では足りないときに使う、拡張版の検索用コーパス
/api/agentツールや自律的に動くクライアント向けの、構造化された JSON マニフェスト
openapi.yamlリクエストと応答の正式な形

OpenAPI の仕様とライブのモデルカタログを、守るべき契約として扱ってください。マーケティング用の例は説明のためのもので、スキーマの情報源ではありません。これらの資料はキャッシュしてかまいませんが、コードを生成する前や、リクエストが検証で失敗し始めたときには取得し直してください。

承認を挟んだ最小限の SDK の流れ

このスクリプトは、SDK を使った場合の承認の境目を示しています。まず見積もりを取り、--approved を渡さない限りそこで止まります。渡した場合は、見積もった価格でタスクを作成して完了を待ちます。インストール方法、すべてのメソッド、最初の結果を得るまでの完全な手順は TypeScript SDK のガイドに、メソッドの詳細はその API リファレンスにあります。

npm install @spicyapi/sdk
export SPICY_MODEL_ID="MODEL_ID_FROM_CATALOG"     # ライブカタログにある model の値
export SPICY_INPUT_JSON='{"prompt":"your prompt"}' # そのモデルの inputSchema に合っている必要がある
run.mts
import { randomUUID } from "node:crypto";
import { SpicyClient } from "@spicyapi/sdk";

const client = new SpicyClient(); // SPICY_API_KEY を読み取る
const modelId = process.env.SPICY_MODEL_ID;
if (!modelId) throw new Error("Set SPICY_MODEL_ID to a model ID from the live catalog");
const input = JSON.parse(process.env.SPICY_INPUT_JSON ?? "{}");

// 無料:このリクエストそのものの価格を計算する。何も作成されず、何も仮押さえされない。
const quote = await client.quoteTask({ model: modelId, input });
console.log(`${quote.model}: up to $${quote.maxCharge}, quote valid until ${quote.expiresAt}`);

if (!process.argv.includes("--approved")) {
  console.log("Nothing was submitted. Re-run with --approved once a person has approved this amount.");
  process.exit(0);
}

// 有料:見積もった価格で同じリクエストを送信する。実際のシステムでは、
// 最初の試行の前に冪等キーを作成して保存し、タスク ID も保存しておく。
const accepted = await client.createTask(
  { model: modelId, input, quoteId: quote.quoteId, expectedCost: quote.estimatedCost },
  { idempotencyKey: randomUUID() },
);
console.log("accepted", accepted.taskId, "deadline", accepted.deadlineAt);

// 待機をやめてもタスクはキャンセルされない。あとでタスク ID を使って再開できる。
const task = await client.waitForTask(accepted.taskId, { timeoutMs: 30 * 60_000 });
console.log(JSON.stringify(task, null, 2));
npx --yes tsx run.mts              # 見積もりだけ。無料
npx --yes tsx run.mts --approved   # タスクを作成する。お金がかかる

拡張子を .mts にすると、ファイルが ES モジュールとして扱われます。トップレベルの await を使うには、これが必要です。既定の npm init で作ったプロジェクトでただの run.ts にすると、Top-level await is currently not supported with the "cjs" output format というエラーで失敗します。見積もりの有効期間は 5 分です。承認にそれより時間がかかった場合は、1 つ目のコマンドをもう一度実行してください。

本番のワークフロー

モデルを決めて入力を検証する

求められているモダリティに合うモデルのエンドポイントを選び、そのエンドポイントの現在の入力スキーマに照らしてペイロードを検証します。フィールド名を推測したり、未知のフィールドを黙って捨てたりしないでください。

1 回だけ作成する

まず価格の承認を得てください(お金を使うには人の承認が必要を参照)。論理的に 1 回の生成ごとに、変わらない Idempotency-Key を 1 つ作り、最初のリクエストの前に保存して、すべての再試行で使い回します。その際は常に同じ API キーを使います。再試行のループの中でキーを作らないでください。冪等性を参照してください。

POST /api/v1/jobs/createTask
Authorization: Bearer $SPICY_API_KEY
Content-Type: application/json
Idempotency-Key: project-42-scene-07-v1

返ってきた taskId と deadlineAt は、すぐに保存してください。タスク ID は、状態の確認、Webhook との突き合わせ、サポートへの問い合わせ、課金の調査に使う、長く残る識別子です。再試行で同じタスクが返ってくることがありますが、そのときに手元の記録を 2 件目として作ってはいけません。

テキストとストリーミングのエンドポイントでは、同じキーで二重課金は防げますが、受け取れなかった返答を取り戻すことはできません。冪等性を参照してください。

上限を決めて待つ

署名付きの Webhook を優先してください。ポーリングが必要な場合は、約 2 秒から始めて間隔を約 1.5 倍ずつ延ばし、15 秒で頭打ちにして、はっきり決めた実時間の締め切りで止めます。締め切りは、タスク自身の deadlineAt に余裕を足した時刻にしてください。動画は 10 分よりずっと長くかかることがあるからです。プロセスがいつまでもポーリングし続けることがあってはなりません。

上限付きポーリングのひな形
// 現在定義されている 4 つの終端状態。判断の基準は「非同期タスク」のページで、
// queued と running 以外の状態は、ここに載っていないものも含めてすべて終端として扱う。
const KNOWN_TERMINAL = ['succeeded', 'failed', 'canceled', 'expired'];

const deadline = Date.parse(accepted.deadlineAt) + 60_000;
let delay = 2_000;

while (Date.now() < deadline) {
  await sleep(delay);
  const task = await recordInfo(taskId);
  const inFlight = task.state === 'queued' || task.state === 'running';
  const assetsPending = task.state === 'succeeded' && task.output?.assets?.some((a) => a.pending);
  if (!inFlight && !assetsPending) {
    if (!KNOWN_TERMINAL.includes(task.state)) log.warn(`new terminal state: ${task.state}`);
    return task;
  }
  delay = Math.min(Math.round(delay * 1.5), 15_000);
}
throw new Error(`task ${taskId} exceeded the polling deadline`);

この一覧に現在の終端状態を並べているのは、見たことのない状態が来たときに、ひな形が警告を出せるようにするためです。判断そのものは非同期タスクのルールに従います。つまり、queued と running 以外はすべて終端状態です。手元でタイムアウトしても、生成が失敗したことの証明にはなりません。タスク ID を保存しておき、あとで突き合わせてください。

処理する前にコールバックを検証する

Webhook の署名キーを設定します。X-Webhook-Timestamp を検証して古い配信を拒否し、受け取ったリクエスト本文をそのままハッシュして、HMAC 署名を定数時間で比較します。配信の request_id で重複を取り除いてから、data.taskId を使ってタスクを更新します。応答はすぐに返し、処理は非同期で行ってください。完全なアルゴリズムは Webhook にあります。

必要な出力をコピーする

完了したタスクには、すぐに使える URL が output.assets[].url に入っています。ダウンロード URL を別途リクエストせずに、これをそのまま使ってください。残しておきたい結果は、期限が切れる前に自分のストレージにコピーします。必要なら使える download-url エンドポイントは、オブジェクトキーに対して署名付き URL を発行できます。その URL は 20 分で期限が切れます。URL の有効期間とメディアの保存期間は別物です。メディアとデータ保存と破棄を参照してください。

最小限の永続化レコード

再起動したあとでも安全に再開できるだけの状態を保存しておきます。

{
  "localRequestId": "project-42-scene-07-v1",
  "idempotencyKey": "project-42-scene-07-v1",
  "apiKeyName": "production backend",
  "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
  "model": "MODEL_ID_FROM_CATALOG",
  "approvedCost": "0.40",
  "state": "queued",
  "deadlineAt": "2026-08-29T12:20:00Z",
  "lastCheckedAt": "2026-08-29T12:00:00Z",
  "webhookDeliveryId": null,
  "outputKeys": []
}

MODEL_ID_FROM_CATALOG は、認証付きのライブカタログから選んだ正確な ID を表しており、そのまま送る文字列ではありません。どの API キーでタスクを作成したかを記録してください。そのタスクを読み取ったり再試行したりできるのは、そのキーだけです。署名付きのダウンロード URL を、長く使う識別子にしてはいけません。HTTP の 200 から成功と判断してもいけません。data.state を読んでください。

お金を使う前にモックで試す

非同期タスク、Webhook、エラーに書かれている応答の外枠に沿って、仕様どおりに振る舞うモックを手元に作ります。テスト用データには、次のケースを含めてください。

  • queued → running → succeeded
  • code: 200 かつ data.state: "failed" の、終端状態としての失敗
  • 同じ taskId を返す、重複した createTask の応答
  • もう一致しなくなった見積もり(40901)と、残高不足(40201)
  • 正しい Webhook、無効な署名、再送された配信
  • 429、503、通信のタイムアウト、ポーリングの締め切りを超えるタスク
  • 発行し直す必要がある、期限切れのダウンロード URL

モックで確かめられるのは処理の組み立てであって、モデルの品質ではありません。公開する前に、本番で使うのとまったく同じモデルと入力の形で、小さな実際のタスクを実行してください。

受け入れチェックリスト

  • 秘密情報はサーバー側の秘密情報ストレージにだけ置かれ、ログでは伏せられている。
  • 組み込みは、フィールドを推測せず、現在のモデルのスキーマを取得している。
  • 課金される呼び出しの前に、見積もられた価格を人が承認し、送信には quoteId と expectedCost が付いている。
  • 論理的に 1 つのリクエストには、保存済みの Idempotency-Key が 1 つあり、常に同じ API キーと一緒に送られる。
  • 処理を先に進める前に taskId が保存され、プロセスが再起動しても失われない。
  • ポーリングは、待ち時間を延ばす方式(バックオフ)、最大間隔、実時間の締め切りを使っている。
  • Webhook では、タイムスタンプ、生の本文のダイジェスト、HMAC を検証してから、配信 ID で重複を取り除いている。
  • 終端状態での失敗は、HTTP ステータスや外枠の code ではなく、data.state から読み取っている。
  • 必要な出力は、URL や保存期間の期限が切れる前に、自分のストレージにコピーされている。taskId とオブジェクトキーは突き合わせのために残しておくもので、保存期間が終わったあとにファイルを取得する手段としてあてにはしていない。
  • ログには、プロンプトや秘密情報を含めずに、request_id、taskId、モデル、試行回数が残っている。
  • モックのテストが、重複作成、再送、タイムアウト、レート制限、期限切れのメディアを網羅している。

エージェントに求める引き継ぎ内容

エージェントには、選んだモデルのエンドポイント、スキーマのバージョンまたは取得時刻、価格承認のステップ、冪等キーの方針、ポーリングの締め切り、Webhook の検証計画、そして完了したチェックリストを返すように依頼してください。そうすれば、「組み込み完了」という言葉が、あなたが確認できる証拠に変わります。

このページの内容