TL;DR (English): A reference implementation of "Concierge" — an AI chat widget that lives on every Storybook story and answers questions about the component currently on screen. Works out of the box with a built-in FAQ (no API key required); plug in an OpenAI / Gemini key for free-form AI answers. Ships with a backend-proxy design so shared builds never contain credentials. MIT licensed.
Storybook の全 story に常駐する AI チャット「Concierge」のリファレンス実装です。 画面右下の FAB からチャットを開くと、いま表示している story の文脈(title / name / description / argTypes / args)を踏まえて質問に答えます。
- API キー無しでも動く: 内蔵 FAQ + ページガイド(storyGuideMap)で応答
- キーを入れると AI(OpenAI / Gemini, AI SDK v6)+ Embedding によるセマンティック検索で自由質問に対応
- 公開デプロイ前提の設計: 共有キーをバンドルに焼き込まない(本番ビルドで機械的に除去 + バックエンドプロキシ + レート制限)
https://storybook-concierge.vercel.app
API キー無しで動きます。 右下のボタンからチャットを開き、「この画面なに?」と聞いてみてください。 いま開いている Story を前提に答えます。
Docs ページ(dark が既定。ツールバーの Theme で light に切り替えられます):
pnpm install
pnpm storybook # http://localhost:6206これだけで動きます。API キーは不要です。キーが無い場合、Concierge は内蔵 FAQ とページガイドで応答します(「この画面は何?」のようなページ文脈の質問にも答えます)。
dark が既定。ツールバーの Theme から light に切り替えられる。
UI カタログは実装者が長時間見るものなので、まず暗い面で成立することを 確認したい、という判断。dark を後付けにすると「塗り面に白文字を固定する」 ような、明るい面でだけ成立する書き方が入り込む。
dark 化には 3 つの層があり、どれか 1 つ欠けると中途半端になる。
| 層 | 設定場所 | 欠けたときの症状 |
|---|---|---|
| chrome(サイドバー・検索) | .storybook/manager.ts |
Canvas は暗いのにサイドバーが白い |
| Docs ページ | parameters.docs.theme |
Docs タブだけ白い |
| Canvas の中身 | preview.tsx の ThemeProvider + CssBaseline |
暗い地に明るい部品が乗る |
両テーマのコントラストは実測で検証している。
pnpm check:contrast # dark + light、チャットパネルの中まで測るsrc/ChatSupport/ は自己完結している(ディレクトリ外への import が無い)。
コピーして Decorator を配線すれば、知識ベースを 1 行も書かなくても
そのプロジェクトのページについて答える。
Storybook が既に持っているメタ情報(story の title / name /
parameters.docs.description.component / 現在の args)を Decorator が実行時に渡すため。
storyGuideMap 登録ゼロの状態で「この画面なに?」と聞いた実際の応答:
Controls パネルで variant / color / size を変更して、見た目の変化を…
- variant — text(最も控えめ)/ outlined(枠線)/ contained(塗りつぶし)
…
このページで触れる props: variant, color, size, disabled, children
より深い回答が要るページは、Storybook から下書きを生成して足す。
pnpm build-storybook
pnpm generate:story-guide > src/ChatSupport/storyGuideMap.generated.tsFAQ は CHAT_FAQ(チャット操作・Storybook 一般。そのまま使える)と
PROJECT_FAQ(このデモ固有。差し替える)に分かれている。
手順の全体と検証ゲートは skills/add-storybook-concierge/SKILL.md にある。
Claude Code なら、このスキルを ~/.claude/skills/ か対象プロジェクトの
.claude/skills/ に置けばそのまま実行できる。
自由質問に AI で答えさせたい場合は、.env.example を .env にコピーしてキーを設定します。
cp .env.example .env
# VITE_OPENAI_API_KEY=... を設定
pnpm storybook各項目の説明は .env.example を参照してください。
このリポジトリは「チームに共有キーを配って各自の .env に貼る」運用を意図的に避けています。
| モード | キーの置き場所 | 用途 |
|---|---|---|
| Direct Mode(開発) | .env の VITE_OPENAI_API_KEY(ブラウザから直接呼ぶ) |
ローカル開発のみ |
| Backend Mode(本番) | サーバー環境変数(OPENAI_API_KEY 等、VITE_ prefix なし) |
公開デプロイ |
build-storybook(本番ビルド)では、.storybook/main.cjsが 資格情報の define をビルド種別で機械的に空文字にするため、.envにキーが残っていても共有ビルドには焼き込まれません- 本番は同一オリジンの
/api/ai(api/ai.ts、Vercel Function)を経由します。キーはサーバー側にだけ存在し、lib/ratelimit.tsが IP ごとの日次上限を適用します - 利用者が自前キーを持つ場合は
X-User-API-Keyヘッダーで送り、レート制限を免除できます - Origin 許可リストは硬い天井にはならないため、請求の上限はプロバイダ側の予算設定で設けてください
flowchart TD
A["Decorator (.storybook/preview.tsx)<br/>全 story に自動注入"] -->|"currentStory<br/>title / name / description / argTypes / args"| B["ChatSupport (src/ChatSupport)"]
B --> C{"APIキーあり or<br/>Backend Mode?"}
C -->|"No"| F["FAQ 応答<br/>faqDatabase.findFaqAnswer<br/>+ storyGuideMap (ページ文脈)"]
C -->|"Yes"| D{"無料枠の<br/>日次上限?"}
D -->|"到達"| F
D -->|"残あり"| E["semanticSearch<br/>(Embedding インデックス)"]
E --> G["callAI (chatAiService)"]
G -->|"開発: Direct Mode"| H["OpenAI / Gemini<br/>ブラウザから直接"]
G -->|"本番: Backend Mode"| I["/api/ai (api/ai.ts)<br/>サーバープロキシ + レート制限"]
G -->|"AI 接続エラー"| F
- Decorator は
viewMode !== 'docs'のときだけ注入します(Docs ページは 1 ページに複数 story が並び、文脈が一意でないため。FAB の重複防止も兼ねる) parameters.disableDecoratorChat: trueで story 単位のオプトアウトができます(ChatSupport 自身のデモ story で使用)- AI 呼び出しに失敗した場合も FAQ にフォールバックし、「送っても何も起きない」状態を作りません
storybook-concierge/
├── .storybook/
│ ├── main.cjs # Storybook 設定(esnext / destructuring 対策、本番でのキー除去)
│ └── preview.tsx # Decorator(全 story への Concierge 注入)
├── src/
│ ├── ChatSupport/ # Concierge 本体(FAB + チャット UI + AI/FAQ ロジック)
│ ├── stories/ # デモ用 story
│ └── theme/ # MUI テーマ / モーショントークン
├── lib/
│ ├── maxOutputTokens.ts # モデル別 maxOutputTokens の単一ソース(ブラウザ/サーバー共用)
│ ├── cors.ts # Origin 許可リスト
│ └── ratelimit.ts # 日次レート制限(Upstash Redis / in-memory)
├── api/
│ └── ai.ts # バックエンドプロキシ(Vercel Function)
├── skills/
│ └── add-storybook-concierge/
│ └── SKILL.md # 既存 Storybook への組み込み手順(Claude Code 用)
└── .env.example # 環境変数の説明
Claude Code が実行できる再現手順を skills/add-storybook-concierge/SKILL.md に置いています。手で組み込む場合も、同ファイルの手順と検証ゲートをそのまま使えます。
解説記事: 準備中
MIT © 2026 BoxPistols

