TECH PLAY

株式会社ZOZO

株式会社ZOZO の技術ブログ

949

.table-of-contents > li:nth-child(1), .entry-content ul > li > ul { display: none; } tr td:first-child { white-space: nowrap; } .nowrap2+table tr td:nth-child(2) { white-space: nowrap; } td { text-align: left !important; } 目次 目次 はじめに この記事の対象読者 背景・課題 背景 課題 AI駆動開発ワークフローの概要 AIサービスごとの役割 Devin Playbook ユーザー起動のPlaybook(Slack → Devin) !ai_task(単一タスク実装) !ai_tasks(タスク分割&並列実装) !human_review(人間承認フロー) 人間レビューが必要なケース ワークフロー自動呼び出しのPlaybook !fix_ci_failure(CI失敗時の自動修正) !fix_review_comments(レビュー指摘の自動修正) !context_curation(AIコンテキストの週次更新) 使用技術 機能一覧 アーキテクチャ SlackからPR承認までの完全フロー 2つのワークフローの役割 フロー別の使い分け 実装 設定ファイル AI Task Implementation:Issueから実装までの自動化 AI Task Dispatcher(ゲートウェイ) 実装ワークフローの動作 ラベルによる動作の分岐 重複実行の防止の仕組み キャンセル時のクリーンアップ AI Review Orchestrator:レビューから承認までの自動化 レビュー・ワークフローの動作 統合レビュー・修正ループの詳細フロー ジョブ構成と責務 Phase 1: Gate(スキップ判定) Phase 2: Perspective Router(ハイブリッド分類方式) 分類フロー 事前分類ルール Phase 3: CI完了待機 CI完了を待つ理由 Phase 4: Claude Review Perspectiveラベルに基づくレビュー レビュープロンプトの構造 分類判定ルール 自動承認の対象外となる変更タイプ 指摘の重大度とblockingフラグ(Conventional Comments準拠) PR説明文の自動生成・更新 関連 Issue の自動取得 PRタイトル・説明文の自動生成 タイトル生成ルール Phase 5: Devin自動修正 Devin修正の種類 修正ループの詳細 ループカウントの管理 Perspectiveラベルの更新ルール ループ終了条件 自動承認の条件 データ連携方式 Claude → Devinのデータ渡し 構造化出力スキーマ 技術的なポイント 1. concurrencyによる並行実行の制御 2. Devinセッションの自動停止 trapハンドラー(即時対応) if: cancelled()バックアップステップ GitHub Actionsのキャンセルシーケンス 3. AWS BedrockによるClaude呼び出し 4. マクロ形式によるプロンプト管理 5. HEAD SHAの検証 使い方 AI Task Implementationの使い方(Slack → Issue → 実装) 起動方法 注意点 AI Review Orchestratorの使い方(PR → レビュー → 承認) 自動レビューの流れ Claudeへの質問・修正依頼 操作手順まとめ 導入効果 定量的な効果 定性的な効果 Renovate PRの自動レビュー・承認 自動承認の条件 AI開発の効果測定(週次レポート) 計測される KPI レポート例 AIコンテキストの自動育成 動作フロー 更新対象ファイル ドキュメントの階層構造 各ディレクトリの役割 残っている課題 運用上の注意点 スキップラベルの活用 人間のレビューが必要なケース ワークフロー別ラベル操作 トラブルシューティング まとめ 実現した効果 重要なポイント 今後の展望 最後に 参考リンク はじめに こんにちは、新規事業部 マネジメントポータルブロックの岡本です。 「PRを作ったけど、レビューまで時間がかかる」「忙しいときレビューが後回しになる」──この悩み、開発者なら誰もが経験したことがあるのではないでしょうか。 私たちのチームでは、ClaudeとDevinを組み合わせたAI駆動開発ワークフローを導入し、レビュー待ち時間ゼロ・CIエラーの自動修正・Issueからの即時の実装開始を実現しました。この記事では、その設計思想から実装詳細、導入効果までを解説します。 この記事の対象読者 GitHub Actionsを使ったCI/CDの経験がある方 AIコーディングアシスタント(Claude、Devinなど)に興味がある方 チーム開発の効率化を検討している開発リーダー・マネージャー 背景・課題 背景 私たちのチームはZOZOマッチ管理画面を少人数で運用しており、各メンバーがフロントエンド・バックエンド・LP作成と複数の領域を担当しています。そのため、コンテキストスイッチが頻繁に発生し、「PRを作成したが、レビュアーが別タスクに集中している」「CIエラーを修正しようとしたら、他の緊急対応が入った」といった状況が日常的に起きていました。 結果として、レビューやCI修正の待ち時間が発生しやすく、開発のリズムが途切れがちでした。そこで、「人間は創造的な作業に集中し、定型的な作業はAIに任せる」という方針のもと、Claude(レビュー・判断)とDevin(実装・修正)を組み合わせたワークフローを整備しました。 課題 従来の開発フローでは、次のような課題がありました。 課題 詳細 レビュー待ち時間 PR作成から人間のレビューまでに数時間〜1日かかることがあった レビュー品質のばらつき レビュアーの経験や専門分野によって指摘内容に差があった Issue着手の遅延 Issueが起票されてから実装開始まで時間がかかっていた ライブラリ更新の負担 Renovateが作成する大量の依存関係の更新PRのレビュー・マージ作業が開発者の負担になっていた これらの課題を解決するため、2つのGitHub Actionsワークフローを中心に自動化を構築しました。 AI駆動開発ワークフローの概要 AIサービスごとの役割 このワークフローでは、2つのAIサービスを組み合わせて活用しています。GitHub Actionsとの連携における実行方式の違いから、次のように役割を分けています。 Devin(実装・修正):Issueの実装、CIエラー修正、レビュー指摘の修正を担当。非同期でセッションを開始し、修正完了まで一貫した作業が可能。ブランチ操作からコミット・プッシュまでを単一セッションで実行でき、修正完了後にPRが更新されてワークフローが再トリガーされる設計とも相性が良い Claude Code(レビュー・判断):PRのレビュー、分類判定、自動承認の判断を担当。同期的に結果を返すため、「自動承認 / 人間レビュー依頼 / Devin修正起動」の分岐処理を同一ワークフロー内で完結できる。構造化出力(JSONスキーマ)により、後続の処理時に扱いやすい形式でレビュー結果を取得できる Devin Playbook Playbookは、Devinに対する再利用可能な指示セットです。タスクの種類に応じた判断ロジック(セキュリティチェック、タスクサイズ判定など)、GitHub連携処理、エラーハンドリングが定義されており、一貫した品質でタスクを実行できます。 Playbookには、Slackからユーザーが起動するものと、GitHub Actionsワークフローが自動呼び出しするものの2種類があります。今回作成したワークフローでは、Devin APIを使用し、macro形式(macro IDとpayloadをJSONで渡す)でPlaybookを呼び出しています。 ユーザー起動のPlaybook(Slack → Devin) Slackで @devin をメンションし、 ! で始まるコマンドを送信すると、対応するPlaybookが実行されます。 コマンド 用途 対象タスク !ai_task 単一タスク実装 Small〜Medium(半日以内) !ai_tasks タスク分割&並列実装 Large(1日以上、複数コンポーネント) !human_review 人間承認フロー セキュリティ関連、要確認タスク !ai_task (単一タスク実装) 指定されたタスクを直接実装し、PRを作成します。 !ai_tasks (タスク分割&並列実装) 複雑なタスクを分析し、最大5つのsub-issueに分割して並列実装します。 !human_review (人間承認フロー) セキュリティ関連や重要な変更など、人間の確認が必要なタスク向けです。 人間レビューが必要なケース 認証・認可の変更 個人情報(PII)の扱い シークレット・APIキーの変更 ビジネスロジックの大幅な変更 本番環境への影響が大きい変更 ワークフロー自動呼び出しのPlaybook 次のPlaybookはGitHub Actionsワークフローから自動的に呼び出されます。ユーザーが直接実行することはありません。 Playbook 用途 呼び出し元 !fix_ci_failure CI失敗時の自動修正 ai-review-orchestrator.yml (CI失敗時) !fix_review_comments レビュー指摘の自動修正 ai-review-orchestrator.yml (blockingの指摘があるとき) !context_curation AIコンテキストの週次更新 devin-context-refresh.yml (毎週月曜09:00 JST) !fix_ci_failure (CI失敗時の自動修正) CI(Lint、Typecheck、Test)が失敗したPRを自動修正します。 GitHub ActionsのCIログを解析してエラーを特定 エラー種別に応じて修正(Lint → pnpm run lint:fix 、Type → 型エラー修正、Test → テスト更新) 修正をコミット・プッシュしてセッション終了 ビジネスロジックの変更が必要な場合は修正せず人間にエスカレーション !fix_review_comments (レビュー指摘の自動修正) Claude Reviewからの指摘( blocking: true )を自動修正します。 Blocker / Major の指摘を必須の修正対象として処理 各指摘の suggestion に従って修正 修正をコミット・プッシュしてセッション終了 認証・認可に関わる変更は修正せず人間にエスカレーション !context_curation (AIコンテキストの週次更新) PRコメントから学習可能な知見を抽出し、AI関連ドキュメントを週次で自動更新します。 直近7日間のPRコメント(Bot除外)を収集 コメントから有用な変更点、落とし穴、ルール例外、命名・設計ガイドラインを抽出 AGENTS.md 、 docs/ai-context/ 、 docs/guidelines/ 、 docs/review-perspectives/ を更新 更新内容をPRとして作成(人間がレビュー・マージ) 使用技術 ワークフローは、次の技術・サービスを組み合わせて構成しています(ワークフロー上の使用順)。 技術・サービス 用途 Slack タスク起点のオペレーション(Devinへの指示) GitHub Issues/PR/Labels 進行管理とトリガー Devin API Issueの実装・CIエラー修正・レビュー指摘の修正 GitHub Actions ワークフロー実行(CI待機、ジョブ分岐、通知) AWS Bedrock Claude実行基盤(セキュリティ要件対応) Claude Code Action PRレビュー実行・分類判定・自動承認 機能一覧 機能 内容 主な効果 AI Task Implementation Slack/ラベル起点で Devinが実装しPRを作成 Issue着手の即時化 AI Review Orchestrator Claudeがレビューし、Devinが修正 レビュー待ちの解消と品質の均一化 CI自動修正 CI失敗時にDevinが修正を実行(最大3回) 手戻り削減 自動承認 AI_ONLYかつ指摘なしで自動承認 PR処理の高速化 週次メトリクス KPIを週次で自動計測しIssue化 効果測定の継続 ナレッジ更新 PRコメントからドキュメントを自動更新 レビュー品質の継続改善 アーキテクチャ SlackからPR承認までの完全フロー 2つのワークフローの役割 ワークフロー トリガー 役割 AI Task Implementation Issueに ai-task / ai-tasks ラベル追加 DevinがIssueの実装を担当し、PRを作成 AI Review Orchestrator PRの作成・更新 Claudeがレビュー、Devinが修正、条件を満たせば自動承認 フロー別の使い分け 各Playbookの詳細なフローは「 Devin Playbook 」セクションを参照してください。 シナリオ Slack コマンド 処理フロー 単一タスク(直接の実装が可能) @devin !ai_task タスク説明 Devin → Issue + PR → 自動レビュー → 承認 複雑なタスク(分割が必要) @devin !ai_tasks タスク説明 Devin → 親 Issue + sub-issue → 並列実装 → 自動レビュー 要承認タスク @devin !human_review タスク説明 Devin → Issue 作成 → 人間承認 → 自動実装 Claudeに質問・修正依頼 @znm-claude-code 依頼内容 Claudeが対応(PR/Issueコメント) 実装 設定ファイル ワークフローはGitHub Actionsの設定ファイルで定義しています。主要ファイルと役割は次のとおりです。 ファイル 役割 .github/workflows/ai-task-dispatcher.yml Issueラベル検知・条件判定のゲートウェイ(条件を満たす場合のみimplementationを起動) .github/workflows/ai-task-implementation.yml DevinによるIssue実装(dispatcher から呼び出し) .github/workflows/ai-review-orchestrator.yml PRレビュー・修正ループ・Renovate統合 .github/workflows/pr-perspective-router.yml Perspectiveラベルの再分類 .github/workflows/ai-renovate-review.yml Renovate PRの依存関係レビュー(orchestrator から呼び出し) .github/workflows/ai-metrics-report.yml 週次メトリクス生成 .github/workflows/devin-context-refresh.yml コンテキスト更新 AI Task Implementation:Issueから実装までの自動化 AI Task Dispatcher(ゲートウェイ) ai-task-dispatcher.yml は、Issueのラベル追加を検知し、条件を満たす場合のみ実装ワークフローを起動するゲートウェイです。 Dispatcherを分離している理由は3つあります。 条件判定の一元化:ラベル検知と条件チェックを単一ファイルに集約し、保守性を向上 不要なワークフロー発火の防止:条件を満たさない場合は早期終了し、リソースを節約 責務の分離:「いつ起動するか」(dispatcher)と「何をするか」(implementation)を明確に分離 実装ワークフローの動作 ai-task または ai-tasks ラベルがIssueに追加されると、dispatcher経由でDevinが自動的に実装を開始します。トリガー条件は次のとおりです。 Issueの labeled イベントで起動(dispatcherが検知) ai-task / ai-tasks ラベル付与時のみ対象 in-progress が付いていない場合だけ開始 起点は2パターンあります。 Playbook起動(Slackなど):PlaybookがIssueを作成 → ai-task / ai-tasks を付与 → このワークフローが起動 既存Issue起点:このワークフローが skip_issue_creation: true を渡してPlaybookを実行(Issue作成はスキップ) ラベルによる動作の分岐 ラベルに応じて対応するPlaybookが実行されます。各Playbookの詳細なフロー(セキュリティチェック、タスクサイズ判定、実装手順など)は「 Devin Playbook 」セクションを参照してください。 ラベル Playbook 動作 ai-task !ai_task 単一タスクとして実装 ai-tasks !ai_tasks タスク分割 → 並列実装 sub-issue作成には gh sub-issue 拡張が必要です(Playbook側 or Devinのリポジトリ初期設定で事前インストール)。 重複実行の防止の仕組み 同じIssueに対する重複実行を防ぐため、concurrency設定でIssue番号単位の同時実行を1つに制御し、進行中の実行があれば新しい実行でキャンセルします。 例えば、同じIssueに短時間でラベル追加が続いた場合、 labeled イベントが複数回発火します。この二重起動を防止します。 キャンセル時のクリーンアップ ワークフローがキャンセルされた場合、次のクリーンアップ処理を実行します。 キャンセル時にDevinセッションを停止 キャンセル時に in-progress ラベルを削除して再実行可能にする 失敗時も in-progress ラベルを削除して再実行可能にする AI Review Orchestrator:レビューから承認までの自動化 レビュー・ワークフローの動作 PRが作成・更新されると、 ai-review-orchestrator.yml が自動実行されます。このワークフローはPRの作成・更新をトリガーに発火し、PRの作成元は問いません。 AI Task Implementation経由のPR: DevinがPR作成 → このワークフローが発火 手動作成されたPR: 開発者の直接PR作成 → このワークフローが発火 なお、 ai-task-dispatcher.yml とこのワークフローは独立しており、直接の呼び出し関係はありません。 統合レビュー・修正ループの詳細フロー ジョブ構成と責務 ai-review-orchestrator.ymlは約1,500行の大規模ワークフローです。6つのフェーズに分かれています。 フェーズ ジョブ名 責務 依存関係 1 gate スキップ判定、PR情報取得、イテレーション管理 なし 2 perspective-router PR分類、perspective:* ラベル付与(初回のみ、Renovate以外) gate 3 wait-for-ci CI完了待機(最大20分、30秒間隔ポーリング) gate, perspective-router 4a devin-ci-fix CI失敗時のDevin自動修正 gate, wait-for-ci (CI失敗時) 4b-1 renovate-review Renovate PR専用のClaude依存関係レビュー( ai-renovate-review.yml 呼び出し) gate, wait-for-ci (CI成功かつRenovate PR時) 4b-2 claude-review CI成功/未検出時のClaudeレビュー実行(Renovate以外) gate, wait-for-ci (CI成功/未検出時) 4c devin-review-fix レビュー指摘ありの場合のDevin自動修正 gate, claude-review (指摘あり時) 4d auto-approve AI_ONLY + 指摘なしの場合の自動承認 gate, claude-review or renovate-review 4e needs-human-review NEEDS_HUMAN + 指摘なしの場合の人間レビュー依頼 gate, claude-review or renovate-review 4f vrt Visual Regression Testingの実行 auto-approve or needs-human-review 5 max-iterations-reached イテレーション上限到達時の通知 gate 6 ci-timeout CI待機タイムアウト時の通知 gate, wait-for-ci Phase 1: Gate(スキップ判定) 次の条件でワークフローをスキップします。 条件 判定方法 理由 Draft PR pull_request.draft == true 作業中のためレビュー不要 Bot作成PR(Devin・Renovate 以外) actor がbotかつDevin・Renovateでない 無限ループ防止 Fork PR pull_request.head.repo.fork == true シークレット/権限制約のためスキップ skip-ai-review ラベル ラベル存在チェック 明示的スキップ Renovate PR actor が renovate[bot] Renovate専用レビュー( renovate-review )へルーティング ai-auto-approved ラベル ラベル存在チェック 既に承認済み また、新しいpushがあった場合はAI関連ラベルを削除し、承認を取り消して再レビューします。ラベルの削除ルールはpush元によって異なります。 人間のpush: ai-auto-approved / needs-human-review / perspective:* を削除して再分類 Botのpush: ai-auto-approved / needs-human-review のみ削除して分類は維持 Phase 2: Perspective Router(ハイブリッド分類方式) PRの変更内容を分析し、適切なレビュー観点を決定します。キーワードベースの事前分類 + Claudeフォールバックのハイブリッド方式を採用しています。 分類フロー 事前分類ルール ラベル パスパターン キーワード perspective:workflow .github/workflows/ , .github/actions/ , action.yml workflow, ci, cd, pipeline, jobs, steps, runs-on perspective:security auth/ , login/ , session/ auth, token, secret, credential, password, encrypt, sanitize, xss, csrf, pii, vulnerability perspective:quality *.test.* , *.spec.* , __tests__/ , eslint , prettier , tsconfig test, spec, lint, format, accessibility, a11y, aria-, vitest, jest perspective:dependency package.json , pnpm-lock.yaml , yarn.lock , package-lock.json dependency, package, version, upgrade, license perspective:api /api/ , openapi , swagger , schema , .dto. api, endpoint, schema, openapi, graphql, rest perspective:perf (パスパターンなし) performance, optimize, cache, lazy, memo, bundle, split, prefetch perspective:business domain/ , business/ permission, role, billing, payment, validation, status, transition, rule perspective:general 上記に該当しない場合のみ - 複数パターンにマッチした場合、該当ラベルがすべて付与されます。 perspective:general は他のラベルと併用されません。 Phase 3: CI完了待機 他のCIジョブ(型チェック、lint、テストなど)の完了を待機します。待機の仕組みは次のとおりです。 自分自身のワークフローやVRTは待機対象から除外 部分一致の除外パターンも併用して精度を上げる 最大20分、30秒間隔でポーリング CI Runが一定時間見つからない場合(約5分)は ci_status=skipped としてClaudeレビューへ進みます。 success / neutral / skipped は成功扱いです。 CI完了を待つ理由 CIが失敗している状態でレビューしても意味がない CI失敗 → Devin修正 → またCI失敗 → またレビュー... という無駄なループを防ぐ CIが通った状態のコードに対してレビューすることで、品質の高いフィードバックが可能 Phase 4: Claude Review Claude Code Actionを使用して、PRを包括的にレビューします。 Perspectiveラベルに基づくレビュー Claude Reviewでは、Phase 2で付与された perspective:* ラベルに応じて、対応するレビュー観点ドキュメントを動的に読み込みます。 ラベル 対応ファイル 主なレビュー観点 perspective:workflow docs/review-perspectives/workflow.md GitHub Actions/CI/CD の設計・セキュリティ perspective:security docs/review-perspectives/security.md 認証・認可・入力検証・機密情報の管理 perspective:quality docs/review-perspectives/quality.md テスト・Lint・アクセシビリティ perspective:dependency docs/review-perspectives/dependency.md 依存関係の追加・更新・削除 perspective:api docs/review-perspectives/api.md API設計・スキーマ・エラーハンドリング perspective:perf docs/review-perspectives/perf.md パフォーマンス・キャッシュ・最適化 perspective:business docs/review-perspectives/business.md ビジネスロジック・権限・バリデーション perspective:general docs/review-perspectives/general.md 汎用的なコード品質(他に該当しない場合) レビュー実行時の読み込みフローは次のとおりです。 PRから perspective:* ラベル一覧を取得 対応する docs/review-perspectives/*.md を読み込み 取得内容をプロンプトに差し込みレビュー実行 たとえば、セキュリティ関連のPRには security.md の観点が、API関連のPRには api.md の観点が適用されるといった具合です。 レビュープロンプトの構造 レビュープロンプトは次のルールで構成されます。 Bedrock経由でClaudeを実行 Perspective指定と重大度定義をプロンプトに含める Blocker/Majorを blocking: true として扱う 分類判定ルール 判定 条件 自動承認 AI_ONLY テストコード、型定義、明確なバグ修正、リファクタリング 可能 NEEDS_HUMAN ドキュメント、スタイル、設定ファイル、セキュリティ関連 不可 自動承認の対象外となる変更タイプ 次の変更は必ず人間のレビューが必要です( NEEDS_HUMAN と判定)。 ドキュメント更新:README.md, *.mdファイル、docs/配下の変更 スタイル調整:CSS, Tailwindクラス、デザイン・レイアウト変更 設定ファイル変更:tsconfig, eslint, prettier, vite.config等 指摘の重大度とblockingフラグ(Conventional Comments準拠) 重大度 blocking 意味 修正要否 [Blocker] true 必ず修正が必要 必須 [Major] true 強く推奨される修正 必須 [Minor] false 推奨される改善 任意 [Nit] false 微細な指摘(スタイル等) 任意 [Question] false 仕様・意図の確認質問 回答必須(修正不要) [Praise] false 良いコードへの称賛 対応不要 blocking: true の指摘が1件以上あると、Devin自動修正が実行されます。 PR説明文の自動生成・更新 Claude Reviewでは、レビュー結果と同時にPR説明文を自動生成・更新する機能があります。 関連 Issue の自動取得 PR本文からclosing keywords( Closes #123 , Fixes #456 など)を自動的に抽出し、関連Issueを特定します。抽出されたIssue番号はレビュープロンプトに含まれ、Claudeが変更の意図を理解する際のコンテキストとして活用されます。 PRタイトル・説明文の自動生成 ClaudeはPR内の全てのコミットと変更ファイルを分析し、次の手順でタイトルと説明文を自動生成します。 PR内の全コミット一覧を取得(最大50件) 全ての変更ファイルを分析 PR全体を代表するタイトルを日本語で生成(Conventional Commits形式) .github/PULL_REQUEST_TEMPLATE.md に沿って説明文を生成 タイトル生成ルール 形式: {prefix}: {変更内容の要約} (例: feat: ユーザー認証機能の追加 ) prefix:feat / fix / docs / refactor / chore / testから変更種別に応じて選択 50文字以内で簡潔に日本語で記載 複数の変更がある場合は、主要な変更を代表するタイトルに 生成されたタイトルと説明文は、レビュー完了後に自動的にPRへ反映されます(それぞれ生成された場合のみ更新)。PR作成者がタイトルや説明文を適切に書けなかった場合でも、Claudeが内容を自動生成してくれます。最新のコミットだけでなくPRに含まれる全てのコミットを考慮するため、複数回のpushがあっても正確な内容が生成されます。 Phase 5: Devin自動修正 Claudeのレビューで指摘があった場合、またはCIが失敗した場合、Devinが自動修正します。 Devin修正の種類 ジョブ名 用途 参照 Playbook devin-ci-fix CI失敗の修正(CIログを解析して自動修正) fix-ci-failure.devin.md devin-review-fix レビュー指摘の修正(Claudeの指摘に基づいて自動修正) fix-review-comments.devin.md Devinへの修正指示は次の手順で行います。 blocking: true の指摘だけを抽出し、抽出結果をmacro payloadに組み込んでDevinに送信します。 修正ループの詳細 ループカウントの管理 イテレーションはCI修正 + レビュー修正の合計回数でカウントします。 Push種別 カウント動作 理由 人間がPR作成 0からスタート 新規PR Devinがpush カウント継続 自動修正ループ中 人間が修正push 0にリセット 人間の修正後は新規扱い 上限(3回)に達した後でも、人間が修正してpushすればカウントがリセットされ、レビューが再開されます。 Perspectiveラベルの更新ルール Push種別 Perspective Router ラベル動作 人間がPR作成 ✅ 実行 新規ラベル付与 Devinがpush ❌ スキップ 既存ラベル維持 人間が修正push ✅ 実行 既存ラベル削除 → 再分類 ループ終了条件 正常終了:CI成功 + レビュー指摘なし → 自動承認 上限到達: iteration_count >= MAX_ITERATIONS → 人間レビュー依頼 タイムアウト:CI待機が20分超過 → 人間への確認依頼 自動承認の条件 次のすべてを満たした場合のみ自動承認されます。 CI成功 Claudeレビュー完了 分類が AI_ONLY blockingな指摘がない HEAD SHAが変更されていない(レース条件対策) データ連携方式 Claude → Devinのデータ渡し Claudeのレビュー結果をDevinに渡す方式は、ジョブ出力による直接連携を採用しています。 構造化出力スキーマ Claudeに --json-schema オプションでレビュー結果の構造を指定し、次の項目を必須にしています。 classification (AI_ONLY / NEEDS_HUMAN) Blocker/Major/Minorの件数フラグとカウント fix_instructions (修正指示文) issues (指摘一覧:severity / blocking / file / messageなど) 技術的なポイント 1. concurrencyによる並行実行の制御 同一PRに対して1つのワークフローのみ実行( group で制御) cancel-in-progress: true のため、Devinが修正をpushした場合: 新しいワークフローが開始される 古いワークフローは自動的にキャンセルされる Devinセッションはtrapハンドラーにより自動停止される 新しいワークフローはCI完了を待機してからレビュー 2. Devinセッションの自動停止 ワークフローがキャンセルされた際、実行中のDevinセッションを確実に停止するため、二重のセーフティネットを実装しています。 trapハンドラー(即時対応) Devin API呼び出し中にシグナル(SIGINT/SIGTERM)を受信した場合、trapハンドラーが即座にセッションを停止します。SIGINT/SIGTERMを受けると即座にcleanupを実行し、 /tmp/devin_resp.json から session_id を取得して10秒以内にセッションを終了します。 if: cancelled() バックアップステップ API呼び出しが完了してからキャンセルされた場合に備えて、専用のクリーンアップステップを用意しています。 outputs または /tmp/devin_resp.json から session_id を取得してセッションを停止します。 GitHub Actionsのキャンセルシーケンス GitHub Actionsはキャンセル時に次の順序でシグナルを送信します。 SIGINT 送信 → 7.5秒待機 SIGTERM 送信 → 2.5秒待機 強制終了 trapハンドラーは最初の SIGINT でcleanupを実行するため、最大10秒の猶予内でDevinセッションを停止できます。 3. AWS BedrockによるClaude呼び出し セキュリティ要件を満たすため、Claude APIではなくAWS Bedrock経由でClaudeを呼び出しています。AWS認証情報を設定し、Claude Code Actionの use_bedrock を有効化しています。 設定項目 値 リージョン us-east-1 IAMロール <your-bedrock-access-role> モデル(レビュー) Claude(Bedrock Application Inference Profile) モデル(分類) Claude Haiku( anthropic.claude-3-haiku-20240307-v1:0 ) 4. マクロ形式によるプロンプト管理 Devinへの指示は、macro IDとpayloadをJSONとして渡すマクロ形式で標準化しています。Playbookのmacro IDと一対一で対応しており、一貫性のある指示が可能です。 5. HEAD SHAの検証 レース条件を防ぐため、承認前にHEAD SHAが変更されていないことを確認しています。実行開始時のHEAD SHAと現在のSHAを比較し、一致しない場合は自動承認をスキップします。 使い方 AI Task Implementationの使い方(Slack → Issue → 実装) SlackからDevinにタスクを依頼すると、Issue作成から実装、PR作成までを自動で行います。各Playbookの詳細なフローは「 Devin Playbook 」セクションを参照してください。 起動方法 方法 手順 動作 Slackから(推奨) @devin !ai_taskタスク説明 DevinがIssue作成 → ai-task ラベル付与 → ai-task-dispatcher.yml 発火 → 実装開始 Slackから(複雑なタスク) @devin !ai_tasksタスク説明 Devinが親Issue作成 → sub-issueに分割 → 並列実装 GitHub Issueから Issue に ai-task / ai-tasks ラベルを付与 ai-task-dispatcher.yml 発火 → 実装開始 注意点 in-progress ラベルがあるIssueはスキップされます(実装中のため) セキュリティ関連を検出した場合、 !human_review への切り替えを案内してセッション終了 タスクサイズが合わない場合、適切なPlaybookへの切り替えを案内してセッション終了 AI Review Orchestratorの使い方(PR → レビュー → 承認) PRが作成・更新されると、 ai-review-orchestrator.yml が自動起動し、Claudeがレビューします。 自動レビューの流れ CI完了待機(最大20分) Perspective分類(初回のみ、変更内容に応じてラベル付与) Claudeレビュー(Perspectiveに基づく観点でレビュー) 結果に応じた分岐: 指摘あり → Devin自動修正(最大3回) AI_ONLY + 指摘なし → 自動承認 NEEDS_HUMAN → 人間レビュー依頼 Claudeへの質問・修正依頼 PRやIssueに対してClaudeに質問や修正依頼をしたい場合は、 @znm-claude-code をメンションしてコメントします。 操作手順まとめ 入口を選ぶ。 @devin !ai_task (単一タスク)/ @devin !ai_tasks (分割タスク)/ @devin !human_review (人間の承認が前提)。Slackを使わない場合はIssueにラベルを付与する DevinがIssue/PRを作成し、 ai-review-orchestrator.yml が自動起動する CI完了後にClaudeがレビューし、指摘があればDevinが修正する(最大3回) AI_ONLY かつ指摘なしなら自動承認、 NEEDS_HUMAN は人間レビューへ。Claudeへの質問や修正依頼は @znm-claude-code を利用する 運用上の例外( skip-ai-review / renovate など)は「運用上の注意点」を参照してください。 導入効果 定量的な効果 指標 導入前 導入後 PR作成からレビュー開始まで 平均4時間 即時 CIエラー修正時間 平均30分/回 自動 単純なIssueの実装開始 平均1日 即時 RenovatePRの処理 手動確認・マージ 依存関係レビュー後に自動承認 定性的な効果 開発者が集中する時間の確保 定型的なエラー修正から解放され、創造的な作業に集中できるようになった レビュー品質の均一化 Perspectiveに基づくレビューで、一貫した品質のレビューが実現した レビュアーの経験や専門分野に依存しない指摘が可能になった 心理的安全性の向上 AIが最初にレビューすることで、人間のレビューでの「些細な指摘」が減った レビュアーは本質的な議論へ集中できるようになった ナレッジの蓄積 レビュー観点ドキュメント( docs/review-perspectives/ )が知識ベースとして機能 新しいメンバーのオンボーディングにも活用できる ライブラリ更新の自動化 Renovateが作成する依存関係の更新PRをClaudeがレビュー AI_ONLYかつ指摘なしなら自動承認 セキュリティパッチの迅速な適用に貢献 Renovate PRの自動レビュー・承認 Renovateが作成した依存関係の更新PRは、専用ワークフロー( ai-renovate-review.yml )で自動処理されます。 自動承認の条件 patch / minorの安全な更新のみ 破壊的変更なし Blocker / Major指摘が0件 AI開発の効果測定(週次レポート) ai-metrics-report.yml により、AI駆動開発の効果を週次で自動計測し、GitHub Issueとして報告します。 計測される KPI 指標 目標 説明 AI_ONLYレビュー通過率 60%以上 AIのみで承認可能と判定されたPRの割合 自動承認率 - 実際に自動承認されたPRの割合 Devinの修正成功率 70%以上 Devinが修正を試みたPRのうち、マージに至った割合 イテレーション上限到達率 10%以下 修正ループが上限(3回)に達したPRの割合 平均イテレーション回数 1.5回以下 PRあたりの平均の修正回数 CI失敗率 20%以下 CI失敗が発生したPRの割合 レポート例 ## AI 開発効果測定 週次レポート **集計期間**: 2025-12-15 ~ 2025-12-22 **対象 PR 数**: 25 件 ### 📊 主要指標(KPI) | 指標 | 実績 | 目標 | 状態 | |------|------|------|------| | AI _ ONLY レビュー通過率 | 72.0% | 60% 以上 | ✅ | | 自動承認率 | 68.0% | - | - | | Devin 自動修正成功率 | 85.7% | 70% 以上 | ✅ | | イテレーション上限到達率 | 4.0% | 10% 以下 | ✅ | | 平均イテレーション回数 | 1.2 回 | 1.5 回以下 | ✅ | | CI 失敗率 | 16.0% | 20% 以下 | ✅ | 🎉 すべての主要指標が目標を達成しています! このレポートは毎週月曜10:00 JSTに自動生成され、 ai-metrics ラベル付きのIssueとして作成されます。 AIコンテキストの自動育成 devin-context-refresh.yml により、PRコメントからのフィードバックを元にAIコンテキストドキュメントを週次で自動更新します。 動作フロー 更新対象ファイル カテゴリ ファイル ルート AGENTS.md docs/ai-context/ coding-standards.md , context.md , data-model.md , design-template.md , glossary.md , implementation-patterns.md , metrics.md , pr_review_classification.md , pr_review_comment_rules.md , role.md , task-assignment.md docs/guidelines/ architecture.md , environment-variables.md , error-handling.md , naming-conventions.md , validation.md docs/review-perspectives/ api.md , business.md , dependency.md , general.md , perf.md , quality.md , security.md , workflow.md ドキュメントの階層構造 AIエージェントがコンテキストを読み込む際、次の階層構造で参照が行われます。 CLAUDE.md:Claude Codeが最初に読み込むファイル。 @AGENTS.md でメインガイドを参照 AGENTS.md:AIエージェント向けメインガイド。最重要ルール(3-5個)と各ドキュメントへのパス参照を記載(300行未満を推奨) docs/:詳細情報は各サブディレクトリに委譲し、AGENTS.mdからパス参照のみ記載 この構造によってコンテキストの段階的読み込みが可能になり、トークン消費を抑えつつ必要な情報にアクセスできます。 各ディレクトリの役割 ディレクトリ 役割 使用タイミング CLAUDE.md Claude Codeのエントリーポイント。 @AGENTS.md でAGENTS.mdを参照 Claude Code起動時に自動読み込み AGENTS.md AIエージェント向けメインガイド。最重要ルール(3-5個)と各ドキュメントへの参照を記載 Devinがセッション開始時に読み込む docs/ai-context/ コーディング規約、実装パターン、用語集、データモデルなどのコンテキスト情報 Playbook内で参照先として指定。週次更新でPRコメントから学んだ知見を蓄積 docs/guidelines/ アーキテクチャ設計、エラーハンドリング、バリデーションなどの詳細な技術ガイドライン Playbook内で参照先として指定。週次更新で継続的に改善 docs/review-perspectives/ perspective:* ラベルに対応したレビュー観点ドキュメント Claude ReviewがPRのPerspectiveに応じて動的に読み込み( 詳細 ) PRレビューで蓄積されたナレッジが自動的にドキュメントに反映されるため、Claudeのレビュー品質も継続的に向上していきます。 残っている課題 AIでも対応が難しいドメイン固有の判断があり、最終的に人間の合意が必要になるケースが残る ワークフローが複雑になり、運用ルールの理解・周知コストが発生している AIによる正しい指摘でも、チームの合意形成に時間を要する場合がある 運用上の注意点 スキップラベルの活用 特定のPRでAIレビューをスキップしたい場合は、次のラベルを使用します。 skip-ai-review : AIレビューを完全にスキップ renovate : Renovate PR(専用ワークフローで処理) 人間のレビューが必要なケース 次の変更は NEEDS_HUMAN と判定され、必ず人間のレビューが必要です。 セキュリティ関連の変更 ドキュメント・READMEの更新 設定ファイルの変更 スタイル・デザインの変更 ワークフロー別ラベル操作 ワークフロー 付与するラベル 削除するラベル ai-task-dispatcher.yml -(条件判定のみ、ラベル操作なし) - ai-task-implementation.yml in-progress in-progress (失敗時/キャンセル時) ai-review-orchestrator.yml ci-failed , ai-iteration-{N} , ai-auto-approved , needs-human-review ci-failed , ai-auto-approved , needs-human-review , perspective:* , ai-iteration-{N} pr-perspective-router.yml perspective:* perspective:* (再分類時) ai-renovate-review.yml perspective:dependency , renovate - ai-metrics-report.yml ai-metrics , automated (Issueに付与) - トラブルシューティング 問題 対処法 Devinセッションが開始しない DEVIN_API_KEY の設定を確認 Claudeレビューが実行されない AWS IAMロールの権限を確認 イテレーション上限に達した 手動で修正し、 ai-iteration-* ラベルを削除 CIタイムアウト CIの実行時間を確認、 CI_WAIT_MINUTES の調整を検討 まとめ この記事では、Claude(レビュー・判断)× Devin(実装・修正)の役割分担によるAI駆動開発ワークフローについて解説しました。 実現した効果 指標 Before After PRレビュー待ち時間 平均4時間 即時(Claudeが自動レビュー) レビュー指摘の修正 手動対応 Devinが自動修正 CIエラー修正 手動対応 Devinが自動修正(最大3回リトライ) Issue実装開始 平均1日 Slackから即時着手 Renovate PR 手動確認・マージ Claudeが依存関係レビューし、AI_ONLYかつ指摘なしなら自動承認 効果測定 なし 週次レポートでKPIを自動計測 ナレッジ育成 なし PRコメントからドキュメントを週次で自動更新 重要なポイント 役割分担:Claudeはレビュー(判断)、Devinは実装(作業)という明確な役割分担 人間との協調:AIはあくまでサポートであり、最終判断は人間が行う設計 段階的な自動化:すべてを自動化するのではなく、信頼性の高い部分から自動化 透明性:すべての判断理由がPRコメントとして記録される セーフティネット:イテレーション上限、キャンセル時のクリーンアップ、HEAD SHA検証 今後の展望 レビュー観点の自動学習 MCP連携強化 Confluenceドキュメント読み込み Figma MCPからデザイン取得 → 実装まで一気通貫 Jiraチケット起点の連携追加(Slackと同様のフローで起動) 人間レビューコメント時の対応方針の定義(優先度・担当・再レビュー) CodeRabbitとの併用検討 Slack通知の拡充(開始/完了/要人間対応などを分かりやすく通知) 最後に ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com 参考リンク anthropics/claude-code-action - Claude CodeのGitHub Action Devin API Documentation - Devin APIリファレンス
アバター
.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(2) { display: none; } はじめに こんにちは、検索基盤部の 朝原 です。ZOZOTOWNの検索改善を担当しています。 日々の分析業務では、ユーザーの行動ログを集計するSQLクエリを頻繁に作成します。クエリ作成には定型的なパターンも多く、作業時間を短縮する手段として生成AIの活用を検討しました。しかし、社内固有のログ構造や前提条件が多く、生成AIを利用しても期待どおりにクエリを作成できないという課題がありました。 本記事では、Claude CodeのSkills機能やサブエージェント機能を活用してこの課題を解決した方法を紹介します。 目次 はじめに 目次 なぜ生成AIでログ分析のSQLを書くのが難しいのか 1. 社内固有のログ構造を参照できない 2. チームのSQL規約に準拠した出力が担保できない 3. 暗黙的に共有されているナレッジを生成AIが持っていない 解決策の全体像 技術的アプローチ 1. Claude Code向けログ定義書の整備 方針 Skillsの具体的な内容 2. 3段階エージェントによるコンテキスト最適化 エージェント定義ファイル 3. カスタムスラッシュコマンドによるワークフロー自動化 実行例と結果 プラグイン化 課題と今後の展望 より多くのテーブルへの対応 テーブルインデックスの自動更新 まとめ なぜ生成AIでログ分析のSQLを書くのが難しいのか 私たちは、検索体験におけるユーザー行動ログを、日々SQLで集計して指標化し、施策検証や課題発見に活用しています。具体的には、検索結果画面における商品およびサジェスト(キーワード検索の補完機能)のインプレッション、クリック、ならびに購買に至る一連のイベントを集計対象としています。生成AIの登場により多くのコーディング業務が自動化されました。しかし、社内のログ分析においては以下の課題により活用が進んでいませんでした。 1. 社内固有のログ構造を参照できない 生成AIは一般的なSQLの生成が可能です。しかし、社内固有のログのテーブル構造については前提知識がありません。ログのテーブル名やスキーマとクエリが同じリポジトリで管理されていないため、コーディングエージェントがそれらを参照できません。 2. チームのSQL規約に準拠した出力が担保できない インデントの数や予約語の取り扱いなど、チーム内で定められたコーディング規約があります。チーム内でのSQLクエリの共有を考えると、生成AIが作成するクエリも規約に準拠している必要があります。一方で、生成AIの標準的な出力ではこれらを完全に再現できません。 3. 暗黙的に共有されているナレッジを生成AIが持っていない どのIDで集計すべきか、どのタイムスタンプを基準にするべきかといったドキュメント化されていない知識が存在します。こうした暗黙知をコーディングエージェントは持っていません。 こうした要因が重なり、「生成AIにSQLを書かせても、結局手直しが必要で効率化にならない」という状況が続いていました。 解決策の全体像 これらの課題を解決するため、Claude Codeを活用してクエリを自動生成するフローを構築しました。 以下がSQLクエリを生成するフローの全体像です。 以下のようなコマンドを入力するだけで、ログの構造や規約を踏まえた実用的なSQLクエリが自動生成されます。 /make_sql 商品カテゴリ「スニーカー」の表示回数、クリック数、クリック率をプラットフォーム毎に調査するクエリを作成してください 技術的アプローチ LangGraph、Agent Development Kit、Strands Agents等のエージェント開発フレームワークを使うことでよりカスタマイズ性の高いものを構築できます。しかし、今回は「低コストかつ短期間に、社内展開しやすい形で実現する」ためClaude Codeを採用しました。 現在ZOZOではエンジニア社員に月額200米ドルの基準のもと、コーディングエージェントを導入しております。ご興味のある方は以下の記事もご参照ください。 corp.zozo.com 今回紹介する手法で用いるClaude Codeの設定ディレクトリ構造は以下の通りです。 .claude/ ├── agents/ # エージェント定義 │ ├── log-data-advisor.md # 要件定義&ログ情報取得 │ ├── query-builder.md # クエリ作成 │ └── query-validator.md # 品質管理 └── skills/ ├── make_sql/ # ワークフロー定義(スラッシュコマンド) │ └── SKILL.md ├── table-index/ # テーブル一覧・関係性 │ └── SKILL.md ├── table-details/ # テーブル詳細仕様 │ ├── SKILL.md │ ├── product_impressions.md # サポートファイル │ ├── product_clicks.md # サポートファイル │ └── ... └── quality-check/ # 品質チェック └── SKILL.md 以降で各要素について詳しく説明します。 1. Claude Code向けログ定義書の整備 方針 まずはClaude Code向けに社内のログ情報をドキュメント化するところから始めました。 ログは非常に多くのテーブルが存在するため、全ての情報を1つのファイルにまとめるとLLMに渡すコンテキストが肥大化し、必要な情報が埋もれて出力精度が低下しやすくなります。そこで、Claude CodeのSkills機能 1 をドキュメントの定義先として採用し、役割ごとに分割して管理することにしました。Skillsとは SKILL.md ファイルに指示を記述することでClaudeの機能を拡張する仕組みです。 .claude/skills/<skill-name>/SKILL.md というディレクトリ構造で作成します。 Skillsを採用した理由は、エージェントが必要なときに必要な情報だけを参照できる点にあります。すべてのログ情報を一度に読み込むとコンテキストが肥大化しますが、Skillsとして分離しておけば、エージェントは必要なスキルだけを選択的に参照できます。 用途ごとに、以下の3つのSkillsに分離しました。 Skills 用途 table-index ログテーブルの特徴、関係性を把握 table-details ログテーブルの詳細仕様を把握 quality-check チーム内SQL規約に基づいた品質チェック table-index スキルで必要なテーブルを特定し、 table-details スキルで詳細仕様を参照、 quality-check スキルでチーム内のSQL規約に従っているかチェックを行う流れです。ログテーブルの情報を2回に分けることで、エージェントがまずテーブル一覧で必要なテーブルを特定し、次に詳細仕様で実装に必要な情報を取得する、というコンテキスト効率の良い参照フローが実現できます。 Skillsの具体的な内容 以下は各スキルの具体例です。実際のログ情報とは異なります。 .claude/ ├── ... └── skills/ ├── table-index/ # テーブル一覧・関係性 │ └── SKILL.md ├── table-details/ # テーブル詳細仕様 │ ├── SKILL.md │ ├── product_impressions.md # サポートファイル │ ├── product_clicks.md # サポートファイル │ └── ... └── quality-check/ # 品質チェック └── SKILL.md .claude/skills/table-index/SKILL.md このスキルでは、ログテーブルの一覧とテーブル間の関係性を提供します。エージェントはまずこのスキルを参照して、要件に合うテーブルを特定します。テーブル名やカラム名だけでなく、各テーブルの説明やテーブル間の関係性なども自然言語で記載しています。このアプローチは、Tiger Data社の研究 2 で報告されている「セマンティックカタログ」と同様の効果を狙ったものです。同研究ではデータベースのメタデータに加え、ビジネスロジックなどを自然言語で記述したセマンティックカタログを用意することで、LLMによるSQL生成の精度が最大27%向上したと報告されています。 --- name: table-index description: 分析に使用可能なログテーブルの一覧と関係性を取得 --- # ログテーブル一覧   `` `yaml  tables:   product_impressions:   description: "商品が表示された時のログ"   product_clicks:   description: "商品がクリックされた時のログ"  relationships:   - from: product_impressions   to: product_clicks   join_key: [impression_id, platform] description: "プロダクトの表示時にimpression_idが発行され、クリック時に同じimpression_idが記録される"   ``` .claude/skills/table-details/SKILL.md このスキルでは、ログのテーブル定義の詳細を提供します。エージェントは table-index スキルを元に選定したログの詳細情報を、このスキルを参照して取得します。スキルディレクトリには SKILL.md に加えて、マークダウンドキュメントやスクリプトなどのサポートファイルを配置できます。 SKILL.md からサポートファイルを参照することで、エージェントは必要なログテーブルの詳細情報のみを読み込みます。これによりコンテキストを効率的に管理できます。 --- name: table-details description: 指定されたログテーブルの詳細仕様を取得 --- # テーブル詳細仕様 詳細な仕様は以下のサポートファイルを参照してください: - [ product _ impressions.md ]( product_impressions.md ) - [ product _ clicks.md ]( product_clicks.md ) .claude/skills/table-details/product_impressions.md(サポートファイル) このファイルでは、ログテーブルの実際の詳細仕様を記載しています。テーブルの詳細仕様は社内ドキュメントツールで各チームが管理しています。各ログのドキュメントURLを記載することで、エージェントがMCPを通じて最新のテーブル情報を取得します。これにより、ドキュメント側のテーブル構造が更新されても、Claude Code側の設定を変更することなく、最新の情報を参照できます。また、推奨されるJOINの条件やタイムスタンプの扱いなど、ドキュメント化されていない知識もここに記載しています。 # 商品表示ログ(product _ impressions) MCPを使用して以下のドキュメントを取得してください。 - テーブル仕様: https://docs.example.com/logs/product _ impressions ## 暗黙知・注意事項 - JOINする際は必ず ` impression_id ` と ` platform ` の両方で結合 - 期間絞り込みは ` event_timestamp ` を ` Asia/Tokyo ` でDATE変換して行う .claude/skills/quality-check/SKILL.md このスキルでは、SQLのコーディング規約を提供します。エージェントがこのスキルを参照し、生成されたクエリが社内のコード規約を遵守しているかのチェックと自動修正をします。 --- name: quality-check description: SQLコーディング規約のチェックと自動修正 --- # SQLコーディング規約 ## フォーマットルール - インデントは半角スペース2つ - 予約語は小文字(select, from, where等) - カンマは行頭に配置 - 1行の文字数は100文字以内 ## 命名規則 - エイリアスは意味のある短い名前(imp, click等) - CTEは処理内容がわかる名前(impressions, clicks等) 2. 3段階エージェントによるコンテキスト最適化 ドキュメントを整備しただけでもクエリの自動生成は可能でした。しかし、大量のログドキュメントを一度に処理すると、コンテキストの肥大化という問題が発生するため、3段階のサブエージェント 3 に分割し、各エージェントが必要最小限の情報だけを扱う設計にしました。サブエージェントは、特定の種類のタスクを処理する特化した エージェントです。各サブエージェントは、カスタムシステムプロンプト、特定のツールアクセス、および独立したコンテキストウィンドウで実行されます。 ステップ エージェント 役割 入力(コンテキスト) 出力 1 要件定義&ログ情報取得 自然言語からの要件定義、必要なテーブルを特定 table-index スキル 必要なテーブル情報、実装方針 2 クエリ作成 SQLクエリを生成 table-details スキル、ステップ1の出力 SQLクエリ 3 品質管理 規約チェックと自動修正 quality-check スキル、ステップ2の出力 SQL規約準拠済みのSQLクエリ この分割により、各エージェントが扱う情報量を最小限に抑え、コンテキストの肥大化を防いでいます。 エージェント定義ファイル 各エージェントは .claude/agents/ ディレクトリにマークダウンファイルとして定義します。 .claude/ ├── ... ├── agents/ # エージェント定義 │ ├── log-data-advisor.md # 要件定義&ログ情報取得 │ ├── query-builder.md # クエリ作成 │ └── query-validator.md # 品質管理 └── ... 各エージェントが誤って自身に関係のないSkillsを読み込むと、コンテキストが肥大化してしまうおそれがあります。そこで、各エージェントの定義ファイルでは、そのエージェントが参照できるSkillsを明確に指定しています。 .claude/agents/log-data-advisor.md --- name: log-data-advisor description: ユーザーの分析要件から必要なログテーブルを特定する skills: - table-index --- あなたはログデータアドバイザーです。ユーザーの分析要件を理解し、必要なテーブルを特定します。 詳細化が必要な場合は、追加の質問を行ってください。 ## 責務 1. ユーザーの分析目的を理解 2. 必要なログテーブルを特定 3. データの関連性と結合条件を明確化 ## 出力形式 - 使用するテーブル名のリスト - テーブル間の結合条件 - 実装方針の概要 .claude/agents/query-builder.md --- name: query-builder description: テーブル情報に基づいてSQLクエリを生成する skills: - table-details --- あなたはSQLクエリビルダーです。テーブル詳細仕様に基づいてBigQuery向けの最適なクエリを生成します。 ## 責務 1. /table-detailsのスキルを参照してテーブル詳細を取得 2. 暗黙知・注意事項を踏まえたクエリ作成 3. パフォーマンスを考慮した最適化 .claude/agents/query-validator.md --- name: query-validator description: 生成されたSQLがコーディング規約に準拠しているかチェックし、自動修正する skills: - quality-check --- あなたはSQL品質管理者です。生成されたクエリのコーディング規約チェックと自動修正を行います。 ## 責務 1. インデント、予約語の大文字小文字などの規約チェック 2. 規約違反の自動修正 3. 修正内容のレポート 3. カスタムスラッシュコマンドによるワークフロー自動化 ここまででもSQL自動生成は可能ですが、3つのエージェントを個別に呼び出すのは手間がかかるため、カスタムスラッシュコマンドで一連のワークフローとして自動化しました。 Skillsはスラッシュコマンドとしても機能します。 .claude/skills/<skill-name>/SKILL.md として作成すると、 /skill-name で呼び出せます。フロントマターで disable-model-invocation: true を指定すると、Claudeが自動的にスキルを適用するのを防げます。これにより、ユーザーがコマンドで明示的に呼び出した場合のみ実行されるよう制御できます。 .claude/ ├── ... └── skills/ ├── make_sql/ # ワークフロー定義(スラッシュコマンド) │ └── SKILL.md ├── ... ... .claude/skills/make_sql/SKILL.md に以下のような設定ファイルを作成します。 --- name: make _ sql description: "ユーザーの要件に基づいて、ログデータを分析し、最適なSQLクエリを生成します" disable-model-invocation: true allowed-tools: Task(log-data-advisor), Task(query-builder), Task(query-validator) --- # SQLクエリ生成ワークフロー このコマンドは、ユーザーの分析要件に基づいて、内部ログデータから最適なSQLクエリを生成する3段階のワークフローです。 ## ワークフローの流れ ### ステップ1: ログデータ要件の分析 @agent-log-data-advisor を使用して: - ユーザーの分析目的を理解 - 必要なログテーブルを特定 - データの関連性と結合条件を明確化 ### ステップ2: SQLクエリの生成 @agent-query-builder を使用して: - ステップ1で特定した要件に基づくクエリ作成 - パフォーマンスを考慮した最適化 ### ステップ3: 品質管理と規約検証 @agent-query-validator を使用して: - 生成されたクエリのコーディング規約チェック - 自動修正による品質保証 実行例と結果 商品の表示やクリックに関する分析クエリを生成する例を紹介します。なお、以下は説明用のサンプルで、実際のテーブル名・スキーマ構造とは異なります。 入力コマンド /make_sql 商品カテゴリ「スニーカー」の表示回数、クリック数、クリック率をプラットフォーム毎に調査するクエリを作成してください 生成されたSQLクエリ(一部省略) -- 商品の表示・クリック分析 with impressions as ( select platform , impression_id , product_category , event_timestamp from `example_project.search_logs.product_impressions` where true and date (event_timestamp, ' Asia/Tokyo ' ) >= ' 2025-09-01 ' and date (event_timestamp, ' Asia/Tokyo ' ) <= ' 2025-09-30 ' and product_category = ' スニーカー ' ) , clicks as ( select platform , impression_id from `example_project.search_logs.product_clicks` where true and date (event_timestamp, ' Asia/Tokyo ' ) >= ' 2025-09-01 ' and date (event_timestamp, ' Asia/Tokyo ' ) <= ' 2025-09-30 ' ) -- メインクエリ select imp.platform , count ( distinct imp.impression_id) as total_impressions , count ( distinct click.impression_id) as total_clicks , round ( safe_divide( count ( distinct click.impression_id) * 100.0 , count ( distinct imp.impression_id) ), 2 ) as click_through_rate from impressions as imp left join clicks as click on imp.impression_id = click.impression_id and imp.platform = click.platform group by imp.platform order by total_impressions desc このように自然言語で要件を伝えるだけで、社内のログを踏まえ、チームのコード規約に従った分析用のクエリが自動生成されました。 プラグイン化 本記事で紹介した一連のフローは、Claude Codeのプラグイン機能 4 を活用して全社展開しています。 プラグインは、Claude Codeの設定やSkills、サブエージェントなどを配布できる機能です。社内にマーケットプレイスリポジトリ 5 を構築してプラグインを公開しているため、社員であれば以下のコマンドで誰でもインストールして利用できます。 マーケットプレイスリポジトリをClaude Codeに登録する。 /plugin marketplace add { リポジトリ名 } マーケットプレイスリポジトリからプラグインをインストールする。 /plugin install { 登録したマーケットプレイス上のプラグイン名 } プラグインを利用する。 / { 登録したマーケットプレイス上のプラグイン名 } :make_sql { 要件 } プラグインの詳細については 公式ドキュメント を参照してください。 課題と今後の展望 より多くのテーブルへの対応 現在テーブルインデックスに記載しているログは、弊チームでよく使うテーブルに絞っています。しかし、社内にはより多くのログが存在しているため、単純に SKILL.md に定義を追加するだけでは非常に多くのコンテキストを消費してしまいます。そこで、要件に合うテーブルを自動で検索する仕組みを検討しています。 テーブルインデックスの自動更新 今回はMCPでテーブルの仕様を社内ドキュメントから取得し、エージェントにテーブルインデックスを生成させていました。しかし、ログの仕様が変わる毎にテーブルインデックスを更新する必要があるため、運用コストが高い状態です。そこで、長期的には自動更新する仕組みを検討する必要があります。 まとめ 本記事では、Claude CodeのSkills機能を活用したSQLを自動生成するフローについて紹介しました。近年AIエージェントの文脈では、コンテキストエンジニアリングが重要であると言われています。コーディングエージェントにおいても、今回のように社内固有の情報やエンジニアが持っている暗黙知を提供することで、より実用的なアウトプットが得られると実感しました。「生成AIは社内のログの知識を持っていない」という課題に対し、ドキュメント整備とエージェント設計で対処するアプローチは、SQL生成に限らず他の業務自動化にも応用できると考えています。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 hrmos.co Claude Code Skills https://code.claude.com/docs/ja/skills ↩ TigerData. "The Database's New User: LLMs Need a Different Database" https://www.tigerdata.com/blog/the-database-new-user-llms-need-a-different-database ↩ Claude Code Sub-Agents https://code.claude.com/docs/ja/sub-agents ↩ Claude Code Plugins https://code.claude.com/docs/ja/plugins ↩ Claude Code Marketplace https://code.claude.com/docs/ja/plugin-marketplaces ↩
アバター
はじめに こんにちは、Developer Engagementブロックの @wiroha です。2月10日に「 ZOZO.swift #2 」をオンラインで開催しました。ZOZOのiOSエンジニアによるiOS特化のイベントです。昨年12月に第1回を開催しており、今回第2回目を開催できました。 イベントはオンライン開催でしたが、可能なメンバーはオフィスに集まって配信しました。その当日の雰囲気も含めてレポートします! 登壇内容まとめ ZOZOのエンジニア5名と、技術顧問の岸川さんが発表しました。 発表タイトル 登壇者 旅先で iPad + Neovim で iOS 開発・執筆した話 lap/らぷ ( @laprasDrum ) デザインもAIに任せる!iPhoneで行うiOS開発 イッセー / 上田 壱成 ( @15531b ) ZOZOTOWN、SceneDelegateへのお引越し つっきー / 續橋 涼 ( @tsuzuki817 ) LiDARが変えたARの"距離感" かっつん / 渡邊 魁優 Claude Code で画面の仕様書を作ろう だーはま / 濵田 悠樹 ( @ios_hamada ) 浮動小数の比較について(XCTestとswift-numerics、微妙な実装の違い) 岸川克己 ( @k_katsumi ) 当日の発表はYouTubeのアーカイブ動画をご覧ください。 www.youtube.com 旅先で iPad + Neovim で iOS 開発・執筆した話 speakerdeck.com らぷからは、旅行中にiPadとNeovimを使ってiOS開発を行った話について発表しました。iPadでもさまざまなツールを駆使して開発できており、楽しんでいる様子が伝わってきました。 デザインもAIに任せる!iPhoneで行うiOS開発 speakerdeck.com 上田からは、AIを活用し、デザインまで含めてiPhoneでiOS開発をした話について発表しました。PCなしで開発するという趣旨の発表が偶然2件続き、需要の高さを感じます。 ZOZOTOWN、SceneDelegateへのお引越し speakerdeck.com 續橋の発表では、AppDelegateからSceneDelegateへの移行についてお話ししました。影響範囲が広い中、イベントやリスクなど丁寧に分類してQAを進めていたそうです。 LiDARが変えたARの"距離感" speakerdeck.com 渡邊からは、LiDARセンサーを活用した計測について発表しました。鏡を写した場合は信頼度がLowとなるなど、さまざまなケースが想定されているのは興味深かったです。 Claude Code で画面の仕様書を作ろう speakerdeck.com 濵田からは、Claude Codeを使って画面の仕様書を自動生成する取り組みについて発表しました。実際のプロンプト例も紹介され、実用的な内容でした。 浮動小数の比較について(XCTestとswift-numerics、微妙な実装の違い) speakerdeck.com 岸川さんからは、Swiftにおける浮動小数点数の比較について発表いただきました。許容誤差に応じて適切なツールや実装を選択する必要があると分かりました。 最後に 今回のイベントでは、iOS開発を行う環境の多様化やAI活用など、さまざまなトピックについて共有しました。ご参加くださったみなさま、ありがとうございました。今後もZOZO.swiftを開催していきますので、ぜひご参加ください! ZOZOでは一緒に働くエンジニアを募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 corp.zozo.com
アバター
.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(2) { display: none; } はじめに こんにちは。Developer Engagementブロック(略称DevEngブロック)の @wiroha です。ZOZO TECH BLOGの運営や、開発者向けイベントの企画・運営などを担当しています。 TECH BLOGの運営において、レビューには一定の工数がかかるため、効率化を進めています。その一環として、Claude CodeのAgent Skills(以下、スキル)を用いたレビュー支援の仕組みを整備しました。Claude Code上で記事のレビューを依頼すると、定義したルールに基づくレビュー結果を得られます。 以下は、スキルによるレビュー結果の抜粋です。 本記事では、このスキルを用いたTECH BLOGレビューの取り組みについて紹介します。 目次 はじめに 目次 背景・課題 解決の方針 スキルの設計 SKILL.md rules.md スキルの使用方法 実行例 導入効果 運用上の注意点 今後の展望 まとめ 付録:rules.md全文 背景・課題 ZOZO TECH BLOGは執筆したチーム内で技術的な正確性をレビューした後、DevEngブロックで文章表現・社内ルール準拠などをレビューして公開しています。DevEngブロックでは、現在2名体制で年間約100本の記事をレビューしており、次の課題があります。 担当者が少なく属人化しやすい 時間がかかってしまう 指摘が多いと漏れが発生しやすく、修正と再レビューの往復で進行が詰まりやすい 記事はGitHub上で管理しており、文章を校正する textlint のGitHub Actionsを動かしているため、ある程度のチェックは自動化されています。 1 ただ、それだけでは網羅できない観点が多くあるため、AIを活用してレビューの自動化をさらに推進することにしました。 解決の方針 AIを活用したレビューには複数の選択肢がある中で、Claude Codeのスキルを用いることにしました。Claude Codeはブラウザやエディタを介さず、ターミナル(CLI)で動作し、手元のファイルや外部サービスの情報を扱いながら作業を支援できるAIツールです。スキルは特定のタスクを実行するためのカスタムモジュールで、ドメイン知識やルールに基づいた処理を一貫した手順でAIに実行させられます。スキルの詳細はClaude Codeの公式ドキュメントをご覧ください。 code.claude.com 今回の場合、 claude コマンドでClaude Codeを起動し、「記事をレビューしてください」のように指示するだけで、スキルが呼び出されてレビューを実行できます。 AIレビューの手段として、ChatGPTのGPTsやGeminiのGemなど対話型AIのカスタマイズ、GitHub Actionsによる自動チェックも検討しました。最終的にスキルを採用した理由は次のとおりです。 ブラウザで使用するAIツールと異なり、記事本文をコピーしてAIにペーストする手間が省ける 同じ仕組みをDevEngブロック以外も使用でき、執筆者によるセルフレビューとしても使える 社内にはClaude Codeの利用者が多く、操作に慣れている レビュールールをGitHub上で管理し、誰でもPRを出して改善提案する運用にできる Claude Code GitHub Actions や Devin でPR上に自動コメントするよりは、任意利用から始めて少しずつAIの精度を高めたい 何度も実行しやすく、レビュールールを継続的に改善しやすい仕組みが重要だと考えました。 スキルの設計 Claude Codeの公式ドキュメント「 Claude をスキルで拡張する 」を参考に、TECH BLOGレビュー用のスキルを設計しました。 執筆時点でのスキルの内容を掲載します。スキル定義ファイルであるSKILL.mdと、TECH BLOGレビューのルールをまとめたrules.mdの2つのファイルで構成されています。 SKILL.md --- name: techblog-review description: "ZOZO TECH BLOGの記事をレビューする。「記事をレビュー」「テックブログをチェック」「entry.mdを確認」などのリクエストで起動" allowed-tools: Read, WebFetch --- ## テックブログ記事レビュー `entry.md` をZOZO TECH BLOGのルールに基づいてレビューします。 ### 手順 1. `entry.md` を読む 2. `rules.md` のルールに基づいてレビューする 3. 記事内のリンクをWebFetchで確認し、リンク切れがないかチェックする 4. 問題がある場合のみ、以下のルールで出力する(「問題なし」「確認のみ」といった項目は記載しない) ### 出力ルール **L{行番号}** - {観点}:{修正内容の要約} ```diff - {修正前の文} + {修正後の文} ``` ### 出力例 **L73** - 文法:「〜なこと」→「〜こと」 ```diff - ドキュメントが古いなことが原因で + ドキュメントが古いことが原因で ``` **L89** - 冗長表現:「〜というのは」→「〜のは」 ```diff - 自動化できるというのは大きな利点です。 + 自動化できるのは大きな利点です。 ``` descriptionには、スキルを起動する際のキーワードを記載しています。allowed-toolsには、スキルが使用できるツールを指定しています。Readは、rules.mdや記事原稿であるentry.mdを読むために必要です。記事内のリンク切れをチェックするために、WebFetchも許可しています。 rules.md 肝となるレビュールールは、DevEngブロックがこれまでに行ってきたレビューやSuggestコメントをもとにしています。Claude Codeに過去3年間のレビューコメントをGitHub CLI 2 で収集させ、ルール案を作成させました。PR上のレビュー履歴を資産として活用し、案は人手で精査してブラッシュアップしました。 以下では、rules.mdの一部を紹介します。全文は長くなるため、付録として記事の末尾に掲載します。 # ZOZO TECH BLOG レビュールール ## 1. 文体・表現 ### 1.1 文体の統一 - **である調とですます調を混在させない** - 基本的にですます調を使用する ### 1.2 文は短くする - **一文が長すぎると読みにくい** - 複数の情報を含む文は分割する - 目安:一文100文字以内 ### 1.3 口語表現の回避 - 口語表現は文語表現に置き換える | 口語 | 文語 | |------|------| | 〜なんですが | 〜ですが | | 無かったです | ありませんでした | | ですので | そのため / したがって | | ないですが | ありませんが | | 食わせる | 与える / 読み込ませる | ### 1.4 曖昧表現の回避 - 「〜と思います」より「〜です」を使う - 「〜ような」で曖昧にせず断定する - 断定できる場合は断定する ``` ❌ 複数の機能を一度にデプロイするようなリリースサイクルを採用していました。 ✅ 複数の機能を一度にデプロイするリリースサイクルを採用していました。 ``` これらの観点の一部は以前Zennの記事「 技術をわかりやすく伝えるテクニカルライティングのtips 」などにまとめて共有していましたが、そこに含まれない多くの観点を整理できました。なお、rules.mdに含まれる例文は、生成されたルール案をもとに手動で改変し、特定の記事が推測されないよう配慮しています。 スキルの使用方法 スキルはブログ執筆用リポジトリのルートに配置しているため、プロジェクトスキルとして自動的に有効になります。 Claude Code上で「今書いている記事をレビューしてください」などと指示すれば、スキルが起動してレビューを実行します。ローカルにある自分の記事をセルフレビューする場合の例は次のとおりです。 Use skill "techblog-review"? と聞かれるので「Yes」と答えると結果が返ってきます。 DevEngブロックのメンバーは、自分が書いていない記事もレビューします。ローカルにない記事もClaude Code上で「 <URL> の記事をレビューしてください」のように指示すれば、GitHub CLIを使って自動的に記事を取得し、レビューしてくれます。 手動でチェックアウトしてきたり、コピー&ペーストしたりする必要がなく、非常に便利です。 実行例 本記事を執筆しながら、実際にスキルを使ってレビューした際の結果を例として紹介します。 1. SKILL.mdでの指定どおり、リンク切れをチェックして有効である旨を表示しています。 2. ルールに書いてある、冗長表現を見つけて指摘しています。 3. ルールに書いていない観点でもチェックしてくれます。行頭に不要なスペースが入っている例です。 4. リンクを埋め込み形式で表示するための embed:cite が誤って入っているのも見つけています。 5. ルールにある「正式名称・表記」の観点で、サービス名の誤りを指摘しています。 6. 広く知られているサービス名だけではなく、ファイル名の誤りも発見できるのは驚きでした。 7. TODOと書いていなくても、消し忘れか対応漏れかもしれないコメントを指摘しており、細やかです。 8. ルールにある「文の流れ」の観点はやや厳密にも感じますが、たしかに読点を入れた方が読みやすいため修正しました。 9. 次の例ではURLの誤りを指摘しており、それ自体は適切であるものの、修正案が英語版ドキュメントのURLになってしまっていました。手動で日本語の方に修正しましたが、こういった誤りもあるため、自動適用はせず人が確認する運用にしています。 以上のように、表記・記法・リンクといった観点を中心に、幅広く指摘できました。 導入効果 実行例で示したとおり、さまざまな観点でチェックできており、修正案を考える時間を短縮できるようになりました。rules.mdで定義した観点に基づき、過去30件のPRのレビューコメントをAIで分類したところ、約75%をカバーできると推定されました。残り25%は「文章の圧縮・再構成」「表現の適切さ」「説明の追加・明確化」といった文脈依存の判断が必要なもので、ルールベースでの検出ができない点でした。 これまで暗黙知としてDevEngブロックメンバーの中で蓄積されていたチェック観点を、rules.mdとして明文化し、AIが担えるようになりました。その結果、レビュー観点が個人の経験やスキルに依存しにくくなり、再現性のあるチェックが行えるようになっています。 また、特定分野の技術を知っていないと見つけづらいタイポなど、自分では見落としていた点も検出でき、記事の品質向上に寄与しています。細部のチェックをAIに任せることで、全体の構成やわかりやすさといった観点に意識を向けやすくなり、読みやすい記事づくりにつながっています。 執筆者にはまだ展開したばかりで、セルフレビューで用いるのは必須とはしていません。ルールの精度を向上させ、執筆段階でセルフレビューとして使うケースが増えていけば、修正と再レビューの往復が減り、執筆者・レビュアー双方の負担軽減につながると考えています。 運用上の注意点 実行例の部分にも記述したとおり、現在の運用ではAIの指摘をそのまま採用せず、提案として扱っています。指摘に誤りが含まれる場合や、厳密すぎる場合があるためです。たとえばルールでは口語表現を回避するよう定めていますが、イベントレポート系の記事では口語の表現を残した方が感情を伝えやすい場合もあります。そのあたりのバランスは人が判断しています。 また、社外秘や推測可能な情報が混入していないかなども人が確認しています。 今後の展望 今後は、スキルの精度向上と機能拡張に継続して取り組みます。記事をレビューした結果をもとにフィードバックループを回し、改善を図ります。 執筆者からの意見次第では、Claude Code以外のAIツールへの対応も検討しています。rules.mdを整備したことで、同じ観点を他のツールにも転用しやすくなりました。Claude Codeを使用していない人でも利用できる導線を用意してもよいかもしれません。 また、本記事に反響があれば、スキルをオープンソース化し最新のルールを社外の方も利用できるようにすることも検討したいと考えています。各社で技術ブログの運用方法は異なるため、本取り組みを通じて、より良いレビューや運用方法について意見交換ができればと思います。 まとめ 本記事では、Claude CodeのAgent SkillsとしてTECH BLOGのレビュー観点を集約し、効率化した取り組みを紹介しました。AIの発展は目覚ましく、優れたツールが次々と登場しています。DevEngブロックは新しい技術を積極的に取り入れ、より良い執筆体験を提供できるよう努めていきます。今回の記事が生成AIをレビュー支援に取り入れる際の設計・運用のヒントになれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。技術記事の執筆が好きな方も大歓迎です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com 付録:rules.md全文 # ZOZO TECH BLOG レビュールール ## 1. 文体・表現 ### 1.1 文体の統一 - **である調とですます調を混在させない** - 基本的にですます調を使用する ### 1.2 文は短くする - **一文が長すぎると読みにくい** - 複数の情報を含む文は分割する - 目安:一文100文字以内 ### 1.3 口語表現の回避 - 口語表現は文語表現に置き換える | 口語 | 文語 | |------|------| | 〜なんですが | 〜ですが | | 無かったです | ありませんでした | | ですので | そのため / したがって | | ないですが | ありませんが | | 食わせる | 与える / 読み込ませる | ### 1.4 曖昧表現の回避 - 「〜と思います」より「〜です」を使う - 「〜ような」で曖昧にせず断定する - 断定できる場合は断定する ``` ❌ 複数の機能を一度にデプロイするようなリリースサイクルを採用していました。 ✅ 複数の機能を一度にデプロイするリリースサイクルを採用していました。 ``` ### 1.5 体言止めの回避 - **体言止めは曖昧になりやすいため、技術記事では避ける** - 文として言い切る形にする - **ただし、箇条書きの場合は体言止めでも許容** ``` ❌ 先月、ついに新機能をリリース。 ✅ 先月、ついに新機能をリリースしました。 ``` ### 1.6 「〜たり」の用法 - 並列の「〜たり」は**2回以上繰り返して使う** ``` ❌ コードを書いたりレビューができます ✅ コードを書いたりレビューをしたりできます ``` ### 1.7 「〜になります」「〜となります」の回避 - 変化しない場合は「です」を使う ``` ❌ 本ツールはログ収集の定番ライブラリになります。 ✅ 本ツールはログ収集の定番ライブラリです。 ``` ### 1.8 丁寧すぎる表現の回避 - 物に対する過剰な丁寧表現は避ける ``` ❌ 環境変数を設定ファイルに追記してあげる必要があります。 ✅ 環境変数を設定ファイルに追記する必要があります。 ``` ### 1.9 敬称 - 社外の方(技術顧問、登壇者等)には「さん付け」を使う - 自社社員には敬称は不要とする ### 1.10 簡潔にできる表現(任意) 簡潔にしたい場合は、以下のように表現を変える - 「なぜ〜したか」→「〜した理由」 - 「どのように〜するか」→「〜する方法」 - 「どれくらい閲覧されたか」→「閲覧数」 --- ## 2. 受動態と能動態 ### 2.1 能動態を優先する - **受動態が多いと読みづらくなる** - 主語を明確にして能動態で書く ``` ❌ 複数の設定ファイルはバッチ処理から順次実行されることで、対象システムの状態を更新していきます。 ✅ バッチ処理が複数の設定ファイルを順次実行し、対象システムの状態を更新します。 ``` ### 2.2 受動態と能動態の混在を避ける ``` ❌ そのAPIに対してタイムアウトを設定されており ✅ そのAPIにはタイムアウトが設定されており ``` --- ## 3. 主語と述語 ### 3.1 主語を省略しない - 主語がないと何の話かわからない ``` ❌ この処理の流れの特徴として、決済完了のタイミングで管理画面からメールをユーザーへ同期的に送信します。 ✅ この処理の流れの特徴として、システムは決済完了のタイミングで管理画面の機能を介してメールをユーザーへ同期的に送信します。 ``` ### 3.2 主語と述語のねじれを避ける - 主語と述語が対応しているか確認する ``` ❌ 特に気になった変更点は、新しいログ出力機能が追加されました。 ✅ 特に気になった変更点は、新しいログ出力機能が追加されたことです。 ❌ 今回の会場は、東京国際フォーラムで開催されました。 ✅ 今回の会場は、東京国際フォーラムです。 ✅ 今回のイベントは、東京国際フォーラムで開催されました。 ❌ 結論はコストと移行の容易さからマネージドサービスを選定しました。 ✅ 結論として、コストと移行の容易さからマネージドサービスを選定しました。 ``` ### 3.3 「〜とは」の後に意味を書く - 「〇〇とは」と書いたら、その後に定義・意味を書く ``` ❌ マイクロサービスアーキテクチャとは、システムを独立した小さなサービス単位に分割し、開発スピードと拡張性を大幅に向上させることができます。 ✅ マイクロサービスアーキテクチャとは、システムを独立した小さなサービス単位に分割し、開発スピードと拡張性を大幅に向上させる設計手法のことです。 ``` --- ## 4. 助詞の使い方 ### 4.1 助詞の誤用 | 助詞 | 悪い例 | 良い例 | |------|--------|--------| | を | サーバーを起動を開始 | サーバーの起動を開始 | | を | パラメータを設定をしました | パラメータを設定しました | | が | テスト実行にかかる時間が、全体時間を占める割合が増えた | テスト実行にかかる時間の、全体時間に占める割合が増えた | ### 4.2 助詞を省略しない ``` ❌ 空レスポンス ✅ 空のレスポンス ❌ パフォーマンス影響が出た ✅ パフォーマンスに影響が出た ❌ この項目は必須でありません ✅ この項目は必須ではありません ``` ### 4.3 助詞と動詞の対応 ``` ❌ 設定値を環境間に複製 ✅ 設定値を環境間で複製 ❌ 2018年より会社を設立しました ✅ 2018年に会社を設立しました (「より」は継続の開始を示すため、一時点の出来事には「に」を使う) ❌ 会場内にノベルティが配布されました ✅ 会場内でノベルティが配布されました ❌ このツールには標準で自動リトライ機能を提供しています ✅ このツールは標準で自動リトライ機能を提供しています ``` ### 4.4 冗長な表現を避ける | 冗長 | 簡潔 | |------|------| | 〜について | 〜を | | 〜に対して | 〜に | | 〜に関して | 〜の / 〜を | | 記載をします | 記載します | | 〜というのは | 〜のは | | 〜ということで | 〜ため / 〜ので | ``` ❌ 運用コストを削減できるというのは大きなメリットでした。 ✅ 運用コストを削減できるのは大きなメリットでした。 ``` ### 4.5 逆接でない「〜が」を避ける - 逆接でない「〜が」を接続に使うことを避ける - **主格の「が」は問題ない** - 単なる文の接続に「〜が」を使うのは避ける ``` ❌ 本機能は外部APIを利用してデータを取得しますが、取得したデータはデータベースへ保存され、ユーザー画面に表示されます。 ✅ 本機能は外部APIを利用してデータを取得します。取得したデータはデータベースへ保存され、ユーザー画面に表示されます。 ``` --- ## 5. 漢字とひらがなの使い分け ### 5.1 ひらがなにする語 - 公用文作成の要領や記者ハンドブックでは、実質的な意味を持たない言葉をひらがなで書くよう定めており、概ねそれに則る。 1. 常用外漢字 2. 形式名詞 3. 接続詞 4. 補助動詞 5. 一部の動詞 6. 副助詞 7. 副詞 など 具体例 | 漢字 | ひらがな | 分類 | 備考 | |------|----------|------|------| | 殆ど | ほとんど | 常用外漢字・副詞 | | | 為 | ため | 形式名詞 | 「〜のため」 | | 事 | こと | 形式名詞 | 「〜すること」 | | 所 | ところ | 形式名詞 | 「改善したいところ」 | | 尚 | なお | 接続詞 | | | 但し | ただし | 接続詞 | | | 又 | また | 接続詞 | | | 下さい | ください | 補助動詞 | 「ご確認ください」 | | 頂く | いただく | 補助動詞 | 「教えていただく」 | | 居る | いる | 補助動詞 | 「動いている」※補助動詞の場合 | | 無い | ない | 補助動詞 | 「問題ない」※補助形容詞の場合 | | 有る | ある | 補助動詞 | 「設定してある」※補助動詞の場合 | | 出来る | できる | 補助動詞・動詞 | | | 迄 | まで | 副助詞 | | | 位 | くらい/ほど | 副助詞 | | | 是非 | ぜひ | 副詞 | | | 丁度 | ちょうど | 副詞 | | | 更に | さらに | 副詞 | | | 予め | あらかじめ | 副詞 | | | 様々 | さまざま | 慣習 | 公用文では漢字表記 | ### 5.2 送り仮名の確認 ``` ❌ 楽しく話ながら作業した ✅ 楽しく話しながら作業した ``` --- ## 6. 語順・修飾 ### 6.1 修飾語は被修飾語の近くに置く ``` ❌ 細かいアーキテクチャの説明は省略します ✅ アーキテクチャの細かい説明は省略します ``` ### 6.2 語順を整理する ``` ❌ 過去1年間のコミット数を開発状況を可視化するダッシュボードで確認してみたところ ✅ 開発状況を可視化するダッシュボードで過去1年間のコミット数を確認してみたところ ``` ### 6.3 同じ単語・名詞の繰り返しを避ける - ただし、並列構造の場合は同じ単語が続いても問題ない ``` ❌ フローが複雑化してリードタイム低下とならないよう、フローの再設計を行いました。 ✅ 複雑化によるリードタイム低下を避けるため、フローの再設計を行いました。 # 並列構造の例(問題なし) ✅ 2024年には初期段階としてフェーズ1をリリースし、翌年の2025年には計画どおりフェーズ2をリリースしました。 ``` --- ## 7. 見出し・構成 ### 7.1 タイトル - 「〜の話」で終わるタイトルは避ける - 主題と副題をダッシュ(──)で繋ぐ形式を推奨 ### 7.2 見出しの統一 - 見出しの文体・形式を統一する(体言止め or 文) - 見出しがバラバラだと読みにくい ### 7.3 前提を書く - 読者が知らない前提(社内ルールなど)は明記する ### 7.4 結論から書く - 前置きが長いと読者が離脱する - 結論→理由→詳細の順で書く ### 7.5 「後述します」の多用を避ける - 「後述」を多用すると読者が混乱する - 可能な限りその場で説明する ### 7.6 文の流れを意識する - 唐突に話が変わらないようにする - 前後の文脈をつなげる ``` ❌ 現在、チームのチャットツールは有料プランで運用しています。必要な情報を後から見返せる仕組みは、業務上欠かせないと考えています。しかし無料プランへ移行すると、過去ログの検索期間に制限がかかります。 ✅ 現在、チームのチャットツールは有料プランで運用しています。無料プランへ移行すると、過去ログの検索に制限がかかります。業務上、情報を後から見返せる仕組みは不可欠と考えています。 ``` ### 7.7 複雑なことは図示する - 文章だけで説明が難しい場合は図を追加する --- ## 8. 記号・フォーマット ### 8.1 全角スラッシュは使わない - 「/」(全角スラッシュ)は使わない - 「/」(半角スラッシュ)または「・」を使う ### 8.2 英語の頭文字をとった略語は大文字 - API(Application Programming Interface)など、英語の頭文字をとった略語は大文字で表記する - class名やファイル名などで小文字表記の場合は例外とする ### 8.3 引用記法 - 引用記法(`>`)は**実際の引用のときのみ**使用する ### 8.4 CustomPathはハイフンつなぎ - アンダースコアは使わない ``` ❌ CustomPath: my_article_path ✅ CustomPath: my-article-path ``` ### 8.5 ダッシュの表記 - タイトルの主題と副題を繋ぐ際は**─を2つ + 前後に半角スペース**を使用 ``` ❌ システム移行の課題にどう立ち向かうか - 私たちが実践した解決策 ✅ システム移行の課題にどう立ち向かうか ── 私たちが実践した解決策 ``` --- ## 9. 正式名称・表記 ### 9.1 サービス・製品名は正式名称を使用 | 誤 | 正 | 参考URL | |----|----|----| | Golang | Go | https://go.dev/ | | Go言語 | Go | https://go.dev/ | | Spring boot | Spring Boot | https://spring.io/projects/spring-boot | | Alloy DB | AlloyDB | https://cloud.google.com/alloydb | | Firebase app check | Firebase App Check | https://firebase.google.com/docs/app-check | | Secrets manager | Secrets Manager | https://aws.amazon.com/secrets-manager/ | | slack bot | Slackbot | https://slack.com/help/articles/202026038 | | Kintone | kintone | https://kintone.cybozu.co.jp/ (小文字始まり) | | docker | Docker | https://www.docker.com/ (大文字始まり) | | JS Nation | JSNation | https://jsnation.com/ (スペースなし) | | iosDC | iOSDC Japan | https://iosdc.jp/ | | ArgoCD | Argo CD | https://argo-cd.readthedocs.io/ (スペースあり) | ### 9.2 社名・サービス名の正式表記 - 社名やサービス名は公式サイトを確認し、正式名称を使用する - スペースの有無、大文字小文字を正確に記載する - ルールに記載されていない名称も都度検索して確認すること **ZOZOに関する表記** - 「ZOZO」はZOZOTOWNの略称ではない - 「ZOZO Yahoo!店」→「ZOZOTOWN Yahoo!店」 --- ## 10. はてなブログ固有のルール ### 10.1 太字記法 - はてなブログでは `**太字**` の前後に半角スペースは入れない ### 10.2 埋め込み記法 - URLは埋め込み形式にすると見栄えが良い - `[https://example.com/:embed:cite]` ### 10.3 キャプション記法 - 画像にはfigureタグでキャプションを付けられる ```html <figure class="figure-image figure-image-fotolife" title="タイトル"> [f:id:vasilyjp:20230101120000] <figcaption>キャプション</figcaption> </figure> ``` ### 10.4 Markdownの改行 - パラグラフをわけるときは空行を入れる - 同じパラグラフ内で改行する必要がある場合は `<br>` か ` `(半角スペース2つ)による強制改行を使う。ただし、デバイス依存の表示崩れやSEO・アクセシビリティの低下などを招くため、必要な場合のみに留める。 ``` 1行目 2行目(別のパラグラフになる) ``` ### 10.5 箇条書きの前後には空行が必要 - 箇条書き(`-` や `1.`)の前後には空行を入れる - 空行がないとレイアウトが崩れる場合がある ``` ❌ 下記の項目を記載しています。 - 依頼の概要 - 依頼部門の課題 ✅ 下記の項目を記載しています。 - 依頼の概要 - 依頼部門の課題 ``` ### 10.6 続きを読む記法 - `<!-- more -->` ははてなブログで「続きを読む」の区切りとして使用される - このコメントは削除しない --- ## 11. リンク・参照 ### 11.1 リンク切れの確認 - リンクが有効か確認する(404になっていないか) ### 11.2 リンク先の確認 - リンク先が正しいか確認する - リンク先が不適切・不確かな情報でないか確認する ### 11.3 リンクテキストの書き方 - 「こちら」だけでなく、リンク先が分かるようにテキストを明示する ``` ❌ 詳しくは[こちら](https://example.com)をご覧ください。 ✅ 詳しくは[公式ドキュメント](https://example.com)をご覧ください。 ✅ 詳しくは「[独自のアプリケーションの作成](https://example.com)」を参考にしてください。 ``` ### 11.4 採用リンク - 記事末尾の採用リンクは有効か確認する --- ## 12. その他 ### 12.1 ファイル末尾 - ファイル末尾の改行は必須ではない ### 12.2 機密情報 - 機密情報が含まれていないか確認する - 例示で使う数字はダミーにし、その旨を明記する ### 12.3 将来の機能・予定 - 未確定の将来計画は公開可否を確認する ### 12.4 引用・著作権 - 他サイトの画像・スクリーンショットを使用する場合は引用の要件を満たす - 引用元を明記する - 参考: [文化庁「著作権を学ぶ(教材・講習会)」](https://www.bunka.go.jp/seisaku/chosakuken/seidokaisetsu/index.html) ### 12.5 画像の掲載許諾 - 人物写真は掲載許諾を確認する - 他社ロゴ・アイコンは使用許可を確認する ### 12.6 追記の書き方 - 記事公開後に追記する場合は日付を明記する ``` ❌ 追記:〜 ✅ (2023/5/16追記)〜(追記ここまで) ``` 既存の仕組みの詳細は TECH BLOG執筆支援のためのCI/CD導入事例 をご覧ください。 ↩ GitHub公式のコマンドラインツール https://docs.github.com/ja/github-cli/github-cli/about-github-cli ↩
アバター
.table-of-contents > li:nth-child(2) { display: none; } はじめに こんにちは、ZOZOTOWN開発2部Androidブロックの大江です。普段はZOZOTOWN Androidの開発を担当しています。 UIのリグレッション防止を目的として、2024年からVisual Regression Testing(VRT)の導入に取り組んできました。その第1弾として共通UIコンポーネントのモジュールに対してJetpack ComposeのPreviewを使ったVRTを運用しています。 本記事では、VRT導入について、VRTをCI/CDツール上で実行するための工夫を中心に紹介します。 目次 はじめに 目次 ZOZOTOWN Androidの課題 VRTとは VRTの冗長な処理を防ぐための工夫 画像生成 画像比較 結果出力 まとめ ZOZOTOWN Androidの課題 ZOZOTOWN Androidは画面の数が多く、長年の開発によってモジュールの依存関係やUIコンポーネントの共通化の仕組みが複雑になっています。そのため、レイアウト変更を伴う開発では、変更した箇所以外の画面にも影響を及ぼす可能性があります。 こうした影響を漏れなく把握することは難しく、レビュー時の目検だけでは、意図しないレイアウトの変化を見落としてしまうケースが多くありました。これらの課題を解決するため、私たちはVisual Regression Testing(VRT)の導入を決定しました。 VRTとは Visual Regression Testing(VRT、ビジュアルリグレッションテスト)は、レイアウトの差分を自動的に検出するテスト手法です。アプリケーションの実装を変更する前後でスクリーンショットの画像を生成して、両者を比較することで意図しないレイアウトの変更を検出します。VRTを導入することで、目検では見落としやすいレイアウトの変更を自動で検出できるようになります。 VRTは強力な仕組みですが、画像を大量に生成するため、マシンのリソースを消費しやすいなどの課題があります。そのためGitHub Actionsなどで実行する際には冗長な処理を防ぐことが重要です。 VRTの冗長な処理を防ぐための工夫 VRTの冗長な処理を防ぐための工夫を説明します。実際に運用しているGitHub ActionsでPull Requestに対してVRTを適用するフローを紹介します。 例として以下の状況のPull Requestに対してVRTを適用した場合を考えてみます。 develop branchの最新commitのhashがf9c2dcc develop branchからfeature branchを作成 feature branchに対してhashがce56c9dとbfe0a66のcommitを追加 feature branchをdevelop branchにmergeするPull Requestを作成 わかりやすくするために、VRTを以下の3つのステップに分けて説明します。 画像生成 画像比較 結果出力 画像生成 まず実装を変更する前後のアプリケーションに対して画像を生成します。ZOZOTOWN Androidでは、Roborazziというライブラリを使用してComposableのPreviewの画像を生成しています。 実装を変更する前後としてPull Requestのbase branchとhead branchの画像をそれぞれ生成します。画像の生成はマシンのリソースを消費しやすいため、冗長な処理を防ぐことが重要です。そこで生成した画像を可能な限り使い回すように工夫しました。 head branchはcommit&pushされて実装が変更される度に画像を生成する必要があります。一方、base branchはPull Requestがmergeされるまで変更されず、複数のPull Requestで同じbase branchが参照されることもあります。そこで生成したbase branchの画像をbase branchの最新のcommitのhashに紐づけて保存しておき、このhashが変わらない限り使い回すようにしています。 このように生成した画像をcommitのhashに紐づけて保存するためにアプリケーションのプロジェクトとは別にGitHubのRepositoryを用意しました。画像保存用のRepositoryに対して、commitのhashをbranch名として画像を保存することでこの仕組みを簡単に実現できました。前述の例ではf9c2dccというbranch名でbase branchのアプリケーションの画像が保存されます。 この仕組みを次の図に表しました。 画像比較 次にPull Requestのbase branchとhead branchの画像を比較します。 Pull Requestにcommitがpushされる度にhead branchの画像を生成し、base branchは最新のcommitのhashに紐づいた画像がない場合のみ画像を生成します。両者の画像が揃ったところで画像を比較します。 ZOZOTOWN Androidでは、画像の比較にも前述のRoborazziというライブラリを使用しています。Roborazziはコマンドを実行するだけで画像を比較し、その結果を出力してくれます。 比較結果は差分を抽出した画像として出力されます。例として、ボタンの横幅がわずかに変わってしまっていることを検出した際の出力結果を載せておきます。 このように目検では見落としがちな意図しないレイアウトの変更をVRTによって検出できます。 結果出力 最後に比較結果の画像を出力してPull Requestに添付します。 Pull Requestにはリンクで比較結果の画像を添付するため、比較結果の画像をクラウドストレージに保存する必要があります。ZOZOTOWN Androidでは、前述したアプリケーションの画像生成の工程と同様に、比較結果の画像もGitHubのRepositoryに保存しています。ただし管理を容易にするため、アプリケーションの画像を保存するRepositoryと比較結果の画像を保存するRepositoryは分けています。 比較結果の画像はPull Requestと紐づけて管理します。Pull Requestのhead branchとbase branchのbranch名を_to_でつないだ値をbranch名として保存しています。前述の例ではfeature_to_developというbranch名で比較結果の画像が保存されます。 まとめ 本記事では、ZOZOTOWN AndroidにおけるVisual Regression Testing(VRT)の導入について紹介しました。 現状では共通パーツのみにVRTを適用しているため、全体の約14%のComposableがVRTの対象となっています。今後、VRTを適用するモジュールを増やすなど、VRTをさらに活用していきます。Android開発でVRTの導入を検討されている方がいれば、ぜひ本記事を参考にしてみてください。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
アバター
.table-of-contents > li:nth-child(2), .table-of-contents > li > ul > li > ul { display:none; } はじめに こんにちは、情報セキュリティ部の 兵藤 です。日々ZOZOの安全を守るためSOC業務に取り組んでいます。 本記事ではサイバー脅威インテリジェンスプラットフォーム「OpenCTI」からSplunkへの脅威インテリジェンスの取り込みについて紹介します。また、この内容については以下の「OpenCTIをSentinelに食わせてみた」に関連した内容となっています。 techblog.zozo.com 目次 はじめに 目次 背景と概要 構築 Splunkへのログ移行 Splunk Add-on EventhubとMDEの設定 SplunkでのOpenCTI連携設定 OpenCTI Add-on for Splunkの利用 OpenCTI側のStream設定 運用 Splunk ESでのThreat Intelligence framework OpenCTIのインジケータをThreat Intelligence frameworkに組み込む Threat Activity Detected ルール MDEのファイルハッシュのバージョン差分 除外の設定 まとめ おわりに 背景と概要 ZOZOのSOC業務において以前はMicrosoft Sentinelを活用していました。現在では業務範囲の拡大や運用効率化など、様々な理由からSplunk Enterprise Security(以降 Splunk ES)へ移行しました。移行に伴い、Sentinelで構築していた脅威インテリジェンスを用いた検知をSplunkでも行うため、OpenCTIからの脅威インテリジェンスの取り込み方法を再検討する必要がありました。 以前の記事では、Logic Appsを使用してOpenCTIからSentinelへ脅威インテリジェンスを取り込む方法を紹介しましたが、今回はSplunkに対応するため異なるアプローチを採用しました。 Splunkにはデータを取り込むための各種Add-onが提供されており、その中には「 OpenCTI Add-on for Splunk 」も含まれています。このAdd-onを利用することで、OpenCTIからSplunkへのデータ取り込みが容易になります。 上記を用いて、脅威インテリジェンスを用いた検知を自動で行うようにOpenCTIのインテリジェンスをSplunk ESに食わせました。 構築 以下の環境で構築しました。 OpenCTI 6.9.13 Splunk Cloud 9.3 Splunk Enterprise Security 8.3 ※ 最近バージョン7系から8系にアップグレードしました Splunkへのログ移行 SentinelからSplunkへの移行に伴い、各種ログソースのデータをSplunkに取り込む必要がありました。Splunkはオンプレからクラウド環境まで各種ログをindexerというコンポーネントに取り込むことが可能です。取り込み方も多種多様で、各ログソースに応じた方法があります。ここでは、脅威検知の対象となるログの1つとしてMicrosoft Defender for Endpoint(MDE)を例に取り込み方法を紹介します。 Splunk Add-on Splunkで脅威情報を検知するためには、MDEのインシデントやアラートのログだけではなくテレメトリのログを投入する必要があります。MDEのテレメトリログをSplunkに取り込むためには、「 Splunk Add-on for Microsoft Cloud Services 」を使用します。 また、ログを正規化(CIM化)するために「 Microsoft Defender Advanced Hunting Add-on for Splunk 」のAdd-onも追加します。 Microsoft Cloud ServicesのAdd-onを用いてMDEのログを取得するためにはAzureのアプリケーション登録が必要です。Splunkからこのアプリケーション経由でログをとりに行くのですが、そのためにはクライアントシークレットが必要なので以下の画像のようにそちらも登録します。 クライアントシークレット このシークレットを使用してAdd-onの設定上にある「Azure App Account」に登録します。 Azure App Account その後、Add-onの「入力」設定からどのようなログを取り込むかの設定をします。今回はAzureの Event Hubs 経由でMDEのログを取得するように設定しました。ここからの設定は事前にEvent Hubsを構築しておく必要があります。 Event Hubsでの入力 ここでIndexやソースタイプなどを選択可能です。ソースタイプには「Microsoft Defender Advanced Hunting Add-on for Splunk」を入れたら適用できるようになる mscs:azure:eventhub:defender:advancedhunting を設定するとCIM化ができます。 詳しくはこちらの 公式ドキュメント を参照してください。 EventhubとMDEの設定 SplunkにMDEのログを取り込む為にEvent Hubsを作成する必要があります。詳しい作成方法は 公式ドキュメント を参照してください。 先ほどSplunk Add-onで登録したAzureアプリケーションに、このEvent Hubsへの読み取り権限を付与することで、ログを読み取れるようになります。具体的にはAzureのARM APIを用いたロールです。Entraなどの設定ではなくAzure Resourceごとの設定となります。Event Hubsなどの「アクセス制御 (IAM)」の項目から設定できます。 Event HubsのIAM設定 ロールとしてはデフォルトで存在するものでは「Azure Event Hubs のデータ受信者」のロールを付与すればSplunkからログを読み取れるようになります。以前は SASを用いてアクセスする方法 もありましたが、ロールを定義してアクセスする方法が推奨されています。 続いて、MDEからEvent Hubsへログを送信する設定します。MDEのポータルから「システム」→「設定」→「Microsoft Defender XDR」→「ストリーミング API」へ進み、「追加」をクリックします。以下のようにEvent Hubsの情報を入力できます。 ストリーミングAPIの設定 これでMDEのログがEvent Hubsへ送信され、Splunk Add-onで取り込めるようになります。 SplunkでのOpenCTI連携設定 OpenCTI Add-on for Splunkの利用 OpenCTIからSplunkへ脅威インテリジェンスを取り込むには、Notionの 「OpenCTI Ecosystem」 に紹介されている以下の「Splunk App」を利用します。 NotionのOpenCTI Ecosystem Splunkbaseには OpenCTI Add-on for Splunk で紹介されています。このAdd-onをSplunkにインストールし、OpenCTIとの連携設定を行います。基本的に、OpenCTIへのURLとAPIトークンを設定し、入力を作成するだけでOpenCTIからデータを取得できます。 OpenCTI Add-on for Splunkの設定 OpenCTI Add-on for Splunkの入力設定 入力欄の「Stream ID」はOpenCTI側で設定が必要です。Streamを作成しておきましょう。 OpenCTI側のStream設定 Stream の設定はOpenCTIの「データ共有」の項目から設定します。 データ共有 「ライブストリーム作成」をクリックすると以下のようにStreamの作成画面が表示されます。 ライブストリーム作成 基本的に、APIトークンでアクセスするため、公開ストリームにする必要はありません。自作したGroupを「アクセス可能」の欄に設定するだけでそのGroup内に所属するUserであればこのStreamにアクセス可能となります。例えば、SOC運用でOpenCTIを利用しているGroupを作成し、そのGroupにアクセス権限を付与すると以下のようになります。 アクセス可能グループの設定 これでSOC運用を行なっているメンバーがこのStreamへアクセスできるようになります。うまくStreamできていることが確認できれば、Stream読み取り用のロールを持つGroupを作成しておくと良いでしょう。 また、Splunkに連携するインジケータもフィルタリングして選別が可能です。以下のようにスコアなどでフィルタリングできます。 フィルタリング設定 条件をつけ作成したら以下のようにStream IDの発行されたライブストリームが作成されます。例としてAbuse.chのフィードを流すStreamを作成しました。 Stream IDの発行 最後に開始ボタンを押してStreamを開始します。 Stream開始 その後、このStream IDをSplunkのAdd-on側で設定すればOpenCTIからSplunkへデータが流れ込みます。以下のようにAdd-onの「Indicators Dashboard」で確認できれば接続成功です。 Indicators Dashboard 運用 ここからの運用のお話はSplunk ESのバージョン7系のものを引き継いだ内容を記載します。8系の最新機能などはあまり含まれていないため、ご了承ください。 Splunk ESでのThreat Intelligence framework Splunk ESで脅威インテリジェンスを活用するにはどのようなフレームワークを用いているか理解するとスムーズです。どのようなイメージでインテリジェンスを処理しているかはSplunk DEVのドキュメント 1 の図を見るとわかりやすいです。 簡単に整理すると、インテリジェンスのデータソースから読み込んだ脅威インテリジェンスを正規化し、Splunk内にkvstore(脅威インテリジェンスのマスタテーブルに相当するもの)として保存されます。その後、 Threat_Intelligence のデータモデルがある程度マッチさせてくれます。 Threat Intelligence framework Splunk ESでの具体的な名称は以下のようになります。IPアドレスの場合を例に挙げます。 データソース: local_ip_intel emerging_threats_compromised_ip_blocklist kvstore: ip_intel OpenCTIのインジケータをThreat Intelligence frameworkに組み込む ではここでOpenCTIのインジケータがAdd-onを通じてSplunkにどのように取り込まれているか確認します。Add-onの 解説 を見ると、以下のように opencti_lookup のkvstoreに保存されます。 opencti_lookup Add-onにはこのkvstoreを用いたカスタムアラートアクション作成手順が記載されています。ですが、ここではSplunk ESのThreat Intelligenceのframeworkに組み込む方法を紹介します。 Splunk ESのインテリジェンスのデータソースの設定は「設定」→「Threat intelligence」→「Threat intelligence sources」から確認できます。ローカルからのソース設定もできますが、kvstoreの設定はないので直接 opencti_lookup から取り込むことはできそうにありません。そのため opencti_lookup の内容を既存の local_ip_intel や local_file_intel などのデータソースに流し込む必要があります。 解決策としてスケジュールサーチを用いて opencti_lookup からデータを正規化し、各データソースに流し込む方法を採用しました。データソースに流し込むためのフィールドは 公式ドキュメント を参考に設定できます。 以下はIPアドレスのSPL例です。 | inputlookup opencti_lookup | where type IN ("ipv4-addr", "ipv6-addr") AND strptime(valid_from, "%Y-%m-%dT%H:%M:%S.%QZ") <= (now() - 9*3600) AND relative_time(now(), "+12h") <= strptime(valid_until, "%Y-%m-%dT%H:%M:%S.%QZ") | eval description=created_by, ip=value, weight=score | fields description ip weight | outputlookup local_ip_intel インジケータの有効期限のフィールド valid_from や valid_until もOpenCTI側で設定できるため、これらを用いて有効なインジケータのみを取り込むようにしています。また各種フィールド名もSplunk ESのデータソースに合わせて変換しています。最後に outputlookup でデータソースに流し込みます。 description のフィールドは脅威検知のSPLで見ることができる項目のため、こちらに好きなフィールドを設定すると良いでしょう。例えば created_by に設定しておくとどこからのインテリジェンスなのかが一目でわかります。またこの設定だと同じインテリジェンスが複数回 local_ip_intel に流し込まれます。ですが threat_key をKeyとしたkvstore内では上書きが走るだけなのでkvstore内のデータが膨れ上がることはありません。既存のインテリジェンスがあるものは join を利用し where で除外する方法もありますが、最新の情報に更新したい場合はkvstoreの性質を活用し上書きする形が適していました。 このスケジュールスキャンをどのタイミングで実行するかは local_ip_intel が ip_intel に取り込まれるタイミングによって設定すれば良いです。デフォルトの値は12時間(43200s)なので、12時間毎にとりあえず試してみるのも良いでしょう。この値は変更できるので好きなタイミングで実行間隔を制御できます。 Threat Activity Detected ルール kvstoreに格納されたOpenCTIのインテリジェンスを用いて検知するルールは「Threat Activity Detected」のEvent-based detectionのみで完結します。Splunkのデータモデルが活きてくるいい例です。 このdetectionのSPL冒頭は以下となっており、単純にマッチした Threat_Activity を拾ってくるだけで検知することがわかります。 | from datamodel:"Threat_Intelligence"."Threat_Activity" どのようなログのデータをマッチさせているかは「Threat matching」の項目から確認できます。例えば src フィールドに入ってくるIPとマッチさせる設定は以下のようになっています。 srcのマッチング設定 各データモデル( All_Traffic や Web など)の src に脅威インテリジェンスのIPアドレスがマッチした場合に Threat_Activity として検知されます。OpenCTIから取り込んだインジケータもこの ip_intel に格納されているため、OpenCTI由来のインジケータであっても問題なく検知されます。 逆に言えば、各種データモデルのtagが付いてない生ログだとこのデータモデルにログとして取り込まれないので検知がされません。MDEのCIM化されたログは基本的に network などはついているものの、 web などのtagがついていません。MDEのログと脅威インテリジェンスの以下 url などの項目をマッチさせるためには別途「計算済みフィールド」と「タグ」の設定を追加する必要があります。基本的にMDEはエンドポイントのログなので url の設定はお好みで良いでしょう。今回はIPのマッチングやファイルハッシュのマッチングに絞って記載します。 urlのマッチング設定 「Threat Activity Detected」はデフォルトの状態からONにするだけで利用できます。ですが、公開サーバのログを取り込んでいる場合は src のマッチングで大量検知の可能性があります。 dest で抜けていく通信は見たいが、 src で来るものは除外する要件がある場合には工夫が必要です。そこで、ZOZOでは必要に応じてルールの条件を以下のように絞っています。 | where NOT (orig_sourcetype="<公開Productのトラフィックログソースタイプ>" AND source="threatmatch://src") このようにすることで、公開サーバのトラフィックログでの src マッチングを除外しています。必要に応じて条件を追加してみてください。 実際に検知が起こると以下のように表示されます。 Threat Detected ! MDEのファイルハッシュのバージョン差分 MDEのログをCIM化する「Microsoft Defender Advanced Hunting Add-on for Splunk」のAdd-onアプリによって以下の file_hash で検知挙動に大きく差が出ます。 file_hashのマッチング設定 このAdd-onのバージョン 1.4.2 より前では MD5 のハッシュ値が file_hash フィールドに格納されていました。 1.4.2 以降のバージョンでは SHA256 のハッシュ値が格納されるようになりました。この変更後OpenCTIから取り込んだインジケータで SHA256 のハッシュ値を用いている場合、 file_hash でのマッチングが行われます。元々 MD5 でインジケータを取り込んでいる場合はマッチングされなくなるため注意が必要です。 この変更はCommit 0684d4d 2 で行われた内容のようで、最新のバージョン 1.4.2 でSplunkbaseに組み込まれたようです。GitHub上では、まだ 1.3.9 のリリースしか見えないため、バージョン管理が少しわかりづらいですが、Splunkbase上のバージョンを確認して適切なバージョンを利用してください。 除外の設定 脅威検知まではこれでできるようになりますが、このインテリジェンスは調査の結果「検知までする必要はないかも」といったケースも出てきます。そういった場合はサーチマクロを作成し、除外設定を行います。以下が一例です。 where NOT threat_match_value IN ("<IPアドレス1>", "<IPアドレス2>") このマクロを「Threat Activity Detected」内のSPLに追記するだけで除外が可能です。 8系では、 SafeList という除外リスト機能が追加されています。今後はこちらを活用していくことも検討しています。 まとめ 本記事ではOpenCTIからSplunkへ脅威インテリジェンスを取り込み、Splunk ESで脅威検知する手法について紹介しました。OpenCTI Add-on for Splunkを用いることで比較的容易に連携が可能となり、SOC業務での脅威インテリジェンス活用が促進されます。 OpenCTIを用いた自動でのインテリジェンス検知運用の記事は事例が少ないので、参考になれば幸いです。 ZOZOではこれからも脅威インテリジェンスを逐次収集し、SOCの検知能力の向上に努めていき、ZOZOの安全性を高めていきたいと考えています。 おわりに ZOZOでは、一緒に安全なサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください! corp.zozo.com Threat Intelligence framework in Splunk ES ↩ Commit 0684d4d ↩
アバター
ZOZO開発組織の2026年1月分の活動を振り返り、ZOZO TECH BLOGで公開した記事や登壇・掲載情報などをまとめたMonthly Tech Reportをお届けします。 ZOZO TECH BLOG 2026年1月は、前月のMonthly Tech Reportを含む計14本の記事を公開しました。特に次の2記事は反響も大きく、とても多くの方に読まれています。ぜひご一読ください。 techblog.zozo.com techblog.zozo.com 登壇 LODGE XR Talk Vol.35 1月22日に開催された「 LODGE XR Talk Vol.35 」に、技術戦略部の諸星( @ikkou )が登壇しました。 techblog.zozo.com ZOZO開発部門の事例に学ぶ!組織横断で成果を生むAI活用 1月30日に開催された「 ZOZO開発部門の事例に学ぶ!組織横断で成果を生むAI活用 〜潜在課題を定量データで捉える課題解決アプローチ〜 」に、技術戦略部 テックリードの堀江( @Horie1024 )が登壇しました。 掲載 ITmedia ビジネスオンライン ITmedia ビジネスオンラインに、2025年に実施したChatGPTの自作GPTs利用率を競わせる社内の生成AIコンテスト「ZOZO GPTs LEAGUE」に関する記事が掲載されました。 www.itmedia.co.jp マイナビニュース マイナビニュースに、1月29日から31日の3日間にかけて開催された「TOKYO PROTOTYPE」にZOZO NEXTが出展した「Alternative Crafts produced by ZOZO NEXT」の様子が掲載されました。 news.mynavi.jp その他 2026年3月期 第3四半期決算発表 1月30日に2026年3月期 第3四半期決算を開示しました。詳細は以下のリンクにある開示資料をご確認ください。 corp.zozo.com 以上、2026年1月のZOZOの活動報告でした! ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
アバター
.table-of-contents > li:nth-child(2), .table-of-contents > li > ul > li > ul > li { display: none; } はじめに 検索基盤部 検索基盤ブロックの佐藤( @satto_sann )です。普段はZOZOTOWNの検索システムの開発や、社内エンジニア向けAI活用推進に取り組んでいます。 AI活用推進の一環として、エンジニアを対象とした プロンプトエンジニアリングのハンズオン を実施しました。本記事では、そのハンズオンの内容や、実施を通じて得られた学びについて紹介します。 具体的には、以下の内容をまとめています。 代表的な8つのプロンプトエンジニアリング手法と、実務で使える具体的なプロンプト例(良い例・悪い例) ハンズオン実施後に実施した参加者アンケートの結果 企画・運営を通じて見えてきた学びや改善ポイント 社内で生成AIの活用を推進している方や、「プロンプトエンジニアリングという言葉は知っているものの、実務ではうまく使いこなせていない」と感じている方にとって、少しでも参考になれば幸いです。 目次 はじめに 目次 プロンプトエンジニアリングとは ハンズオン実施の背景と目的 背景 なぜハンズオン形式を選んだのか ハンズオンの目的 ハンズオンの内容 基本原則:AIとの対話の基本 紹介した8つの手法 1. ロールプロンプト(役割の付与) 2. Zero-Shot・One-Shot・Few-Shot 3. CoT(Chain-of-Thought) 4. Zero-Shot CoT 5. プロンプトチェーン プロンプトの構造化 6. 区切り記号を使った構造化 7. XML形式を使った高度な構造化 8. 出力形式の指定 ハルシネーションへの注意 ハンズオンの成果 ハンズオンの内容満足度 業務活用イメージ 感想 実施してみての学び 体系的な学習機会の重要性 実務に即した例の効果 継続的な学習のサポート まとめ おわりに プロンプトエンジニアリングとは プロンプトエンジニアリングとは、LLM(大規模言語モデル)から期待通りの出力を得るために、LLMへの指示文(プロンプト)を設計する技術のことです。 どんなに強力なAIツールを使っていても、適切なプロンプトでなければ本来の能力を十分に発揮できません。適切なプロンプトエンジニアリングにより、求めている情報や形式に沿った的確な出力を引き出すことができるようになります。つまり、プロンプトエンジニアリングは、AIという強力なツールを「使いこなす」ための必須スキルです。 ハンズオン実施の背景と目的 背景 生成AIを業務に取り入れる企業が増える中、「プロンプトエンジニアリング」という言葉を耳にする機会も多くなっているのではないでしょうか。しかし、体系的な学習機会がないまま、なんとなくの理解で終わっている方も少なくないと感じています。 社内でもChatGPTをはじめとする生成AIツールの活用が進んでいましたが、プロンプトエンジニアリングを体系的に学べる資料や機会は提供できていませんでした。そのため、各手法をどこまで知っているかは個人によってばらつきがある状態だったと考えています。 また、プロンプトエンジニアリングの手法についてまとめた記事は世の中にたくさんありますが、実務で応用しやすい形式になっているものは意外と少ないと感じていました。そこで、社内のエンジニア向けに体系的な学習ができるハンズオンを企画・実施しました。 なぜハンズオン形式を選んだのか 社内でプロンプトエンジニアリングに関する知識を共有する方法として、ドキュメント配布や動画共有も検討しました。しかし、プロンプトエンジニアリングは実際に手を動かして試行錯誤することで身につく部分が大きいと考え、ハンズオン形式を採用しました。 参加者が自分の手でプロンプトを入力し、出力の違いを体感することで、より実践的な理解が得られると期待していました。 ハンズオンの目的 参加者が以下を持ち帰れることを目指しました。 「なんとなく使っていた」状態から「意図を持って使い分けられる」状態への変化 明日から業務で試せる具体的なプロンプトパターン ハンズオンの内容 ハンズオンでは、まずプロンプトを書く上での 基本原則 を確認した後、実務で活用しやすい 8つの手法 を実際に試してもらいました。最後に、生成AIを使う上で避けて通れない ハルシネーション についても注意喚起を行いました。 以降では、それぞれの内容を具体的なプロンプト例とともに紹介します。 基本原則:AIとの対話の基本 プロンプトを書く上での基本原則は以下の4つです。 簡潔 :過不足なく簡潔に伝える 明確 :タスクは具体的かつ明確に定義する 一問一答 :質問は1つずつ絞る 察して禁止 :AIはあなたの意図を理解しません。例や背景、課題を添えて理解を助ける 特に「察して禁止」については、人間同士のコミュニケーションとの違いとして強調しました。AIは文脈を察する能力が限定的なため、必要な情報は明示的に伝える必要があります。 紹介した8つの手法 1. ロールプロンプト(役割の付与) 項目 説明 使いどころ ・ 特定の専門知識や視点を持つ人物として回答してほしいとき ・ 複数の立場からの意見を得たいとき メリット ・ 回答の深さ・具体性が増す ・ トーンや表現が一貫する 注意点 ・ 役割設定が曖昧だと効果が薄れる ・ ターゲット(誰向けか)も併せて指定すると効果的 AIに特定の役割やペルソナを与える手法です。「あなたは熟練したPythonエンジニアです」のように役割を指定することで、その専門性を持った視点からの回答を得られます。 ハンズオンでは、コードレビューのシーンを例に、役割を指定した場合としない場合の出力の違いを確認しました。 ❌ 悪い例 以下コードをレビューしてください ### コード ### import csv # CSVファイルを読み込む with open('data.csv', mode='r', encoding='utf-8') as file: reader = csv.reader(file) for row in reader: この例では、相手(ペルソナ)の属性が不明で、誰の立場でフィードバックするかが不明確です。 ✅ 良い例 あなたは熟練したPythonの知識があり、チーム開発を行う優しい先輩エンジニアです。 Pythonに不慣れな後輩エンジニアから以下コードのレビュー依頼があったので フィードバックしてください。 ### コード ### import csv # CSVファイルを読み込む with open('data.csv', mode='r', encoding='utf-8') as file: reader = csv.reader(file) for row in reader: ロールが明確になることで、指摘の深度や伝え方が調整され、より実務的で具体的なフィードバックが得られることを体感してもらえたと思います。 2. Zero-Shot・One-Shot・Few-Shot 項目 説明 使いどころ ・ Zero-Shot:シンプルなタスク ・ One-Shot:フォーマット指定 ・ Few-Shot:複雑なルールの理解 メリット ・ 例を増やすほど精度と一貫性が向上 ・ 微妙なニュアンスにも対応可能 注意点 ・ 例が多すぎるとコストが増加 ・ タスクの複雑さに応じて適切な例数を選択する AIに示す例の数を調整する手法です。 Zero-Shot :例を示さずにタスクだけを渡す One-Shot :1つの例を示して参考にさせる Few-Shot :複数の例を示して複雑なパターンを理解させる 実務では、例なしで十分な場合はZero-Shot、精度を上げたい場合はFew-Shotを使うケースが多いです。ハンズオンでは、商品属性の抽出を例に、Zero-ShotとFew-Shotの違いがより顕著に現れることを体感してもらいました。 ❌ 悪い例(Zero-Shot) 商品説明から属性を抽出して 「柔らかなカシミヤ100%のタートルネックニット。上品なベージュカラー。」 ✅ 良い例(One-Shot) 商品説明文から、以下の属性を抽出してください。 入力: 「軽量で撥水加工を施したナイロンジャケット。ブラックとネイビーの2色展開。」 出力: - 素材: ナイロン - 機能: 撥水加工, 軽量 - カラー: ブラック, ネイビー - アイテム: ジャケット 【抽出対象】 入力:「柔らかなカシミヤ100%のタートルネックニット。上品なベージュカラー」 出力: 1つの例を示すことで、例と同じ形式で出力されます。形式が決まっているようなタスクには One-Shot が有効です。 加えて、例を増やす( Few-Shot )ことでより多様なパターンへ対応できます。 ✅ 良い例(Few-Shot) 商品説明文から、以下の属性を抽出してください。 【例1】 入力: 「軽量で撥水加工を施したナイロンジャケット。ブラックとネイビーの2色展開。」 出力: - 素材: ナイロン - 機能: 撥水加工, 軽量 - カラー: ブラック, ネイビー - アイテム: ジャケット 【例2】 入力: 「ストレッチ性に優れたコットンデニム。スリムフィット。ダメージ加工。」 出力: - 素材: コットンデニム - 機能: ストレッチ - フィット: スリム - デザイン: ダメージ加工 - アイテム: デニム 【抽出対象】 入力: 「柔らかなカシミヤ100%のタートルネックニット。上品なベージュカラー。」 出力: 複数の例で抽出パターンを示すことで精度が上がり、一貫した出力が得られます。 3. CoT(Chain-of-Thought) 項目 説明 使いどころ ・ 複雑な判断や計算が必要なとき ・ AIの判断根拠を知りたいとき メリット ・ 思考プロセスが可視化される ・ 各ステップのチューニングが容易になる 注意点 ・ シンプルなタスクには過剰 ・ ステップ設計に手間がかかる場合はZero-Shot CoTを検討 複雑な問題を小さなステップに分解し、AIに段階的に考えさせる手法です。 ❌ 悪い例 この商品の適正価格を教えて 商品: レザージャケット 原価: ¥15,000 この例では判断基準が不明で、思考プロセスが見えません。 ✅ 良い例 以下の商品の販売価格を提案してください。 段階的に考えて、最終的な推奨価格を提示してください。 【商品情報】 - 商品: レザージャケット - 原価: ¥15,000 - ブランド: URBAN LEATHER (中価格帯、若年層向け) - ターゲット: 20-30代男性 - 季節: 秋冬 - カテゴリ平均価格: ¥25,000〜¥35,000 【価格設定の考え方ステップ】 ステップ1: 原価率から基本価格を算出 - 原価の2.5〜3.5倍が一般的な小売価格 - 計算してください ステップ2: 競合分析を考慮 - 同カテゴリの平均価格帯を考慮 - ブランドポジショニング(中価格帯)を反映 ステップ3: ターゲット顧客の支払い意欲 - 20-30代の価格感度を考慮 - 購入しやすい価格設定 ステップ4: 最終的な推奨価格 - 上記を総合して、推奨価格を提示 - 理由も簡潔に説明 【出力形式】 各ステップの考察と、最終推奨価格を記載してください。 ステップごとに判断根拠が可視化され、最終的な出力結果が意図しない場合もステップごとの出力を見て修正・変更ができるようになります。 4. Zero-Shot CoT 項目 説明 使いどころ ・ 日常的なタスクで手軽に精度を上げたいとき ・ CoTのステップ設計が面倒なとき メリット ・ 一文追加するだけで精度向上 ・ 導入コストが極めて低い 注意点 高度に専門的なタスクでは、明示的なステップ設計(CoT)の方が効果的な場合もある プロンプトに「ステップバイステップで考えて」の一文を追加するだけで、AIが自動で段階的な思考をするようになる手法です。 カフェテリアには23個のリンゴがありました。 彼らは20個を昼食に使い、さらに6個のリンゴを購入した場合、現在何個のリンゴがありますか? ステップバイステップで考えてください。 日常的なタスクで手軽に使える点が好評でした。思考プロセスをAIに指示するのはカロリーが高いですが、この一文を追加するだけで精度が高くなる傾向にあります。 5. プロンプトチェーン 項目 説明 使いどころ ・ 大規模タスクを段階的に進めたいとき ・ 各ステップで異なる視点や出力形式を求めたいとき メリット ・ 途中段階で確認・修正が可能 ・ ロールを段階的に変化させて精度を高められる 注意点 ・ 複数回のAI呼び出しが必要 ・ シンプルなタスクには過剰な場合がある 複雑なタスクを複数の小さなタスクに分割し、各タスクの出力を次のタスクの入力として使用する手法です。資料作成を例に、4つのステップに分けて実演しました。 ステップ1:要件整理 あなたは社内向けのプレゼン資料作成の専門家です。 ChatGPTをチーム内で正式導入するため、上司(AIツールに詳しくないが、 業務効率化に関心がある)へ提案する資料を作成してください。 ### ステップ1の指示 ### 次の提案資料を作る前に、以下の項目を整理してください。 - 提案の目的(何を実現したいか) - 想定読者(上司の立場や関心) - 伝えたいメッセージ(この資料で最も訴えたい点) テーマ:「ChatGPTをチーム内に正式導入する提案」 出力は箇条書きで、簡潔にまとめてください。 ステップ2:構成設計 あなたはビジネスプレゼンの構成設計者です。 ### ステップ2の指示 ### ステップ1で整理した内容をもとに、上司向けの提案資料構成を設計してください。 各スライドのタイトルと要点(2~3行)を提示してください。 ステップ3:本文作成 あなたはプレゼン資料作成の編集者です。 ### ステップ3の指示 ### ステップ1で整理した内容とステップ2構成案をもとに、 各スライド内容をMarkdown形式で作成してください。 出力形式: - 各スライドを「# スライドタイトル」「- 要点」で表記 - ビジネスプレゼンに適した簡潔な文章 - 上司が3分以内に理解できる情報量 ステップ4:添削・リライト あなたはIT企業で働くチームマネージャー (AIツールに詳しくないが、業務効率化に関心がある)です。 ### ステップ4の指示 ### メンバーが作成したプレゼン資料について、伝わりやすいトーンにリライトしてください。 修正ポイント: - 専門用語を避け、平易な表現に言い換える - 「なぜ導入すべきか」を強調する - 文のリズムを整え、読みやすくする 「一度で完璧を目指す」よりも「段階的に精度を積み上げる」という考え方がプロンプトチェーンの本質です。途中段階で確認と修正を挟めるため、最終出力が意図と大きくずれるリスクを軽減できます。 プロンプトの構造化 ここまで、役割付与や思考プロセスの設計など、プロンプトの「内容」に関する手法を紹介してきました。ここからは、プロンプトの「構造」を整理することで、AIの解釈精度を高める3つの手法を紹介します。 これらの手法は、指示・データ・制約条件を明確に区別することで、AIの誤解釈を防ぎ、より正確な出力を得ることができます。 6. 区切り記号を使った構造化 項目 説明 使いどころ ・ 常に利用をオススメ ・ 特に長文や複雑な構造を持つプロンプトで効果的 メリット ・ 指示・データ・制約の境界が明確になる ・ AIの解釈ミスを防止できる 注意点 ・ 区切り記号の種類は統一する ・ 過度に複雑な構造はXML形式を検討 ### 、 """ 、 --- などの区切り記号を使って、プロンプトを指示・入力データ・制約条件などに明確に分離する手法です。情報の境界を視覚的に示すことで、AIの解釈ミスを防ぎます。 商品説明の翻訳を例に確認しました。 ❌ 悪い例 以下の日本語商品説明を英語に翻訳してください。 - 対象読者: 海外の30代女性顧客 - トーン: 商品説明として自然で魅力的な英語 - 専門用語は一般的な表現に - ECサイトの商品ページに掲載される文章 このニットは柔らかいカシミヤ100%で作られています。 肌触りが良く、保温性に優れているため、秋冬シーズンに最適です。 上品なデザインで、カジュアルからオフィススタイルまで幅広く対応します。 お手入れは手洗いを推奨しております。 翻訳された英語テキストのみを出力してください。 この例では、どこまでが翻訳対象か不明確で、指示と内容が混在しています。 ✅ 良い例 以下の日本語商品説明を英語に翻訳してください。 ### 指示 ### - 対象読者: 海外の30代女性顧客 - トーン: 商品説明として自然で魅力的な英語 - 専門用語は一般的な表現に - ECサイトの商品ページに掲載される文章 ### 翻訳対象 ### """ このニットは柔らかいカシミヤ100%で作られています。 肌触りが良く、保温性に優れているため、秋冬シーズンに最適です。 上品なデザインで、カジュアルからオフィススタイルまで幅広く対応します。 お手入れは手洗いを推奨しております。 """ ### 出力形式 ### 翻訳された英語テキストのみを出力してください。 この手法は常に利用することをオススメしており、参加者からも「すぐに使える」という声が多く聞かれました。 7. XML形式を使った高度な構造化 項目 説明 使いどころ ・ プロンプトが複雑かつ長文のとき ・ データを変数として埋め込みたいとき メリット ・ 開始・終了が明確でAIの誤認を防止 ・ ネスト構造で複雑なデータも表現可能 注意点 ・ シンプルなタスクには過剰 ・ 区切り記号で十分な場合はそちらを優先 <data>xxx</data> のようなXMLタグを使い、指示や情報を明確に区別する手法です。プロンプトの構造が複雑な場合や、データを変数として埋め込む際に特に有効です。 顧客レビュー分析を例に確認しました。 ❌ 悪い例 あなたはデータアナリストです。 A社とB社の顧客レビューを分析し、経営チーム向けにレポートを作成してください。 A社の配送が早くて助かりました。品質も良いです。なお、今後もこのスピードを維持してほしいです。 B社の商品はデザインが良いが、到着が遅かったです。なお、配送体制の見直しが必要だと思います。 A社のカスタマーサポートが親切でした。なお、対応スピードも安定しています。 なお、成功した施策と改善が必要な領域を明確にしてください。 この例では、レビュー内容と指示が混在しており、AIがどこまでがデータでどこからが指示かを判断しづらくなっています。 ✅ 良い例 あなたはデータアナリストです。 A社やB社のレビューを分析し、<report_target name='A社'>経営チーム</report_target> 向けのレポートを作成してください。 ### レビュー <review> <good_reviews> - A社の配送が早くて助かりました。品質も良いです。なお、今後もこのスピードを維持してほしいです。 - A社のカスタマーサポートが親切でした。なお、対応スピードも安定しています。 </good_reviews> <bad_reviews> - A社のサイトは少し使いにくいと感じました。商品検索がもう少し改善されると嬉しいです。 - B社の商品はデザインが良いが、到着が遅かったです。なお、配送体制の見直しが必要だと思います。 </bad_reviews> </review> ### 命令 成功した施策と改善が必要な領域を明確にしてください。 <good_reviews> や <bad_reviews> タグ内にレビューをまとめることで、以下メリットが受けられます。 データと命令の境界が明確になる タグのネストも可能で、自然言語を複雑な構造で表現できる タグを利用することでデータの変更・追加がしやすくなり、再利用性も向上 その他タグを使った例やベストプラクティスについては以下ドキュメントが参考になります。 OpenAI Prompt Engineering ガイド(Markdown/XMLのメッセージ整形) Claude 公式ドキュメント(XMLタグ) Google Vertex AI 公式ドキュメント(構造化プロンプト) 8. 出力形式の指定 項目 説明 使いどころ ・ システム連携やデータ処理に使うとき ・ 複数項目を一貫した形式で取得したいとき メリット ・ 出力の一貫性が保証される ・ 後続処理やデータベース登録が容易になる 注意点 形式だけでなく、各項目の定義や欠損時の扱いも明示すると精度が向上 JSON、Markdown、CSV、表など、出力形式を明示的に指定する手法です。システム連携やデータ処理を見据えた構造化データを取得したい場合に活用します。 商品データの構造化抽出を例に確認しました。 ❌ 悪い例 商品情報を抽出して 「このシルクブラウスは高級感のある光沢が特徴。 サイズはS、M、Lの3展開。ホワイト、ブラック、ネイビーの3色。 価格は¥9,800です。」 この例ではフォーマットが不明で、出力結果の一貫性が保証されません。 ✅ 良い例 以下の商品説明から情報を抽出し、JSON形式のみ出力してください。 ### 商品説明 ### 「このシルクブラウスは高級感のある光沢が特徴。 サイズはS、M、Lの3展開。ホワイト、ブラック、ネイビーの3色。 価格は¥9,800です。」 ### 出力形式 ### 以下のJSON形式で出力してください: { "item_name": "商品名", "material": "素材", "sizes": ["サイズ1", "サイズ2", ...], "colors": ["カラー1", "カラー2", ...], "price": 数値(円), "features": ["特徴1", "特徴2", ...] } ### 注意事項 ### - 記載のない項目はnullとする - 価格は数値のみ(円記号不要) - サイズとカラーは配列で返す 明確なデータ構造を指定することで、システム連携が容易になり、一貫した出力が得られます。 より厳格な出力を求める場合は、各社モデルで提供している 構造化出力(Structured Output) をAPI経由で利用するのがオススメですが、ここでは詳細は省きます。気になる方は以下ドキュメントを参考にしてください。 OpenAI 公式ドキュメント(Structured Outputs) Gemini API 公式ドキュメント(Structured Output) Claude 公式ドキュメント(Structured Outputs) ハルシネーションへの注意 ハンズオンでは、手法の紹介だけでなく、ハルシネーション(幻覚)への注意喚起も行いました。LLMが実際には存在しない情報をあたかも正しいように生成してしまう現象であり、これを理解しておくことは非常に重要だと考えています。 回避方法として、以下を紹介しました。 情報源を明示的に尋ねる 事実確認が必要な場合は公式サイトや一次情報で裏付けを取る 曖昧な質問を避け、具体的かつ検証可能な指示を出す わからない場合は「わかりません」と答えるように促す ハンズオンの成果 ハンズオン実施後、参加者にアンケートを実施しました。76名の方にご回答いただき、以下のような結果が得られました。 ハンズオンの内容満足度 「ハンズオンの内容には満足いただけましたか?」という質問(5段階評価)に対して、 上位2段階(「満足」及び「大変満足」)と回答した方が100% でした。 大変満足:68.4% 満足:31.6% 業務活用イメージ 業務活用イメージについても同様に5段階評価で質問したところ、 上位2段階(「ある程度湧いた」及び「とても湧いた」)と回答した方が100% でした。 とても湧いた:68.4% ある程度湧いた:31.6% 感想 参加者からいただいた感想の中から、いくつかの声をご紹介します。 学習効果について :「良い例・悪い例を比較できたことが、プロンプトの理解に効果的だった」 構成について :「内容が整理されており、初心者でも理解しやすかった。業務への応用イメージも湧いた」 今後の活用について :「様々な手法やテクニックを知れた。目的や状況に応じた使い分けを実践していきたい」 実施してみての学び 今回のハンズオンを実施して、アンケート結果や参加者の声からいくつかの学びを得ることができました。 体系的な学習機会の重要性 プロンプトエンジニアリングは、日々の業務の中で自然と身につく部分もあります。しかし、参加者の感想にあった「様々な手法やテクニックがあることを知れた」という声からも分かるように、体系的な学習機会の提供が効果的です。基本原則や代表的な手法を学ぶことで、より効率的にスキルを習得できることを実感しました。 業務活用イメージの設問で100%の方が「湧いた」と回答してくださったことは、「なんとなく使っていた」状態から「意図を持って使い分けられる」状態への変化を示唆していると考えています。 実務に即した例の効果 ハンズオンでは、コードレビューや資料作成、商品データの抽出など、参加者の実務に近い例を多く取り入れました。「業務への応用イメージも湧いた」「初心者でも理解しやすい構成だった」といった感想をいただけたことから、抽象的な説明よりも具体的なユースケースを通じて学ぶアプローチが効果的だったと感じています。 また、「良い例・悪い例の形式が理解に効果的だった」という声もあり、比較を通じて違いを体感できる構成が学習効果を高めたと考えています。 継続的な学習のサポート ハンズオンは一度きりの機会ですが、参加者が日常業務の中で思い出せるよう、資料を「チートシート」として活用できる形式で提供しました。学んだ内容を定着させるためには、継続的に振り返れる仕組みが重要だと考えています。 こうしたハンズオンの実施も、ZOZOが開発組織の全体で進めているAI活用推進の一環です。現在、開発者の約77%がAI活用に満足しているという調査結果も出ています。詳細は以下のスライドをご確認ください。 speakerdeck.com AIワーキンググループの設置や知見の組織的な展開など、継続的な取り組みを進めています。今後は新しい手法やツールの登場に合わせた追加セッションの開催や、実践事例の共有会なども検討しており、組織全体でAI活用スキルを高めていく取り組みを継続していく予定です。 まとめ 本記事では、社内で実施したプロンプトエンジニアリングのハンズオンの取り組みについて紹介しました。 プロンプトエンジニアリングは、生成AIの業務活用で欠かせないスキルになりつつあります。しかし、体系的に学ぶ機会がないまま、なんとなくの理解で終わっている方も多いのではないでしょうか。 同様の課題を抱えている方や、社内でのAI活用推進を検討されている方にとって、本記事が何かしらの参考になれば幸いです。 おわりに ZOZOでは、AIを活用した業務効率化やプロダクト開発に積極的に取り組んでいます。一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご覧ください。 corp.zozo.com
アバター
.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(2) { display: none; } はじめに こんにちは、ブランドソリューション開発本部WEAR開発部Androidブロックの武永です。普段は ファッションコーディネートアプリ「WEAR」 のAndroidアプリを開発しています。 背景 本記事では、Epoxy(Airbnb製のRecyclerView向けUI構築ライブラリ)を撤去し、Jetpack Compose(Compose)へ移行した背景を整理します。 移行の直接のきっかけは、当時の構成でKotlin Annotation Processing Tool(kapt)依存が残っていたことです。kapt依存が残っていたため、Kotlin Symbol Processing(KSP)への移行が進みませんでした。その結果、KotlinおよびDagger Hilt(Hilt)の更新判断も先送りされていました。 一方で、Epoxy自体はKSP対応が進んでいました。しかし、Data Binding用プロセッサはKSP未対応で、Data Bindingを使う構成ではkaptが必要でした( Epoxy 5.0.0beta02のリリースノート )。ビルド時間でもボトルネックになっていました。その際、EpoxyのData Bindingを外して使い続ける案も検討しましたが、UIをComposeに統一するメリットが大きいと判断し、修正量は増えてもCompose化に踏み切りました。 本記事の範囲 あわせて、進め方と得られた効果もまとめます。Data Bindingを使っている場合はkaptを外すための置き換えも必要です。この記事ではその前提を踏まえ、EpoxyのCompose化にフォーカスします。 記載方針 本記事では当時の実体験と現在の判断を分けて整理します。当時はAIエージェント(GitHub Copilot)をVisual Studio Codeで使っていました。現在は選択肢が増えていますが、内容は当時の作業基準に基づいています。以降の「リファクタリングする画面の問題点」から「Epoxy削除とCompose移行を進める」までは当時の判断をもとに記載します。一方、「改善の余地があった点」は現在の視点で振り返った内容です。 ※コードは社外向けに匿名化・簡略化しています。 目次 はじめに 背景 本記事の範囲 記載方針 目次 リファクタリングする画面の問題点 KotlinおよびHiltのバージョンアップが止まっていた kapt依存がボトルネックになっていた Epoxy削除とCompose移行を進める 同等性の検証方法 画面全体のCompose化 プロンプト 差分(抜粋) Epoxyの1モデルを1コンポーネントに置き換える プロンプト 差分(抜粋) AI支援による効率化 改善の余地があった点 まとめ リファクタリングする画面の問題点 KotlinおよびHiltのバージョンアップが止まっていた kapt依存のある構成ではKotlinおよびHiltの更新が進まず、関連するライブラリやビルド設定の更新にも踏み切れませんでした。結果としてアップデート作業が停滞していました。 kapt依存がボトルネックになっていた kapt依存はビルド時間の増加にもつながり、見直しのきっかけになりました。事前に簡易計測したところ、kaptの削減だけでも少なくとも30〜60秒は短縮できそうだと見込めました。 Kotlin Annotation Processing Tool(kapt)はJavaベースのアノテーション処理で、ビルド時にJavaのstubファイルを生成してから処理するため時間がかかる KSPはKotlin専用のアノテーション処理で、Kotlinコードを直接処理できるため、kaptより高速 EpoxyのData Binding用プロセッサがkapt依存だったため、Data Bindingを使う構成ではKSP移行が進まず、ビルド時間にも影響していた Epoxy削除とCompose移行を進める Epoxyを撤去し、UIをComposeへ移行 XMLレイアウト(ConstraintLayout中心)とEpoxyRecyclerViewを廃止し、ComposeのScaffold + LazyVerticalGridで再構成 AIエージェントの自動置換を活用し、確認と微調整をしながら置換作業を進めた 特に重視したのは見た目の完全一致と依存の排除です。EpoxyやXMLのConstraintLayoutを避ける構成にし、Composeの標準的なレイアウト(Column/Row)で組み直しました。 同等性の検証方法 移行前後で同一条件のスクリーンショット(同一端末・同一画像サイズ)を撮り、目視で比較しました。差分が出た場合は余白・文字サイズ・色の順で確認しました。今回は目視確認でしたが、Compose化したことで今後はスクリーンショットテストの導入も検討したいと考えています。 画面全体のCompose化 画面全体をComposeに置き換える場合は、EpoxyRecyclerView中心の構造をComposeの画面構成へ移す方針で進めました。 プロンプト TestGridLayoutFragment の EpoxyRecyclerViewの構成を Compose に置き換えてください。 - デザインは既存UIと完全に一致させること - Epoxyの使用は禁止 - ComposeのConstraintLayoutは使わず、シンプルなレイアウトで実装する - @Preview を必ず作成する 差分(抜粋) 1) FragmentのView生成(XML→ComposeView) Before(XML + Data Binding) _binding = DataBindingUtil.inflate(inflater, R.layout.fragment_test_grid_layout, container, false ) return binding.root After(ComposeView) return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { AppTheme { DetailScreen() } } } 2) グリッド構築(Epoxy→LazyVerticalGrid) Before(Epoxy + RecyclerView) binding.recyclerView.layoutManager = GridLayoutManager(requireContext(), SPAN_COUNT) binding.recyclerView.withModels { // Epoxy models... } After(Scaffold + LazyVerticalGrid) Scaffold(topBar = { AppTopBar.Default(...) }) { paddingValues -> LazyVerticalGrid( columns = GridCells.Fixed(SPAN_COUNT), state = gridState, modifier = Modifier.fillMaxSize().padding(paddingValues), ) { item { /* Section */ } } } Epoxyの1モデルを1コンポーネントに置き換える 画面全体の置き換えだけでなく、Epoxyの「モデル単体」をComposeに置き換える方法も進めました。Epoxyのモデルクラス( @EpoxyModelClass )とXMLレイアウトを削除し、同じ役割のComposableを用意して既存の表示フローに組み込みます。 プロンプト ActionCompletedModel をComposeのUIコンポーネントに置き換えてください。 - デザインは既存UIと完全に一致させること - EpoxyモデルとXMLレイアウトは削除する - Epoxyの使用は禁止 - ConstraintLayoutは使わず、シンプルなレイアウトで実装する - @Preview を必ず作成する 差分(抜粋) Before(Epoxyモデル + XML) @EpoxyModelClass abstract class ActionCompletedModel : DataBindingModel<ActionCompletedBinding>() { @EpoxyAttribute lateinit var actionInfo: ActionInfo @EpoxyAttribute (DoNotHash) var onPrimaryActionClick: View.OnClickListener? = null override fun getDefaultLayout(): Int = R.layout.action_completed override fun bind(binding: ActionCompletedBinding, context: Context) { binding.actionButton.setOnClickListener(onPrimaryActionClick) binding.actionTitle.text = actionTitle(context, actionInfo) binding.actionSubtitle.text = actionSubtitle(context, actionInfo) } override fun unbind(binding: ActionCompletedBinding) { binding.actionButton.setOnClickListener( null ) } } After(Composable + 組み込み) LazyColumn { item { ActionCompletedContent( actionInfo = actionInfo, onPrimaryActionClick = { onPrimaryAction(actionInfo) }, ) } } Before(XML) <androidx . constraintlayout . widget . ConstraintLayout android : layout_width = "match_parent" android : layout_height = "wrap_content" > <com . example . RoundRectLayout android : id = "@+id/actionButton" android : layout_width = "match_parent" android : layout_height = "56dp" > <androidx . constraintlayout . widget . ConstraintLayout android : layout_width = "match_parent" android : layout_height = "match_parent" > <TextView android : id = "@+id/actionTitle" /> <ImageView android : id = "@+id/actionIcon" /> <TextView android : id = "@+id/actionSubtitle" /> </androidx . constraintlayout . widget . ConstraintLayout> </com . example . RoundRectLayout> </androidx . constraintlayout . widget . ConstraintLayout> After(Compose / ActionCompletedContent 内の抜粋) @Composable fun ActionCompletedContent( actionInfo: ActionInfo, onPrimaryActionClick: () -> Unit , ) { val context = LocalContext.current Column(modifier = Modifier.fillMaxWidth()) { Surface( modifier = Modifier .fillMaxWidth() .height( 56 .dp), shape = RoundedCornerShape( 12 .dp), ) { Box( modifier = Modifier .fillMaxSize() .clickable(onClick = onPrimaryActionClick), ) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Row(verticalAlignment = Alignment.CenterVertically) { Text(text = actionTitle(context, actionInfo)) Icon( painter = painterResource(id = R.drawable.ic_action), contentDescription = null , ) } Text(text = actionSubtitle(context, actionInfo)) } } } } } AI支援による効率化 ざっくりした記録ですが、AIに置換を任せたPRは合計28件でした。1件あたりの作業時間は、人力だと3〜5時間、AI支援後は1〜2時間くらいで終わるケースが多かったです。単純計算で1件あたり2〜4時間短縮できたので、合計では約56〜112時間(7〜14人日)ほどの短縮になりました。 副次効果として、反復系の単純作業をAIに任せることで、人間が差分を照合するときのケアレスミスを減らせた実感がありました。 改善の余地があった点 プロンプトで「完全一致」と指示しても既存UIを完全には再現できず、細かい余白・フォント・色差で手戻りが発生しました。色定義をテーマから使うべきところで個別指定になり、見た目の統一にも手戻りが出ました。XMLのConstraintLayoutで組まれていたレイアウトが多く、Composeでも旧来の発想に引っ張られ、Column/Row前提で組み直す場面もありました。 今振り返ると、AIエージェント向け作業ガイドを整備しておけば、こうした手戻りを減らせたと思います。例えば、画面ごとのUI要素の対応表、余白/フォント/色の参照先、Composeでの置き換えルール、スクリーンショット比較の手順を1枚にまとめる、といった形です。 まとめ Epoxy削除によりkapt依存の主要因を外し、KSP移行に向けて前進できました。KotlinおよびHiltのアップデート準備も進みました。依存解消を目的にUI移行を進めると判断がぶれにくく、AIエージェントは置換作業の加速に向いていると感じました。Compose移行は「更新可能性」と「開発体験」の改善にもつながりました。 最後までご覧いただきありがとうございました。ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
アバター
.table-of-contents > li > ul > li > ul, .table-of-contents > li:nth-child(2) { display: none } はじめに こんにちは、データシステム部MLOpsブロックの 木村 です。MLOpsブロックではZOZOTOWN、WEAR by ZOZOをはじめとして、弊社で提供するさまざまなサービスに関わるML機能を開発・運用しています。 本記事で紹介する ZOZOマッチ (以下、本アプリ)は「ファッションで恋する」をコンセプトとしたマッチングアプリです。本アプリもML機能を持ち、MLOpsブロックが機能を開発・運用しています。 本アプリの特徴的なML機能として ファッションジャンル診断 (以下、ジャンル診断)があります。ジャンル診断とは全身画像をストリートやモードなどZOZOが定義した12種類のファッションジャンルに分類し、該当するジャンルの割合を「ジャンル傾向」として円グラフで表示する機能です。 ジャンル診断には2つの利用方法があります。1つ目はプロフィールに登録した自分の全身画像から自分の「ジャンル傾向」を判定する方法です。2つ目は好みのコーディネート画像を複数枚選んで自分の「好みの雰囲気のジャンル傾向」を判定する方法です。これら2つの利用方法によって「ファッションで恋する」レコメンドを実現しています。 このジャンル診断を実現するために、全身画像からファッションジャンルを判定する「ジャンル診断API」を開発しました。ジャンル診断APIはMLモデルによる推論処理を行い、全身画像を入力として12種類のジャンルそれぞれに属する確率を返します。 今回、このジャンル診断APIの推論サーバーとして Triton Inference Server (以下、Triton)を導入しました。TritonはNVIDIAが提供するオープンソースの推論サーバーです。 本記事ではジャンル診断APIの開発で推論サーバーにTritonを採用した背景から、導入時に遭遇した課題とその解決策、導入効果までを説明します。Tritonの導入にあたり 公式ドキュメント の説明だけでは理解しにくかった点もあり、いくつかの課題に直面しました。これらの知見がTritonを使った推論サーバーの構築・運用を検討する際の参考になれば幸いです。 本記事の内容は2026年2月時点の情報であることにご留意ください。 目次 はじめに 目次 Tritonを導入した背景 従来のML推論API構成とその課題 推論サーバーの選定 Tritonを採用するメリット 推論処理の高速化 処理の分離による保守性向上 設定変更の容易さ 推論単体の性能検証 ジャンル診断APIへの導入経緯 システム構成 全体構成 リクエストの流れ 通信プロトコルの選定 Tritonがサポートするプロトコル HTTPとgRPCの比較 Kubernetesリソースの構成 Tritonコンテナの起動設定 Deploymentマニフェスト InitコンテナとVolume Mountによるモデル共有 OpenTelemetryによるトレース設定 ヘルスチェック設定 セキュリティ設定 Triton Podへの接続設定 GCSの認証設定 オートスケーリングの設定 モデルのディレクトリ構成 モデルの設定 推論ゲートウェイAPIの実装 Triton接続の設定 StyleFeatureExtractorクラスの実装 推論リクエスト処理の実装 導入時の課題と解決策 課題1:gRPCで負荷分散されない 原因 解決策 1. Headless Serviceの採用 2. クライアント側でround_robinロードバランシングを設定 スケールアウト時の負荷分散 課題2:Datadog APMでトレースが連携されない 原因 解決策 1. W3C Trace Contextによるトレース伝播 2. Triton専用NodeにDatadog AgentをDaemonSetで配置 3. Tritonの起動オプションでトレース設定 課題3:PyTorchモデルがロードされない 原因 解決策 導入効果 導入時の注意点 コスト面の考慮 まとめ Tritonを導入した背景 従来のML推論API構成とその課題 MLOpsブロックでは従来、前処理から推論、後処理までの全処理を単一のAPI内で実行していました。Pod起動時に Google Cloud Storage (以下、GCS)からMLモデルをダウンロードし、入力データの取得から後処理までを同一プロセス内で実行する構成です。 この構成には2つの課題がありました。 1つ目はリソース効率が悪いことです。画像ダウンロードのようなI/Oバウンドな処理とMLモデルでの推論のようなCPU/GPUバウンドな処理ではボトルネックが異なります。しかし単一のAPIではこれらを分離できません。このリソース効率の悪さはパフォーマンス面とコスト面の両方に影響がありました。パフォーマンス面では、どちらか一方がボトルネックでもAPI全体を水平・垂直スケールする必要があり、ボトルネックではない処理のリソースまで増やすことになっていました。コスト面では、推論にGPUを使用したい場合、すべてのAPI PodにGPUを割り当てる必要があります。ただし実際にGPUを使うのは推論処理の間だけです。前処理・後処理の間もGPUを占有し続けるためGPU使用率が低くなり、無駄なコストが発生していました。 2つ目は推論速度の向上が難しいことです。ML推論APIではモデルの推論処理に要する時間が全体のレイテンシの大部分を占めます。しかし推論処理は単一のAPI内に組み込まれていることで、汎用的なWebフレームワーク上で実行されるため推論に特化した最適化手段が限られていました。そのため推論のレイテンシ改善が難しく、リアルタイム推論を活用したUXの良い機能の提供が困難でした。 推論サーバーの選定 これらの課題を解決するため推論サーバーの導入を検討しました。主要な推論サーバーを比較した結果は次の通りです。 名称 対応フレームワーク 実装言語 TorchServe PyTorch専用 Java/Python TensorFlow Serving TensorFlow専用 C++ BentoML 複数対応 Python Triton Inference Server 複数対応 C++ MLOpsブロックではPyTorchやTensorFlowなど複数のMLフレームワークを利用しています。また今回のジャンル診断だけでなく今後の別プロジェクトでも活用できる共通基盤として、複数フレームワークに対応しつつ高速な推論基盤が必要でした。 TorchServe と TensorFlow Serving はどちらも特定フレームワークのモデルで手軽にAPIを作成したい場合に有効です。しかし、複数フレームワークに対応していません。 BentoML は様々なMLフレームワークに対応していますが、Python実装のためパフォーマンス面で不利な場合があります。 一方でTritonは様々なMLフレームワークに対応しておりC++実装のため高速です。構成が複雑でキャッチアップコストはありますが、汎用性・機能性が最も高く共通基盤として最適と判断しTritonを選定しました。 Tritonを採用するメリット 複数の推論バックエンドに対応していることに加えてTritonを導入するメリットは次の4つがあります。 推論処理の高速化 処理の分離による保守性向上 設定変更の容易さ 推論単体の性能検証 以降で各メリットについて詳しく説明します。 推論処理の高速化 FastAPIなどPythonベースの推論サーバーは汎用的なWebフレームワークであり推論処理に最適化されていません。一方Tritonは推論処理に特化したサーバーです。リクエストの受付と推論実行を別々のスレッドプールで処理するため、推論中も新しいリクエストを受け付けられます。またC++で実装されているためPythonのGlobal Interpreter Lockの制約を受けず、推論処理をマルチスレッドで並列実行できます。 また Dynamic Batching により複数の推論リクエストをサーバー側でバッチ処理できます。最初のリクエストを受け取ってから指定した待機時間内に届いたリクエストを動的にまとめることで、個別処理よりもスループットが向上します。 さらに instance_group により同一モデルの複数インスタンスを並列実行できます。デフォルトでは同時に1つのリクエストのみ実行されますが、インスタンス数を増やすことで複数のリクエストを並列処理できます。 処理の分離による保守性向上 推論処理をTritonに委譲することでAPIサーバーは前処理・後処理に、Tritonは推論に専念できます。この分離により各コンポーネントの責務が明確になりコードの保守性が向上します。 スケーリングの観点でも大きなメリットがあります。具体的には水平スケール(Pod数の増減)や垂直スケール(CPUやメモリの割り当て変更)をAPIサーバーとTritonそれぞれに対して個別に適用できるようになります。推論がボトルネックの場合はTriton Podのみを、前処理・後処理がボトルネックの場合はAPIサーバー Podのみをそれぞれ独立してスケールできます。従来の一体型構成では推論のボトルネック解消のためにPod全体をスケールする必要がありました。分離構成ではボトルネックとなっているコンポーネントのみをスケールすればよくリソース効率が向上します。またAPIサーバーとTritonのメトリクスを個別に監視することでどちらがボトルネックになっているかを把握しやすくなります。 設定変更の容易さ Tritonではモデルの設定を config.pbtxt で宣言的に管理します。バッチサイズの上限、モデルインスタンス数、Dynamic Batchingの待機時間などをコード修正なしで変更できます。 従来のPythonベースの実装でもバッチサイズ等を環境変数で外部化できます。一方 config.pbtxt では max_batch_size や dynamic_batching 、 instance_group など推論サーバーに必要な設定項目が標準化されています。これらの設定項目を自前で設計する必要がありません。また config.pbtxt とモデルファイルを含むディレクトリをそのまま別環境にコピーすれば動作するため、環境間での設定の共有も容易になります。 推論単体の性能検証 従来の構成では推論性能の測定にはAPI全体への検証が必要であり、前処理・後処理の影響を含んだ結果しか得られませんでした。 Triton公式の Perf Analyzer を使用することで推論部分のみの性能を切り出して測定できます。Perf AnalyzerはTritonで実行される機械学習モデルの推論パフォーマンスを測定・最適化するためのCLIツールです。汎用的な負荷試験ツールでTritonをテストする場合はテンソル形式でのリクエスト組み立てを自前で実装する必要があります。一方Perf Analyzerではモデルのメタデータを自動取得してリクエストを生成するためその実装が不要です。さらにサーバー内部のレイテンシをqueueとcomputeに分解して出力します。queueは推論スケジューラのキュー内でリクエストが待機していた時間を示し、computeは入力データの準備、実際の推論実行、出力データの取得にかかった時間を示します。この分解によりボトルネックの切り分けが容易になり、モデル更新時のパフォーマンス確認や推論パラメータの調整に役立ちます。 ジャンル診断APIへの導入経緯 ここではなぜジャンル診断APIにTritonを導入したのかを説明します。 ジャンル診断APIは「ファッションで恋する」を実現するZOZOマッチの根幹となる機能です。このAPIには低レイテンシとスケーラビリティの2つの要件がありました。 オンボーディング時に使用されるAPIのため遅延があるとユーザー離脱につながります。またプロフィール画像を変更するたびに診断が実行されるためユーザー増加に伴いスケーラビリティも必要です。前述の通りTritonはC++実装やDynamic Batchingにより低レイテンシと高スループットを実現でき、これらの要件に適していると判断しました。 さらにTritonは社内での導入実績がありませんでした。既存の本番サービス(ZOZOTOWN、WEAR等)は利用者数が多く、問題発生時の影響範囲が大きくなります。一方ZOZOマッチはサービス立ち上げ段階であり、ZOZOTOWNやWEARなどの既存サービスと比較するとユーザー数が少ないです。また新規構築のため既存システムとの依存関係も少なく、新技術の検証に適していると判断しました。 以降ではジャンル診断APIのシステム構成とTritonを利用した推論API開発時に遭遇した課題について説明します。 システム構成 全体構成 本システムではAPIサーバーとTritonを分離する構成を採用しました。前述の「 処理の分離による保守性向上 」で説明した通り、この構成により責務の明確化とボトルネックに応じた柔軟なスケーリングが可能になります。 リクエストの流れ 次の図にリクエストの流れを示します。 ユーザーが自分の全身画像をプロフィール画像として登録するとBackend Serverがその画像データをAPIサーバーに送信します。APIサーバーはリサイズと正規化の前処理を行いgRPCプロトコルでTritonに推論リクエストを送信します。Tritonは12種類のファッションジャンルそれぞれに対して0〜1のスコアを返します。スコアが高いほどそのジャンルに該当する可能性が高いことを示します。以下はレスポンス例です。 { " genres ": [ { " genre_id ": 1 , " score ": 0.045 } , { " genre_id ": 2 , " score ": 0.696 } , ... { " genre_id ": 12 , " score ": 0.333 } ] } APIサーバーがこの診断結果をBackend Serverに返し、最終的にユーザーへレスポンスが返却されます。なおオンボーディング時に表示される選択肢画像についてはバッチ処理で事前にスコアを算出済みのためリアルタイムでの推論は不要です。リアルタイム推論が必要なのはユーザーがプロフィール画像を登録・変更した場合のみです。 通信プロトコルの選定 上記の通りAPIサーバーとTriton間の通信にはgRPCを採用しています。ここではTritonがサポートするプロトコルとgRPCを選定した理由を説明します。 Tritonがサポートするプロトコル TritonはHTTPとgRPCの2種類のプロトコルで推論リクエストを受け付けます。HTTPはポート8000、gRPCはポート8001で公開されます。どちらのプロトコルも同じ推論機能を提供しますがパフォーマンス特性が異なります。 HTTPとgRPCの比較 TritonのHTTPエンドポイントはHTTP/1.1ベースで、curlや各種HTTPライブラリで簡単にリクエストでき、ブラウザからも直接アクセスできるため広く利用されています。一方gRPCはHTTP/2ベースでサーバー間の内部通信に適していますが、ブラウザAPIでサポートされていないためブラウザから直接呼び出すことができません。しかし本システムではAPIサーバーからTritonへのサーバー間通信のみを対象としておりブラウザからのアクセスは不要なため、この制約は問題になりません。 gRPCがサーバー間通信に適している理由はHTTP/2による多重化です。HTTP/1.1では1つのTCP接続で同時に1つのリクエストしか処理できないため、高頻度のリクエストでは接続のオーバーヘッドが課題となります。HTTP/2では単一の接続上で複数のリクエストを同時に処理できます。本システムではAPIサーバーからTritonへ頻繁にリクエストを送信するためこの多重化の特性が有効です。 一方でgRPCを採用する際の注意点があります。HTTP/2の長時間接続がKubernetesの負荷分散と相性が悪いという問題です。この課題と解決策については後述の「 課題1:gRPCで負荷分散されない 」で説明します。 Kubernetesリソースの構成 MLOpsブロックではパブリッククラウドにGoogle Cloudを使用し Google Kubernetes Engine (以下、GKE)上にサービスを構築しています。ここではTritonをGKE上でデプロイするために作成したKubernetesリソースを解説します。TritonをGKE上で動かすために以下のリソースを作成しました。 Deployment :Triton Podの稼働 Service :Triton Podへのネットワークアクセスの提供 ServiceAccount :GCSアクセスのための認証設定 ScaledObject :KEDAによるオートスケーリングの設定 Tritonコンテナの起動設定 TritonをデプロイするためのDeployment構成を解説します。まず全体のマニフェストを示し、その後に各設定の詳細を説明します。 Deploymentマニフェスト apiVersion : apps/v1 kind : Deployment metadata : name : triton-inference-server labels : app : triton-inference-server spec : selector : matchLabels : app : triton-inference-server template : metadata : labels : app : triton-inference-server spec : serviceAccountName : triton-inference-server affinity : nodeAffinity : requiredDuringSchedulingIgnoredDuringExecution : nodeSelectorTerms : - matchExpressions : - key : cloud.google.com/gke-nodepool operator : In values : - "<triton-nodepool-name>" tolerations : - key : "dedicated" operator : "Equal" value : "<triton-nodepool-name>" effect : "NoSchedule" initContainers : - name : gcloud image : gcr.io/google.com/cloudsdktool/cloud-sdk:418.0.0 env : - name : MODEL_REPOSITORY_PATH value : gs://your-bucket/model_repository - name : MODEL_DIR value : "/tmp/models/" volumeMounts : - name : models mountPath : "/tmp/models/" command : - "/bin/sh" - "-c" - "gsutil cp -r $(MODEL_REPOSITORY_PATH)/* $(MODEL_DIR)" containers : - name : triton-inference-server image : "nvcr.io/nvidia/tritonserver:25.03-py3" imagePullPolicy : IfNotPresent resources : limits : cpu : 3000m memory : 3Gi command : [ "tritonserver" ] args : - "--model-store=/tmp/models/" - "--trace-config" - "mode=opentelemetry" - "--trace-config" - "opentelemetry,url=http://$(DD_AGENT_HOST):4318/v1/traces" - "--trace-config" - "rate=1" - "--trace-config" - "level=TIMESTAMPS" - "--grpc-max-connection-age=30000" - "--grpc-max-connection-age-grace=10000" env : - name : DD_AGENT_HOST valueFrom : fieldRef : fieldPath : status.hostIP volumeMounts : - name : models mountPath : "/tmp/models/" ports : - containerPort : 8000 name : http - containerPort : 8001 name : grpc livenessProbe : initialDelaySeconds : 10 periodSeconds : 5 failureThreshold : 6 httpGet : path : /v2/health/live port : http readinessProbe : initialDelaySeconds : 10 periodSeconds : 5 failureThreshold : 6 httpGet : path : /v2/health/ready port : http volumes : - name : models emptyDir : {} securityContext : runAsUser : 1000 fsGroup : 1000 以下、マニフェストの各設定について詳しく解説します。 InitコンテナとVolume Mountによるモデル共有 本システムでは Initコンテナ でGCSからMLモデルをダウンロードしています。InitコンテナはPod内でアプリケーションコンテナの前に実行されるコンテナで、マニフェストの initContainers フィールドで定義します。ダウンロード先のディレクトリをTritonコンテナでもVolume MountすることでダウンロードしたMLモデルをTritonから参照できる仕組みです。 initContainers : - name : gcloud image : gcr.io/google.com/cloudsdktool/cloud-sdk:418.0.0 env : - name : MODEL_REPOSITORY_PATH value : gs://your-bucket/model_repository - name : MODEL_DIR value : "/tmp/models/" volumeMounts : - name : models mountPath : "/tmp/models/" command : - "/bin/sh" - "-c" - "gsutil cp -r $(MODEL_REPOSITORY_PATH)/* $(MODEL_DIR)" containers : - name : triton-inference-server volumeMounts : - name : models mountPath : "/tmp/models/" volumes : - name : models emptyDir : {} emptyDir はPod起動時に作成される一時ボリュームで同一Pod内のコンテナ間でデータを共有できます。 Triton Pod起動時の処理の流れは次の通りです。 Initコンテナが起動し、GCSからモデルリポジトリ全体を /tmp/models/ にダウンロード Initコンテナが正常終了 アプリケーションコンテナ(Triton)が起動し、同じ /tmp/models/ からモデルをロード この構成によりモデルファイルをコンテナイメージに含める必要がなく、モデル更新時もイメージの再ビルドが不要になります。 OpenTelemetryによるトレース設定 本システムではDatadog APMでモニタリングを行っています。Tritonは OpenTelemetry (以下、OTel)形式でのトレース出力に対応しています。そのためTritonコンテナ起動時に以下の引数を指定することでDatadog APMと連携できます。 args : - "--trace-config" - "mode=opentelemetry" - "--trace-config" - "opentelemetry,url=http://$(DD_AGENT_HOST):4318/v1/traces" - "--trace-config" - "rate=1" - "--trace-config" - "level=TIMESTAMPS" env : - name : DD_AGENT_HOST valueFrom : fieldRef : fieldPath : status.hostIP 各パラメータの意味は次の通りです。 パラメータ 設定値 説明 mode opentelemetry OTelのAPIを使用してトレースを出力 opentelemetry,url http://$(DD_AGENT_HOST):4318/v1/traces トレースデータの送信先 rate 1 N番目のリクエストごとにトレース。 1 は全リクエスト、 1000 なら1000件に1件(デフォルト: 1000) level TIMESTAMPS 各リクエストの実行タイムスタンプを記録 DD_AGENT_HOST には status.hostIP (NodeのIP)を指定しています。Datadog Agentの推奨構成ではUNIXドメインソケットを使用します。しかしTriton(25.03時点)はOTLP Exporter経由のトレース送信においてUNIXドメインソケットに対応しておらず、HTTPプロトコルのみに対応しています。そのためホストIPとポートを指定する構成としました。 ヘルスチェック設定 livenessProbe : httpGet : path : /v2/health/live port : http readinessProbe : httpGet : path : /v2/health/ready port : http Tritonがサポートするヘルスチェックエンドポイントを livenessProbe、readinessProbe に設定しています。 /v2/health/live はTritonがリクエストを受け取れる状態かを確認し /v2/health/ready はすべてのモデルが正常にロードされたかを確認します。 セキュリティ設定 securityContext : runAsUser : 1000 fsGroup : 1000 セキュリティの観点からコンテナは非rootユーザーで実行しています。 runAsUser でコンテナ内のプロセスを実行するユーザーIDを指定しrootでの実行を避けています。 fsGroup でマウントされたボリュームのグループ所有権を同じIDに設定することで非rootユーザーでもモデルファイルへアクセスできるようにしています。 Triton Podへの接続設定 APIサーバーからTritonに接続するためのServiceを作成します。 KubernetesのServiceは複数のPodへのアクセスを抽象化するリソースです。通常のClusterIP ServiceではServiceに仮想IP(Cluster IP)が割り当てられ、クライアントはこの単一のIPアドレスにアクセスします。コンテナからの通信は kube-proxy によりDNATされ背後のPodにロードバランシングされます。 一方 Headless Service は clusterIP: None を指定したServiceであり仮想IPが割り当てられません。DNS名前解決ではServiceに紐づくすべてのPodのIPアドレスが直接返されます。これによりクライアント側で接続先のPodを選択できます。 本システムではHeadless Serviceを使用しています。通常のClusterIP Serviceを使用しない理由はgRPCの負荷分散に関係しています。詳細は後述の「 課題1:gRPCで負荷分散されない 」で説明します。 以下がServiceのマニフェストです。 apiVersion : v1 kind : Service metadata : name : triton-inference-server labels : app : triton-inference-server spec : type : ClusterIP clusterIP : None ports : - port : 8000 targetPort : http name : http-inference-server - port : 8001 targetPort : grpc name : grpc-inference-server selector : app : triton-inference-server 前述の通り8000番ポート(HTTP)はヘルスチェック用、8001番ポート(gRPC)は推論リクエスト用に公開しています。 GCSの認証設定 InitコンテナがGCSからモデルをダウンロードするにはGCSへのアクセス権限が必要です。GKEでは Workload Identity を使用することでKubernetesのServiceAccountとGCPのサービスアカウントを紐付けられます。これによりPod内からサービスアカウントキーを管理することなくGCPリソースにアクセスできます。 apiVersion : v1 kind : ServiceAccount metadata : name : triton-inference-server annotations : iam.gke.io/gcp-service-account : <gcp-service-account>@<project-id>.iam.gserviceaccount.com オートスケーリングの設定 KEDAを使用してCPU使用率に基づくオートスケーリングを設定しています。KEDAはKubernetes Event-driven Autoscalingの略で様々なメトリクスに基づいてPod数を自動調整できます。 KEDAについては下記の記事でも触れられていますのでぜひこちらもご参照ください。 techblog.zozo.com apiVersion : keda.sh/v1alpha1 kind : ScaledObject metadata : name : triton-inference-server spec : scaleTargetRef : name : triton-inference-server maxReplicaCount : 5 minReplicaCount : 2 cooldownPeriod : 300 triggers : - type : cpu metricType : Utilization metadata : value : "50" この設定ではCPU使用率50%を超えるとスケールアウトし、最小2台〜最大5台の範囲でPod数を調整します。最小2台としているのは可用性を担保するためです。 モデルのディレクトリ構成 Tritonでモデルを管理するには所定のディレクトリ構成に従う必要があります。本システムではその制約に従い、GCS上のモデルリポジトリを次のような構成にしました。 model_repository/ └── genre_extract_torchscript/ # モデル名(config.pbtxtのnameを指定する場合は一致させる) ├── config.pbtxt # モデルの入出力形式やバッチサイズなどを定義 ├── 1/ # モデルのバージョン │ └── model.pt └── 2/ # モデルのバージョン └── model.pt ディレクトリ名がそのままモデル名として使われます。今回は genre_extract_torchscript です。config.pbtxtで name フィールドを指定する場合はディレクトリ名と一致させる必要があります。 1/ や 2/ はバージョンを示すディレクトリでバージョン番号をディレクトリ名として使用します。この構成により複数バージョンのモデルを同時に管理でき、バージョン切り替えも容易になります。 実際にロードするバージョンはconfig.pbtxtのversion policyで制御でき、デフォルトでは最新バージョンのみがロードされます。config.pbtxtの詳細は「 モデルの設定 」で説明します。 モデルの設定 Tritonではモデルごとに config.pbtxt を用意します。config.pbtxtではモデルの入出力形式やバッチサイズなどを定義します。ジャンル診断モデルで使用しているconfig.pbtxtを以下に示します。 name: "genre_extract_torchscript" platform: "pytorch_libtorch" max_batch_size: 1 input [ { name: "image_input" data_type: TYPE_FP32 format: FORMAT_NCHW dims: [ 3, 224, 224 ] } ] output [ { name: "genre_scores" data_type: TYPE_FP32 dims: [ 12 ] } ] この設定では224×224ピクセルのRGB画像を入力として受け取り、12次元のベクトルを出力します。本システムでは12種類のファッションジャンルに対するスコアに対応しています。 platform フィールドの pytorch_libtorch はTorchScript形式のPyTorchモデルを使用することを示しています。 input[].format フィールドのNCHW形式とは画像データの配列順序を表しており、N(バッチサイズ)、C(チャンネル数=3)、H(高さ=224)、W(幅=224)の順にデータが並びます。 前述の「 推論処理の高速化 」で説明したDynamic Batchingを有効にするには以下のように設定します。 dynamic_batching { max_queue_delay_microseconds: 100 } max_queue_delay_microseconds はバッチを形成するためにリクエストを待機する最大時間をマイクロ秒で指定します。この待機時間内に届いたリクエストをまとめて1回の推論で処理します。 本システムでは max_batch_size: 1 としてDynamic Batchingを無効にしています。リリース直後でリクエスト数が少なくバッチを形成するための待機時間がレイテンシ悪化につながるためです。リクエスト数が増加した場合は max_batch_size を増やし dynamic_batching を有効にすることでスループットを向上できます。 GPUで推論する場合は instance_group で kind: KIND_GPU を指定します。 instance_group [ { kind: KIND_GPU count: 2 } ] count はGPUごとに起動するモデルインスタンス数を指定します。本システムではCPUで推論しています。リリース直後はリクエスト数が少なくCPUでも十分な処理性能を確保できるためです。GPUはCPUと比べてコストが高いため、リクエスト数の増加によりCPUでの処理が追いつかなくなった段階でGPUへの移行を検討します。 推論ゲートウェイAPIの実装 推論ゲートウェイAPIはBackend Serverからのリクエストを受け取り推論結果を返すAPIです。本システムでは tritonclient ライブラリのgRPCクライアントを使用しています。 Triton接続の設定 tritonclientで推論リクエストを送信する際の設定項目は以下の通りです。 設定項目 必須/任意 説明 接続先URL 必須 Tritonサーバーのホストとポート。gRPCのデフォルトポートは8001 モデル名 必須 config.pbtxtの name フィールドと一致させる モデルバージョン 任意 使用するモデルのバージョン番号。省略時は最新バージョンを使用 タイムアウト 任意 リクエストのタイムアウト時間 gRPCチャンネル設定 任意 gRPCチャンネルに渡す引数 本システムではこれらの設定を以下のように定義しています。 # 接続先URL: KubernetesのHeadless Service名とgRPCポートを指定 triton_url = "triton-inference-server.match-genre-extract.svc.cluster.local:8001" # モデル名: config.pbtxtのnameフィールドと一致させる model_name = "genre_extract_torchscript" # モデルバージョン model_version = "1" # タイムアウト timeout_ms = 1000 # gRPCチャンネル設定: ロードバランシングポリシーをround_robinに設定 channel_args = [( "grpc.lb_policy_name" , "round_robin" )] channel_args でgRPCクライアントのロードバランシングポリシーを設定できます。値の指定によりTritonサーバーへの負荷分散の方法を指定できます。この設定の詳細は後述の「 課題1:gRPCで負荷分散されない 」で説明します。 StyleFeatureExtractorクラスの実装 Tritonへの推論リクエストを行うクラスを実装します。なおコード例では入出力名やデータ型といったモデル依存の設定値をハードコーディングしていますが、実際の運用では環境変数から取得しています。 import numpy as np import tritonclient.grpc as grpcclient from opentelemetry.propagate import inject from tritonclient.grpc import InferResult from api.env_settings import settings class StyleFeatureExtractor : _INPUT_NAME = "image_input" _OUTPUT_NAME = "genre_scores" _DATA_TYPE_FP32 = "FP32" def __init__ (self): self.triton_client = grpcclient.InferenceServerClient( url=settings.triton_url, channel_args=settings.channel_args ) def predict_score_and_vector (self, image_data: np.ndarray) -> list [MLGenreScore]: trace_headers = {} inject(trace_headers) inputs = [ grpcclient.InferInput( self._INPUT_NAME, image_data.shape, self._DATA_TYPE_FP32 ) ] inputs[ 0 ].set_data_from_numpy(image_data) outputs = [grpcclient.InferRequestedOutput(self._OUTPUT_NAME)] response: InferResult = self.triton_client.infer( model_name=settings.model_name, inputs=inputs, outputs=outputs, model_version=settings.triton_model_version, headers=trace_headers, timeout=settings.triton_timeout_ms, ) scores = response.as_numpy(self._OUTPUT_NAME) _INPUT_NAME と _OUTPUT_NAME は前述の config.pbtxt で定義した入出力名と一致させます。 __init__ では前述の設定値を使って InferenceServerClient を初期化します。 predict_score_and_vector メソッドではW3C Trace Contextヘッダーを準備しています。これはDatadog APMとの連携に使用するもので、詳細は「 課題2-Datadog APMでトレースが連携されない 」で説明します。 推論リクエスト処理の実装 前述の StyleFeatureExtractor を使用して推論します。まず前処理としてダウンロードした画像をモデルの入力形式に変換します。tritonclientは入力データとしてnumpy配列を受け取るため、PyTorchテンソルから変換して渡します。 # 前処理 PREDICT_IMAGE_TRANSFORM = transforms.Compose([ transforms.Resize(( 224 , 224 )), transforms.ToTensor(), transforms.Normalize(mean=[ 0.485 , 0.456 , 0.406 ], std=[ 0.229 , 0.224 , 0.225 ]), ]) img_tensor = PREDICT_IMAGE_TRANSFORM(img) image_data = img_tensor.unsqueeze( 0 ).numpy() # 推論リクエスト ml_genre_scores = STYLE_FEATURE_EXTRACTOR.predict_score_and_vector(image_data) # 後処理... transforms.Resize で224×224ピクセルにリサイズし、 transforms.ToTensor でPyTorchテンソルに変換します。 transforms.Normalize ではImageNetデータセットの平均と標準偏差で正規化しています。最後にtritonclient用として unsqueeze(0) でバッチ次元を追加し numpy() でnumpy配列へ変換します。 推論リクエストでは前述の StyleFeatureExtractor の predict_score_and_vector メソッドを呼び出してTritonに送信します。 後処理では、Tritonから返却された12種類のファッションジャンルに対するスコアを、サービス要件に合わせたジャンルIDへマッピングして診断結果を生成します。詳細は本記事のスコープ外のため省略します。 以上が推論ゲートウェイAPIの実装です。 導入時の課題と解決策 ここまでTritonの導入方法を説明しましたが、導入にあたりいくつかの課題に直面しました。本節ではそれらの課題とその解決策を説明します。 課題1:gRPCで負荷分散されない Tritonサーバーでデプロイした推論APIの性能を測るために実施した負荷試験においてTriton Podを1台から複数台構成にしても処理できるリクエスト数がほとんど増加しませんでした。Pod数を増やして、適切に負荷分散がされれば、処理できるリクエスト数は増加します。 調査の結果、負荷が均等に分散されておらず、特定のPodのCPU使用率が100%近くに達する一方で他のPodはほとんど使用されていないことが判明しました。 原因 この問題はgRPCの特性およびKubernetesの負荷分散の仕組みに起因していました。 gRPCはHTTP/2上に構築されたL7プロトコルです。HTTP/1.1では1つのTCPコネクション上で同時に1つのリクエストしか処理できないため複数の並行リクエストには複数のTCPコネクションが必要です。一方HTTP/2では単一の長寿命なTCPコネクションを維持しその上で複数のリクエストを多重化します。 しかしこの特性はKubernetesの負荷分散との相性に問題があります。KubernetesにおいてService経由のトラフィックの負荷分散を担うのはkube-proxyです。kube-proxyはiptablesやIPVSを使用してTCPコネクション単位で負荷分散を行いコネクションが確立されるタイミングで振り分け先のPodを決定します。ただしkube-proxyはL4での負荷分散を行うため、L7プロトコルであるgRPCの個々のリクエストを認識できません。HTTP/2では単一のTCPコネクションが長時間維持されるためそのコネクション上のすべてのgRPCリクエストが最初の振り分け先Podへ集中してしまいます。 次の図に問題のある構成を示します。クライアントからのTCPコネクションが1つのPodに集中し他のPodはアイドル状態になっています。 解決策 gRPCの負荷分散の問題に対しては主に2つの解決策があります。 1つ目は Linkerd や Istio などのサービスメッシュの導入です。各Podにサイドカープロキシを注入してL7で負荷分散を行うためアプリケーションコードを変更せずにgRPC負荷分散を実現できます。一方で各Podにサイドカーが必要となりCPUやメモリを消費します。またコントロールプレーンの運用も必要になります。 2つ目はクライアントサイド負荷分散です。gRPCクライアントの設定でHeadless ServiceとDNSベースの負荷分散を利用します。サイドカーを経由しないため高いパフォーマンスを実現できます。一方でクライアント側での設定変更が必要であり負荷分散アルゴリズムはround_robinなど基本的なものに限定されます。 本システムではクライアントサイド負荷分散を採用しました。追加コンポーネントが不要で既存のKubernetesリソースとgRPCクライアントの設定変更のみで実現できるためです。また本システムではAPIサーバーからTritonへの内部通信のみが対象でありクライアント側の設定変更が可能です。推論処理の所要時間がほぼ一定であるためround_robinによる均等分散で十分な負荷分散の効果が得られると判断しました。具体的には次の2つの変更をしました。 1. Headless Serviceの採用 通常のClusterIP Serviceの代わりにHeadless Service( clusterIP: None )を使用しました。Headless ServiceはDNSクエリに対してPodのIPアドレスリストを直接返すためクライアント側で接続先を制御できます。 2. クライアント側でround_robinロードバランシングを設定 gRPCクライアントの設定で round_robin ロードバランシングポリシーを指定しました。これによりDNSで取得した複数のPod IPに対してリクエストを均等に分散できます。 tritonclient ライブラリでは InferenceServerClient の channel_args パラメータでgRPCチャンネルオプションを設定できます。 channel_args: list [ tuple [ str , str ]] = [( "grpc.lb_policy_name" , "round_robin" )] self.triton_client = grpcclient.InferenceServerClient( url=settings.triton_url, channel_args=settings.channel_args # round_robinを設定 ) grpc.lb_policy_name を round_robin に設定することでHeadless ServiceのDNSが返す複数のPod IPに対してリクエストごとに接続先を切り替えます。 次の図に解決後の構成を示します。クライアントがDNSから取得した複数のPod IPに対してround_robinでリクエストを分散しています。 スケールアウト時の負荷分散 上記のクライアントサイド負荷分散により起動時に存在するTriton Pod間での負荷分散は実現できました。しかし HPA などによるスケールアウトで新しいPodを追加した場合、そのPodへトラフィックを分散できない問題が残りました。 gRPCクライアントはすべてのSubchannelが切断された場合のみDNS名を再解決する仕様になっています。本システムではAPIサーバー起動時にTritonクライアントを生成しアプリケーション動作中は同じ接続を維持します。そのため既存の接続が維持されている限りDNS再解決は行われずスケールアウトで追加された新しいPodを検出できません。 この問題に対してはサーバー側で接続が存続できる最大時間を設定することで解決しました。gRPCは接続がクローズされるとDNS名を再解決するためサーバー側で定期的に接続を切断することでクライアントに再接続を促し、新しいPodを含めた負荷分散を実現できます。 具体的にはTritonの起動オプションで以下を設定しました。 --grpc-max-connection-age=30000 --grpc-max-connection-age-grace=10000 grpc-max-connection-age は接続が存在できる最大時間をミリ秒で指定するパラメータです。この時間を超えるとサーバーが接続を終了します。また grpc-max-connection-age-grace は接続終了後に処理中のRPCが完了するまでの猶予時間です。 30秒という値は新しいPodがReadyになってから最大30秒で負荷分散が開始されることを意味します。HPAによるスケールアウト自体に数分かかることを考えると30秒の遅延は許容範囲です。10秒という猶予時間は本システムの推論処理が非機能要件で1秒以内と定義されているため処理中のRPCを完了させるのに十分な時間です。 これらの変更によりPod数の増加に応じて線形にスループットが向上するようになりました。 課題2:Datadog APMでトレースが連携されない APIサーバーとTritonを分離した構成を導入したところDatadog APMでAPIサーバーとTritonのトレースを連携できない問題が発生しました。従来の一体型構成では1本のトレースとして追跡できていましたが、分離した構成では別々のトレースとして表示され同一リクエストの処理として紐付けられませんでした。障害調査時にエンドツーエンドでレイテンシを分析するためにはトレースの連携が必要でした。 原因 この問題はトレースコンテキストの伝播形式の違いに起因していました。 分散トレーシングではサービス間でトレース情報をHTTPヘッダーにより伝播し、複数のサービスを横断するリクエストを1本のトレースとして追跡します。しかしAPIサーバーとTritonでは伝播形式が異なっていました。 APIサーバー側:Datadog独自形式( x-datadog-* ヘッダー) Triton側: OpenTelemetry 形式のみサポート 各トレーシングツールで独自のヘッダー形式を使用していたため、サービス間でトレース情報が正しく伝播されず、トレースの分断が発生していました。 次の図は Monitor OpenTelemetry with Datadog and W3C Trace Context から引用した異なるトレーサーが混在する環境での問題を示しています。OTel TracerとDD Tracerがそれぞれ独自形式のTrace IDを使用するため各サービスのSpanがDatadogに送信されても同一トレースとして紐付けられません。図右側のAPM FlamegraphではDD Tracerを使用するSERVICE BのSpanのみが表示され、OTel Tracerを使用するSERVICE AとCは別トレース扱いになっています。本システムでも同様の問題が発生していました。 解決策 この問題を解決するために次の3つの対応をしました。 W3C Trace Contextによるトレース伝播 Triton専用NodeにDatadog AgentをDaemonSetで配置 Tritonの起動オプションでトレース設定 1. W3C Trace Contextによるトレース伝播 W3C Trace Contextは異なるトレーシングシステム間でトレース情報を共有するための標準規格です。HTTPヘッダー( traceparent 、 tracestate )を通じてトレースIDを伝播します。 APIサーバーからTritonへのリクエスト時にOTelの inject 関数を使用してトレース情報をヘッダーに埋め込みます。 from opentelemetry.propagate import inject def predict_score_and_vector (self, image_data: np.ndarray): # 現在のトレースコンテキストをW3C Trace Context形式でヘッダーに埋め込む trace_headers = {} inject(trace_headers) # Tritonへの推論リクエスト時にヘッダーを付与 response = self.triton_client.infer( model_name=self.model_name, inputs=inputs, outputs=outputs, headers=trace_headers, # W3C Trace Contextヘッダー timeout=settings.triton_timeout_ms, ) inject 関数はddtraceが管理する現在のスパンのトレースIDとスパンIDをW3C Trace Context形式のヘッダーに変換します。Triton側はこのヘッダーを読み取り同じトレースIDで新しいスパンを作成します。これによりddtraceで計装されたAPIサーバーとOTelで計装されたTritonの間でトレースが連携されます。本実装ではddtrace 2.21.8を使用しています 1 。 2. Triton専用NodeにDatadog AgentをDaemonSetで配置 TritonはOTel形式でトレースを出力するためOpenTelemetry Protocol(以下、OTLP)形式を受け取れるDatadog Agentが必要です。 KubernetesにおけるDaemonSetはクラスタ内の各NodeでPodが1つずつ稼働することを保証するリソースです。ログ収集やモニタリングエージェントなど各Nodeで実行が必要なシステム機能のデプロイに適しています。次の図にNode内でのトレース送信の流れを示します。 OTelで計装されたTritonからのトレースは同一Node上のDatadog AgentがOTLP形式で受け取りDatadog Backendに送信します。Tritonが稼働するNodeにDaemonSetでDatadog Agentを配置しport 4318でOTLPリクエストを受け取るよう設定しました。以下はその設定例です。 apiVersion : apps/v1 kind : DaemonSet metadata : name : triton-datadog-agent spec : template : spec : affinity : nodeAffinity : requiredDuringSchedulingIgnoredDuringExecution : nodeSelectorTerms : - matchExpressions : - key : cloud.google.com/gke-nodepool operator : In values : - <triton-nodepool-name> containers : - name : datadog-agent image : datadog/agent:7.56.1 env : - name : DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT value : "0.0.0.0:4318" ports : - containerPort : 4318 hostPort : 4318 name : traceporthttp protocol : TCP なお hostPort を使用するとNodeのポートが外部に公開されクラスタへのネットワーク侵入経路となる可能性があるため絶対に必要な場合を除き避けることが推奨されています。本記事の構成ではTritonが稼働するNode上でのみDatadog Agentと通信するために使用しておりプライベートなGKEクラスタ内での利用を想定しています。パブリックなクラスタで利用する場合はファイアウォールルールやネットワークポリシーで適切にアクセス制限を行ってください。 3. Tritonの起動オプションでトレース設定 Triton側では起動オプションでOTelによるトレース出力を有効にしました。 --trace-config level=TIMESTAMPS --trace-config rate=1 --trace-config mode=opentelemetry --trace-config opentelemetry,url=http://$(DD_AGENT_HOST):4318/v1/traces これらの設定によりAPIサーバーからTritonへの流れが1本のトレースとして表示され、スパンごとのボトルネック分析が可能になりました。次の図は本システムで実際にDatadog APMに表示されたトレースです。 APIサーバーとTritonのSpanが1本のトレースとして連携されていることがわかります。 課題3:PyTorchモデルがロードされない Tritonを起動したところPyTorchモデルのロードに失敗してTritonが起動しませんでした。モデルファイルは正しいパスに配置されているにもかかわらず以下のようなエラーが出力されました。 UNAVAILABLE: Internal: failed to load model 'genre_extract_torchscript': PytorchStreamReader failed locating file constants.pkl: file not found このエラーは、通常のPyTorchモデルを、TorchScript形式のモデルとして読み込もうとした場合に発生します。 原因 TritonのPyTorch BackendはTorchScript形式のモデルのみをサポートしています。配置していたモデルはTorchScript形式ではなかったためロードできませんでした。 解決策 PyTorch Backend ではすべてのPyTorchモデルをTorchScript形式に変換する必要があります。本システムでは torch.jit.script を使用してモデルを変換しました。 # Before: PyTorchモデルの重みをそのまま保存 torch.save(model.state_dict(), 'best_model.pt' ) # After: TorchScript形式で保存 jit_script = torch.jit.script(model).eval() jit_script.save(f "{model_dir}/best_model_torchscript.pt" ) この変更によりモデルが正常にロードされました。 導入効果 本節では本番運用を通じて実際に確認できた効果を紹介します。 性能面では同じ総Pod数で目標レイテンシ以内に処理可能なリクエスト数が約38%向上しました。Triton導入前はAPI Pod 4台(CPU)で24rpsが上限でした。導入後はAPI Pod 3台とTriton Pod 1台(計4台、いずれもCPU)で33rpsまで処理できるようになりました。 運用面では config.pbtxt による宣言的な設定管理により設定変更が容易になりました。 項目 Before(Triton導入前) After(Triton導入後) バッチサイズ設定 コード修正が必要 max_batch_size で設定 推論インスタンス数設定 ワーカー数やPod数を調整 instance_group で設定 Dynamic Batching設定 自前実装が必要 dynamic_batching で設定 モデル入出力定義 コード内で暗黙的に定義 input / output で設定 モデルバージョン管理 独自の命名規則で運用 ディレクトリ構造で標準化 またTriton公式のPerf Analyzerにより前処理・後処理の影響を排除した推論単体の性能検証が可能になりました。 スケーラビリティの面ではジャンル診断APIの低レイテンシとスケーラビリティの要件に備えた体制が整いました。APIサーバーとTritonで責務を分離したことで水平スケールや垂直スケールをそれぞれ独立して適用できます。推論がボトルネックになればTritonにGPUを割り当て、前処理がボトルネックになればAPIサーバーのCPUを増強するといった柔軟なリソース配分が可能です。Dynamic Batchingの有効化も設定変更だけで対応できます。 本番稼働から半年以上が経過し大きな障害なく安定稼働しています。社内でもTriton導入を検討するチームがあり本番運用の実績とナレッジを共有できる状態になりました。 導入時の注意点 コスト面の考慮 Tritonを導入する際はコスト面での検討が重要です。リクエスト数が少ない場合、逆にコスト増となる可能性があります。 MLOpsブロックではAPI系サービスの可用性担保のためAPIサーバーを最小3台構成としており、導入前は3台で運用していました。導入後はAPIサーバー3台に加えて可用性担保のためTriton Pod 2台が必要になり合計5台構成となりました。同スペックのPodを使用しているため単純計算で約67%のコスト増です。 一方前述の性能検証で示した通りTriton導入により同Pod数でスループットが約38%向上しています。つまりAPIサーバーのみで同等のスループットを得るには5台以上のPodが必要となる計算です。リクエスト数の増加でスケールアウトが必要な場合、Triton構成はコスト効率が良くなります。 現時点ではサービス開始直後でリクエスト数が少なく最小構成での運用となっているためコスト削減効果があるとは言えません。ただし運用・保守性の向上というメリットは得られています。またリクエスト増加時には同Pod数でスループットが約38%向上している点に加えGPUの活用やDynamic Batchingの有効化によるさらなるコスト効率の改善が見込まれます。 まとめ 本記事ではZOZOマッチのジャンル診断APIにTritonを導入した背景から課題と解決策、導入効果までを紹介しました。 社内初の導入で様々な課題に直面しましたがそれぞれ解決できました。 結果として同Pod数でスループットが約38%向上しconfig.pbtxtによる宣言的管理で運用性が向上しました。またAPIサーバーとTritonで役割を分担することで責務が明確になりました。 導入前は社内での運用実績がなく、トラブル発生時の対応やナレッジ不足を懸念していました。しかし本番稼働から半年以上が経過し大きな障害なく安定稼働しています。社内でもTriton導入を検討するチームがあり本番運用の実績とナレッジを共有できる状態になりました。 本記事で紹介した導入時の知見がこれからTritonの導入を検討している方の参考になれば幸いです。 最後になりますがZOZOでは一緒にサービスを作り上げてくれる方を募集中です。MLOpsブロックでも絶賛採用を行っているためご興味ある方は以下のリンクからぜひご応募ください。 hrmos.co 将来のバージョンではこの手動でのヘッダー埋め込みが不要になる可能性があります。詳細は GitHub Discussion を参照してください。 ↩
アバター
.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(1) { display: none; } こんにちは。検索基盤部の橘です。検索基盤部では、ZOZOTOWN検索の品質向上を目指し、検索結果の改善に取り組んでいます。 ZOZOTOWN検索の並び順の精度改善の取り組みについては以下の記事をご参照ください。 techblog.zozo.com 検索基盤部では新しい改善や機能を導入する前にA/Bテストを行い効果を評価しています。A/Bテストの事前評価として、オフラインの定量評価と定性評価を実施しています。これらの評価によりA/Bテストの実施判断をしています。 過去のフィルタリング処理の効果検証として導入したオフライン定量評価の方法については以下の記事をご参照ください。 techblog.zozo.com techblog.zozo.com 本記事では、検索基盤部が最近実践したLLMを用いた新しいオフライン定量評価の手法についてご紹介します。 目次 目次 オフライン定量評価の課題 課題解決のアプローチ LLMによる関連性判定の研究事例とTips プロンプト設計の重要性 マルチモーダル情報の活用 品質保証とモニタリング LLMと人間の判定傾向の違い MLLMを用いて定量評価を実施するまでのフロー ステップ1:関連性の判定基準の改善とゴールドセット作成 ステップ2:ゴールドセットによるラベリングMLLMの評価と最適化 ステップ3:ラベリングMLLMを用いた検索ロジックの定量評価 関連性の判定基準の改善とゴールドセット作成 実施した改善詳細 策定された判定基準の例 デザイン・感性・審美的要素の「程度」の主観的判断に関する基準 Highly relevant Acceptable Substitute Irrelevant ゴールドセットによるラベリングMLLMの評価 評価方法と指標 評価データ 評価対象 評価結果 結果の考察 MLLMモデルの性能と精度 プロンプト改善の効果 ラベリングMLLMを用いた検索ロジックの定量評価 評価フロー 評価対象クエリの選定 検索ロジックの実行 ラベリングMLLMによる関連性判定 評価指標の算出 nDCG@K(normalized Discounted Cumulative Gain) Precision@K ロジック間の性能比較 ラベリングMLLMを用いた評価手法の利点 バイアスの排除 スケーラビリティ まとめ オフライン定量評価の課題 ZOZOTOWN検索の精度評価への取り組み では、検索結果ログを用いた定量評価の手法を紹介しました。ZOZOTOWN検索のオフライン定量評価は、『フィルタリング(絞り込み)ロジック』と『ランキング(並び順)ロジック』を対象としています。 検索結果ログを用いた評価は、ランキングロジックでは有効ですが、フィルタリングロジックでは課題があります。検索結果ログは既存のcontrolロジックの結果にバイアスがかかっているため、treatmentロジックには不利な評価になる傾向があります。上記のテックブログで紹介した手法でも、treatmentロジックがcontrolロジックと同等である可能性は評価できますが、優れているとまでは判断できませんでした。 課題解決のアプローチ 検索クエリとヒットした商品の関連性を判定するアプローチとして、一般的に以下が存在します。 検索結果ログによる判定 :検索クエリとヒットした商品のコンバージョンにより判定 人手による判定 :人間が目視で関連性を評価 LLMによる判定 :大規模言語モデルが関連性を自動判定 近年、eコマースや情報検索の分野では、従来は人手で行っていた関連性判定をLLMに代替させる取り組みが進んでいます。 LLMを利用して関連性を判定するメリットは他の手法に比べて以下があります。 大量データの処理が可能 :人手による判定と比較して、数千から数万件のデータを短時間で処理できる 一貫性のある判定基準 :人によるばらつきを抑え、定義された基準に基づいて一貫した判定が可能である コスト効率の良さ :人手によるアノテーション作業と比較して、コストと時間を大幅に削減できる 柔軟な基準適用 :プロンプトの調整により、複雑な判定基準を容易に適用・変更できる バイアスの軽減 :検索結果ログに含まれる既存ロジックへのバイアスを避けられる ただし、以下のような懸念点も想定されます。 判定精度の不確実性 :人間による判定と同様、LLMの判定精度も100%には達しておらず、誤判定を含む可能性がある ドメイン知識の限界 :ファッションECサイト特有の用語や文脈について、正確に理解できない可能性がある プロンプト依存性 :プロンプトの書き方次第で判定結果は大きく変動する可能性がある これらの懸念点に適切に対処するため、既存の研究事例から得られた知見を参考にしました。 LLMによる関連性判定の研究事例とTips LLMを用いた検索システムの関連性評価は、近年のeコマースや情報検索の分野で活発に研究されています。本取り組みでは、特に以下の研究事例から得られた知見を参考にしました。 Retrieve, Annotate, Evaluate, Repeat: Leveraging Multimodal LLMs for Large-Scale Product Retrieval Evaluation (Zalando, 2024):画像とテキストのマルチモーダルLLMを使用して、大規模な商品検索の関連性評価を自動化した実践研究 Large Language Models can Accurately Predict Searcher Preferences (Microsoft, SIGIR 2024):検索システム評価に必要な関連性ラベルを、LLMで高精度かつ大規模に生成できることを実証した研究 Automated Query-Product Relevance Labeling using Large Language Models for E-commerce Search (Walmart, 2024):Eコマースサイトにおけるクエリと商品の関連性ラベリングをLLMで自動化した実践研究 これらの研究から、LLMによる関連性判定を実用化する上で重要となるTipsを以下にいくつか共有します。 プロンプト設計の重要性 LLMによる判定精度は、プロンプトの設計に大きく依存します。Microsoftの研究では、プロンプトを以下の5つのパートに分けて構成することが効果的であると示されています。 Role(役割) :「あなたは検索結果の評価者です」と役割を明示し、判定基準の定義を提示する Description(クエリの要約) :クエリの意図を簡潔に要約する Narrative(判定基準の詳細) :関連あり/なしの境界条件や、迷った時の優先順位を明示する Aspects(評価観点) :判定を複数の観点に分解して段階的に評価させる。例えば、topicality(クエリとの話題一致度)とtrust(情報の信頼性・整合性)を個別に評価させた上で、overall(最終判定)を出力させる Output(出力形式) :JSONなど構造化された形式で出力させ、自動処理を容易にする 特に、 Aspects(評価観点)を明示的に設計する ことで精度向上に大きく寄与します。例えば、topicality(クエリとの話題一致度)とtrust(情報の信頼性・整合性)を個別に評価させた上で、overall(最終判定)を出力させます。この構成により、判断の安定性と精度の向上を実現できると示されています。 同研究では、プロンプト要素の組み合わせを評価した結果、 Description + Narrative + Aspects(DNA)の構成が最も高い精度 を示しました。また、プロンプトの表現によっても精度が変動するため、少量のゴールドラベルに基づいてプロンプトを最適化することが推奨されています。 マルチモーダル情報の活用 ファッションECサイトにおいては、商品の視覚的特徴(色、形状、デザインなど)に関連性判定で重要な役割を果たします。Zalandoの研究では、 商品のテキスト情報と画像を同時に入力できるマルチモーダルLLM を使用することで、テキストのみの場合と比較して判定精度の向上を示しています。 品質保証とモニタリング LLMによる判定の精度は100%でありません。そのため、 人間による品質保証とモニタリングの重要性 の推奨がなされています。具体的に、以下のような運用方法の効果が示されています。 難例や微妙なケースは人間が判定 :LLMと人手で判定が大きく食い違うケース、主観的で曖昧なクエリなどは、人間が優先的に確認する 継続的なモニタリング :定期的にLLMのラベリング結果をサンプリングし、人手で再ラベリングして精度を監視する ゴールドセットによる検証 :少量の高品質なゴールドセット(人手で慎重にラベリングしたデータ)を作成し、LLMの判定精度を定量的に評価する LLMと人間の判定傾向の違い 関連性判定におけるLLMと人間の判定傾向には特徴的な違いのあることが知られています。 LLMのアノテーションの判断は厳しめに寄りやすく、人間の判定は甘めによる傾向がある と示されています。 具体的には、人間にブランド名やカテゴリの誤判定が多く見られる一方で、LLMには翻訳ミスや視覚的な誤認識の発生があります。この違いを理解した上で、 アノテーションのラベル設計を比較的粗めにする(3段階など) ことが一般的です。粗めのラベル設計には以下のような利点があります。 認知負荷の軽減 :判定者がラベルを選択する際の迷いを減らし、判定作業を効率化できる 曖昧な境界の削減 :細かすぎるラベル分類では判定基準が曖昧になりやすく、一貫性を保つことが困難になる デバッグの容易性 :誤判定が発生した際に、原因を特定しやすく、プロンプトの改善につなげやすい 以上の知見を踏まえ、本取り組みでは マルチモーダルLLM(MLLM) を採用し、以下のアプローチを実施しました。 ラベル設計 :MLLMと人間の判定傾向の違いを考慮し、3段階のラベル(Highly relevant / Acceptable substitute / Irrelevant)を設定(詳細は後述)。認知負荷を軽減し、曖昧な境界を減らして一貫性を確保。 マルチモーダル情報の活用 :商品のテキスト情報だけでなく画像も判定に利用。ファッションECサイト特有の視覚的要素を反映し、より人間に近い精度の関連性評価を実現。 判定基準の明確化 :関連あり/なしの境界条件や季節性、複数基準の優先順位、曖昧なクエリの扱いなど、難しいケースへの対応方針をプロンプトに具体的に記述。 評価と検証のプロセス :検索基盤部チーム作成のゴールドセットでMLLMの判定精度を検証し、最適なプロンプトとモデルを選定。大量データ処理でも判定品質を担保し、チームの判定と同等の精度を目指す。 その上で、選定したMLLMを用いて大量の評価用データを作成し、検索ロジックの定量評価を実施することにしました。 MLLMを用いて定量評価を実施するまでのフロー MLLMによる関連性判定には「判定精度の不確実性」という課題があります。MLLMの判定が100%正確ではないため、まず 信頼できるラベリングMLLMを構築すること が重要です。そのためには、MLLM自体の性能を評価し、検索基盤部チームの判定基準に沿った判定ができるかを検証する必要があります。 本取り組みでは、以下の3段階のアプローチで進めました。 ステップ1:関連性の判定基準の改善とゴールドセット作成 まず、チーム内で暗黙知となっていた判定基準を明確化し、言語化します。ペアラベリング→不一致分析→基準改善を実施することで、判定基準を体系的に整理すると同時に、高品質なゴールドセット(正解データ)を作成します。 このゴールドセットは、次のステップでMLLMの性能を評価するための基準となります。 ステップ2:ゴールドセットによるラベリングMLLMの評価と最適化 作成したゴールドセットを用いて、複数のMLLMモデルとプロンプトの組み合わせを評価します。ゴールドセットに最も近い判定結果を出せる最適な組み合わせを選定することで、検索基盤部チームの判定基準を再現できる信頼性の高いラベリングMLLMを構築します。 この検証により、大量データ処理においても判定品質を担保できることを確認します。 ステップ3:ラベリングMLLMを用いた検索ロジックの定量評価 選定したラベリングMLLMを用いて、数千から数万件のクエリ商品ペアを自動ラベリングし、検索ロジックの定量評価を実施します。従来の検索結果ログに依存した評価と異なり、既存ロジックへのバイアスを排除した公平な評価が可能になります。 以下、各ステップの詳細を説明します。 関連性の判定基準の改善とゴールドセット作成 MLLMに検索クエリと商品の関連性を判定させるためには、明確な判定基準をプロンプトに盛り込むことが重要です。しかし、当初は「関連性の判定基準」がチーム内で完全に言語化・網羅されているわけではなく、曖昧な部分も多く存在していました。 そこで本取り組みでは、 段階的な基準策定プロセス を採用しました。ペアラベリング→不一致分析→基準改善をすることで、判定基準とゴールドセットの品質を同時に高めていきました。 実施した改善詳細 以下に実施した改善の具体的な詳細を示します。 手順1:既存基準の整理 チーム内で共有されていた関連性の判定基準を洗い出し、明文化しました。 手順2:ペアラベリング 既存基準を元に2人1組で同じ検索クエリと商品ペアを独立にラベリング。 手順3:不一致分析 1,000件中293件(29.3%)で判定が不一致。不一致データから不一致原因を特定しました。 手順4:基準追加・修正 不一致原因から11個の新しい基準をチームで相談の上に作成しました。その新基準を既存基準に結合します。 その後不一致データに対して修正された基準を元にラベリングを行い、不一致を解消していきました。 策定された判定基準の例 この改善プロセスを通じて策定された基準の一例として、以下のような「デザイン・感性・審美的要素」に関する判定基準があります。 デザイン・感性・審美的要素の「程度」の主観的判断に関する基準 「デザイン性」「雰囲気」など、数値化できない主観的な要素が、どのレベルなら適合とみなすか。 Highly relevant 以下のいずれかに該当する場合、 Highly relevant と判定する。 1. テキストに明示的に含まれる 商品情報のテキスト(商品名・説明文・タグ等)において、該当キーワードが明示的に含まれている 例: デザイン → デザインニット 例: シアー → シアーシャツ 2. 同義語が含まれる 商品情報のテキストに、該当キーワードの同義語が含まれている 例: デザイン → Patterned knit (patternedということはデザインがあるということ) 3. 画像から明確に読み取れる 商品情報の画像から、該当キーワードの要素が明確かつ視覚的に読み取れる 例: シティボーイ → (シティボーイが着てそうなアイテムの画像) Acceptable Substitute 以下のいずれかに該当する場合、 Acceptable Substitute と判定する。 1. 画像から関連要素が読み取れる テキストには明示されていないが、商品情報の画像から該当キーワードに関連する要素が読み取れる 例: シティボーイ → (シティボーイ好きな人が着そうなアイテムの画像だが、テキストには記載なし) 2. 関連語や関連カテゴリ 該当キーワードそのものではないが、商品情報のテキストに関連語や関連カテゴリの特性が記載されている 例: 柄 → (ワンポイントやラインのみのアイテム) (柄とは言わないが関連するカテゴリ) 3. 周辺カテゴリに属する テキストや画像に明確な該当要素はないものの、該当キーワードの文脈で許容できる周辺カテゴリに属する 例: シアー → (透け感はないが薄手の軽やかな素材のアイテム) Irrelevant 以下の場合、 Irrelevant と判定する。 上記のHighly relevantおよびAcceptable Substituteのいずれの条件にも該当しない 商品情報のテキストや画像において、該当キーワードとの関連性が見出せない 例: 柄 → (無地のTシャツ) 例: デザイン → (シンプルな無地のTシャツで特にデザイン性がないもの) このアプローチにより、検索基盤部チームの知見を体系的な基準として言語化し、MLLMのプロンプトへの反映ができるようになりました。 また、上記の過程で得られた不一致が解消され人間によってラベリングされたデータは『ゴールドセット』としてこの後のラベリングMLLMの評価で利用しました。 ※ただし、すべての不一致を完全に解消することは難しく、一部のデータは判定が分かれたままとなりました。ゴールドセットでは、こうしたケースについては複数のラベルを付与した状態で管理しています。 ゴールドセットによるラベリングMLLMの評価 ゴールドセットを作成した後、次のステップとしてMLLMによる関連性判定の精度を評価しました。この評価の目的は、 最適なMLLMモデルとプロンプトの組み合わせを選定すること です。 評価方法と指標 MLLMによる判定結果と、ゴールドセットのラベルを比較し、精度を算出しました。具体的には、MLLMによる判定が ゴールドセットのラベルにマッチしていれば正解 、そうでなければ不正解としました。 評価データ 評価には、前述の方法で作成したゴールドセットを使用しました。ゴールドセットには検索クエリと商品の組み合わせに対して、ラベルが付与されています。 評価対象 以下のMLLMモデルとプロンプトの組み合わせを評価しました。 評価対象MLLMモデル : Gemini 2.5 Flash Gemini 2.5 Flash Lite Gemini 2.0 Flash Lite 評価対象プロンプト : 改善前プロンプト:初期に作成したラベリング基準のみを含むプロンプト 改善後プロンプト:改善プロセスで追加されたラベリング基準を統合したプロンプト 評価結果 以下に各設定における精度評価の結果を示します。 MLLMモデル プロンプト 精度 Gemini 2.5 Flash 改善後 74.1% (+18.7%) Gemini 2.5 Flash Lite 改善後 66.4% (+11.0%) Gemini 2.0 Flash Lite 改善後 61.7% (+6.3%) Gemini 2.5 Flash 改善前 73.0% (+17.6%) Gemini 2.5 Flash Lite 改善前 66.2% (+10.8%) Gemini 2.0 Flash Lite 改善前(ベースライン) 55.4% 括弧内の数値はGemini 2.0 Flash Liteの改善前プロンプトをベースラインとした際の向上率を示しています。 結果の考察 評価結果から以下の知見が得られました。 MLLMモデルの性能と精度 より高性能なMLLMモデルほど関連性判定の精度が向上する傾向を確認できました。特にGemini 2.5 Flashは 74.1%と最高精度を記録 し、実用的なラベリング精度に到達していると判断できました。 プロンプト改善の効果 改善プロセスによってプロンプトを改善した結果、全モデルで精度の向上が見られました。特にGemini 2.0 Flash Liteでは 6.3ポイントの改善 があり、基準の明確化が効果的であることを確認できました。 ただし、Gemini 2.5 Flash間では1.1ポイントと、改善幅は限定的でした。この結果については、以下のような要因が考えられます。 プロンプトに基準を追加したことで内容が複雑化し、MLLMによる意図通りの判定ができなくなったケースも存在する ゴールドセット自体のラベリングの一貫性や正確性に課題があり、MLLMの判定と正解データの必ずしも一致しない場合も存在する ラベリングMLLMを用いた検索ロジックの定量評価 前節で選定したラベリングMLLMを用いることで、検索ロジックの定量評価ができます。具体的には以下のような評価が可能です。 全文検索のロジックAとロジックBの検索結果の比較 ベクトル検索のロジックAとロジックBの検索結果の比較 本セクションでは、このラベリングMLLMをどのように活用して検索ロジックを評価するかについて説明します。 評価フロー ラベリングMLLMを用いた検索ロジック評価は、以下の流れで実施します。 この流れにより、複数の検索ロジック(例:既存の全文検索と新規のベクトル検索)を公平に比較評価できます。 評価対象クエリの選定 評価に使用するクエリセットを選定します。弊社では実際のユーザー検索ログから代表的なクエリを抽出し、評価対象とするケースが多いです。 検索ロジックの実行 評価対象となる各検索ロジックで検索し、基本的には上位N件の検索結果を取得します。複数のロジックを比較する場合、同じクエリセットで公平に評価することが重要になります。 ラベリングMLLMによる関連性判定 取得した検索結果(クエリと商品のペア)に対して、ラベリングMLLMで関連性ラベルを自動付与します。 入力 :検索クエリ、商品情報(テキスト・画像) 出力 :Highly relevant / Acceptable substitute / Irrelevant 処理方式 :バッチ処理により大量のクエリと商品のペアを効率的にラベリング なお、検索結果の上位N件をラベリングする際の「N」の値は、評価の信頼性・網羅性、MLLMの処理時間・コストに大きく影響するので、よく検討しておくことが必要です。 評価指標の算出 ラベリング結果に基づいて、以下のような評価指標を算出できます。 nDCG@K(normalized Discounted Cumulative Gain) 検索結果の上位K件における関連性の質を評価する指標です。関連性ラベルに応じてスコアを付与します。例えば、Highly relevantは3点、Acceptable substituteは1点、Irrelevantは0点とします。上位の結果ほど重要とみなして順位による割引を適用し、さらにその値を理想的な順位の場合のスコアで割って正規化します。 この指標により、「関連性の高い商品が上位に表示されているか」を定量的に測定できます。 Precision@K Precision@Kは、検索結果の上位K件のうち、関連性があると判定された商品の割合を示す指標です。検索結果の「精度」を定量的に評価できます。 関連商品の定義については、Highly relevantのみを関連商品とする場合と、Highly relevantおよびAcceptable substituteの両方を関連商品とする場合があります。どちらを採用するかは、評価の目的や運用方針に応じて検討が必要です。 ロジック間の性能比較 上記の評価指標を用いて、複数の検索ロジックを比較評価します。例として、このMLLMによる検索ロジック候補群の評価を実施した結果を示します。 ロジック Precision@5 Precision@10 Precision@20 nDCG@5 nDCG@10 nDCG@20 ロジックA 0.8623 0.8507 0.8322 0.9318 0.9322 0.9313 ロジックB 0.8510 0.8389 0.8230 0.9214 0.9222 0.9227 ロジックC 0.8798 0.8696 0.8578 0.9424 0.9416 0.9413 ロジックD 0.8928 0.8846 0.8722 0.9419 0.9431 0.9421 この結果から、ロジックDが最も高いPrecision@Kを示しており、検索精度の向上が確認できます。 ラベリングMLLMを用いた評価手法の利点 バイアスの排除 従来の検索結果ログベースの評価では、既存ロジックの結果にバイアスがかかっていました。ラベリングMLLMを用いることで、ログに依存せず、クエリと商品の本質的な関連性に基づいて評価できるようになりました。 スケーラビリティ 人手によるラベリングと比較して、数千から数万件のクエリと商品のペアを短時間で評価できます。これにより、継続的な評価や複数ロジックの比較が現実的になります。 関連性の判定基準を改善する際に実施したペアラベリングでは、100個の検索クエリと商品のペアを人手でラベリングするのに2時間ぐらいの時間が掛かります。一方、MLLMであれば1分以内でラベリング可能になりました。 まとめ 本記事では、マルチモーダルLLMを用いたZOZOTOWN検索のオフライン定量評価の手法について紹介しました。 このラベリングMLLMを活用することで、検索結果ログに依存せず、クエリと商品の本質的な関連性に基づいた公平な評価が可能になりました。今後は継続的な評価体制の構築を進め、より精度の高い検索システムの開発を推進していきます。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
アバター
.table-of-contents > li > ul > li > ul > li, .table-of-contents > li:nth-child(2) { display: none; } はじめに こんにちは、ZOZOMO部FBZブロックの杉田です。普段は Fulfillment by ZOZO が提供するAPIシステムを開発・運用しています。昨年からは、社内における開発者向けAI支援ツール(Claude、Devin、MCPなど)の導入・教育・推進・管理を担う専門チームでも兼務で活動しています。 本記事では、開発ガイドライン準拠チェックをClaude Code Plugins × Atlassian MCPで全社展開した取り組みを紹介します。手作業の確認コストを下げつつ、最新ガイドラインに基づいたレビューを日常的に回せるようにした経緯と、実装・運用のポイントをまとめます。 目次 はじめに 目次 背景・課題 開発ガイドラインについて 開発ガイドラインにまつわる課題 Claude Code Pluginsとは Claude Code Pluginsを採用した理由 実施内容 開発ガイドラインの参照 プラグインの作成 plugin.json guideline-review.md プラグインの登録 プラグインの利用方法 チーム展開時の設定 プラグインの実行結果 関連ガイドラインの特定 適合状況のサマリー 詳細レポート 今後の展望 まとめ 背景・課題 開発ガイドラインについて ZOZOでは、サービス開発における「良い実装」「具体的な実装方法」「実装の目安」を明確にするため、開発ガイドラインをConfluence上で整備・管理しています。開発のガードレールとして機能し、品質の均一化と属人化の防止を目的としています。 執筆時点で、開発ガイドラインは以下のプラットフォーム・領域ごとに整備されています。 API Backend DB ML Infra Frontend iOS Android Q&A Batch Go Java Flutter 共通 各項目には RFC2119 に基づく要請レベル(MUST、RECOMMENDED、MAYなど)が設定され、「必ず従うべき項目」と「推奨事項」が明確に区別されています。また、開発ガイドラインの内容は定期的に見直され、継続的に更新されます。 開発ガイドラインの詳細については以下の記事で解説されていますので、あわせてご参照ください。 techblog.zozo.com 開発ガイドラインにまつわる課題 開発ガイドラインは整備されているものの、実際の運用では次の課題がありました。 人手による確認工数 : プラットフォーム・領域ごとに数十の項目以上のチェックが必要で、レビューの負担が大きい 継続的なチェック体制の不在 : リリース前の目視確認が中心で、更新内容の追従が難しい チーム間のばらつき : 観点の揺れが生まれ、統一した品質基準でのチェックが難しい こうした課題を解消するため、開発ガイドライン準拠チェックを自動化・標準化する仕組みづくりに取り組みました。 Claude Code Pluginsとは Claude Code Plugins は、Claude Codeの機能をチームや組織内部で再利用しやすい形にまとめる拡張機構です。公式ドキュメントに沿って、AgentsやSkillsなどをプラグイン単位で管理・共有できます。 プラグインに含められる代表的な要素は以下の通りです。 構成要素 説明 Skills 1 Claude Codeの機能を拡張する指示セット Agents 特定タスクに特化したサブエージェント Hooks ツール実行前後に介入するフック処理 さらに、プラグインごとに .mcp.json を配置することで、MCPサーバーと連携できます。外部サービス(Confluenceなど)の情報をプラグイン内から参照できる点が大きな特徴です。 プラグインは .claude-plugin/plugin.json を起点として定義され、Skillsは /plugin-name:skill-name のような名前空間付きで提供できます。そのため、開発者が複数のプラグインを導入しても衝突が発生しづらく、チームや組織内での再利用に適した形となります。 Claude Code Pluginsを採用した理由 当初は、開発ガイドラインの内容をGitHubリポジトリに保存して参照する方式を検討していました。具体的には、Confluence上で管理されている開発ガイドラインをMarkdown化し、任意のGitHubリポジトリ配下で管理し参照する方式です。しかし最終的に、Claude Code Plugins × Atlassian MCPの構成に方針を変更しました。 理由は以下の4点です。 1. 情報の鮮度を常に保てる MCPを経由してConfluenceを直接参照することで、実行時点で常に最新の開発ガイドラインを参照できます。 2. 二重管理によるメンテナンスコストを避けられる GitHubリポジトリ配下で管理する場合、Confluenceとの同期が必要になります。MCPで直接参照する方式により、二重管理を回避できました。 3. マーケットプレイス機能による全社配布の容易さ Claude Code Pluginsのマーケットプレイス機能により、各プロダクトチームへの配布が容易になりました。共通のレビュー基準をコマンド1つで導入できます。 4. プラグインの自動更新による最新化の容易さ Claude Code Pluginsはマーケットプレイスとインストール済みプラグインを起動時に自動更新する機能を備えています。また、 /plugin marketplace update marketplace-name コマンドで手動更新も可能です。これにより、プラグインの改善や機能追加を迅速に開発者に展開できます。 実施内容 開発ガイドラインの参照 Confluenceに格納されている開発ガイドラインを参照するため、 Atlassian MCP を活用しました。Atlassian MCPはリモートMCPとして提供されています。ローカルにMCPサーバーを起動せずとも、Atlassianがホストするサーバーに直接接続してConfluenceやJiraのデータにアクセスできます。 プラグイン側で .mcp.json を以下のように設定し、読み取り専用モードでConfluenceにアクセスします。 { " mcpServers ": { " atlassian ": { " command ": " npx ", " args ": [ " -y ", " mcp-remote ", " https://mcp.atlassian.com/v1/mcp " ] , " env ": { " READ_ONLY_MODE ": " true " } } } } ポイントは以下の通りです。 リモートMCP : mcp-remote を使用してAtlassian MCPサーバーに接続 READ_ONLY_MODE : 書き込み操作を無効化し、安全にConfluenceを参照できる 認証 : 初回接続時にブラウザでOAuth 2.1ベースの認証が行われ、以降は自動的に認証情報が管理される プラグインの作成 本セクションで紹介するプラグイン構成は、説明用に簡略化したサンプルです。Claude Code Pluginsの最新の機能や仕様については 公式ドキュメント をご参照ください。 開発ガイドラインレビュー用プラグインは、以下のようなディレクトリ構成で作成しました。 plugins/guideline-review/ ├── .claude-plugin/ │ └── plugin.json # プラグイン設定 ├── .mcp.json # MCP Atlassian統合設定 ├── commands/ │ └── guideline-review.md # ガイドラインレビューコマンド定義 └── README.md # プラグイン詳細ドキュメント plugin.json プラグイン本体の設定ファイルです。プラグインの名前や説明、利用するコマンドを定義します。 { " name ": " guideline-review ", " version ": " 1.0.0 ", " description ": " 開発ガイドラインの準拠状況をレビューするプラグイン ", " commands ": [ " commands/guideline-review.md " ] } guideline-review.md コマンドの振る舞いをMarkdown形式で定義します。ファイル先頭のフロントマターには、 /help で表示されるコマンドの説明( description )や使用可能なツール( allowed-tools )などのメタ情報を記述します。 記述可能なメタ情報の詳細は以下をご参照ください。 code.claude.com フロントマター以降の本文部分がプロンプトとして実行されます。 --- description: 開発ガイドラインの準拠状況をレビューする allowed-tools: - mcp __ atlassian _ _* - Task --- # 開発ガイドラインレビュー 現在のプロジェクトが開発ガイドラインに準拠しているかをチェックします。 ## 手順 1. Atlassian MCPを使用して、Confluenceから開発ガイドラインを取得 2. 関連するプラットフォーム・領域の開発ガイドラインに対して、サブエージェントで並列してチェックを実行 3. 各項目について「適合」「非適合」「確認不可」を判定 4. 非適合の場合は具体的な指摘箇所と改善案を記載 このコマンド定義のポイントは以下の通りです。 allowed-tools : 使用可能なMCPツールを制限(Atlassianへのアクセスとサブエージェント起動のみ許可) サブエージェントによる並列チェック : 開発ガイドラインは種類も多く、各開発ガイドラインの項目も数十に及ぶため、コンテキストウィンドウの消費を抑える観点からもサブエージェントを活用 構造化された出力 : 適合・非適合の判定と詳細を明確に記載 プラグインの登録 作成したプラグインを全社で利用できるようにするため、マーケットプレイスとして登録します。リポジトリのルートに .claude-plugin/marketplace.json を配置します。 { " name ": " xxxxx-marketplace ", " plugins ": [ { " name ": " guideline-review ", " description ": " 開発ガイドラインの準拠状況をレビューします ", " version ": " 1.0.0 ", " source ": " ./plugins/guideline-review " } ] } このリポジトリを公開し、利用者側で extraKnownMarketplaces に追加します。 .claude/settings.json に以下を追加します。 { " extraKnownMarketplaces ": { " guideline-review ": { " source ": { " source ": " github ", " repo ": " your-org/claude-plugins " } } } } プラグインの利用方法 設定が完了したら、以下のコマンドでプラグインをインストールして利用できます。 # マーケットプレイスの追加 /plugin marketplace add your-org/claude-code-plugins-name # プラグインのインストール /plugin install guideline-review-plugin # 開発ガイドラインレビューの実行(プラグイン名:コマンド名の形式) /guideline-review-plugin:guideline-review チーム展開時の設定 チーム全体でプラグインを利用する場合、プロジェクトの .claude/settings.json に許可設定を追加します。 { " plugins ": { " allowed ": [ " guideline-review-plugin " ] } } プラグインの実行結果 コマンドを実行すると、以下のような流れで処理が行われます。 関連ガイドラインの特定 プロジェクトの技術スタックに基づき、関連する開発ガイドラインが自動的に特定されます。以下は、プラグインを実行した結果の抜粋です。 ⏺ 配下のページを確認しました。 このリポジトリ(Python + AWS Lambda + Serverless Framework + DynamoDB)に関連するガイドラインを特定します 関連するガイドライン: 1. 開発ガイドライン - API - APIシステムのため 2. 開発ガイドライン - 共通 - 全プロジェクト共通 3. 開発ガイドライン - Backend - バックエンドシステムのため 4. 開発ガイドライン - Batch - バッチ処理を含むため 5. 開発ガイドライン - Infra - AWS Lambda等のインフラ関連 6. 開発ガイドライン - DB(共通) - DynamoDB使用のため 適合状況のサマリー 確認すべき開発ガイドラインが分かった後は、対象の開発ガイドラインに対するチェックが行われ、結果が以下のようなサマリー形式で出力されます。 状態 件数 項目番号 ✅ 適合 46 1-4, 10-12, 18, 20-23, 26-47, 49-54, 58 ⚠️ 非適合 5 7, 8, 19, 24, 25 ❓ 確認不可/対象外 7 9, 13-17, 48, 55-57 このサマリーはAPIガイドラインに対する適合状況の例です。適合・非適合・確認不可の判定が一覧化されています。 詳細レポート サマリーに続き、各ガイドライン項目の詳細なチェック結果が出力されます。以下、JSON命名規約に関するレポートの抜粋です。 # 項目 必須度 状態 詳細・根拠 18 プロパティ名はASCIIスネークケース MUST ✅ 適合 order_key, member_id等全て準拠 19 enumの値はUPPER_SNAKE_CASE形式 MUST ⚠️ 非適合 new_arrival, soldout等が小文字 20 Booleanのプロパティにnullを使わない MUST ✅ 適合 boolean型にnullable設定なし 21 Booleanのプロパティ名は命名規約に従う RECOMMENDED ✅ 適合 is_main, can_return等準拠 24 日時プロパティ名に_atサフィックス MUST ⚠️ 非適合 order_date等_date使用 25 日付プロパティ名に_onサフィックス MUST ⚠️ 非適合 日付専用プロパティも_date使用 26 日時型・日付型の値はISO 8601に準拠 MUST ✅ 適合 format: date-timeで指定 各項目に対して具体的なコード箇所や根拠が示されます。また、非適合項目については項目別の具体的な修正案も提示されるため、それを参考に修正計画を立てることができます。 今後の展望 本取り組みを通じて、開発ガイドライン準拠チェックの自動化の基盤ができました。今後は以下の拡張を検討しています。 プラグインの拡充 : 開発ガイドラインのレビュー以外にも、コードレビュー支援やテスト生成などのプラグインを追加し、開発フロー全体をカバーする Skills/Agentsの活用 : 任意のタイミングでのチェックだけでなく、開発中も常に開発ガイドラインに準拠した実装が担保される仕組みを展開していく まとめ 本記事では、開発ガイドライン準拠チェックをClaude Code Plugins × Atlassian MCPで全社展開した取り組みを紹介しました。Claude Code Pluginsは、組織固有のワークフローをパッケージ化して容易に配布・メンテナンスできる仕組みです。今回ご紹介した開発ガイドラインレビュー以外にも、さまざまな用途に応用できる可能性があります。また、Claude Codeは継続的にアップデートされているため、最新バージョンに追従することで新機能や改善の恩恵を最大限に受けることができます。AI駆動の開発プロセス標準化に取り組む方の参考になれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com Claude Code v2.1.3 でCommandsとSkillsが統合されました。本記事の執筆時点ではリリース前だったため、一部サンプルコードはCommands形式で記載していますが、挙動に変わりはありません。 ↩
アバター
.table-of-contents > li > ul, .table-of-contents > li:nth-child(2) { display: none; } はじめに こんにちは、ZOZOTOWN開発本部リプレイスバックエンドブロックのばやです。普段はZOZOTOWN BFFのリプレイス開発を担当しています。 システムリプレイスのプロジェクトでは、実装に入る前段階として 既存コードの調査 が必ず発生します。特にレガシーシステムの場合、ドキュメントが整備されていなかったり、仕様が暗黙知として埋もれていたりすることが多く、コードを読み解くことでしか仕様を把握できないケースも少なくありません。 一方で、この調査フェーズは成果物の形式や進め方が属人化しやすく、プロジェクト全体の生産性に大きな影響を与えるポイントでもあります。調査に時間がかかればプロジェクト全体のスケジュールに影響しますし、調査品質が低ければ後工程での手戻りにつながります。 本記事では、リプレイスにおける既存コード調査の課題に対し、調査業務をテンプレート化しその後、 Claude CodeでAgent Skillsに落とし込むことで効率化した取り組み をご紹介します。 目次 はじめに 目次 背景 リプレイスにおける既存コード調査の重要性 調査フェーズが抱えていた課題 人による改善:既存コード調査業務の標準化 調査対象コードのコピーによるスコープ明確化 調査テンプレートの設計 テンプレート化だけでは解決できなかった課題 経験者と未経験者の工数差 調査結果の粒度とレビュー工数のバランス AIによる改善 Agent Skillsとは なぜAgent Skillsを選んだのか 「Slash Command + Skills」を採用した理由 常時コンテキストを圧迫することを防ぐ Anthropic公式のSkills作成のSkillsが存在した 専門知識を一般化された状態で格納できる Agent Skillsの実装 ディレクトリ構成 実行フロー この構成にした理由 1. LSPがないレガシー言語への対応 2. コンテキストが膨大な処理の並列化 3. 中間ドキュメントによる精度向上 各Skillの役割 成果物 運用フロー 導入効果 定量的な効果 定性的な効果 見えてきた課題 コンテキストの欠落 ハルシネーションの見落とし 今後の展望 検証プロセスの強化 より人間がわかりやすいドキュメントを再作成する 新規実装やテストコードと旧コードのマッピング Agent Skillsの継続的な改善 他チームへの展開 まとめ 背景 リプレイスにおける既存コード調査の重要性 リプレイスでは、既存システムの仕様や振る舞いを正しく理解することが不可欠です。 特に以下のような情報は、後続の設計・実装の品質を大きく左右します。 実際に使われている機能・使われていない機能 暗黙的な仕様や例外的な処理 データ構造や依存関係 パフォーマンスや運用上の注意点 そのため、既存コード調査は「とりあえず読む」ではなく、 一定の品質を担保した成果物として残す必要 があります。 成果物として残すことで、レビューしプロジェクト全体で品質を担保できます。 調査フェーズが抱えていた課題 一方で、従来の調査フェーズには次のような課題がありました。 調査結果の形式がメンバーごとに異なる どこまで調査すれば十分なのか判断基準が曖昧 調査内容の抜け漏れがレビューまで発覚しない キャッチアップコストが高く、経験者と未経験者で工数差が大きい タスクの見積もりが人によって大きくブレる 人による改善:既存コード調査業務の標準化 これらの課題を解決するために、まず着手したのは 既存コード調査業務の標準化 です。具体的には、以下の2つを行いました。 調査対象コードのコピーによるスコープ明確化 調査対象のコードを、元のリポジトリから現開発環境のリポジトリにコピーします。 単純ではありますが、確認するコードを限定することで作業者とレビュアの双方にとって範囲が明確になり、作業効率が向上しました。 当時はAgent Skillsの導入を意識していたわけではありませんでしたが、結果的にこの運用がAgent Skillsへの移行をスムーズにしました。対象コードが分析用ディレクトリにまとまっていたことで、Agent Skillsが旧コードベースを読み込んで解析できる状態が既に整っていました。 調査テンプレートの設計 私たちのチームでは、BFFを開発する上で最低限必要となる情報の調査に特化したテンプレートを使用しています。 ## API名 ## 概要シーケンス ## リクエスト例 ## リクエストパラメータおよび変換処理 ## コード内での呼び出しAPI / 呼び出しテーブル ## エラーレスポンス ## レスポンス例 ## レスポンスパラメータおよび変換処理 テンプレートにより「何を調べるべきか」「何を書けばよいか」が明確になり、調査の抜け漏れを防げるようになりました。また、成果物の形式が統一されたことで、レビューもしやすくなりました。 テンプレート化だけでは解決できなかった課題 テンプレート化により「何を書けばよいか」は明確になりましたが、以下の課題は依然として残っていました。 経験者と未経験者の工数差 レガシーコードに慣れている経験者は効率的に調査を進められますが、未経験者は同じテンプレートを埋めるのにも時間がかかってしまいます。テンプレートは「何を書くか」を示してくれますが、「どうやって調べるか」までは教えてくれません。 調査結果の粒度とレビュー工数のバランス レビュアの既存コード調査レビューはそれなりに工数を伴います。リプレイスする機能は多岐に渡り、1つの機能がどのような機能かを詳細に把握することは大変です。 一方で一機能のリリースにおける、最序盤のタスクである既存調査に対してプロジェクトとしてはあまり工数をかけたくなく、既存調査の情報粒度は2〜3日程度で完成できる程度としていました。 必然的にレビュアの負担をかけることで、品質を担保するようにしていました。 これにより、レビューが重なったタイミングなどはレビュー遅延による待ちなどが発生して、プロジェクト全体の遅延が懸念されました。 これらの課題を解決するために、次にAgent Skillsの導入を検討しました。 AIによる改善 Agent Skillsとは Agent Skills は、Claudeの機能を拡張するモジュール型の機能です。指示、メタデータ、およびオプションのリソース(スクリプト、テンプレートなど)をパッケージ化し、Claudeが関連するタスクで自動的に使用できるようにします。 Agent Skillsを使う主な利点は以下の3点です。 専門知識を注入し、暗黙知を削減 : 既存知識をドキュメントに移譲することで、暗黙知となっていた業務知識を公開ドキュメントとできる 繰り返しを削減する : 一度作成すれば、複数の会話で同じガイダンスを繰り返す必要がなくなる 機能を組み合わせる : 複数のSkillsを組み合わせて複雑なワークフローを構築できる Agent Skillsは SKILL.md ファイルとして定義します。YAMLフロントマターでメタデータを記述し、本文に具体的な指示を記載します。Claude Codeでは、このファイルを .claude/skills/ ディレクトリに配置することで、Claudeが自動的に検出して使用します。 なぜAgent Skillsを選んだのか Claude Codeには、カスタマイズのための複数の仕組みがあります。今回の用途に適した方式を選ぶため、それぞれの特徴を比較しました。 方式 特徴 用途 CLAUDE.md 起動時に常に読み込まれる 常に必要な情報 Skills 必要に応じて読み込まれる 特定の作業タイミングでのみ必要な指示 Slash Commands ユーザーが明示的に呼び出す 定期的に実行したいタスクのショートカット Subagents 独立したタスクを別エージェントに委譲 調査過程が膨大になる場合の結果のみ取得 現状Agentの呼び出し方法には複数の選択肢があり、チームでは Slash Commands + Skills の組み合わせ を採用しました。 エントリーポイント : Slash Command 業務知識の保存 : Skills(詳細な調査手順とテンプレート) Slash Commandをエントリーポイントとしてのみの薄い層とし、実際の調査テンプレートや調査方法はSkillsに格納しました 1 。 「Slash Command + Skills」を採用した理由 常時コンテキストを圧迫することを防ぐ CLAUDE.mdへの記載は、常時コンテキストを圧迫することになるので避けました。 Anthropic公式のSkills作成のSkillsが存在した 我々のチームでは、CLAUDE.mdなどを読み込ませる際に、どのような文章構成がよりAgentの性能を上げてくれるのかが手探りの状態でした。 導入当時、Skillsは公式が作り方をサポートしてくれるSkillsを作成していました 2 。こちらに沿って作成すればスムーズにSkillsを作成でき、最適な形であることが保証できました。 Anthropic公式の マーケットプレイス も参照ください。 専門知識を一般化された状態で格納できる 作成した既存調査のSkillsは更新・改善を見込んでいます。このように階層に分けたドキュメント配置が公式から指定されていることで、我流のドキュメント配置を抑制できます。一般化された階層を誰もが実現でき、可読性が高い状態を維持できます。 なお、当時はSkillsを明示的に呼び出すことができなかったため、呼び出し部分だけSlash Commandで作成しました。結果的にSkills側に専門知識を寄せたことにより、現状のSlash CommandとSkillsの統合への変更が最小限で済みました。 Slash Command内で並列化することにより、いくつかのSkillsはSubagent的な役割を実現させています。これは個人的な意見にはなりますが、SubagentもいずれSkillsに統合され、Skillsを同期的に呼ぶか非同期で呼ぶかという観点のみが残るような気がしています。 Agent Skillsの実装 調査テンプレートをAgent Skillsとして定義しました。複数のSkillsを組み合わせ、以下の流れで調査を自動化しています。 ディレクトリ構成 {plugin-name}/ ├── commands/ # エントリーポイント │ └── analyze.md # 分析実行コマンド ├── skills/ # Skills本体(詳細な手順) │ ├── find-dependencies/ # 依存関係調査 │ ├── analyze-code/ # コード詳細分析 │ ├── extract-files/ # ファイル抽出 │ └── generate-readme/ # ドキュメント生成 │ └── templates/ # 出力テンプレート └── agents/ # Subagents定義(並列実行用) 実行フロー 1. /analyze を実行(Slash Command) ↓ 2. find-dependencies(依存関係調査) ↓ 3. 並列実行: ├─ analyze-code(コード詳細分析) └─ extract-files(ファイル抽出) ↓ 4. generate-readme(ドキュメント生成) ↓ 5. 成果物出力 この構成にした理由 1. LSPがないレガシー言語への対応 旧コードにはLSP(Language Server Protocol)が存在しないため、「定義へジャンプ」や「参照検索」といった機能が使えません。関数の検索方法や依存関係の追跡方法をドメイン知識としてSkillsに注入する必要がありました。 find-dependencies を最初に実行することで、後続の処理に必要な情報を確実に収集します。 2. コンテキストが膨大な処理の並列化 従来の手作業では、既存コードのコピーとテンプレートへの情報抽出を順番に行っていました。どちらもコードベース全体を読み込む必要があり、コンテキストが膨大になります。これらを analyze-code と extract-files として分離し、並列実行することで処理時間を短縮しています。 3. 中間ドキュメントによる精度向上 依存関係の調査結果を analyze-code で中間ドキュメント(markdown)として出力し、後続のSkillsが参照する構成にしています。一度にすべてを処理するのではなく、段階を踏むことでAIの出力精度が向上します。 各Skillの役割 Skill 役割 find-dependencies 関数検索、依存関数の追跡、グローバル変数の特定 analyze-code 入力パラメータ、出力データ、データアクセス、変換処理を抽出。旧フローのテンプレート化に相当 extract-files メイン関数、依存関数を個別ファイルに分離。旧フローのコードコピーに相当 generate-readme テンプレートに準拠した調査結果ドキュメントを生成。旧フローのテンプレート化に相当 成果物 実行後、以下の成果物が出力されます。 output/{対象関数名}/ ├── README.md # 調査結果(テンプレート準拠) ├── 01.{メイン関数}.ext # メイン関数 ├── 02.{依存関数1}.ext # 依存関数 └── ... 調査結果のREADME.mdは、先述のテンプレートに沿った形式で出力されます。依存関数も個別ファイルとして抽出されるため、レビュー時にコード全体を把握しやすくなります。 運用フロー この仕組みにより、調査担当者は以下の流れで作業できるようになりました。 1. 対象リポジトリやディレクトリを指定 2. Agent Skillsを実行 3. 出力された調査結果を確認・修正 4. レビューを実施 5. 必要に応じて追加調査 従来は「コードを読む → 理解する → まとめる」という一連の作業をすべて人間が行っていました。Agent Skillsを活用することで「AIが下書きを作成 → 人間が確認・修正」という流れに変わりました。 導入効果 定量的な効果 Agent Skillsの導入により、調査作業のリードタイムは大きく改善しました。 項目 従来 導入後 調査の実行時間 数時間〜数日 数分程度 全体のリードタイム 2〜5日 数時間 成果物の形式 バラバラ 統一 調査結果の確認・修正・レビューを含めても 数時間で完結 するようになりました。従来の「数日かかる調査タスク」と比較すると、大幅な短縮です。 また、レビュー体験も改善しました。成果物の形式が統一されているため、レビュアは「何がどこに書いてあるか」を把握しやすくなり、内容の精査へ集中できるようになりました。 特にリードタイムの短縮は顕著で、これまでボトルネックになっていた調査フェーズがスムーズに進むようになりました。 定性的な効果 定量的な効果に加えて、以下のような定性的な効果も得られました。 メンバー間で調査観点が揃い、レビューがしやすくなった 情報粒度が上がったので、細かい内容まで記載があって、レビューがしやすくなった 新しく参加したメンバーでも、同じ品質で調査できるようになった 特に、新規メンバーのキャッチアップコストが下がったことは大きな効果でした。従来は既存システムの知識がないと調査に時間がかかっていましたが、Agent Skillsを使うことで、知識の有無に関わらず一定品質の調査が可能になりました。 見えてきた課題 Agent Skillsの導入により多くの効果が得られた一方で、新たな課題も見えてきました。 コンテキストの欠落 従来は時間をかけて調査することで、各メンバーが暗黙的な知識を蓄積していました。「なぜこのような実装になっているのか」「過去にどのような経緯があったのか」といった背景知識は、コードを読み込む過程で自然と身についていました。 しかし、AIによる高速な調査では「調べた結果」は得られるものの、「理解」を伴わないケースもあります。具体的には以下のような問題に直面しました。 実装フェーズで想定外の考慮漏れが発生する 全体像を把握していないことによる手戻り 調査結果に書いてあることしか把握していない状態 この課題に対しては、調査結果を「読むだけ」で終わらせず、チーム内で共有・議論する時間を設けることが重要だと感じています。 AIが作成した調査結果をベースに、チームで同期的な理解の時間を設けることで後のフェーズである、設計・実装の部分でスムーズな進行ができると感じました。 ハルシネーションの見落とし AIが大量の情報を要約して出力するため、全項目を精査することが難しくなります。結果として、誤った情報がそのままレビューを通過してしまうリスクがあります。 これはLLMを使っている以上避けては通れないリスクではありますが、現状、根本的な解決策は見つかっていません。 レビュイー・レビュア双方による注意深い確認が重要だとされがちです。しかし、それでは業務負荷が作成からレビューに移行しただけで、AI駆動開発の恩恵を受けられません。 今後の展望 見えてきた課題を踏まえ、今後は以下の取り組みを進めていく予定です。 検証プロセスの強化 AIの出力をより効率的に検証するため、現在以下の取り組みを行っています。 より人間がわかりやすいドキュメントを再作成する 既存調査 → 新規設計 → 実装という形でフェーズが進行しており、レビュアが見やすい粒度・構成の設計書を模索しながら作成しています。 新規実装やテストコードと旧コードのマッピング レビュイー本人も漏れを防ぐため、リプレイス後のコードに旧コードのGitHub上での該当箇所URLをコメントすることで擬似チェックリストのようなものを再現しています。愚直ではありますが、これによって実装者が漏れを把握して未然にバグを防げたので、有効性は確認できています。 今後は以下の仕組みも検討しています。 調査結果と実際のコードを自動で照合するスクリプトの作成 重要度の高い項目(データ構造、エラーハンドリングなど)のチェックリスト化 実装フェーズでの差分検出と調査結果へのフィードバックループ Agent Skillsの継続的な改善 プロンプトの精度向上や、新たな観点の追加を継続的に行います。 調査対象のコードパターンに応じたプロンプトの分岐 よくある誤りパターンの収集と、それを防ぐための指示の追加 チームからのフィードバックに基づくテンプレートの更新 他チームへの展開 現在は特定のチームで運用していますが、他のリプレイスプロジェクトへの展開も視野に入れています。 Agent Skillsの汎用化と、プロジェクト固有の設定の分離 導入ガイドやベストプラクティスの整備 成功事例・失敗事例のナレッジ蓄積 まとめ 本記事では、リプレイスにおける既存コード調査の課題に対し、以下のアプローチで業務を効率化した事例を紹介しました。 調査業務をテンプレート化し、「何を調べるか」「何を書くか」を明確にする その手順をAgent Skillsに落とし込み、AIによる調査の自動化を実現する 結果として、調査タスクのリードタイムを大幅に短縮しつつ、品質の安定化も実現できました。 一方で、「コンテキストの欠落」や「ハルシネーションの見落とし」といった新たな課題も見えてきました。これらは、AIを活用した業務効率化において避けて通れない課題であり、運用の工夫や検証プロセスの改善によって対処していく必要があります。 リプレイスでは「調査」は避けて通れない工程だからこそ、人に依存しない形で品質とスピードを両立させることが重要だと考えています。AIの活用はその有力な手段の1つですが、AIの出力を鵜呑みにせず、人間による検証とチームでの知識共有を組み合わせることが成功の鍵です。 既存コード調査やリプレイスに課題を感じている方の参考になれば幸いです。 ZOZOTOWN開発本部リプレイスバックエンドブロックでは、大規模なシステムリプレイスを推進するエンジニアを募集しています。AIを活用した開発プロセスの改善や、レガシーシステムのリプレイスに興味のある方、ぜひご応募ください。 corp.zozo.com 2026/01/21現在、SkillsとSlash Commandは統合され、Skillsを明示的に呼び出せるようになりました。こちらの分割は現在不要となりましたが、実行役割を持つSkillsと知識をもつSkillsの分割はパターンとして未だに有効だと考えています。 ↩ Anthropicの skill-creator というSkillsを参考にしてください。 ↩
アバター
こんにちは、技術戦略部のikkouです。2025年12月16日に「 AI Engineering Summit Tokyo 2025 」が開催されました。ZOZOはGoldスポンサーとして協賛し、スポンサーブースを出展しました。 AI Engineering Summit Tokyo 2025 会場エントランスのスポンサーパネル technote.zozo.com 本記事ではZOZOから登壇したセッションの紹介と、ZOZOの協賛ブースの様子をまとめてお伝えします! セッションレポート ファッション×AI:「似合う」を届けるためのWEARのAI戦略 登壇中のブランドソリューション開発本部 本部長の増田 ブランドソリューション開発本部 本部長の増田が登壇し、ZOZOが「似合う」というテーマに取り組む背景から、それをWEARでどのように実現しているのかまでを紹介しました。 WEARにおけるAI活用の考え方や戦略について、具体的な事例を交えながら語られたセッションとなりました。登壇資料はSpeaker Deckで公開されています。 speakerdeck.com 見逃した方は、Findy Conferenceのアーカイブ動画もぜひご覧ください。 conference.findy-code.io セッション後のAsk the Speakerもとても盛り上がっていました。会場にお越しいただいた方、オンラインでご視聴いただいた方、ありがとうございました。 エンジニア組織として全社のAI活用をどうリードしていくのか パネルディスカッションに登壇中の執行役員 兼 CTOの瀬尾 続いて、執行役員 兼 CTOの瀬尾( @sonots )が「AI時代の開発組織の未来像 ― 5年後の姿をどう考えるか?」をテーマとしたパネルディスカッションに登壇しました。 本セッションは会場限定での実施となりましたが、アーカイブ動画が公開されています。このトピックに関心のある方は、ぜひご視聴ください。 conference.findy-code.io ZOZOブースレポート ZOZOの協賛ブースでは、当日登壇したセッション資料を含むAI関連の取り組み資料をスライドショー形式で紹介しました。 特に、瀬尾がパネルディスカッション内で言及していた「ZOZOグループで利用可能な代表的なAIツール」に関心を持たれる方が多く、ブースでも活発な交流が行われていました。 ZOZOグループで利用可能な代表的AIツール(2025年12月現在) ブース内では、「似合う」に関する取り組みを紹介するパネル展示も行いました。 ZOZOの独自性を生み出す「似合う4大要素」の開発サイクル 『ZOZOの独自性を生み出す「似合う4大要素」の開発サイクル』を解説する冨田 speakerdeck.com データサイエンス部 コーディネートサイエンスブロックの冨田が、ZOZOの独自性を支える「似合う4大要素」の開発サイクルについて解説しました。進行中のプロジェクトのため公開できる内容には限りがありましたが、プロジェクトに深く関わっている立場から、一人ひとりに丁寧な説明が行われました。 ファッションコーディネートアプリ「WEAR」におけるコーディネートレコメンドの動作インフラとその周辺技術 『ファッションコーディネートアプリ「WEAR」におけるコーディネートレコメンドの動作インフラとその周辺技術』を説明する岡本 ファッションコーディネートアプリ「WEAR」におけるコーディネートレコメンドの動作インフラとその周辺技術 もうひとつの展示では、ファッションコーディネートアプリ「WEAR」におけるコーディネートレコメンドの仕組みと、その周辺技術を紹介しました。データシステム部 MLOpsブロックの岡本が、関連するコーディネート情報を活用した推薦システムのアーキテクチャを、MLOpsの観点から解説しました。 前月に協賛してブースを出展したアーキテクチャConference 2025 でも感じたことですが、アーキテクチャやシステム構成に関心を持つ方は多く、こちらの展示も大きな盛り上がりを見せていました。 おわりに ZOZOから参加したメンバーの一部で記念撮影! 今回の協賛・出展を通して、改めてAIの活用や開発に対する関心の高まりを強く感じました。ZOZOのAIに対する考え方や取り組みが、少しでも来場者の皆さまに伝わっていれば嬉しいです。 ZOZOでは、今回ご紹介したWEARのエンジニアをはじめ、さまざまな分野でエンジニアを募集しています。ご興味のある方は、ぜひ採用ページをご覧ください。 corp.zozo.com
アバター
.table-of-contents > li:nth-child(2) { display: none; } はじめに こんにちは、2025年にiOSエンジニアとして新卒入社したZOZOTOWN開発1部iOSブロックの だーはま です。普段はZOZOTOWNのiOSアプリを開発しています。本記事では、新卒1年目の私がZOZOTOWNの画面へMVVM+UseCaseアーキテクチャを導入した過程と、工夫を紹介していきます。 目次 はじめに 目次 背景と目的 チーム配属から1か月でMVVM+UseCaseアーキテクチャ導入を担当した経緯 デバッグ画面について 課題 コードやドメインに対する知識不足 700行以上に及ぶFatViewController 課題を解決するための取り組み Claude CodeのSubagentsを使いキャッチアップを高速化 MVVM+UseCaseアーキテクチャ導入 結果 デバッグ画面のテストカバレッジ0→93.5% UI実装をStoryboardからSwiftUIへ完全移行 開発工数40%減少 MVVM+UseCaseアーキテクチャ導入を振り返って 最後に 背景と目的 チーム配属から1か月でMVVM+UseCaseアーキテクチャ導入を担当した経緯 ZOZOTOWN iOSでは、MVVMとUseCaseで構成されるチーム標準のアーキテクチャがあります(以後アーキテクチャという表記はチーム標準のアーキテクチャを指します)。現在、アプリ全体の保守性と開発効率を向上させるため、 チーム全体でこのアーキテクチャへの統一を進めるプロジェクトが進行しています 。 私自身、学生時代から設計への関心は高く、個人開発で同様の構成を取り入れた経験はありました。しかし、ZOZOTOWNのような歴史ある大規模なアプリの画面で、UIからビジネスロジックまでを含めて構成を見直した経験はありません。また、当時は入社して間もなく、経験したことのあるタスクはUIの修正やログ送信といった特定箇所の改修のみで、 ZOZOTOWNの大規模なコードの全体像も把握できていない状態 でした。 このような状況ではありましたが、iOSチームには以下のように アーキテクチャ導入を支える手厚いフォロー体制 1 が整っていました。 アーキテクチャの指針書が整備されている 各レイヤーの責務が言語化されている レイヤーごとにコードを使った実装例がまとめられている 設計段階でのレビューが義務化されており、実装後の手戻りを抑えられる これらの環境に背中を押され、アーキテクチャ導入がチームへの貢献と自己成長へ繋がると考えました。そのため、チームへ配属されて1か月目というタイミングではありましたが、 自ら手を挙げタスクオーナーとしてアーキテクチャ導入を進める ことになりました。 取り組む目的は以下の通りです。 技術的負債を抱えた画面のメンテナンスコストを下げること 疎結合でテスタブルなコードにする ZOZOTOWN iOSが採用しているアーキテクチャに慣れること ZOZOTOWNのiOSチームが採用しているアーキテクチャへの理解を深め、他画面のコードリーディングを高速化する テストコードの実装にも慣れるため、可能な限りテストを実装する 大規模なコードを高速にキャッチアップするための練習をする AIと協働して大規模なコードを高速にキャッチアップする方法を模索する 上記の目的を満たす画面をチーム内で議論し、社内向けでありユーザー影響のないデバッグ画面を選びました。 デバッグ画面について デバッグ画面は、ZOZOTOWNのデバッグ用にデータを編集する画面です。開発している案件に応じてサーバーの向き先を切り替えるなど、開発する上で便利な機能が複数用意されています。デバッグ画面を使うことでQAや案件ごとに実施される動作確認をスムーズに行えます。このデバッグ画面はiOSエンジニアだけでなく、QAチームやバックエンドを始めとした社内の様々なチームおよびメンバーが触れるため、 開発する上でとても重要な画面 です。 しかし、新規案件の実装を優先しておこなっていく中で、社内向けでありプロダクトコードではないことから保守に手をつけることができずにいました。その結果、UIからビジネスロジックまでの実装が1つのViewControllerに記述され、機能を追加するたびにメンテナンスコストが増大していました。 課題 アーキテクチャ導入にあたり解決すべき課題は以下の2つです。 コードやドメインに対する知識不足 700行以上に及ぶFatViewController コードやドメインに対する知識不足 先述したようにデバッグ画面は依存関係が複雑です。 また、導入にあたり以下のような課題がありました。どの課題にも共通して「キャッチアップするべき項目が多く、十分にできていなかった(本人は十分にできているつもりだった)」ということが挙げられます。 触れたことのないアーキテクチャの理解 学生時代に個人開発で触れていたアーキテクチャ(MVVM,TCAなど)とは思想やルール、実装する上での責任が異なるため、チームが採用しているアーキテクチャを理解して慣れるまで時間がかかる 既存実装の仕様を確認する難しさ プロパティの初期値やデータソースへのアクセス、データを読み書きするタイミングなど、既存実装には多くの仕様が存在しており、それらすべてを確認して実装を進めるのが困難 テストコード実装時に発覚した「隠れた密結合」 設計のレビューを通過し各レイヤーの責務を定義したはずが、いざ実装を進めるとデータソースの抽象化ができておらずユニットテストを書けない状態で手戻りが発生する 700行以上に及ぶFatViewController 既存の実装では1つのViewController内に画面の状態からデータソースへのアクセスまで全てのコードが記述されていました。責務分離がなされていないためクラスをDIできず、ユニットテストの記述が困難な状況です。また、StoryboardでUIを開発していたためメンテナンスコストが高くなっていました。実際にメンバーからは「デバッグ用のフラグを1つ追加したいだけなのに、Storyboardの修正や依存関係のあるコードの把握に時間がかかってしまう」という声が上がっていました。本来は開発を効率化するためのツールであるはずのデバッグ画面が、機能追加のたびに負債が溜まっていく画面になっていたのです。 課題を解決するための取り組み 前述した課題を解消するため2点取り組みました。 Claude CodeのSubagentsを使いキャッチアップを高速化 MVVM+UseCaseアーキテクチャ導入 Claude CodeのSubagentsを使いキャッチアップを高速化 1つ目の課題で挙げたコードやドメインに対する知識不足を解決するため、コードの分析からプランニングまでをサポートするClaude CodeのSubagentsを3つ作成しました。Subagentsで分析することで、自力で読み解くよりも高速なドメイン知識の獲得が可能です。また、分析結果をもとにドキュメントを作成してもらうことで、レビューの説明文にも活かせます。 作成したSubagentsと役割は以下の通りです。 spec-refactorer 既存コードを解析し、現状のロジックや仕様を整理 ios-architecture-engineer アーキテクチャ導入に向けた技術的な調査と、依存関係の整理 refact-planner 調査結果を元に、実装の優先順位や具体的な手順のプランニングを提案 3つのプロンプトを載せることは記事の都合上できないため、 spec-refactorer のみを紹介します。 spec-refactorer のプロンプトは以下の通りです。 ## 出力フォーマット に記載されているような画面の構成やデータフローなどをまとめます。 // 重要な部分のみ抜粋 ## 目的 * 画面を開いてからの **データフロー**(依存解決→API→変換→State更新→描画)と、ユーザー操作(例: **Aというコンポーネントをタップ**)時の **処理・I/O・状態/遷移** を一目で把握できるようにする。 ## 出力フォーマット 1. TL;DR(初回ロードと主要アクションのI/Oを箇条書き) 2. 画面の構成(UI/State/Lifecycleの表) 3. データフロー(画面表示→初回ロードのシーケンス図) 4. UIコンポーネント×アクション×副作用の対応表(Cell/ボタン/トグル/Pull-to-Refresh/ページネーション/セル内ボタン含む) 5. 主アクション毎のシーケンス図(最低A=主ボタン, Refresh, セル選択) 6. ネットワークI/O一覧(Method/Path/Auth/キャッシュ/失敗時/呼出根拠) 7. 状態管理(公開State, アクション(必ず網羅すること。アクションを過不足なく網羅できるかがUXに直結する), 非同期/キャンセル) 8. DI(依存解決の流れ)/ナビゲーション 9. エラー/ローディング/空状態 10. イベントログ送信箇所(ない場合は無しと記載) 11. 分析イベント/フラグ 12. リスク・改善 13. Reference ## 探索範囲 * **関連するファイルは可能な限り探索・調査** する(ViewModel/Reducer/Repository/API/Router/DI/テスト/拡張/ユーティリティを横断参照)。 ## 網羅してほしい操作例(該当するもののみ) * 画面表示(初回ロード) * 主要ボタン(例: AddToCart/Favorite/Buy)タップ * セル選択(詳細遷移) * Pull-to-Refresh / ページネーション * 失敗時のエラーハンドリング ## 出力スタイル * 判断根拠となるコードは丁寧に過不足なく提示してください。 * 技術構成を図に描く場合は、左側にUI層で右に行くほどDomain,Data層になるようにしてください。 * 出力は日本語でお願いします。 以下の点が整理されるようにSubagentsを設計しました。これによってハルシネーションや考慮漏れを抑えた出力を得られるようになります。 判断根拠となるコードをドキュメントの最後に記載させる ユーザーアクションを軸にしてデータフローを整理させる 見落としがちなポイントも確認させる エラーやローディング時の挙動 ログ送信の有無 外部へ公開しているプロパティ(画面の状態)は何か MVVM+UseCaseアーキテクチャ導入 2つ目の課題で挙げられていた「Fat」な実装を解消するため、MVVM+UseCaseアーキテクチャを導入しました。MVVM+UseCaseアーキテクチャは Android Architecture Components を参考にしたアーキテクチャであり、以下のように役割を分担させます。 View / ViewController UIレイアウトや画面遷移 ViewModel イベントハンドリングやViewに最適化したデータ整形などのプレゼンテーションロジック UseCase キャッシュ管理などの特定のViewに依存しないビジネスロジックをカプセルし、原則として状態を持たない Translator DataSourceで取得したデータをUseCaseで扱える型へ変換することで、UseCaseにAPI等の知識が入り込むことを防ぐ DataSource データソース(APIやUserDefaultsなど)へアクセス ViewModel、UseCase、DataSourceはprotocolで抽象化しDIを可能にしました。これにより、UseCase、DataSourceのモックを作ることで、依存関係のあるクラスのテストを書けるようになります。 アーキテクチャ導入によりプレゼンテーションとドメインが疎結合となり、UI層のコードの変更が容易になったため、SwiftUIへの移行がスムーズに行えました。 結果 取り組みによって得られた結果は以下の通りです。 デバッグ画面のテストカバレッジ0→93.5% UI実装をStoryboardからSwiftUIへ完全移行 開発工数40%減少 デバッグ画面のテストカバレッジ0→93.5% 2つ目の課題で挙げたように既存実装は密結合が原因でテストを書けませんでしたが、ViewModel、UseCase、Translatorに対してユニットテストを書けるようになりました。 カバレッジを計測するとデバッグ画面に関連するテストの平均が93.5%でした。残りの6.5%はテスト不要なコードが対象となっているため、実質100%となります。 UI実装をStoryboardからSwiftUIへ完全移行 画面の実装をStoryboardからSwiftUIへ完全に移行させました。既存実装ではカスタムコンポーネントを使っていました。しかし、これらはOSのアップデートに伴うデザインシステムの変更や仕様変更の影響を受けやすく、その都度レイアウトの調整や挙動の修正が必要になるため、メンテナンスコストが増大していました。このメンテナンスコストを抑えるため、カスタムコンポーネントをSwiftUI化に合わせて廃止し、 Apple標準のコンポーネントのみ で画面を構成しました。 開発工数40%減少 既存実装に合わせたViewModelのリファクタリングや網羅的なユニットテスト実装など、Claude Codeに実装をサポートしてもらいました。 結果、Claude Code導入前に(先輩社員と相談し)見積もっていた 工数35日が、Claude Codeを使うと20日で完了 しました。約40%の工数削減となります。また、コードを書く時間が減り設計や実装方針を考える時間が増えたためClaude Codeなしに比べきれいなコードを書けました。 MVVM+UseCaseアーキテクチャ導入を振り返って 振り返ると、序盤は以下のような不安を抱えていました。 デバッグ画面のキャッチアップに時間がかかりすぎて、他画面のキャッチアップに時間を使えないのではないか 既存仕様を確認できておらずリグレッションを発生させてしまうのではないか (今後アサインされるであろう)他案件と折り合いがつかず中途半端に終わらせてしまうのではないか 冒頭でもお話ししたように、これらの不安の根本は「広い範囲のキャッチアップをうまく行えない」ことです。原因に序盤で気づき、Claude CodeのSubagentsを使ったキャッチアップ方法を模索するようになりました。初めは欲しい情報を得られずプロンプトを何度もチューニングしていましたが、今では開発に活きる情報(画面の状態やデータフローなど)が得られるようになっています。このように試行錯誤しながら作り上げたSubagentsのおかげでスピード感を持ってキャッチアップを進められました。また、Subagentsに調査結果をドキュメント化してもらうことで、ドキュメント生成のスピードも格段に上がりました。 これらの実績をまとめ、2025年10月には社内の全エンジニアを対象とした技術共有会で紹介しました。紹介をきっかけにiOSチーム以外の方にも活動を認知してもらい、「デバッグ画面を使いやすくなって作業が楽になりました!」のようなポジティブなフィードバックを多くいただきました。嬉しかったです。 アーキテクチャ導入を担当させてくれてサポートまでしてくれた上長やチームメンバーに感謝でいっぱいです。 最後に 本記事では、新卒1年目の私がAIを使ってデバッグ画面にアーキテクチャを導入した際の取り組みを紹介しました。現在は、その経験を活かして、より難易度の高い別画面への導入を進めています。試行錯誤の日々ですが、今回作成したSubagentsのおかげでコードリーディングの負担は格段に減り、開発スピードも上がったように感じています。本記事が、私と同じくキャッチアップの速度に課題を感じている方に届き、開発の生産性向上に少しでも貢献できれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com アーキテクチャの設計およびレビュー体制は「 ZOZOTOWN iOSアプリでのFatViewController解消への取り組み 」をご覧ください ↩
アバター
.table-of-contents > li > ul > li > ul > li { display: none; } はじめに こんにちは、データシステム部MA推薦ブロックの佐藤( @rayuron )です。私たちは、主にZOZOTOWNのメール配信のパーソナライズなど、マーケティングオートメーションに関するレコメンドシステムを開発・運用しています。本記事では、 GitHub Projects 、 BigQuery 、 Looker Studio を組み合わせて作業工数を可視化し、改善サイクルを回すための仕組みを構築した取り組みについてご紹介します。 はじめに 背景と課題 1. ボトルネックの特定に手間がかかり改善に着手しにくい 2. 工数に対する事業価値を把握できていない 3. AI活用の効果測定とナレッジの蓄積ができていない 解決策 1. GitHub Projectsの運用整備 カスタムフィールドの作成 自動入力の仕組み 2. データ収集の自動化 BigQueryへのデータ保存 GitHub Actionsで日次エクスポート 3. ダッシュボードと振り返り会の運用 週次振り返り 施策完了時の振り返り 半期振り返り 工数入力 効果 課題に対する効果 1. ボトルネックの可視化と改善 2. 工数と事業価値の関連付け 3. AI活用の効果測定とナレッジ蓄積 副次的な効果 今後の展望 フィールド入力の自動化 施策の優先度決めでの活用 AI活用のさらなる促進 おわりに 背景と課題 MA推薦ブロックはこれまでGitHub Projectsでタスクを管理してきましたが、担当者やリポジトリごとに記入フォーマットが異なり、運用ルールが統一されていませんでした。そのためGitHub Projectsによる進捗確認はできるものの、工数の集計や振り返りなどに活かせていませんでした。具体的には以下の3つの課題がありました。 1. ボトルネックの特定に手間がかかり改善に着手しにくい プロジェクトを進める中で「なんとなく時間がかかった」という感覚はあっても、どの工程に工数が多くかかっているのか、予想と実際の工数の差が大きい箇所はどこなのかを把握できていませんでした。また、当時の状況やドキュメントがSlack、Confluence、GitHubに点在しており、ログが散逸していたためボトルネックの特定に時間がかかり改善に着手しにくい状況でした。 2. 工数に対する事業価値を把握できていない プロジェクトの事業価値や投資対効果を把握するために必要となる、過去のプロジェクト工数と事業KPIの変化を紐付けて記録できていませんでした。 3. AI活用の効果測定とナレッジの蓄積ができていない あるタスクに対してどのようなAI活用が効果的だったかというナレッジを蓄積・共有する仕組みがありませんでした。そのため人によっては効率的にAIを活用している一方で活用が進んでいないメンバーもおり、チーム全体でのAI活用の波及が進んでいない状況でした。 解決策 これらの課題に対して以下の解決策を考えました。 課題 解決策 1 ボトルネックの特定に手間がかかり改善に着手しにくい 施策の工数予実を可視化し振り返ることでボトルネックを特定し改善する仕組みを作る 2 工数に対する事業価値を把握できていない プロジェクトごとの工数と事業KPIの変化を関連付けて振り返る仕組みを作る 3 AI活用の効果測定とナレッジの蓄積ができていない タスクごとにAI活用のナレッジを蓄積・共有する仕組みを作る 上記の解決策を実現するため、GitHub Projects + BigQuery + Looker Studioを組み合わせた仕組みと、振り返り会を導入しました。 1. GitHub Projectsの運用整備 まず初めに、タスクに紐づく工数予実などのデータを取得する必要がありましたが、これには引き続きGitHub Projectsを活用することにしました。GitHub Projectsではカスタムフィールドを活用することで、タスクに任意のメタデータを付与できます。さらにGitHub Actionsとの連携により、様々なオートメーションの構築も容易です。社内では既に Findy Team+ が導入されており主にPRベースで定量的な工数集計と可視化が可能ですが、PRに紐づかないタスクの工数集計や拡張性の観点で最終的にGitHub Projectsを選択しました。 カスタムフィールドの作成 GitHub Projectsにカスタムフィールドを設定しIssueに紐付けます。先述の工数やAI活用度合いを計測するため、以下のカスタムフィールドを用意しました。 フィールド名 種類 説明 入力 案件 単一選択 どの案件に紐づくタスクかを選択 必須 作業内容 単一選択 どの工程のタスクかを選択 必須 予定開始日 日付 タスクの開始予定日 必須 予定完了日 日付 タスクの完了予定日 必須 開始日 日付 タスクの実際の開始日 必須 完了日 日付 タスクの実際の完了日 必須 AI活用 単一選択 AI活用方法を記録 任意 AI削減時間 数値 AI活用による削減時間 任意 工数予実を記録するために開始予定日と完了予定日、開始日と完了日の入力を必須としています。また、作業内容と工数の紐付けを行うため、案件と作業内容の入力も必須としました。さらに、Issueのテンプレート機能を活用し、概要と完了条件を必須項目として設定し最低限のコンテキストがIssueの中で整理されるようにしました。 自動入力の仕組み 上記のように多くのフィールドを用意したことで手動での運用負荷が増加します。これを軽減するためGitHub Projectsに紐づくステータスやIssueに紐づくイベントと GitHub Actions を連携させることで解決を図っています。具体的には以下の自動入力の仕組みを作成しています。 Issueをオープンした時に、オープンした人をassigneeに自動設定する Issueのステータスを「進行中」に変更すると「開始日」が自動入力される Issueをクローズすると「完了日」が自動入力される Issueのステータス変更時にin-progress, in-reviewなどのラベルが自動付与される Issueに紐づくPRでレビューリクエストをするとIssueのassigneeとステータスが自動更新される Issueに紐づくPRをApproveするとIssueのassigneeとステータスが自動更新される 上記に加え、Issue作成のための Claude Code の Skills を作成して運用しています。Skillsでは、 GitHub CLI とClaude CodeのAskUserQuestion Toolを組み合わせています。Issueの作成や編集を選択式で行えるため、コンテキストの整理や運用負荷の軽減につながっています。 2. データ収集の自動化 上記で紹介したGitHub Projectsの情報は GitHub GraphQL API を通して取得可能なため、BigQueryに自動的に保存する仕組みを作成しました。 BigQueryへのデータ保存 まず初めにGitHub GraphQL APIを使用して、GitHub Projectsのデータを取得するスクリプトを作成しました。GitHub Projectsから取得したデータは社内でよく使用するBigQueryに以下のようなカラム構成で保存しています。この時、簡単な集計や営業日計算もスクリプト内で実装しています。 カラム名 説明 url IssueのURL title Issueのタイトル body Issueの本文 comments Issueのコメント assignees 担当者 labels Issueのラベル in_review_label_duration_days レビュー中ラベルが付与されている日数 in_progress_label_duration_days 進行中ラベルが付与されている日数 review_started_at_jst レビュー開始日時 review_ended_at_jst レビュー終了日時 progress_started_at_jst 進行開始日時 progress_ended_at_jst 進行終了日時 created_at Issue作成日時 closed_at Issueクローズ日時 github_project GitHub Project名 status Issueのステータス project 案件名 planned_start_date 予定開始日 planned_end_date 予定完了日 actual_start_date 実際の開始日 actual_end_date 実際の完了日 work_type 作業内容 ai_success_failure AI活用方法を記録 ai_hours_reduction AI活用による削減時間 actual_days 実績日数 planned_days 予定日数 delay_days 予実差(日数) estimation_accuracy 見積もり精度(実績日数/予定日数) project_work_type 案件×作業内容の組み合わせ year_month 年月(YYYY-MM形式) FYH 会計年度と半期(例:FY24H1) GitHub Actionsで日次エクスポート 次に上記のスクリプトをGitHub Actionsで日次実行し、BigQueryにエクスポートする仕組みを構築しました。 name : Export GitHub Project to BigQuery on : schedule : - cron : '0 0 * * *' workflow_dispatch : jobs : export-to-bq : runs-on : ubuntu-latest permissions : contents : read id-token : write steps : - uses : actions/checkout@v4 - name : Install uv uses : astral-sh/setup-uv@v4 - name : Generate GitHub App Token id : generate_token uses : actions/create-github-app-token@v2 with : app-id : ${{ secrets.APP_ID }} private-key : ${{ secrets.APP_PRIVATE_KEY }} owner : your-org repositories : | target-repo-1 target-repo-2 - name : Authenticate to Google Cloud uses : google-github-actions/auth@v2 with : workload_identity_provider : ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} service_account : ${{ secrets.GCP_SERVICE_ACCOUNT }} - name : Set up Cloud SDK uses : google-github-actions/setup-gcloud@v2 - name : Export data to BigQuery env : GH_TOKEN : ${{ steps.generate_token.outputs.token }} GCP_PROJECT_ID : ${{ secrets.GCP_PROJECT_ID }} BQ_DATASET : github_aggregation BQ_TABLE : github_project_items GITHUB_ORG : your-org GITHUB_PROJECT_NUMBER : 123 run : uv run export_github_project_to_bigquery.py 3. ダッシュボードと振り返り会の運用 上記のようにして収集したデータをもとにLooker Studioでダッシュボードを作成し、以下の振り返り会で活用しています。 振り返り会 頻度 ダッシュボード 用途 週次振り返り 毎週20分 WBS、AI活用状況 タスク進捗確認、AI活用ナレッジ共有 施策完了時の振り返り 施策完了時 振り返り用 予実差の確認、ボトルネック特定 半期振り返り 半期ごと 半期振り返り用 プロジェクト横断での指標振り返り 工数入力 月初 工数管理システム入力用 工数割合の可視化 以下では、各振り返り会について説明します。 週次振り返り 週次振り返りでは毎週20分のミーティングで、WBSとAI活用状況を確認します。WBSはGitHub Projectsの予定開始日と予定完了日フィールドとRoadmap viewを活用して作成しています。 WBSを確認することで、メンバー同士がお互いのタスク進捗を把握でき、タスクが遅延している場合にその理由を知ることができます。さらに他メンバーの補助やタスクの分散を検討することで、遅延の早期解消につなげています。 次に、AI活用状況ダッシュボードを見て、AI活用の目標に対する進捗の確認とタスクごとのAI活用事例を共有します。その週最もAI活用が効果的だった事例をピックアップし、社内のピアボーナスアプリで表彰する運用をしています。 施策完了時の振り返り 施策が完了した際に、施策振り返り用ダッシュボードを参照して振り返りを行います。具体的には以下の指標を確認します。 作業内容別の予定日数と実績日数の比較 作業日数合計に対して各作業内容が占める割合 タスク一覧と予実差の確認 予定日数と実績日数の散布図による見積もり精度の確認 ※一部の情報はマスクしています。 上記のダッシュボードを参照しながら、以下の観点で振り返りを行います。 項目 説明 施策の効果 施策が事業KPIにどれだけ貢献したかを確認 開発工数 予定と実際の工数のずれを確認しボトルネックとなった工程を特定 具体的な原因の深掘りと次回の改善点を議論 施策KPT 継続すべき事、問題点、改善案を整理し、次回施策に向けたアクションを決定 半期振り返り 半期振り返り用ダッシュボードを使用して、半期の活動を振り返ります。施策振り返り用ダッシュボードが1案件単位での振り返りに特化しているのに対し、こちらはプロジェクト横断で同様の指標を振り返りできるようにしています。 上記のダッシュボードを参照しながら、以下の観点で振り返りを行います。 項目 説明 案件とタイムライン 半期に取り組んだ案件の一覧とスケジュールを振り返る 事業KPI 案件毎の事業KPIの変化を確認 事業KPI 以外での貢献 登壇、チームの仕組みづくりによる影響などKPI以外の価値を評価 コスト インフラコストの変化を把握 開発生産性 案件や作業内容の割合、予定日数と実績日数の差を分析 ブロックのテーマ チームとして注力したテーマを振り返る 半期KPT 継続すべき事、問題点、改善案を整理し、次期半期に向けたアクションを決定 工数入力 社内では工数管理システムへの工数登録が必要なため、入力支援用のダッシュボードを作成しています。案件×作業内容についての工数割合を円グラフで可視化しています。担当者と年月でフィルタリングをすると、各カテゴリの工数割合が一目でわかり入力の補助になります。 効果 これらの取り組みにより当初の課題が解決されただけでなく、副次的な効果も得られました。 課題に対する効果 1. ボトルネックの可視化と改善 施策振り返り時に、予定と実際の工数のずれや、工数がかかっている箇所が可視化されるようになりました。感覚的に捉えていたボトルネックをデータで把握できるようになりました。振り返り時にはIssueからその時のコンテキストを参照できるので、解像度の高い課題の特定と正確なネクストアクションの設定が可能です。これまで具体的に以下のようなアクションを設定できました。 要件の抜け漏れによる手戻りの発生に対して、テンプレートとなるドキュメントの更新 実装後にコストが想定外となり調整に時間がかかった課題に対して、コスト試算フローの追加 コードが複雑なので可読性が落ち理解に時間がかかったり、バグを埋め込み手戻りが発生したりするという課題に対して、コードの複雑度を自動計測する仕組みを導入 忘れかけていたボトルネックを振り返り会で再発見することもあり、改善に向けた具体的なアクションを設定できるようになりました。 2. 工数と事業価値の関連付け 施策振り返り時や半期の評価時に、施策ごとの工数と事業価値の関連を確認できるようになりました。特定の案件の工数とKPIの変化を紐付けて振り返ることで、投資対効果の高い施策を記録し説明できるようになりました。 3. AI活用の効果測定とナレッジ蓄積 AI活用フィールドにより、どのタスクでAIを活用したか、どの程度の工数削減につながったかを記録・可視化できるようになりました。IssueあたりのAI活用率は2025年4月の44%から12月には73%へと上昇しました。また、Issueの作成単位は作業内容により異なりますが、平均的な削減時間は分析・レポーティング(3.8h)、執筆・登壇業務(3.7h)、モデル開発(2.8h)、システム開発(2.7h)の順で大きい傾向があります。 さらに、AI活用事例がBigQueryに蓄積されているため、過去の成功パターンを簡単に参照できます。以下は実際に蓄積されたAI活用事例の一部です。 marimo のドキュメント調査をした。marimoはcurlで CLAUDE.md を取得可能で体験がよかった。分析手法の計画とクエリ作成、分析は全てClaude Codeに任せてその後自分で調整と確認をした Claude Codeを使ったドキュメントの推敲をした。目次と参考資料からドキュメントを生成してもらいそれを推敲した。画像はVS CodeのDraw.io Extensionでプレビューを確認しつつ、ローカル上でClaude Codeに作成してもらいそれを手動調整した 副次的な効果 当初の課題以外でも以下のような副次的な効果が得られました。 GitHub ProjectsとGoogle SpreadsheetでのWBSの二重管理が解消された SlackでのPRレビュー依頼がなくなり、必要のないコミュニケーションが減少した 過去のプロジェクトデータにBigQueryを通してアクセスできるようになり、深掘り分析が容易になった 週1の定例でプロジェクトの遅延を可視化でき、早期のフォローが可能になった AI活用の定量的な目標設定と振り返りが可能になった AI活用事例を他チームと共有しやすくなった 社内の工数管理システムへの入力が効率化された 今後の展望 フィールド入力の自動化 Issue作成には先述したオートメーションを活用しているものの、まだ運用負荷が高いためフィールド入力のさらなる自動化を検討しています。また、AI活用事例も現在は自己申告ベースのため、自動化や精緻な効果測定の仕組み作りを考える必要があります。 施策の優先度決めでの活用 工数と事業価値の関連付けと記録は可能になりましたが、それを用いた将来的な施策の優先度付けは感覚的な活用に留まっており十分ではありません。今後は、工数と事業価値の関連データを活用した施策の優先度決めの仕組み作りを進めていきたいと考えています。 AI活用のさらなる促進 また、Issueに「概要・完了条件」を構造化して記載する運用により GitHub Copilot やClaude CodeがIssueを読み取ってPRを作成するワークフローとの相性も良くなっています。今後はAIによるタスク自動達成ケースの分析・評価と仕組み作りを進め、AI活用のさらなる促進を目指します。 おわりに 本記事では、GitHub Projects、BigQuery、Looker Studioを組み合わせて作業工数を可視化し、改善サイクルを回すための仕組みを構築した取り組みについてご紹介しました。本仕組みにより、ボトルネックの可視化と改善、工数と事業価値の関連付け、AI活用の効果測定とナレッジ蓄積が可能となりました。 ZOZOでは一緒にサービスを作り上げてくれる方を募集しています。ご興味がある方は以下のリンクからぜひご応募ください! corp.zozo.com
アバター
.table-of-contents > li > ul > li > ul { display: none } p + ul { margin-top: -10px !important } img[src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vasilyjp/20260114/20260114193736.png"] { width: 50% } img[src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vasilyjp/20260114/20260114223243.png"] { width: 40% } img[src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vasilyjp/20260118/20260118003257.jpg"] { width: 50% } .images-row { width: 100% !important } p.footnote { margin: 0 !important } こんにちは、XR × Fashion TechやXR × Beauty Tech領域の取り組みを推進している創造開発ブロックの @ikkou です。 2026年1月6日から9日の4日間にかけてラスベガスで開催された「 CES 2026 」に一般参加者として現地参加しました。個人としては7回目、ZOZO所属としては5回目の参加です。継続参加し、業界動向を定点観測しています。 CES 2025 現地レポート: XRとFashion Techの未来を探る CES 2024 参加レポート - コロナ禍以前の活況を取り戻した CES CES 2023 参加レポート - 3年ぶりの現地参加 CES 2020参加レポート: 現地参加3年目の目線で視た #CES2020 前半はCESの概要と関連する情報のアップデートを、後半は特に私が注目したトピックについてお伝えします。 CES 2026全体のトレンドについては、会期中に配布される「CES Daily」(デジタル版)の Day 1 / Day 2 / Day 3 などもあわせてご覧ください。 CES 2026の概況 CES 2026のメインテーマは「Innovators Show Up」 CES 2026の6つのトレンドと49のカテゴリー CES 2026におけるメガトレンド 出展社数と参加者数の推移 CES 参加バッジの価格 会場の概要 LVCC Campus Venetian Campus その他の会場 会場間の移動 Vegas Loop Zoox 注目のトピック:XR Tech XR関連企業の出展動向 出展ブースのXRデバイスから見るトレンド 中国企業の大攻勢 中国系の眼鏡型デバイスを一手に引き受けるMeta-Bounds 見かける機会の減ったHMD型デバイス 日本発企業が示したXRデバイス向け要素技術 Play For Dream Cellid Even Realities Meta 注目のトピック:Fashion TechとBeauty Tech Mode Wearablesの「ModeX Bomber Jacket」 デジタルカラーチェンジネイルの「iPolish」 カスタム3Dプリントシューズの「Fitasy」 ReFaの「ReFa AI color recipe PRO」 花王の「my Symmetry & THE CORE」 Perfectの「YouCam AI API」 注目のトピック:その他 NVIDIA Samsung Razer Robot, Robot and Robot... おわりに CES 2026の概況 今年のCESは「人が戻った」だけでなく、“会場の重心”がLVCC中心から周辺会場に少し移動したように感じました。 大手企業の出展の仕方が変わり、LVCCの景色が去年と違う 一方で来場者は増え、移動のボトルネックは相変わらない このあと前半で全体像、後半でXR Tech、Fashion Tech、Beauty Tech目線の“刺さった展示”をまとめます。 数年来の工事が完了して綺麗に整備されたLas Vegas Convention Center CES は CTA (Consumer Technology Association)が主催する、毎年1月にラスベガスで開催される世界最大級のテックイベントです。公式サイトでは「 The Most Powerful Tech Event in the World 」と表現されています。「セス」と呼ぶ方もいますが、正しくは「 シーイーエス 」です。CESはかつて『Consumer Electronics Show』と呼ばれていましたが、現在この表記は使用されていません。現在は「Consumer Electronics Show」の表記は前面に出されず、CESとしてブランド化されています。 CES 2026のメインテーマは「Innovators Show Up」 メインテーマが掲げられているVenetian 2Fのサイネージ CES 2024の「All ON」、CES 2025の「DIVE IN」に続くCES 2026のメインテーマは「 Innovators Show Up 」でした。 *1 "CES is where innovators show up, business accelerates, partnerships ignite, and technology transforms real-world challenges into bold opportunities" CTA CEOのGary Shapiro氏は上記のように、CESはイノベーターが集う場であることを強調しています。 CES 2026の6つのトレンドと49のカテゴリー CESには毎年CTAの打ち出すトレンドがあり、2026年のトップトレンドとして、次の6つが挙げられています。 *2 AI Digital Health Energy Enterprise Mobility Robotics 「AI」「Digital Health」「Mobility」は昨年に引き続きトップトレンドとして挙げられています。一方、昨年のトップトレンドであった「Energy Transition」「Quantum」「Sustainability」は外れ、新たに「Energy」と「Enterprise」が加わりました。 トップトレンドとは別に、次に挙げる49の技術領域が公式カテゴリーとして設定されています。 Accessibility, Accessories, Additive Manufacturing & 3D Printing, AgTech, Artificial Intelligence, Audio, Beauty Tech , Biotech, Blockchain & Digital Assets, Cloud Computing, Computing, Construction & Industrial Tech, Content & Entertainment, Cybersecurity, Digital Health, Drones, Education Tech, Energy Transition, Enterprise, Fashion Tech , Fintech, Fitness , Food Tech, Gaming & Esports , Home Entertainment & Office Hardware, Imaging, Investment & Venture Capital, IoT/Sensors, Lifestyle, Longevity, Marketing & Advertising, Next G, Pet & Animal Tech, Quantum, Retail/E-Commerce , Robotics, Smart Communities, Smart Home & Appliances, Sourcing & Manufacturing, Space Tech, Sports, Startups, Streaming, Supply & Logistics, Sustainability, Travel & Tourism, Vehicle Tech & Advanced Mobility, Video & Display, XR & Spatial Computing 私が特に注目しているカテゴリーは太字で示しています。CES 2025では「AR/VR/XR」がカテゴリーとして設けられていましたが、今年は「 XR & Spatial Computing 」と名称が変更されました。同様にいくつかのカテゴリー名が変更された一方で、「Cryptocurrency」「Metaverse」「NFT」などは外れています(それらのカテゴリーの出展が完全になくなったわけではありません)。 CES 2026におけるメガトレンド 先に発表された6つのトップトレンドとは別に、CTAはトレンド予測セッション「 Trends to Watch 」で2026年のメガトレンドとして次の3つを挙げました。 Intelligent Transformation Longevity Engineering Tomorrow なかでも「 Longevity (ロンジェビティ)」、つまり「長寿、健康」に関するAgeTech、LongevityセクターはCES 2026の会場内でも多く見かけました。健康寿命を延ばすことに関心が高まっていることを示しています。 このセッションはメディア向けセッションのため現地では聴講していませんが、 アーカイブ動画 と PDF資料 が公開されています。CTAの謳うテックトレンドに少しでも興味がある方は、一度は目を通すことをおすすめします。 出展社数と参加者数の推移 出展社数は過去最高タイだったCES 2025の4,500社以上から4,100社以上に減少しましたが、参加者数はコロナ禍以降では過去最高の148,000人以上となりました。2018〜2026年の出展社数と参加者数の推移は以下の通りです。 年度 出展社数 参加者数 CES 2018 3,900社以上 182,198人 *3 CES 2019 4,500社以上 175,212人 *4 CES 2020 約4,500社 171,268人 *5 CES 2021 約2,000社 約80,000人 CES 2022 2,300社以上 44,000人以上 CES 2023 3,200社以上 117,841人 *6 CES 2024 4,300社以上 138,789人 *7 CES 2025 4,500社以上 142,465人 *8 CES 2026 4,100社以上 148,000人以上 CESの参加者数は各年のATTENDANCE AUDIT SUMMARYに1桁単位で精密な数字が公表されています(コロナ禍の影響で完全オンライン化された2021年とハイブリッドで開催された2022年は公式発表なし)。これは参加バッジの発行数をもとにした、重複なしの参加者数です。CES 2026の正確な参加者数は後日公開される見込みです。 CES 参加バッジの価格 CESへの参加には参加登録が必要です。メディアやインフルエンサーではない一般参加者は有償で参加バッジを得られます。 参加バッジは紛失時に350 USDの手数料がかかるので要注意 CES 2026の参加登録は昨年同様9月11日に開始され、通常のチケットであるExhibits Plus Passの価格は同12月1日までの早期登録が149 USD、以降は350 USDでした。私は昨年に続き過去回の参加者特典により無料でした。 参加バッジは空港で受け取るとスムーズ 過去に一度だけCES公式のセッションを聴講するためのカンファレンスプログラミングパスを追加しましたが、以下の観点から今年も追加せずにブースのみを巡りました。 カンファレンスに参加するとブースを巡るための時間を削減せざるを得ない 多くのセッション動画は後日アーカイブ動画として無償で公開される セッション以外の動画も含まれていますが、実際に CES 2026関連の公式アーカイブ動画は500本以上が公開 されています。 会場の概要 Image source: https://www.ces.tech/explore-ces/maps-and-locations/ CESの展示会場は昨年同様、 LVCC Campus ・ Venetian Campus ・ C Space Campus の3つで構成されています。 LVCC Campus 大きく様変わりしたLVCC Centralの内部 LVCC CampusはLVCCことLas Vegas Convention and World Trade CenterのWest Hall・North Hall・Central Hall・South Hallを中心に構成されていて、CESのメイン会場とも言えます。特にCentral Hallは大規模工事を経て外観・内観が大きく変わりました。CES 2025以前の参加者はその変化に驚いたはずです。 GoogleはCVSやSphereでGeminiやAndroid XRをアピールしていた 今年は、CESの初回となる1967年から58年間連続で出展してきたSonyをはじめとして、Google、Samsungなどの大手企業がLVCCでの出展を取り止めています。もっともSonyはSony Honda Mobilityとしてブースを構え、販売を控えるAFEELAを大々的にアピールしていました。参加バッジのストラップもSonyではなくSony Honda Mobilityでした。また、Googleは会場外でGeminiやAndroid XRをアピールし、SamsungはWynnに独自のブースを構えていました。 昨年に続きWestgateは会場として利用されず、 CES Foundry や後述するNVIDIA Live、NVIDIA Showcaseの会場として、2023年12月にオープンしたラスベガスで最高層の Fontainebleau Las Vegas が追加されました。 Venetian Campus Venetian Expoの2Fから1Fを俯瞰する Venetian ExpoはCESの1か月前に開催されるAWS re:Inventでもお馴染みのVenetianを会場として、スタートアップ企業の集まるEureka Parkが設けられています。 Eureka Parkで勢いを増しているKorean Tech Image source: https://exhibitors.ces.tech/8_0/floorplan/?st=country&hallID=M&sv=South%20Korea このEureka Parkには、スタートアップ企業による個別のブースとは別に、特定の国が1カテゴリーとして集まっているという特徴があります。これまではフランス発のLa French Techが非常に目立っていましたが、近年は韓国発のKorean Techが勢いを増しています。 JETROが支援するJapan Pavilionとクリエイティヴ・ヴィジョンが推進するJAPAN TECH 日本発としてはJETROが支援する「 Japan Pavilion 」から31社、クリエイティヴ・ヴィジョンが推進する「 JAPAN TECH 」から37社がEureka Parkにブースを構えていました。 その他の会場 C Space Campusは主にマーケター向けの会場で、CESの出展社や参加者がビジネスミーティングするためのスペースが提供されています。 その他、前述の通りSamsungやMetaはCESのプログラムとは別に、Wynnに独自の会場を設けていました。ホテルの一室を利用してプライベートミーティングの場を設ける企業もよく見聞きします。 一般の来場者向けの会期は4日間ありますが、1人ですべての会場・すべてのブースを巡るのは非現実的です。私は今回、到着日をDay 0として、Day 3まで滞在し、以下の行程で会場を巡りました。 Day 0:NVIDIA Live Day 1:LVCC Campus Day 2:Venetian Campus Day 3:周りきれなかったLVCC CampusとVenetian Campus 会期中はとにかく歩き回りました。歩きやすい履き慣れた靴の準備が欠かせません。今年はOnのスニーカーを履いて臨みました。事前に訪れるブースを決めてルートを最適化して効率的に巡っているとはいえ、4日間の平均歩数は昨年同様に毎日20,000歩を超えていました。 会場間の移動 会場間の移動には時間がかかります。例年同様、会場間の移動には徒歩とVegas Loop、そしてライドシェアを利用しました。徒歩とライドシェアについての特筆すべきアップデートはありません。LVCCとVenetianの移動に使えるラスベガスモノレールや2階建てバスのDEUCE(デュース)、通常のタクシーも選択肢として挙げられますが、私はこれらを利用しませんでした。 日中は非常に混雑して会場間の移動には使えませんでしたが、Amazonの子会社であるZooxが2025年9月からラスベガス市内で提供している自動運転車による特定スポット間の移動も利用できました。 Vegas Loop LVCC Central Underground Station 開通以降、毎回お世話になっている Vegas Loop ですが、今年も何度か利用しました。Vegas Loopはイーロン・マスク氏率いるThe Boring Companyが運営するLVCCの会場間を繋ぐ地下トンネルです。徒歩ではそれなりに時間のかかるルートを、Teslaで効率よく結ぶ交通システムです。 Vegas Loop Map Station Map(2026年1月20日現在) Image source: https://www.lvcva.com/vegas-loop/ CES参加者向けの路線は、LVCCのWest Hall・Central Hall・South Hall間を結ぶ無償ラインと、LVCCからホテルを結ぶ有償ラインがあります。この有償ラインは、昨年のCES 2025時点では RESORTS WORLD線 しかありませんでしたが、CES 2025閉幕直後の2025年1月18日に WESTGATE RESORT線 が開通、そして2025年4月8日に Encore線 が開通しました。 運良く乗車できたVegas Loopを走るCybertruck 今回はLVCCからWynnへの移動時に一度だけ有償のEncore線に乗車しました。Vegas Loopを走っている車両はTesla Model S、3、X、そしてYの4種類ですが、時々Cybertruckが走っている姿を見かけていました。この機会に運良く乗車できれば、と考えていたところ、見事このEncore線でCybertruckに偶然にも乗車できました。 Vegas Loopのチケット購入ページ Image source: https://lvloop.com/tickets このVegas LoopはLVCC周辺だけではなく、将来的にはラスベガスの大通りを行き交うルートが計画されています。CES 2026の期間中も10:00から21:00までの時間限定ではあるものの、Resorts World Stationからハリー・リード国際空港へ向かう空港線が運行していました。今回は都合が合わず乗車できませんでしたが、片道12 USDとライドシェアよりも安価なので、次回ラスベガス入りする際には試してみたいところです。 Zoox ZooxのカウンターがあるResorts Worldのエントランス前乗り場 It’s not a car. を標榜する Zoox はラスベガスに登場した新しい「ロボタクシー」です。2020年にAmazonが買収し、現在は独立子会社として運営されていて、システムのバックエンドには AWS(EC2, S3, EKS, CloudWatch)が使われています 。 2026年1月現在は、誰でも無料乗車できる形でラスベガスを、ウェイトリストに登録する形でサンフランシスコをテスト走行しています。 AWS re:Invent期間中も多くの方が乗車レポートを投稿していましたが、CES 2026でも多くの方が乗車していました。今後はSXSWの開催地であるオースティン、そしてマイアミへの展開が 計画されています 。 ラスベガスのZoox走行エリア(2026年1月20日現在) Image source: Zoox Android App CES参加者の目線では、Vegas Loopが主にLVCCの会場間を繋いでいる一方で、Zooxはラスベガス市街のホテルやショッピングモールなどの施設を繋いでいます。ホテルからAREA15やTopgolf間の移動にも使えるので、市内観光の一環としても便利かもしれません。 Resorts WorldのZooxカウンター 私はVegas Loop StationもあるResorts WorldからZooxに乗りました。 Zooxは2025年5月にResorts Worldの公式ロボタクシーパートナーになっていて 、エントランスには有人のZooxカウンターが設けられています(記念撮影してもらえます)。 アプリで無人車両のドアを開ける直前の様子 Zooxの車両は完全に無人です。車両の予約はもちろん、ドアの開閉もすべてZooxアプリ経由で行います。このZooxアプリのインストールにはUSのアプリストアアカウントが必要です。JPリージョンのアプリストアアカウントではインストールできないので、Zooxに乗りたい場合は事前にアカウントの作成が必要です(もっとも開発者に属する多くの方は検証用途などで持っているとは思いますが)。 目的地に到着後、誰も乗せずに走り去っていくZoox 運転席もない自動運転車への乗車は初めての体験でしたが、特段の違和感を覚えることもなく、目的地までスムーズに到着しました。時期的に予約が集中していたため、私の利用時は30分以上待機しました。そのため日中の移動手段としては使いづらい場面もありますが、今後、車両台数や運用が拡充されれば、利便性はさらに向上すると考えられます。前述の通りMobilityはCES 2026のトップトレンドの1つです。特にラスベガスで開催されるテックイベントであるGoogle Cloud Next、re:Invent、そしてCESの参加者はぜひ乗車してみてください。 注目のトピック:XR Tech CESのカテゴリーのひとつに「 XR & Spatial Computing 」が設けられています。これは私が強い関心をもって継続的に追っている技術領域です。このパートでは、それらをXR Techとしてまとめてお伝えします。 XR関連企業の出展動向 CES 2026全体を通して「 XR & Spatial Computing 」カテゴリーの出展社数は 265 でした。CES 2025では「 AR/VR/XR 」カテゴリーの出展社数は 355 だったので、 昨年対比で90ブース減った ことになります。 LVCC Central Hallの会場地図 Image source: https://exhibitors.ces.tech/8_0/floorplan/?st=category&hallID=B 昨年同様、CES 2026では、LVCC Central Hallに「 GAMING | XR 」として分類された一画が設けられていました。 LVCC Central Hall内「GAMING | XR」エリアの会場地図 Image source: https://exhibitors.ces.tech/8_0/floorplan/?st=category&hallID=B このLVCC Centralの「GAMING | XR」エリアにブースを構える「 XR & Spatial Computing 」カテゴリーの出展社数は 32 でした。このエリアは例年通り、Insta360、XREAL、Pimax Technologyなどの中国企業が目立っていました。 また、CES 2024まではXR関連エリアの設けられていたスタートアップ企業が集まるVenetian CampusのEureka Parkですが、今年もそういったエリアは見当たりませんでした。 CESでは毎年、デザインやエンジニアリングにおいて優れた製品を表彰する CES Innovation Awards を実施しています。XR関連の製品は XR & Spatial Computing として分類され、計 12 製品が受賞しました。いわゆるHMD型・眼鏡型のXRデバイスは次の 5 製品です。 CES Innovation Awardsの会場とQualcommのブースに展示されていたGalaxy XR Galaxy XR Vuzix Ultralite Pro Enterprise Platform World's Lightest Full-Feature Colorful AR+AI Glasses World's Lightest Stylish AR Glasses XREAL Project Aura 奇しくも Android XR のHMD型デバイスである「 Galaxy XR 」と眼鏡型デバイスである「 XREAL Project Aura 」の両方が受賞していました。しかし、いずれもデモとして試せる状態になっている動態展示はありませんでした。 出展ブースのXRデバイスから見るトレンド CES 2026で体験したXRデバイス(HMD型・眼鏡型)の一部 例年通り、CES 2026でもたくさんのXRデバイスを試してきました。この写真はHMD型・眼鏡型のXRデバイスの一部です。 体感として CES 2024 では、まだHMD型のバリエーションを感じられましたが、 CES 2025 では、HMD型デバイスを抑えて一気に眼鏡型デバイスが増えました。そして今年のCES 2026ではさらにその傾向が強まり、とにかくたくさんの、それこそ食傷気味になるくらい多くの眼鏡型デバイスを見かけました。 この背景として以下の要因が考えられます。 世はまさに“大AI時代”であること 眼鏡型デバイスの先駆者である「XREAL」の成功 さらに後発にあたるMeta発の眼鏡型デバイス「Ray-Ban Meta」の大成功 中国企業の大攻勢 私自身は展示されていた眼鏡型デバイスのすべてを数えたわけではありませんが、 英香港紙SCMPのレポート によると、眼鏡系デバイスは約60社あり大半を中国ブランドが占めていたそうです。また、 中国メディア我爱音频のレポート 曰くAI機能を持つ主要な眼鏡型デバイスは36種あり、 中国メディア智东西のレポート 曰く27社の中国企業が「AIスマートグラス、VR、AR」製品を展示していたそうです。 中国は、国家戦略として6,000元(2026年1月20日現在で約13.6万円)以内のスマートグラスを対象に価格の15%、500元(同約1.1万円)を上限に 買い替え補助金の支給を公布 し、2026年1月1日から施行したと報じられています。今後、デバイスの「生産」に加えて一般消費者による「購入」もいっそう加速することが予測されます。 中国系の眼鏡型デバイスを一手に引き受けるMeta-Bounds 中国系の眼鏡型デバイスを一手に引き受けるMeta-Bounds EssilorLuxottica がRay-BanやOakleyをはじめとする 非常に多くのアイウェアブランド の眼鏡型デバイスを引き受ける一方で、「oppo」「ZTE」「ThinkAR」「Lenovo」をはじめとする中国系デバイスの多くを Meta-Bounds(莫界科技) が引き受けています。この Meta-Boundsのブランド名がMojie であり、CES Innovation Awardsで2製品がノミネートされています。 見かける機会の減ったHMD型デバイス 眼鏡型デバイスが増え続ける一方で、HMD型の新作はあまり見かけませんでした。既存デバイスを生かした展示という観点でも、前述の通りGalaxy XRの動態展示はなく、Apple Vision Proも“野良展示”を1件見かけただけでした。また、CES 2025で強く打ち出していたSonyのXYN Headsetは協業しているSiemensのブースで見かけた程度でした。もちろん見逃しているブースもあるかもしれませんが、全体的にHMD型デバイスの存在感は薄れている印象を受けました。 HMD型デバイスを打ち出し続けているShiftallとPimaxのブース しかし、昨年から継続してLVCC Centralで出展しているShiftallの「 MeganeX 8K Mark II 」や、例年通り存在感を放っていたPimaxなど、HMD型デバイスを全く見なくなったわけではありません。また、後述しますが、今年はPlay For Dream社から“伏兵”も発表されています。 日本発企業が示したXRデバイス向け要素技術 眼鏡型デバイスではEssilorLuxotticaとMeta-Boundsの二強とも言えますが、複数の日本発企業からXR関連デバイスに生かされる新しい要素技術が発表されています。 www.youtube.com 例えばガラスで有名なAGCは、高屈折率ガラス基板など、次世代デバイスに必要な要素を兼ね備えた複数の要素技術を発表しました。今後、これらの技術はXRグラスメーカーに技術供与されそうです。 www.youtube.com また、TDKはHapticセンサーやMeta-Optic Mirrorによる網膜投影技術を搭載したスマートグラスのソリューション「 TDK AIsight 」を発表しました。他にも、アルプスアルパインは触覚・嗅覚・聴覚を刺激する新しい五感刺激を発表するなど、技術立国として日本も存在感を見せていました。 XR Techのトレンドに引き続き、個人的に興味を惹かれたブースをいくつか紹介します。 Play For Dream Play For DreamのMR R&Dプロトタイプ Image source: Play For Dream Unveils MR Prototype at CES 2026 Apple Vision Proライクな製品で話題になったPlay For Dreamは、“事前発表なし”でMRのR&Dプロトタイプを発表しました。 G-X100-M1のリファレンスデザイン Image source: 万有引力(宁波)科技有限公司 これは2025年12月に公開された GravityXR(万有引力) の G-X100 を搭載した G-X100-M1のリファレンスデザイン を採用したものと思われるデバイスです。現時点ではまだプロトタイプなのでこのままリリースされるとは限りませんが、リファレンスデザイン通りなら重量は93gで遅延はわずか9msという非常に軽量かつ低遅延なデバイスです。 Cellid 過去最大級のブースを出展していたCellid ARグラス用ディスプレイを開発するCellidは、次世代ARグラス「 HJ1 AI Smart Glasses 」を展示していました。このデバイスは、Foxconn × Cellidによる取り組みで、Cellidはレンズ部のウェイブガイドを担当しました。そしてJorjin Technologies(Foxconn)はグラス本体を、GIS(Foxconn)は精密光学と表示関連を担当したそうです。 グラスを各パーツに分解した状態の展示 Cellidの特長のひとつである最大2000nitsの高輝度表示は美しく、約46gの軽量サイズでありながらEye Trackingモジュールを内蔵するなど、非常に期待できる眼鏡型デバイスです。 CellidはこれまでLVCC CentralではなくNorthに小規模なブースを構えていましたが、今年は同じNorthではあるものの壁際に過去最大級のブースを構えていました。グラスの表示部に関わる要素技術において突出している日本発企業であり、ここ数年の変化を見てきているだけに、個人的にも嬉しい気持ちになりました。 Even Realities Even Realities Even G1が日本で局所的な盛り上がりを見せた後、2025年11月に後継機となる Even G2 を発売した Even Realities は満を持してCentral Hallに大きく洗練されたブースを構えていました。 The Even Realities booth at #CES2026 in a flash. Where to find us: Booth #16833, Central Hall. January 6–9. pic.twitter.com/Q70D1fRTvq — Even Realities (@EvenRealities) 2026年1月8日 Even RealitiesはXREALに代表されるような拡張ディスプレイの類いではなく、必要な情報を重畳することを目的としたスマートグラスです。カメラがついていないのでプライバシーの問題が起きず、何よりこのジャンルでは数少ない国内利用に必要な「技適」取得済みで、安心して使える点も魅力です。 当時のOculus Riftがそうであったように、XR関連デバイスにおいて日本は重要な市場のひとつと言えます。 Mogura VR Newsのインタビュー記事 によれば、実際にEven G2は日本の店舗がもっとも売れているそうです。今後の展開が非常に気になる、そしていわゆるキャズムを越える可能性のあるデバイスのひとつだと考えています。 Meta Metaはプライベートブースの他に2か所のMeta Labを推していた MetaはWynn内に商談を目的としたプライベートブースを構えていました。私はこのプライベートには行きませんでしたが、Metaの旗艦店であるMeta Labに行きました。 Wynn Plaza Shopsに店舗を構えるMetaの旗艦店Meta Lab Las Vegas Metaは2025年10月に常設の Meta Lab Las Vegas を Wynn Plaza Shopsにオープンしました 。Meta LabではMeta AIグラスのRay-Ban MetaやMeta Ray-Ban Displayを購入できます。いずれも日本国内ではまだ購入できない眼鏡型デバイスです。 Meta Labで調達したMeta Ray-Ban Display(技適特例申請済み) Meta Lab Las Vegasの存在は事前に把握していたので、あらかじめ予約した上で現地に赴き、その場で Meta Ray-Ban Display を購入しました。このMeta Ray-Ban Displayは特に人気のデバイスで、生産数が限られているため予約なしでは購入できません。 CES初日、この Meta Ray-Ban Displayに大きなアップデートが入り 、テレプロンプターやEMGリストバンドによる手書き入力など複数の機能が追加されました。 EMGリストバンドで自動車のナビを操作する Image source: Garmin Newsroom LVCC West HallのGarminブースでは、 EMGリストバンドを自動車のナビ操作などに活用する実践的なPoC が紹介されていました。 Wynn内にオープンしていた特設のMeta Lab また、 Encore Esplanade のAwakening theater付近にもうひとつのMeta Labを構えていました。こちらの店舗は予約の仕組みがなく、並んだ順の入店となっていました。 注目のトピック:Fashion TechとBeauty Tech CES 2025に続き、CES 2026でも「 Fashion Tech 」と「 Beauty Tech 」のカテゴリーが設けられていました。また、同様にCES Innovation Awardsのカテゴリーとしても設けられていました。 CES 2026で「 Fashion Tech 」カテゴリーとして登録されているブース数は 111 、「 Beauty Tech 」カテゴリーとして登録されているブース数は 121 でした。 CES Innovation Awardsで 2026 Honoree in Fashion Tech を受賞したのは次の7製品でした。 fēnix® 8 Pro - MicroLED Galaxy Watch8 Littlebird Safety Tracker ModeX Bomber Jacket Snapdragon W5+ Gen 2/W5 Gen 2 Wearable Platforms VRING:ON, From Sketch to Factory, in One Flow WE-STIM™ CalfSleeve: Advanced muscle anti-aging device 同様に2026 Honoree in Beauty Techを受賞したのは次の10製品でした。うち4製品は L'Oréal Groupe で、業界随一の強さを感じました。 BALANCE AI Rejuvenation Shower System Hyper Rejuvenating Eye Patch Light Straight MASTER AI Multi-Therapy Pod maXpace Renergie Nano-Resurfacer | 400 Booster SCAR: AI-Powered Precision Scar Treatment and Coverage System Device SkinBoosters Jet Skinsight Water Saver Dose 個人的に興味を惹かれたブースをいくつか紹介します。 Mode Wearablesの「ModeX Bomber Jacket」 Mode Wearablesの「ModeX Bomber Jacket」 「 ModeX Bomber Jacket 」は、2026 Honoree in Fashion Techを受賞したモジュラー式スマートウェアです。スマートウェアと言えば、例えば2019年にLevi'sが発売した「 Levi’s Trucker Jacket with Jacquard by Google 」が思い浮かぶかもしれません。これはスマートフォンの拡張デバイスに近い立ち位置でしたが、ModeX Bomber Jacketは現代の利用シーンに即した機能を備えています。 *9 Wearable charging, energy harvesting, and power transfer solutions. Programmable illumination for signaling and fashion applications. Haptic feedback systems for improved situational awareness. Spatial computing products leveraging on-device AI. 眼鏡型デバイスが一般社会に普及した先には、特に消費電力の課題が考えられるので、こういったスマートウェアが必要になる未来が想像できます。 デジタルカラーチェンジネイルの「iPolish」 デジタルカラーチェンジネイルの「iPolish」 Eureka Parkにブースを構えていたフロリダ発の「 iPolish 」は、ネイルチップを含む専用のデバイスとアプリを使い、わずか5秒で400色以上のカラーを切り替えられる デジタルネイル です。百聞は一見に如かず、まずはX公式アカウントの動画を見てください。 When it's hard to choose, choose iPolish! Pre-orders available now at https://t.co/DogxQcrORr . #iPolish #ces2026 pic.twitter.com/hAWLOQcaaP — iPolish® (@iPolish_inc) 2026年1月11日 これは映画「トータル・リコール」を想起させる体験で、印象に残りました(聞いたことがない方は「トータル・リコール ネイル」で検索してみてください)。原理としてはネイルチップに電気泳動フィルム(electrophoretic film)が貼り付けられています。いわゆるE Inkと似たような仕組みなので、色の変わり方に見覚えがある方もいるのではないでしょうか。 デジタルデバイスという制約があるため、例えばネイルチップのサイズを個々人に最適化するのは難しいといった課題があると思われますが、かなり興味を惹かれたデバイスでした。 公式サイト では2026年6月発送予定で95 USDのプレオーダーで受け付けていますが、残念ながら発送先に日本は含まれていませんでした。 カスタム3Dプリントシューズの「Fitasy」 カスタム3Dプリントシューズの「Fitasy」 Image source: Fitasy Unveils Breakthrough Technology Making Custom-Fit 3D Printed Shoes More Accessible and Affordable, Supporting Better Foot Health 「 Fitasy 」は3Dプリンターで制作するシューズです。3Dプリンター×シューズの組み合わせは、例えば「 STARAY 」をはじめとして既に製品化されているものも多く、新規性は感じられない方が多いかもしれません。このFitasyはスマートフォンアプリでスキャンした足底面と側面にAIを組み合わせて正確な3Dモデルを作成し、そのデータに基づき、個々人に最適化したシューズを印刷するソリューションです。スマートフォンアプリで計測するという観点は「 ZOZOMAT 」に通じるものがあり、非常に興味を唆られました。 ラスベガスのメディア「FOX5 Vegas」にFitasyのCEOのインタビュー動画が公開されています。スキャンの様子も少し映っているので、興味のある方はぜひ見てください。2026年春に発送予定で210 USDのプレオーダーを受付中です。 www.fox5vegas.com ReFaの「ReFa AI color recipe PRO」 CES初出展となったReFaのMTG社 ここ数年、美容系の国内企業がCESに初出展を果たしていますが、今年は美容機器のReFaを展開するMTG社がCESに初出展していました。ブースではよく見かけるReFaの製品群ではなく美容師向けのアプリ「 ReFa AI color recipe PRO 」を展示していました。 美容師向けの「ReFa AI color recipe PRO」 このアプリは「 ハイトーンカラーの技術を、AIテクノロジーで再現するプロフェッショナル向け次世代ヘアカラーAIソリューション 」 *10 とのことで、ブリーチやカラーを日常的に行う派手髪クラスタとしても、特に関心を惹かれる製品でした。 花王の「my Symmetry & THE CORE」 花王のコンディショニング・ライフケアブランド「花王ライフケア研究所」によるTHE CORE 花王が2025年10月に立ち上げたコンディショニング・ライフケアブランド「花王ライフケア研究所」はJAPAN TECHブースのひとつとしてブースを出展していました。このブースでは、“8歩あるくだけで身体のゆがみを測定できる”アプリケーションの「 my Symmetry 」と、測定結果に応じて着用時のゆがみを補正するためのインソール「 THE CORE 」を展示していました。 カテゴリーとしてはHealth Techの方が相応しいかもしれませんが、身体の歪みを補正するソリューションはBeauty Techにも通じるものがあると考えています。ちなみに私の総合スコアは32点でした。しばらくこのアプリを利用してみます。 Perfectの「YouCam AI API」 Perfectの「YouCam AI API」 ZOZOは2022年4月から「 ZOZOCOSME 」でARメイクを提供しています。このARメイクにパーフェクト社が提供するソリューションを利用しています。そのパーフェクト社はCES 2026では 次世代「AIビューティーエージェント」を展示 していました。また、「 The New Retail Experience in the AI-Powered Store 」と題したセッションに登壇していました。 注目のトピック:その他 一部、XR Tech・Fashion Tech・Beauty Techを含むものの、その他の気になった展示を紹介します。 NVIDIA NVIDIAショーケースの巨大オブジェ NVIDIAはCES本編とは別に、FontainebleauのBleauLive TheaterでCEOによる講演「 NVIDIA Live with CEO Jensen Huang 」があり、講演後からFontainebleau 4FのCobalt Foyerでショーケースとして関連する様々なものを展示していました。私はラスベガスに到着したDay 0に講演に現地参加し、展示も眺めてきました。 革ジャンが代名詞となっているNVIDIAのフアンCEOによる講演 非常に多くの人で賑わっていたショーケースの様子 講演とショーケースを通して感じたのはまさに王者の風格です。NVIDIAにとってもはやLVCCにブースを構える必要などないのかもしれません。NVIDIAに所以のあるブースにはNVIDIAのオブジェが置かれていたので、ブースは出さずとも、NVIDIAの存在をCES会場のいたるところで感じられました。 Samsung SamsungのAIギャラリーゾーン Samsungは1月4日のメディア公開後、1月7日まで Wynnの特別会場で「 The First Look 」と題したイベントを開催 しました。前述の通りSamsungもこれまではLVCC Centralにブースを構えていましたが、今年は独自のブースを展開していました。 CES 2026の会期は1月9日まででしたが、Samsungの展示は1月7日までだったため、会期終盤に見られなかったという声もありました。 ブース全体の様子を映した60秒動画がYouTubeに公開されている ので、興味のある方は覗いてみてください。 パーソナライズされた美容ケアを実現するAI Beauty Mirrorと新製品のSamsung Galaxy Z TriFold 「AIライフパートナー」をテーマにしていたこともあってか、展示されていたものは家電をはじめとした生活に紐づくものが多く感じられました。Beauty Techに分類される「 AI Beauty Mirror 」は、いわゆるミラー系デバイスですが、オンデバイスで動作するAIが搭載されていることや、偏光ミラーとハーフミラーの組み合わせによって鮮明な映像を実現していることが特長的でした。 *11 In the next area, visitors entered a space modeled after a powder room where a circular mirror revealed itself as the AI Beauty Mirror. Powered by on-device AI, the technology signalled Samsung’s technology expansion into the beauty space. The hybrid design combines a polarised mirror with a half mirror to improve reflectivity and transparency, delivering clearer, more precise visuals. このデバイスは韓国ビューティー大手であるAMOREPACIFIC社の技術を生かしたものです。その根底には2026 Honoree in Beauty Techを受賞した「 Skinsight 」の技術が取り込まれています。 一方で、そのテーマ性もあってか「CES Innovation Awards」で「2026 Honoree in XR & Spatial Computing」を受賞した「Galaxy XR」が、このSamsung特設会場のどこにも展示されていなかったことは、個人的には少し残念でした。 Razer AIコンパニオンのひとりに日本人のSAOさんを起用したProject AVA RazerはCES 2025で “AI esports coach”として発表していた「 Project AVA 」 を、CES 2026では “YOUR AI DESK COMPANION”としてその全貌を公開 しました。 プライベートエリアに展示されていたデスクサイズのProject Ava いわゆるポストGateboxとも言える“キャラクター召喚装置”は目新しいものではなく、2025年にKickstarterでクラウドファンディングを実施していた「 CODE27 」や、CES 2026にも出展していた「 Dipal 」や「 Lepro Ami 」などが挙げられます。そういった状況でこのAIが前提になった時代にRazerが本格的に参入してきたことは、潮目が変わりつつあると言えるかもしれません。 CES 2026の翌週、1月11日から13日にかけて開催された NRF 2026 で GoogleはAgentic Shopping時代に向けた共通規格となる「UCP」を発表 しました。もしかすると、こういったAIコンパニオンがその間に介在する未来も十二分に考えられるのではないでしょうか。 Robot, Robot and Robot... 数年前は「NorthはEVのCES」という雰囲気もありましたが、EV市場は頭打ちになりつつあると言われています。今年はそれらがロボットに置き換わっていました。トップトレンドにAIとRoboticsが含まれているだけあります。とにかく多種多様なロボットを見ました。ここで言うロボットは人型ロボットです。効率を最大化するとき、人型であることが最適解なのかは気になりますが、フィジカルAIの機運を感じられました。 AI とロボットの発展によって人が“洗濯物を畳む”という作業から解放される日は来るか? これはあまりに好きすぎてしばらく眺めていた Dyna Robotics 社によるシャツの折り畳みロボット🤖🧺 #CES2026 pic.twitter.com/O4UvKzF7A4 — HEAVEN ちゃんᯅ a.k.a. ikkou (@ikkou) 2026年1月9日 Roboticsは専門分野ではないため深掘りはしませんが、DYNAの洗濯物を畳むロボットはとても印象的でした。興味があれば動画を再生してみてください。 おわりに 帰国時にハリー・リード国際空港で体験したAmazon Oneによる手のひら決済 例によって今回のCES視察は開発部門の福利厚生である「 セミナー・カンファレンス参加支援制度 」を利用しての参加となります。 昨年同様、今回もアメリカン航空の直行便を選択したためフライトは割高になりましたが、乗り継ぎがない分、時間を有効に使えました( ハワイアン航空のStarlink機内Wi-Fi にも惹かれましたが!)。CES 2027の開催日程は、今年と同じ日程の1月6日から9日の4日間と発表されています。参加意向のある方は、できるだけ早くフライトとホテルを手配すると良いでしょう。 例年通りのことですが、フライトとホテル以外にも一定の金銭的コストが発生しています。CESに限らず、海外で開催されるカンファレンスにおいては、そのコストに対して得られる成果に対するコストの正当性を説明するのは難しいかもしれません。しかし、XR領域は「 百聞は一見ならぬ“一体験”にしかず 」です。CESに関するニュース記事はCESの会期中から多く目にしますし、ChatGPTのdeep research機能などを使えば、ある程度の情報は得られます。現地に足を運び、自らの目と手で体験し、一次情報を得ることが重要だと認識しています。Zooxの乗車体験や、帰国時に体験したAmazon Oneによる手のひら決済も、一次情報を得る取り組みの一環だと考えています。 モーニングミーティングで訪れたご当地ファミリーレストランのBlueberry Hill Restaurant そして、CESはビジネスショーという性質上、個別に会話するプライベートブースが用意されています。いくつか参加しましたが、こういったオンサイトならではの対面コミュニケーションも、インターネットメディアの記事等からは得られない大きなメリットだと考えています。わざわざ現地に赴いて参加するのであれば、あらかじめそういった場をセッティングしておくことを強くおすすめします。 最後までご覧いただきありがとうございました。来年もまた、CES 2027のレポートをお届けできるように努めてまいります。 ZOZOでは、各種エンジニアを採用中です。ご興味のある方は以下のリンクからご応募ください。 corp.zozo.com 以上、現地からのレポートでした! *1 : https://www.ces.tech/press-releases/innovators-show-up-ces-2026-opens-today *2 : https://www.ces.tech/press-releases/what-not-to-miss-at-ces-2026/ *3 : https://cdn.ces.tech/ces/media/pdfs/ces-2018-audit-summary.pdf *4 : https://cdn.ces.tech/ces/media/pdfs/ces-2019-audit-summary.pdf *5 : https://cdn.ces.tech/ces/media/pdfs/2020-ces-attendance-audit-summary.pdf *6 : https://cdn.ces.tech/ces/media/pdfs/attendee-audit-summary-2023.pdf *7 : https://www.ces.tech/media/5tmfzidk/attendeeauditsummary_2024.pdf *8 : https://www.ces.tech/media/53eghnx5/ces-2025-attendee-audit-summary.pdf *9 : https://www.modewearables.com/ *10 : https://www.mtg.gr.jp/news/detail/2026/01/article_2481.html *11 : https://news.samsung.com/uk/ces-2026-an-entertainment-companion-for-every-moment-seen-and-heard
アバター
.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(2) { display: none; } はじめに こんにちは、検索基盤部検索研究ブロックの小倉です。普段はZOZOTOWNの検索精度改善を担当しています。検索研究ブロックでは2020年から検索結果の「あなたにおすすめ順」(以降「おすすめ順」と呼びます)とその改善に取り組んできました。その過程で「これまで積み重ねてきた改善は、トータルでどの程度効果があったのか?」を確かめるために、ネガティブテスト(最新のロジックと古いロジックを比較するA/Bテスト)を導入・運用してきました。しかし実際に運用を重ねる中で、ユーザー体験の悪化やロジック改善の機会損失といった問題も見えてきました。本記事では、ZOZOTOWN検索おすすめ順におけるネガティブテストを「導入して効果を測った段階」から「運用を見直すに至った段階」まで、その経緯と学びをご紹介します。 目次 はじめに 目次 背景・課題 ZOZOTOWN検索おすすめ順の改善 「1年間の積み上げ改善」をどう測るかという課題 候補に挙がった評価方法とその検討 ネガティブテストの設計と実施 ネガティブテスト設計時に意識したポイント 実施したネガティブテスト 結果から得られたこと 運用を重ねて見えてきた課題 ユーザー体験やビジネスへのマイナス影響 古いロジックの再現にかかる工数 改善活動が止まることによる機会損失 早期切り戻しとなった場合の改善幅の計測精度の低さ ネガティブテストの再定義と今後の運用方針 毎年の定期イベントから外す判断 KPI妥当性検証のためのスポットテストとしての再定義 プロジェクト成果はA/Bテスト積み上げで見る方針へ まとめ 背景・課題 ZOZOTOWN検索おすすめ順の改善 ZOZOTOWNでは、ユーザーごとにパーソナライズされた「おすすめ順」を検索結果の並び順として提供しています。私たちは、2020年からこの「おすすめ順」の改善プロジェクトを継続的に進めてきました。当初はヒューリスティックな手法が使われていましたが、ランキング学習で構築した機械学習(Machine Learning: ML)モデルを導入し、導入後も反復的にロジックを改善してきました。これらの改善を実施する際は、基本的には常にオンラインA/Bテストを行い、その効果を測った上でリリースの可否を判断しています。 「1年間の積み上げ改善」をどう測るかという課題 短期間で行うA/Bテストでは、売上金額などの指標はKPI(Key Performance Indicator)としては感度が低く、有意な差を検出しづらいという問題があります。そこでおすすめ順改善では、コンバージョン率(Conversion Rate: CVR)やクリック率(Click Through Rate: CTR)などのより感度が高い指標をKPIとしています。売上金額はKGI(Key Goal Indicator)であり、KPIはKGIの代理指標である、という位置付けです。感度の高い指標をKPIとすることで、小さな改善でも積極的にリリースしやすくなります。 プロジェクトを進める上では、ユーザー体験を良くすることだけでなく、中長期的にはKGIの向上に寄与することも求められます。1回1回のA/Bテストで「前より良くなった」ことは確認できる一方で、「この1年間の改善の積み重ねは、KGIの向上に寄与しているのか?」を定量的に把握できないことが課題になってきました。 そこで私たちは、「これまで積み重ねてきた改善の効果を、より直接的に測る方法はないか?」という問いに対して、さまざまなアプローチを検討することになりました。 候補に挙がった評価方法とその検討 1年間の改善を測る方法としては、主に次のような案が検討されました。 1. 個々のA/Bテスト結果の積み上げ試算 1回1回のA/Bテストで得られたupliftを積み上げ、1年間のupliftを推定する方法 シンプルで説明しやすい一方で、以下の懸念点がある 改善施策同士の相互作用(重複効果や打ち消し合い)の扱いが不適切 統計的有意差が出ていない結果を積み上げてしまうのは不適切 2. 長期ホールドアウト 一部ユーザーに対して長期間古いロジックの結果を表示し、最新のロジックと比較するA/Bテスト 積み上げた効果を直接比較できたり長期的な影響を観察できたりする一方で、以下の問題点がある 一部ユーザーに与えるマイナス影響 古いロジックを維持し続ける運用保守コスト 対象ユーザー数が少ないことによる検出力の低さ 3. ネガティブテスト(vs. 古いロジック) 通常のA/Bテストと同じ期間で、最新のロジックと古いロジック(1年前のロジック)を比較するA/Bテスト 通常のA/Bテストと同じ枠組みで実施できるため、既存のシステム・運用フローを流用しやすく、検出力も同程度を期待できる 「古いロジックに戻すことで、ユーザーの体験を意図的に悪化させる」という明確なデメリットがある 1年間で有効な改善を積み重ねているほど、悪化も大きくなる 4. ネガティブテスト(vs. 簡単なロジック) ランダム順など、ごく単純なロジックとの比較をするA/Bテスト 比較対象をベースラインとしてupliftを計測できるが、3の手法よりもさらにユーザー体験が悪化する おすすめ順プロジェクトではまず2の長期ホールドアウトを採用していましたが、KPIの有意な差を検出できませんでした。その後実施した3のネガティブテストではKPIの有意な差を感度高く検出できたため、以降はネガティブテストを採用しました。 なお、これらの評価方法を検討する上で私たちは『 A/Bテスト実践ガイド 真のデータドリブンへ至る信用できる実験とは 』という書籍を参考にしました。2の手法「長期ホールドアウト」は第15章で触れられています。3と4の手法については、第23章で「リバース実験」として紹介されている手法をベースに考案したもので、社内用語で「ネガティブテスト」と呼んでいます。こちらは一般的な名称ではなく、別の意味を持つ既存の専門用語と被っているため誤解を招く恐れがありますが、本記事では引き続き「ネガティブテスト」と表記します。 また、検索おすすめ順の改善とネガティブテストの思想については、以下のTech Blog記事でも触れています。 techblog.zozo.com ネガティブテストの設計と実施 ネガティブテスト設計時に意識したポイント ネガティブテストは、意図的に最新のロジックから古いロジックに切り替えるため、treatment群のユーザーにとっては体験が悪化します。十分なデータが取れるように短すぎず、かつユーザー体験への悪影響を抑えられるように長すぎない、適切なタイミングで切り戻しを行いたいと考え、設計段階で特に次の3点を重視しました。 指標 「この指標が有意差ありで悪化したら即時切り戻す」というガードレール指標を明確化し、ネガティブな影響が広がる前にテストを止められるようにした ガードレール指標の設計は、ネガティブテスト導入時の議論を通じて整備され、通常のA/Bテストにおける判断基準にもつながっている テスト期間 基本的には通常のA/Bテストと同じ期間設定で実施 曜日の影響を考慮して1週間分のデータが欲しいものの、ユーザー影響の大きさを鑑みて最低限のテスト期間は2〜3日に設定した 切り戻し判断とエスカレーションフロー ガードレール指標が一定の閾値を割った場合に「誰が」切り戻しを判断するのかを合意してから実施 ネガティブテストはその性質上切り戻しになる可能性が十分にあるため、事前にプロダクトオーナーの了承を得た上で実施 実施したネガティブテスト ZOZOTOWN検索おすすめ順におけるネガティブテストは、過去3回実施しました。 第1回 「MLモデル vs ヒューリスティックモデル」という構図で、初めての本格的なネガティブテストを実施 事前に複数回のMTGを行い、ガードレール指標や切り戻し基準を整備した上でスタートした 第2回 前年の運用を踏襲しつつ、「最新のMLモデル vs 1年前のMLモデル」という形でネガティブテストを実施 モデル自体を公平に比較するため、「1年前のMLモデル」も「最新のMLモデル」と同じ期間のログデータから学習した 第3回 第2回と同じ形式でネガティブテストを実施 後述する懸念を受け、「ネガティブテスト自体をどう位置づけるか?」を再定義するための議論・検討を集中的に行った 結果から得られたこと 第1回ネガティブテストでは、ヒューリスティックモデルに対してMLモデルが優位であることを明確に確認できました。プロダクトオーナーには、次のようなメッセージを分かりやすく伝えることができました。 「この1年間で、検索おすすめ順は確かに良くなっている」 「MLベースのアプローチに継続的に投資する価値がある」 一方で、第2回と第3回ネガティブテストはMLモデル同士の比較となりましたが、それぞれ次のような結果となりました。 第2回では、最新のMLモデルが優位ではあるものの、切り戻すほどの大きな差が出ないまま規定のテスト期間が経過 第3回では、早い段階で大きな差をつけて最新のMLモデルが圧勝し、切り戻し これらの結果から、 個々のA/Bテストを重ねることでおすすめ順は着実に改善できている と言えます。 運用を重ねて見えてきた課題 ユーザー体験やビジネスへのマイナス影響 あらかじめ想定・許容していたことではありますが、ネガティブテストでは次のような影響があります。 あえて精度の低いモデルを出すことで、ユーザーが欲しい商品に辿り着きにくくなる その結果、短期的にKGIやKPIが低下する ユーザーに「ZOZOTOWN検索は使いにくい」という印象を持たれてしまうリスクがある 第2回ネガティブテストでは、「マイナス影響を覚悟してテストを実施したものの、最終的に十分な差が得られなかった」という状況も経験しました。このとき、「ユーザーに負担をかけてまで得られる知見として、割に合っていたのか?」という問いが、プロジェクト内で意識されるようになりました。 古いロジックの再現にかかる工数 ネガティブテストで古いロジックを再現するには、単に古いモデルファイルをデプロイするだけでは済みません。 過去1年の間に実施されたシステム変更(特徴量の追加・削除、前処理の仕様変更、インフラ構成の変更など)を洗い出す それらを元に戻すか、互換性のある形で再現する テスト終了後には再度最新の状態へ戻す これらには相応の工数がかかりますし、1年前当時の状態を完全には再現できない場合もあります。 加えて、MLモデル同士の比較に特有の問題もあります。学習データとなるログの取得期間を比較対象のモデル同士で揃える場合、そのログは「最新の精度が良いモデルによって並び替えられた検索結果」に依存しています。これにより古いモデルがログを通じて「賢く」なってしまい、モデルの性能差が現れにくくなります。 改善活動が止まることによる機会損失 ネガティブテストの期間中は、他の改善施策のリリースを止めざるを得ません。本来であれば実施できたかもしれない改善施策を、一時的に棚上げすることになります。また、ネガティブテスト自体の準備・分析にも工数がかかるため、他の改善施策が後回しになります。 この「改善活動が止まることによる機会損失」は、テストを重ねるほど無視できないコストとして認識されるようになりました。 早期切り戻しとなった場合の改善幅の計測精度の低さ ネガティブテストでは、ユーザーへのマイナス影響を抑えるため、途中で切り戻しになる可能性があります。この場合、「1年間の改善幅」をどこまで精度高く推定できるかには、次のような制約が生じます。 規定のテスト期間を完走した場合と比べてサンプルサイズが小さくなり、推定値の信頼区間が広がる 切り戻しのタイミングや理由によっては、特定の曜日・キャンペーン期間などに偏ったデータしか取得できない その結果、「着実に良くなっている」という傾向は確認できても、「年間で◯%改善した」と言い切れるほどの精度では評価しづらい このように、早期切り戻しは「ユーザー体験やビジネスへのマイナス影響」を抑える一方で、「1年間の改善幅の計測精度」が低くなるというトレードオフの関係があります。 ネガティブテストの再定義と今後の運用方針 毎年の定期イベントから外す判断 当初、ネガティブテストは「1年間の改善を測るための定期イベント」として位置づけていました。しかし、複数回の運用を経て、次のようなデメリットが当初の想定よりも大きいことが分かってきました。 ユーザー体験やビジネスへのマイナス影響 古いロジックの再現にかかる工数 改善活動停止による機会損失 切り戻しとなった時の改善幅の計測精度の低さ こうした懸念は、プロダクトオーナーからも明確に共有されるようになりました。「ネガティブテストは、毎年なんとなくやるものではない」「目的に対して割に合わないタイミング・やり方もある」という認識のもと、 定期的に実施する運用からは外す という判断に至りました。 KPI妥当性検証のためのスポットテストとしての再定義 ただし、ネガティブテストを完全にやめてしまうわけではなく、その役割と使いどころを絞るという方針に切り替えました。具体的には、ネガティブテストを KGIの代理指標としての、KPIの妥当性を検証するためのスポットテスト として位置づけ直しています。例えば、A/BテストにおけるKPIの定義を変更した後、「そのKPIに沿って改善を積み重ねることで本当にKGIは向上するのか?」を確かめる場合などに実施します。 これにより、ネガティブテストの実施回数を絞りつつ、実施する際には「なぜ今やるのか」「どんな問いに答えるのか」を明確にする、という運用にしました。 プロジェクト成果はA/Bテスト積み上げで見る方針へ これまでの経緯から、現在のおすすめ順プロジェクトでは成果を以下の方針で測ることとしています。 有意に改善したA/Bテスト結果の積み上げ 統計的有意差ありで改善したA/Bテストの件数やupliftを一覧化し、「この1年間で、どのような施策でどれだけ改善したか」を整理 開発チーム内の目標として、「有意差ありの改善をN件出す」といったA/Bテスト起点の指標を置く 前述の懸念に対する対処 施策同士の相互作用は正確には分からないものと割り切る 有意差なしでもシステム面でのメリットが大きい場合はリリースするが、その結果は改善の積み上げには含めない ネガティブテスト自体は依然有効な選択肢です。ただしそれは「とりあえず毎年やるもの」ではなく、KPIを再設計したい時や既存の評価軸に大きな不安がある時など、限られた局面で慎重に実施するべきテストです。これが、数年にわたってネガティブテストを導入・運用してきた結果として得られた結論です。 まとめ 本記事ではZOZOTOWN検索おすすめ順改善におけるネガティブテストの運用をご紹介しました。ネガティブテストの導入によって複数回のA/Bテストによる改善の積み上げがトータルでどの程度の効果をもたらしているかを測ることができました。また、ネガティブテストの運用を見直したことでユーザー体験の悪化を最小限に抑え、開発リソースを前向きな改善施策へ回せるようになりました。本記事が、ネガティブテストの導入を検討している方や、既にネガティブテストを実施していて課題を感じている方の参考になれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
アバター
.entry .entry-content .table-of-contents > li > ul { display: none; } はじめに こんにちは、グローバルシステム部フロントエンドブロックの平田です。 私が所属するチームでは ZOZOMETRY というtoBサービスを開発しています。スマートフォンを用いて身体計測し、計測結果を3Dモデルやデータとして可視化します。計測結果はWeb上で管理できます。 このサービスのフロントエンドではReact(Next.js)を採用しています。さらにそれらの知見を深めるために、NYで開催されたJSNation、React Summit US 2025に参加してきました。 この記事では現地参加ならではの経験や、参加したセッションへの考察などを紹介していきます! はじめに JSNationとReact Summitとは? Day 1 - JSNation Day 2 - React Summit After Party 気になったセッションについて The AI-Native Software Engineer AIネイティブ・ソフトウェアエンジニアへのシフト 「70%のAI」と「30%の人間」の境界線 生産性のパラドックスと検証の重要性 「Vibe Coding」とエンジニアリングの規律 まとめ Full-Stack AI Orchestration: Modular Agents, Observability, and the Edge モジュール型エージェントとLangGraph 信頼性を担保する「観測可能性(Observability)」 Temporalによる耐久性のあるワークフロー 実用的なAI技術スタックの全体像 まとめ React Strict Dom: Cross-Platform React Based on the Web 「Web標準」をクロスプラットフォームの基盤に 厳格なサブセットによる「予測可能性」の向上 低コストな移行とメンテナンス性 まとめ Goodbye, useState useState の積み重ねが招く「Rube Goldbergマシン」 「どこに保存するか」よりも「どうモデリングするか」 React 19とサーバーコンポーネントによる簡素化 まとめ Workshop The React Developer's Guide to AI Engineering LLMを「関数」として扱う設計思考 ストリーミングUIとRAGの実装 まとめ Debugging with Sentry AI using Seer, MCP, and Agent Monitoring 自社開発AI「Seer」によるエラー解析の自動化 MCPによるエコシステムの連携 まとめ イベント全体を通じて感じたこと 最後に JSNationとReact Summitとは? JSNation と React Summit は、JavaScriptおよびReactに特化した国際的なカンファレンスで、 GitNation が主催しています。現地参加とオンライン参加のハイブリッド形式で開催され、それぞれJavaScript、React.js関連の様々なセッションが行われます。また、ネットワーキングやオンラインワークショップ、アフターパーティーなど、多彩なプログラムが提供されています。最新の技術の動向を学び、世界中の開発者と交流する絶好の機会です。 様々な国や地域で開催されていますが、今回は2日間に渡ってアメリカで開催されたJSNation、React Summitに参加してきました。ZOZOからは昨年 アメリカ・ニューヨークで開催された同イベント にもエンジニアが参加しており、今回も引き続き参加できることになりました。 日付 時間帯(EST) イベント 場所 2025/11/17 9:00 - 17:00 JSNation Liberty Science Center 2025/11/18 9:00 - 18:00 React Summit Liberty Science Center 2025/11/18 19:00 - 22:00 After Party Zeppelin Hall Beer Garden それでは早速参加したそれぞれのイベントについて詳しく紹介していきたいと思います。 Day 1 - JSNation 今年も会場は昨年と同様のLiberty Science Centerです。 会場に向かう途中で見ることができるマンハッタンの光景 今年は昨年の同時期よりとても寒かったです チェックイン後にもらえる参加者バッジ(デザインが昨年と変わっていました) オープニング前の交流スペースの様子 今年の朝食はシンプルなベーグルでした オープニングの様子(写真提供:GitNation) 最初のセッションの様子(写真提供:GitNation) 質問ルーム(毎セッション後にスピーカーに質問できます)(写真提供:GitNation) お昼ご飯 ランチタイムはフィンテック系企業でフルスタックエンジニアをしている人々と、デザイン系SaaS企業でバックエンドエンジニアをしている人と一緒に過ごしました。我々グローバルシステム部が扱っているプロダクトはやはり世界的にもユニークなので、興味を持ってもらえることが多くて嬉しかったです。午前中のセッションの感想や旅行の話などをしたらあっという間に午後のセッションの時間になったので移動しました。 ちなみにデザイン系SaaS企業の人は、昨年会社が企業ブースを出したので、無料で参加できたそうです。 またセッション以外には、複数のスピーカー達が意見を交わすディスカッションコーナーが設けられており、リスナーとしても参加可能でした。 セッションよりも近い距離で見ることができ、質問も可能なためセッションにはない面白さがありました。 ディスカッションの様子 おやつタイム(写真提供:GitNation) 1日目のエンディング(写真提供:GitNation) Day 2 - React Summit ノベルティのマグカップ React Summitのオープニング(写真提供:GitNation) 企業ブース(写真提供:GitNation) 企業ブースもたくさんあり、幾つか回りました。こちらはおなじみのFigmaです。 プロジェクトによってはフロントチームがデザインをすることがあり、その際にFigmaのMCPサーバを使ってUIの実装をすることがあります。仕上がりにバラつきがあるので安定してUIを作れないかどうか聞いてみましたがそれはまだまだ難しいとのことでした。これからに期待ですね。 2日目のランチ この日のランチタイムは、前日に出会ったフィンテック企業の人々と、新たに出会ったノルウェーのコンサルティング企業のエンジニアと当日のスピーカーのNicolasさんと一緒にお昼を食べました。 セッションの感想やどんな仕事をしているか基本的な会話をした後、移住の話題で盛り上がりました。ノルウェーのエンジニアがスイスに移住検討中であること、Nicolasさんがイギリスから元々アメリカに移住したことなどを話しました。地理的にも、言語的にも色々と移動しやすいことはとても良いですね。 CodeRabbitのブース前にあった可愛いキャロットケーキ CodeRabbitのブース(写真提供:GitNation) こちらはPRをAIがレビューするツールのCodeRabbitのブースです。AIの導入によりPRの作成が高速化した一方で、チーム内レビューが追いついていないという問題は、どこのチームでも発生していると思います。このツールはコードレビューを自動化するサービスで、PR作成からマージまで大幅に高速化できるので、実際のプロダクトでも試してみたいと思いました。 イベントエンディングの様子(写真提供:GitNation) 気づけばあっという間に2日目も終了しました。この2日間はエネルギッシュな雰囲気に触れ、非常に刺激的で充実した時間を過ごすことができました。 After Party React Summitの後には、After Partyの場が用意されています。今年はZeppelin Hall Beer Gardenでの開催でした。せっかくの機会なのでぜひ参加したかったのですが、今回宿泊したホテルが会場からかなり遠かったため、安全面を考慮して泣く泣く参加を見送りました。代わりに、宿泊先が割と近かった一部の参加者と夕食を食べることになりました。 プチアフターパーティーの様子 気になったセッションについて それでは、特に印象に残ったセッションを4つご紹介します。これらはGitNationのウェブサイトでも視聴可能ですので、ぜひチェックしてみてください。 gitnation.com The AI-Native Software Engineer gitnation.com まず1点目は、Google ChromeのエンジニアリングリーダーであるAddy Osmaniさんによるセッションです。AI時代のエンジニアはどうあるべきかという問いに対し、 「The Human 30%(人間の30%)」 というキーワードを軸に、AIとの共存戦略を説いた非常に刺激的な内容でした。 プレゼンテーションの様子(写真提供:GitNation) 以下、気になったポイントをまとめます。 AIネイティブ・ソフトウェアエンジニアへのシフト AIは単なる補助ツールではなく、開発プロセスの初期段階から組み込まれる「AIネイティブ」なワークフローへと進化しているという点が非常に印象的でした。エンジニアの役割は、自ら一行ずつコードを書く「実装者」から、複数のAIエージェントを指揮して目的の成果物を得る「オーケストレーター」へと変化しています。これからは「どうコードを書くか」ではなく「どう正しいコードを構築させるか」という、より高次な視点が求められると感じました。 「70%のAI」と「30%の人間」の境界線 AIは定型的なコーディングやプロトタイプ作成など、タスクの約70%を驚異的なスピードでこなせます。しかし、残りの30%には人間の介入が不可欠です。特に、複雑なアーキテクチャ設計、エッジケースの考慮、セキュリティリスクの判断といった「高コンテキスト」な領域は、依然として人間にしかできない重要な付加価値です。AIが得意な「低コンテキスト」な作業を委ねることで、人間はより本質的な問題解決に集中できるという考え方に強く共感しました。 生産性のパラドックスと検証の重要性 AIによってコードの生成速度は上がりましたが、一方でプルリクエスト(PR)のサイズが肥大化し、レビューに要する時間が大幅に増加しているという「生産性のパラドックス」についても触れられていました。AIの提案は一見正しく見えても、微妙な誤りを含むことがあります。そのため、 「信頼せよ、されど検証せよ(Trust, but verify)」 という姿勢が、AI時代の品質担保において最も重要なスキルになると感じました。 「Vibe Coding」とエンジニアリングの規律 スピード重視の「Vibe Coding(ノリで書くコーディング)」と、厳格なプロセスを重視する「AI支援エンジニアリング」を、状況に応じて使い分けるスキルの重要性が語られていました。新規機能のプロトタイピングには前者を、ミッションクリティカルな本番環境の構築には後者をといった、AIとの距離感をコントロールする能力がこれからのエンジニアの武器になると確信しました。 まとめ このセッションを通じて、AIに代替されることを恐れるのではなく、AIを「能力の増幅器」としてどう使いこなすかというポジティブなマインドセットの重要性を再認識しました。 特に「人間の30%」である、複雑な文脈の理解や最終的な品質責任を担う部分は、エンジニアとしての専門性がより光る領域です。AIを単なる効率化ツールとしてだけでなく、自身の学習を加速させるパートナーとして活用し、日々の開発業務の質を一段階引き上げていきたいです。 Full-Stack AI Orchestration: Modular Agents, Observability, and the Edge gitnation.com 2点目は、NetflixのシニアエンジニアであるJamal Sinclair O'Garroさんによるセッションです。 AIを単なる「便利な機能」としてではなく、エンタープライズレベルの本番環境で耐えうる「堅牢なシステム」として構築するための、フルスタックなオーケストレーション戦略についてです。 プレゼンテーションの様子(写真提供:GitNation) 以下、気になったポイントをまとめます。 モジュール型エージェントとLangGraph Netflixのような大規模システムを支える視点から、AIエージェントを巨大な1つの塊にするのではなく、特定の責務に特化した「モジュール」として分割する設計が推奨されていました。セッションでは LangGraph を用いてエージェントをグラフ構造として定義します。それらを組み合わせることで、複雑なタスクを処理できる「モジュラーモノリス」なAIシステムを構築する手法が紹介されました。 信頼性を担保する「観測可能性(Observability)」 AIシステムのブラックボックス化を防ぐため、 LangSmith などのツールを活用してトレース(追跡)を行う重要性が強調されました。エージェントが「なぜその回答に至ったのか」の思考プロセスや、APIコールの履歴、トークンコストを可視化することで、エンジニアがデバッグ可能な状態を維持できます。これがプロダクション導入への鍵であると再認識しました。 Temporalによる耐久性のあるワークフロー AIの挙動は予測不可能で、外部APIの失敗もつきものです。そこで、ワークフローエンジンである Temporal を組み合わせるアプローチが紹介されました。これにより、処理が途中で失敗してもリトライや状態の復元が可能になり(Durable Execution)、システム全体の耐障害性を劇的に向上させることができます。 実用的なAI技術スタックの全体像 セッションでは、プロンプト管理から評価(Evaluation)、デプロイまで、モダンなフルスタックAI開発に必要なツール群が体系的に紹介されていました。 「ただAPIを叩くだけ」のフェーズから、エンジニアが主導して「信頼性の高いAIシステムを設計・運用する」フェーズへと移行していることが明確に示されていました。フロントエンドだけでなく、バックエンドやインフラ領域に興味があるエンジニアにとっても非常に興味深い内容でした。 まとめ 「AIを魔法として扱うのではなく、システム設計の一部として捉える」というJamalさんの言葉が非常に印象的でした。LangGraphでロジックを組み、Temporalで耐久性を高め、LangSmithで監視する。この「モダン・エンタープライズ・スタック」は、信頼性が求められるプロダクトにAIを組み込む上で、非常に参考になるアーキテクチャでした。 React Strict Dom: Cross-Platform React Based on the Web gitnation.com 3点目は、MetaのNicolas Gallagherさんによるセッションです。WebとネイティブReact Nativeの間にある「スタイリングとAPIの乖離」を解消する、 react-strict-dom についての解説でした。 プレゼンテーションの様子 以下、気になったポイントをまとめます。 「Web標準」をクロスプラットフォームの基盤に これまでのクロスプラットフォーム開発では、Webとネイティブで別々のコンポーネントやスタイル記述が必要でした。react-strict-domは、 div や span といった「Web標準のHTMLタグ」と「CSS」のサブセットを共通のインタフェースとして使用します。Webエンジニアが慣れ親しんだ知識をそのままネイティブ開発に転用できるだけでなく、プラットフォーム間のフラグメンテーション(断片化)を根本から解決しようとするアプローチです。Metaの強力な意志を感じました。 厳格なサブセットによる「予測可能性」の向上 「Strict(厳格)」という名の通り、あえて使用できるCSSプロパティを限定することで、どのプラットフォームでも一貫した挙動を保証しています。Meta内部での採用実績も紹介されており、開発効率の向上だけでなく、成果物の品質も担保されている点は非常に説得力がありました。特に「AIによるコード生成」との相性も良く、構造化された厳格なAPIがAIの生成精度を高めるという視点は、これからの開発において大きなメリットになると感じました。 低コストな移行とメンテナンス性 既存のReact NativeコンポーネントをWebへ、あるいはその逆へと移行する際のコストを大幅に下げることができます。特定のプラットフォームに依存した「方言」を排除し、標準的な記述に寄せることで、長期的なメンテナンス性が向上します。マルチプラットフォーム展開を前提とした大規模プロジェクトにおいて、この共通基盤は非常に強力な武器になると確信しました。 まとめ このセッションは、Reactエコシステムが再び「Web標準」に回帰しつつ、ネイティブの力を最大限に引き出そうとしている流れを象徴していました。 「Web向け」「アプリ向け」とエンジニアを分けるのではなく、共通の「React Strict DOM」という言語を通じて、より効率的にプロダクト価値を届ける未来がすぐそこまで来ていると感じます。我々のチームでも、今後のマルチデバイス展開を見据えて注視していきたい技術です。 Goodbye, useState gitnation.com 最後は、XStateの作者としても知られるDavid Khourshidさんによる、状態管理のパラダイムシフトを提案するセッションです。「複雑なアプリをuseStateだけで管理し続けるのはもう限界ではないか?」という、全Reactエンジニアが一度は直面する課題への処方箋でした。 プレゼンテーションの様子(写真提供:GitNation) 以下、気になったポイントをまとめます。 useState の積み重ねが招く「Rube Goldbergマシン」 簡単な状態管理には useState は最適です。しかし、アプリが成長するにつれて useState が10個、20個と並び、それらが相互に影響し合う「Rube Goldbergマシン(無駄に複雑な仕掛け)」のようなコードになってしまいます。この指摘には、深く頷かされるものがありました。特に「Boolean explosion(真偽値の爆発)」によって、あり得ない状態(例えば、読み込み中かつエラー中など)が理論上発生できてしまう設計の危うさを再確認しました。 「どこに保存するか」よりも「どうモデリングするか」 Davidさんは、状態をメモリやURLのどこに置くかという「ストレージ」の議論の前に、まず「アプリケーションのロジック(状態遷移)」を正しくモデリングすべきだと説いています。 単に値をセットするのではなく、「イベント(何が起きたか)」に基づいて「次の状態(どう変わるか)」を決定する宣言的な設計手法は、コードの予測可能性を劇的に高めます。この「イベント駆動」の考え方は、フロントエンドだけでなくシステム設計全般に役立つ視点だと感じました。 React 19とサーバーコンポーネントによる簡素化 React 19での新しいフック( useActionState など)や、Server Components、URLパラメータの活用により、手動で useState を管理するシーンはさらに減っていきます。そのような展望が示されました。 「状態を同期するために useEffect を頑張って書く」のではなく、プラットフォームやフレームワークが提供するプリミティブを使いこなします。ロジックをシンプルに保つ手法が今後のモダンな開発のスタンダードになると感じました。 まとめ セッションのタイトルこそ刺激的ですが、その本質は「Reactの基本機能に頼りすぎるあまり、設計を疎かにしていないか?」という問いかけでした。 AIがコードを生成してくれる時代だからこそ、人間は「あり得ない状態を排除する堅牢なモデル」を設計する能力を磨くことが必要だと感じました。 我々も、目の前の機能実装だけでなく、数年後もメンテナンス可能な「シンプルで美しい状態管理」を追求していきたいと強く思いました。 Workshop JSNation、React Summitでは参加者は現地セッションの他に、オンラインでワークショップを受講できます。その中で私は下記の2つを受講したので紹介します。 The React Developer's Guide to AI Engineering gitnation.com このワークショップでは、Reactエンジニアが「AI機能をただ呼び出すだけ」の段階を超えて、いかにAIをアプリケーションのコアロジックに組み込み、高度なユーザー体験を構築するかを実践的に学びました。 オンラインワークショップのスクリーンショット LLMを「関数」として扱う設計思考 AIを単なるチャットUIとして捉えるのではなく、構造化されたデータ(JSON)を返す「信頼できる関数」として定義する手法が中心でした。Zodなどのスキーマバリデーションを組み合わせ、LLMの出力を型安全に扱います。フロントエンドのUIコンポーネントとAIをシームレスに結合させる設計が非常に参考になりました。 ストリーミングUIとRAGの実装 LLMの長い生成時間をユーザーに感じさせないための「ストリーミングレスポンス」の実装を体験しました。また、独自のデータソースをAIに参照させるRAG(Retrieval-Augmented Generation)の基本構成もハンズオン形式で学びました。Vercel AI SDKなどを用いた実装は、すぐにプロダクトに応用できる内容でした。 まとめ AIエンジニアリングの本質は「プロンプトの調整」だけではなく、AIを既存のソフトウェアアーキテクチャの中にいかに「疎結合」に配置し、観測可能性を確保するかにあると強く感じました。Reactが得意とする宣言的なUIとAIを掛け合わせることで、Webアプリの可能性がさらに広がることを実感できたワークショップでした。 Debugging with Sentry AI using Seer, MCP, and Agent Monitoring gitnation.com このワークショップでは、エラー監視ツールの定番であるSentryが、AIやMCPを活用してデバッグ作業を自動化・効率化する方法を深掘りしました。 オンラインワークショップのスクリーンショット 自社開発AI「Seer」によるエラー解析の自動化 Sentryが開発した独自のAIサービス「Seer」の仕組みを学びました。Seerは、発生したエラーを過去の類似問題やスタックトレース、ソースコードの文脈と照らし合わせ、単なるエラー通知を超えた「根本原因の推測」と「修正案の提示」を自動で行います。人間がログを読み解く時間を大幅に短縮できる可能性を強く感じました。 MCPによるエコシステムの連携 最近注目されているMCPについても触れられていました。MCPを介してSentryのデータとAIエージェントを接続することで、エージェントが自律的にエラー情報を取得し、デバッグの調査を代行するデモが行われました。開発者がダッシュボードを確認しに行くのではなく、AIが自ら問題を調査して報告してくれる未来が現実味を帯びていました。 まとめ AIによるデバッグ支援は「便利」な段階から「不可欠」なインフラへと進化していると実感しました。特に大規模なアプリケーション(ZOZOMETRYなど)では、膨大なエラーログから真に重要な問題を特定するのは困難です。こうしたAIツールを適切に監視フローへ組み込むことで、サービス品質の向上と開発者のメンタル負荷軽減を両立できると確信しました。 上記2つは個人で受講したのですが、この他のワークショップをチームメンバーにシェアして取り組んでみようと計画中です。 イベント全体を通じて感じたこと 実は昨年も同イベントに参加したのですが、1年で大きく変わったように感じました。まず、今回のセッションの内容は予想通りAI関係のものが大幅に増えていました。さらに、内容もシニア向けにフォーカスしたものが増えた印象でした。 セッションの中でも「AIの導入により54%のリーダーがジュニアレベルの採用を減らすことを検討している」という発表がありました。実際、去年は求職中の卒業生やジュニアレベルのエンジニアなど様々な人がいました。一方、今回会話した人々は全員経験のあるエンジニアでした(たまたまなのかもしれませんが)。AIの台頭により我々エンジニアを取り巻く環境は大きく変わりつつあることを実感しました。 昨年は「世界のエンジニアと交流でき、技術知識をアップデートできた! モチベーションが上がった! 楽しい!」という感想が主でした。今回はそれだけでなく、エンジニアとしてこれからの社会を生きていく上で気持ちが引き締まり、去年とはまた違った方向性でモチベーションが上がりました。ワクワクするような新しい技術がこれからもたくさん出てくるでしょう。その中でも楽しみながらアップデートして行きたいと改めて感じたカンファレンスでした。 「AIはあくまで『掛け算』だ。重要なのは、そこに何を掛けるかだ」— Addy Osmani(写真提供:GitNation) 最後に 最後に、ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください! corp.zozo.com
アバター
.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(2) { display: none; } はじめに こんにちは。SRE部会員ID基盤SREブロックの田中です。 ZOZOではマイクロサービスの増加に伴い、昨今高度化するサイバー攻撃に対応しつつ、各システム間のセキュリティとその統一性を維持するための仕組みが求められていました。なかでも、認可基盤の統一は長年にわたる重要な課題のひとつでした。 そこで今回、Istioを活用することで認可機能をアプリケーションから切り離し、ZOZOTOWN共通の認可基盤を実装する方針を採用することにしました。 本記事では、IstioのAuthorizationPolicy機能を利用した認可制御の実装方法について紹介します。 目次 はじめに 目次 Istioとは 課題 Istioを選定した理由 AuthorizationPolicyによる認可制御 AuthorizationPolicyの基本構造 metadata selector(適用対象) action(動作) rules(認可ルール) ヘッダーを用いた認可の動作検証 正しくヘッダーを付与した場合 ヘッダーを付与せずにリクエストした場合 dry-runをfalseにした場合 導入時の注意点 istio-proxyの負荷上昇 段階的なロールアウト dry-runモードの活用 導入により得られた効果 まとめ Istioとは Istio は、分散システムやマイクロサービス間の通信を一元管理するために設計されたオープンソースのサービスメッシュです。各サービスにサイドカーとしてEnvoy Proxyを配置し、サービス間のトラフィックを透過的に制御します。 Istioを利用することで、以下のような機能を実現できます。 トラフィック管理(ルーティング、ロードバランシング) 可観測性(メトリクス、トレーシング、ロギング) セキュリティ(認証・認可、mTLS) 課題 ZOZOTOWN基盤ではマイクロサービス化の進行により、各アプリケーションが独自に認可処理を実装していましたが、徐々に以下のような課題が顕在化していきました。 課題 影響 アプリケーションごとにポリシーや実装方式が異なる 統一性が欠如し、セキュリティホールが生まれやすい。レビュー負荷の上昇 セキュリティ要件の変更時にすべてのアプリケーションへ個別対応が必要 改修コストとリリース速度への影響 これらの課題を解決するため、アプリケーションごとに認可機能を実装する方針から、Istioサービスメッシュによる統一的な制御を実現する方針に転換することを決定しました。 Istioを選定した理由 Istioを選定した主な理由は以下の通りです。 アプリケーションコードの変更が不要 : Envoy Proxyによる透過的な通信制御により、既存アプリケーションへの影響を最小化 言語・フレームワーク非依存 : Go、Java等の技術スタックに関係なく統一したポリシーを適用可能 宣言的な設定管理 : Kubernetesネイティブなリソースとして管理でき、GitOpsとの親和性が高い 豊富な認証・認可機能 : JWTの検証、mTLS、きめ細かなアクセス制御を標準機能として提供 AuthorizationPolicyによる認可制御 Istioの認可ポリシーは AuthorizationPolicy というリソースによって定義します。定義された認可ポリシーは各Envoy Proxyの認可エンジンによって評価され、リクエストに対してALLOW・DENYのどちらかを決定します。 AuthorizationPolicyはクラスタ全体や特定のNamespace・Podに対して設定が可能です。認可ポリシーはリクエストの属性(例:ヘッダー、JWTクレーム、メソッド、パス、宛先サービスなど)を条件として指定でき、ALLOW・DENYを決めるルールを柔軟に定義できます。 AuthorizationPolicyの基本構造 AuthorizationPolicyの基本構造をサンプルコードをもとに確認します。以下は検証用のnginx Podを対象とした認可制御を適用するAuthorizationPolicyの例です。 apiVersion : security.istio.io/v1 kind : AuthorizationPolicy metadata : name : nginx-test-authz namespace : authz-policy-test annotations : istio.io/dry-run : "true" spec : selector : matchLabels : app : nginx-test action : ALLOW rules : - to : - operation : methods : - "GET" paths : - "/*" when : - key : request.headers[x-test-token] values : - "allow-me" AuthorizationPolicyは大きく分けてmetadataと3つのセクション(selector、action、rules)で構成されています。 metadata 作成するAuthorizationPolicyの名前、作成するNamespace、dry-runモードのオン・オフを設定します。 dry-runモードを有効にした場合、認可ポリシーによる評価は行われますが、実際のアクションは実行されません。 selector(適用対象) AuthorizationPolicyは3つのスコープで適用できます。 (1) Workload単位(最も一般的) spec : selector : matchLabels : app : myapp (2) Namespace全体 metadata : namespace : default spec : {} (3) メッシュ全体(istio-system) metadata : namespace : istio-system spec : {} action(動作) アクション 説明 ALLOW 条件に一致した通信を許可 DENY 条件に一致した通信を拒否 AUDIT 条件に一致した通信を許可しつつ、ログに記録(検証用) AUDIT actionとdry-runモードは役割が似ており混同しやすいですが、利用目的が異なります。 前者はリクエストが認可エンジンにより評価されたかどうかの証跡・可視化を、後者はどのActionの対象として評価されたかの確認を目的としています。 どちらも実際の通信への影響はありません。 rules(認可ルール) rulesは from 、 to 、 when の3つの要素で構成されます。 from(リクエスト元の制御) 今回のサンプルコードにはありませんが、例えばService Accountを用いてリクエスト元を制御できます。 rules : - from : - source : principals : [ "cluster.local/ns/default/sa/sample-app" ] to(リクエスト先の制御) - to : - operation : methods : - "GET" paths : - "/*" 主な条件として以下が指定できます。 条件 説明 methods HTTPメソッド(GET、POSTなど) paths リクエストパス ports ポート番号 hosts ホスト名 when(追加条件) when : - key : request.headers[x-test-token] values : - "allow-me" 使用できるキーの例は以下の通りです。 キー 説明 request.headers[...] リクエストヘッダーの値 request.auth.claims[...] JWTのクレーム値 source.ip リクエスト元IPアドレス その他の制御条件については 公式ドキュメント を参照ください。 ヘッダーを用いた認可の動作検証 サンプルコードを用いて実際にAuthorizationPolicyを作成し、ログを確認しながら認可の様子を確認します。 前提として、nginx Podがすでに作成されており、サービスメッシュを通して通信ができる状態とします。 今回はヘッダーを用いた認可を検証します。dry-runモードを有効化することでリクエストに影響を与えず認可結果を確認できます。 先ほどのサンプルコードをapplyした後、以下のリクエストを実行します。 正しくヘッダーを付与した場合 $ curl -I -H " x-test-token: allow-me " http://nginx-test.authz-policy-test.svc.cluster. local HTTP/ 1 . 1 200 OK server: istio-envoy ... Istioのログを確認すると、リクエストが許可(shadow allowed)されていることがわかります。 2025-12-31T03:37:41.966028Z debug envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:166 shadow allowed, matched policy ns[authz-policy-test]-policy[nginx-test-authz]-rule[0] thread=35 ヘッダーを付与せずにリクエストした場合 $ curl -I http://nginx-test.authz-policy-test.svc.cluster. local HTTP/ 1 . 1 200 OK server: istio-envoy ... dry-runモードのためリクエストは成功していますが、Istioのログを確認すると、リクエストが拒否されるものと評価されていることがわかります(shadow denied)。 2025-12-31T03:38:01.890287Z debug envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:173 shadow denied, matched policy none thread=34 dry-runをfalseにした場合 次にdry-runをfalseに変更して、実際の挙動を確認します。 正しくヘッダーを付与した場合 $ curl -I -H " x-test-token: allow-me " http://nginx-test.authz-policy-test.svc.cluster. local HTTP/ 1 . 1 200 OK server: istio-envoy ... ヘッダーを付与せずにリクエストした場合 $ curl -I http://nginx-test.authz-policy-test.svc.cluster. local HTTP/ 1 . 1 403 Forbidden ... 事前にdry-runモードで確認したように、想定通りリクエストが拒否されました。 このように、AuthorizationPolicyを用いることでIstioを介したリクエストの認可制御を行うことができます。 導入時の注意点 ここで、私たちがAuthorizationPolicyを本番環境に導入する際、注意した点を紹介します。 istio-proxyの負荷上昇 認可処理を追加することでistio-proxyの負荷が上昇します。負荷試験等を実施し、上昇値が許容範囲かどうかあらかじめ確認しておくことを推奨します。 AuthorizationPolicyの認可方法によって上昇率が異なりますが、CPU使用量が1.2〜1.5倍まで上昇したケースも見られました。 段階的なロールアウト 対象ワークロード向けの全リクエストに対して最初から認可処理を実行するのは、本来許可すべきリクエストまでブロックされてしまうリスクがあります。 ZOZOでは Flagger を用いた手動カナリアリリースを実施しました。手動カナリアリリースの詳細については以下の過去ブログから参照できます。 techblog.zozo.com 上記に加え、AuthorizationPolicyのselectorによる認可対象コントロールを用いた下記の手順でリリースを実施しました。 カナリアPodに専用ラベルを付与し、selectorに設定することで独立した認可対象とする 段階的にカナリアPodへ向けるリクエストのパーセンテージを増加する 特定の段階までリクエストパーセンテージを増加させ意図しないブロックなどが行われていないことを確認する selectorをプライマリーPodに付与されているラベルへ変更し認可対象を切り替える 段階的に認可制御を適用することで、リスクを最小限に抑えたAuthorizationPolicyの本番導入が可能です。 dry-runモードの活用 本番導入前にdry-runモードを活用することで、実際のリクエストに影響を与えずに認可ポリシーの動作を確認できます。 Istioのログを分析し、想定通りの認可判定が行われているかを事前に確認することを推奨します。 導入により得られた効果 Istio AuthorizationPolicyを導入することにより、以下の効果が得られました。 効果 詳細 認可ロジックの一元化 アプリケーションコードから認可処理を分離し、統一的な管理が可能に セキュリティ要件変更時の対応工数を削減 AuthorizationPolicyの変更のみで全サービスに適用可能 開発チームの負担軽減 各アプリケーションで認可機能を実装・保守する必要がなくなった キャッチアップ工数の削減 認可処理が統一されたことにより、キャッチアップが必要なリソースが単一化された まとめ 本記事では、IstioのAuthorizationPolicyを活用したマイクロサービスの認可基盤の構築について紹介しました。 AuthorizationPolicyを導入することで、アプリケーションコードを変更せずに統一的なセキュリティポリシーを適用でき、認可処理の一元化を実現できました。また、宣言的な設定管理により、セキュリティ要件の変更にも柔軟に対応できるようになりました。 マイクロサービス環境での認可基盤の統一を検討している方がいれば、ぜひ参考にしてみてください。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 hrmos.co corp.zozo.com
アバター