jpskill.com
📦 その他 コミュニティ

ai-handler

ReplicateのAIモデルと連携し、バックグラウンド処理やS3ストレージ、クレジットシステムを統合することで、AI機能を効率的にビジネスへ組み込み、自動化を促進するSkill。

📜 元の英語説明(参考)

Integrate Replicate AI models with background processing, S3 storage, and credit systems

🇯🇵 日本人クリエイター向け解説

一言でいうと

ReplicateのAIモデルと連携し、バックグラウンド処理やS3ストレージ、クレジットシステムを統合することで、AI機能を効率的にビジネスへ組み込み、自動化を促進するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o ai-handler.zip https://jpskill.com/download/16896.zip && unzip -o ai-handler.zip && rm ai-handler.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16896.zip -OutFile "$d\ai-handler.zip"; Expand-Archive "$d\ai-handler.zip" -DestinationPath $d -Force; ri "$d\ai-handler.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して ai-handler.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → ai-handler フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Replicate AI Handler Skill

この Skill は、Replicate AI モデルを統合するための、本番環境に対応したパターンを提供します。Inngest のバックグラウンドジョブを使用して長時間実行される予測を処理し、結果を S3 に保存し、ユーザーのクレジットを管理し、データベースの状態を更新します。

アーキテクチャ

  1. トリガー: ユーザーが API 経由で生成をリクエストします (例: /api/app/ai-images)。
  2. バリデーション: ユーザーのクレジットを確認/差し引きます。
  3. 状態: status: "processing" でデータベースレコードを作成します。
  4. キュー: Replicate API 呼び出しを処理するために、Inngest 関数をトリガーします。
  5. 処理:
    • Replicate API を呼び出します。
    • 完了を待ちます (ポーリングまたは webhook)。
    • 結果をダウンロードし、S3 にアップロードします (サーバーサイド)。
  6. 完了: S3 URL と status: "completed" でデータベースレコードを更新します。
  7. 失敗: 失敗した場合、クレジットを払い戻し (オプション)、ステータスを failed に更新します。

前提条件

  • replicate パッケージがインストールされていること (npm install replicate)。
  • .envREPLICATE_API_TOKEN が設定されていること。
  • S3 と Inngest が設定されていること。

実装手順

1. API ルート (トリガー)

src/app/api/app/generate/route.ts

import withAuthRequired from "@/lib/auth/withAuthRequired";
import { db } from "@/db";
import { generations } from "@/db/schema";
import { inngest } from "@/lib/inngest/client";
import { checkCredits, deductCredits } from "@/lib/credits"; // 仮のヘルパー関数

export const POST = withAuthRequired(async (req, { session }) => {
  const body = await req.json();

  // 1. クレジットの確認
  const hasCredits = await checkCredits(session.user.id, "image_generation", 1);
  if (!hasCredits) return new Response("Insufficient credits", { status: 403 });

  // 2. DB レコードの作成 (保留中)
  const [record] = await db.insert(generations).values({
    userId: session.user.id,
    prompt: body.prompt,
    status: "processing",
  }).returning();

  // 3. クレジットの差し引き (楽観的)
  await deductCredits(session.user.id, "image_generation", 1, { source: "api", refId: record.id });

  // 4. バックグラウンドジョブのトリガー
  await inngest.send({
    name: "app/ai.generate",
    data: {
      generationId: record.id,
      prompt: body.prompt,
      userId: session.user.id
    }
  });

  return Response.json({ id: record.id, status: "processing" });
});

2. Inngest 関数 (プロセッサ)

src/lib/inngest/functions/app/ai/generate.ts

import { inngest } from "@/lib/inngest/client";
import Replicate from "replicate";
import uploadFromServer from "@/lib/s3/uploadFromServer";
import { db } from "@/db";
import { generations } from "@/db/schema";
import { eq } from "drizzle-orm";

const replicate = new Replicate({ auth: process.env.REPLICATE_API_TOKEN });

export const generateAI = inngest.createFunction(
  { id: "ai-generation-worker", concurrency: 5 },
  { event: "app/ai.generate" },
  async ({ event, step }) => {
    const { generationId, prompt } = event.data;

    try {
      // 1. Replicate の呼び出し (Step によりネットワークエラー時の再試行が保証されます)
      const prediction = await step.run("call-replicate", async () => {
        return await replicate.predictions.create({
          version: "model-version-hash",
          input: { prompt }
        });
      });

      // 2. 完了を待機
      // Replicate は通常時間がかかります。webhook を使用している場合は waitForEvent を使用できます。
      // webhook が設定されていない場合は、sleep を使用した単純なポーリングループを使用できます。
      // 簡単にするために、sleep を使用したポーリングパターンを以下に示します。
      let finalPrediction = prediction;
      while (finalPrediction.status !== "succeeded" && finalPrediction.status !== "failed") {
        await step.sleep("wait-for-gpu", "5s");
        finalPrediction = await step.run("check-status", () => 
          replicate.predictions.get(prediction.id)
        );
      }

      if (finalPrediction.status === "failed") {
        throw new Error(finalPrediction.error);
      }

      // 3. S3 へのアップロード
      // Replicate は一時 URL を返します。永続化する必要があります。
      const outputUrl = finalPrediction.output[0]; // モデルの出力に基づいて調整

      const s3Url = await step.run("upload-to-s3", async () => {
        // イメージバッファの取得
        const response = await fetch(outputUrl);
        const arrayBuffer = await response.arrayBuffer();
        const base64 = Buffer.from(arrayBuffer).toString("base64");

        // 既存の S3 skill を使用
        return await uploadFromServer({
          file: base64,
          path: `generations/${generationId}.png`,
          contentType: "image/png"
        });
      });

      // 4. DB の更新
      await step.run("update-db", async () => {
        await db.update(generations)
          .set({ status: "completed", url: s3Url })
          .where(eq(generations.id, generationId));
      });

    } catch (error) {
      // 失敗の処理
      await step.run("mark-failed", async () => {
        await db.update(generations)
          .set({ status: "failed" })
          .where(eq(generations.id, generationId));

        // オプション: ここでクレジットを払い戻す
      });
      throw error; // Inngest ダッシュボードで失敗を表示するために再スロー
    }
  }
);
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Replicate AI Handler Skill

This skill provides a production-ready pattern for integrating Replicate AI models. It handles long-running predictions using Inngest background jobs, stores results in S3, manages user credits, and updates database state.

Architecture

  1. Trigger: User requests a generation via API (e.g., /api/app/ai-images).
  2. Validation: Check/deduct user credits.
  3. State: Create a database record with status: "processing".
  4. Queue: Trigger an Inngest function to handle the Replicate API call.
  5. Processing:
    • Call Replicate API.
    • Wait for completion (polling or webhook).
    • Download result and upload to S3 (server-side).
  6. Completion: Update database record with S3 URL and status: "completed".
  7. Failure: Refund credits if failed (optional) and update status to failed.

Prerequisites

  • replicate package installed (npm install replicate).
  • REPLICATE_API_TOKEN in .env.
  • S3 and Inngest configured.

Implementation Steps

1. API Route (Trigger)

src/app/api/app/generate/route.ts

import withAuthRequired from "@/lib/auth/withAuthRequired";
import { db } from "@/db";
import { generations } from "@/db/schema";
import { inngest } from "@/lib/inngest/client";
import { checkCredits, deductCredits } from "@/lib/credits"; // Hypothetical helpers

export const POST = withAuthRequired(async (req, { session }) => {
  const body = await req.json();

  // 1. Check Credits
  const hasCredits = await checkCredits(session.user.id, "image_generation", 1);
  if (!hasCredits) return new Response("Insufficient credits", { status: 403 });

  // 2. Create DB Record (Pending)
  const [record] = await db.insert(generations).values({
    userId: session.user.id,
    prompt: body.prompt,
    status: "processing",
  }).returning();

  // 3. Deduct Credits (Optimistic)
  await deductCredits(session.user.id, "image_generation", 1, { source: "api", refId: record.id });

  // 4. Trigger Background Job
  await inngest.send({
    name: "app/ai.generate",
    data: {
      generationId: record.id,
      prompt: body.prompt,
      userId: session.user.id
    }
  });

  return Response.json({ id: record.id, status: "processing" });
});

2. Inngest Function (Processor)

src/lib/inngest/functions/app/ai/generate.ts

import { inngest } from "@/lib/inngest/client";
import Replicate from "replicate";
import uploadFromServer from "@/lib/s3/uploadFromServer";
import { db } from "@/db";
import { generations } from "@/db/schema";
import { eq } from "drizzle-orm";

const replicate = new Replicate({ auth: process.env.REPLICATE_API_TOKEN });

export const generateAI = inngest.createFunction(
  { id: "ai-generation-worker", concurrency: 5 },
  { event: "app/ai.generate" },
  async ({ event, step }) => {
    const { generationId, prompt } = event.data;

    try {
      // 1. Call Replicate (Step ensures retries on network error)
      const prediction = await step.run("call-replicate", async () => {
        return await replicate.predictions.create({
          version: "model-version-hash",
          input: { prompt }
        });
      });

      // 2. Wait for completion
      // Replicate usually takes time. We can use waitForEvent if using webhooks, 
      // or simple polling loop with sleep if webhooks aren't set up.
      // For simplicity, here is a polling pattern using sleep:
      let finalPrediction = prediction;
      while (finalPrediction.status !== "succeeded" && finalPrediction.status !== "failed") {
        await step.sleep("wait-for-gpu", "5s");
        finalPrediction = await step.run("check-status", () => 
          replicate.predictions.get(prediction.id)
        );
      }

      if (finalPrediction.status === "failed") {
        throw new Error(finalPrediction.error);
      }

      // 3. Upload to S3
      // Replicate returns a temporary URL. We must persist it.
      const outputUrl = finalPrediction.output[0]; // Adjust based on model output

      const s3Url = await step.run("upload-to-s3", async () => {
        // Fetch image buffer
        const response = await fetch(outputUrl);
        const arrayBuffer = await response.arrayBuffer();
        const base64 = Buffer.from(arrayBuffer).toString("base64");

        // Use existing S3 skill
        return await uploadFromServer({
          file: base64,
          path: `generations/${generationId}.png`,
          contentType: "image/png"
        });
      });

      // 4. Update DB
      await step.run("update-db", async () => {
        await db.update(generations)
          .set({ status: "completed", url: s3Url })
          .where(eq(generations.id, generationId));
      });

    } catch (error) {
      // Handle Failure
      await step.run("mark-failed", async () => {
        await db.update(generations)
          .set({ status: "failed" })
          .where(eq(generations.id, generationId));

        // Optional: Refund credits here
      });
      throw error; // Re-throw to show failure in Inngest dashboard
    }
  }
);