◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-04-30上級

Rork × AIモデレーション実装:UGCアプリで通報・自動審査・段階的制裁を本番投入する

Rork で作ったUGCアプリに、AIモデレーション・通報・段階的制裁を本番品質で組み込む実践ガイド。OpenAI Moderation・Perspective API・Cloudflare Workers Queueで設計する3層パイプラインを、コード付きで解説します。

Rork585UGCコンテンツモデレーション3OpenAI ModerationPerspective APICloudflare Workers25Trust and Safety

✦ プレミアム記事

UGCアプリを Rork で初めて App Store に出した翌週、通報メールが30件以上届きました。個人開発で運営しているコミュニティアプリでしたが、想定していなかったタイプの不適切投稿(自傷示唆を含むテキスト、子どもの写真の無断アップロード、有名人の名前を使ったなりすまし)が次々と現れ、対応に1日6時間かかった日もありました。

その経験から学んだのは「モデレーションを最初から3層で設計しておかないと、リリース直後に手作業でつぶれる」ということです。ここではRork で作るUGCアプリに、事前チェック・投稿時の自動審査・事後の通報処理を本番品質で組み込む実装を、私が実際に運用しているコード(Cloudflare Workers + Queue + KV)と共に解説します。

OpenAI Moderation API の使いどころと限界、Perspective API との使い分け、AppleとGoogleのガイドラインで明文化されている要件、DSA(EU デジタルサービス法)と日本の青少年保護条例で求められる対応まで、個人開発者が実装段階で必要になる情報を一気にまとめました。

モデレーションは「事前 / 投稿時 / 事後」の3層で考える

最初のリリースで失敗したのは、すべての判定を「投稿時の自動審査」だけに集約しようとしたことでした。1層に集中させると、APIコストが膨らむ・誤判定の影響範囲が広い・通報後の追加対応ができない、という3つの問題が同時に出ます。

私は今、すべてのUGC機能を以下の3層で設計するようにしています。

  • 事前(Client-side preflight): ユーザーが投稿ボタンを押す前に、明らかにNGなパターン(空文字、規定文字数超過、URL のみ、絵文字スパム等)をクライアント側で弾く。これでサーバー負荷の40〜60%を削減できます。
  • 投稿時(Server-side moderation): 投稿が API に届いた時点で、AIモデレーションAPIに非同期で投げ、スコアに応じて「即時公開 / 保留(レビューキュー) / 即時拒否」の3分岐に分ける。
  • 事後(Post-publication): 公開済みコンテンツに対する通報・追加学習・人間レビュー結果の反映。法令上必要な「アピール窓口」もここに含める。

この3層を Cloudflare Workers の Queue で繋ぐと、コストとレスポンス速度のバランスが取りやすくなります。投稿時はQueueへenqueueだけ即時返し、Workerが裏で判定してKVに結果を書き、フロントは結果が確定するまで「審査中」の状態を表示する、という構成です。

事前チェック:Rork クライアント側のpreflightバリデーション

Rork で作る投稿フォームは React Native コンポーネントになるため、TextInput と ImagePicker の段階で軽量チェックを噛ませます。これは「攻撃」を防ぐためではなく、明らかなノイズを99%減らすためのものです。

// app/components/PostComposer.tsx
import { useState } from "react";
import { TextInput, View, Pressable, Text } from "react-native";
 
const NG_PATTERNS = [
  /^https?:\/\/\S+$/,                    // URLのみの投稿は禁止
  /(.)\1{20,}/,                          // 同じ文字を21回以上繰り返す
  /[\u{1F300}-\u{1F9FF}]{30,}/u,         // 絵文字を30個以上連続
];
 
const MIN_CHARS = 2;
const MAX_CHARS = 1000;
 
type PreflightResult =
  | { ok: true }
  | { ok: false; reason: string };
 
function preflight(text: string): PreflightResult {
  const trimmed = text.trim();
  if (trimmed.length < MIN_CHARS) {
    return { ok: false, reason: "投稿は2文字以上必要です" };
  }
  if (trimmed.length > MAX_CHARS) {
    return { ok: false, reason: `投稿は${MAX_CHARS}文字以下にしてください` };
  }
  for (const pattern of NG_PATTERNS) {
    if (pattern.test(trimmed)) {
      return { ok: false, reason: "投稿の形式が不正です" };
    }
  }
  return { ok: true };
}
 
export function PostComposer({ onSubmit }: { onSubmit: (text: string) => void }) {
  const [text, setText] = useState("");
  const [error, setError] = useState<string | null>(null);
 
  const handleSubmit = () => {
    const result = preflight(text);
    if (!result.ok) {
      setError(result.reason);
      return;
    }
    setError(null);
    onSubmit(text.trim());
    setText("");
  };
 
  return (
    <View>
      <TextInput
        value={text}
        onChangeText={setText}
        multiline
        placeholder="今、何が起きていますか?"
        maxLength={MAX_CHARS + 100}
      />
      {error && <Text style={{ color: "#d33" }}>{error}</Text>}
      <Pressable onPress={handleSubmit}>
        <Text>投稿する</Text>
      </Pressable>
    </View>
  );
}

このpreflightは「サーバー側のバリデーションを置き換えるためのもの」ではない点に注意してください。クライアントは改ざん可能なため、サーバー側でも同じチェックを通すことが前提です。preflightの目的はあくまでUX(無駄なAPI呼び出しを抑え、即座にエラー表示する)とサーバー負荷の軽減です。

なぜこの3パターンを最初に入れているかというと、UGCの初期リリースで実際に集中して発生するパターンだからです。URL Only投稿はスパム業者の偵察、文字繰り返しはスクリプトキディの動作テスト、絵文字スパムは10代ユーザーの「炎上参加」の典型形です。1日5,000投稿のうち約30%がこのパターンに該当した時期があり、preflightだけでサーバー負荷を3割削減できました。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦UGCアプリで通報や手動削除に追われていた個人開発者が、自動モデレーションパイプラインを今日から本番投入できるようになる
✦OpenAI Moderation・Perspective API・独自モデルの使い分けを理解し、自社プロダクトに最適な構成を設計できる
✦DSA・こども保護・Apple/Google ガイドラインに準拠した「事前 / 投稿時 / 事後」3層モデレーションを、Rork + Cloudflare Workers の具体的なコードとして手に入れられる
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-05-09
Rork の A/B テスト基盤を GrowthBook と PostHog でゼロから組む
Firebase に頼らず、GrowthBook と PostHog をオープンソースで組み合わせて Rork アプリの A/B テスト基盤を構築する手順を、SDK 統合からベイズ統計の解釈、運用上の落とし穴までまとめました。
⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。
⬡ 開発ツール2026-10-13
リリース前に、書き出した Expo バンドルの中から自分の鍵を探してみる
ソースに鍵がなくても、出来上がったアプリの中に鍵が残ることがあります。Expo SDK 57 で実際に expo export を実行し、バンドルの中身を grep と 60 行ほどの Node スクリプトで調べて、見つかる鍵と見つからない鍵の違いを確かめました。