## 概要

AIアシスタントは、ドキュメントサイトにチャットダイアログを追加します。ユーザーはヘッダーのスパークルアイコンをクリックしてダイアログを開き、ドキュメントの内容について質問できます。

アシスタントは[llms.txtインテグレーション](https://zudo-doc.takazudomodular.com/ja/docs/guides/llms-txt)で生成されたドキュメント全文をコンテキストとして利用するため、サイト上のあらゆるページに関する質問に答えられます。

## アシスタントの有効化

`zfb.config.ts`で`aiAssistant`を`true`に設定します：

```ts title="zfb.config.ts"
export default defineConfig(
  zudoDoc({
    aiAssistant: true,
    // ...
  }),
);
```

有効にすると：

- ヘッダーバーにスパークルアイコンが表示されます
- `POST /api/ai-chat`エンドポイントがCloudflare WorkersのSSRルートとして利用可能になります

[Dynamic content unavailable in text export]

以下で説明するproduction handler、`worker-entry.ts`、`AiChatDailySpendCap` classはこの
showcase repositoryが所有しており、`@takazudo/zudo-doc`からexportされたりfresh scaffoldへ
追加されたりしません。package-owned SSR routeは安全なseamで、demo modeでは固定応答を返し、
host handlerなしでlive modeを選ぶとHTTP 501を返します。downstream projectはdemo modeを維持するか、
host側の`/api/ai-chat` handler、source Worker entry、Durable Object class、migrationを実装する必要が
あります。bindingを追加するだけではlive AIは有効になりません。



## デモモード無効化時のセキュリティ警告

[Dynamic content unavailable in text export]

`aiChatDemoMode: false` を設定すると、実際のAnthropicのAPIキーを使うエンドポイントが公開されます。
設定を誤ると、認証なしのAPIコールによるコスト悪用のリスクがあります。本番稼働前に以下を確認してください：

1. **CORSオリジンを制限する** — `aiChatAllowedOrigins` にデプロイ済みのドキュメントサイトのURL
   を設定します。デフォルトの空配列はすべてのクロスオリジンブラウザリクエストをブロックします。
2. **有料呼び出しの正確な上限を設定する** — `aiChatGlobalDailyLimit` で、全IPを合算した
   UTC日ごとのAnthropic fetch試行数を制限します。SQLite Durable Objectが同時判定を直列化するため、
   設定値を超過しません。
3. **Cloudflare経由でデプロイする** — IPごとのレート制限に使用される `cf-connecting-ip` は、
   Cloudflareのネットワークを経由したリクエストにのみ信頼できます。

`aiChatDemoMode` が `false` の場合は両方のガードがフェイルクローズです。IPごとのKV読み取り
障害はHTTP 429を返し、正確な上限のバインディング欠落やRPC障害はAnthropic呼び出し前に
HTTP 500を返します。



```ts title="zfb.config.ts"
export default defineConfig(
  zudoDoc({
    aiChatDemoMode: false,
    // Allow only your deployed docs site to make cross-origin requests.
    aiChatAllowedOrigins: ["https://your-docs-site.example.com"],
    // Exact UTC-day paid-call admission cap (false = no exact cap).
    aiChatGlobalDailyLimit: 500,
    // ...
  }),
);
```

## 環境設定

このshowcase repositoryでは、チャットエンドポイントはCloudflare WorkersのSSRルート
（`pages/api/ai-chat.tsx`）として動作し、`wrangler.toml`のCloudflare環境bindingから設定を
読み込みます。downstream実装はこのgraphを参照できますが、これらhost-owned fileはpackage APIではありません。

### 必要なCFバインディング

**シークレット** — `wrangler secret put ANTHROPIC_API_KEY`で設定します：

```env
ANTHROPIC_API_KEY=sk-ant-...
```

**変数** — `wrangler.toml`で設定します：

```toml title="wrangler.toml"
[vars]
DOCS_SITE_URL = "https://your-docs-site.workers.dev"
RATE_LIMIT_PER_MINUTE = "10"
RATE_LIMIT_PER_DAY = "100"
```

**KV名前空間** — ソフトなIPごとのレート制限とprivacy-safeな結果監査record用（prompt、response、
IP、IP hashフィールドは保存しません）：

```sh
wrangler kv namespace create RATE_LIMIT
```

返却された`id`を`wrangler.toml`の`[[kv_namespaces]]`ブロックに貼り付けます。

**Durable Object名前空間** — UTC日ごとの正確な有料呼び出し許可判定：

```toml title="wrangler.toml"
[[durable_objects.bindings]]
name = "AI_CHAT_DAILY_SPEND_CAP"
class_name = "AiChatDailySpendCap"

[[migrations]]
tag = "v1-ai-chat-daily-spend-cap"
new_sqlite_classes = ["AiChatDailySpendCap"]
```

これらの名前は正確に維持してください。最初の`wrangler deploy`がWorker migrationを適用します。
これは`wrangler d1 migrations`ではありません。本番のカスタム`worker-entry.ts`は、生成された
`dist/_worker.js`のアダプターハンドラー、`dist/_zfb_inner.mjs`サイドカー、Durable Objectクラスを
すべて維持します。Wranglerの前には必ずビルドしてください。

UTC日ごとに新しいオブジェクトへルーティングするため、上限は`00:00 UTC`にリセットされます。
許可判定はリクエスト検証、よりソフトで結果整合なIPごとのKVガード、ドキュメントコンテキスト
準備の後、ちょうど1回のAnthropic fetch直前に行われます。fetch失敗時も許可枠は返却されず、
プロバイダーが確定した請求数ではありません。`aiChatGlobalDailyLimit: false`は正確な上限だけを
無効化し、`aiChatDemoMode: true`はKV、Durable Object、Anthropicをすべて迂回します。

Durable Objectsを実装するversionにはプレビューURLが生成されないため、プレビューエイリアスは
アダプター専用のサービス／設定を使います。検証対象はSSRアダプターとassets経路で、有料呼び出し
許可判定ではありません。migrationの適用は本番の`wrangler deploy`が担います。Cloudflareの
[Durable Object migrations](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/)
と[Cloudflare WisdomのDurable Objects記事](https://github.com/Takazudo/zudo-cloudflare-wisdom/blob/main/src/content/docs/workers/durable-objects.mdx)、
[Workers Logsのstructured-objectガイド](https://developers.cloudflare.com/workers/observability/logs/workers-logs/)も参照してください。

応答の高速性とコストを抑えるため、`claude-haiku-4-5-20251001`を使用しています。

## チャットダイアログ

ダイアログはネイティブの`<dialog>`要素を使ったpackage-ownedのクライアントアイランド（`@takazudo/zudo-doc/ai-chat-modal`）です。このshowcaseでは`pages/lib/_body-end-islands.tsx`（`BodyEndIslands`のchrome-bindings slot）経由でマウントしています。

### レイアウト

- **狭いビューポート**（`lg`/1024px未満）：ビューポートの幅と高さいっぱいに表示
- **広いビューポート**（1024px以上）：中央配置、90vw/90vhでmax-widthは52.5rem、ボーダー付き

### 機能

- 吹き出し風のメッセージバブル（ユーザーは右、アシスタントは左）
- アシスタント応答内のマークダウンレンダリング（太字、イタリック、コード、リスト、リンク）
- API呼び出し中の「Thinking...」インジケーター
- エラーメッセージのインライン表示
- 背景クリックまたはEscapeキーで閉じる
- ダイアログを閉じると会話はリセットされる

## APIリファレンス

エンドポイントの詳細仕様（リクエスト／レスポンスの型、エラーコード、環境変数）は[AI Assistant APIリファレンス](https://zudo-doc.takazudomodular.com/ja/docs/reference/ai-assistant-api)を参照してください。

## ファイル構成

```
pages/
└── api/
    ├── ai-chat.tsx               # CF Workers SSR endpoint (prerender = false)
    ├── _ai-chat-admission.ts     # Exact UTC-day paid-call admission cap
    ├── _ai-chat-audit.ts         # Privacy-safe outcome audit logging
    ├── _ai-chat-client.ts        # Claude API client (raw fetch)
    ├── _ai-chat-cors.ts          # CORS origin allowlist
    ├── _ai-chat-observability.ts # Structured operational logging
    ├── _ai-chat-payload.ts       # Request/response payload builders
    ├── _ai-chat-rate-limit.ts    # Per-IP KV rate limiting
    ├── _ai-chat-screening.ts     # Prompt-injection screening
    └── _ai-chat-types.ts         # Shared route types
src/
└── types/
    └── ai-chat.ts                # ChatMessage, AiChatRequest/Response types
worker-entry.ts                   # Production Worker entry (wraps dist/_worker.js)
```

チャットダイアログのアイランド（`@takazudo/zudo-doc/ai-chat-modal`）と、チャットメッセージ用の
マークダウンレンダラー（`@takazudo/zudo-doc/render-markdown`）はpackage-ownedであり、
このリポジトリのファイルツリーには含まれません。