zudo-doc
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

AI Assistant API

作成 2026年5月10日更新 2026年7月24日Takeshi Takatsudo
タグ:#ai

AIアシスタントのチャットエンドポイントのAPI仕様です。

showcase実装の境界

このreferenceはzudo-doc showcase repositoryのhost-ownedなlive実装を説明します。@takazudo/zudo-doc package/scaffoldは、このhandler、worker-entry.tsAiChatDailySpendCapを出荷しません。host実装なしでlive modeを選ぶとpackage-owned routeは HTTP 501を返します。downstream projectは同等のhandlerとWorker graphを用意する必要があります。

エンドポイント

POST /api/ai-chat
Content-Type: application/json

リクエストボディ

interface AiChatRequest {
  message: string;
  history?: ChatMessage[];
}

interface ChatMessage {
  role: "user" | "assistant";
  content: string;
}
フィールド必須説明
messagestringはいユーザーの現在のメッセージ。空でないこと。
historyChatMessage[]いいえ過去の会話メッセージ。不正な形式(非配列、50件超、コンテンツが8192文字超、またはインジェクション一致)の場合はHTTP 400で拒否。

成功レスポンス (200)

interface AiChatResponse {
  response: string;
}

responseフィールドはアシスタントの返答をマークダウン文字列として含みます。

例:

// Request
{
  "message": "How do I add a new page?",
  "history": []
}

// Response
{
  "response": "Create an MDX file in `src/content/docs/`:\n\n1. Add frontmatter with `title`\n2. Write your content in MDX\n3. The page appears in the sidebar automatically"
}

エラーレスポンス (400 / 500)

interface AiChatErrorResponse {
  error: string;
}
ステータス条件
400無効なJSONボディ
400messageが空でない文字列ではない
400messageが4000文字の制限を超えている
400入力スクリーニング(プロンプトインジェクションガード)で拒否された
400historyの形式が不正(フィールド説明を参照)
405リクエストメソッドがPOSTでもOPTIONSでもない
415Content-Typeがapplication/jsonではない
429レート制限超過(Retry-Afterヘッダーを含む)
500Anthropic APIコール失敗

このエンドポイントはPOST(チャット)とOPTIONS(CORSプリフライト)のみを受け付けます。それ以外のメソッドはすべて{ "error": "Method not allowed" }とともに405を返します。

CORS

このエンドポイントはオリジンごとの許可リストを使用します。aiChatDemoModefalseの場合、Access-Control-Allow-OriginaiChatAllowedOrigins設定にリストされたリクエストオリジンに対してのみエコーされます。それ以外のオリジンにはallow-originヘッダーが返されず、ブラウザによってブロックされます。(デモモードでは後方互換性のため常に*が返されます。)これはワイルドカードCORS(*)を使用するSearch Workerよりも意図的に厳格です。各コールに実際のAnthropic APIコストが伴うAIチャットエンドポイントはオリジンでゲートしますが、検索はメーター課金のないオプトインサービスだからです。2つのエンドポイントが同じCORSポリシーを共有していると仮定しないでください。

CF環境バインディング

バインディング種別必須説明
ANTHROPIC_API_KEYsecretはいAnthropic APIキー
DOCS_SITE_URLvarはいデプロイ済みのドキュメントサイトURL(llms-full.txtの取得に使用)
RATE_LIMITKV namespaceはいソフトなIPごとのカウンタとprivacy-safeな結果監査recordを保存
AI_CHAT_DAILY_SPEND_CAPDurable Object namespaceaiChatGlobalDailyLimitが数値の場合正確な有料呼び出し許可。クラスAiChatDailySpendCap、SQLite migration v1-ai-chat-daily-spend-cap
RATE_LIMIT_PER_MINUTEvarいいえIPあたりの1分間の最大リクエスト数(デフォルト10
RATE_LIMIT_PER_DAYvarいいえIPあたりの1日の最大リクエスト数(デフォルト100

設定

以下のzfb.config.ts内のzudoDoc({...})フィールドがエンドポイントの動作を制御します(上記のCF環境変数とは 別のもので、こちらはCloudflare側のランタイム設定です)。

設定デフォルト説明
aiChatDemoModebooleanfalse(ショーケース: true固定返答でショートサーキット。APIキー、KV、Durable Object、provider fetchにアクセスしない
aiChatAllowedOriginsstring[][]CORSオリジン許可リスト(非デモ時のみ有効)。空配列 = 全クロスオリジンリクエストをブロック
aiChatGlobalDailyLimitnumber | falsefalseUTC日ごとの正確なAnthropic fetch許可上限。false = 正確な上限なし

セキュリティ

このエンドポイントは、レガシーのスタンドアロンWorkerから移植された多層的な防御を備えています:

  • ハードニングされたシステムプロンプト — XMLタグでコンテキストを区切り、明示的なガードレールによってモデルが設定情報を漏洩したり、トピック外の指示に従ったりすることを防ぎます。さらに、過去の会話ターンはすべてクライアント由来の信頼できない入力として扱うようモデルに指示します(下記の チャット履歴の信頼モデル を参照)

  • 入力スクリーニング — 一般的なプロンプトインジェクションパターンを、どちらのリミッターやClaude APIよりも先に正規表現で検査します。その後、validation rejectionを返す前にIPごとのKVガードを実行するため、拒否された入力も近似的なIPごとのquotaを消費しますが、audit writeはそのガードより後ろに保たれます

  • IPごとのソフトガードRATE_LIMIT KVを使う近似的で結果整合な制限。aiChatDemoModefalse の場合は フェイルクローズ(KV障害時 → HTTP 429)。デモモードではフェイルオープン(デモのショートサーキットが先に実行されるため、実際にはレートリミッターには到達しない)

  • CORSオリジン許可リスト — 非デモ時、Access-Control-Allow-OriginaiChatAllowedOrigins に含まれるオリジンにのみエコーされます。リスト外のオリジンからのクロスオリジンリクエストはブラウザでブロックされます。デモモードは後方互換性のため常に * を送信します

  • 正確な有料呼び出し許可 — UTC日ごとのSQLite AiChatDailySpendCapが、ちょうど1回のAnthropic fetch直前の許可判定を直列化します。名前空間はAI_CHAT_DAILY_SPEND_CAP、Worker migration tagは v1-ai-chat-daily-spend-cap、設定はnew_sqlite_classes = ["AiChatDailySpendCap"]です。上限超過は 次のUTC日まで429、binding/RPC/storage障害は500でフェイルクローズします。provider/network障害でも 許可枠は返却せず、providerが確定した請求数ではありません

  • Privacy-safeな監査recordRATE_LIMIT KVのaudit:にはtimestamp、completed/blocked outcome、任意の限定されたblock-reason enumだけを保存します(7日のTTL、fire-and-forget)。prompt/response text、IP、IP hashは永続化しません

  • メッセージ長制限 — 4000文字を超えるメッセージはAPIに到達する前に拒否されます

  • cf-connecting-ipの注意点 — IPごとのレート制限にはこのヘッダーを使用しており、WorkerがCloudflareのネットワーク経由でデプロイされている場合にのみ信頼できます

運用ログ

Wranglerの[observability] enabled = trueにより、Workers Logsへ正確な上限のadmitteddeniedfailed_closedをclosed-schemaオブジェクトとして記録します。各イベントにはutc_dayconfigured_limitと非機密な結果フィールドだけが含まれます。別のper_ip_kvイベントで、通常の 上限拒否とKV障害によるフェイルクローズを区別できます。prompt、response、生IP、IP hash、secret、 Durable Object名/ID、raw error textは記録しません。

チャット履歴の信頼モデル

history配列はクライアント由来かつステートレスです。サーバーはセッション記録を一切保持しないため、assistantロールのターンが実際に過去のモデル応答によって生成されたものかを検証できません。各エントリは依然としてハードニングされています。すなわち、user/assistantへの厳格なロールホワイトリスト、上記のエントリ件数および1エントリあたりの長さ制限、そして紛れ込んだ余分なフィールドを取り除く{ role, content }への再構築です。userロールのターンはインジェクションスクリーニングされますが、assistantロールのターンはされません(本物のアシスタント応答はインジェクションのような文面を正当に引用しうるためです)。

ロールが検証不可能であるため、呼び出し元は敵対的な指示を含むassistantターンを偽造してユーザーターンのスクリーニングを回避できます。この残存リスクは意図的に受容しています。本チャットはブラスト半径の小さいドキュメントアシスタントであり、システムプロンプトはすべての過去ターンを、自身のルールを上書きできない信頼できない入力として扱うようモデルに指示しているためです。堅牢な対策(サーバー発行の署名付き履歴)はシークレットのプロビジョニングとクライアント/サーバー間ペイロード契約の変更を必要とし、この機能には見合いません。決定の全記録はissue #2036を参照してください。

ドキュメントコンテキスト

このエンドポイントは、llms.txtインテグレーションで生成されたllms-full.txtDOCS_SITE_URLから取得し、CF Workersのアイソレートが生きている間メモリにキャッシュします(ベストエフォート、約1時間)。取得したコンテンツはシステムプロンプトに<documentation>XMLコンテキストとして含められます。

Revision History

Takeshi Takatsudo作成: 2026-05-10T21:56:58+09:00更新: 2026-07-25T03:26:37+09:00

AI Assistant

Ask a question about the documentation.

Preview theme

Loading theme previews…