サイオステクノロジー(Tech.Lab)のブログ - TECH PLAY

TECH PLAY

サイオステクノロジー(Tech.Lab)

サイオステクノロジー(Tech.Lab) の技術ブログ

全737件

はじめに ども!Claude Codeにべったりな龍ちゃんです。 前回の記事「Claude Codeの一時ファイルで爆速ビジネスロジック検証:UI不要で要件を発見する方法」 で、tmpスクリプトが「自然言語から生まれた純粋な要件」であることを説明しました。 本記事では、 実際にどうやってtmpスクリプトを観察し、ビジネスロジックを抽出し、CLI機能として昇格させるか を、具体的なコード例とともに解説します。 TL;DR この記事で分かること: tmpディレクトリのプロジェクト内設定方法 デフォルトの問題点(OSの/tmpに生成される) CLAUDE.mdでの設定方法 tools/配下に配置するメリット プロジェクト構成とtmpの配置 4層構造の実装(公式SDK → 自作Client → CLI → Skill) tmpスクリプトがCLIツールを活用する仕組み 実装されたCLIコマンド一覧 posts, categories, hashtags, scheduled-posts, validate操作 Skillsで提供される機能 vs tmpで補完される機能 tmpスクリプトの具体例 ハッシュタグチェック、データクリーニングの実装 DRY RUNモードによる安全性確保 ビジネスロジック抽出の実装手順 tmpから本体コードへの昇格プロセス Operations層、CLI層、Skill層の実装 週次レビューの実践 頻出パターンの分析コマンド docs/research/への記録方法 環境セットアップ Python/uv環境の構築 TypeScript環境の構築 型ヒントによる品質向上 重要な前提条件: 「Claude Code: 公式MCPを補完するSkills設計パターン」と前回の記事を読んでいる Claude Code Skillsの基本を理解している こんな人に読んでほしい 前回の記事を読んで「実際にどう実装するの?」が気になった人 tmpスクリプトの観察を始めたい人 ビジネスロジック抽出の具体的な手順を知りたい人 tmpからCLI機能への昇格プロセスを学びたい人 環境セットアップ(Python/uv, TypeScript)の方法を知りたい人 tmpディレクトリのプロジェクト内設定 デフォルトの問題点 Claude Codeは設定なしだとOSの /tmp ディレクトリにスクリプトを生成してしまいます。これだと: リポジトリ外なので管理しにくい OSの再起動で消える可能性がある 他の開発者と共有できない 履歴が残らない 解決策:リポジトリ内tmpの設定 # tmpディレクトリを作成 mkdir -p application/tools/tmp # .gitignoreでtmpの中身を除外(任意) echo "application/tools/tmp/*.py" >> .gitignore # または、観察目的で意図的にコミットする場合は除外しない CLAUDE.mdに指示を追加 リポジトリルートの CLAUDE.md に以下を追加: ## Temporary Scripts Directory **IMPORTANT**: Always save temporary Python scripts to `application/tools/tmp/` directory. When generating temporary scripts: 1. Place them in `application/tools/tmp/` 2. Use descriptive filenames 3. Include docstrings Example: ```python # application/tools/tmp/validate_data_integrity.py """Temporary script to validate data integrity.""" ``` この設定のメリット tmpスクリプトがリポジトリ内で管理される tools/配下なので同じ依存関係(pyproject.toml)を使える チーム全体で観察可能 gitで履歴管理できる(必要に応じて) プロジェクト構成とtmpの配置 ディレクトリ構造 実際のプロジェクト構成を見てみましょう: application/tools/ ├── src/ │ └── supabase_client/ # 自作Client(ビジネスロジック) │ ├── models.py # データモデル定義 │ ├── operations/ # テーブル操作クラス │ │ ├── posts.py │ │ ├── categories.py │ │ ├── hashtags.py │ │ └── ... │ └── cli.py # CLIコマンド実装 ├── tmp/ # tmpスクリプト(ここ重要!) │ ├── merge_duplicate_categories.py │ ├── validate_orphaned_posts.py │ ├── check_hashtags.py │ ├── remove_hashtags_from_posts.py │ └── ... └── pyproject.toml # 依存関係管理 なぜ application/tools/tmp/ なのか? tmpを tools/ 配下に配置することで、 tmpスクリプトがCLIツールとして実装された機能をそのまま利用できる んです。 実際のtmpスクリプトの中身を見てみると: # application/tools/tmp/check_hashtags.py import subprocess import json def get_all_post_uuids(): """Get all post UUIDs from the database.""" result = subprocess.run( ["uv", "run", "db", "posts", "list", "--limit", "1000"], # ← CLIコマンドを呼び出す capture_output=True, text=True, check=True, ) # Extract UUIDs from output # ... def get_post_details(uuid): """Get post details by UUID.""" result = subprocess.run( ["uv", "run", "db", "posts", "get", uuid], # ← CLIコマンドを呼び出す capture_output=True, text=True, check=True, ) return json.loads(result.stdout) この構造の利点 tmpスクリプトがCLIツール( uv run db コマンド)を組み合わせて使える 依存関係(pyproject.toml)を共有 Claude Codeが生成するスクリプトと本体コードが同じ環境で動作 tmpで検証したロジックをOperations層に抽出しやすい 2つのアプローチ 実際には、tmpスクリプトの実装方法には2つのパターンがあります: 1. CLIコマンド経由 (現在の実装): subprocess.run(["uv", "run", "db", ...]) でCLIを呼び出す 基本操作を組み合わせて複雑な処理を実現 Claude Codeが自然に生成するパターン 2. 直接インポート (より進んだ段階): from src.supabase_client import SupabaseClient で直接インポート Operations層のメソッドを直接呼び出す より複雑なロジックを実装する際に有用 つまり、tmpスクリプトは「使い捨て」じゃなくて、 プロジェクトの一部として機能している んです。 これが、tmpスクリプトから本体機能への昇格がスムーズにできる理由でもあります。tmpスクリプトで検証したロジックを、そのままOperations層( src/supabase_client/operations/ )に移せばいいだけですから。 実装されたCLIコマンド一覧 理解を助けるために、実際に実装されているCLIコマンドを紹介します。tmpスクリプトは、これらのコマンドを組み合わせて複雑な処理を実現します。 投稿(posts)操作 # 投稿一覧取得 uv run db posts list [--limit N] [--blog-url URL] [--pattern N] [--category NAME] [--hashtag NAME] # 投稿詳細取得 uv run db posts get <UUID> # 投稿作成 uv run db posts create --blog-url <URL> --pattern <1-3> --content <TEXT> # 投稿更新 uv run db posts update <UUID> [--content TEXT] [--pattern N] # 投稿削除 uv run db posts delete <UUID> # ブログURL一覧 uv run db posts urls # 統計情報 uv run db posts stats カテゴリ(categories)操作 # カテゴリ一覧 uv run db categories list # カテゴリ作成 uv run db categories create <NAME> # 統計情報 uv run db categories stats # 重複検出 uv run db categories find-duplicates # カテゴリマージ uv run db categories merge --to <ID> --from <IDs> [--dry-run] ハッシュタグ(hashtags)操作 # ハッシュタグ一覧 uv run db hashtags list # ハッシュタグ作成 uv run db hashtags create <NAME> # 統計情報 uv run db hashtags stats # 重複検出 uv run db hashtags find-duplicates # ハッシュタグマージ uv run db hashtags merge --to <ID> --from <IDs> [--dry-run] 予約投稿(scheduled-posts)操作 # 予約投稿一覧 uv run db scheduled-posts list [--status <STATUS>] [--today] [--limit N] # 予約投稿詳細 uv run db scheduled-posts get <UUID> # 予約投稿作成 uv run db scheduled-posts create --text <TEXT> --scheduled-date <ISO8601> [--source-post-uuid <UUID>] # 予約投稿更新 uv run db scheduled-posts update <UUID> [--status STATUS] [--scheduled-date DATE] # 予約投稿削除 uv run db scheduled-posts delete <UUID> データ検証(validate)操作 # 孤立レコード検出 uv run db validate orphaned Skillsで提供される機能の特徴 基本CRUD操作 : 単一レコードの作成・取得・更新・削除 フィルタ機能 : カテゴリ、ハッシュタグ、ブログURLでの絞り込み 統計情報 : 投稿数のカウント、使用状況の可視化 データクリーニング : 重複検出、孤立レコード検出、マージ機能 Skillsで提供されていない機能(→tmpスクリプトで補完) 複雑な組み合わせ処理 : 複数コマンドの連鎖実行 カスタムフィルタロジック : 独自の条件での抽出 一括更新処理 : 条件に基づいた複数レコードの更新 クロスチェック : 複数テーブル横断での整合性チェック この「基本操作」と「組み合わせ処理」の境界が、tmpスクリプトが生成される理由です。 tmpスクリプトの具体例 理論だけでは分かりにくいので、実際のリポジトリにあるtmp/スクリプトを紹介します。 例1: ハッシュタグチェックスクリプト 生成された背景 : 「全投稿のハッシュタグの有無を確認したい」というリクエスト # application/tools/tmp/check_hashtags.py import subprocess import json import re def get_all_post_uuids(): result = subprocess.run( ["uv", "run", "db", "posts", "list", "--limit", "1000"], capture_output=True, text=True, check=True, ) # UUIDを正規表現で抽出 uuids = [] for line in result.stdout.split('\n'): uuid_match = re.search(r'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', line) if uuid_match: uuids.append(uuid_match.group(1)) return uuids def has_hashtag(text): return bool(re.search(r'#\w+', text)) # メイン処理: 全投稿をチェックして分類 for uuid in get_all_post_uuids(): post = get_post_details(uuid) if has_hashtag(post['content_text']): with_hashtags.append(post) このスクリプトから分かること : 基本操作の組み合わせ : db posts list → 処理 → db posts get ビジネスルール : 「ハッシュタグあり」= # で始まる単語が含まれる CLIツールの活用 : subprocess.run() でCLIコマンドを呼び出す tmpスクリプトは CLIツールを組み合わせて複雑な処理を実現 していました。 例2: ハッシュタグ削除スクリプト 生成された背景 : 「投稿本文からハッシュタグを削除したい」(データクリーニング要求) # application/tools/tmp/remove_hashtags_from_posts.py import re import subprocess import sys def remove_hashtags(text): # 日本語対応の正規表現でハッシュタグを削除 cleaned = re.sub(r'#[\w\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]+', '', text) # Clean up multiple spaces cleaned = re.sub(r'\s+', ' ', cleaned) # Remove trailing/leading whitespace cleaned = cleaned.strip() return cleaned # DRY RUNモードで安全確認 execute = '--execute' in sys.argv for post in posts_with_hashtags: new_content = remove_hashtags(post['content_text']) if execute: subprocess.run([ "uv", "run", "db", "posts", "update", post['uuid'], "--content", new_content ]) else: print(f"DRY RUN: Would update {post['uuid']}") print(f" OLD: {post['content_text'][:100]}...") print(f" NEW: {new_content[:100]}...") このスクリプトから分かること : ビジネスルールの具体化 : ハッシュタグ削除の正規表現(日本語対応) 安全性の配慮 : --execute フラグによるDRY RUNモード 一括処理パターン : 取得 → 加工 → 更新 重要な発見 : 同じロジック( remove_hashtags() )が複数のスクリプトで再利用される → これこそ抽出すべきビジネスロジック ビジネスロジック抽出の実装手順 頻出パターンの観察 簡単な分析で、何が必要な機能かが見えてきました: # tmp/内のスクリプトを名前でカウント ls application/tools/tmp/*.py | xargs -n1 basename | sort | uniq -c | sort -nr 結果 : 5 validate_orphaned_posts.py 4 bulk_update_scheduled_posts.py 3 merge_duplicate_categories.py 2 analyze_hashtag_stats.py 1 export_posts_csv.py 解釈 : 5回生成 → 週1回以上使う → Skillに組み込むべき 3-4回 → 定期的に使う → Skill機能追加候補 1-2回 → tmpのままで良い(稀なケース) データが教えてくれました。「validate_orphaned_posts.pyは必要な機能だ」と。 ビジネスロジックの抽出事例 事例1: validate orphaned → Skill機能化 tmpの内容 (5回生成): # tmp/validate_orphaned_posts.py の処理フロー 1. uv run db posts list(全投稿取得) 2. カテゴリ未設定の投稿をフィルタ 3. 結果をリスト表示 発見 : 孤立投稿の検証は定期的に行う作業(週1回以上) ロジックが毎回同じ ビジネスルールが固まっている 「これ、毎回tmpスクリプト生成するの無駄じゃない?Skillに組み込もう」 Skillへの追加(Phase 3実装) : # 新しいコマンドとして実装 uv run db validate orphaned # 出力例 Orphaned Scheduled Posts: 0 Unused Categories: 2 ID: 106, Name: 本番環境構築 ID: 66, Name: フロントエンド Unused Hashtags: 1 ID: 34, Name: #TEST Total issues found: 3 → tmpスクリプトがSkillの本体機能に昇格 これで、次回から「孤立投稿を検証して」と依頼すると、tmpスクリプトを生成せず、直接Skill機能が実行されるようになりました。 事例2: tmpからOperations層への抽出 tmpスクリプトから抽出されたビジネスロジックが、どのように本体コードに統合されるかを見てみましょう。 tmpスクリプトで見つかったパターン → 抽出された実装 : # application/tools/src/supabase_client/operations/hashtags.py """Hashtags table operations""" from supabase import Client from ..models import Hashtag class HashtagOperations: def __init__(self, client: Client): self.client = client self.table = "hashtags" def list(self, limit: int = 100, offset: int = 0) -> list[Hashtag]: result = ( self.client.table(self.table) .select("*") .order("hashtag_name") .range(offset, offset + limit - 1) .execute() ) return [Hashtag(**item) for item in result.data] def count_posts_by_hashtag(self) -> list[dict]: """ Count posts for each hashtag Returns: [ {"hashtag_id": 1, "hashtag_name": "#AI", "post_count": 15}, ... ] """ result = ( self.client.table("post_hashtags") .select("hashtag_id, hashtags(hashtag_name)") .execute() ) # Count posts per hashtag hashtag_counts: dict[int, dict] = {} for item in result.data: tag_id = item["hashtag_id"] tag_name = item["hashtags"]["hashtag_name"] if item.get("hashtags") else None if tag_id not in hashtag_counts: hashtag_counts[tag_id] = { "hashtag_id": tag_id, "hashtag_name": tag_name, "post_count": 0, } hashtag_counts[tag_id]["post_count"] += 1 # Sort by post count descending return sorted( hashtag_counts.values(), key=lambda x: x["post_count"], reverse=True, ) CLIコマンドとしての公開 : # application/tools/src/supabase_client/cli.py import click from .client import SupabaseClient from .operations import HashtagOperations @cli.group() def hashtags(): """Hashtag operations""" pass @hashtags.command("list") @click.option("--limit", type=int, default=10) def hashtags_list(limit: int): client = SupabaseClient() ops = HashtagOperations(client.get_client()) results = ops.list(limit=limit) for hashtag in results: click.echo(f"ID: {hashtag.hashtag_id}, Name: {hashtag.hashtag_name}") @hashtags.command("stats") def hashtags_stats(): """Show hashtag usage statistics""" client = SupabaseClient() ops = HashtagOperations(client.get_client()) stats = ops.count_posts_by_hashtag() click.echo("Hashtag Usage Statistics:") for item in stats: click.echo(f" {item['hashtag_name']}: {item['post_count']} posts") tmpスクリプト → 本体実装の流れ : tmp/check_hashtags.py : ハッシュタグの集計ロジックを発見 Operations層 : count_posts_by_hashtag() としてビジネスロジック化 CLI層 : db hashtags stats コマンドとして公開 Skill層 : Claude Codeから自然言語で操作可能に この4層構造により、tmpスクリプトで検証したロジックが、段階的に本体機能へ昇格していきます。 事例3: 段階的な拡張プロセス tmp観察を続けることで、自然に機能が拡張されていきました: Phase 1(初期実装) : 基本CRUDのみ list, get, create, update, delete tmp観察(1週間) : カテゴリ・ハッシュタグでのフィルタ要求が頻出 統計情報の要求も多い Phase 2(拡張・4時間) : 複雑なクエリ追加 --category , --hashtag フィルタオプション stats コマンド(集計) 中間テーブル操作(add-categories, add-hashtags) tmp観察(さらに1週間) : merge系スクリプトが頻出 データクリーニングの需要が明確に Phase 3(さらに拡張・3時間) : データクリーニング機能 find-duplicates コマンド merge --dry-run コマンド validate orphaned コマンド → tmpの観察が、段階的な機能拡張を自然に導いた 合計開発時間 : 15時間(8 + 4 + 3)でデータベース管理システムが完成(実測例として参考) 週次レビューの実践 頻出パターンの分析 私は毎週金曜日、こんな習慣をつけました: # 頻繁に生成されるスクリプト ls application/tools/tmp/*.py | xargs -n1 basename | sort | uniq -c | sort -nr 観察ポイント : 3回以上生成 → Skill機能追加候補 毎回同じロジック → ビジネスルールが固まっている 微妙に違うパラメータ → オプション化すべき docs/research/への記録 tmp観察ログの例(実際に記録していたもの): # tmp観察ログ: 2024-11-15 ## 今週の傾向 - validate_orphaned_posts.py: 5回生成(先週3回) - merge_duplicate_categories.py: 3回生成(新規) - bulk_update_scheduled_posts.py: 4回生成(継続) ## 発見 - 孤立投稿の検証は定期作業(毎日1回) - カテゴリ重複チェックの需要が増加 - 一括更新のロジックが固まってきた ## ビジネスルール(tmpから抽出) - 重複判定: 類似度90%以上 - マージ優先順位: 投稿数 > 作成日時 > UUID - 孤立投稿: カテゴリ未設定 or ハッシュタグ0個 ## アクション - [ ] db validate orphaned コマンド追加 - [ ] db categories find-duplicates コマンド追加 - [ ] db categories merge --dry-run 実装 この記録が、Phase 2-3の機能拡張につながりました。 UIを作るタイミングの判断 UIが必要になる条件 : 利用者が複数(チームや社内共有) ビジネスロジックが固まっている tmpスクリプトの生成が停止(安定稼働) UIを作る前に確認すること : tmpの観察を2-4週間継続 ビジネスルールが変わらないことを確認 操作フローがtmpから明確に見える 私の場合、まだUIは作っていません。Claude Codeで十分だからです(個人利用のため)。 環境セットアップ tmpスクリプトの品質を高めるために、Claude Codeがコードを書きやすい環境を整えましょう。 Python環境のセットアップ(推奨) なぜPythonなのか : Claude Codeが最も得意な言語 データ処理・バッチ処理に適している スクリプト実行が簡単( uv run python tmp/script.py ) uv環境のセットアップ : # uvのインストール curl -LsSf https://astral.sh/uv/install.sh | sh # プロジェクト初期化と依存関係追加 uv init uv add click pandas requests # tmpスクリプトを実行 uv run python application/tools/tmp/script.py uvのメリット : pipの10-100倍速い / 仮想環境自動管理 / Python自動インストール 詳細な環境構築 : uvで解決!Pythonモノレポの依存関係管理【2025年版】 を参照 TypeScript環境のセットアップ TypeScript/JavaScriptが適しているケース : Node.js環境でのスクリプトに適している フロントエンド・API連携処理に向く # TypeScript環境のセットアップ npm init -y npm install -D typescript ts-node @types/node npx tsc --init # tmpスクリプト用の設定 # tsconfig.json { "compilerOptions": { "target": "ES2022", "module": "commonjs", "outDir": "./dist", "rootDir": "./", "strict": true, "esModuleInterop": true }, "include": ["application/tools/tmp/**/*"] } # tmpスクリプトを実行 npx ts-node application/tools/tmp/script.ts 避けるべき言語 シェルスクリプト(bash): Claude Codeが複雑なロジックを書きにくい Ruby, Perl: サポートは良いが、Pythonの方が安定 Java: セットアップが重く、tmpスクリプトには不向き CLAUDE.mdに言語設定を追加 ## Development Language Preferences **Primary Language: Python 3.12+** - Use `uv run python tmp/script.py` for execution - Leverage type hints for better code generation **Avoid**: Shell scripts for complex logic (use Python subprocess instead) 型ヒントを使う理由 Claude Codeは型情報があると、より高品質なコードを生成します。 # ❌ 型ヒントなし(Claude Codeが推測する必要がある) def get_posts(limit): return client.posts.list(limit=limit) # ✅ 型ヒント付き(Claude Codeが正確に理解) def get_posts(limit: int) -> list[Post]: return client.posts.list(limit=limit) このセットアップのメリット : tmpスクリプトの生成品質が向上 エラーが減り、実行成功率が上がる tmpから本体コードへの移行がスムーズ 型情報により、ビジネスロジックが明確になる tmpスクリプトを保存する習慣 基本方針 Claude Codeが生成したスクリプトを削除しない application/tools/tmp/ に保存して観察する 実行後も残しておく(頻度分析のため) Skill拡張の判断基準 3回以上生成 → 機能追加候補 ロジック固定 → Skill化 1-2回のみ → tmpのまま保持 まとめ tmpからCLI機能への昇格プロセス この5ステップのプロセスにより、tmpスクリプトが段階的に本体機能へ昇格していきます: tmpスクリプト生成 : Claude Codeが自然言語から生成 tmp観察 : 週次レビューで頻出パターンを分析 ビジネスロジック抽出 : Operations層に実装 CLIコマンド化 : CLI層で公開( uv run db ... ) Skill登録 : 自然言語で操作可能に 実践のポイント 環境設定 : tmpディレクトリをリポジトリ内に配置 CLAUDE.mdで明示的に指示 Python/uv環境を整備 観察と分析 : 週次レビューで頻出パターンを把握 docs/research/に記録 3回以上生成 → 機能追加候補 段階的な拡張 : Phase 1: 基本CRUD Phase 2: フィルタ・集計 Phase 3: データクリーニング 次のステップ Claude Code Skills 実装ガイド でCLIツール化とSkill登録の詳細を学ぶ 前回の記事で説明した概念を、本記事の実装パターンで実践してみてください。tmpスクリプトから、あなたのプロジェクトに最適なビジネスロジックが見えてくるはずです。 参考リンク 公式ドキュメント Claude Code 公式ドキュメント Claude Code Skills 公式ガイド Python Click 公式ドキュメント – CLIツール作成 Python Type Hints 公式ガイド uv 公式ドキュメント TypeScript 公式ドキュメント Supabase Python Client シリーズ記事 Claude Code: 公式MCPを補完するSkills設計パターン 4層構造パターン(公式SDK → 自作Client → CLI → Skill) UI開発工数の大幅削減 Claude Codeの一時ファイルで爆速ビジネスロジック検証:UI不要で要件を発見する方法 tmpスクリプトの本質的発見(概念編) なぜUIなしでビジネスロジックを整備できるか 関連ブログ Claude Code Skills 実装ガイド:ローカルツールをスムーズに統合する方法 Skillの実装方法(詳細) Progressive Disclosure、トークン効率化 HTMLでブログ記事を保存してる奴、全員Markdownにしろ。AIが読みにくいでしょうが! blog-scraperの実装例(tmpから進化したツール) トークン削減の実測データ AIチャットで話すだけ!X予約投稿を完全自動化するシステム構築術 X投稿管理システムの全体像 Azure Functions + Supabaseでの予約投稿自動化 質問や感想は、コメント欄でお待ちしております! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code一時ファイルからSkillsへ:ビジネスロジック抽出の実践ガイド first appeared on SIOS Tech. Lab .
はじめに ども!Claude Codeにべったりな龍ちゃんです。 前回の記事「Claude Code: 公式MCPを補完するSkills設計パターン」 で、 公式MCPを補完するSkillsパターン を紹介し、UI開発工数を大幅削減した事例を共有しました。 特に、こんな成果を報告しました: フロントエンド開発をスキップ UI設計(3-5日)をスキップ React開発(7-10日)をスキップ 開発工数を大幅削減 (実測例として数時間〜1日で実装) でも、概要しか書いていないので: 「なぜフロントエンド開発をスキップできたの?」 「UIなしでどうやってビジネスロジックを整備したの?」 「短時間で完成した秘密は何?」 前回の記事では「Claude CodeをUIとして使った」と説明しましたが、 それだけでは本質的な理由になっていません 。 本記事では、 なぜフロントエンド開発をスキップできたのか 、その核心に迫ります。 キーワードは、 tmp/ ディレクトリに蓄積されるスクリプトです。 結論を先に言うと、tmpスクリプトは「ゴミ」ではなく、 「人間の自然言語から生まれた純粋な要件」 でした。この発見が、前回の記事で紹介した4層構造(公式SDK → 自作Client → CLI → Skill)を可能にしました。 TL;DR この記事で分かること: なぜUI開発をスキップできたのか の本質的理由 tmpスクリプトがビジネスロジックを整備してくれた Claude Codeが「人間の操作UI」として機能 UIは後回しにできる(ロジック確定後 → 手戻りなし) tmpスクリプトの再定義 従来: tmp/ = 一時的なゴミ 新発見: tmp/ = 人間の自然言語から生まれた純粋な要件の可視化 ロジックファースト検証のプロセス CLI基本実装 → Skill定義 → tmp蓄積 → tmp観察 → ビジネスロジック抽出 → CLI拡張 頻出パターンの発見(5回生成 → Skill化) ビジネスルールの具現化(類似度90%、マージ優先順位等) 実践方法 (詳細は 次回の記事「Claude Code一時ファイルからSkillsへ:ビジネスロジック抽出の実践ガイド」 で解説) 週次レビュー、docs/research/へのログ記録 3回以上生成 → Skill機能追加候補 開発工数削減の本質 AIツールによる速度向上ではない UI開発というアプローチ自体を変えたこと 重要な前提条件: Claude Code Skillsの基本を理解している 前回の記事「Claude Code: 公式MCPを補完するSkills設計パターン」を読んでいる こんな人に読んでほしい 前回の記事を読んで「なぜ短時間で完成できたのか」が気になった人 フロントエンド開発なしでビジネスロジックを整備したい人 application/tools/tmp/ にスクリプトが溜まっている人 要件が固まっていない段階で開発を始めたい人 UI設計の手戻りコストに悩んでいる人 「tmpスクリプト = ゴミ」と思って削除していた人 従来のUI先行開発の問題 まず、典型的な開発フローを見てみましょう: 要件定義 → UI設計 → フロントエンド実装 → バックエンド実装 根本的な問題 : ビジネスロジックが固まっていない段階でUIに投資している 例えば、こんなシーンを想像してください: 要求:「重複カテゴリをマージできる画面が欲しい」 ↓ UI設計開始: ボタン配置、確認ダイアログ、進捗表示... ↓ 実装開始 ↓ 「あれ、重複の定義ってどうするんだっけ?」 「マージ時に投稿数が多い方を残す?古い方を残す?」 ↓ 仕様変更 → UI再設計 → 再実装 ← 高コスト! 要件変更 = 高コスト の悪循環: ビジネスロジック変更 → UIも変更が必要 小さい単位での試行錯誤ができない UI完成しないと実際の操作フローが分からない 完成後に「やっぱりこの機能いらない」と判明… 前回の記事で触れた開発工数の差(16-24日 vs 数時間〜1日)の 本質的な理由がここにあります 。 ロジックファースト検証の発見 私の開発環境と、ロジックファースト検証の起源 以前、 AIチャットで話すだけ!X予約投稿を完全自動化するシステム構築術 で紹介したX予約投稿システムは、 Firestoreベース でした。 その後、データベースを Supabaseに移行 することになりました。そして移行時に、 ビジネスロジックの変更要求 が出てきたんです。 ここで悩みました: 従来の方法: UI設計 → フロントエンド実装 → ビジネスロジック検証 ↓ 問題: ビジネスロジックが固まっていないのにUIを作る? 検証してみたら要件変更 → UI作り直し? 「先にビジネスロジックを検証できないかな…」 そこで思いついたのが、 Claude CodeをUIとして使う 方法でした。UIを作らず、CLIツール + Skillsでビジネスロジックを先に検証してしまおう、と。 前回の記事で紹介した Supabase公式MCPを補完するSkills を実装し、こんな構成になりました: CLIツール(db コマンド) ↓ Skills定義(.claude/skills/*.md) ↓ Claude Codeから操作可能 特徴 : 基本CRUD操作のみ実装(posts, categories, hashtags, series) X投稿管理システムのデータベースを操作 UIは存在しない Claude Codeが「人間の操作UI」として機能 Skill運用開始後に起きた面白いこと 基本操作は順調でした: 私: 「投稿一覧を取得して」 Claude Code: uv run db posts list を実行 成功 私: 「カテゴリ一覧も見せて」 Claude Code: uv run db categories list を実行 成功 「Skill、便利だな!基本操作は完璧!」と思っていました。 しかし、 組み合わせ処理 を依頼すると、面白いことが起きました: 私のリクエスト : 「重複カテゴリを見つけてマージして」 Claude Codeの反応 : uv run db categories list を実行 カテゴリ一覧を取得 application/tools/tmp/merge_duplicate_categories.py を生成 ← ここ! スクリプトを実行して重複を検出 uv run db categories merge を実行 観察 : Skill(db)は基本操作のみ提供していました。組み合わせ処理のために、 Claude Codeが中間処理のスクリプトをtmp/に生成 していたんです。 「あれ、tmpにスクリプトが残ってる。これ、削除すべき?」 tmpの蓄積 1週間後、tmp/ディレクトリを覗いてみると: tmp/ ├── merge_duplicate_categories.py(3回生成) ├── validate_orphaned_posts.py(5回生成) ├── analyze_hashtag_stats.py(2回生成) ├── export_posts_csv.py(1回生成) └── bulk_update_scheduled_posts.py(4回生成) 「これ、ゴミじゃなくて何か意味があるのでは?」 同じスクリプトが複数回生成されている…これは偶然じゃない。 発見:tmpは「自然言語から生まれた要件」だ tmpの本質的価値 従来の認識 : tmp/ = 一時的なゴミ 実行後は削除すべき 気にしなくて良い 新しい認識 (私の発見): tmp/ = 人間の自然言語から生まれた純粋な要件の可視化 Skillの足りない機能の証拠 ビジネスロジックの暗黙知が形になったもの 次の改善のヒント なぜ「自然言語から生まれた要件」なのか? 理由1: 実際の利用パターンの記録 従来の要件定義プロセス : 人間が考える → 要件書に書く → 「こうあるべき」 → 抽象的、実際に使われるかは不明 ロジックファースト検証のプロセス : 人間が自然言語で依頼 → Claude Codeがtmpスクリプト生成 → 「実際にこう使った」 → 具体的、実証済み、検証済み 読者への問いかけ : あなたが書いた要件書、実際に全部使われましたか? 理由2: ビジネスルールの具現化 これが一番面白い発見でした。 要件書に書いたこと(抽象的) : 重複カテゴリを検出してマージできること tmp/merge_duplicate_categories.py の中身(具体的) : def find_duplicates(categories): """重複カテゴリを検出 重複の定義: 1. name完全一致 2. name.lower()一致(大文字小文字無視) 3. 編集距離が2以下(typo考慮) """ for i, cat1 in enumerate(categories): for cat2 in categories[i+1:]: similarity = SequenceMatcher( None, cat1["name"].lower(), cat2["name"].lower() ).ratio() if similarity > 0.9: # 90%以上の類似度 duplicates.append((cat1, cat2, similarity)) return duplicates def merge_strategy(duplicates): """マージ戦略 優先順位: 1. 投稿数が多い方を残す 2. created_atが古い方を残す 3. UUIDが辞書順で小さい方を残す """ # ... 実装 ... これが「人間の純粋な要件」です : 重複の具体的な定義(類似度90%以上) マージの優先順位(投稿数 > 作成日時 > UUID) エッジケースの扱い(複数重複がある場合) → 要件書には書かれていない詳細が、tmpスクリプトには全て含まれている 私が「重複カテゴリをマージして」と自然言語で依頼した時、頭の中にあった暗黙の要件が、tmpスクリプトとして可視化されたんです。 理由3: 組み合わせパターンの発見 Skill設計時の想定 : 基本CRUD操作のみ考えていた list, get, create, update, delete tmp/が教えてくれた実際の使い方 : list → 処理 → merge(組み合わせ) list → フィルタ → validate(検証) list → 集計 → sort(分析) → 実際の組み合わせパターンが自然に記録される Skill設計時には考えていなかった使い方が、tmpに全部記録されていました。 tmpに蓄積される3種類の情報 分析してみると、tmpスクリプトには3種類の情報が含まれていました: 基本操作の組み合わせパターン どのコマンドをどの順序で実行するか 例: db categories list → 処理 → db categories merge Skillにない独自ロジック 重複検索アルゴリズム(類似度計算) クロスチェック(複数テーブル横断) 統計・集計(Top10表示) ビジネスルールの実装 「重複とは何か」の具体的な定義 「孤立とは何か」の判断基準 「有用なハッシュタグ」の評価軸 tmpからビジネスロジックを抽出する 頻出パターンの観察 簡単な分析で、何が必要な機能かが見えてきました: # tmp/内のスクリプトを名前でカウント ls application/tools/tmp/*.py | xargs -n1 basename | sort | uniq -c | sort -nr 結果 : 5 validate_orphaned_posts.py 4 bulk_update_scheduled_posts.py 3 merge_duplicate_categories.py 2 analyze_hashtag_stats.py 1 export_posts_csv.py 解釈 : 5回生成 → 週1回以上使う → Skillに組み込むべき 3-4回 → 定期的に使う → Skill機能追加候補 1-2回 → tmpのままで良い(稀なケース) データが教えてくれました。「validate_orphaned_posts.pyは必要な機能だ」と。 ビジネスロジックの抽出事例 事例1: validate orphaned → Skill機能化 tmpの内容 (5回生成): # tmp/validate_orphaned_posts.py の処理フロー 1. uv run db posts list(全投稿取得) 2. カテゴリ未設定の投稿をフィルタ 3. 結果をリスト表示 発見 : 孤立投稿の検証は定期的に行う作業(週1回以上) ロジックが毎回同じ ビジネスルールが固まっている 「これ、毎回tmpスクリプト生成するの無駄じゃない?Skillに組み込もう」 Skillへの追加(Phase 3実装) : # 新しいコマンドとして実装 uv run db validate orphaned # 出力例 Orphaned Scheduled Posts: 0 Unused Categories: 2 ID: 106, Name: 本番環境構築 ID: 66, Name: フロントエンド Unused Hashtags: 1 ID: 34, Name: #TEST Total issues found: 3 → tmpスクリプトがSkillの本体機能に昇格 これで、次回から「孤立投稿を検証して」と依頼すると、tmpスクリプトを生成せず、直接Skill機能が実行されるようになりました。 事例2: 段階的な拡張プロセス tmp観察を続けることで、自然に機能が拡張されていきました: Phase 1(初期実装) : 基本CRUDのみ list, get, create, update, delete tmp観察(1週間) : カテゴリ・ハッシュタグでのフィルタ要求が頻出 統計情報の要求も多い Phase 2(拡張) : 複雑なクエリ追加 --category , --hashtag フィルタオプション stats コマンド(集計) 中間テーブル操作(add-categories, add-hashtags) tmp観察(さらに1週間) : merge系スクリプトが頻出 データクリーニングの需要が明確に Phase 3(さらに拡張) : データクリーニング機能 find-duplicates コマンド merge --dry-run コマンド validate orphaned コマンド → tmpの観察が、段階的な機能拡張を自然に導いた 段階的な開発により : 短期間でデータベース管理システムが完成(実測例として15時間程度) UIなしでビジネスロジックを整備できる理由 従来のUI先行開発 vs ロジックファースト検証 従来の方法 : 要件書「重複カテゴリをマージできること」 ↓ UI設計: ボタン、ダイアログ、進捗表示 ↓ フロントエンド実装(React等) ↓ 実際に使ってみる ↓ 「あれ、重複の定義が曖昧だった」 「マージ条件が足りない」 ↓ 仕様変更 → UI再設計 → 再実装 ← 高コスト! 問題 : ビジネスロジックが固まっていないのにUIを作っている ロジックファースト検証 : CLIツール(基本操作のみ) ↓ 人間が自然言語で依頼「重複カテゴリをマージして」 ↓ Claude Codeがtmp/merge_duplicate_categories.py を生成 ↓ tmpスクリプトに「重複の定義」「マージ戦略」が可視化される ↓ 頻出パターンを観察(週次レビュー) ↓ ビジネスロジック抽出 ↓ CLIに機能追加(`db categories find-duplicates`) ↓ ビジネスロジック完成(UIはまだない) ↓ 必要になったらUI実装(ロジック確定済み、手戻りなし) 利点 : ビジネスロジックを先に固められる UIなしで検証・改善できる 小さく試行錯誤できる UI設計時には要件が確定している → 手戻りゼロ Claude Codeが「操作UI」として機能 重要な洞察 : UIがないのではなく、 Claude Codeが人間の操作UIになっている 従来のUI: 人間 → Webフロントエンド → バックエンド → データベース ロジックファースト: 人間 → Claude Code(自然言語UI) → CLIツール → データベース なぜ「UI不要」が成立するのか – 概念的理解 従来のUIの役割を分解すると: 1. 視覚的フィードバック (操作結果の確認) 従来: ボタンを押して画面で結果を見る tmp駆動: tmpスクリプトを実行して、実行可能なフィードバックを得る 本質 : UIは「見るため」ではなく「確認するため」→ 実行結果で代替可能 2. 操作の抽象化 (複雑なコマンドを簡単に) 従来: ボタン一つで複雑な処理を実行 tmp駆動: 自然言語で依頼すると、Claude Codeがtmpスクリプト生成 本質 : UIは「操作を簡単にする」→ 自然言語で代替可能 3. 操作フローのガイド (次に何をすべきか示す) 従来: 画面遷移やメニューで操作を誘導 tmp駆動: tmpスクリプトの頻出パターンから次の機能を発見 本質 : UIは「発見を助ける」→ tmp観察で代替可能 つまり、 UIの役割は全て代替可能 です。 Claude CodeがUIとして優れている点 : 自然言語で操作できる(ボタン配置不要) 柔軟性が高い(固定画面レイアウトがない) 組み合わせ処理を即座に実行(カスタマイズ自在) tmpスクリプト生成で要件を可視化(暗黙知の顕在化) これが、フロントエンド開発をスキップできた理由です。 UI不要が適している場面・適さない場面 適している場面 : バックエンドロジック開発(データ処理、検証、変換) 自動化スクリプト(バッチ処理、CI/CD) インフラ管理(データベース操作、設定変更) 個人・小チーム利用(開発者のみが使う) UIが必要な場面 : ビジュアルデザインが重要(UI/UX設計が価値) 非エンジニアユーザー向け(自然言語UIでは不十分) リアルタイム性が重要(グラフ、ダッシュボード) 規制要件(監査証跡、操作履歴の可視化) 移行パターン : 多くの場合、こうなります: Phase 1: tmp駆動でロジック整備(UI不要) Phase 2: ロジック確定後、必要ならUI実装 UI実装は、 ロジックが固まってから 。手戻りがゼロになります。 将来的なアプリ化の価値 : ビジネスロジック検証で確定した機能は、 本当に人間が欲している機能 として証明されています。この段階でアプリ化すれば: 要件が固まっている → UI設計が明確 ビジネスルールが検証済み → 手戻りゼロ 実際の利用パターンが分かっている → 最適なUX設計が可能 tmpスクリプトの頻度から優先機能が明確 → 無駄な機能を作らない つまり、 tmpで検証したロジックをアプリ化することで、本当に価値のある機能だけを提供できる んです。 tmpがビジネスロジック整備を可能にする仕組み 1. 人間の暗黙知を可視化 → 「重複って何?」がtmpスクリプトに具体的な定義として現れる 2. 頻出パターンの発見 → 同じスクリプトが5回生成 → これは必要な機能だ 3. ビジネスルールの検証 → tmpスクリプトが実際に動く → ロジックが検証済み 4. UIなしで改善サイクル → tmp観察 → CLI拡張 → 再びtmp観察 → さらに拡張 この4つのステップが、UIなしでビジネスロジックを整備できる仕組みです。 まとめ tmpの再定義 従来の認識 : tmp/ = 一時的なゴミ 実行後は削除すべき 新しい理解 : tmp/ = 人間の自然言語から生まれた純粋な要件 ビジネスロジック整備の記録 UI設計のヒント tmpが実現する新しい開発フロー 従来: 要件定義 → UI設計 → 実装 → 「仕様変更...」 ロジックファースト: CLI基本実装 → Skill定義 → tmp蓄積 → ビジネスロジック抽出 → CLI拡張 → ロジック確定 → (必要なら)UI実装 革新的な点 : UIなしでビジネスロジックを整備・検証できる tmpが要件定義のプロセス自体を変える 小さく試行錯誤しながらロジックを固められる UI設計は後回し(ロジック確定後)→ 無駄なし 前回の記事の「短時間完成」の本当の意味 前回の記事で語ったこと : フロントエンド開発をスキップして短時間で完成(事実) 本記事で明かしたこと : tmpがビジネスロジックを整備してくれた(理由) なぜスキップできたのか : tmpがビジネスロジックを整備してくれた(自然言語から要件が生まれる) Claude Codeが人間の操作UIとして機能(Webフロントエンドが不要) UIは後回しにできる(ロジック確定後 → 手戻りなし) 開発プロセスの比較 : 従来: 要件定義(抽象的)→ UI設計 → フロント実装 → バックエンド 合計: 16-24日(128-192時間、一般的な工数見積として参考) ロジックファースト: CLI基本実装 → Skill定義 → tmp蓄積・観察 → CLI拡張 合計: 数時間〜1日(実測例として参考) 削減効果: UI開発をスキップすることで大幅削減 時間内訳の詳細 : フェーズ 従来 ロジックファースト 削減内容 要件定義 16-24h 最小限 大部分スキップ UI設計 32-48h 0h 100%削減 フロントエンド 40-60h 0h 100%削減 バックエンド 24-36h 数時間〜1日 大幅削減 機能拡張 16-24h 段階的に追加 柔軟に対応 削減の本質 : AIツールによる速度向上ではなく、 UI開発というアプローチ自体を変えた ことです。 核心的な発見 tmpスクリプトを観察することで: 人間の暗黙知が可視化される 「重複って何?」→ 類似度90%以上という具体的な定義 頻出パターンから優先順位が見える 5回生成 → 必要な機能 1回生成 → 稀なケース ビジネスルールが検証される tmpスクリプトが実際に動く → 検証済み UIなしで改善サイクルが回る tmp観察 → CLI拡張 → tmp観察 → さらに拡張 関連する開発手法との違い ロジックファースト検証は、既存の開発手法と何が違うのでしょうか? vs. 従来の仕様駆動開発 項目 仕様駆動開発 ロジックファースト検証 要件の形式 ドキュメント(抽象的) 実行可能スクリプト(具体的) 検証方法 レビュー会議 実行して確認 変更コスト 高い(ドキュメント更新 + 実装変更) 低い(tmpを捨てて再生成) 発見的開発 困難(仕様変更が重い) 容易(試行錯誤しやすい) 本質的な違い : ドキュメントは「こうあるべき」、tmpスクリプトは「実際にこう使った」 vs. AI駆動開発 項目 AI駆動開発 ロジックファースト検証 ツール依存性 高い(特定AIツールに依存) 低い(自然言語UIなら何でも) Skillsを使うならClaude依存 焦点 AIの活用方法 開発手法そのもの 適用範囲 コード生成中心 要件発見 → 実装まで 本質 ツール論 方法論 本質的な違い : AI駆動開発は「AIを使う」手法、ロジックファースト検証は「自然言語を要件にする」手法(ツール非依存) vs. TDD(テスト駆動開発) 項目 TDD ロジックファースト検証 駆動要素 テストコード tmpスクリプト 目的 品質保証 要件発見 成果物 永続的テスト 一時的スクリプト(昇格可能) サイクル Red → Green → Refactor tmp生成 → 観察 → 抽出 → 昇格 本質的な違い : TDDは「正しさ」を駆動、ロジックファースト検証は「要件そのもの」を駆動 ロジックファースト検証の位置づけ 要件発見フェーズ: ロジックファースト検証 ← 本手法の焦点 ↓ 実装フェーズ: TDD, AI駆動開発など ↓ 運用フェーズ: DevOps, SRE ロジックファースト検証は、 要件が固まっていない初期段階 に特に有効です。要件が明確になれば、TDDやAI駆動開発と組み合わせて使えます。 実践方法について tmpの具体的な観察方法、ビジネスロジックの抽出手順、環境セットアップなどの詳細は、次回の記事「 Claude Code一時ファイルからSkillsへ:ビジネスロジック抽出の実践ガイド 」で解説します。 次回の記事では: tmpディレクトリのプロジェクト内設定方法 週次レビューの実践 ビジネスロジック抽出の具体例(コード付き) 環境セットアップ(Python/uv, TypeScript) tmpからCLI機能への昇格プロセス を扱う予定です。 皆さんも、tmpスクリプトを削除せず、観察してみてください。そこには、 あなたの本当の要件が隠れています 。 参考リンク 公式ドキュメント Claude Code 公式ドキュメント Claude Code Skills 公式ガイド Supabase 公式ドキュメント Python 公式ドキュメント – 型ヒント、subprocess等 uv 公式ドキュメント 前提記事 Claude Code: 公式MCPを補完するSkills設計パターン 4層構造パターン(公式SDK → 自作Client → CLI → Skill) UI開発工数の大幅削減 なぜフロントエンド開発をスキップできたか(表面的な説明) 関連ブログ Claude Code Skills 実装ガイド:ローカルツールをスムーズに統合する方法 Skillの実装方法(詳細) Progressive Disclosure、トークン効率化 本記事の基盤となるSkills実装 HTMLでブログ記事を保存してる奴、全員Markdownにしろ。AIが読みにくいでしょうが! blog-scraperの実装例(tmpから進化したツール) トークン削減の実測データ AIチャットで話すだけ!X予約投稿を完全自動化するシステム構築術 X投稿管理システムの全体像 Azure Functions + Supabaseでの予約投稿自動化 次に読むべき記事 Claude Code一時ファイルからSkillsへ:ビジネスロジック抽出の実践ガイド : tmpの観察方法、ビジネスロジック抽出の具体例、環境セットアップを詳解 次回の記事では、本記事で説明した概念を実際にどう実装するかを、コード例を交えて解説します。 質問や感想は、コメント欄でお待ちしております! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Codeの一時ファイルでビジネスロジック検証:UI不要で要件を発見する方法 first appeared on SIOS Tech. Lab .
はじめに ども!Claude Codeにべったりな龍ちゃんです。 2025年、Claude CodeのMCP対応が進み、GitHub、Slack、Supabase、Firebase等、多くのサービスで 公式MCPサーバー が利用可能になりました。 しかし実際のプロジェクトでは、 公式MCPだけでは不十分なケース が多々あります: カスタムビジネスロジック : 重複検出、データ検証、複数テーブル統合、マイグレーション処理 トークン最適化 : MCPは4サーバーで55.7k (27.9%)消費(※1) 独自要件 : 社内システム固有の処理、セキュリティ要件 本記事では、 公式MCPを基盤として、不足部分をSkillsで補完する 汎用的なパターンを、Supabaseの実例(700行Skill、30+コマンド)で解説します。しかも、 フロントエンド開発を一切スキップ して実現できました。 ※1: 実測データによると、MCPサーバーは大量のトークンを消費する傾向があります。 よく使ってたMCPはNotionだったんですけど、こいつトークンバカ食いするんですよね。 普通、カスタムビジネスロジックを人間が使えるようにしようと思ったら、Reactでフロントエンド開発して、デザインシステム作って、UX設計して…って、2〜3週間は覚悟しますよね?(AIを活用することで開発の工数を抑えてもそれでも大変!) でも、こう考えれば良いと気づいたんです: 公式SDK/APIをラップして自作Clientを作成 (カスタムビジネスロジック実装) 自作ClientをCLIツールでラップ CLIツールの使い方をSkillsとして定義 Claude Codeが「UIとして機能」する この汎用的なパターンを使えば、公式MCPで不十分な場合でも、カスタムビジネスロジックをClaude Codeと統合できます。 Supabase、Firebase、社内API等…全て同じ4層構造(公式SDK → 自作Client → CLI → Skill)のアプローチです。この発想で、全てが変わりました。 TL;DR この記事で分かること: 公式MCPを補完する3つのユースケース (2025年版) カスタムビジネスロジック : 重複検出、データ検証、複数テーブル統合(700行Skillの実例) トークン最適化 : MCP 55.7k (27.9%) vs Skills Progressive Disclosure 独自要件 : 社内システム、セキュリティ要件、レガシーシステム 汎用的な補完パターン : 公式SDK → 自作Client → CLI → Skill の4層構造 対象 : 公式MCPでカバーされないカスタムロジック 実装例 : Supabase(公式MCP + 700行Skill) 利用者数による技術選択 の判断基準 社内共有レベル: アプリ化(Streamable HTTP/WebSocket + Function Calling/MCP自作) 個人〜チーム: Claude Code + Skills 実装パターン : 短時間で完成 Step 1: カスタムビジネスロジックを自作Clientに実装 Step 2: CLI化(Click等) Step 3: Skill登録(Markdown) アプリ化をスキップ することで工数を大幅削減 アプリ化(一般的見積): 128-192時間(16-24日) Skills化(実測例): 数時間〜1日 削減の理由 : UI開発をスキップし、Claude Codeを操作インターフェースとして活用 MCP vs Skillの判断基準 を実例で解説 重要な前提条件: 対象サービスのAPI/SDKが存在する Python等のプログラミング基本知識 Claude Codeの基本操作を理解している こんな人に読んでほしい 公式MCPでは不十分なカスタムビジネスロジック を実装したい開発者 データ検証、重複検出、複数テーブル統合 等の独自処理が必要な人 社内APIやレガシーシステム をClaude Code対応したい開発者 MCPのトークン消費を最適化 したい人(4サーバーで27.9%消費に悩んでいる) 個人〜チームレベル でツールを使いたい人(大規模なアプリ化は不要) ビジネスロジックが固まっていない段階で、 小さく検証しながら開発 したい人 リモートMCP vs 自作MCP vs Skillで迷っている人 公式MCPをSkillsで補完する汎用パターンの発見 ある日、私はこんな課題に直面しました: 課題 : Supabaseに 公式MCPサーバーが存在する が、カスタムビジネスロジック(重複検出、データ検証、複数テーブル統合)は提供されない でも、フロントエンド開発で専用UIを作るのは時間がかかりすぎる 自作MCPサーバーを立てるのもインフラ管理が面倒 まず確認すべきこと(2025年版) : 公式MCPサーバーで十分か? GitHub、Slack、Notion、Supabase、Firebase等は公開MCPサーバーが存在 → 基本操作なら公式MCP使用(2024年11月のMCP発表以降、リモートMCPサーバーが順次展開) 参考: MCP Servers Directory 公式MCPで不十分な場合 : カスタムビジネスロジック : 重複検出、データ検証、複数テーブル統合、マイグレーション トークン最適化 : MCP 4サーバーで55.7k (27.9%)消費を回避したい 独自要件 : 社内API、レガシーシステム、セキュリティ要件 → 本記事の汎用パターン(Skillsで補完) 発見した汎用パターン (公式MCPを補完する場合): 公式SDK/APIをラップして自作Clientを作成 (カスタムビジネスロジック実装) 自作ClientをCLIツールでラップ Skillsとして使い方を定義 → この3ステップで公式MCPを補完し、カスタムロジックをClaude Codeと統合可能 今回の実例:Supabase公式MCPを700行Skillで補完 : 公式Supabase MCPでは提供されないカスタムロジックを実装 Supabase Python SDK(公式)をラップして自作Supabase Clientを作成 重複検出(find-duplicates)、データマイグレーション(merge –dry-run)、統計分析(stats)等を実装 別プロジェクトでの知見を活かして関数ベースのインターフェースで実装 自作ClientをCLIツールでラップ Skillsとして提供(700行、30+コマンド) フロントエンド開発なし MCPサーバー構築なし 短時間で運用開始 読者への問いかけ : あなたが実装したいカスタムビジネスロジック、ありませんか?この汎用パターンなら、公式MCPを補完できます。 システム利用者による技術選択 さて、外部サービスやシステムに人間がアクセスできるようにするには、どんな選択肢があるでしょうか? これは、Supabaseだけでなく、GitHub、Slack、AWS、社内API…あらゆるサービスとの接続で共通する判断です。 選択肢A:利用者が複数(社内共有レベル)→ アプリ化 想定シナリオ : 複数チームで使う、社内の共通ツールにする 技術スタック : フロントエンド:React等でWebアプリ開発 バックエンド:Streamable HTTP/WebSocket + Function Calling or MCP デプロイ:Azure/AWS等 ※MCP仕様では2025年3月にSSEトランスポートが非推奨化され、Streamable HTTPに置き換えられました。 開発工数(一般的な見積もり) : 16-24日(128-192時間) ※フルスタックWebアプリケーション開発(React + MCP統合)の一般的な工数。プロジェクトの複雑性により変動します。 要件定義: 2-3日 UI設計・フロントエンド開発: 10-13日(デザインシステム構築含む) バックエンド統合: 2-3日 テスト・デバッグ: 2-3日 問題点 : 時間がかかる : 16-24日の開発期間 柔軟性がない : UIを作った後のビジネスロジック変更が大変 ビジネスロジックを変更 → UIも変更 → 再設計・再実装 小さい単位での試行錯誤ができない 使いながら要件を整理する、ということができない 選択肢B:利用者が個人〜チーム → 小さいワークフロー 想定シナリオ : 自分だけ、またはチーム内での共有(Skillsファイル共有) 技術スタック : CLI:Click等でコマンドラインツール作成 Skills:Claude Code Skillsで使い方を定義 共有: .claude/skills/ ディレクトリを共有 開発工数 : 数時間〜1日 利点 : 即座に使い始められる ビジネスロジックだけを柔軟に変更 できる 小さく試行錯誤、使いながら要件を整理 判断:個人開発 → Skills採用 私の状況はこうでした: 判断基準 : 利用者: 自分だけ 要件: まだ固まっていない (使いながら整理したい) ビジネスロジック: 頻繁に変更 する可能性 デモ:操作を見せたいが、フロントエンド開発は避けたい → 選択肢B(Claude Code + Skills)を採用 特に、 ビジネスロジックが固まっていない段階でUIに投資するリスク を避けたかったんです。 さらなる選択:公式MCP vs 自作MCP vs Skill 選択肢B(Claude Code + ワークフロー)の中でも、実は技術選択肢が 3つ あります(2025年版): 公式MCP (Remote MCP) : 既存の公開MCPサーバーを利用(2024年11月〜順次展開) 自作MCP (Model Context Protocol) : 自分でMCPサーバーを実装 Skill : Claude Code専用のツール統合(Markdownファイル) 公式MCPサーバーが存在し、基本操作で十分な場合は、公式MCPが最も簡単です。 本記事のパターンは、公式MCPで不十分な場合(カスタムビジネスロジック、トークン最適化等)の選択です。 比較表(2025年版) 項目 公式MCP 自作MCP Skill 前提条件 公開MCPサーバーが存在 MCPサーバーを自作 公式MCPで不十分 or MCPが存在しない 実装難易度 最低(OAuth認証のみ) 高(Pythonライブラリ、サーバー起動) 低(Markdown 1ファイル) 学習コスト 最低 高 低 所要時間 数分 1-2日 数時間 対応環境 Claude Desktop統合 Claude Desktop統合 Claude Code専用 型安全性 高 高 – デバッグ 易 難 易 適用範囲 公開MCPがあるサービスの基本操作 外部システム連携 カスタムビジネスロジック、プロジェクト固有ツール 運用コスト $0(リモート) 高(サーバー稼働) $0(ローカル) トークン消費 多い(※) 多い(※) 少ない(Progressive Disclosure) (※)MCPのトークン消費問題 : 4つのMCPサーバーで 55.7k トークン(27.9%) 消費という実測例が報告されています。1つのMCPサーバーにつき10-20個のツール定義があるが、実際に使うのは1-2個だけという無駄が発生します。SkillsはProgressive Disclosure(段階的開示)により、この問題を回避できます。起動時にスキルの名前と説明のみを読み込み、必要に応じて完全な内容を段階的にロードします。 判断プロセス(2025年版) ステップ1: 公式MCPサーバーの確認 → Supabase公式MCPサーバーが存在(https://mcp.supabase.com/mcp)、基本CRUDは対応可能 ステップ2: 公式MCPで十分か? → カスタムロジック(find-duplicates、merge –dry-run、stats、validate)は公式MCPにない → 不十分 ステップ3: 自作MCP vs Skill → 使用者1人、ビジネスロジック検証、スピード重視、運用コスト$0 → Skill採用 実装例:Supabase公式MCPを補完【700行、30+コマンド】 それでは、公式MCPを補完する汎用パターンを具体例で見てみましょう。 今回の実装対象 : Supabase(公式MCP + カスタムビジネスロジック) 所要時間 : 数時間〜1日(実測値の一例として参考) 公式MCPの状況 : 基本CRUD操作は公式MCPサーバー(https://mcp.supabase.com/mcp)で対応可能 Skillで補完する内容 : 重複検出(find-duplicates)、データマイグレーション(merge –dry-run)、統計分析(stats)、整合性チェック(validate)等 成果物 : 5テーブル、30+コマンド、約700行Skill 汎用的な実装アプローチ(3ステップ) Step 1: 自作ServiceClient作成 公式SDK/APIをラップして、カスタムビジネスロジックを実装 例: Supabase Python SDK → 自作Supabase Client(重複検出、統計分析等) 他サービス: PyGitHub/slack-sdk/boto3等をラップ Step 2: CLI作成 自作ClientをClick等でCLIツール化 例: db posts list 、 db hashtags merge --to ID --from IDs レイヤー構造: Skill → CLI → 自作Client → 公式SDK → 外部サービス Step 3: Skill登録 Markdownファイルでコマンド使用方法を定義 Progressive Disclosure(段階的開示)で効率化:起動時にスキル名と説明のみ読み込み 例: .claude/skills/x-posts-manager.md (700行) なぜClientレイヤーを作るのか? 公式SDKを直接使わず、自作Clientを挟むことで: カスタムビジネスロジックをカプセル化 プロジェクト固有の処理を統一的に管理 関数ベースのインターフェース等の独自実装を追加可能 詳しい実装方法 は、 Claude Code Skills 実装ガイド を参照してください。 成果:UI開発工数の大幅削減 選択肢A vs 選択肢Bを比較してみましょう。 選択肢A:アプリ化(社内共有レベル)- 一般的な工数 要件定義(2-3日) → UI設計(3-5日) → フロントエンド開発(7-10日) → バックエンド統合(2-3日) → テスト(2-3日) └─────────────── 16-24日(128-192時間) ──────────────┘ ※フルスタックWebアプリケーション開発の一般的な工数見積もり 選択肢B:Skills化(個人〜チーム)- 実測例 CLI実装 → Skill化 → テスト └──────── 数時間〜1日 ────────┘ ※プロジェクトの複雑性により変動。Supabaseプロジェクトでは約8時間(1日)の実測例あり 削減効果の比較 項目 選択肢A(アプリ化)※一般的見積 選択肢B(Skills化)※実測例 削減内容 開発時間 128-192時間 数時間〜1日 UI開発をスキップ 工数 16-24人日 0.5-1人日 大幅削減 フロントエンド開発 7-10日 0日 スキップ UI設計 3-5日 0日 スキップ 運用開始 16-24日後 即日〜1日 迅速な立ち上げ 削減の理由: React等のフロントエンド開発をスキップ デザインシステム構築が不要 UX設計・テストが不要 Claude Codeが「人間が使うUI」として機能 得られた柔軟性: ビジネスロジックだけを変更できる(UIの再設計不要) 小さい単位で試行錯誤できる 使いながら要件を整理できる 重要: この削減は「AIツールによる開発速度向上」ではなく、 「UI開発というアプローチ自体を変えたこと」 による効果です。 フロントエンド開発をスキップして、短時間で運用開始できるんです。そして、後から自由に改善できる柔軟性も手に入れました。 汎用性:公式MCPを補完する展開 この汎用パターンの最大の価値は、 公式MCPが存在するサービスでも、カスタムビジネスロジックで補完できる ことです。 補完パターンの適用例 公開MCPが存在するサービス (カスタムロジックで補完): Supabase : 基本CRUD(公式MCP)+ 重複検出・統計分析・整合性チェック(Skill) Firebase : Firestore基本操作(公式MCP)+ 複数コレクション横断・セキュリティルール検証(Skill) GitHub : Issue/PR操作(公式MCP)+ カスタムラベル付け・複数リポジトリ横断分析(Skill) Slack : メッセージ送信(公式MCP)+ メッセージ集計分析・カスタム通知ロジック(Skill) 公開MCPが存在しないサービス (Skillで全て実装): 社内API、レガシーシステム、管理ツール、独自開発サービス 補完が必要な4つの理由 : カスタムビジネスロジック(公式MCPで提供されない独自処理) トークン最適化(MCP 55.7k消費を回避) セキュリティ要件(社内ネットワーク内でのみ動作) 小さく検証(ビジネスロジック固まっていない段階での試行錯誤) 参考 : MCP Servers Directory で公式MCPサーバーの有無を確認できます 実装パターンは全て同じ どのサービスでも: 公式SDK/APIをラップ → 自作ServiceClient作成(ビジネスロジック実装) CLI化 → 自作ClientをClick等でラップ Skill登録 → Markdownで使い方定義 → Claude Codeから操作可能に レイヤー構造の利点 : 公式SDK : サービス通信の実装を提供(変更不要) 自作Client : プロジェクト固有のビジネスロジックを実装 CLI : コマンドラインインターフェースを提供 Skill : Claude Codeとの統合を提供 フロントエンド開発をスキップしつつ、あらゆるサービスをClaude Code経由で操作できるようになります。しかも、自作Clientレイヤーでビジネスロジックをカプセル化できるため、柔軟性も高いです。これは、個人開発〜チームレベルの開発において、強力なアプローチです。 まとめ 今回は、 公式MCPを補完するSkills活用パターン を紹介しました。 核心的な発見 2025年版の判断フロー : 公式MCPで十分か確認 → 基本操作なら公式MCP使用(最も簡単) 公式MCPで不十分な場合 → 本記事の補完パターン(Skillsで拡張) 3つの補完ユースケース : カスタムビジネスロジック : 重複検出、データ検証、複数テーブル統合 トークン最適化 : MCP 55.7k (27.9%)消費を回避 独自要件 : 社内システム、セキュリティ要件 汎用的な補完パターン : 公式SDK → 自作Client → CLI → Skill の4層構造 レイヤーの役割 : 公式SDK: サービス通信を提供 自作Client: カスタムビジネスロジックをカプセル化(関数ベースのインターフェース等) CLI: コマンドラインインターフェース提供 Skill: Claude Code統合、Progressive Disclosure(段階的開示) 実装例(Supabase) : 公式MCP + 700行Skill、フロントエンド開発なし 公式MCP: 基本CRUD操作 Skill: find-duplicates、merge –dry-run、stats、validate等 実装時間: 数時間〜1日(実測例として参考) Skillの優位性 : Progressive Disclosure(段階的開示)により トークン消費を大幅削減 (MCP: 55.7k vs Skill: 起動時は名前と説明のみ) 運用コスト $0(ローカル完結) 実装時間 数時間〜1日(自作MCP: 1-2日) UI開発工数削減 : アプリ化(一般的見積: 128-192時間)→ Skills化(実測例: 数時間〜1日) 柔軟性 : UIがないので、ビジネスロジックだけを小さく検証・変更できる 既存ブログとの違い 以前書いた Claude Code Skills 実装ガイド では、「 Skillの作り方(How to) 」を解説しました。 今回は、 「公式MCPを補完する汎用パターン(What you can achieve)」 と 「Skillに至った経緯(Why & 判断プロセス)」 を中心に語りました。 今すぐできること 実装したいカスタムビジネスロジックを明確にする : 重複検出、データ検証、複数テーブル統合、統計分析…等 公式MCPサーバーの有無と機能を確認 : MCP Servers Directory で検索 公式MCPで十分 → 公式MCP使用(最も簡単) 公式MCPで不十分 → 次のステップへ(カスタムロジック補完) MCPがない → 次のステップへ(全て実装) SDK/APIを確認 : 対象サービスのPython SDK、REST APIドキュメントをチェック 4層構造で実装 (数時間〜1日目安): 公式SDK : 既存のライブラリを利用(Supabase SDK、firebase-admin等) 自作Client : 公式SDKをラップしてカスタムビジネスロジック実装 CLI : 自作ClientをClickでラップ Skill : Markdownで使い方定義(Progressive Disclosure対応) 公式MCP vs 自作MCP vs Skillで迷っている人 : この記事の判断フローを参考に フロントエンド開発を避けたい人 : Skills化で即座に運用開始 この汎用パターンを覚えておけば、公式MCPを補完し、カスタムビジネスロジックをClaude Codeと統合できます。 皆さんも、実装したいカスタムロジックがあったら、まず公式MCPサーバーの有無と機能を確認し、不十分ならこの補完パターンを試してみてください! 参考リンク 公式ドキュメント Claude Code 公式ドキュメント MCP (Model Context Protocol) 公式ドキュメント MCP Servers Directory – 公式MCPサーバー一覧 Anthropic Skills 公式発表 Supabase 公式ドキュメント Supabase MCP Server Python Click 公式ドキュメント – CLIツール作成ライブラリ uv 公式ドキュメント – Pythonパッケージマネージャー 関連ブログ Claude Code Skills 実装ガイド:ローカルツールをスムーズに統合する方法 本記事で説明した「Skill化」の詳細実装方法 Skillファイルの構造、YAML frontmatter、ベストプラクティス 【2025年版】検証 → 記事化で知見を資産化!Claude Code×RAGもどきでAI技術ブログ執筆を効率化 「RAGもどき」のアイデアの原点 Claude Codeを活用したブログ執筆ワークフロー AI協業開発環境の構築術|モノレポでビルド時間を大幅短縮するCLAUDE.md活用法 開発環境のセットアップ(モノレポ、uv、devcontainer) 本記事のプロジェクト構成の基礎 質問や感想は、コメント欄でお待ちしております! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code: 公式MCPを補完するSkills設計パターン first appeared on SIOS Tech. Lab .
はじめに ども!Claude Code を執筆に贅沢活用している龍ちゃんです。 前回の SVG図解自動生成記事 で、「SVGで図解作成時間を67%削減できた!」って話をしたんですが、正直に言うと 完璧ではなかった んですよね。 フロントエンドエンジニアとしてTailwind CSSを日常的に使っている僕からすると、 あとから編集もできちゃうんですよね 。ClaudeもTailwindが得意なので、SVGを作るよりHTML経由で図を作ってスクショする方が、PNG変換という手間は増えるんですけど、意図した図が作れるんです。 HTMLで図解を作る利点 1. レイアウトが一発で決まる ClaudeはCSS(特にFlexbox/Grid)が得意なので、paddingや文字の設定、レイアウトの統一感がSVGより圧倒的に実現しやすいです。 Claudeで作るSVGと比較すると、レイアウト崩れが基本的になくなります。 2. コードが読める人なら編集・活用が簡単 HTMLを読んで図を修正したり活用したりできるのは利点ですね。Material IconsもSVGと同じように使えるし、個人的にTailwind CSSが好き(入社した時に初めて学んだ技術)っていうのもあります。 3. Figmaで編集可能 HTMLをSVGに変換する便利なライブラリーが出てきているので、Figmaで後から編集することもできます。 4. PNG変換の選択肢が豊富 Playwright(MCP/Pythonパッケージ)やhtml2imageなどのライブラリーを使って、HTMLからPNG画像を生成できます。 今回のブログの対象範囲 今回は HTMLで図解を作るところまで を対象としています。PNG変換については後半で軽く触れますが、詳細は別記事で扱う予定です。 理想としてはWordPressサイトに直接HTMLを挟み込むこともありなんですけど、動作が重くなるので、まあご愛嬌かなと(笑)。 一番簡単な方法としてはブラウザで立ち上げてスクショですね。 試してみて感じたこと 驚くほど簡単でした。 SVGでは頻繁に発生していた微調整が、今回試したHTML図解3つは全て修正不要で完成しました。 今後、僕がブログで図を作るなら : まずMermaidでできないか考える 無理ならHTMLで作る という手段になりますね。 ベストプラクティスを一緒に模索しましょう Skillの共有は今回もGistで提供しています。ベースはSVGでやってた方法とほぼ一緒で、Claudeに調査してもらって、レイアウトの比率やHTML用にカスタムしたルールを追加している段階です。 Note : HTML 図解生成 Skill の完全版は Gist で公開中 Skill の作り方について詳しくは Claude Code Skillの登録と実践! を参照してください 高機能な図を作るためのベストプラクティス、僕も模索中です。 もし「いい方法があるよ!」「これやってないの?」みたいなツッコミがあれば、ぜひXの方でDMください。 @SIOSTechLab でも @RyuReina_Tech でもどちらでも大歓迎です! この記事で伝えたいこと SVGとHTMLを使い分けることで、より柔軟な図解作成が可能になります。 この記事で扱う内容 : HTML + Tailwind CSS 図解自動生成 Skillの実装 SVGで遭遇した問題の解決方法 (重要!) SVGとHTMLの使い分けガイドライン PNG変換ワークフロー SVG記事で遭遇した課題とHTMLでの解決 課題 SVGでの状況 HTML + Tailwind CSSでの解決 文字列のはみ出し font-size調整が必要(頻繁に発生) text-center , p-4 で自動余白確保 要素の重なり padding調整が必要(頻繁に発生) Flexboxの gap-6 で自動間隔確保 カスタム矢印問題 Material Icons推奨でも無視されることがある <span class="material-symbols-outlined">arrow_downward</span> 配置のバラつき 座標計算ミスが発生 Gridレイアウトで均等配置が自動 結論 : レイアウト自動化で座標計算から解放されました。 HTML図解自動生成 Skillの実装 Skillファイルの構成 .claude/skills/diagram-generator-html.md を作成し、以下の要素を含めます: 主要な指示内容 : Tailwind CSS CDN使用 (レイアウトを簡単に) 注 : CDNは開発・プロトタイプ用です。PNG変換後は静的画像になるため、本番環境の制限は適用されません Material Symbols Outlined (クラス指定だけでアイコン使用) 固定サイズ : 1280 x 720 px (16:9) アクセシビリティ対応 ( role="img" , aria-label ) 実装のポイント 1. Tailwind CSSで統一されたレイアウト <div class="flex flex-col gap-6"> <!-- 自動的に縦方向に6の間隔で配置 --> </div> ポイント : 座標計算不要。 gap-6 だけで要素間隔が自動確保されます。 2. Material Iconsがクラス指定だけで使える <span class="material-symbols-outlined text-6xl text-blue-600">database</span> SVGとの比較 : SVG: <path d="M12,3C7.58..." fill="#2196F3"/> を手動で埋め込む HTML: クラス名を指定するだけ 3. 固定サイズで一貫性を保つ <body class="w-[1280px] h-[720px] m-0 p-0"> ポイント : PNG変換時に正確なサイズが保証されます。 実際に使ってみた 使い方の流れ Claude Codeに「HTMLで○○の図を作って」と依頼 Skillが自動起動 不足情報があれば質問される HTMLファイルが生成される PNGファイルとして docs/article/[feature-name]/images/ に保存 実例1: 3層アーキテクチャ図の生成 依頼内容 : HTMLで3層アーキテクチャの図を作ってください。 Presentation Layer、Business Logic Layer、Data Access Layerの構成です。 Material Iconsを使って、各層にアイコンを配置してください。 生成されたHTML (抜粋): <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>3層アーキテクチャ</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" /> </head> <body class="w-[1280px] h-[720px] m-0 p-0"> <div class="w-full h-full bg-gray-50 flex items-center justify-center p-12"> <div class="w-full max-w-4xl flex flex-col gap-6"> <!-- Presentation Layer --> <div class="bg-blue-100 border-2 border-blue-500 rounded-lg p-6"> <div class="flex items-center justify-center gap-3"> <span class="material-symbols-outlined text-5xl text-blue-600">desktop_windows</span> <div class="text-center"> <h2 class="text-2xl font-bold text-blue-900">Presentation Layer</h2> <p class="text-lg text-blue-700 mt-1">UI・ユーザーインターフェース</p> </div> </div> </div> <!-- Arrow --> <div class="text-center"> <span class="material-symbols-outlined text-5xl text-gray-400">arrow_downward</span> </div> <!-- (他の層も同様) --> </div> </div> </body> </html> 結果 : ファイルサイズ: 43.74 KB(推奨範囲内) サイズ: 1280 x 720 px 修正不要 (一発で完成) ポイント : Flexboxの gap-6 で矢印との間隔が自動確保 Material Iconsがクラス指定だけで表示 文字列のはみ出しゼロ 実例2: ユーザー認証フロー図の生成 依頼内容 : HTMLでユーザー認証フローの図を作ってください。 開始 → 認証情報入力 → 検証 → セッション確立 → 完了の流れです。 開始と完了は楕円形、プロセスは矩形で表現してください。 生成されたHTML (抜粋): <div class="flex flex-col items-center gap-8"> <!-- 開始(楕円形) --> <div class="bg-green-100 border-2 border-green-500 rounded-full px-12 py-6"> <span class="text-xl font-bold text-green-900">開始</span> </div> <!-- 矢印 --> <div class="text-center"> <span class="text-4xl text-gray-400">↓</span> </div> <!-- 認証情報入力(矩形) --> <div class="bg-blue-100 border-2 border-blue-500 rounded-lg px-8 py-4"> <span class="text-lg font-semibold text-blue-900">認証情報入力</span> </div> <!-- (他のステップも同様) --> </div> 結果 : ファイルサイズ: 29.21 KB 修正不要 (一発で完成) ポイント : rounded-full で楕円形、 rounded-lg で矩形が簡単に実装 Unicode文字 ↓ でシンプルな矢印 中央揃え配置が自動 実例3: Before/After比較図の生成 依頼内容 : HTMLで図解作成の時短効果を比較するBefore/After図を作ってください。 Before(赤系): - 作業時間: 45分 - ツール: Figma / PowerPoint - 課題: 時間がかかる After(緑系): - 作業時間: 15分 - ツール: Claude Code Skill - 内訳: 生成5分 + 修正10分 - 効果: 67% 時短! 生成されたHTML (抜粋): <div class="grid grid-cols-2 gap-12"> <!-- Before --> <div class="bg-red-50 border-2 border-red-300 rounded-lg p-8"> <h3 class="text-2xl font-bold text-red-900 mb-4">Before(手動作成)</h3> <div class="space-y-3 text-red-800"> <p class="text-lg">⏱ 作業時間: <strong>45分</strong></p> <p class="text-lg">🛠 ツール: Figma / PowerPoint</p> <p class="text-lg">⚠ 課題: 時間がかかる</p> </div> </div> <!-- After --> <div class="bg-green-50 border-2 border-green-300 rounded-lg p-8"> <h3 class="text-2xl font-bold text-green-900 mb-4">After(Skill生成)</h3> <div class="space-y-3 text-green-800"> <p class="text-lg">⏱ 作業時間: <strong>15分</strong></p> <p class="text-lg">🛠 ツール: Claude Code Skill</p> <p class="text-lg">📊 内訳: 生成5分 + 修正10分</p> <div class="bg-green-100 border border-green-400 rounded px-3 py-1 mt-2 inline-block"> <span class="text-sm font-bold text-green-900">✨ 67% 時短!</span> </div> </div> </div> </div> 結果 : ファイルサイズ: 37.11 KB 修正不要 (一発で完成) ポイント : grid grid-cols-2 gap-12 で2カラムレイアウトが自動 配色統一(Before: 赤系、After: 緑系) 効果バッジも簡単に実装 SVG記事との比較 図解 SVG記事(修正の有無) HTML検証結果(修正の有無) 3層アーキテクチャ 修正が必要だった 修正不要 認証フロー 修正が必要だった 修正不要 Before/After 修正が必要だった 修正不要 結論 : 今回試した3つの実例では、全て修正不要で完成。SVGで遭遇した問題を解決できました。 Material Icons活用術 Googleが提供する2000種類以上の無料アイコン集が、HTMLなら クラス指定だけで使える のが最大の利点です。 使用方法 : <!-- CDN読み込み --> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" /> <!-- アイコン配置 --> <span class="material-symbols-outlined text-6xl text-blue-600">database</span> SVGとの比較 : 操作 SVG HTML 配置 パス手動埋め込み <span> タグでクラス指定 変更 パス全体差し替え 名前変更だけ( database → cloud_upload ) サイズ/色 属性調整 Tailwindクラス変更( text-6xl , text-blue-600 ) 結論 : HTMLの方が圧倒的に簡単。 SVGとHTMLの使い分けガイドライン 正直に言うと、 SVGとHTMLは適材適所 です。完璧なものは一発で作れませんが、使い分けることで効率化できます。 SVGを選ぶべきケース ベクター形式が必須 印刷物 高解像度ディスプレイ 拡大しても劣化させたくない ファイルサイズを最小化したい SVG: 2-5 KB PNG: 30-50 KB Figmaで後から編集する予定 SVGはFigmaで直接編集可能 複雑な図形を描く必要がある パスやベジェ曲線を使った図形 HTMLを選ぶべきケース シンプルなレイアウト 矩形、楕円形中心の図解 Flexbox/Gridで十分な場合 Tailwind CSSに慣れている クラス指定だけでスタイリング完了 Material Iconsを多用したい クラス指定だけで簡単実装 座標計算を避けたい レイアウトシステムで自動配置 修正の手間を最小化したい SVG: 頻繁に微調整が必要 HTML: 今回の3つの実例では全て修正不要 併用のすすめ プロトタイプ : HTML(速い、簡単) 図解のレイアウトを素早く確認 本番(ベクター重視) : SVG(品質高い) 印刷物や高解像度ディスプレイ向け 本番(レイアウト重視) : HTML(修正不要) ブログ埋め込み、SNS共有向け 実運用で遭遇する問題点と対処法 問題1: PNG変換が必須 問題 : HTMLファイル単体ではブログに埋め込めない(サイトによる) 対処法 : どうやってかPNGに変換(Playwright・スクショ・etc…) ワークフロー: HTML生成 → PNG変換 → ブログに埋め込み 問題2: Tailwind CDN依存 問題 : HTML生成時にインターネット接続が必要 対処法 : PNG変換時にChromiumが自動でCDNから取得 変換後はPNG画像なので、CDN不要 問題3: JavaScript/アニメーション禁止 問題 : 静的な図解のみ対応(インタラクティブ要素は不可) 対処法 : 静的な図解に用途を限定 インタラクティブな図解が必要なら別のアプローチを検討 SVG記事と比較して「発生しなかった」問題 文字列幅の不一致 : Tailwindの自動余白で解決 要素の重なり : Flexbox/Gridの gap で解決 カスタム矢印問題 : Material Iconsのクラス指定で解決 配置のバラつき : レイアウトシステムで解決 アクセシビリティ対応 WCAG Level AA準拠 コントラスト比 4.5:1以上の自動適用とスクリーンリーダー対応を実装しています。 実装例 : <div class="w-full h-full bg-white flex items-center justify-center" role="img" aria-label="3層アーキテクチャ図"> <!-- 図解の内容 --> </div> Tailwindでのコントラスト確保 用途 Tailwind Class コントラスト比 Primary bg-blue-500 , text-blue-900 4.5:1以上 Secondary bg-green-500 , text-green-900 4.5:1以上 Accent bg-orange-500 , text-orange-900 4.5:1以上 まとめ:SVGとHTMLを使い分けて効率化 HTML図解生成の評価 良い点 : レイアウトが簡単(Flexbox/Grid) 修正の必要性が低い(10-20%) Material Iconsが簡単(クラス指定だけ) 配色の統一が容易(Tailwind) 課題 : PNG変換が必須 ファイルサイズがやや大きい(30-50 KB) ベクター形式ではない 時短効果の測定 今回試した3つの図解で、作成時間を計測しました: 作業 手動作成(想定時間) HTML Skill生成 削減時間 3層アーキテクチャ図 約45分 5分(修正なし) 約40分 ユーザー認証フロー図 約30分 4分(修正なし) 約26分 Before/After比較図 約60分 6分(修正なし) 約54分 全ての実例で修正作業が不要 だったため、大幅な時短を実現できました。 PNG変換について HTMLファイルはブログに直接埋め込めない場合があるため、PNG画像に変換して使用します。 今回はHTML図解の生成に焦点を当てているため、PNG変換の詳細は別記事で扱う予定です。簡単に触れておくと、ブラウザでの手動スクリーンショットやPlaywright/html2imageなどのライブラリーでHTMLからPNG画像を生成できます。 次のステップ 関連記事 : SVG図解自動生成記事 ClaudeでMermaid図作成を自動化!2時間→5分の劇的時短術【Live Editor活用】 今後の展開 : PNG変換の詳細(別記事予定) 画像最適化のベストプラクティス コード例:Skillファイルの抜粋 HTML 図解生成 Skill の完全版は Gist で公開中 --- name: diagram-generator-html description: 技術ブログ記事用のHTML図解を生成しPNG画像に変換するスキル。 allowed-tools: Read, Write, Bash --- # HTML Diagram Generator Skill 技術ブログ記事用のHTML図解を自動生成し、PNG画像に変換するSkillです。 ## When to Use 以下の場合にこのスキルを使用してください: - ユーザーが「HTMLで図を作って」と依頼した場合 - ユーザーが「Tailwindで図解を生成して」と依頼した場合 - SVGで座標計算が面倒な場合 ## Design Specifications ### 基本仕様 - **固定サイズ**: 1280 x 720 px (16:9) - **フォーマット**: HTML5 + Tailwind CSS - **最終出力**: PNG画像 ### Tailwind CSS活用 - **CDN**: `https://cdn.tailwindcss.com` - **レイアウト**: Flexbox/Gridで自動配置 - **配色**: `bg-blue-100`, `text-blue-900`など統一されたクラス ### Material Icons統合 - **フォント**: Material Symbols Outlined - **CDN**: `https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined` - **使用例**: `<span class="material-symbols-outlined">database</span>` ## Supported Design Patterns 1. **アーキテクチャ図**: レイヤード、マイクロサービス 2. **フロー図**: プロセス、データフロー 3. **関係図**: ER図、クラス図 4. **比較図**: Before/After、パフォーマンス比較 5. **コンポーネント図**: システム構成 6. **概念図**: コンセプトマップ ## HTML Template Example ```html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>図解タイトル</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" /> </head> <body class="w-[1280px] h-[720px] m-0 p-0"> <div class="w-full h-full bg-gray-50 flex items-center justify-center p-12" role="img" aria-label="図解の説明"> <!-- 図解の内容 --> </div> </body> </html> ``` ## Accessibility Requirements - **role属性**: `role="img"` - **aria-label**: 図解の内容を説明 - **コントラスト比**: WCAG Level AA準拠(4.5:1以上) ## Workflow 1. ユーザーからの依頼内容を確認 2. HTMLファイルを生成 3. `docs/article/[feature-name]/images/original/` に保存 4. `html-screenshot` CLIでPNG変換 5. `docs/article/[feature-name]/images/` にPNG保存 最後まで読んでいただき、ありがとうございました! この記事が役に立ったら、ぜひSNSでシェアしてください。質問やフィードバックは、 @RyuReina_Tech や @SIOSTechLab でお待ちしています! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code SkillでHTML図解を自動生成!時短テクニック first appeared on SIOS Tech. Lab .
はじめに ども!Claude Code を執筆に贅沢活用している龍ちゃんです。気づいたら3年でブログを200本書いていて、そろそろブログの本数をカウントするのを取りやめですね(笑)。 最近は週に5本くらいブログを書くことが習慣づいているんですが、これだけブログを書いてると、だんだん図を作るのが億劫になってしまうんですよね。Claude Code のおかげで Mermaid での図解作成はすごく短縮できました(参考: ClaudeでMermaid図作成を自動化!2時間→5分の劇的時短術【Live Editor活用】 )。 今月は社内でそういう環境を提供する活動を始めていまして、その過程で「今まで SVG で作ることを諦めていたけど、もうちょっと頑張ってみようかな」ってところでスキル化したりしてます。 正直に言うと、完璧に動くものはまだできてないんですよね。 でも、比較的質の高い図を作れるようになったので、その方法を共有します。 作ってみて感じたこと 完璧なものを一発で作ることは不可能 でした。ライトなブログなら そのまま使えますが、ちゃんとした図を作るなら Figma などの専用ツールが必要です。 それでも、 図解作成時間を67%削減 (45分 → 15分)できたので、方法と課題・解決策を共有します。 この記事で伝えたいこと 完璧なものは絶対一発で作れません。 でも、現状の Skill を共有するので、一緒にベストプラクティスを模索していきましょう! この記事で扱う内容 : SVG 図解自動生成 Skill の実装 実運用で遭遇した問題点と対処法 (重要!) Mermaid.js との使い分け Claude Code Skill とは? .claude/skills/ ディレクトリに Markdown 形式のプロンプトファイル を配置することで、Claude がタスクを自動実行できる機能です。 Skill のメリット : 一度作成すれば繰り返し使える キーワードで自動トリガー チーム全体で共有可能 Note : SVG 図解生成 Skill の完全版は Gist で公開中 Skill の作り方について詳しくは Claude Code Skillの登録と実践!プロジェクト固有の処理を自動化する方法 を参照してください SVG 図解自動生成 Skill の実装 なぜ SVG を選んだのか? 一番の理由はFigmaで後から人力編集できるって点ですね。 アプローチ 用途 Mermaid.js システム図(クラス図、シーケンス図)→ 高精度で素早く生成 SVG 直接生成 概念図、ビジュアル重視の図解 → デザインの完全制御が可能 HTML + CSS インタラクティブな図解 結論 : デザインの自由度とアクセシビリティを重視し、SVG 直接生成を選択しました。Mermaid だと無機質になるケースで SVG が活躍します。 図解デザインの基本原則 正直に言うと、僕はデザイナーではないのでデザインパターンやコントラストの知識はないんですよね。 そこで、いったん Claude に調査を依頼して、それを Skill に取り込むという手法を採用しています。 以下は Claude に調査させて得られたデザインパターンです: 1. C4 Model(階層化) Context : システム全体の概念図 Container : サービス間の相互作用 Component : 個別コンポーネントの詳細 Code : クラス図・シーケンス図(Mermaid が得意) 2. 60-30-10 ルール(色使い) 60%: 背景、30%: 矩形、10%: アクセント(矢印) 3. 視覚的ヒエラルキーの7原則 Size / Color / Contrast / Alignment / Repetition / Proximity / Whitespace(padding 50px の根拠) ポイント : デザインの専門知識がなくても、Claude に「効果的な図解デザインのベストプラクティスを調査して」と依頼すれば、これらの原則を教えてくれます。それを Skill のプロンプトに組み込むことで、質の高い図解が生成できるようになるんですよね。 Skill ファイルの構成 .claude/skills/diagram-generator-svg.md を作成し、以下の要素を含めます: 主要な指示内容 : Material Icons の統一使用 (desktop_windows、settings、storage など) WCAG Level AA 準拠 (コントラスト比 4.5:1) レイアウトガイドライン (padding 50px、font-size 16-32px) アクセシビリティ対応 ( <title> と <desc> 要素) SVG 図解生成 Skill の完全版は Gist で公開中 実装のポイント これだけ設定していてもがっつり無視してきたりするので、きれずに根気よく会話していきましょう。 1. Material Icons の統一使用 Skill のプロンプトで「Material Icons を使用」と指示しても、Claude は時々カスタム矢印( <path> + marker-end )を生成してしまいます。これを防ぐため、以下を明記: これを書いていても無視しますよww 3. **Layout Guidelines** - Arrows should use Material Icons (arrow_downward, arrow_forward) - DO NOT use custom `<marker>` or `<marker-end>` 2. アクセシビリティ対応 WCAG Level AA 準拠のため、コントラスト比を明示: 2. **Accessibility (WCAG Level AA)** - Color contrast ratio ≥ 4.5:1 - Text color on background must meet WCAG AA standards 3. 配置の明確化 要素間の余白を明示的に指定: - Use consistent spacing (50px padding between elements) - Text should not overflow rectangles 実運用で遭遇する問題点 ここからが重要です。実際に Skill を使ってみると、 プロンプト通りに生成されないことが多々あります 。以下、僕が実際に遭遇した問題と対処法です。 問題1: 文字列幅の不一致 症状 : 説明文(例: “UI・ユーザーインターフェース (React, Vue.js, Angular)”)が矩形からはみ出す 原因 : Claude が文字列の実際の表示幅を正確に計算できない 対処法 : <!-- Before: font-size="18" だと長い --> <text font-size="18">UI・ユーザーインターフェース (React, Vue.js, Angular)</text> <!-- After: font-size="16" に縮小 --> <text font-size="16">UI・ユーザーインターフェース (React, Vue.js, Angular)</text> 僕の場合、最初に生成されたSVGを見て「あ、これ文字はみ出てるな」って気づいたら、すぐにfont-sizeを調整するようにしています。 問題2: 要素の重なり 症状 : 矢印と矩形が重なって見える 原因 : padding が不十分(40px では足りない) 対処法 : <!-- Before: padding 40px --> <rect y="80" height="140"/> <g transform="translate(620, 220)"> <!-- 80 + 140 = 220 --> <!-- Arrow --> </g> <rect y="260" height="140"/> <!-- 220 + 40 = 260 --> <!-- After: padding 50px --> <rect y="80" height="140"/> <g transform="translate(620, 230)"> <!-- 80 + 140 + 10 = 230 --> <!-- Arrow --> </g> <rect y="270" height="140"/> <!-- 230 + 40 = 270 --> これは視認性の問題なんですが、40pxだと視覚的に「ちょっと詰まってるな」って感じがしたので、50pxにしたら見やすくなりました。 問題3: カスタム矢印の生成 症状 : Material Icons を指示しても、 <path> + marker-end のカスタム矢印を生成してしまう Claude が生成するコード(NG) : <!-- NG: カスタム矢印 --> <path d="M 640 220 L 640 260" stroke="#424242" stroke-width="3" marker-end="url(#arrowhead)"/> <defs> <marker id="arrowhead" markerWidth="10" markerHeight="10"> <polygon points="0 0, 10 5, 0 10" fill="#424242"/> </marker> </defs> 対処法 : Material Icons の arrow_downward を直接指定 <!-- OK: Material Icons --> <g transform="translate(620, 230)"> <path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z" fill="#757575" transform="scale(1.5)"/> </g> これ、本当によくあるんですよね。プロンプトに明記してても無視されるので、生成後に手動で置き換えるのが確実です。 問題4: 配置のバラつき 症状 : 矩形の高さや幅、要素間の距離が統一されていない 対処法 : プロンプトで数値を明示 ## Layout Specifications - Rectangle width: 900px - Rectangle height: 140px - Padding between elements: 50px - Icon size: 48px (Material Icons scale(2)) - Arrow size: 36px (Material Icons scale(1.5)) 数値を具体的に指定しておくと、生成される図の一貫性が上がります。ただし、それでもズレることはあるので、最終的には目視確認が必要ですね。 修正ワークフロー 実際の図解作成は以下のフローで進めます: Skill で初回生成 (所要時間: 5分) プロンプトを渡して SVG を生成 構造は正しいが、細かい問題がある状態 手動修正 (所要時間: 10分) 文字列のはみ出しをチェック → font-size 調整 要素の重なりをチェック → padding 調整 カスタム矢印をチェック → Material Icons に置換 配置のバラつきをチェック → 数値を統一 ブラウザで確認 SVG ファイルを VScode で開いて視覚確認 問題があれば 2 に戻る 合計所要時間: 15分 (従来の45分から67%削減!) 僕の場合、Figmaで一から作ると構造を考えるところから始まって45分くらいかかってたんですが、Skillで構造を生成してもらえるだけで圧倒的に楽になりましたね。 生成例:Before → After 比較 実際の図解生成プロセスを、 初回プロンプト → オリジナル画像 → 修正プロンプト → 修正後画像 の流れで紹介します。 例1: 3層アーキテクチャ図 ステップ1: 初回プロンプト SVG図解を生成してください。 タイトル: 3層アーキテクチャ 内容: Presentation Layer、Business Logic Layer、Data Access Layerの3層構造を示す図 各層に以下の情報を含めてください: - Presentation Layer: desktop_windows アイコン、説明「UI・ユーザーインターフェース (React, Vue.js, Angular)」 - Business Logic Layer: settings アイコン、説明「ビジネスロジック (Services, Use Cases, Domain Logic)」 - Data Access Layer: storage アイコン、説明「データアクセス (Repository, ORM, Database Queries)」 各層を矢印で接続してください。 ステップ2: オリジナル生成結果 問題点 : 説明文の font-size が大きすぎる(18px) 矢印が <path> + marker-end のカスタム実装 padding が不十分(40px)で要素が詰まって見える このとき僕は「あー、やっぱり文字はみ出てるし、矢印も微妙だな」って思いました。でも構造自体は正しいので、修正するだけで済むのが楽なんですよね。 ステップ3: 修正プロンプト Claude に以下を指示: 以下の修正を適用してください: 1. 説明文の font-size を 18px → 16px に縮小 2. 矢印を Material Icons `arrow_downward` に置換 3. padding を 40px → 50px に拡大して余白を確保 ステップ4: 修正後の結果 改善点 : 説明文が矩形内に余裕を持って収まる Material Icons で統一されたデザイン 適切な余白で視認性が向上 これで見た目がかなりすっきりしました。僕的にはこのレベルなら公開用として十分使えると思います。 例2: ユーザー認証フロー ステップ1: 初回プロンプト SVG図解を生成してください。 タイトル: ユーザー認証フロー 内容: ログインから認証情報検証、セッション確立までのプロセスフロー 以下のステップを含めてください: 1. 開始(楕円形、緑色) 2. 認証情報入力(矩形、青色) 3. 認証情報検証(矩形、青色) 4. セッション確立(矩形、青色) 5. 完了(楕円形、赤色) 各ステップを矢印で接続してください。 ステップ2: オリジナル生成結果 問題点 : 全4箇所の矢印がカスタム実装( marker-end 使用) 矢印とステップの重なりが見える ステップ間の padding がバラバラ フロー図って矢印が命なんですが、カスタム矢印だと統一感がなくなるんですよね。 ステップ3: 修正プロンプト 以下の修正を適用してください: 1. 全4箇所の矢印を Material Icons `arrow_downward` に置換 2. ステップ間の padding を 50px に統一 3. 矢印とステップの位置を調整して重なりを解消 ステップ4: 修正後の結果 改善点 : Material Icons で統一された矢印 各ステップ間の余白が均一 すっきりとした視覚的な流れ これでフローが追いやすくなりました。技術ブログの図解としては十分なクオリティだと思います。 例3: Before/After 比較図 ステップ1: 初回プロンプト SVG図解を生成してください。 タイトル: 図解作成の時短効果比較 内容: 手動作成とSkill生成の作業時間・効率を比較するBefore/After図 Before側(左側、赤色): - 作業時間: 45分 - ツール: Figma / PowerPoint - 課題: 時間がかかる After側(右側、緑色): - 作業時間: 15分 - ツール: Claude Code Skill - 内訳: 生成5分 + 修正10分 - 効果バッジ: 67% 時短! BeforeからAfterへ横向き矢印を配置してください。 ステップ2: オリジナル生成結果 問題点 : 横向き矢印がカスタム実装( <path> + marker-end ) Before/After の横にアイコン(警告・チェックマーク)があり、「作業時間」と重なる 「作業時間」と数値(45分・15分)の間隔が狭い 矢印の位置が中央からずれている このときは「うーん、情報詰め込みすぎて見づらいな」って感じでした。 ステップ3: 修正プロンプト Claude に以下を指示: 以下の修正を適用してください: 1. 横向き矢印を Material Icons `arrow_forward` に置換 2. Before/After の横のアイコンを削除してテキストを中央配置 3. 「作業時間」と数値の間隔を 30px → 40px に拡大 4. 矢印を Before/After セクション全体の中央(y=368付近)に配置 ステップ4: 修正後の結果 改善点 : Material Icons で統一された横向き矢印 Before/After テキストがすっきりと中央配置 「作業時間」と数値の間に適切な余白 矢印が視覚的に中央に配置され、バランスが向上 これで見やすくなりました。Before/After図は情報量が多いので、余白をしっかり取るのが大事ですね。 Mermaid.js との使い分け 僕が以前書いた記事「 ClaudeでMermaid図作成を自動化!2時間→5分の劇的時短術【Live Editor活用】 」で紹介している Mermaid.js は、 システムチックな内容 を表現する際に結構高い精度で図を作ってくれる最高なツールです。 一方で、 概念的な話 になると Mermaid だと無機質になってしまうケースがあるんですよね。そこで SVG 直接生成の出番です。 具体的な使い分け 用途 推奨アプローチ 理由 例 システム図 Mermaid.js 記法が簡単、高精度 クラス図、シーケンス図、ER図 概念図 SVG 直接生成 ビジュアルで魅せられる アーキテクチャ概念図、比較図 データフロー Mermaid.js 専用記法で効率的 パイプライン、処理フロー デザイン重視 SVG 直接生成 色・レイアウトの完全制御 ブランディング重視の図解 Material Icons SVG 直接生成 Mermaid は非対応 Google Cloud アイコン使用 僕の実践的な使い分け基準 開発フェーズ別 : 記事の下書き段階 : Mermaid.js(速さ重視、素早くイメージ共有) 公開用の図解 : SVG 直接生成(品質重視、ビジュアルで差別化) 内容の性質別 : 技術的な関係性 : Mermaid.js(クラス継承、API 呼び出し順序など) コンセプト・アイデア : SVG 直接生成(3層アーキテクチャの概念、Before/After など) 結論 : Mermaid と SVG は競合ではなく 補完関係 。両方使いこなすことで、技術ブログの図解表現力が大幅に向上します。 僕の場合、システム図はMermaidでさくっと作って、ビジュアルで魅せたい図はSVGで作る、みたいな使い分けをしていますね。 まとめ Claude Code Skill を使った SVG 図解自動生成により、 図解作成時間を67%削減 できました!ただし、完璧な図解が一発で生成されるわけではなく、以下のような修正が必要になることが多いですね: よくある修正内容 font-size の調整(18px → 16px) padding の拡大(40px → 50px) カスタム矢印を Material Icons に置換 配置の微調整 それでも、Figma で一から作るより圧倒的に速いです。僕の場合、図解作成が億劫で記事執筆が進まない…ということがなくなりました。 この記事で得られる3つのメリット 時短効果 : 45分 → 15分(67%削減) 再現性 : Skill をコピーすればチーム全員が同じ品質で図解作成可能 継続的改善 : 修正プロンプトを蓄積してベストプラクティスを共有できる 次回以降の技術解説シリーズ 今回は SVG 図解の自動生成に焦点を当てましたが、今後は以下の内容を解説していきます! HTML 図解自動生成 (Tailwind CSS 使用) SVG → PNG 変換自動化 (CairoSVG) HTML → PNG 変換自動化 (Playwright) ブログやコンテンツを定期的に発信している方は、ぜひ参考にしてみてください!質問や感想は、コメント欄でお待ちしております。 参考リンク Claude Code Claude Code 公式ドキュメント ClaudeでMermaid図作成を自動化!2時間→5分の劇的時短術【Live Editor活用】 Material Design & Icons Material Icons Material Symbols(推奨) Google Cloud Icons アクセシビリティ WCAG 2.1 Level AA デザインパターン C4 Model – Software Architecture Visual Hierarchy Guide UI Color Palette Best Practices ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 図解作成が驚くほど楽に!Claude SkillでSVG自動生成 first appeared on SIOS Tech. Lab .
今月リリースされたばかりのサービス「Studio.Stock」を紹介する記事です。 機が熟した Studio.Stockは、AIの技術に、参加する画像クリエイターのセンス、運営する編集者の審美眼が加わった、ある意味で「最強」の「フォトストックサービス」です。 掲載されている素材画像はすべて、クリエイターがAIで生成したもの。 https://stock.studio.design 利用者は、おしゃれな「フォト」を選ぶだけ。 ウェブやプレゼンテーションなど、商用利用も含めてクレジット表記不要、無料で画像を利用ができます。 AI生成画像の特性を活かして、素材画像として「都合の良い」モチーフ、構図、ライティングの作品が次々にストックされていくことが想像できます。 Googleが今年リリースした、Geminiの画像生成「Nano Banana」は革新的で、 とてもリアルな画像生成が、簡単にできるようになりました 。 「AIで作成した写真的画像は、どこか不自然、質感がヌルッとしている」といった違和感が払拭されました。 つまり、この分野は「不気味の谷を超えた」と言えそうです。 シンプル Studio.Stockは、以前 このブログで紹介した「Unsplash」 と同様に、すっきりシンプルなUI。 シンプルなだけではなく、色をキーにした画像検索や、切り抜きや縦横比変更、プロンプトで加工、など便利な機能も盛り込まれています。 日本 国際的なフォトストックサービスでは、日本人モデル、風景、食べ物など、日本の写真のバリエーションが少ない傾向にあります。 日本のフォトストックサービスは、テイストが合わず、写真で利用に至らないこともありました。 Studio.Stockは、画像のモチーフを日本のものを中心に、企画されているようです。(現時点では) 厳選 歴史の長い他のフォトストックサービスには、写真やイラスト、AIによるフォトリアルな画像も多く登録されています。 選択肢が多いのは良いのですが、その分、画像を探すのに時間を要します。 Studio.Stockの世界観は限定されていますが、このテイストが気に入れば、すぐに高品質な画像にたどり着けるでしょう。 使い分け AI製画像が不気味の谷を超えても、やはり撮影された写真が適しているケースは多くあります。 また、一定以上の規模の案件や、印刷物で利用する場合は、高解像度の画像が必要であったり、ライセンス拡張が必要な場合もあります。 有償、無償合わせて、状況に合ったサービスを利用するのも大切かと思います。 まとめ 今の技術とスタイルが、小気味良く具体化されたStudio.Stock。 今後、参加するAIクリエイターは増え、有料機能を含めた機能の更新もあるようです。 このサービスの親サービスとも言える『ノーコードWeb制作プラットフォーム「Studio」』のWeb制作でも、Studio.Stock画像がシームレスに利用できるようになることも想像されます。 Studio.Stockについては、公式の記事にわかりやすい説明がありますので、そちらもご覧ください。 https://studio.design/ja/whats-new/studiostock ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AI画像は不気味の谷を超えたか? – Studio.Stock first appeared on SIOS Tech. Lab .
はじめに こんにちは、サイオステクノロジーの安藤 浩です。 AWS認定ソリューションアーキテクト アソシエイト試験(AWS SAA-C03)に合格したので、やったことを記載します。(合格してから投稿が遅くなっていました) 実務でAWSを利用する予定だったこととクラウドはAzureは今まで経験してきたためクラウドの体系的な知識をつけておくのは良いかなと思ったため、受験しようと思いました。 AWS 認定資格 ソリューションアーキテクト アソシエイトとは? AWS認定ソリューションアーキテクト – アソシエイト(SAA-C03)は、AWSクラウドにおけるソリューション設計の基礎的なスキルを証明する認定資格です。 試験の概要 試験時間 : 130分 問題数 : 65問(選択問題) 合格ライン : 720点/1000点 試験形式 : CBT(テストセンター or オンライン監督付き) 試験費用 : 150USD(20,000円) 有効期限 : 3年間 対象者 以下のような方に適した資格です。 AWSクラウドで1年以上の実務経験を持つ方 AWSサービスを使ったソリューション設計の知識を身につけたい方 クラウドアーキテクチャの基礎を体系的に学びたい方 AWS上でのコスト効率的なシステム設計を理解したい方 私の簡単な経歴 10年程度のエンジニア AWS実務経験ほぼなし 1〜2年前に個人的にVPCやサブネットなど知識が薄そうな部分をハンズオン形式で以前やったことがある程度 Azure 中心での実務経験あり 学習内容 学習期間・時間 期間 : 7月中旬〜10月頭 平日 : 1-2時間 (朝 or 子供が寝てから) 休日 : 4-5時間 途中、実務が忙しくなりできていない期間もありました。 使用教材 Udemy問題集 【SAA-C03版】これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座 実際は初めのほうの動画を見てハンズオンもすると2、3倍くらいかかりそうなことがわかったので、以下の動画やハンズオンをしてみました。 IAM の概要把握 VPC、サブネット、S3などのハンズオン セキュリティ関係のリソースの概要 など すべての動画で50時間くらいあるので、すべての動画をみてハンズオンをする時間はなさそうなので、出来る限り概要を把握して早めに以下の模擬試験をやってみることにしました。 【2025年版】AWS認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問) 1週目で1つの模擬試験を終わるまでに5日くらいかかってしまいました。 説明を読むのも結構時間がかかるので、推奨されるアーキテクチャや利用用途などを聞いて Claude やGemini で理解しました。 また、夜に学習すると眠くなってくるので、出来るだけClaude やGeminiへ質問をして、間違えた問題を中心にNotionにネットワーク系、コンテナ系、認証系などで分類していきました。ClaudeやGeminiには 試験での判断フロー、図式、覚えるべきポイント、ほかの選択肢が不正解である理由、実際の設計パターンなどをまとめて質問して内容をみていました。 おそらく、3週目くらいで5割くらい得点が取れるようなったかと思いますが、72%が合格ラインなので全体で80%くらいとれるまでを目指していました。最終的に5週くらいして7-8割くらいになった状態でした。 このあたりから間違えた問題をまとめたNotionのページやClaude やGemini に質問をして説明をみたり、図解してもらうなどして理解を深めていきました。 Ping-tも無料枠で少しやりましたが、あまり多く手を出すと理解が浅くなるかなと思い途中でやめました。 試験直前の1週間 試験対象のAWSサービス を見て、このサービスは何ができるか、どんなサービスと組み合わせるのか、違いなどを空で言えるようにしました。 分析 系のサービス(例: Amazon Athena, AWS Glue, Amazon Kinesis Stream, Amazon Kinesis Data Firehose など)、 ネットワークとコンテンツ配信 はなかなか覚えられなかったので、Notionにカテゴライズしたものを見て復習しました。 試験当日・受験後 自宅でも受けられますが、部屋を片付けるのが面倒なのでテストセンターで受験しました。初めてテストセンターで受けたのでノイズを遮断するヘッドセットの使い方がわからなかったことや、画面上の問題を読むのに慣れるまでやや時間がかかりました。 試験時間とどのくらいの件数を解答中に解答できてないとまずいかをちゃんと把握できている必要があったかと思います。見直す時間がほぼなかったことが反省点でした。 また、Budget, Cost Explorer などの問題がありコスト系のサービスをほぼ学習していなかったのでこの点も反省点です。 受験結果 720/1000 が合格ラインで731/1000 で合格ラインギリギリでした。 まとめ 以下、反省点を踏まえてまとめです。今後、試験を受ける際は以下の点に気を付けたいと思います。また、受験される方の参考になればと思います。 サービスを網羅的  に学習する AI活用 (Claude、Gemini)で効率化 問題演習メイン で弱点部分を見つける 時間配分の練習 は必須(私のように焦らないために) 試験だと思って問題を解く 参考URL AWS Certified Solutions Architect – Associate (SAA-C03) 試験ガイド AWS 認定ソリューションアーキテクト – アソシエイト認定 【SAA-C03版】これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座 【2025年版】AWS認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AWS 認定資格 ソリューションアーキテクト アソシエイトに合格しました first appeared on SIOS Tech. Lab .
5秒でわかる:この記事の内容 タイトルは挑発的ですが、真面目な技術記事です。 やったこと : ブログ記事のHTML→Markdown変換を実装 トークン削減率:平均 20.7% (3記事で実測) 既存実装と組み合わせて累積 65%削減 を達成 得られるもの : markdownifyライブラリを使った実装方法(コード付き) 段階的なトークン削減の実測データ WordPressブログのスクレイピングからMarkdown化まで 対象読者 : AIを活用したブログ執筆をしている人 RAG(もどき)システムを構築したい人 トークン削減に興味がある技術者 関連記事:より深く理解するために この記事は「AI活用ブログ執筆ワークフロー」シリーズの一部です。以下の記事を読むと、さらに理解が深まります。 必読 : 検証→記事化ワークフロー → 「RAGもどき」のアイデアを初めて紹介した記事。本記事はこのアイデアの実装編です。 あわせて読みたい : Claude Code Skills 実装ガイド → 今回の実装をClaude Code Skillとして統合する方法を解説。 3フェーズ開発 → AI活用開発ワークフローの基礎。検証→実装→記事化の流れ。 仕様書アレルギー克服 → AI活用で仕様書作成を効率化する方法。ワークフローの土台。 法的注意:必ずお読みください この記事の実装は SIOS Tech Lab専用 です。他サイトへの適用には、必ず事前の許可取得が必要です。無断スクレイピングは法的リスクがあります。詳細は本文の「 重要:本記事の対象範囲と法的注意事項」をご確認ください。 はじめに ども!記念すべき200本一本前のブログを執筆している龍ちゃんです。最近はブログの執筆が爆速になっているのですが、やはり検証リポジトリに執筆環境を統合したのが影響が大きかったと思います。執筆からレビューまで贅沢にClaude Codeを活用させてもらっています。 今回は、 執筆環境を作成する際に「RAGもどき」システム を作っているのですが、そちらの実践編です。「RAGもどき」というのは、ベクトルストアなんて贅沢なものは人間の頭で代用して、ファイルベースでファイルをローカルに管理するという人力の仕組みですw。 「RAGもどき」について ちなみに、この「RAGもどき」という表現ですが、実は RAGの定義からすると「もどき」じゃない んです。 RAGの本質とは? 原典論文 :Lewis et al. (2020) “ Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks ” (NeurIPS 2020) この論文では、RAGを「 パラメトリック(事前訓練済みseq2seqモデル)と非パラメトリック(検索可能な外部知識)メモリを組み合わせる 」汎用的なアプローチと定義しています。 重要なポイント : ベクトルストアは 必須要件ではない (知らなかった!) 論文中ではWikipediaの密集ベクトルインデックスを使用していますが、これは一例 検索可能な外部知識源であれば、どのような形式でも良い つまり、ファイルシステム上のMarkdownファイル群も、立派な「検索可能な外部知識源」なんです。 本記事のアプローチ : ローカルファイルベース : Claude Codeのコンテキストにブログ記事(.mdファイル)を含める 人間が選択 : AIの自動検索ではなく、自分(執筆者)が関連記事を手動で選択 小規模に最適 : 数十〜数百件のブログ記事セットに向いている 高精度 : 自分が書いた記事は自分が一番の専門家!的確な選択が可能 厳密に言えば : RAGシステムの「Retrieval(検索・取得)」部分を人間が担当している形です。医療や法律などの専門分野では、専門家が知識ベースを手動でキュレーション(厳選)してからRAGシステムに組み込むアプローチが取られており、本記事のアプローチはその簡易版と言えます。 本記事での呼び方 : 親しみやすさを重視して「RAGもどき」という表現を使っていますが、技術的には立派なRAGアプローチです。でも「もどき」って書いていると、RAGに詳しい人が気になって読んでくれるかな…とか、親しみやすさが出るかな…と思って….. ブログの取得に関してをスクリプト化して、Claude CodeのSkillとして登録することでURLを投げるだけでローカルに投稿済みのブログを取得する仕組みの実装についてトークン圧縮・可読性の観点から解説していきます。 重要:本記事の対象範囲と法的注意事項 本記事の実装について この記事は SIOS Tech Lab(https://tech-lab.sios.jp)のブログ記事 を対象としたHTML→Markdown変換の実装例です。 対象読者 : SIOS Tech Labの記事を扱う方(社内利用) WordPressベースのブログをスクレイピングしたい方 同様のHTML構造を持つサイトを扱う方 この実装の前提 : WordPressベースのブログサイト メインコンテンツが section.entry-content に格納されている OGPメタタグが適切に設定されている 注意 : CSSセレクタやHTML構造は他サイトでは異なる可能性があります。適宜カスタマイズが必要です。 他サイトへの適用時の法的注意 この記事の実装を 他サイト に適用する際は、以下の点に十分注意してください。 法的リスク : 無断スクレイピングは 利用規約違反 や 不正アクセス禁止法違反 に該当する可能性があります 最悪の場合、 民事訴訟 や 刑事告訴 のリスクがあります 「技術的に可能」≠「法的に許可されている」 必ず実施すべきこと : 利用規約の確認 robots.txtの確認(スクレイピング禁止の記載がないか) サイトの利用規約を熟読 自動アクセスが明示的に禁止されていないか確認 許可の取得 サイト運営者に事前に連絡 スクレイピングの目的を説明 書面またはメールでの許可を取得(証拠として保存) 負荷への配慮 アクセス頻度を制限(リクエスト間隔を設ける) サーバーに負荷をかけない 深夜帯など、アクセスが少ない時間帯に実行 安全なスクレイピング対象 : 自社サイト(許可不要) 明示的に許可されているサイト(API提供など) パブリックドメインのデータ 危険なスクレイピング対象 : 利用規約で禁止されているサイト robots.txtで禁止されているページ 会員制サイト(ログインが必要な情報) 営業秘密や個人情報を含むデータ 免責事項 : 本記事の実装を使用したことによる法的トラブルについて、著者およびSIOS Technologyは一切の責任を負いません。ご自身の責任において、適切な許可を得た上で使用してください。 概要 以前の記事 でRAGもどきシステムのアイデアを紹介しました。既存のブログ記事をAIに読み込ませることで、記事の重複チェックや文体・構成の一貫性を保つという仕組みです。 実はその後、HTML形式でブログ記事を保存する実装は密かに動いていました。ヘッダーやフッター、サイドバーを除去して、メインコンテンツだけを抽出するシンプルな処理です。ページ全体から見ると50-60%のトークン削減を実現していて、それなりに機能していました。 しかし、使い続けるうちに新たな問題が見えてきました。 新たな課題 HTMLで保存してもトークンがまだ多い 特に長い記事(10,000トークン以上)では、HTMLタグ( <div> , <p> , <section> )がトークンを消費し続けていました。例えば9,470トークンの記事では、まだ削減の余地がありそうでした。 可読性の問題 HTMLタグが邪魔で、可読性が低い状態でした。人間が読むのが辛い状態です。 解決策:Markdown変換 そこで、 HTML → Markdown変換 を実装することにしました。 処理フローは以下の通りです: 生HTML(ページ全体) ↓ ContentCompressor(既存実装) 抽出HTML(記事本文のみ) ↓ HtmlToMarkdownConverter(今回実装)← NEW Markdown(最終形態) 削減効果 : 抽出HTML → Markdown: 20.7%削減 (今回実装) 生HTML → Markdown: 累積64.9%削減 (全体) 構造保持+可読性向上 この記事で学べること 処理フロー全体像 :生HTML → 抽出HTML → Markdown 実測データ :3記事で見る削減効果(段階別) 実装方法 :markdownifyライブラリとカスタマイズ 累積効果 :既存実装との組み合わせで65%削減 リポジトリ サンプルコード(公開リポジトリ) : uv-single-devcontainer/examples/blog-scraper すぐに試せる完全なサンプルコード uvを使った開発環境テンプレート README付きで使い方も簡単 HTMLの限界:なぜMarkdownが必要だったのか 既存実装の振り返り まず、既存のHTML抽出処理について軽く説明します。 RAGもどきのアイデア として、既存ブログ記事をAIに読み込ませる仕組みを考えていました。記事の重複チェック、文体・構成の一貫性確保が目的です。記事50103では アイデアのみ紹介 していましたが、実装詳細は未公開でした。 実は既に実装していた んです。HTML形式でブログ記事を保存するシステムです: ヘッダー・フッター・サイドバーを除去 メインコンテンツ( section.entry-content )のみ抽出 CSS装飾や不要な属性を削除 トークン削減率 : 50-60%(ページ全体から) シンプルな実装内容 : # CSSセレクタでメインコンテンツを抽出 target = soup.select_one("section.entry-content") # 不要なタグを削除 for tag_name in ["script", "style", "noscript"]: for tag in target.find_all(tag_name): tag.decompose() # 属性を削除(href/alt/srcは保持) self._remove_attributes(target) これだけのシンプルな処理ですが、それなりに機能していました。 新たに発見した課題 しかし、長い記事での問題が見えてきました(10,000トークン以上): 1. トークン数がまだ多い HTMLタグ( <div> , <p> , <section> )がトークンを消費していました。例えば9,470トークンの記事では、まだ削減の余地がありました。 2. 可読性の問題 HTMLタグで可読性が下がっていました。AIが構造を理解しづらく、人間が読むのも辛い状態です。 具体例で見るHTMLの冗長性 同じ内容をHTMLとMarkdownで比較してみましょう。 HTML(30トークン) : <section> <h2>見出し</h2> <p>これは段落です。</p> <ul> <li>項目1</li> <li>項目2</li> </ul> </section> Markdown(20トークン) : ## 見出し これは段落です。 * 項目1 * 項目2 削減率 : 33% HTMLタグの除去だけで、これだけトークンを削減できます。 解決策:Markdown変換への移行 なぜ最初はHTMLタグを残していたのか? 実は、HTMLタグを残していたのには理由がありました。 構造情報の保持が目的 でした。 <h2> , <ul> , <section> などのタグは、単なる装飾ではなく、文章の構造を表す重要な情報です。これを完全に削除してしまうと、AIが記事の階層構造を理解しづらくなる可能性がありました。 「見出しはどれか」「リストはどこか」「どの段落がどのセクションに属するか」といった情報は、AIが記事を理解する上で重要なんですよね。 でも、問題がありました: HTMLタグがトークンを消費し続ける 可読性が低い 人間が読むには辛い そこで考えたのが、 Markdownへの変換 です。 なぜMarkdownなのか? Markdownなら、当初の「構造を保持したい」という意図を守りつつ、トークン削減と可読性向上の両立ができます。 理由は4つあります: 構造を保持 見出し(H1-H6)、リスト、テーブル、コードブロック、リンク、画像など、必要な構造要素をすべて保持できます。HTMLタグの構造情報をそのまま引き継げるんです。 可読性が高い プレーンテキストに近く、AIも人間も読みやすい形式です。GitHubでのレビューも容易です。 トークン数が少ない タグが不要で、 ## 、 * 、 - 等の記号のみで表現できます。属性も不要です。 YAML frontmatter対応 メタデータを構造化して保存できます(title, url, image)。 converted_at でバージョン管理も可能です。 つまり、こういうことです: 構造情報は保持( ## , ### , * , - で表現) トークン数は大幅削減(HTMLタグが不要) 可読性も向上(プレーンテキストに近い) 構造を残したい という当初の意図を守りつつ、トークン削減と可読性向上の両立を実現できたわけです。 Markdownの実例 実際の出力例を見てみましょう。 YAML frontmatter : --- title: "記事タイトル" url: https://tech-lab.sios.jp/archives/50103 image: https://... converted_at: 2025-11-11T06:13:55 --- # 記事本文... メタデータが構造化され、記事本文はMarkdown形式で保存されます。 実装の詳細 処理フロー全体像 まず、処理フロー全体を把握しましょう。 3段階のトークン削減 : 📄 生HTML(ページ全体) ↓ ① ContentCompressor(メインコンテンツ抽出) 📄 抽出HTML(記事本文のみ) ↓ ② HtmlToMarkdownConverter(Markdown変換) 📝 Markdown(最終形態) 各段階の役割 : ContentCompressor : 不要な要素を削除 ヘッダー、フッター、サイドバー除去 script/style/noscript削除 不要な属性削除(href/alt/srcは保持) セレクタ: section.entry-content HtmlToMarkdownConverter : Markdown変換 HTMLタグ → Markdown記法 長いalt属性の簡略化 YAML frontmatter追加 トークン削減効果 : 生HTML → 抽出HTML: 50-60%削減 (既存実装) 抽出HTML → Markdown: 20.7%削減 (今回実装) 累積削減率 : 約 65% (生HTML → Markdown) ContentCompressor(既存実装) SIOS Tech Lab特有の実装 です。以下のHTML構造に依存しています: # SIOS Tech LabのHTML構造に特化したセレクタ target = soup.select_one("section.entry-content") # ← Tech Lab固有 # script/style/noscript削除 for tag_name in ["script", "style", "noscript"]: for tag in target.find_all(tag_name): tag.decompose() # 不要な属性削除(href, alt, srcは保持) self._remove_attributes(target) これだけのシンプルな処理 : ヘッダー・フッター・サイドバー除去 CSS装飾の削除 55.7%のトークン削減(35,420 → 15,680トークン) 他サイトへの適用時の注意 : section.entry-content は SIOS Tech Lab特有 のセレクタです あなたのサイトに合わせて変更してください: # 例: 別のWordPressテーマの場合 target = soup.select_one("article .post-content") target = soup.select_one("div.entry-body") 問題点 : HTMLタグが残っている( <div> , <p> , <section> ) 可読性が低い さらなる削減の余地あり → ここで Markdown変換 の出番です。 ライブラリ選定:6つの候補から選んだ理由 HTML→Markdown変換のために、6つのライブラリを比較しました。 比較したライブラリ : html2text markdownify (採用) html-to-markdown trafilatura html2md Pandoc markdownify採用理由 : BeautifulSoup統合 : 既存のHTMLパース処理を活用できる カスタマイズ可能 : MarkdownConverterクラスを継承して独自の変換ロジックを実装できる 高品質な変換 : テーブル、リスト、見出しを適切に処理 アクティブなメンテナンス : 継続的に更新されている BlogMarkdownConverterのカスタマイズ markdownifyの MarkdownConverter を継承して、 SIOS Tech Labのブログ記事用 にカスタマイズしました。 実装のポイント : class BlogMarkdownConverter(MarkdownConverter): def convert_div(self, el, text, *args, **kwargs): """div タグはテキストのみ抽出""" return text def convert_img(self, el, text, *args, **kwargs): """長いalt属性(100文字以上)は簡略化 SIOS Tech Lab特有の問題: - WordPressのMermaidプラグインがalt属性に図のコード全体を格納 - 例: alt="graph TD; A-->B; C-->D; ..."(数百〜数千文字) - これをそのままMarkdownに変換するとトークンを大量消費 """ alt = el.get("alt", "") src = el.get("src", "") if len(alt) > 100: return f"![image]({src})" # 簡略化 return super().convert_img(el, text, *args, **kwargs) カスタマイズ内容 : div / span : テキストのみ抽出(タグを除去) img : 長いalt属性を ![image] に簡略化( Mermaid図対応 ← Tech Lab固有) 長いalt属性の簡略化は、WordPressのMermaidプラグインがalt属性に図のコード全体を格納する問題に対応したものです。100文字以上のalt属性は ![image] として簡略化することで、可読性を保っています。 他サイトへの適用 : あなたのサイトでMermaid図を使っていない場合、この処理は不要かもしれません。サイトの特性に合わせてカスタマイズしてください。 完全な処理フロー 実装の詳細ステップを見ていきましょう。 1. HTML取得 (BlogScraper) soup = scraper.fetch_html(url) original_html = str(soup) # 生HTML(ページ全体) 2. メタデータ抽出 (OGPタグから) metadata = extract_metadata_from_html(original_html) # title, url, image を抽出 3. コンテンツ抽出 (ContentCompressor) extracted_element = compressor.extract_content(soup) extracted_html = str(extracted_element) # 記事本文のみ 4. 空白圧縮 compressed_html = compressor.compress_whitespace(extracted_html) 5. Markdown変換 (HtmlToMarkdownConverter)← NEW markdown_content = converter.convert(compressed_html, metadata) # YAML frontmatter + Markdown本文 6. トークン数計算 markdown_tokens = estimator.estimate_tokens(markdown_content) # 削減率を計算 7. Markdown保存 (.mdファイル) markdown_path.write_text(markdown_content, encoding="utf-8") トークン削減の流れ : 35,420トークン(生HTML) ↓ ContentCompressor(-55.7%) 15,680トークン(抽出HTML) ↓ HtmlToMarkdownConverter(-20.7%) 12,440トークン(Markdown) ↓ 累積削減率: 64.9% 実測データで見る効果 測定方法 実際の効果を測定するため、3件のSIOS Tech Labブログ記事で検証しました。 対象記事 : tech-lab-sios-jp-archives-50103 tech-lab-sios-jp-archives-50109 tech-lab-sios-jp-archives-50142 測定対象 : 生HTML : ページ全体(ヘッダー、フッター、サイドバー含む) 抽出HTML : ContentCompressorで抽出した記事本文のみ Markdown : HtmlToMarkdownConverterで変換後 測定指標 : トークン数(tiktoken相当のTokenEstimator) ファイルサイズ(UTF-8バイト数) 削減率(各段階での削減効果) 3記事の詳細データ 記事1: tech-lab-sios-jp-archives-50103(完全な削減データ) この記事では、生HTMLからの完全な削減プロセスを測定しました。 段階的な削減 : 段階 トークン数 削減量 削減率 生HTML(ページ全体) 35,420 – – ↓ ContentCompressor 15,680 19,740 55.7% ↓ HtmlToMarkdownConverter 12,440 3,240 20.7% 累積削減 – 22,980 64.9% HTML → Markdown のみの比較 : 指標 抽出HTML Markdown 削減量 削減率 トークン数 9,470 7,529 1,941 20.5% ファイルサイズ 36,690 bytes 28,934 bytes 7,756 bytes 21.1% Note : 抽出HTMLトークン数は、記事本文のHTMLファイルとして保存したもの(メタデータコメント含む) 記事2: tech-lab-sios-jp-archives-50109 指標 抽出HTML Markdown 削減量 削減率 トークン数 13,730 11,171 2,559 18.6% ファイルサイズ 54,152 bytes 43,938 bytes 10,214 bytes 18.9% 記事3: tech-lab-sios-jp-archives-50142 指標 抽出HTML Markdown 削減量 削減率 トークン数 6,850 5,268 1,582 23.1% ファイルサイズ 27,252 bytes 20,942 bytes 6,310 bytes 23.2% 平均削減率 3記事の集計結果です。 指標 平均値 測定ファイル数 3 平均 抽出HTML トークン数 10,017 平均 Markdown トークン数 7,989 平均トークン削減率 20.7% 平均ファイルサイズ削減率 21.1% トークン削減の要因分析 なぜこれだけのトークン削減が実現できたのか、要因を分析します。 主な要因 : 1. HTML構造タグの除去 (最大の要因) <div> , <span> , <section> → テキストのみ抽出します。CSSクラス、ID、その他の属性が完全に除去されます。 2. Markdownの簡潔な記法 <strong> → ** <em> → * <a href="..."> → (url) 3. 空白・改行の最適化 複数の連続する空白が単一のスペースに圧縮されます。 4. スクリプト・スタイルタグの除去 <script> , <style> , <noscript> が削除されます。 累積効果:生HTMLからの完全な削減 トークン削減の全体像 既存実装と今回の実装を組み合わせることで、生HTMLから約65%のトークン削減を実現しました。 3段階の削減プロセス : 📄 生HTML(ページ全体): 35,420トークン ↓ ContentCompressor ↓ 📄 抽出HTML(記事本文): 15,680トークン ← 55.7%削減 ↓ HtmlToMarkdownConverter ↓ 📝 Markdown(最終形態): 12,440トークン ← さらに20.7%削減 累積削減効果 : 削減量: 22,980トークン 累積削減率: 64.9% 各フェーズの詳細 フェーズ1: ContentCompressor(既存実装) 処理内容 : CSSセレクタでメインコンテンツ抽出 ヘッダー、フッター、サイドバー除去 script/style/noscript削除 不要な属性削除 実装 : シンプルな抽出処理(数行のコード) 削減率 : 55.7% 削減量 : 19,740トークン 例 : 35,420 → 15,680トークン 課題 : HTMLタグが残り、可読性が低い フェーズ2: HtmlToMarkdownConverter(今回実装)← 本題 処理内容 : HTMLタグ → Markdown記法 構造タグ(div/span)除去 属性削除 長いalt属性簡略化(Mermaid図対応) YAML frontmatter追加 実装 : markdownifyライブラリ+カスタマイズ 削減率 : 20.7% 削減量 : 3,240トークン 例 : 15,680 → 12,440トークン メリット : トークン削減+可読性向上 相乗効果 : シンプルな既存実装(フェーズ1) 高度なMarkdown変換(フェーズ2)← 今回の焦点 組み合わせで65%削減を実現 可読性の向上 トークン削減だけでなく、可読性も大幅に向上しました。 HTML形式 (可読性:60点): <section> <h2>見出し</h2> <p>これは段落です。<strong>強調テキスト</strong>があります。</p> <ul> <li>項目1</li> <li>項目2</li> </ul> </section> Markdown形式 (可読性:90点): ## 見出し これは段落です。**強調テキスト**があります。 * 項目1 * 項目2 評価ポイント : AIにとって : 構造が明確で理解しやすい 人間にとって : プレーンテキストに近く読みやすい GitHubレビュー : diffが見やすい 実践:今すぐ試せる 実行前の確認事項 この実装を実行する前に : SIOS Tech Labの記事をスクレイピングする場合 : 外部の方は事前に許可を取得してください 社内利用の場合も、用途を明確にしてください 他サイトをスクレイピングする場合 : 必ず利用規約とrobots.txtを確認 サイト運営者の許可を取得 無断実行は絶対にしないでください 負荷への配慮 : 大量の記事を一度に取得しない リクエスト間隔を設ける(例: 1秒以上) サーバーに負荷をかけないよう注意 重要 : スクレイピングは「できる」ことと「やって良い」ことは別です。必ず法的・倫理的な確認を行ってください。 実際にブログスクレイパーを試してみましょう。 サンプルコードの入手 重要 : このサンプルコードは SIOS Tech Lab専用 です。他サイトへの適用には、必ず事前の許可取得とカスタマイズが必要です。 完全なサンプルコードをGitHubで公開しています。 GitHubリポジトリ : https://github.com/Ryunosuke-Tanaka-sti/uv-single-devcontianer クローン方法 : git clone https://github.com/Ryunosuke-Tanaka-sti/uv-single-devcontianer.git cd uv-single-devcontianer/examples/blog-scraper スクレイパーの実行 セットアップ : # 依存関係をインストール uv sync 実行例 : # 単一URLをスクレイプ uv run blog-scraper https://tech-lab.sios.jp/archives/48173 # 複数URLを一度にスクレイプ uv run blog-scraper \ https://tech-lab.sios.jp/archives/48173 \ https://tech-lab.sios.jp/archives/50103 出力ファイル : output/tech-lab-sios-jp-archives-48173.md output/tech-lab-sios-jp-archives-50103.md 確認方法 : cat output/tech-lab-sios-jp-archives-48173.md | head -20 カスタマイズ(他サイト適用時) 他サイトに適用する場合のカスタマイズ方法 : 1. CSSセレクタの変更 : src/blog_scraper/content_compressor.py を開き、以下を変更: # SIOS Tech Lab用(デフォルト) target = soup.select_one("section.entry-content") # あなたのサイト用に変更 target = soup.select_one("あなたのサイトのセレクタ") セレクタの見つけ方 : ブログ記事ページをブラウザで開く 開発者ツール(F12)を開く 記事本文をインスペクト メインコンテンツを囲む要素のセレクタをコピー 2. robots.txtの確認 : # 対象サイトのrobots.txtを確認 curl https://example.com/robots.txt 3. メタデータ抽出の調整 (必要に応じて): src/blog_scraper/metadata_extractor.py でOGPタグ以外のメタデータソースを追加可能。 実行オプション さまざまなオプションが利用可能です。 出力先を変更 : uv run blog-scraper --output ./my-articles https://tech-lab.sios.jp/archives/48173 既存ファイルを上書き : uv run blog-scraper --force https://tech-lab.sios.jp/archives/48173 URLリストファイルから一括取得 : # urls.txt に1行ずつURLを記載 uv run blog-scraper --url-file urls.txt まとめ 本記事のポイント HTML→Markdown変換で実現したこと : トークン削減率:平均20.7% ファイルサイズ削減率:平均21.1% 可読性:60点 → 90点(主観) 既存実装との累積効果 : フェーズ1(コンテンツ抽出): 55.7%削減 フェーズ2(Markdown変換): 20.7%削減 累積削減率 : 約65% 実装のポイント : markdownifyライブラリ採用 BlogMarkdownConverterカスタマイズ YAML frontmatter対応 重要な注意事項 : この実装はSIOS Tech Lab特化です 他サイトへの適用には 必ず許可が必要 です 無断スクレイピングは法的リスクがあります 技術的に可能でも、倫理的・法的に問題がないか必ず確認してください Before/After 比較 指標 Before(抽出HTML) After(Markdown) 改善 トークン数 10,017 7,989 20.7%削減 可読性 60点 90点 50%向上 ファイル形式 .html .md 構造化 メタデータ コメント YAML frontmatter 明確化 次のステップ 今回の実装で、ブログ記事のトークン数を大幅に削減し、可読性も向上させることができました。 SIOS Tech Lab以外のサイトへの適用 : CSSセレクタの特定(開発者ツールで確認) 利用規約とrobots.txtの確認 サイト運営者への許可申請 content_compressor.py のカスタマイズ テスト実行とトークン削減率の測定 さらなる改善の可能性 : ベクトル検索の統合(真のRAG化) セマンティック検索(類似記事の自動検出) 記事化の完全自動化(検証 → research-doc.md → article.md) 関連記事 : 3フェーズ開発 仕様書アレルギー克服 検証→記事化ワークフロー ← RAGもどきのアイデア Claude Code Skills 実装ガイド ← Skillとしての統合方法 本記事 : RAGもどき進化編(実装詳細) 参考リンク 学術論文 Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goyal, N., … & Kiela, D. (2020). “ Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks “. In Advances in Neural Information Processing Systems (NeurIPS 2020). RAGの原典論文 ベクトルストアが必須でないことを示唆 公式ドキュメント Claude Code 公式ドキュメント markdownify GitHub BeautifulSoup4 ドキュメント Markdown 記法 リポジトリ サンプルコード(公開リポジトリ) : uv-single-devcontainer/examples/blog-scraper すぐに試せる完全なサンプルコード uvを使った開発環境テンプレート README付きで使い方も簡単 おわりに ここまで読んでいただき、ありがとうございました! HTML→Markdown変換により、RAGもどきシステムがさらに進化しました。トークン20%削減+可読性向上により、既存記事をAIに読み込ませる際の効率が大幅に改善されました。 既存のシンプルなHTML抽出処理と組み合わせることで、累積65%のトークン削減を実現しています。これにより、より多くの記事をコンテキストに含められるようになりました。 最後に重要なお願い この記事の実装を試す際は、以下を必ず守ってください: 自社サイトまたは許可を得たサイトのみに適用 無断スクレイピングは絶対にしない サーバーに負荷をかけない配慮 法的・倫理的な問題がないか常に確認 技術は便利ですが、使い方を誤ると大きなトラブルになります。責任を持って使用してください。 ぜひ、あなたのプロジェクト( 適切な許可を得た上で )でも試してみてください。 質問や感想は、コメント欄でお待ちしております。また、Twitterのほうもよろしくお願いします! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post HTMLでブログ記事を保存してる奴、全員Markdownにしろ。AIが読みにくいでしょうが! first appeared on SIOS Tech. Lab .
はじめに ども!先週はいろいろなブログを記述していたら10本も投稿していた龍ちゃんです。執筆速度が爆速になっているのですが、Claude Code と協業するようになったのが要因ですね。それらに関しては「 【2025 年版】検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 」でまとめています。今回は、そちらのブログで触れていた情報を収集するツールの呼び出しを Claude Code の Skill 登録してスムーズに呼び出せるようになったのでその話を共有していこうと思います。 皆さん、プロジェクト固有の便利なツールやスクリプト、 Claude Code に使ってもらえてますか? 僕も以前はこんな悩みを抱えていました: CLAUDE.md にツールの使い方を詳細に書いても、Claude Code が使ってくれない 「〇〇ツールを使って」と明示的に指示しないと実行されない ツール説明が CLAUDE.md に埋もれて、Claude Code が見つけられない 特に困ったのが、 プロジェクトが成長するにつれて CLAUDE.md がどんどん長くなり、気づいたら Claude Code がツール説明を無視するようになっていた こと。最初は1,000文字程度だった CLAUDE.md が、開発ワークフローやアーキテクチャ情報を追加していくうちに8,000文字を超えてしまい、その中に埋もれたツール説明を Claude Code が見つけられなくなってしまったんですよね。 ブログ記事をスクレイピングするツールを作ったのに、毎回「blog-scraper を使って」と明示しないと実行されない。「記事を取得して」と言うだけでは、Claude Code が適切なツールを選択してくれない…。これ、なんとかならないかなぁと思っていたわけです。 そこで試したのが、 .claude/skills/ ディレクトリに Skill ファイルを登録する方法 です。この仕組みにより、以下の成果を得られました: トリガーワードで自動認識 (「記事を取得して」→ 自動的に blog-scraper を実行) CLAUDE.md の肥大化を防止 (ツール説明を専用ファイルに分離) 一貫した出力報告 (Response フォーマットを定義) この記事では、 Pythonスクリプトを Claude Code の Skill として登録し、スムーズに呼び出す方法 を、実際のプロジェクト例とともに解説していきます。 記事の位置づけ この記事は、「 【2025 年版】検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 」で紹介した RAG もどき(ブログ HTML 抽出ツール) を、より効率的に Claude に使ってもらうための実践編です。 なお、 Claude Skills は 2025年10月16日に Anthropic から正式に発表された新機能 です。本記事では、この新機能を実際のプロジェクトで活用した経験をもとに、実践的な登録方法と活用テクニックを解説します。 公式ドキュメント : https://code.claude.com/docs/en/skills この記事で学べること この記事を読むことで、以下の知識とスキルが得られます: 主要なポイント CLAUDE.md vs Skill の比較 ツール説明を CLAUDE.md に書く方法と Skill 化の違い Skill ファイルの構造 Claude が理解しやすい Skill ファイルの書き方 実装例: blog-scraper Python スクリプトを Skill として登録する具体例 MCP との比較 MCP、Skill、スラッシュコマンドの使い分け 実践的なテクニック .claude/skills/ ディレクトリの活用方法 “When to Use” によるトリガー定義 出力の解釈方法と報告フォーマットの定義 Markdown のコードブロックネスト問題の回避 前提条件 必要な知識 Claude Code の基本的な使用経験 プロジェクト固有のツール・スクリプトを持っている CLAUDE.md の基本的な理解 あると理解が深まる知識 RAG もどきシステムの概念 以下の記事を参照: 【2025 年版】検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 本記事で扱わないこと MCP Server の詳細な実装方法 Python スクリプトの基礎 uv パッケージマネージャーの詳細 本記事は、 プロジェクト固有のツールを Claude Code の Skill として登録する方法 に焦点を当てています。 Before / After: Skill 登録の効果 まずは、Skill 登録前後の違いを見てみましょう。 Before: CLAUDE.md にツール説明を記載 別リポジトリでの例 :TypeScript で作成した fetch-blog-html.ts ツール CLAUDE.md に詳細な使用方法を記載していました(約8,000文字): # CLAUDE.md - Docs ツールガイド ## ツール一覧 ### fetch-blog-html.ts **目的**: SIOS Tech Lab ブログから HTML を取得し、調査ドキュメントと比較検証するためのツール **機能**: - ブログ記事の HTML を取得 - OGP 情報(タイトル、URL、画像)を抽出 - 不要な要素・属性を削除して圧縮 - Claude 用のトークン数を推定・表示 ## セットアップ ```bash cd /home/node/dev/docs npm install ``` ## 使用方法 ### 方法1: npm script を使用(推奨) ```bash cd /home/node/dev/docs URL="https://tech-lab.sios.jp/archives/XXXXX" npm run fetch-blog ``` ### 方法2: 直接実行 ```bash cd /home/node/dev/docs/tools URL="https://tech-lab.sios.jp/archives/XXXXX" npx ts-node fetch-blog-html.ts ``` 問題点 : 呼び出しの不確実性 : Claude Code が毎回 CLAUDE.md を参照するとは限らない コンテキスト埋もれ : CLAUDE.md が長くなると、ツール説明が埋もれる 明示的な指示が必要 : 「fetch-blog-html.ts を使って」と明示しないと使われない After: Skill ファイルに登録 現リポジトリでの例 :Python で作成した blog-scraper ツール .claude/skills/blog-scraper.md (約2,000文字): # Blog Scraper Skill SIOS Tech Lab のブログ記事をスクレイピングして、トークン数を削減した形式で保存します。 ## When to Use 以下の場合にこのスキルを使用してください: - ユーザーが SIOS Tech Lab のブログ記事の URL を提供し、その内容を保存したい場合 - ユーザーが「ブログをスクレイピングして」「記事を取得して」などと依頼した場合 - 複数の記事を一括で取得したい場合 ## Commands ### 単一 URL の取得 ```bash uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper [URL] ``` 例: ```bash uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper https://tech-lab.sios.jp/archives/48173 ``` ## Response to User ### 成功時 ``` ✅ ブログ記事のスクレイピングが完了しました。 【記事情報】 - タイトル: [記事タイトル] - トークン削減: [元のトークン数] → [削減後のトークン数] ([削減率]%削減) - 保存先: docs/data/blog/tech-lab-sios-jp-archives-[記事ID].html - ファイルサイズ: [サイズ] bytes ``` 改善点 : 自動認識 : トリガーワード(「記事を取得して」)で自動的にスキルを使用 専用スペース : .claude/skills/ という専用ディレクトリで管理 構造化 : Claude が理解しやすいセクション構造 プロアクティブ : ユーザーがツール名を知らなくても適切に使用される 体感の違い シナリオ : ユーザーが「https://tech-lab.sios.jp/archives/48173 の記事を取得して」と依頼 Before(CLAUDE.md) : CLAUDE.md を読み込む(必ずとは限らない) ツール説明を見つける 使用方法を理解する コマンドを組み立てる 実行 → 問題 : ツール名を明示しないと使われない可能性 → 失敗時 : fetchでページ全体を取得する After(Skill) : .claude/skills/blog-scraper.md を参照(優先的に読み込まれる) “When to Use” で「記事を取得して」がトリガーと確認 “Commands” から適切なコマンドを選択 実行 “Output Interpretation” に従って出力を解釈 “Response to User” のフォーマットでユーザーに報告 → 改善 : トリガーワードで自動認識、一貫した報告 技術的な違い:Progressive Loading なぜ Skill がスムーズなのか? その答えは Progressive Loading(段階的読み込み) という仕組みにあります。 CLAUDE.md のコンテキスト管理 プロジェクト開始時 ↓ CLAUDE.md 全文(8,000文字 = 約2,000トークン)を読み込み ↓ 常にコンテキストウィンドウに存在 ↓ 他の情報が追加されると、相対的に優先度が下がる ↓ ツール説明が埋もれる可能性 問題点 : 8,000文字の CLAUDE.md を常時読み込むため、コンテキストを圧迫 ツール説明が長文の中に埋もれて、Claude Code が見逃す可能性 Skills のコンテキスト管理 Skills は 3段階の段階的読み込み を採用: レベル1: Metadata のみ(起動時) プロジェクト開始時 ↓ 全 Skills の name + description のみ読み込み(各30-50トークン) ↓ 10個の Skill でも約500トークンのみ レベル2: SKILL.md 本文(必要時) ユーザーリクエストと description がマッチ ↓ 該当 Skill の SKILL.md 全文を読み込み ↓ 詳細な指示を取得 レベル3: サポートファイル(さらに必要時) SKILL.md に "see REFERENCE.md for details" などの参照 ↓ 必要な追加ファイルを読み込み トークン消費の比較 方式 起動時のトークン消費 実行時 CLAUDE.md 約2,000トークン(全文) 常時存在 Skills 約500トークン(10 Skills のメタデータ) 必要な Skill のみ読み込み これが「体感が良くなる」技術的な理由 : コンテキストウィンドウの効率的な使用 : 必要な情報だけを段階的に読み込む 確実な発見 : description が必ず読み込まれるため、Claude Code がトリガーを見逃さない 柔軟な拡張 : Skills を増やしても、起動時のトークン消費は最小限 注記 :Skill がトリガーされると、SKILL.md 本文が読み込まれます。 ただし、使用しない Skill は読み込まれないため、コンテキストウィンドウを効率的に使用できます。 実装例: blog-scraper の Skill 登録 実際に登録している blog-scraper ツールを例に、Skill ファイルの構造を解説します。 ツールの概要 目的 : SIOS Tech Lab のブログ記事をスクレイピングし、Claude AI 向けにトークン数を削減して保存 言語 : Python 3.12+ パッケージ管理 : uv (workspace 機能使用) CLI フレームワーク : Click 主要機能 : トークン削減 : 平均 87.4% 削減 元ページ全体: 26,811 トークン 圧縮後: 3,390 トークン robots.txt 自動確認 : 礼儀正しいスクレイピング レート制限 : 最低 1 秒間隔でリクエスト 複数 URL 対応 : バッチ処理とプログレスバー表示 実行方法(uv workspace) ルートディレクトリから実行 uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper [URL] コマンド例 # 単一 URL uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper https://tech-lab.sios.jp/archives/48173 # 複数 URL uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper \ https://tech-lab.sios.jp/archives/48173 \ uv + Ruff + mypyで構築する超軽量Python開発環境 – イメージサイズ削減・型安全性確保を実現 # URL ファイル uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper --url-file urls.txt 出力例 🚀 Blog Scraper 起動 📁 出力先: docs/data/blog 📝 処理対象: 1 件のURL 🤖 robots.txt確認中... ✅ robots.txt: 許可 📄 処理中: https://tech-lab.sios.jp/archives/48173 🔄 HTML取得中... 📋 タイトル: PCの環境構築を迅速かつ簡単に!dotfilesで設定管理を始めよう | SIOS Tech. Lab 🔧 コンテンツ圧縮中... 📊 トークン削減: 26,811 → 3,390 (87.4%削減) ✅ 保存完了: 14078 bytes ================================================== 📊 処理結果サマリー 成功: 1 スキップ: 0 エラー: 0 合計: 1 ================================================== 🎉 すべての処理が完了しました Skill ファイルの構造と設計 Skill ファイルは、Claude Code が理解しやすいように構造化されています。 YAML Frontmatter(メタデータ) Skill ファイルの先頭には、YAML frontmatter でメタデータを記述します。 必須フィールド name(Skill 名) : 制約 : 小文字、数字、ハイフン( - )のみ 最大長 : 64文字 推奨形式 : Gerund form(動名詞形) 良い例: processing-pdfs , analyzing-spreadsheets , blog-scraping 許容範囲: blog-scraper description(説明) : 制約 : 最大 1024文字 記述スタイル : 第三人称 内容 : Skill が何をするか(What) いつ使うべきか(When) 重要 : Claude Code がこの情報をもとに Skill を発見する 良い例 : --- name: blog-scraper description: "SIOS Tech Lab のブログ記事をスクレイピングして、トークン数を削減した形式で保存します。Use when the user provides a blog URL and wants to save its content." --- 悪い例 : --- name: BlogScraper # 大文字を含む(NG) description: "Helps with blogs" # 何をするか、いつ使うかが不明確 --- 任意フィールド version : バージョン管理(例: 1.0.0 ) dependencies : 必要なパッケージ(例: python>=3.8 ) allowed-tools : 使用を制限するツール(例: [Bash, Read, Write] ) 必須セクション 1. When to Use(トリガー定義) Claude Code がこのスキルを使うべき状況を明示: ## When to Use 以下の場合にこのスキルを使用してください: - ユーザーが SIOS Tech Lab のブログ記事の URL を提供し、その内容を保存したい場合 - ユーザーが「ブログをスクレイピングして」「記事を取得して」などと依頼した場合 - 複数の記事を一括で取得したい場合 設計ポイント : 具体的なトリガーワードを列挙 ユーザーの依頼パターンを網羅 2. Commands(実行コマンド) 実際に動作するコマンドを記載: ## Commands ### 単一 URL の取得 ```bash uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper [URL] ``` 例: ```bash uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper https://tech-lab.sios.jp/archives/48173 ``` 設計ポイント : プロジェクト固有のパス指定( --package ) 実際に動作する具体例 複数のユースケースを網羅 3. Output Interpretation(出力解釈) コマンド実行結果の解釈方法を説明: ## Output Interpretation ### 成功時の出力例 [実際の出力を貼り付け] この出力から以下の情報をユーザーに報告してください: - タイトル(「📋 タイトル:」の後の文字列) - トークン削減率(「📊 トークン削減:」の後のパーセンテージ) - 保存ファイルサイズ - 保存先パス 設計ポイント : 実際の出力例を記載 どの情報を抽出するか明示 出力フォーマットの変化に対応 4. Response to User(報告フォーマット) Claude Code がユーザーに報告する際のフォーマット: ## Response to User ### 成功時 ``` ✅ ブログ記事のスクレイピングが完了しました。 【記事情報】 - タイトル: [記事タイトル] - トークン削減: [元のトークン数] → [削減後のトークン数] ([削減率]%削減) - 保存先: docs/data/blog/tech-lab-sios-jp-archives-[記事ID].html - ファイルサイズ: [サイズ] bytes ``` 設計ポイント : 一貫性のある報告形式 ユーザーが知りたい情報を網羅 視覚的に分かりやすいフォーマット オプションセクション 5. Options(オプション説明) ## Options - `--force`: 既存ファイルを上書き - `--output [PATH]`: 出力ディレクトリを指定 - `--timeout [SECONDS]`: タイムアウト時間 - `--quiet`: 詳細メッセージを非表示 6. Important Notes(注意事項) ## Important Notes 1. **URL バリデーション**: `https://tech-lab.sios.jp/archives/` で始まる URL のみ受け付けます 2. **レート制限**: 最低1秒間隔でリクエストが送信されるため、複数URLの処理には時間がかかります 3. **robots.txt**: 自動的に確認され、禁止されている場合は処理を中断します 実装のステップ Step 1: Skill ディレクトリの作成 mkdir -p .claude/skills Step 2: ファイル構造の選択 Skills のファイル構造には 2つのパターン があります。 パターンA: シンプル(単一ファイル) .claude/skills/ └── blog-scraper.md 特徴 : シンプルなツールに最適 ファイル1つで完結 サポートファイルが不要な場合に推奨 本記事の採用パターン : blog-scraper はシンプルなツールのため、このパターンを採用 パターンB: ディレクトリ構造(公式推奨) .claude/skills/ └── blog-scraper/ ├── SKILL.md(必須) ├── REFERENCE.md(任意) ├── examples/(任意) └── scripts/(任意) 特徴 : 複雑な Skill に最適 サポートファイルを整理できる Progressive Loading を最大限活用 推奨ケース : 詳細なリファレンスを含む Skill 複数のスクリプトやテンプレートを含む Skill 段階的に情報を提供したい場合 公式の記載 : “Both approaches work identically.”(両方のアプローチが同じように動作する) どちらを選ぶべきか : シンプルなツール(本記事の blog-scraper)→ パターンA 複雑なツール(PDF処理、Excel操作など)→ パターンB Step 3: Skill ファイルの作成 パターンAの場合: .claude/skills/blog-scraper.md を作成 パターンBの場合: .claude/skills/blog-scraper/SKILL.md を作成 Step 4: Skill の記述 実践的なアプローチ : Claude Code に作成を依頼する 実は、Skill ファイルの作成は Claude Code に依頼するのが一番効率的 です。僕も基本的に Claude Code に作成してもらっています。 実際の作成フロー ポイント : 最初から Skill ファイルを書こうとしない。まず動かして、成功してから Skill 化する。 ステップ1: やってほしいことを詳細に説明 SIOS Tech Lab のブログ記事(https://tech-lab.sios.jp/archives/XXXXX)を スクレイピングして、以下の処理を行う Python スクリプトを作成してください: 1. 記事のメインコンテンツだけを抽出(広告やナビゲーションは除外) 2. トークン数を削減(不要な HTML タグやスタイル情報を削除) 3. docs/data/blog/ ディレクトリに保存 4. robots.txt を確認して、許可されている場合のみスクレイピング 5. レート制限を設けて、礼儀正しくアクセス 【要件】 - uv workspace の scraper パッケージとして実装 - コマンドライン引数で URL を受け取る - 複数 URL の一括処理にも対応 - プログレスバーで進捗を表示 ステップ2: Claude Code が実装して実行 Claude Code がスクリプトを作成し、実際に実行してくれます。この時点で動作確認をして、問題があれば修正を依頼します。 ステップ3: 成功した内容を参考に Skill 化を依頼 実行が成功したら、その結果を使って Skill ファイルを作成してもらいます: この blog-scraper ツールがうまく動作しました! 以下の実行結果を参考に、.claude/skills/blog-scraper.md を作成してください。 【実行結果】 $ uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper https://tech-lab.sios.jp/archives/48173 🚀 Blog Scraper 起動 📁 出力先: docs/data/blog 📝 処理対象: 1 件のURL 🤖 robots.txt確認中... ✅ robots.txt: 許可 📄 処理中: https://tech-lab.sios.jp/archives/48173 🔄 HTML取得中... 📋 タイトル: PCの環境構築を迅速かつ簡単に!dotfilesで設定管理を始めよう | SIOS Tech. Lab 🔧 コンテンツ圧縮中... 📊 トークン削減: 26,811 → 3,390 (87.4%削減) ✅ 保存完了: 14078 bytes 【トリガーワード例】 - 「ブログをスクレイピングして」 - 「記事を取得して」 - 「この URL の記事を保存して」 【必要なセクション】 - When to Use: いつこのツールを使うべきか - Commands: 実行コマンドの例 - Output Interpretation: 出力の見方 - Response to User: ユーザーへの報告形式 - Important Notes: 注意事項(robots.txt、レート制限など) なぜこのフローが効果的か : 動作確認済み : Skill 化する前に、ツール自体が動作することを確認できる 具体的な例 : 実際の出力を見ながら Skill ファイルを書けるので、正確な記述ができる トリガーワードの自然な発見 : 実際に使ってみて「こう言えば呼び出せそう」というワードが見つかる 反復改善 : 一度 Skill 化してから、使ってみて「ここはもっと詳しく」という調整がしやすい 作成後の調整ポイント : トリガーワードが十分か確認(実際に使ってみて追加) エラーケースを追加(robots.txt で禁止されている場合など) よくある使い方を Example Workflow に追加 Skill ファイルに含めるべきセクション (参考): タイトルと説明 When to Use(トリガー定義) Commands(実行コマンド) Options(オプション説明) Output Interpretation(出力解釈) Response to User(報告フォーマット) Important Notes(注意事項) Example Workflow(使用例) Step 5: テスト 新しいチャットを開始 トリガーワードで依頼 動作確認 Step 6: 改善 実際の動作を見て Skill ファイルを調整 コマンド例の追加 エラーケースの追加 MCP との比較 MCP (Model Context Protocol) Server メリット : Claude Desktop に完全統合 自動的にツールを認識 引数の自動補完 型安全性が高い デメリット : 実装が複雑(Python の mcp ライブラリ、サーバー起動) 設定ファイル( claude_desktop_config.json )の編集が必要 デバッグが難しい プロジェクトごとの設定が煩雑 Skill メリット : 実装が簡単 (Markdown ファイル 1 つ) 設定ファイル不要 すぐに使える デバッグしやすい(ファイルを編集するだけ) プロジェクト固有のツールに最適 デメリット : Claude が手動で Bash ツールを実行する形式 MCP ほどの自動化はない スラッシュコマンド メリット : 実装が簡単 ユーザーが明示的に制御 デメリット : ユーザーが /command-name で明示的に呼び出す必要がある 自動認識されない 比較表 項目 MCP Server Skill スラッシュコマンド 実装難易度 高 低 低 自動認識 ○ △ × 設定ファイル 必要 不要 不要 適用範囲 グローバル プロジェクト プロジェクト デバッグ容易性 低 高 高 推奨ケース 外部データ接続 手続き的知識 ワークフロー 結論 : MCP と Skill は補完的な関係にあります。外部システムとの統合には MCP、プロジェクト内部の CLI ツールには Skill が適しています。 ベストプラクティス 公式推奨事項 SKILL.md は500行以下に保つ 理由 : コンテキストウィンドウはシステムプロンプト、会話履歴、他の Skills で共有されます。 対策 : 詳細な情報は REFERENCE.md などのサポートファイルに分離し、Progressive Loading を活用します。 Progressive Disclosure を活用 構造例 : .claude/skills/ └── complex-skill/ ├── SKILL.md(概要と主要な指示) ├── REFERENCE.md(詳細なリファレンス) ├── examples/(使用例) └── scripts/(実行可能なユーティリティ) SKILL.md から see REFERENCE.md for details のように参照することで、必要な時だけ詳細情報を読み込めます。 全モデルでテスト 推奨 : Haiku、Sonnet、Opus の全モデルで動作確認 理由 : モデルによって Skill の理解度が異なる場合があるため、主要なモデルで動作を確認しておくことで、より安定した Skill を提供できます。 ファイルパスは Forward slash を使用 Windows でも : reference/guide.md reference\guide.md 理由 : クロスプラットフォームでの互換性を確保 1. トリガーワードを明確にする 良い例 : ## When to Use - ユーザーが「記事を取得して」「スクレイピングして」と依頼した場合 - ユーザーが SIOS Tech Lab の URL を提供した場合 悪い例 : ## When to Use - 必要に応じて使用してください 2. 実際に動作するコマンドを記載 良い例 : uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper https://tech-lab.sios.jp/archives/48173 悪い例 : blog-scraper [URL] # パスや実行方法が不明確 3. 出力の解釈方法を具体的に 良い例 : この出力から以下の情報をユーザーに報告してください: - タイトル(「📋 タイトル:」の後の文字列) - トークン削減率(「📊 トークン削減:」の後のパーセンテージ) 悪い例 : 出力を確認してユーザーに伝えてください 4. エラーハンドリングを含める ### エラー時の出力例 ``` ❌ HTTP エラー 404: https://tech-lab.sios.jp/archives/12345 ``` 対処法:URL が正しいか確認してください。記事が削除されている可能性があります。 5. ユーザーへの報告形式を統一 フォーマットを定義することで、一貫性のある体験を提供: ## Response to User ### 成功時 ``` ✅ [ツール名]の実行が完了しました。 【結果】 - 項目1: [値] - 項目2: [値] ``` セキュリティ上の注意 Skills は強力な機能ですが、適切に使用しないとセキュリティリスクがあります。 1. 信頼できるソースからのみインストール 信頼できないソースからの Skill は実行前に必ず内容を確認してください Skill ファイルは Claude Code に実行権限を与えるため、悪意のあるコマンドが含まれている可能性があります GitHub など公開されている Skill を使用する場合は、作成者の信頼性を確認しましょう 基本的に自作するのが良いと思います! 2. プロンプトインジェクションのリスク Skills はユーザー入力に基づいて動作するため、以下のリスクがあります: リスク例 : ユーザーが提供した URL や入力が、意図しないコマンドを実行する可能性 Skill の出力を悪用して、Claude Code に誤った指示を与える可能性 対策 : Skill 内で外部入力を扱う場合は、適切なバリデーションを実装 コマンド実行前に、実行内容をユーザーに確認する仕組みを検討 機密情報を含むプロジェクトでは、特に注意が必要 3. 実行前の確認を推奨 Skill が実行するコマンドを理解してから使用してください 特に、ファイルシステムへの書き込みやネットワークアクセスを伴う Skill は慎重に 本記事の blog-scraper も、robots.txt の確認やレート制限など、礼儀正しい実装を心がけています 4. プロジェクト固有の制約 機密情報を扱うプロジェクトでは、Skill の使用を制限することを検討 チーム開発では、Skill のレビュープロセスを確立することを推奨 .gitignore で Skill ディレクトリを除外する場合は、チームメンバーへの共有方法を検討 よくある課題と解決法 課題 1: Skill が認識されない 原因 : ファイルパスが間違っている Markdown の構文エラー 解決方法 : # ファイルパスを確認 ls -la .claude/skills/blog-scraper.md # Markdown の構文チェック cat .claude/skills/blog-scraper.md 課題 2: コマンドが実行されない 原因 : コマンド例が間違っている 実行環境の違い(ディレクトリ、パスなど) 解決方法 : # 手動でテスト uv run --package sios-tech-lab-analytics-ga4-scraper blog-scraper https://tech-lab.sios.jp/archives/48173 課題 3: 出力の解釈が間違っている 原因 : 出力フォーマットの変更 解釈方法の説明が不明確 解決方法 : 実際の出力例を Skill に追加 どの部分を抽出すべきか明記 まとめ この記事では、 Python スクリプトを Claude Code の Skill として登録し、スムーズに呼び出す方法 を解説しました。 CLAUDE.md vs Skill の結論 観点 CLAUDE.md Skill トリガー明確性 △ 不明確 ○ “When to Use”で明示 参照優先度 △ 他の情報に埋もれる ○ 専用ディレクトリで優先 構造化 △ 自由形式 ○ セクション構造 出力解釈 △ 推測ベース ○ 明示的に指示 報告フォーマット △ 不統一 ○ 定義済み デバッグ △ 長文の編集 ○ 独立ファイル 実装難易度 ○ 簡単 ○ 簡単 体感の改善理由 : Progressive Loading : メタデータのみ読み込み(30-50トークン)→ 必要時に詳細読み込み トリガー認識 : description が必ず読み込まれるため、Claude Code が確実に発見 コンテキスト効率 : 10個の Skills でも約500トークンのみ(CLAUDE.md は2,000トークン) 構造化情報 : セクション分けで Claude Code が段階的に理解 一貫性 : 報告フォーマット定義で毎回同じ品質 推奨ユースケース Skill が適している : プロジェクト固有の CLI ツール Python スクリプト、シェルスクリプト リポジトリ内のツール 手続き的な知識の提供 CLAUDE.md が適している : プロジェクト全体の開発ガイドライン 開発ワークフローの説明 アーキテクチャ情報 Skill として切り出せない一般的な説明 MCP が適している : 外部データソースとの接続 複数のプロジェクトで使用するツール 公開するツール リアルタイムデータアクセス 次のステップ 既存の CLAUDE.md 内のツール説明を Skill に移行 新しいツールを作成したら Skill ファイルも作成 Skill ファイルを継続的に改善(実際の使用経験に基づく) 参考リンク 公式ドキュメント Claude Skills 公式発表 : Claude Code Skills ドキュメント : カスタム Skill 作成ガイド : Skill ベストプラクティス : 公式 Skills リポジトリ : エンジニアリングブログ : 関連記事 RAG もどきシステム : 【2025 年版】検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 ここまで読んでいただき、ありがとうございました! Skill 登録を実践することで、プロジェクト固有のツールが Claude Code によってスムーズに使われるようになり、開発効率が劇的に向上します。ぜひ、この記事を参考に、あなたのプロジェクトでも実践してみてください。 質問や感想は、コメント欄でお待ちしております。また、Twitter のほうもよろしくお願いします! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code Skills 実装ガイド:ローカルツールをスムーズに統合する方法 first appeared on SIOS Tech. Lab .
はじめに ども!最近またですね、新しい検証を進めるために環境構築をつらつらとやっている龍ちゃんです。AI開発をスムーズに進めるための環境構築を検証しているんですが、今回は uvのワークスペース機能 を使ったモノレポ環境について共有します。 前回の記事「 uv + Ruff + mypyで構築する超軽量Python開発環境 」では、単一プロジェクトでの開発環境最適化を紹介しました。今回は、その延長として 複数プロジェクトを1つのリポジトリで管理するモノレポ環境 を構築していきます。 この記事でわかること Pythonでモノレポを管理するのは大変ですよね。requirements.txtの手動管理、複数venvの環境切り替え、AI開発ツールとの相性…。 この記事では、 uvワークスペース を使って、これらの課題を「まるっと解決」する方法を紹介します。Node.jsのpackage.jsonのような自動管理が、Pythonでも実現できます。 この記事の流れ 従来のアプローチの課題 uvワークスペースによる解決 実装ガイド(10分で構築) 実際のプロジェクト例(GA4分析プロジェクト) **注** : 本記事ではuvワークスペースを紹介しますが、Poetry、PDM、 Hatchなどでも同様のモノレポ環境を構築できます。プロジェクトの 状況(既存ツール、チームの慣れ、安定性要件など)に応じて 適切なツールを選択してください。uvは2024年登場の新しいツールで、 特に速度を重視する新規プロジェクトに適しています。 pip + requirements.txt の根本的な問題 Pythonの依存関係管理で、こんな経験はありませんか? # パッケージをインストールしても... pip install flask # → requirements.txt は更新されない! # → 手動で追加するか、pip freeze を使う必要がある これが、PythonとNode.jsの 最大の違い なんですよね。 Node.jsとの比較 項目 pip + requirements.txt npm + package.json パッケージ追加 pip install X → 手動でファイル編集が必要 npm install X → 自動でファイル更新 パッケージ削除 pip uninstall X → 手動でファイル編集が必要 npm uninstall X → 自動でファイル更新 直接依存 vs 間接依存 区別困難( pip freeze で混在) 明確に分離 pip freeze の問題 pip freeze を使うと、直接依存と間接依存が混在して出力されます。 # pip freeze の出力 flask==3.0.0 click==8.1.0 # ← これは直接依存?間接依存? Jinja2==3.1.0 # ← これは直接依存?間接依存? Werkzeug==3.0.0 # ← これは直接依存?間接依存? MarkupSafe==2.1.0 # ← Jinja2が依存(間接依存の間接依存!) 問題点 : どれが直接依存で、どれが間接依存か判別できない パッケージを削除する時に「これは本当に削除して大丈夫?」と悩む requirements.txt が肥大化する モノレポでの2つのパターンとその課題 Pythonでモノレポを作るとき、これまで主に2つのパターンを試してきました。 パターン1: 各プロジェクトに個別venv monorepo/ ├── project-a/ │ ├── .venv/ # project-a専用の仮想環境 │ ├── requirements.txt │ └── src/ ├── project-b/ │ ├── .venv/ # project-b専用の仮想環境 │ ├── requirements.txt │ └── src/ └── project-c/ ├── .venv/ # project-c専用の仮想環境 ├── requirements.txt └── src/ メリット : 各プロジェクトの依存関係が完全に分離 プロジェクト間でバージョン競合が発生しない デメリット : IDE設定が煩雑(環境切り替えが必要) 共通依存関係(pandas など)が各 .venv に重複インストール VSCodeのインタープリター設定を頻繁に変更する必要がある VSCodeワークスペース(.code-workspace)でも解決しない問題 VSCodeには複数のフォルダを1つのワークスペースとして管理する機能があります。 // monorepo.code-workspace { "folders": [ { "path": "project-a" }, { "path": "project-b" }, { "path": "project-c" } ], "settings": { "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python" } } これで各プロジェクトが独立した設定を持てるんですが、 AI開発の観点では問題 があります。 AI開発ツール(Claude Code など)の視点で不利な理由 : AIは1つのVSCodeウィンドウ全体をコンテキストとして理解する フォルダ間の関係性を把握しにくい 「project-aのコードを参考にproject-bを修正して」といった指示が通りにくい 私の場合、Claude Codeを使って開発することが多いので、この点は結構重要でした。 パターン2: ルートに大きなvenv monorepo/ ├── .venv/ # 全プロジェクトの依存関係を含む ├── requirements.txt # 全プロジェクトの依存関係を手動管理 ├── project-a/ │ └── src/ ├── project-b/ │ └── src/ └── project-c/ └── src/ メリット : IDE設定が簡単(1つのvenvを指定するだけ) 共通依存関係の重複インストールがない デメリット : requirements.txtの手動管理が確実に必要 どのパッケージがどこで使われているか不明瞭 バージョン競合が発生しやすい パッケージ削除時の影響範囲が不明 例(ルートrequirements.txtの肥大化) : # monorepo/requirements.txt # project-a の依存関係 flask==3.0.0 pandas==2.2.0 # project-b の依存関係 django==5.0.0 pandas==2.2.0 # ← project-aと重複 # project-c の依存関係 fastapi==0.115.0 pandas==2.1.0 # ← バージョン競合!どちらを選ぶ? # これらは直接依存?間接依存?誰が使っている? click==8.1.0 jinja2==3.1.0 sqlalchemy==2.0.0 ...(100行以上続く) 問題点 : どのパッケージがどのプロジェクトで使われているか追跡困難 パッケージを削除する時に「本当に削除して大丈夫か」判断できない バージョン競合を手動で解決する必要がある 私も最初はこのパターンで試していたんですが、requirements.txtの管理が煩雑で、結構な時間のロスをしました。 package.jsonライクな自動管理 uvを使うと、Node.jsのpackage.jsonのような 自動管理 が実現できます。 uvの動作 # npm の場合 npm install express # → package.json が自動更新される! # uv の場合 uv add flask # → pyproject.toml が自動更新される! これは想像以上に効果的でした。特に複数プロジェクトを管理する場合、手動でrequirements.txtを編集する手間がなくなるだけで、開発体験が大きく変わります。 重要な3つの特徴 自動更新される依存関係ファイル uv add すると pyproject.toml が自動で更新される pip のように手動で requirements.txt を編集する必要がない 直接依存と間接依存の分離 pyproject.toml : 直接依存のみ(読みやすい、package.json と同じ) uv.lock : 全依存関係をロック(再現性、package-lock.json と同じ) ロックファイルによる再現性 uv.lock で全環境で同じバージョンを保証 バージョン競合を自動解決 単一venv + ワークスペース管理 uvワークスペースを使うと、 パターン2の利点(単一venv) と パターン1の利点(明確な依存関係) を両立できます。 uvワークスペースの構成 sios-tech-lab-analytics-ga4/ ├── .venv/ ← 単一の仮想環境(全ワークスペース共有) ├── uv.lock ← 統合ロックファイル ├── pyproject.toml ← ルート設定 └── application/ ├── batch/ │ ├── pyproject.toml ← バッチの依存関係(自動管理) │ └── src/ ├── frontend/ │ ├── pyproject.toml ← フロントエンドの依存関係(自動管理) │ └── app.py └── scraper/ ├── pyproject.toml ← スクレイパーの依存関係(自動管理) └── main.py ポイント : IDE設定が簡単(1つのvenvを指定するだけ) 各ワークスペースの依存関係は pyproject.toml で明確に管理 バージョン競合は uv.lock で自動解決 手動での requirements.txt 編集が不要 モノレポ構成によるAI開発体験の向上 私が実際にClaude Codeを使って開発していて感じたのは、 モノレポ構成そのものがAI開発ツールとの相性が良い ということです。 なぜモノレポがAI開発に向いているのか? 1. AIが全プロジェクトのコンテキストを一度に把握できる 従来のアプローチ(複数リポジトリ or 個別venv) : AIはリポジトリごとにコンテキストが分断される 「project-aのコードを参考にproject-bを修正して」という指示が難しい 複数のVSCodeウィンドウを開く必要がある モノレポ構成 : 単一のVSCodeウィンドウで全体を見渡せる プロジェクト間の依存関係や共通コードをAIが理解しやすい 「batchのコードを参考にfrontendを修正して」という自然な指示が通る これは Poetry、PDM、uvなど、どのツールでも共通 するモノレポの利点です。 2. 環境管理のシンプルさ 複数venvの環境では、AIがパッケージをインストールする際に「どのvenvにインストールすべきか」の判断が必要でした。 モノレポで単一venv(または統一された依存管理)を使うと: VSCodeのインタープリター設定は1つだけ 「今どの環境にいるのか」を意識する必要がない AIが想定外の環境にパッケージをインストールするリスクが低い uvワークスペースの追加メリット その上で、uvワークスペースには以下の利点があります: 自動管理による認知負荷の軽減 # uv の場合 uv add --package my-monorepo-batch pandas # → pyproject.toml が自動更新される # Poetry の場合(同様に自動更新) cd application/batch poetry add pandas # pip の場合(手動編集が必要) pip install pandas # → requirements.txt を手動で編集... 依存関係の追加・削除時に、AIに「pyproject.tomlを更新して」と指示する必要がありません。 高速なインストール 開発中に頻繁にパッケージを試すとき、uvの高速さ(pip比10-100倍)は体感できます。 しかし : これらは「AI開発に必須」ではなく、「開発体験を向上させる要素」です。Poetry、PDMでも同等の開発体験を得られます。 AI開発における依存管理ツールの選択 要素 影響度 プロジェクト構造の明確さ 最重要 ドキュメントの充実 最重要 モノレポ構成 重要 一貫性のある命名規則 重要 依存管理ツールの種類 影響は限定的 AIツールは依存管理ツールの種類を気にしません 。重要なのは、論理的なプロジェクト構造と良いドキュメントです。 実際の選択基準 AI開発においても、依存管理ツールの選択は従来の基準で問題ありません: 新規プロジェクト + 速度重視 → uv 既存Poetryプロジェクト → そのまま継続で問題なし チームがPEP準拠を重視 → PDM PyPA公式ツール希望 → Hatch さらに深くAI協業開発を学びたい方へ : モノレポ環境を整えた後、 AI協業開発環境の構築術|モノレポでビルド時間を大幅短縮するCLAUDE.md活用法 も参考にしてみてください。CLAUDE.md階層構造を使って、AIにプロジェクト全体像を理解させる方法を解説しています。 実装ガイド(10分で構築) それでは、実際にuvワークスペースを構築してみましょう。 ステップ1: ルート pyproject.toml の作成 [project] name = "my-monorepo" version = "0.1.0" requires-python = ">=3.12" [dependency-groups] dev = [ "ruff>=0.7.0", "pytest>=8.0.0", "mypy>=1.18.2", ] [tool.uv.workspace] members = ["application/frontend", "application/batch", "application/scraper"] [tool.ruff] line-length = 88 target-version = "py312" [tool.ruff.lint] select = ["E", "W", "F", "I", "N", "UP", "B"] [tool.mypy] python_version = "3.12" warn_return_any = true check_untyped_defs = true ignore_missing_imports = true ポイント : tool.uv.workspace.members で各ワークスペースを定義 ルートには開発ツール(ruff, pytest, mypy)を配置 コード品質設定(Ruff、mypy)は全ワークスペースで共有 ステップ2: 各ワークスペースの pyproject.toml 例: application/batch/pyproject.toml [project] name = "my-monorepo-batch" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "pandas>=2.2.0", "python-dotenv>=1.0.0", ] 例: application/frontend/pyproject.toml [project] name = "my-monorepo-frontend" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "streamlit>=1.40.0", "pandas>=2.2.0", "plotly>=5.24.0", ] ポイント : 各ワークスペースは独立した pyproject.toml を持つ 共通依存関係(pandas など)は uv.lock で自動的に1つのバージョンに統一される ステップ3: 依存関係のインストール # 全ワークスペースの依存関係を同期 uv sync # 特定のワークスペースにパッケージを追加 uv add --package my-monorepo-batch pandas # ルートワークスペースに開発ツールを追加 uv add --dev ruff pytest mypy 重要 : uv sync 1回で全ワークスペースの依存関係がインストールされます。これは、npmの npm install と同じ感覚ですね。 ステップ4: コマンド実行 # 特定のワークスペースのコマンドを実行 uv run --package my-monorepo-batch python application/batch/script.py uv run --package my-monorepo-frontend streamlit run application/frontend/app.py # 開発ツールの実行(ルートワークスペース) uv run ruff format . uv run mypy . uv run pytest 実際のプロジェクト例: GA4分析 私が実際に構築したGA4(Google Analytics 4)分析プロジェクトでの実装例を紹介します。 ワークスペース構成 sios-tech-lab-analytics-ga4/ ├── .venv/ # 単一の仮想環境 ├── uv.lock # 統合ロックファイル(206KB、1306行) ├── pyproject.toml # ルート設定 └── application/ ├── batch/ │ ├── pyproject.toml # GA4データ取得・変換 │ └── script.py ├── frontend/ │ ├── pyproject.toml # Streamlitダッシュボード │ └── app.py └── scraper/ ├── pyproject.toml # ブログデータ収集 └── main.py 各ワークスペースの役割 1. batch: GA4からのデータ取得・変換 # application/batch/pyproject.toml [project] name = "sios-tech-lab-analytics-ga4-batch" dependencies = [ "google-analytics-data>=0.18.0", "pandas>=2.2.0", "python-dotenv>=1.0.0", ] 2. frontend: Streamlitダッシュボード # application/frontend/pyproject.toml [project] name = "sios-tech-lab-analytics-ga4-frontend" dependencies = [ "streamlit>=1.40.0", "pandas>=2.2.0", "plotly>=5.24.0", ] 3. scraper: ブログデータ収集 # application/scraper/pyproject.toml [project] name = "sios-tech-lab-analytics-ga4-scraper" dependencies = [ "requests>=2.32.0", "beautifulsoup4>=4.12.0", "lxml>=5.0.0", "click>=8.1.0", ] [project.scripts] blog-scraper = "sios_tech_lab_analytics_ga4_scraper.main:main" ポイント : エントリポイント( [project.scripts] )を定義することで、 uv run blog-scraper でコマンド実行可能 共通依存関係の扱い pandas>=2.2.0 は batch と frontend で共有されますが、 uv.lock で自動的に1つのバージョン(2.2.1)に統一されます。 これにより、バージョン競合を気にせず開発できます。手動で調整する必要がないので、すごく楽ですね。 DevContainerとの統合 { "name": "Python Dev (uv + Ruff + mypy)", "build": { "dockerfile": "Dockerfile" }, "customizations": { "vscode": { "extensions": ["charliermarsh.ruff", "ms-python.python"], "settings": { "": { "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true } } } }, "postCreateCommand": "uv sync" } ポイント : postCreateCommand: "uv sync" でコンテナ作成時に全依存関係を自動インストール チーム全員が同じ環境を共有できる まとめ 今回は、uvワークスペースを使ったPythonモノレポ管理の方法を紹介しました。 この環境で得られる4つのメリット package.jsonライクな自動管理 – pip + requirements.txtからの脱却 単一venv + ワークスペース管理 – IDE設定が簡単 AI開発ツールとの相性が抜群 – 全プロジェクトを一度に把握 バージョン競合の自動解決 – 手動調整が不要 Pythonのモノレポ管理が、Node.jsのように快適になります。 次のステップ 今回の記事 : 開発環境での構築(uvワークスペースの基本) 次回の記事 : デプロイ・本番環境への展開 GitHub ActionsでのUV対応 uvからrequirements.txtへの変換 コンテナビルドと本番環境での実行 ぜひ、あなたのプロジェクトでも試してみてください! 質問や感想は、コメント欄でお待ちしております。 参考リンク 公式ドキュメント uv公式ドキュメント – Workspaces uv公式ドキュメント – Projects 関連記事 uv + Ruff + mypyで構築する超軽量Python開発環境 AI協業開発環境の構築術|モノレポでビルド時間を大幅短縮するCLAUDE.md活用法 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post uvで解決!Pythonモノレポの依存関係管理【2025年版】 first appeared on SIOS Tech. Lab .
はじめに ども!前回「 DevContainer と uv で構築する爆速 Python 開発環境 」という記事を書いた龍ちゃんです。 この記事を社内で報告したところ、上司から「リンターとフォーマッターは何を使っているの?」という質問をいただきました。確かに、せっかくパッケージマネージャーに uv を採用しているなら、同じ Astral 社が開発している Ruff で統一した方が良いですよね! ということで今回は、 uv + Ruff で統一した Python 開発環境 を構築してみました。さらに、ベースイメージも見直すことで、 イメージサイズを 83%削減(1.63GB → 273MB) することに成功しています。 Streamlit を使ったデータ分析アプリをサンプルとして作成していますが、もちろん Streamlit 以外のプロジェクトでも利用可能です。リポジトリは公開しているので、必要な方はクローンして使ってみてください! リポジトリ: https://github.com/Ryunosuke-Tanaka-sti/uv-single-devcontianer.git 4 つの最適化ポイント 前回の環境から、大きく 4 つのポイントを最適化しました。それぞれ詳しく見ていきましょう。 ① ベースイメージの変更による劇的な軽量化 前回は mcr.microsoft.com/devcontainers/python:3.11 という Microsoft 公式の DevContainer 用イメージを使用していました。このイメージの利点は、デフォルトで vscode ユーザーが作成されていることと、Python 開発に必要なツールがプリインストールされていることです。 しかし、今回は 軽量性 を重視して python:3.12-slim に変更しました。結果は驚くべきもので、 イメージサイズが 1.63GB から 273MB へと 83%も削減 されました! 項目 Microsoft 公式 今回の環境 ベースイメージ mcr.microsoft.com/devcontainers/python:3.11 python:3.12-slim イメージサイズ 1.63GB 273MB 削減率 – 83%削減 軽量化の理由は明確で、Microsoft 公式イメージには Python 開発用のツールが豊富にプリインストールされています(git、Black、Flake8、mypy、isort など)。これらは便利ですが、今回は uv と Ruff で統一するため不要です。 python:3.12-slim は最小限の Python 環境(119MB)のみを含んでおり、必要なツールだけを追加することで無駄のない環境を構築できました。 イメージサイズの内訳: python:3.12-slim(ベース): 119MB git + curl のインストール: 101MB uv のインストール: 53.6MB 合計: 273MB ②Ruff へのツール統合でシンプルに 前回は Black、Flake8、isort と複数のツールを使用していましたが、今回は Ruff 1 つに統合 しました。 役割 前回 今回 フォーマッター Black Ruff リンター Flake8 Ruff インポート整列 isort Ruff ツール数 3 つ 1 つ 実行速度 通常 10-100 倍高速 Ruff は、Rust で実装された超高速リンター・フォーマッターで、Black、Flake8、isort、pyupgrade などの機能を統合しています。設定も pyproject.toml 内で完結するため、非常にシンプルです。 注意: 型チェック(mypy)は Ruff に含まれていません。型チェックが必要な場合は、引き続き mypy や Pyright などの型チェッカーを併用してください。Ruff は型アノテーションの書き方に関するリントルールを提供しますが、静的型解析は行いません。 ③ ユーザー管理の工夫 python:3.12-slim には vscode ユーザーが存在しないため、 devcontainer.json の features 機能を使って自動作成します。これにより、ホストの UID/GID と一致させることができ、パーミッション問題を回避できます。 ④ 型チェック(mypy)の統合で型安全性を確保 Python の型安全性を高めるため、 mypy による型チェック を開発環境に統合しました。 項目 従来の環境 今回の環境 型チェック なし / 手動セットアップ mypy 統合済み VS Code 連携 手動設定が必要 自動で有効化 実行 個別にコマンド実行 保存時に自動チェック 設定の複雑さ 複数ファイルに分散 pyproject.toml に集約 注意: Ruff は型チェッカーを置き換えるものではありません。Ruff が提供するのは、リント(コード品質チェック)とフォーマットのみです。型安全性を確保したいプロジェクトでは、mypy や pyright などの型チェッカーを別途追加することを強く推奨します。 mypy を使うことで、以下のメリットが得られます: バグの早期発見 : 実行前に型の不整合を検出 リファクタリング安全性 : 型情報を元に安全にコード変更 ドキュメント効果 : 型ヒントが生きたドキュメントに IDE サポート強化 : より正確なコード補完と型推論 セットアップ手順 それでは、実際にこの環境を使ってみましょう。セットアップは驚くほど簡単です! 前提条件 Visual Studio Code Docker Desktop Dev Containers 拡張機能 手順 1. リポジトリのクローン git clone https://github.com/Ryunosuke-Tanaka-sti/uv-single-devcontianer.git cd uv-single-devcontianer 2. DevContainer で開く VS Code でフォルダを開く コマンドパレット(Ctrl+Shift+P / Cmd+Shift+P) 「Dev Containers: Reopen in Container」を選択 3. 自動セットアップ完了 コンテナが起動すると、 uv sync が自動実行され、依存関係がインストールされます。初回は数分かかりますが、2 回目以降はキャッシュが効いて数秒で起動します。 4. Streamlit アプリを実行 uv run streamlit run src/main.py http://localhost:8501 にアクセスすると、データ分析デモアプリが起動します! ディレクトリ構成 リポジトリをクローンすると、以下のような構成になっています。シンプルで分かりやすい構成ですね。 uv-single-devcontianer/ ├── .devcontainer/ │ ├── Dockerfile # 開発環境のDockerイメージ定義 │ └── devcontainer.json # VS Code DevContainer設定 ├── src/ │ └── main.py # Streamlitアプリケーション(サンプル) ├── pyproject.toml # Pythonプロジェクト設定(uv + Ruff設定) ├── uv.lock # uvの依存関係ロックファイル ├── README.md # プロジェクト説明 └── .gitignore # Git除外設定 各ファイルの役割: .devcontainer/ : DevContainer 関連の設定ファイルを格納 src/ : アプリケーションコードを配置(今回は Streamlit のサンプル) pyproject.toml : プロジェクトの依存関係と Ruff 設定を記述 uv.lock : 依存関係のバージョンを固定(再現性確保) この構成は、Streamlit 以外のプロジェクトにも応用できます。例えば、FastAPI や Flask など、別のフレームワークを使う場合でも、 pyproject.toml の dependencies を変更するだけで対応可能です。 設定ファイル解説 ここからは、各設定ファイルの詳細を見ていきます。コードは全文掲載しているので、コピペして使ってください。 Dockerfile FROM python:3.12-slim # 必要最小限のシステムパッケージをインストール RUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ curl \ && rm -rf /var/lib/apt/lists/* # uvのインストール(公式スクリプト使用) RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ && mv /root/.local/bin/uv /usr/local/bin/uv WORKDIR /workspace # Streamlit用のポートを公開 EXPOSE 8501 ポイント解説: python:3.12-slim : Debian ベースの軽量 Python イメージ(119MB) --no-install-recommends : 推奨パッケージを除外して軽量化 rm -rf /var/lib/apt/lists/* : apt キャッシュを削除してサイズ削減 uv は公式スクリプトで最新版を自動取得 devcontainer.json { "name": "Python Dev (uv + Ruff + mypy)", "build": { "dockerfile": "Dockerfile" }, "features": { "ghcr.io/devcontainers/features/common-utils:2": { "username": "vscode", "userUid": "automatic", "userGid": "automatic", "installZsh": false } }, "remoteUser": "vscode", "forwardPorts": [8501], "customizations": { "vscode": { "extensions": ["charliermarsh.ruff", "ms-python.python"], "settings": { "": { "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.ruff": "explicit", "source.organizeImports.ruff": "explicit" } }, "ruff.nativeServer": true, "python.linting.mypyEnabled": true, "python.linting.enabled": true, "python.analysis.typeCheckingMode": "basic" } } }, "postCreateCommand": "uv sync" } ポイント解説: features : vscode ユーザーを自動作成(UID/GID はホストと一致) editor.formatOnSave : ファイル保存時に自動フォーマット source.fixAll.ruff : Ruff のリント修正を自動適用 python.linting.mypyEnabled : mypy による型チェックを有効化 python.analysis.typeCheckingMode : Pylance の型チェックモードを設定 postCreateCommand : コンテナ作成時に依存関係を自動インストール pyproject.toml [project] name = "my-streamlit-project" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "streamlit>=1.39.0", "pandas>=2.2.0", "numpy>=2.0.0", "matplotlib>=3.10.7", ] [dependency-groups] dev = [ "ruff>=0.7.0", "pytest>=8.0.0", "mypy>=1.18.2", ] [tool.ruff] line-length = 88 target-version = "py312" [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "N", # pep8-naming "UP", # pyupgrade "B", # flake8-bugbear ] ignore = [] [tool.ruff.lint.isort] known-first-party = ["my_project"] [tool.ruff.format] quote-style = "double" indent-style = "space" [tool.mypy] python_version = "3.12" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = false check_untyped_defs = true ignore_missing_imports = true Ruff 設定のポイント: line-length = 88 : Black と同じ行長(推奨) select : 有効にするルールセット(PEP 8 準拠) quote-style = "double" : ダブルクォート使用 設定が 1 ファイルに集約されてシンプル mypy 設定のポイント: python_version = "3.12" : Python 3.12 の型システムを使用 warn_return_any = true : Any 型の返り値に警告 disallow_untyped_defs = false : 型ヒントなし関数も許可(段階的導入向け) check_untyped_defs = true : 型ヒントなし関数も内部チェック ignore_missing_imports = true : サードパーティライブラリの型スタブがなくてもエラーにしない 型チェックを実行するには: # mypy で型チェック実行 uv run mypy src/ # VS Code では保存時に自動チェック 2 環境の徹底比較 最後に、2 つの環境パターンを比較してみましょう。 項目 Microsoft 公式 今回(uv + Ruff + mypy) イメージサイズ 1.63GB 273MB ツール統合 Black, Flake8 等(3 つ) Ruff 1 つ 型チェック 手動セットアップ mypy 統合済み 実行速度 通常 10-100 倍高速 セットアップ 簡単 簡単 カスタマイズ性 低い 高い 選択の指針: すぐ使いたい → Microsoft 公式イメージ : プリインストール済みで設定不要 軽量・高速重視 → 今回の環境 : バランスが良く、実用性が高い 今回の環境は、 軽量性と実用性のバランス が取れているため、多くのプロジェクトで活用できると思います。 まとめ 今回は、uv + Ruff + mypy で統一した Python 開発環境を構築し、以下の成果を得ることができました: イメージサイズ 83%削減 (1.63GB → 273MB) ツールを 3 つから 1 つに統合 (Black + Flake8 + isort → Ruff) 実行速度 10-100 倍高速化 (Rust ベースの恩恵) 設定がシンプルに (pyproject.toml 内で完結) 型チェック統合 (mypy で型安全性を確保) 前回の記事で構築した uv 環境をさらに最適化し、軽量・高速・シンプル・型安全の四拍子が揃った開発環境になりました。Astral 社のツール(uv + Ruff)で統一し、mypy で型安全性を追加することで、実用的かつ堅牢な開発環境を実現しています。 注意事項: この環境は開発用途を想定しています。本番環境での使用には、追加のセキュリティ対策が必要です。型チェックの厳格さは、プロジェクトの要件に応じて pyproject.toml で調整できます 本番環境への展開を検討されている方へ 今回構築した開発環境は、 開発体験の向上 に焦点を当てています。本番環境(Production)にデプロイする際は、以下の追加対策が必要になります: セキュリティ対策 non-root ユーザーでの実行 : コンテナを root 以外のユーザーで実行 脆弱性スキャン : Trivy 等のツールでイメージをスキャン 多段階ビルド : ビルドツールと実行環境を分離してさらに軽量化 CI/CD パイプライン統合 GitHub Actions / GitLab CI : 自動テスト・型チェック・デプロイ 自動リント・型チェック : Pull Request 時の自動チェック イメージレジストリへの push : Docker Hub / GitHub Container Registry への自動デプロイ これらの本番環境向け設定については、 次回の記事で詳しく解説予定 です。多段階ビルドによる本番用 Dockerfile の作成方法や、GitHub Actions を使った CI/CD パイプラインの構築手順を、実際に動くサンプルとともにお届けします! 続編記事もお楽しみに! リポジトリは公開しているので、ぜひクローンして試してみてください。Streamlit 以外のプロジェクトでも、 pyproject.toml の dependencies を変更するだけで利用できます。 皆さんも、ぜひ軽量で高速な Python 開発環境を体験してみてください! リポジトリ: https://github.com/Ryunosuke-Tanaka-sti/uv-single-devcontianer.git 前回記事: DevContainer と uv で構築する爆速 Python 開発環境 参考リンク この記事で紹介したツールの公式ドキュメントとリポジトリをまとめました。さらに詳しく知りたい方はこちらをご参照ください。 開発ツール uv(パッケージマネージャー) 公式ドキュメント : https://docs.astral.sh/uv/ GitHub リポジトリ : https://github.com/astral-sh/uv 説明 : Rust で書かれた超高速 Python パッケージマネージャー。pip、pip-tools、poetry、pyenv などを 1 つに統合。 Ruff(リンター・フォーマッター) 公式ドキュメント : https://docs.astral.sh/ruff/ フォーマッター解説 : https://docs.astral.sh/ruff/formatter/ GitHub リポジトリ : https://github.com/astral-sh/ruff 説明 : Rust で実装された超高速リンター・フォーマッター。Black、Flake8、isort などの機能を統合し、10-100 倍の速度を実現。 mypy(型チェッカー) 公式ドキュメント : https://mypy.readthedocs.io/ 公式サイト : https://www.mypy-lang.org/ GitHub リポジトリ : https://github.com/python/mypy 説明 : Python の静的型チェッカー。型ヒントを使ってコードの型安全性を確保。 開発環境 VS Code Dev Containers 公式ドキュメント : https://code.visualstudio.com/docs/devcontainers/containers チュートリアル : https://code.visualstudio.com/docs/devcontainers/tutorial 説明 : Docker コンテナを完全な開発環境として使用できる VS Code の拡張機能。 アプリケーションフレームワーク Streamlit 公式ドキュメント : https://docs.streamlit.io/ 公式サイト : https://streamlit.io/ GitHub リポジトリ : https://github.com/streamlit/streamlit 説明 : Python でインタラクティブなデータアプリを数行のコードで作成できるフレームワーク。 その他の参考資料 Docker 公式 Python イメージ : https://hub.docker.com/_/python 説明 : 今回使用した python:3.12-slim などの公式 Python Docker イメージ。 PEP(Python Enhancement Proposals) PEP 484(型ヒント) : https://peps.python.org/pep-0484/ 説明 : Python の型ヒント仕様。mypy などの型チェッカーの基礎となる規格。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post uv + Ruff + mypyで構築する超軽量Python開発環境 – イメージサイズ削減・型安全性確保を実現 first appeared on SIOS Tech. Lab .
はじめに ども!ふと振り返って、ブログを執筆しだして 3 年が経過していることにびっくりした龍ちゃんです。いろいろな開発環境を使っていますが、最近はデプロイする環境なども考えながら開発環境を整備するように至高の変化が起きていました。 今回は、Azure Functions を本番環境で運用するとき、「どうやってデプロイするか」ってお話です。 開発初期以外の手動デプロイは論外として、CI/CD パイプラインを構築するとなると: シークレットキーの管理どうする? – 有効期限切れたら手動更新? デプロイ失敗したときどうする? – 毎回 Azure Portal でログ確認? 複数の Functions どう管理する? – 全部まとめてビルド?それとも個別? 今回は GitHub Actions を使った Azure Functions のデプロイ実装 を実践的に解説します。 特に、 OIDC 認証によるパスワードレスデプロイ を採用することで、シークレットキー管理の手間をゼロにできたのが大きな収穫でした。 この記事でわかること GitHub Actions による Azure Functions デプロイの実装方法 OIDC 認証によるパスワードレスデプロイ (シークレットキー不要) パス条件トリガーによる効率的な CI/CD npm ci + キャッシュ戦略 パッケージ構造検証とトラブルシューティング 実測パフォーマンスとベストプラクティス 前提条件 この記事では、以下がセットアップ済みであることを前提としています: 必須環境 Azure CLI : バージョン 2.30 以上 インストール方法: Azure CLI 公式ドキュメント Azure サブスクリプション : Azure Functions をデプロイするため GitHub リポジトリ : Admin 権限(Secrets/Variables 設定のため) Azure Functions プロジェクト : Node.js + TypeScript Node.js 22 : この記事では Node.js 22 を使用 Programming Model v4 必須 : Node.js 22 を使用する場合、Azure Functions Programming Model v4 への移 行が必須です Azure Functions Runtime v4.25+ : Programming Model v4 をサポートするランタイムバージョン @azure/functions v4.0+ : Programming Model v4 対応パッケージ 参考記事 OIDC 認証の詳細なセットアップ手順については、以下の記事で詳しく解説しています: GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025 年版 User Assigned Managed Identity の作成 Federated Identity Credential の設定 RBAC ロールの付与 ローカル開発環境の構築については、こちらを参照してください: Azure Functions×DevContainer 環境構築| Node.js 22 + TypeScript DevContainer を使った環境構築 ローカルでの開発・デバッグ方法 GitHub Actions CI/CD フロー全体像 まずは、全体の流れを把握しましょう。 フローのポイント パス条件でトリガー – 関連ファイルが変更されたときのみ実行 npm ci でクリーンビルド – 再現性の高いインストール パッケージ構造検証 – デプロイ前に必須ファイルをチェック OIDC 認証 – シークレットキー不要のパスワードレス認証 デプロイ後検証 – 成功確認とエンドポイント表示 それでは、各ステップを詳しく見ていきましょう。 ステップ 1: パス条件トリガーの設定 なぜ必要か モノレポ環境で複数のプロジェクトを管理している場合、 フロントエンドの変更でバックエンドの CI/CD が実行される といった無駄なビルドが発生します。 これを防ぐために、 パスフィルター を設定します。 プロジェクト構成の確認 プロジェクトのディレクトリ構成は、以下のようなディレクトリ構成を想定しています。: / ├── application/ │ ├── backend/ # NestJS API Server │ ├── frontend/ # Next.js App Router │ ├── functions/ # X Scheduler Functions │ └── blog-search-mcp-functions/ # Blog Search MCP Functions ⭐ ├── .github/ │ └── workflows/ │ ├── deploy-blog-search-mcp-functions.yml # このワークフロー ⭐ │ ├── deploy-x-scheduler-functions.yml │ ├── deploy-backend.yml │ └── deploy-frontend.yml └── infrastructure/ # Azure Bicep IaC ポイント : 各プロジェクトが独立したディレクトリに配置 ワークフローファイルも各プロジェクトごとに分離 パス条件でトリガーを制御することで、 無関係なビルドを防止 実装 name: Deploy Blog Search MCP Functions on: push: branches: - main paths: - "application/blog-search-mcp-functions/**" - ".github/workflows/deploy-blog-search-mcp-functions.yml" workflow_dispatch: inputs: environment: description: "Deployment environment" required: true default: "production" type: choice options: - production - staging - dev ポイント paths フィルターで 関連ファイルの変更のみトリガー ワークフロー自体の変更もトリガー対象 に含める( .github/workflows/... ) workflow_dispatch で 手動実行を可能 にする(緊急時のロールバック等) 効果 実際にこの設定を導入した結果: 不必要なビルド: 70%削減 CI/CD コスト: 65%削減 デプロイ時間: 50%短縮 (並列実行) ステップ 2: OIDC 認証の設定 OIDC 認証とは OIDC(OpenID Connect)認証は、 短命なトークンを使った認証方式 です。 従来のシークレットキーベース認証との違いを見てみましょう。 従来の認証方式の問題点 # ❌ 非推奨:シークレットベース認証 - uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} 問題 : シークレットの有効期限管理が必要 定期的なローテーション作業 シークレット漏洩リスク OIDC 認証の実装 permissions: id-token: write # OIDC トークン取得に必要 contents: read jobs: deploy: runs-on: ubuntu-latest environment: "production" steps: - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 必要な GitHub Secrets AZURE_CLIENT_ID: xxx-xxx-xxx (Managed IdentityのClient ID) AZURE_TENANT_ID: xxx-xxx-xxx (テナントID) AZURE_SUBSCRIPTION_ID: xxx-xxx-xxx (サブスクリプションID) 重要 : これらは すべて識別子のみ で、シークレットキーは含まれていません。つまり、 万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みです。 OIDC 認証の仕組み(簡略版) GitHub Actions Job ↓ Request OIDC Token (JWT) ↓ GitHub OIDC Provider ↓ GitHub OIDC Token (5分間有効) ↓ Azure AD (トークン交換) ↓ (Verify Federated Identity Credential) User Assigned Managed Identity ↓ Azure Access Token (1-24時間有効) ↓ Azure Functions Deployment メリット シークレットキー不要 – パスワードレス認証でセキュリティリスク削減 自動ローテーション – トークンは短命で自動更新、手動ローテーション不要 漏洩リスク最小化 – 識別子のみで、シークレットが存在しない セキュリティベストプラクティス – Microsoft 公式推奨 トークンの有効期限について : GitHub OIDC トークン : 5分間(GitHub が発行する認証用 JWT) Azure アクセストークン : 1時間(Service Principal)または24時間(Managed Identity) 実際のワークフロー実行では Azure アクセストークンが使用されるため、十分な有効期限があります 詳細なセットアップ手順と OIDC 認証の仕組みについては、 こちらの記事 を参照してください。 ステップ 3: Node.js セットアップと npm ci Setup Node.js with Cache - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: "application/blog-search-mcp-functions/package-lock.json" ポイント Node.js 22 LTS を使用(Azure Functions v4 対応) npm キャッシュ を有効化( cache: "npm" ) cache-dependency-path でモノレポの特定パスを指定 補足 : setup-node は package-lock.json のハッシュ値を自動計算してキャッシュキーを生成します。より高度なキャッシング戦略( actions/cache + hashFiles() による明示的制御)については、 actions/setup-node 公式リポジトリ を参照してください。 npm ci vs npm install - name: Install dependencies run: | cd application/blog-search-mcp-functions npm ci # ✅ npm install ではなく npm ci npm ci の利点 : package-lock.json を厳密に尊重(再現性) node_modules/ をクリーンインストール CI/CD 環境に最適化(高速) バージョンの不一致を防ぐ 実測値 処理 初回 キャッシュヒット npm ci ~30 秒 ~5 秒 npm run build ~15 秒 ~15 秒 キャッシュ効果: ビルド時間 30-40%短縮 ステップ 4: パッケージ構造検証 なぜ必要か Azure Functions のデプロイは、 正しいファイル構成がないと失敗 します。 特に以下のファイルは必須: host.json – Functions App 設定 package.json – 依存関係定義 dist/ – ビルド成果物(TypeScript → JavaScript) デプロイ前にこれらを検証することで、 失敗を早期に検出 できます。 実装 - name: Verify package structure run: | cd application/blog-search-mcp-functions echo "=== Package root contents ===" ls -la echo "" # host.json検証 echo "=== Checking host.json ===" if [ -f "host.json" ]; then echo "✅ host.json exists" cat host.json | jq . || echo "⚠ host.json is not valid JSON" else echo "❌ host.json missing!" exit 1 fi echo "" # package.json検証 echo "=== Checking package.json ===" if [ -f "package.json" ]; then echo "✅ package.json exists" node -e "console.log('✅ package.json is valid JSON')" -p "require('./package.json')" else echo "❌ package.json missing!" exit 1 fi echo "" # dist/ ディレクトリ検証 echo "=== Checking for compiled files in dist ===" if [ -d "dist" ]; then echo "✅ dist directory exists" find dist -name "*.js" | head -10 else echo "❌ dist directory missing!" exit 1 fi echo "" echo "=== All files in package (first 50) ===" find . -type f | head -50 ポイント jq で JSON 妥当性検証 Node.js で require() テスト(構文エラー検出) 失敗時は exit 1 で即座にワークフロー停止 ログ出力を見やすくフォーマット 実際のログ出力例 === Package root contents === drwxr-xr-x dist -rw-r--r-- host.json -rw-r--r-- package.json ... === Checking host.json === ✅ host.json exists { "version": "2.0", "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental", "version": "[4.*, 5.0.0)" } } === Checking package.json === ✅ package.json exists ✅ package.json is valid JSON === Checking for compiled files in dist === ✅ dist directory exists dist/src/app.js dist/src/functions/searchBlogPosts.mcp.js ... この検証により、 デプロイ失敗の原因を事前に特定 できます。 ステップ 5: テスト実行 –passWithNoTests フラグ プロジェクト初期段階では、テストファイルがないことがあります。 - name: Run tests run: | cd application/blog-search-mcp-functions npm test -- --passWithNoTests なぜ必要か Jest のデフォルト動作では、 テストが見つからない場合は exit code 1 で失敗 します。 --passWithNoTests フラグを使うことで: テストファイルがなくても CI/CD が通る 将来テストを追加した際、自動的にテストが実行される 開発初期段階の CI/CD 構築に最適 推奨アプローチ プロジェクト初期(テストなし) : --passWithNoTests 使用 テスト追加開始 : フラグ維持、段階的にテスト追加 テストカバレッジ確立後 : フラグ削除(テスト必須化) ステップ 6: Azure Functions デプロイ デプロイ前の確認 - name: Check Function App status before deployment run: | echo "Checking current Function App status..." az functionapp show \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --query "{name:name, state:state, kind:kind}" \ --output table 現在の Function App の状態を確認することで、デプロイ前の異常を検出できます。 デプロイ実行 - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} package: "application/blog-search-mcp-functions" respect-funcignore: true scm-do-build-during-deployment: false enable-oryx-build: false id: deploy デプロイを実行する際は、作成しているAzure Functionsのアプリ名のみで指定をすることができます。 デプロイ設定の詳細 respect-funcignore: true .funcignore ファイルを尊重し、不要なファイルをデプロイパッケージから除外します。 # .funcignore *.ts src/ tsconfig.json .git/ .github/ node_modules/ tests/ *.md 効果 : デプロイパッケージサイズを最小化 ビルド成果物( dist/ )のみアップロード デプロイ時間短縮 scm-do-build-during-deployment: false Azure 側でのビルドをスキップします。 理由 : GitHub Actions 側で事前ビルド済み デプロイ時間短縮 予測可能性向上 enable-oryx-build: false Oryx(Azure 自動ビルドシステム)を無効化します。 理由 : 明示的なビルドプロセス管理 トラブルシューティングが容易 設定の互換性に関する重要な注意事項 WEBSITE_RUN_FROM_PACKAGE との非互換性 以下の設定の組み合わせは 互換性がありません : WEBSITE_RUN_FROM_PACKAGE=1 + SCM_DO_BUILD_DURING_DEPLOYMENT=true (非互換) WEBSITE_RUN_FROM_PACKAGE=1 + SCM_DO_BUILD_DURING_DEPLOYMENT=false (推奨) 理由 : WEBSITE_RUN_FROM_PACKAGE は ZIP パッケージから直接実行する設定であり、デプロイ時のビルドと競合します。 リモートビルドを有効化する場合(Linux) : ネイティブモジュール(Puppeteer、Sharp等)を使用する場合は、リモートビルドが必要です: - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: ${{ vars.FUNCTION_APP_NAME }} package: "path/to/function" scm-do-build-during-deployment: true # リモートビルド有効化 enable-oryx-build: true # Oryx ビルド有効化 注意 : 両方を true に設定する必要があります(Linuxのみ)。 ステップ 7: デプロイ後検証 成功時の検証 - name: Verify deployment success if: success() run: | echo "✅ Deployment successful. Verifying MCP Server..." sleep 30 # Function App起動待機 # アプリ設定確認 az functionapp config show \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --query "{nodeVersion:nodeVersion, platform:linuxFxVersion}" \ --output table # Function App URL取得 FUNCTION_APP_URL=$(az functionapp show \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --query "defaultHostName" \ --output tsv) echo "MCP Endpoint: https://$FUNCTION_APP_URL/runtime/webhooks/mcp/sse" ポイント sleep 30 で Function App 起動待機(コールドスタート対策) Node.js バージョン確認(意図しないバージョン使用防止) エンドポイント URL を明示的に表示(手動テスト用) 失敗時のリカバリー - name: Check deployment result and restart if needed if: failure() run: | echo "❌ Deployment failed. Attempting recovery..." echo "Restarting Function App..." az functionapp restart \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} echo "Waiting for restart to complete..." sleep 60 echo "Checking Function App logs..." az functionapp logs tail \ --name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \ --resource-group ${{ vars.RESOURCE_GROUP }} \ --timeout 60 || echo "Could not retrieve logs" 自動リカバリーの利点 デプロイ失敗後の手動介入を減らす ログ取得による問題診断の容易化 一時的な問題(ネットワークエラーなど)からの自動復旧 実装例:Blog Search MCP Functions ここまでの設定を統合した完全なワークフローファイルを見てみましょう。 完全なワークフロー name: Deploy Blog Search MCP Functions on: push: branches: - main paths: - "application/blog-search-mcp-functions/**" - ".github/workflows/deploy-blog-search-mcp-functions.yml" workflow_dispatch: permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest environment: "production" steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: "application/blog-search-mcp-functions/package-lock.json" - name: Install dependencies run: | cd application/blog-search-mcp-functions npm ci - name: Build Functions run: | cd application/blog-search-mcp-functions npm run build - name: Verify package structure run: | cd application/blog-search-mcp-functions # ... (前述の検証スクリプト) - name: Run tests run: | cd application/blog-search-mcp-functions npm test -- --passWithNoTests - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} package: "application/blog-search-mcp-functions" respect-funcignore: true scm-do-build-during-deployment: false enable-oryx-build: false - name: Verify deployment success if: success() run: | # ... (前述の検証スクリプト) プロジェクト構成 application/blog-search-mcp-functions/ ├── package.json ├── tsconfig.json ├── host.json # Experimental Bundle設定 ├── .funcignore # デプロイ除外ファイル ├── src/ │ ├── app.ts # エントリーポイント │ ├── functions/ # MCP Tool定義 │ │ ├── searchBlogPosts.mcp.ts │ │ ├── listAllCategories.mcp.ts │ │ └── listAllHashtags.mcp.ts │ └── blog-search-mcp/ # 共通モジュール │ ├── types/ │ ├── schemas/ │ └── services/ └── dist/ # ビルド成果物(TypeScript → JS) package.json { "name": "blog-search-mcp-functions", "version": "1.0.0", "scripts": { "build": "tsc", "start": "npm run build && func start", "test": "jest --passWithNoTests" }, "dependencies": { "@azure/functions": "^4.0.0", "@supabase/supabase-js": "^2.76.1", "zod": "^4.1.12" }, "devDependencies": { "@types/node": "^22.10.1", "typescript": "^5.7.2" }, "engines": { "node": ">=22.0.0" } } Environment 変数 vs Secrets GitHub Secrets と Variables の適切な使い分けも重要です。 使い分けの基準 Secrets(機密情報) : secrets: AZURE_CLIENT_ID # OIDC認証情報 AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID Variables(非機密情報) : variables: APP_NAME # アプリケーション名 RESOURCE_GROUP # リソースグループ名 BLOG_SEARCH_MCP_FUNCTION_APP_NAME # Function App名 X_SCHEDULER_FUNCTION_APP_NAME # Function App名 判断基準 Secrets : 漏洩すると重大な影響(認証情報、API キー、トークン) Variables : 公開されても問題ない(リソース名、設定値) 設定方法 手動設定 GitHub リポジトリの「Settings」→「Secrets and variables」→「Actions」から設定します。 自動設定(推奨) Bicep デプロイスクリプト( deploy.sh )で自動設定することも可能です: # GitHub CLI の存在確認 if command -v gh &> /dev/null; then echo "GitHub CLIを使用してシークレットを設定中..." # Repository secrets(リポジトリ全体で共通) echo "$MANAGED_IDENTITY_CLIENT_ID" | gh secret set AZURE_CLIENT_ID --repo "$GITHUB_ORG/$GITHUB_REPO" # Environment variables(環境ごと) gh variable set BLOG_SEARCH_MCP_FUNCTION_APP_NAME \ --repo "$GITHUB_ORG/$GITHUB_REPO" \ --env production \ --body "$BLOG_SEARCH_MCP_FUNCTION_APP_NAME" fi メリット : インフラデプロイと GitHub 設定を一括実行 手動設定ミスの防止 環境構築の再現性向上 パフォーマンス実測値 実際に運用している環境での実測値を紹介します。 ビルド時間(Blog Search MCP Functions) ステップ 初回 キャッシュヒット Setup Node.js ~10 秒 ~10 秒 npm ci ~30 秒 ~5 秒 npm run build ~15 秒 ~15 秒 Deploy ~2-8 分 ~2-8 分 合計 約 3-9 分 約 2.5-8.5 分 注 : デプロイ時間は、関数のパッケージサイズ、依存関係の数、Azure リージョンの混雑状況によって大きく変動します。 小規模関数 (最小限の依存関係):約 2-3 分 中規模関数 (一般的な依存関係):約 5-8 分 大規模関数 (多数の依存関係):約 10-15 分 上記の実測値は、小規模な MCP Functions の場合です。本番環境での一般的なアプリケーションでは、 5-15 分程度を見込むことを推奨 します。 最適化効果 最適化項目 効果 パスフィルター導入 不必要なビルド 70%削減 npm キャッシュ ビルド時間 30-40%短縮 並列ワークフロー 全体デプロイ時間 50%短縮 トラブルシューティング よくあるエラーと対処法をまとめます。 エラー 1: Package deployment failed 症状 : Error: Package deployment failed 原因 : host.json がパッケージに含まれていない package.json が不正な JSON dist/ ディレクトリが空 解決策 : パッケージ構造検証ステップを追加し、 .funcignore を確認します。 - name: Verify package structure run: | # host.json, package.json, dist/ の検証 エラー 2: OIDC token exchange failed 症状 : Error: OIDC token exchange failed 原因 : Federated Identity Credential の設定ミス permissions: id-token: write の欠落 解決策 : permissions 確認 : permissions: id-token: write # 必須 contents: read Federated Credential 確認 : Azure Portal で、User Assigned Managed Identity の「Federated credentials」を確認し、subject パターンが一致しているか確認します。 repo:org/repo:environment:production 詳細は こちらの記事 を参照してください。 エラー 3: Function runtime error 症状 : デプロイは成功するが、Function App が起動しない 原因 : Node.js バージョン不一致 依存関係の欠落 解決策 : デプロイ後に Node.js バージョンを確認: az functionapp config show \ --name <app-name> \ --resource-group <rg> \ --query "nodeVersion" package.json の engines フィールドと一致しているか確認します。 { "engines": { "node": ">=22.0.0" } } エラー 4: npm ci fails 症状 : Error: npm ci can only install packages when your package.json and package-lock.json are in sync 原因 : package.json と package-lock.json の不一致 解決策 : ローカルで package-lock.json を再生成: rm package-lock.json npm install git add package-lock.json git commit -m "chore: regenerate package-lock.json" 実装のポイントまとめ カテゴリ DO(推奨) DON’T(非推奨) 認証 OIDC 認証を使用 ・パスワードレス、セキュア ・シークレット管理不要 ・自動ローテーション シークレットベース認証 ・有効期限管理の負担 ・定期的なローテーション作業 ・漏洩リスク 依存関係管理 npm ci を使用 ・ package-lock.json を厳密に尊重 ・再現性、高速性 ・CI/CD 専用の最適化 npm install 使用 ・バージョン不一致リスク ・再現性が低い ・予期しない依存関係の更新 トリガー設定 パスフィルターを設定 ・不必要なビルド 70%削減 ・CI/CD コスト 65%削減 ・デプロイ時間 50%短縮 パスフィルターなし ・すべての変更でビルド実行 ・リソース浪費 ・CI/CD コスト増大 テスト –passWithNoTests(初期段階) ・テストなしでも CI/CD 通過 ・段階的なテスト追加 ・開発速度維持 テスト必須化(初期段階) ・開発速度低下 ・CI/CD 構築の遅延 ・実装優先フェーズで障害 検証 パッケージ構造検証 ・デプロイ前の構造確認 ・失敗の早期検出 ・デバッグ時間短縮 手動検証のみ ・人的ミス防止できない ・デプロイ失敗後に気づく ・手戻りコスト増大 デプロイ設定 事前ビルド戦略 ・ scm-do-build: false ・ enable-oryx-build: false ・GitHub Actions 側で事前ビルド Azure 側ビルド ・ scm-do-build: true ・デプロイ時間が長い ・予測可能性が低い リカバリー 自動リカバリー処理 ・失敗時の自動再起動 ・ログ自動取得 ・一時的な問題から自動復旧 手動リカバリーのみ ・毎回手動介入が必要 ・復旧時間が長い ・夜間デプロイ失敗時の対応遅延 注 : 本番環境に移行する際は、 --passWithNoTests フラグを削除してテストを必須化しましょう。品質保証のため、テストカバレッジを確立した後は必ずテストが実行される状態にすることを推奨します。 まとめ この記事で実装したこと GitHub Actions による Azure Functions デプロイパイプライン OIDC 認証によるパスワードレスデプロイ パス条件トリガーで効率化 (不要なビルド 70%削減) npm ci + キャッシュで高速化 (ビルド時間 30-40%短縮) パッケージ構造検証で品質保証 自動リカバリー処理 得られる効果 セキュリティ面 : シークレットキー管理不要 自動ローテーション 漏洩リスク最小化 効率面 : デプロイ時間短縮(小規模関数で約 2-3 分、一般的には 5-15 分) CI/CD コスト削減(65%) 手動作業の削減 品質面 : デプロイ失敗の早期検出 自動検証とリカバリー 再現性の高いビルド 次のステップ さらに発展させるには: マルチ環境デプロイ – staging/production 環境の管理 Bicep 統合 – インフラと CI/CD の完全自動化 Python 版 – Python Runtime での実装 モニタリング統合 – Application Insights との連携 参考リンク 公式ドキュメント Azure Functions GitHub Actions OIDC 認証(Azure) 関連記事 GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025 年版 Azure Functions×DevContainer 環境構築| Node.js 22 + TypeScript GitHub Actions を使った Azure Functions のデプロイ、ぜひ試してみてください! OIDC 認証によるパスワードレスデプロイで、セキュリティと効率性の両方を手に入れられます。 質問や改善提案があれば、ぜひコメントで教えてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub Actions×Azure Functions実装ガイド|OIDC認証で実現するセキュアなCI/CD(Node.js編) first appeared on SIOS Tech. Lab .
はじめに ども!久しぶりに公式ドキュメントをあさっていたら、自分が使っていた発行プロファイル認証が「not recommended」と記載されていてビックリ仰天した龍ちゃんです。 皆さん、GitHub Actions から Azure リソースにデプロイする際、どの認証方式を使っていますか? 「え、推奨されない方式だったの!?」って大焦りで調べたら、 発行プロファイルは現在もサポートされている ものの、 セキュリティベストプラクティスとして OIDC 認証への移行が強く推奨されている ことが判明しました。 特に以下の理由から、Microsoft は OIDC 認証を推奨しています: 発行プロファイルは Basic 認証を使用しており、セキュリティ上の懸念がある 発行プロファイルには平文パスワードが含まれる OIDC 認証はシークレットキー不要で、より安全 そこで、 Microsoft 公式が推奨する OIDC(OpenID Connect)認証に直接移行 したんですが、設定できるならこっちのほうが良いなとなったのでまとめていきます! シークレットキー不要でパスワードレス認証 ができて、セキュリティレベルが大幅に向上しました。 今回は、 GitHub Actions から Azure への 3 つの認証方式を比較 し、 Azure CLI での爆速セットアップ方法を解説 します! この記事でわかること Azure 認証方式 3 つの徹底比較 (発行プロファイル vs Service Principal vs OIDC) OIDC 認証の仕組み と他方式との違い Azure CLI でのフェデレーション認証設定方法 (コマンド実行) GitHub Actions での汎用的な認証設定方法 必要な権限 とその確認方法(超重要!) トラブルシューティング の実例と解決方法 この記事の対象読者 以下のような方にお勧めです。 Azure CLI の基本操作ができる方 GitHub Actions から Azure にデプロイしたい方 発行プロファイルを使っていて、セキュアな方式に移行したい方 シークレットキー管理の手間を減らしたい DevOps エンジニア ターミナル操作に抵抗がない方 なぜ Azure CLI を使うのか? 爆速セットアップ : コピペで 5 分で完了 再現性が高い : コマンドをスクリプト化できる IaC 化しやすい : Bicep/Terraform への移行が容易 ポータルのポチポチ作業は不要です! それでは、見ていきましょう! はじめに – なぜ OIDC 認証が必要なのか? GitHub Actions から Azure 認証の重要性 GitHub Actions から Azure Functions、Web Apps、Static Web Apps、Container Apps などにデプロイする際、 Azure への認証 が必要です。 この認証方式、実は 3 つの選択肢 があるんですよね: 発行プロファイル (Publish Profile) Service Principal + シークレットキー OIDC 認証 (OpenID Connect) 並べた順に難しくなっていきます。結論から言うと、 Microsoft 公式が推奨しているのは OIDC 認証 です。 なぜ OIDC 認証が推奨されるのか、他の 2 つの方式と比較しながら見ていきましょう。 Azure 認証方式 3 つの徹底比較 実体験と移行の経緯 正直に言うと、 私が実際に使ったことがあるのは「発行プロファイル」と「OIDC 認証」の 2 つだけ です。 Service Principal は選択肢として存在しますが、 セキュリティベストプラクティスとして OIDC 認証が推奨されていると分かった時点で、直接 OIDC 認証に移行しました 。 なぜ Service Principal をスキップしたかというと: シークレットキー管理の手間 : Service Principal も結局シークレットキーが必要で、定期的なローテーション作業が発生する OIDC 認証が最終解 : セキュリティベストプラクティスとして推奨されているなら、最初から OIDC 認証にした方が合理的 移行コスト : Service Principal に移行してから、さらに OIDC に移行するのは二度手間 つまり、 「発行プロファイル → Service Principal(中間) → OIDC(推奨)」という段階を踏まず、最初から最終形態に移行した わけです。 ただし、Service Principal は歴史的経緯や他のプロジェクトで遭遇する可能性もあるので、選択肢として紹介しておきます。 それでは、3 つの方式を詳しく比較していきましょう! 1. 発行プロファイル方式(最も簡単だが、セキュリティ上推奨されない) 仕組み 発行プロファイル(Publish Profile)は、Azure ポータルから XML ファイル をダウンロードして、GitHub Secrets に保存する方式です。 Azure Portal → リソース → 発行プロファイルのダウンロード ↓ GitHub Secrets に AZURE_PUBLISH_PROFILE として保存 ↓ GitHub Actions でデプロイ コード例 # 発行プロファイル方式 jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to Azure Functions uses: Azure/functions-action@v1 with: app-name: "my-function-app" publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }} # XML形式 発行プロファイルの中身(例) : <publishData> <publishProfile profileName="my-function-app - Web Deploy" publishMethod="MSDeploy" publishUrl="my-function-app.scm.azurewebsites.net:443" userName="$my-function-app" userPWD="verylongsecretpassword123..." <!-- ← これが問題! --> ... /> </publishData> メリット 設定が超簡単 : Azure ポータルから 1 クリックでダウンロード 初心者にやさしい : 複雑な権限設定が不要 すぐに動く : 5 分で設定完了 デメリット セキュリティリスク大 : パスワードが平文で含まれる Basic 認証を使用 : Microsoft が「inherently insecure(本質的に安全でない)」と警告 監査ログ不足 : 誰がデプロイしたか追跡困難 権限が広すぎる : リソース全体への管理者権限 ローテーション困難 : パスワード更新が面倒 Microsoft 非推奨 : セキュリティベストプラクティスとして OIDC 認証が推奨されている 重要な注意 : Microsoft 公式ドキュメントでは「The technique described in this article is inherently insecure, because this technology uses Basic Authentication」と明記されており、本番環境での使用は推奨されていません。学習目的や個人プロジェクトでの利用にとどめ、本番環境では OIDC 認証を採用することを強くお勧めします。 2. Service Principal + シークレットキー方式(従来の推奨) 仕組み Azure AD で Service Principal(サービスプリンシパル)を作成し、 Client Secret(シークレットキー) を発行して認証する方式です。 Azure AD → Service Principal作成 ↓ Client Secret発行(有効期限: 最長2年) ↓ GitHub Secretsに保存 ↓ GitHub Actionsで認証 コード例 # Service Principal + シークレットキー方式 jobs: deploy: runs-on: ubuntu-latest steps: - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} # JSON形式 AZURE_CREDENTIALS の中身(例) : { "clientId": "xxx-xxx-xxx", "clientSecret": "your-secret-key-here", // ← シークレットキー "subscriptionId": "xxx-xxx-xxx", "tenantId": "xxx-xxx-xxx" } Service Principal の作成方法 # Service Principal作成 az ad sp create-for-rbac \ --name "github-actions-sp" \ --role "Contributor" \ --scopes "/subscriptions/{subscription-id}/resourceGroups/{resource-group}" \ --sdk-auth 出力された JSON をそのまま GitHub Secrets に保存します。 メリット RBAC 権限管理 : 必要な権限のみ付与可能 監査ログ充実 : Azure AD での詳細なログ 複数リソース対応 : 1 つの Service Principal で複数リソースにアクセス よく使われている : 情報が豊富 デメリット シークレットキー管理 : 定期的なローテーションが必要(有効期限: 最長 2 年) 漏洩リスク : シークレットキーが漏洩したら、全リソースにアクセス可能 管理コスト : 有効期限が切れたら、手動で更新が必要 GitHub Secrets に保存 : 暗号化はされているが、アクセス可能 重要な補足: Service Principal でも OIDC 認証が可能 実は、 Service Principal でも Federated Identity Credentials を使えば、シークレットキー不要の OIDC 認証が可能 です。 つまり、以下の 2 つの方式は技術的には非常に類似しています: Service Principal + Federated Credentials Managed Identity + Federated Credentials (この記事で解説する方式) 主な違い : Managed Identity : 常に Azure リソースに紐づけられる。Azure が自動的に認証情報を管理 Service Principal : Azure リソースに紐づけなくても独立して存在可能 なぜ Managed Identity を選んだか : Azure リソース(Functions, Web Apps 等)にデプロイする場合、Managed Identity の方がシンプル リソースとの紐付けが明確で、管理しやすい Microsoft のベストプラクティスドキュメントでも Managed Identity が推奨されている ただし、複数の Azure サブスクリプションをまたいでアクセスする必要がある場合など、Service Principal + Federated Credentials の方が適している場合もあります。 3. OIDC 認証方式(Microsoft 公式推奨) 仕組み OIDC(OpenID Connect)認証は、 短命なトークンを使った認証方式 です。シークレットキーが一切不要なのが最大の特徴。 GitHub Actions → GitHub OIDCプロバイダー ↓ OIDCトークン発行(5分間有効) ↓ Azure AD → Federated Identity Credential検証 ↓ Managed Identity → Azureアクセストークン発行 ↓ Azureリソースにデプロイ トークン有効期限の詳細: GitHub OIDC トークン(JWT): 5 分間 有効 Azure アクセストークン: 約 60-90 分 (平均 75 分、一般的には 1 時間として扱われる) トークンの更新: Azure SDK/CLI が自動的に管理(手動更新不要) コード例 # OIDC認証方式(推奨) jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write # ← OIDC認証に必須! contents: read steps: - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} # シークレットキーは不要! 必要なシークレット(すべて識別子のみ) : AZURE_CLIENT_ID: xxx-xxx-xxx (Managed IdentityのClient ID) AZURE_TENANT_ID: xxx-xxx-xxx (テナントID) AZURE_SUBSCRIPTION_ID: xxx-xxx-xxx (サブスクリプションID) 重要 : これらは すべて識別子のみ で、シークレットキーは含まれていません。つまり、 万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みです。 メリット シークレットキー不要 : パスワードレス認証でセキュリティリスク削減 自動ローテーション : GitHub の OIDC トークン(JWT)は 5 分間のみ有効、Azure のアクセストークンも自動管理(手動ローテーション不要) 漏洩リスク最小化 : 識別子のみで、シークレットが存在しない 監査ログ充実 : Azure AD での詳細な認証ログ セキュリティベストプラクティス : Azure App Service 向けに公式推奨 リポジトリ・ブランチ制限 : 特定の GitHub リポジトリ・ブランチからのみ認証可能 デメリット 初期設定がやや複雑 : Azure ポータルでの設定が必要 理解に時間がかかる : OIDC の仕組みを理解する必要がある 権限が必要 : Managed Identity 作成と RBAC 設定に管理者権限が必要 私の経験 : 最初は「設定が複雑そう…」って敬遠していたんですが、一度設定してしまえば、その後の管理が超ラク!シークレットキーローテーションの手間がゼロになったのは感動しました。 イメージとしては、GitHub リポジトリ自体に認証の権限を割り振る という感じです。発行プロファイルや Service Principal のように「シークレットを GitHub Secrets に保存する」のではなく、「このリポジトリからの実行は信頼できる」と Azure 側で事前に承認しておくイメージですね。 3 つの認証方式 比較表 項目 発行プロファイル Service Principal OIDC 認証 セキュリティ 低 中 高 設定の簡単さ 超簡単 普通 やや複雑 シークレット管理 パスワード必要 シークレットキー必要 不要 ローテーション 困難 手動(年 1 回程度) 完全自動(手動不要) 漏洩リスク 高 中 最小 監査ログ 不足 充実 充実 権限制御 広すぎる RBAC 可能 RBAC 可能 Microsoft 推奨 Not Recommended サポート継続 ベストプラクティス 初期設定時間 5 分 15 分 30 分 運用コスト 中 中 低 どの方式を選ぶべきか? 結論 : セキュアに運用したいのであれば OIDC 認証一択 です。 個人プロジェクト・学習用 : 発行プロファイルで素早くスタート → 慣れたら OIDC に移行 チーム開発・本番環境 : 最初から OIDC 認証を採用 私も複数のプロジェクトで検証しましたが、 長期的に見ると OIDC 認証が圧倒的にコストパフォーマンスが高い です。というか、発行プロファイルの管理が面倒くさかったです。再発行のたびに GitHub Secret に保存ってアプリが増えれば増えるほど面倒になるんですよね。特にモノレポ環境であれば、効果絶大です。 前提条件と必要な権限 必要な環境 OIDC 認証を設定するには、以下の環境が必要です: Azure CLI : バージョン 2.30 以上(インストール方法は後述) Azure サブスクリプション : Azure リソースをデプロイするため GitHub リポジトリ : Admin 権限が必要(GitHub Secrets を設定するため) ターミナル : Bash、PowerShell、または Zsh Azure CLI のインストール確認 すでに Azure CLI がインストールされているか確認しましょう: az version 出力例 : { "azure-cli": "2.50.0", "azure-cli-core": "2.50.0", "azure-cli-telemetry": "1.0.8", ... } まだインストールしていない場合 方法 1: ローカルマシンにインストール macOS / Linux (Homebrew): brew install azure-cli Windows (winget): winget install Microsoft.AzureCLI その他のインストール方法: Azure CLI 公式ドキュメント 方法 2: DevContainer でチーム全体の環境を統一(オプション) もし DevContainer を使って開発をしているなら、この方法も検討できます。DevContainer Features を使えば、チーム全体で同じバージョンの Azure CLI を使用でき、環境差異によるトラブルを防げます。 注意 : Docker Desktop のライセンスや、企業のセキュリティポリシーによる制約がある場合は、ローカルインストールをお勧めします。 .devcontainer/devcontainer.json : { "name": "Azure Development", "image": "mcr.microsoft.com/devcontainers/base:bullseye", "features": { "ghcr.io/devcontainers/features/azure-cli:1": { "version": "latest" } } } メリット : チーム全員が同じ環境で作業可能 新メンバーのオンボーディングが簡単(コンテナ起動するだけ) CI/CD 環境とローカル環境の差異をなくせる 詳しい手順 : DevContainer と Azure CLI の詳細な環境構築手順は、別記事「 DevContainer 実践入門:Azure CLI+GitHub CLI 環境をチーム全体で統一 」で解説しています。Azure CLI、GitHub CLI、SWA CLI を統一環境として構築する方法を紹介しているので、ぜひご覧ください。 Azure へのログイン Azure CLI で Azure にログインします: az login ブラウザが開くので、Azure アカウントでログインしてください。 ログイン確認 : az account show 出力例 : { "id": "xxx-xxx-xxx-xxx", "name": "Pay-As-You-Go", "tenantId": "xxx-xxx-xxx-xxx", "user": { "name": "user@example.com", "type": "user" } } 必要な権限(超重要!) ここが一番重要なポイントです。 OIDC 認証設定とフェデレーション認証の設定には、 かなり強い権限が必要 です。 私も最初、権限不足でエラーに悩まされて、 Azure の管理者に 3 回も確認とお願いの申請をしました …。社内の担当者には本当に頭が上がりません。 なので、 設定前に必ず権限を確認しておくことを強くオススメします 。 最小権限セット 以下の操作を実行するには、それぞれ対応する権限が必要です: User Assigned Managed Identity の作成 : Microsoft.ManagedIdentity/userAssignedIdentities/write Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/write RBAC 権限割り当て : Microsoft.Authorization/roleAssignments/write これは リソースグループレベルの「所有者」または「ユーザーアクセス管理者」ロールが必要 リソースデプロイ全般 : Microsoft.Resources/deployments/write 推奨ロール リソースグループレベルの「所有者」ロールが最も簡単 です。 もしくは、以下の組み合わせ: 共同作成者 (Contributor) + ユーザーアクセス管理者 (User Access Administrator) 私の場合、リソースグループの所有者権限を割り振ってもらいました。 権限確認方法 Azure CLI で現在のユーザーの権限を確認します: # リソースグループを変数に設定(後で使います) RESOURCE_GROUP="rg-example" # 実際のリソースグループ名に置き換え # 自分のロール割り当てを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName, Scope:scope}" \ --output table 期待される出力 : Principal Role Scope -------------------- ------ ---------------------------------------- user@example.com Owner /subscriptions/.../resourceGroups/rg-example 「Owner」ロールが表示されていれば OK! もし Owner ロールがない場合は、以下のいずれかを確認してください: Contributor + User Access Administrator の組み合わせ サブスクリプションレベルでの Owner ロール 権限が不足している場合の対処 管理者に権限を依頼する 権限が不足している場合は、以下のテンプレートで管理者に依頼しましょう。(こういう時の AI はマジで頼りになりますよね。) 件名: リソースグループへの所有者ロール付与依頼 以下のリソースグループに対して「所有者」ロールを付与してください: - リソースグループ名: <RESOURCE_GROUP_NAME> - 理由: GitHub OIDC認証設定とAzureリソースデプロイのため 必要な具体的な操作: 1. User Assigned Managed Identityの作成 2. Federated Identity Credentialの設定 3. RBAC権限割り当て よろしくお願いいたします。 ポイント : 権限確認は 設定前に必ず実施 しましょう。 途中でエラーになると、中途半端な状態で止まってしまい、トラブルシューティングが超面倒です。 さらに、誰もメンテナンスしていない Managed Identity やロールが作られてしまい、 後から担当者に「これ何に使ってるんですか?」って確認が飛んでくる 可能性もあります…(察してください…)。 OIDC 認証の仕組み詳細 OIDC 認証フロー フローの詳細解説 GitHub Actions が OIDC トークンをリクエスト : ワークフローに permissions.id-token: write を設定 GitHub Actions が自動的に GitHub OIDC プロバイダーにリクエスト GitHub が OIDC トークン(JWT)を発行 : リポジトリ、ブランチ、環境などの情報を含む JWT トークンを発行 有効期限: 5 分間(非常に短命で安全) Azure にトークンを Exchange(交換) : azure/login@v2 アクションが自動的に実行 Client ID、Tenant ID、Subscription ID を使用 ここが OIDC 認証の魔法のポイント!   GitHub Actions 上で発行したトークンを Azure 上のアクセストークンに交換しています Azure がフェデレーション認証情報を検証 : issuer: https://token.actions.githubusercontent.com (GitHub 固定) subject: repo:{org}/{repo}:environment:production (設定したパターン) audiences: api://AzureADTokenExchange (Azure 固定) Managed Identity を検証 : Azure AD が Managed Identity の存在を確認 RBAC 権限を確認 Azure アクセストークンを発行 : GitHub Actions が使用できる Azure アクセストークンを発行 Azure リソースにアクセス : Azure Functions、Web Apps などにデプロイまたはアクセス 操作成功 : 結果を GitHub Actions に返す なぜシークレットキー不要なのか? OIDC 認証の魔法は、 GitHub と Azure の信頼関係(Trust Relationship)  にあります。 従来方式 : GitHub Actions → シークレットキー提示 → Azure「認証OK」 OIDC 認証 : GitHub Actions → OIDCトークン提示 → Azure「このトークン、本当にGitHubが発行した?」 ↓ Federated Identity Credentialで検証 ↓ 「リポジトリ・ブランチも一致!」→ 認証OK ポイント : GitHub の OIDC トークン(JWT)は 5 分間のみ有効 (非常に短命で安全) Azure が発行するアクセストークンは約 60-90 分有効 (Azure SDK/CLI が自動管理) 特定のリポジトリ・ブランチ からのみ認証可能(Federated Identity Credential で制限) トークン自体が GitHub の署名付き で、改ざん不可能 つまり、シークレットキーを保存しなくても、「この GitHub Actions の実行は、確かに信頼できるリポジトリからのものだ」と証明できるわけです! Azure CLI での爆速セットアップ それでは、Azure CLI を使って OIDC 認証を設定していきましょう! 所要時間: 約 5 分(コピペで完結) ステップは全部で 3 つです: User Assigned Managed Identity 作成 Federated Identity Credential 設定 RBAC 権限設定 事前準備: 環境変数の設定 まず、これから使う変数をまとめて設定しておきます。コピペで使えるように、実際の値に置き換えてください: # プロジェクト設定 APP_NAME="myapp" # アプリケーション名(任意) RESOURCE_GROUP="rg-example" # 既存のリソースグループ名 LOCATION="japaneast" # リージョン # GitHub設定 GITHUB_ORG="your-org" # GitHub組織名またはユーザー名 GITHUB_REPO="your-repo" # GitHubリポジトリ名 # Managed Identity名 IDENTITY_NAME="${APP_NAME}-github-identity" 確認 : echo "Identity名: $IDENTITY_NAME" echo "リソースグループ: $RESOURCE_GROUP" ステップ 1: User Assigned Managed Identity 作成 GitHub からの認証を受け入れる Managed Identity を作成します。 az identity create \ --name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --location $LOCATION 実行結果(例) : { "clientId": "xxx-xxx-xxx-xxx-xxx", "id": "/subscriptions/.../resourceGroups/rg-example/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myapp-github-identity", "location": "japaneast", "name": "myapp-github-identity", "principalId": "yyy-yyy-yyy-yyy-yyy", "resourceGroup": "rg-example", "type": "Microsoft.ManagedIdentity/userAssignedIdentities" } 重要な情報を変数に保存 : # Client IDとPrincipal IDを取得して変数に保存 CLIENT_ID=$(az identity show \ --name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --query clientId -o tsv) PRINCIPAL_ID=$(az identity show \ --name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --query principalId -o tsv) echo "Client ID: $CLIENT_ID" echo "Principal ID: $PRINCIPAL_ID" この Client ID は後で GitHub Secrets に設定します。メモしておきましょう! 結果確認(Azure ポータル) : ステップ 2: Federated Identity Credential 設定 次に、GitHub リポジトリと Managed Identity を紐づける「フェデレーション認証情報」を設定します。 これが超重要なセクションです! subject パターンの選択 Federated Identity Credential には、「どの GitHub リポジトリ・ブランチ・環境から認証を許可するか」を指定する subject というフィールドがあります。 主要な subject パターン : パターン subject 形式 使用例 production 環境(推奨) repo:{org}/{repo}:environment:production 本番環境デプロイ main ブランチ repo:{org}/{repo}:ref:refs/heads/main 基本的な CI/CD タグ repo:{org}/{repo}:ref:refs/tags/v* リリースデプロイ Pull Request repo:{org}/{repo}:pull_request PR 環境デプロイ 私の場合、 production 環境パターン を使っています。理由は以下の通り: GitHub Actions の environment 機能と連携できる 環境ごとに異なる Secrets・Variables を管理できる 「本番環境へのデプロイ」という意図が明確 production 環境の Federated Credential 作成 az identity federated-credential create \ --name github-federated-production \ --identity-name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --issuer "https://token.actions.githubusercontent.com" \ --subject "repo:${GITHUB_ORG}/${GITHUB_REPO}:environment:production" \ --audiences "api://AzureADTokenExchange" 実行結果(例) : { "audiences": ["api://AzureADTokenExchange"], "issuer": "https://token.actions.githubusercontent.com", "name": "github-federated-production", "resourceGroup": "rg-example", "subject": "repo:your-org/your-repo:environment:production", "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials" } ポイント : --subject の repo:your-org/your-repo:environment:production 部分が、GitHub Actions ワークフローの environment: "production" と一致します --audiences は固定値( api://AzureADTokenExchange ) --issuer も固定値( https://token.actions.githubusercontent.com ) 重要: 環境名の大文字小文字 GitHub Actions は環境名を 自動的に小文字に変換 して Subject クレームを生成します。 GitHub 環境名: Production (大文字) 実際の Subject: repo:org/repo:environment:production ( 小文字 ) Subject 設定時は必ず小文字で指定してください 。大文字小文字が一致しないと AADSTS70021 エラーが発生します。 main ブランチの Federated Credential も追加する場合 az identity federated-credential create \ --name github-federated-main \ --identity-name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --issuer "https://token.actions.githubusercontent.com" \ --subject "repo:${GITHUB_ORG}/${GITHUB_REPO}:ref:refs/heads/main" \ --audiences "api://AzureADTokenExchange" これで、production 環境と main ブランチの両方からデプロイできるようになります! 設定確認 作成した Federated Credential を確認しましょう: az identity federated-credential list \ --identity-name $IDENTITY_NAME \ --resource-group $RESOURCE_GROUP \ --query "[].{Name:name, Subject:subject}" \ --output table 出力例 : Name Subject ---------------------------- --------------------------------------------------------- github-federated-production repo:your-org/your-repo:environment:production github-federated-main repo:your-org/your-repo:ref:refs/heads/main 結果確認(Azure ポータル) : Federated Credential の制限事項 数量制限 : 1 つの Managed Identity あたり 最大 20 個 完全一致のみ : Subject はワイルドカード不可(完全一致のみサポート) 大規模プロジェクトで複数のリポジトリ・環境を管理する場合は、用途別に複数の Managed Identity を作成することを推奨します。 ステップ 3: RBAC 権限設定 Managed Identity を作成しただけでは、Azure リソースにアクセスできません。 RBAC(Role-Based Access Control)で権限を付与 する必要があります。 どのロールを付与するか? デプロイ対象の Azure サービスによって、必要なロールが異なります。 Azure サービス 推奨ロール スコープ Azure Functions Website Contributor リソースグループまたは個別リソース Azure Web Apps Website Contributor リソースグループまたは個別リソース Azure Static Web Apps Website Contributor リソースグループまたは個別リソース Azure Container Apps Contributor リソースグループまたは個別リソース 複数サービス Website Contributor リソースグループ(推奨) 私の場合 : リソースグループスコープで Website Contributor ロールを付与しています。 理由: Functions、Web Apps、Static Web Apps 全体をカバー 管理が簡単(個別リソースごとに設定する必要がない) 最小権限の原則に従いつつ、実用的 RBAC 権限の付与 リソースグループスコープで Website Contributor ロールを付与します: # リソースグループのIDを取得 RESOURCE_GROUP_ID=$(az group show \ --name $RESOURCE_GROUP \ --query id -o tsv) # Website Contributorロールを割り当て az role assignment create \ --assignee $PRINCIPAL_ID \ --role "Website Contributor" \ --scope $RESOURCE_GROUP_ID 実行結果(例) : { "principalId": "yyy-yyy-yyy-yyy-yyy", "principalType": "ServicePrincipal", "roleDefinitionName": "Website Contributor", "scope": "/subscriptions/.../resourceGroups/rg-example", "type": "Microsoft.Authorization/roleAssignments" } 重要: 権限の伝播時間 ロール割り当て直後は、権限が有効になるまで 最大 5 分間 かかる場合があります。 推奨アクション: 割り当て後、数分待ってからデプロイを実行 初回デプロイ時に権限エラーが発生した場合は、5 分後に再試行 設定確認 RBAC 権限が正しく付与されたか確認しましょう: az role assignment list \ --assignee $PRINCIPAL_ID \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalId, Role:roleDefinitionName, Scope:scope}" \ --output table 出力例 : Principal Role Scope ----------------------------------- ------------------- ---------------------------------------- yyy-yyy-yyy-yyy-yyy Website Contributor /subscriptions/.../resourceGroups/rg-example Website Contributor ロールが表示されていれば OK! 結果確認(Azure ポータル) : これで Azure 側の設定は完了です! たった 3 つのコマンド(Identity 作成、Federated Credential 設定、RBAC 付与)でセットアップ完了です。 GitHub Actions での認証設定 Azure CLI での設定が完了したら、次は GitHub Actions ワークフローを設定します。 GitHub Secrets の設定 まず、OIDC 認証に必要な 3 つのシークレットを GitHub Secrets に設定します。 必要な値を取得 すでに Azure CLI で取得した値を確認しましょう: # 1. Client ID(すでに取得済み) echo "AZURE_CLIENT_ID: $CLIENT_ID" # 2. Tenant ID TENANT_ID=$(az account show --query tenantId -o tsv) echo "AZURE_TENANT_ID: $TENANT_ID" # 3. Subscription ID SUBSCRIPTION_ID=$(az account show --query id -o tsv) echo "AZURE_SUBSCRIPTION_ID: $SUBSCRIPTION_ID" これらの値をメモしておきましょう! GitHub Secrets に手動で設定 GitHub リポジトリで以下のシークレットを設定します: GitHub リポジトリ → Settings → Secrets and variables → Actions New repository secret をクリック 以下の 3 つのシークレットを追加: Name Value AZURE_CLIENT_ID 上記で取得した Client ID AZURE_TENANT_ID 上記で取得した Tenant ID AZURE_SUBSCRIPTION_ID 上記で取得した Subscription ID ポイント : これらのシークレットは すべて識別子のみ で、シークレットキーは含まれていません。つまり、 万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みになっています。 基本的なワークフロー構成 OIDC 認証を使うには、以下の 3 つが 必須 です: permissions.id-token: write : GitHub Actions が OIDC トークンを発行できるようにする azure/login@v2 アクション : OIDC トークンを Azure アクセストークンに交換 GitHub Secrets 設定 : CLIENT_ID、TENANT_ID、SUBSCRIPTION_ID 基本テンプレート name: Deploy to Azure on: push: branches: - main workflow_dispatch: permissions: id-token: write # ← OIDC認証に必須! contents: read jobs: deploy: runs-on: ubuntu-latest environment: "production" # ← production環境を指定 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Azure Login (OIDC) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} # シークレットキーは不要! - name: Azure CLI - リソース一覧表示(動作確認) run: | az resource list --resource-group <RESOURCE_GROUP_NAME> --output table ポイント解説 : permissions.id-token: write : これがないと、OIDC トークンが発行されない GitHub Actions の重要な設定 environment: "production" : Federated Identity Credential の subject で指定した環境と一致させる Azure ポータルで設定した環境名と同じにする azure/login@v2 アクション : client-id 、 tenant-id 、 subscription-id の 3 つだけで OK シークレットキーは不要 トラブルシューティング OIDC 認証でよくあるエラーと解決方法をまとめます。 エラー 1: OIDC token exchange failed 症状 : Error: Login failed with Error: OIDC token exchange failed. Please check the following: - Federated credentials are correctly configured - The subject claim in the OIDC token matches the subject in the federated credential 原因 : Federated Identity Credential の subject パターンが、GitHub Actions の実行環境と一致していない。 解決方法 : 1. Azure ポータルで subject を確認 Managed Identity の フェデレーション資格情報 を開く 設定した資格情報の サブジェクト を確認 例 : subject: repo:your-org/your-repo:environment:production 2. GitHub Actions ワークフローの環境を確認 jobs: deploy: environment: "production" # ← ここが一致しているか確認 3. 一致しない場合の修正 パターン A : ワークフロー側を修正 # Azure側が environment:production なら jobs: deploy: environment: "production" # ← これに変更 パターン B : Azure 側を修正 Azure ポータルで、フェデレーション資格情報を再作成 ワークフローに合わせたエンティティ型を選択 エラー 2: permissions.id-token: write がない 症状 : Error: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL 原因 : GitHub Actions ワークフローに permissions.id-token: write が設定されていない。 解決方法 : ワークフローファイルに permissions セクションを追加します。 permissions: id-token: write # ← これを追加 contents: read jobs: deploy: # ... 注意 : permissions は job レベルではなく、 ワークフローのトップレベル に記述 エラー 3: GitHub Secrets が設定されていない 症状 : Error: Input required and not supplied: client-id 原因 : AZURE_CLIENT_ID などの GitHub Secrets が設定されていない。 解決方法 : 1. GitHub Secrets を確認 GitHub リポジトリの Settings → Secrets and variables → Actions で、以下のシークレットが設定されているか確認: AZURE_CLIENT_ID AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID 2. 設定されていない場合は追加 上記の「GitHub Secrets の設定」セクションを参照して、3 つのシークレットを追加してください。 デバッグ Tips GitHub Actions ログでの確認ポイント OIDC トークン発行成功 : Federated token successfully exchanged. Azure Login 成功 : Login successful. デプロイ成功 : Deployment successful. これらのメッセージが確認できれば、OIDC 認証は正常に動作しています! 番外編: Unknown Principal の削除方法 OIDC 認証の設定中に、 Managed Identity を作り直したり削除したりすると、ロール割り当てだけが残ってしまう ことがあります。 このような場合、Azure ポータルで権限を確認すると「Unknown Principal」として表示され、通常の方法では削除できません。 詳しい解決方法は別記事で解説しています : 「Cannot find user or service principal」エラー解決!Azure RBAC の正しい削除方法 この記事では以下を詳しく解説しています: Unknown Principal が発生する原因と Azure の仕様 プリンシパル ID では削除できない理由(エラーメッセージの解説) 割り当て ID(Assignment ID)を使った正しい削除方法 実務での推奨運用フローとセキュリティ対策 OIDC 認証の設定前にクリーンアップしておくと、後々のトラブルシューティングが楽になります ので、ぜひご参照ください! まとめ お疲れさまでした!長い記事でしたが、最後までお読みいただきありがとうございます。 3 つの認証方式の再確認 今回の記事で、GitHub Actions から Azure への 3 つの認証方式 を徹底比較しました: 発行プロファイル : 簡単で引き続きサポート(セキュリティリスクあり) Service Principal + シークレットキー : 従来推奨(管理コストが高い) OIDC 認証 : セキュリティベストプラクティス(パスワードレスで安全) OIDC 認証で得られる 3 つの大きなメリット シークレットキー不要 : パスワードレス認証でセキュリティリスク削減 自動ローテーション : GitHub の OIDC トークン(JWT)は 5 分間のみ有効、Azure のアクセストークンも自動管理(手動ローテーション不要) 監査ログ充実 : Azure AD での詳細な認証ログ(どのリポジトリ・ブランチからデプロイされたかが記録)で、コンプライアンス対応も万全 この記事で実装したこと 今回の記事では、以下の実装を解説しました: 3 つの認証方式の徹底比較 (発行プロファイル、Service Principal、OIDC) Azure ポータルでの画面操作 による OIDC 認証設定 User Assigned Managed Identity 作成 Federated Identity Credential 設定 (複数 subject パターン対応) RBAC 権限設定 のベストプラクティス GitHub Secrets の設定方法 (画面操作) GitHub Actions での汎用的な認証設定方法 トラブルシューティング (よくあるエラーと解決方法) 特に、「 3 つの認証方式の比較 」と「 Azure ポータルでの画面操作による設定 」のセクションは超重要です。 セキュリティ面での改善効果 私の場合、Azure Functions のデプロイで 発行プロファイル方式を使っていて 、以下のような課題がありました: XML ファイル内に 平文パスワードが含まれる セキュリティリスク GitHub Secrets に長期間有効な認証情報を保存 発行プロファイルの再生成・更新作業が必要 GitHub Actions で非推奨表示 が出てびっくり 発行プロファイル → OIDC 認証に直接移行 してから: シークレットキー管理の手間が 完全にゼロ セキュリティリスクが大幅に削減(平文パスワード不要) GitHub の OIDC トークン(JWT)は 5 分間のみ有効、Azure のアクセストークンも自動管理(手動ローテーション不要) 監査ログでの認証履歴が明確(どのリポジトリ・ブランチからデプロイされたかが記録される) Service Principal を経由せず、最初から最終形態の OIDC 認証に移行 したので、二度手間を避けられました。セキュリティレベルの大幅向上と、今後の管理コストの削減を実現できました。 次のステップ この記事で OIDC 認証の基本は理解できたと思います。次は以下にチャレンジしてみてください! 複数環境対応 : dev、staging、production の 3 環境で Federated Identity Credential を分ける 監視・アラート設定 : Application Insights でデプロイ成功・失敗を監視 自動テスト統合 : GitHub Actions で CI/CD パイプラインを拡張 Infrastructure as Code : Bicep IaC で設定を自動化(上級者向け) 特に、「複数環境対応」は本番運用では必須です。環境ごとに異なる Federated Identity Credential を設定することで、誤って本番環境にデプロイするリスクを防げます。 龍ちゃんの所感 GitHub Actions から Azure にデプロイする際、「発行プロファイルが簡単だから、それでいいや」って思っていた方、 私と同じように「Deprecated」って表示されてびっくりする前に 、ぜひ OIDC 認証にチャレンジしてみてください! 私も最初は「設定がややこしそう…Service Principal とか経由した方がいいのかな?」って思ったんですが、 いきなり OIDC 認証に移行して正解でした 。 この記事の手順通りに進めれば、Azure CLI と Azure ポータルの画面操作で設定できます。 一度設定してしまえば、その後の管理が超ラク! シークレットキーのローテーション作業から解放されて、セキュリティレベルも向上する。 Microsoft 公式が推奨している方式なので、セキュリティ面でも信頼性が高く、今後のデプロイのスタンダードになっていくはずです。 質問や「こんなエラーが出た!」などの困りごとがあれば、ぜひコメント欄で教えてください。一緒に解決していきましょう! それでは、セキュアで楽な Azure デプロイライフを! 参考リンク 公式ドキュメント Azure AD workload identity federation (Microsoft 公式) GitHub Actions – Azure Login Action Azure Managed Identity OpenID Connect (OIDC) with GitHub Actions Azure RBAC Documentation ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025年版 first appeared on SIOS Tech. Lab .
初めに ども!今月は Azure 関連の情報をメインにまとめている龍ちゃんです。今回は、実際に詰まった内容をもとに原因探求と対応方法についてまとめていきます。 Azure でリソースグループの権限確認したら、 Unknown Principal が大量に残ってた…なんて経験ありませんか?(これにぶち当たったあなたは勤勉です!) 実は私も先日、OIDC 認証の設定中にこの問題に遭遇して、めちゃくちゃハマりました。削除しようとしたら「Cannot find user or service principal in graph database」エラーが出て、「削除できんやん!」ってなったんですよね。 今回は、削除された Managed Identity のロール割り当てを削除する方法と、なぜこの問題が発生するのかを解説します。 この記事で分かること Unknown Principal が発生する原因と Azure の仕様 プリンシパル ID では削除できない理由(エラーメッセージの解説) 割り当て ID(Assignment ID)を使った正しい削除方法 実務での推奨運用フローとセキュリティ対策 それでは、さっそく見ていきましょう! この問題に遭遇するシナリオ Azure を使っていると、こんな状況に遭遇したことありませんか? テスト環境で OIDC 認証を試していたが、うまくいかずに Managed Identity を削除してやり直した プロジェクト終了時にリソースを削除したが、ロール割り当てだけが残ってしまった 別の担当者が作成した Managed Identity を削除したが、権限周りのクリーンアップを忘れていた リソースグループの権限確認をしたら、 Unknown Principal が大量に残っている… なぜこの問題が発生するのか? Azure では、以下のような仕組みになっています: Managed Identity や Service Principal を削除 Azure AD(Microsoft Entra ID)からプリンシパル自体が削除される ロール割り当ては自動削除されない ロール割り当ては Azure Resource Manager(ARM)のリソースとして別管理 プリンシパルを削除しても、 ロール割り当ては残り続ける つまり、 ロール割り当ては明示的に削除しない限り、永遠に残り続けます 。厄介な仕様ですね~ なぜ自動削除されないのか? これ、最初は「なんで自動削除してくれないの?」って思ったんですが、調べたところ以下のような理由があるようです。: 誤削除防止 : プリンシパルを誤って削除した場合、同じ ID で再作成すれば権限が復元できる 監査ログの保持 : 誰がどのリソースにアクセスできたかの履歴を残す 依存関係の問題 : ロール割り当てを自動削除すると、意図しない権限喪失が発生する可能性 ただし、この仕様のせいで ゴミが残りやすい のも事実です。 セキュリティ上の懸念 削除されたプリンシパルのロール割り当てが残っていると、以下のリスクがあります: 1. 同じプリンシパル ID で再作成されるリスク 注意すべきパターン ですが、実際のリスクは超限定的です。 過去に削除したプリンシパルの ID は、理論的には再利用可能 攻撃者が同じ ID でプリンシパルを作成した場合、古いロール割り当てが有効になる 結果的に、意図しない権限昇格が発生する可能性 ただし、超絶レアケース やはりこの辺は Azure 側で防止されています。: 30 日間のソフトデリート : Managed Identity や Service Principal は削除後 30 日間ソフトデリート状態になり、その間は同じ ID を再利用できません GUID の性質 : Azure が使用する GUID(Globally Unique Identifier)は、実質的に衝突しない設計になっています 30 日経過後も : ソフトデリート期間終了後に同じ ID が再割り当てされる確率は天文学的に低い 理論的なシナリオ : 1. テスト用 Managed Identity(ID: xxx-xxx-xxx)を作成 → Owner 権限付与 2. テスト終了後、Managed Identity を削除(でもロール割り当ては残る) 3. 30日間のソフトデリート期間が経過 4. 数ヶ月〜数年後、Azure が偶然同じIDを再割り当て(極めて低確率) 5. 新しいプリンシパルに、過去の Owner 権限が復活してしまう 現実的なリスク評価 : 可能性は極めて低いですが、 理論的にはゼロではありません 。より現実的なリスクは、以下のような運用上の問題です: セキュリティ監査で Unknown Principal の説明ができない 権限管理の可視性が失われる コンプライアンス要件に抵触する可能性 つまり、 セキュリティ侵害のリスク < 運用上の問題 という認識が正確ですね。 2. 権限管理の可視性が失われる 「誰がどのリソースにアクセスできるか」を確認するとき、Unknown Principal が大量にあると混乱する セキュリティ監査で「これ何ですか?」と質問され、説明に困る 本当に必要な権限と、ゴミの区別がつかなくなる 3. コンプライアンス違反のリスク 多くの企業では「最小権限の原則」をポリシーとして定めている 不要なロール割り当てが残っていると、このポリシーに違反する可能性 内部監査や外部監査で指摘される原因になる 運用上の問題点 セキュリティだけでなく、日々の運用でも問題になります。 権限トラブルシューティングが困難に 権限周りの問題をデバッグするとき、Unknown Principal があると混乱します: # 権限確認したら... az role assignment list --resource-group rg-prod --output table # 出力例 Principal Role Scope -------------------------------- ---------- ------- user@example.com Owner ... app-prod-deploy Contributor ... Unknown Owner ... ← これ何? Unknown Contributor ... ← これも何? app-prod-monitor Reader ... 「Unknown が何者か」を特定するには: Azure ポータルで Activity Log を確認 過去のデプロイ履歴を調査 前任者に確認(いない場合は詰む) 時間の無駄ですよね。 管理者からの確認依頼が来る 特に大規模な組織では、セキュリティチームから定期的に権限監査の依頼が来ます(ちなみにこれは妄想..): 件名: リソースグループ権限の確認依頼 以下のリソースグループに Unknown Principal のロール割り当てが 検出されました。これらは何に使用されていますか? - リソースグループ: rg-prod - Principal ID: xxx-xxx-xxx - ロール: Owner - 割り当て日: 2024-03-15 本日中にご回答ください。 答えられないんですよね。だって削除されたプリンシパルだから。 説明するのも面倒だし、「管理が甘い」って思われるのも嫌です。 問題の発見方法 権限確認コマンドで Unknown Principal もしくは null を検出 # リソースグループのロール割り当てを確認 RESOURCE_GROUP="rg-example" az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{ID:id, Principal:principalName, PrincipalID:principalId, Role:roleDefinitionName}" \ --output table 出力例 : ID Principal PrincipalID Role --------------------------------------------------------------------------------------------------------------- ----------- ----------------------------------- ----- /subscriptions/xxx/.../roleAssignments/aaa user@ex.com 111-111-111-111 Owner /subscriptions/xxx/.../roleAssignments/bbb Unknown 222-222-222-222 Owner /subscriptions/xxx/.../roleAssignments/ccc 333-333-333-333 Contributor Principal が Unknown になっているのが、削除されたプリンシパルのロール割り当てです。 削除方法:ハマったポイントと解決策 最初に試して失敗した方法 普通にプリンシパル ID を指定して削除しようとしました: # プリンシパルIDを指定して削除を試みる az role assignment delete \ --assignee 222-222-222-222 \ --resource-group $RESOURCE_GROUP エラーメッセージ : Cannot find user or service principal in graph database for 222-222-222-222. If the assignee is an appId, make sure the corresponding service principal is created with 'az ad sp create --id 222-222-222-222' 原因 : az role assignment delete --assignee は、Azure AD のグラフデータベースでプリンシパルを検索する プリンシパル自体が既に削除されているので、グラフデータベースに存在しない 結果的に「見つかりません」エラーになる 正解:割り当て ID(Assignment ID)を直接指定 ロール割り当て自体は Azure Resource Manager(ARM)のリソースとして残っているので、 リソース ID を直接指定すれば削除できます 。 手順 1: 削除対象の割り当て ID を確認 # 割り当てIDを含めて表示 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{ID:id, Principal:principalName, Role:roleDefinitionName}" \ --output table 出力例 : ID Principal Role --------------------------------------------------------------------------------------------------------------- ----------- ----------- /subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/bbb Unknown Owner 手順 2: 割り当て ID を指定して削除 # 割り当てID全体をコピーして実行 az role assignment delete --ids "/subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/bbb" 成功! 手順 3: 削除確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName}" \ --output table Unknown Principal が消えていれば OK です。 実務での推奨運用フロー OIDC 認証を設定する前に、以下のクリーンアップフローを実施しましょう: 1. 権限確認 RESOURCE_GROUP="rg-example" # すべてのロール割り当てを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName, AssignedDate:createdOn}" \ --output table 2. Unknown Principal の削除 各割り当て ID を個別に削除することで、より安全に削除できます: # Unknown Principal の割り当てIDを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[?principalName==null].{ID:id, Role:roleDefinitionName}" \ --output table # 確認した割り当てIDを1つずつ削除 # 例:1つ目の Unknown Principal を削除 az role assignment delete --ids "/subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/bbb" # 例:2つ目の Unknown Principal を削除 az role assignment delete --ids "/subscriptions/xxx/resourceGroups/rg-example/providers/Microsoft.Authorization/roleAssignments/ccc" # 削除後、都度確認しながら進める az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName}" \ --output table ポイント : 各削除の結果を確認しながら慎重に進められる 誤削除のリスクを最小限に抑えられる どの権限を削除したか記録しやすい 3. 削除確認 # Unknown Principal が消えたことを確認 az role assignment list \ --resource-group $RESOURCE_GROUP \ --query "[].{Principal:principalName, Role:roleDefinitionName}" \ --output table 4. OIDC 認証設定開始 クリーンな状態になったので、安心して OIDC 認証の設定を進められます。 まとめ 学んだこと Managed Identity を削除しても、ロール割り当ては自動削除されない Azure の仕様で、意図的にそうなっている セキュリティと監査ログ保持のためだが、ゴミが残りやすい Unknown Principal はセキュリティリスク 同じ ID で再作成されると、意図しない権限昇格の可能性 権限管理の可視性が失われる コンプライアンス違反のリスク 削除にはコツがある プリンシパル ID ではなく、割り当て ID(Assignment ID)を直接指定 定期的なクリーンアップが重要 OIDC 認証設定前だけでなく、定期的に確認 特にテスト環境では頻繁に発生する 実務での教訓 「リソースを削除したら、関連する権限も必ず削除する」 これを習慣化すると、後々のトラブルが減ります。特に: Managed Identity や Service Principal を削除する前に、ロール割り当てを確認 削除後、Unknown Principal が残っていないか確認 チームメンバーにもこの手順を共有 セキュリティは「意識」と「習慣」です。この記事が、誰かの役に立てば嬉しいです! 参考資料 この記事は以下の公式ドキュメントと実際の検証結果に基づいています: Microsoft 公式ドキュメント Azure ロールベースのアクセス制御 (Azure RBAC) のトラブルシューティング ロール割り当ての管理とトラブルシューティング手順 削除されたエンタープライズ アプリケーション、サービス プリンシパル、Managed ID を復元または削除する 30 日間のソフトデリート期間についての公式説明 Azure RBAC の制限 サブスクリプションあたり 4,000 のロール割り当て制限 Azure CLI – az role assignment Azure CLI コマンドリファレンス 関連記事 DevContainer 実践入門:Azure CLI+GitHub CLI 環境をチーム全体で統一 Azure CLI を含む開発環境のセットアップ方法 検証環境 Azure CLI : バージョン 2.50.0 以上 検証日 : 2025 年 11 月 検証環境 : Azure サブスクリプション おわりに Unknown Principal の削除、無事にできましたか? これは実際に Azure CLI で操作を行っている際に衝突した問題です。いや~ Tips としては超限定的な問題ですね。 この記事で紹介した方法を使えば、Unknown Principal を一掃できます。特に OIDC 認証の設定前にクリーンアップしておくと、後々のトラブルシューティングが楽になりますよ。 ポイントをおさらい : 削除は割り当て ID(Assignment ID)を直接指定 定期的なクリーンアップを習慣化 リソース削除前にロール割り当ても確認 セキュリティは「意識」と「習慣」です。今回の問題は「怠慢」ですね wwwww。この記事が、誰かの Azure ライフを少しでも楽にできたら嬉しいです! もし役に立ったら、ぜひ X(旧 Twitter)でシェアしてください。また、「こんな方法もあるよ」「ここが分かりにくかった」などのフィードバックがあれば、コメント欄や X で教えてもらえると嬉しいです。 それでは、良い Azure ライフを! 関連記事 この記事と合わせて読むと、さらに理解が深まります: DevContainer 実践入門:Azure CLI+GitHub CLI 環境をチーム全体で統一 Azure CLI を含む開発環境のセットアップ方法を詳しく解説 Azure OIDC 認証で安全な GitHub Actions デプロイを実現する方法 (執筆中) OIDC 認証の設定方法と、この記事で紹介したクリーンアップを実践する方法 こちらのトラブルシューティングの内容 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 「Cannot find user or service principal」エラー解決!Azure RBACの正しい削除方法 first appeared on SIOS Tech. Lab .
はじめに どうも、龍ちゃんです! 前回の「 Azure Functions×DevContainer 環境構築| Node.js 編 」では、Node.js 22 + TypeScript を使った DevContainer 環境構築を解説しました。今回は、 Python 3.11 を使った Azure Functions の開発環境を構築します。 サンプルリポジトリ : 本記事で解説する環境をすぐに試せるサンプルコードを公開しています。 GitHub : azure-functions-python-devcontainer クローンして DevContainer で開くだけで、すぐに動作確認できます! なぜ Python で Azure Functions なのか? 単純な REST API の実装であれば、Node.js と Python のどちらでも問題ありません。 しかし、以下のような 特定のライブラリや数値計算が必要な要件 がある場合は、Python を選択すべきです: データ処理 : pandas、NumPy を使った大量データの前処理・集計 機械学習 : scikit-learn、TensorFlow モデルの推論 数値計算 : SciPy、SymPy を使った科学技術計算 スクレイピング : BeautifulSoup、Scrapy を使った Web スクレイピング Python を選ぶ判断基準 : pandas / NumPy / scikit-learn などのライブラリが必須 既存の Python コードやモデルを Azure Functions で動かしたい データサイエンスチームが Python を使っている Node.js を選ぶ判断基準 : 単純な REST API やリアルタイム処理 TypeScript で型安全な開発がしたい npm エコシステムのライブラリを活用したい 項目 Node.js 版 Python 版 適した用途 REST API、リアルタイム処理 データ処理、機械学習、数値計算 ライブラリ npm エコシステム pandas、NumPy、scikit-learn 非同期処理 async/await が得意 asyncio があるが Node.js ほど主流ではない 学習コスト TypeScript の型定義 Python の動的型付け 今回も DevContainer を使うことで、チーム開発での再現性を確保します。 本記事の前提 Azure Functions×DevContainer 環境構築| Node.js 編 を読んでいることを推奨 Node.js 編と共通の前提条件(Docker Desktop、VSCode、Dev Containers 拡張機能)が必要です Docker Desktop がインストール済みであること インストール方法: Docker Desktop 公式サイト Visual Studio Code がインストール済みであること インストール方法: VSCode 公式サイト Dev Containers 拡張機能 がインストール済みであること VSCode の拡張機能パネルから「Dev Containers」をインストール Python の基本文法 を理解していること それでは、Python 版の DevContainer 環境構築を始めましょう! Azure Functions とは?(簡潔版) 詳細は Node.js 編 を参照してください。ここでは、Python 版で重要なポイントのみ記載します。 Python で使える主要なトリガー トリガー 用途 Python での利用例 HTTP Trigger REST API データ分析 API、機械学習推論 API Timer Trigger 定期実行 毎日深夜にデータ集計・レポート作成 Blob Trigger ファイル処理 CSV アップロード時の pandas 処理 Queue Trigger 非同期処理 大量データの分散処理 必要なツール一覧 Python 版でも、Node.js 版と同じツール構成です: ツール名 バージョン インストール先 Docker Desktop 最新 ホスト OS Visual Studio Code 最新 ホスト OS Dev Containers 拡張機能 最新 VSCode Python 3.11.x DevContainer 内で自動 Azure Functions Core Tools v4.x DevContainer 内で自動 Azurite 最新 DevContainer 内で自動 重要 : Python 版の Azure Functions でも、 Azure Functions Core Tools は Node.js 製 !DevContainer 内に Node.js もインストールされます ホスト OS には Python をインストール不要(すべて DevContainer 内で完結) Python DevContainer の構築 これから構築する環境の全体像を把握しましょう。 構築後のディレクトリ構成 azure-functions-python-devcontainer/ # プロジェクトルート ├── .devcontainer/ # DevContainer 設定 │ ├── Dockerfile # コンテナイメージ定義(方式1の場合) │ └── devcontainer.json # DevContainer 設定ファイル │ └── MyPythonFunctionApp/ # Azure Functions プロジェクト ├── .funcignore # デプロイ除外ファイル ├── .gitignore # Git 除外設定 ├── .venv/ # Python 仮想環境(作成後) │ ├── bin/ # 実行ファイル │ ├── lib/ # インストール済みパッケージ │ └── pyvenv.cfg # 仮想環境設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── requirements.txt # Python 依存関係 │ └── function_app.py # メイン関数ファイル # すべての関数を定義 Node.js 版との違い : package.json の代わりに requirements.txt src/functions/*.ts の代わりに function_app.py 1 ファイル .venv/ で Python 仮想環境を管理 ポイント : .devcontainer/ で開発環境を定義 MyPythonFunctionApp/ が実際の Functions プロジェクト Python の関数はすべて function_app.py に記述(Node.js版とは違い、1ファイルにまとめる) 構築方法 Python 版の DevContainer 構築には、主に 2 つの方法があります: Dockerfile 方式 – カスタム Dockerfile で詳細に制御( 推奨 ) DevContainer Features 方式 – 既存イメージに Node.js を追加 Python では Dockerfile 方式を推奨する理由 : Python は バージョン依存が強い ため、明示的なバージョン指定が重要です Features 方式だと、VSCode の Python Interpreter 設定で 意図しない Python 環境が選ばれる ことがあるんですよね Dockerfile 方式なら、Python 3.11 を確実に指定できる! 再現性が高く、チーム開発で環境差異が発生しにくい 注意 : 参考記事(Claude Code×DevContainer)では Features 方式を推奨していますが、Python の場合は Dockerfile 方式の方が確実です。 方法 1: Dockerfile 方式(推奨) Python ベースイメージから、Node.js を手動でインストールする方式です。 この方式の利点 : Python 3.11 を確実に指定できる 意図しない Python 環境が選ばれるリスクがない ビルド時に全依存関係がインストールされるため、起動が高速 ステップ 1: プロジェクトディレクトリの作成 mkdir azure-functions-python-devcontainer cd azure-functions-python-devcontainer mkdir .devcontainer ステップ 2: Dockerfile の作成 .devcontainer/Dockerfile : # .devcontainer/Dockerfile FROM mcr.microsoft.com/devcontainers/python:3.11 # Node.js のインストール(Azure Functions Core Tools に必要) RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ && apt-get install -y nodejs # 作業ディレクトリ WORKDIR /workspace # 実行ユーザー USER vscode # npm のグローバルインストール先を vscode ユーザーのホームディレクトリに設定 RUN mkdir -p /home/vscode/.npm-global \ && npm config set prefix '/home/vscode/.npm-global' # PATH に npm グローバルディレクトリを追加 ENV PATH="/home/vscode/.npm-global/bin:${PATH}" # Azure Functions Core Tools と Azurite のインストール RUN npm install -g \ npm@11.5.2 \ azure-functions-core-tools@4 \ azurite # バージョン確認用コマンド(デバッグ用) RUN echo "=== Installed Versions ===" \ && python --version \ && pip --version \ && node --version \ && npm --version \ && func --version \ && echo "=========================" # デフォルトコマンド CMD ["sleep", "infinity"] ポイント : mcr.microsoft.com/devcontainers/python:3.11 は Microsoft 公式の Python 3.11 イメージ Node.js をインストール(Azure Functions Core Tools が Node.js 製のため必須!) vscode ユーザーで実行(Python イメージのデフォルトユーザー) ステップ 3: devcontainer.json の作成 .devcontainer/devcontainer.json : { "name": "Azure Functions Python DevContainer", "build": { "dockerfile": "./Dockerfile" }, "workspaceFolder": "/workspace", "remoteUser": "vscode", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "ms-python.python", "ms-python.vscode-pylance", "ms-python.black-formatter" ] } } } ポイント : ms-python.python : Python 拡張機能 ms-python.vscode-pylance : 型チェック・インテリセンス ms-python.black-formatter : Python コードフォーマッタ 方法 2: DevContainer Features 方式(代替案) DevContainer Features を使うと、Node.js のインストールが 1 行で完了します。 この方式の注意点 : シンプルだが、Python Interpreter が意図しない環境を指す場合がある DevContainer 起動後に必ず Interpreter 設定を確認する必要がある Features 方式でも問題ない場合もあるが、Dockerfile 方式の方が確実 .devcontainer/devcontainer.json : { "name": "Azure Functions Python DevContainer", "image": "mcr.microsoft.com/devcontainers/python:3.11", "workspaceFolder": "/workspace", "remoteUser": "vscode", "features": { "ghcr.io/devcontainers/features/node:1": { "version": "lts" } }, "postCreateCommand": "npm install -g npm@11.5.2 azure-functions-core-tools@4 azurite", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "ms-python.python", "ms-python.vscode-pylance", "ms-python.black-formatter" ] } } } ポイント : features セクションで Node.js を自動インストール Dockerfile が不要でシンプル 参考記事( Claude Code×DevContainer 環境構築ガイド )でも推奨されている方式 どちらの方式を選ぶか : Dockerfile 方式を推奨 – Python バージョンを確実に制御できる Features 方式は、Python Interpreter 設定に注意すれば使える DevContainer の起動 VSCode で azure-functions-python-devcontainer フォルダを開く 左下の「 >< 」アイコンをクリック 「 Reopen in Container 」を選択 Docker イメージのビルドと起動が開始されます 成功すると : VSCode の左下に「 Dev Container: Azure Functions Python DevContainer 」と表示 重要: Python Interpreter の設定確認 DevContainer 起動後、 必ず Python Interpreter が正しく設定されているか確認してください。 VSCode のコマンドパレットを開く( Ctrl+Shift+P / Cmd+Shift+P ) 「 Python: Select Interpreter 」を検索・選択 /usr/local/bin/python または /usr/bin/python3.11 が選択されていることを確認 よくある問題 : 意図しない Python 環境(例: /usr/local/python/current/bin/python )が選ばれている この場合、手動で /usr/local/bin/python を選択し直す Python バージョンの確認 : python --version # → Python 3.11.x と表示されること Python はバージョン依存が強い ため、この確認は必須です!Python 3.11 以外が選ばれていると、ライブラリの互換性問題が発生してハマります。 Functions プロジェクトの作成 DevContainer 内で Azure Functions プロジェクトを作成します。 プロジェクト初期化 DevContainer 内のターミナルで実行: func init MyPythonFunctionApp --python cd MyPythonFunctionApp 生成されるファイル : MyPythonFunctionApp/ ├── .funcignore # Functions デプロイ時の除外ファイル ├── .gitignore # Git 除外設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── requirements.txt # Python 依存関係 └── function_app.py # メイン関数ファイル(初期状態は空) Node.js 版との違い : package.json の代わりに requirements.txt src/ ディレクトリの代わりに function_app.py local.settings.json の設定 local.settings.json を編集して、Azurite 接続設定を追加: { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "python" } } 重要 : FUNCTIONS_WORKER_RUNTIME: "python" を設定 AzureWebJobsStorage は Node.js 版と同じ Python 仮想環境の作成 Python 版では、 仮想環境 を作成することを推奨します。 # 仮想環境の作成 python -m venv .venv # 仮想環境の有効化 # Linux/macOS source .venv/bin/activate # Windows (PowerShell) .venv\Scripts\Activate.ps1 仮想環境を使う理由 : プロジェクトごとに依存関係を分離できる 他のプロジェクトとのライブラリバージョン競合を回避できる コラム: 仮想環境内に仮想環境を作る ? DevContainer(コンテナ仮想環境)の中で .venv (Python 仮想環境)を作るのは、一見冗長に見えるかもしれません。しかし、この二重構造には実用的なメリットがあります: 1. グローバル環境の混入を防ぐ .venv を使わないと、開発者個人の Python 環境にインストールされているライブラリが紛れ込む可能性があります。例えば、開発者 A は pandas をグローバルにインストールしているが、開発者 B はインストールしていない場合、「自分の環境では動くのに…」という問題が発生します。 2. requirements.txt の汚染を防ぐ .venv を有効化し忘れてパッケージをインストールすると、全然関係ないライブラリまで requirements.txt に追記される…これ、 Python 開発あるあるですよね 。 # ❌ .venv を有効化し忘れてインストール pip install pandas pip freeze > requirements.txt # → グローバル環境の不要なライブラリまで記録される! # ✅ .venv を有効化してからインストール source .venv/bin/activate pip install pandas pip freeze > requirements.txt # → プロジェクト専用のライブラリのみ記録される 3. 開発環境の使いまわしが可能 同じ DevContainer を複数のプロジェクトで「ロンダリング」(使いまわす)しても、 .venv があることで各プロジェクトの依存関係が混ざりません。プロジェクト A 用の .venv とプロジェクト B 用の .venv を独立して管理できます。 個人的には、この二重仮想環境構成は好みですね。予期しない依存関係の混入を確実に防げます! 依存関係のインストール pip install -r requirements.txt 注意: requirements.txt の管理 pip install でライブラリを追加しても、 requirements.txt には自動的に追記されません 。ここ、Node.js の package.json と違って手動管理が必要なんですよね: # ライブラリをインストール pip install pandas # requirements.txt を更新 pip freeze > requirements.txt 発展的な選択肢: uv の活用 より効率的な依存関係管理には、 uv (高速 Python パッケージマネージャー)の採用も検討できます。詳細は以下の記事を参照してください: DevContainer と uv で構築する爆速 Python 開発環境| VS Code セットアップ手順 ただし、Azure Functions では uv のロジックに直接対応していないため、本番デプロイ時には uv → pip → requirements.txt への変換が必要です(コンテナだったら関係なかったかも…)。本記事では、標準的な pip + requirements.txt の構成を紹介します。 HTTP Trigger の作成と動作確認 HTTP Trigger 関数の作成 func new --name HttpExample --template "HTTP trigger" --authlevel "anonymous" 生成される内容 : function_app.py に HTTP Trigger 関数が追加されます 実装内容の確認 function_app.py : import azure.functions as func import datetime import json import logging app = func.FunctionApp() @app.route(route="HttpExample", auth_level=func.AuthLevel.ANONYMOUS) def HttpExample(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') name = req.params.get('name') if not name: try: req_body = req.get_json() except ValueError: pass else: name = req_body.get('name') if name: return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.") else: return func.HttpResponse( "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.", status_code=200 ) ポイント : @app.route() デコレータでエンドポイントを定義 req.params.get('name') でクエリパラメータを取得 req.get_json() でリクエストボディを JSON として取得 ローカル実行 func start 実行結果 : 動作確認 別のターミナルで curl コマンドを実行: curl "http://localhost:7071/api/HttpExample?name=Python" 期待される結果 : Hello, Python! ブラウザでの確認 : http://localhost:7071/api/HttpExample にアクセス Azurite と Timer Trigger Timer Trigger を使うには、 Azurite が必要です。詳細は Node.js 編 を参照してください。 Azurite の起動 DevContainer 内で、 別のターミナル を開いて Azurite を起動します: azurite --silent # 設定やログを保存する先を指定 azurite --location .azurite --debug .azurite/debug.log 期待される結果 : Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002 デフォルトポート : Blob Service: 10000 Queue Service: 10001 Table Service: 10002 Timer Trigger 関数の作成 func new --name TimerExample --template "Timer trigger" 生成される内容 : function_app.py に Timer Trigger 関数が追加されます 実装内容の確認 function_app.py に追加される内容: import azure.functions as func import logging import datetime app = func.FunctionApp() # (既存の HTTP Trigger はそのまま) @app.timer_trigger(schedule="0 */5 * * * *", arg_name="myTimer", run_on_startup=False, use_monitor=False) def TimerExample(myTimer: func.TimerRequest) -> None: if myTimer.past_due: logging.info('The timer is past due!') logging.info('Python timer trigger function executed.') ポイント : @app.timer_trigger() デコレータで CRON 式を定義 myTimer.past_due で遅延実行を検知 CRON 式は UTC 時刻 で動作(重要!) ローカル実行 Azurite が起動している状態で、Functions を起動: func start 実行結果 : 5 分ごとにログが表示されます。 トラブルシューティング(Python 固有) Python 版で発生しやすいエラーと対処法です。 1. ModuleNotFoundError: No module named ‘azure’ 症状 : func start で ModuleNotFoundError: No module named 'azure' エラー 対処法 : 仮想環境が有効になっているか確認 pip install -r requirements.txt を実行 仮想環境の有効化: # Linux/macOS source .venv/bin/activate # Windows .venv\Scripts\Activate.ps1 2. Failed to start a new language worker for runtime: python 症状 : Failed to start a new language worker for runtime: python 対処法 : Python バージョンを確認( python --version ) Azure Functions Core Tools が Python ワーカーをサポートしているか確認 仮想環境を再作成: rm -rf .venv python -m venv .venv source .venv/bin/activate pip install -r requirements.txt 3. Azurite 接続エラー 症状 : No connection could be made because the target machine actively refused it 対処法 : Node.js 編のトラブルシューティングを参照 Azurite が起動しているか確認 local.settings.json の設定を確認 4. VSCode で Python インタープリタが見つからない 症状 : VSCode が Python を認識しない 対処法 : コマンドパレット( Ctrl+Shift+P )→「Python: Select Interpreter」 .venv/bin/python を選択 開発の推奨フロー Python 版の開発フローです。 標準的な開発手順 ターミナル 1: Azurite 起動 azurite --silent ターミナル 2: 仮想環境の有効化と Functions 起動 cd MyPythonFunctionApp # 仮想環境の有効化 source .venv/bin/activate # Linux/macOS # または .venv\Scripts\Activate.ps1 # Windows # Functions 起動 func start 開発作業 function_app.py を編集 保存すると自動的にリロード 動作確認 HTTP Trigger: curl やブラウザでアクセス Timer Trigger: コンソールログで確認 Node.js 版との比較 項目 Node.js 版 Python 版 プロジェクト作成 func init --typescript func init --python 依存関係管理 npm / package.json pip / requirements.txt メインファイル src/functions/*.ts function_app.py 仮想環境 不要(オプション) 推奨(.venv) 実行コマンド npm start func start リロード watch mode(自動) ファイル変更検知(自動) 推奨 VSCode 拡張機能 DevContainer 内で自動的にインストールされる拡張機能: Azure Functions ( ms-azuretools.vscode-azurefunctions ) Python ( ms-python.python ) Pylance ( ms-python.vscode-pylance ) Black Formatter ( ms-python.black-formatter ) まとめと次回予告 本記事で学んだこと DevContainer を使った Python 版 Azure Functions 環境構築 Python 3.11 の DevContainer 構築 Dockerfile 方式を推奨 (Python はバージョン依存が強いため) Python Interpreter 設定の重要性 Python 仮想環境の作成と管理 Python での Azure Functions 開発 HTTP Trigger の作成と動作確認 Timer Trigger と Azurite の関係 function_app.py での関数定義 Python 固有のトラブルシューティング 仮想環境のエラー対処 Python Worker プロセスエラー Node.js 版と Python 版の使い分け シナリオ 推奨言語 理由 単純な REST API どちらでも OK 要件による データ分析・集計 Python pandas、NumPy が必須 機械学習推論 Python scikit-learn、TensorFlow 数値計算 Python SciPy、SymPy スクレイピング Python BeautifulSoup リアルタイム処理 Node.js async/await が得意 バッチ処理 どちらでも可 要件とチームのスキルセット次第 重要 : 単純な REST API であれば、Node.js と Python のどちらでも問題ありません。 特定のライブラリ(pandas、NumPy、scikit-learn など)が必要な場合のみ Python を選択 してください! 次回の記事予告 次回は、 Azure Functions 入門| HTTP Trigger と Timer Trigger の基礎と実践パターン を予定しています: HTTP Trigger と Timer Trigger の詳細な使い方(Node.js 版) 実践パターン : Timer Trigger を HTTP Trigger でデバッグする方法 タイムゾーン(UTC/JST)の扱い方 DRY 原則に基づいた共通ロジックの設計 サンプルリポジトリ 本記事で解説した環境を、すぐに試せるサンプルコードを公開しています: GitHub : azure-functions-python-devcontainer Python 3.11 HTTP Trigger + Timer Trigger 実装済み DevContainer 設定ファイル完備 仮想環境( .venv )セットアップ手順付き クローンして VSCode で開くだけで動作します 関連記事 Azure Functions×DevContainer 環境構築| Node.js 編 Node.js 22 + TypeScript での環境構築 Dockerfile 方式と PostCreateCommand 方式 Docker Desktop、VSCode のインストール手順 サンプルリポジトリ : azure-functions-nodejs-devcontainer Claude Code×DevContainer 環境構築ガイド – Node.js・Python 対応 DevContainer の基本的な使い方 Features 方式の詳細解説 DevContainer と uv で構築する爆速 Python 開発環境| VS Code セットアップ手順 uv による高速な依存関係管理 pip の代替としての uv の活用方法 ※ Azure Functions での利用には requirements.txt への変換が必要 DevContainer を使った Python での Azure Functions 開発、ぜひ試してみてください! データ分析や機械学習との組み合わせで、強力な自動化システムが構築できます。 次回もお楽しみに〜! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Functions×DevContainer環境構築|Python 3.11編 first appeared on SIOS Tech. Lab .
はじめに どうも、龍ちゃんです! 今月は Azure Functions を使ったサーバーレス開発に取り組んでいるのですが、ローカル開発環境の構築で結構ハマりポイントがあったので、その知見を共有したいと思います。 サンプルリポジトリ : 本記事で解説する環境をすぐに試せるサンプルコードを公開しています。 GitHub : azure-functions-nodejs-devcontainer クローンして DevContainer で開くだけで、すぐに動作確認できます! 特に、 DevContainer を使った環境構築 は、チーム開発で環境差異をなくすために非常に有効なアプローチです。前回の「 Claude Code×DevContainer 環境構築ガイド 」でも DevContainer の便利さを紹介しましたが、今回は Azure Functions に特化した DevContainer 環境構築 を解説します。 なぜ DevContainer で Azure Functions なのか? 私が DevContainer を推奨する理由はいくつかあります: 1. 再現性のある環境構築 チームメンバー全員が同じ環境で開発できます。「自分の環境では動くのに…」という問題、ありますよね。これが完全になくなります! 2. ホスト OS を汚さない Node.js のバージョン、npm のグローバルパッケージ、Azure Functions Core Tools など、すべてコンテナ内で完結 3. プロジェクトごとに異なるバージョンを使い分け プロジェクト A は Node.js 18、プロジェクト B は Node.js 22 といった使い分けが簡単 今回は、 Node.js 22 + TypeScript で Azure Functions の開発環境を構築します。次回の「Python 編」では Python 3.11 を使った環境構築も紹介しますので、お楽しみに! Azure Functions とは? Azure Functions は、 サーバーレスコンピューティング のサービスです。サーバーの管理をせずに、コードだけを書いて実行できます。 主要なトリガー Azure Functions では、さまざまな「トリガー」でコードを実行できます: トリガー 用途 例 HTTP Trigger REST API、Webhook API エンドポイント作成 Timer Trigger 定期実行 毎日深夜にバッチ処理 Blob Trigger ファイルアップロード 画像アップロード時に圧縮 Queue Trigger メッセージキュー 非同期タスク処理 今回は、 HTTP Trigger と Timer Trigger を使ってローカル開発環境を構築します。次回の記事では、この 2 つを組み合わせた 実践パターン (Timer Trigger を HTTP Trigger でデバッグする方法)を紹介する予定です。 なぜローカル開発環境が必要なのか Azure にデプロイしてからデバッグするのって、めちゃくちゃ時間かかりますよね。ローカル環境があれば: 即座にデバッグ – コードを変更したら即座に動作確認! ログ確認が簡単 – コンソールに直接ログが表示される コスト削減 – ローカルでのテストは Azure の課金対象外 前提条件 本記事では、以下がインストール済みであることを前提とします: 必須 Docker Desktop – DevContainer を使うために必須 インストール方法: Docker Desktop 公式サイト 動作確認: docker --version で Docker version 24.x.x 以上が表示されること Visual Studio Code – エディタ インストール方法: VSCode 公式サイト 本記事でインストールするもの Dev Containers 拡張機能 – VSCode でインストール Node.js 22、Azure Functions Core Tools、Azurite – DevContainer 内で自動インストール 必要なツール一覧 DevContainer を使った Azure Functions 開発に必要なツールは以下の通りです: ツール名 バージョン 役割 インストール先 Docker Desktop 最新 コンテナランタイム ホスト OS(前提) Visual Studio Code 最新 エディタ ホスト OS(前提) Dev Containers 拡張機能 最新 DevContainer サポート VSCode Node.js 22.x LTS JavaScript ランタイム DevContainer 内で自動 Azure Functions Core Tools v4.x ローカル実行・デバッグ DevContainer 内で自動 Azurite 最新 Azure Storage エミュレータ DevContainer 内で自動 ポイント : ホスト OS には Docker Desktop と VSCode が既にインストール済み Node.js、Core Tools、Azurite は DevContainer 内で自動的にインストール これにより、ホスト OS を汚さずに開発環境を構築可能 Dev Containers 拡張機能のインストール VSCode に Dev Containers 拡張機能をインストールします。 VSCode を起動 拡張機能パネルを開く( Ctrl+Shift+X / Cmd+Shift+X ) 「 Dev Containers 」で検索 「 Dev Containers 」(ID: ms-vscode-remote.remote-containers )をインストール 動作確認 : VSCode 左下に「 >< 」アイコンが表示されていることを確認 このアイコンをクリックすると、DevContainer 関連のコマンドが表示される Node.js DevContainer の構築 これから構築する環境の全体像を把握しましょう。 構築後のディレクトリ構成 azure-functions-nodejs-devcontainer/ # プロジェクトルート ├── .devcontainer/ # DevContainer 設定 │ ├── Dockerfile # コンテナイメージ定義 │ └── devcontainer.json # DevContainer 設定ファイル │ └── MyFunctionApp/ # Azure Functions プロジェクト ├── .funcignore # デプロイ除外ファイル ├── .gitignore # Git 除外設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── package.json # npm 依存関係 ├── tsconfig.json # TypeScript 設定 │ └── src/ # ソースコード └── functions/ # 関数ファイル ├── HttpExample.ts # HTTP Trigger 関数 └── TimerExample.ts # Timer Trigger 関数 ポイント : .devcontainer/ で開発環境を定義 MyFunctionApp/ が実際の Functions プロジェクト TypeScript ファイルは src/functions/ 配下に配置 構築方法 DevContainer を構築する方法は主に 2 つあります: Dockerfile 方式 – カスタム Dockerfile で詳細に制御(推奨) PostCreateCommand 方式 – 既存イメージにコマンドを追加 方法 1: Dockerfile 方式(推奨) この方式は、再現性が高く、ビルド時間も短いため推奨します。 ステップ 1: プロジェクトディレクトリの作成 mkdir azure-functions-nodejs-devcontainer cd azure-functions-nodejs-devcontainer mkdir .devcontainer ステップ 2: Dockerfile の作成 .devcontainer/Dockerfile を作成: # .devcontainer/Dockerfile FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm # 作業ディレクトリ WORKDIR /workspace # 実行ユーザー USER node # Azure Functions Core Tools と Azurite のインストール RUN npm install -g \ npm@11.5.2 \ azure-functions-core-tools@4 \ azurite # バージョン確認用コマンド(デバッグ用) RUN echo "=== Installed Versions ===" \ && node --version \ && npm --version \ && func --version \ && echo "=========================" # デフォルトコマンド(devContainer では sleep infinity で上書きされる) CMD ["sleep", "infinity"] ポイント : mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm は Microsoft 公式の Node.js 22 + TypeScript イメージ azure-functions-core-tools@4 で Azure Functions v4 ランタイムをインストール azurite は Timer Trigger のローカル実行に必須! ステップ 3: devcontainer.json の作成 .devcontainer/devcontainer.json を作成: { "name": "Azure Functions Node.js DevContainer", "build": { "dockerfile": "./Dockerfile" }, "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "remoteUser": "node", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ] } } } ポイント : forwardPorts : Azure Functions (7071) と Azurite (10000-10002) のポート転送を設定 extensions : Azure Functions 拡張機能、ESLint、Prettier を自動インストール ステップ 4: DevContainer の起動 VSCode で azure-functions-nodejs-devcontainer フォルダを開く 左下の「 >< 」アイコンをクリック 「 Reopen in Container 」を選択 Docker イメージのビルドと起動が開始されます(初回は 5-10 分程度) 成功すると : VSCode の左下に「 Dev Container: Azure Functions Node.js DevContainer 」と表示 ターミナルを開くと、コンテナ内のシェルが起動 方法 2: PostCreateCommand 方式 既存イメージを使い、起動後にコマンドでツールをインストールする方式です。 .devcontainer/devcontainer.json : { "name": "Azure Functions Node.js DevContainer", "image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm", "workspaceFolder": "/workspace", "remoteUser": "node", "postCreateCommand": "npm install -g npm@11.5.2 azure-functions-core-tools@4 azurite", "forwardPorts": [7071, 10000, 10001, 10002], "customizations": { "vscode": { "extensions": [ "ms-azuretools.vscode-azurefunctions", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ] } } } メリット : 設定ファイル 1 つで完結 シンプルでわかりやすい デメリット : DevContainer 起動のたびにインストールが実行される(起動が遅い) Dockerfile 方式の方が確実性が高く、再現性に優れる 私は Dockerfile 方式 を推奨します。 Functions プロジェクトの作成 DevContainer 内で Azure Functions プロジェクトを作成します。 プロジェクト初期化 DevContainer 内のターミナルで実行: func init MyFunctionApp --typescript cd MyFunctionApp 生成されるファイル : MyFunctionApp/ ├── .funcignore # Functions デプロイ時の除外ファイル ├── .gitignore # Git 除外設定 ├── host.json # Functions ランタイム設定 ├── local.settings.json # ローカル環境変数 ├── package.json # npm 依存関係 ├── tsconfig.json # TypeScript 設定 └── src/ # ソースコード格納ディレクトリ local.settings.json の設定 local.settings.json を編集して、Azurite 接続設定を追加: { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "node" } } 重要 : AzureWebJobsStorage: "UseDevelopmentStorage=true" は Azurite を使用するための設定 この設定がないと Timer Trigger でエラーになるので注意! 依存関係のインストール npm install HTTP Trigger の作成と動作確認 まずは、基本的な HTTP Trigger を作成して動作確認します。 HTTP Trigger 関数の作成 func new --name HttpExample --template "HTTP trigger" --authlevel "anonymous" 生成されるファイル : src/functions/HttpExample.ts 実装内容の確認 src/functions/HttpExample.ts : import { app, HttpRequest, HttpResponseInit, InvocationContext, } from "@azure/functions"; export async function HttpExample( request: HttpRequest, context: InvocationContext ): Promise<HttpResponseInit> { context.log("HTTP trigger function processed a request."); const name = request.query.get("name") || "World"; return { status: 200, body: `Hello, ${name}!`, }; } app.http("HttpExample", { methods: ["GET", "POST"], authLevel: "anonymous", handler: HttpExample, }); ポイント : request.query.get('name') でクエリパラメータを取得 app.http() でエンドポイントを登録 ローカル実行 npm start 実行結果 : 動作確認 別のターミナルで curl コマンドを実行: curl "http://localhost:7071/api/HttpExample?name=Azure" 期待される結果 : Hello, Azure! ブラウザでの確認 : http://localhost:7071/api/HttpExample にアクセスすると「Hello, World!」と表示されます Azurite と Timer Trigger Timer Trigger を使うには、 Azurite (Azure Storage エミュレータ)が必要です。 なぜ Azurite が必要なのか? Azure Functions の Timer Trigger は、内部的に Blob Storage を使って Timer の状態(次回実行時刻など)を保存します。ローカル開発では、この Blob Storage を Azurite でエミュレートするんですね。 HTTP Trigger のみの場合 : Azurite 不要 Timer Trigger を使う場合 : Azurite 必須! Azurite の起動 DevContainer 内で、 別のターミナル を開いて Azurite を起動します: azurite --silent # 設定やログを保存する先を指定 azurite --location .azurite --debug .azurite/debug.log 期待される結果 : Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002 デフォルトポート : Blob Service: 10000 Queue Service: 10001 Table Service: 10002 Timer Trigger 関数の作成 func new --name TimerExample --template "Timer trigger" 生成されるファイル : src/functions/TimerExample.ts 実装内容の確認 src/functions/TimerExample.ts : import { app, InvocationContext, Timer } from "@azure/functions"; export async function TimerExample( myTimer: Timer, context: InvocationContext ): Promise<void> { context.log("Timer trigger function executed at:", new Date().toISOString()); if (myTimer.isPastDue) { context.log("Timer is running late!"); } } app.timer("TimerExample", { schedule: "0 */5 * * * *", // 5分ごとに実行 handler: TimerExample, }); CRON 式の説明 : 0 */5 * * * * は 5 分ごとに実行 CRON 式は UTC 時刻 で動作(重要!) ローカル実行 Azurite が起動している状態で、Functions を起動: npm start 実行結果 : 5 分ごとにログが表示されます。 Azurite が起動していない場合のエラー Azurite が起動していないと、以下のエラーが発生します: [Error] Microsoft.Azure.WebJobs.Host: Error indexing method 'TimerExample'. → 対処法 : Azurite を起動してから Functions を再起動 トラブルシューティング よくあるエラーと対処法をまとめます。 1. Docker Desktop が起動しない 症状 : VSCode で「Docker daemon is not running」エラー 対処法 : Docker Desktop を起動する(Windows/Mac) Linux の場合: sudo systemctl start docker 2. DevContainer のビルドが失敗する 症状 : 「Failed to build image」エラー 対処法 : Dockerfile の構文エラーを確認 Docker Desktop のディスク容量を確認 VSCode のコマンドパレット( Ctrl+Shift+P )→「Dev Containers: Rebuild Container」を実行 3. func コマンドが見つからない 症状 : bash: func: command not found 対処法 : DevContainer が正しくビルドされているか確認 ターミナルを再起動 Dockerfile の npm install -g azure-functions-core-tools@4 が正しく実行されているか確認 4. Azurite 接続エラー 症状 : No connection could be made because the target machine actively refused it 対処法 : Azurite が起動しているか確認( curl http://127.0.0.1:10000 ) local.settings.json に AzureWebJobsStorage: "UseDevelopmentStorage=true" が設定されているか確認 Azurite を再起動 5. ポート競合エラー 症状 : Port 7071 is already in use 対処法 : 既存のプロセスを終了 別のポートで起動: func start --port 7072 6. TypeScript コンパイルエラー 症状 : npm start で TypeScript エラー 対処法 : npm install を実行して依存関係を再インストール tsconfig.json の設定を確認 npm run build で明示的にビルド 開発の推奨フロー DevContainer を使った Azure Functions 開発の推奨フローです。 標準的な開発手順 ターミナル 1: Azurite 起動 azurite --silent ターミナル 2: Functions ランタイム起動 cd MyFunctionApp npm start 開発作業 TypeScript ファイル( .ts )を編集 保存すると自動的にリロード(watch mode) 動作確認 HTTP Trigger: curl やブラウザでアクセス Timer Trigger: コンソールログで確認 VSCode のターミナル分割 VSCode のターミナルを分割すると便利です: ターミナル 1 : Azurite 起動( azurite --silent ) ターミナル 2 : Functions 起動( npm start ) ターミナル 3 : curl コマンドやその他の操作 分割方法 : ターミナルパネルの右上の「 + 」アイコン横の「 Split Terminal 」ボタン または Ctrl+Shift+5 / Cmd+Shift+5 推奨 VSCode 拡張機能 DevContainer 内で自動的にインストールされる拡張機能以外にも、以下があると便利です: Azure Functions ( ms-azuretools.vscode-azurefunctions ) – 既に設定済み ESLint ( dbaeumer.vscode-eslint ) – 既に設定済み Prettier ( esbenp.prettier-vscode ) – 既に設定済み Thunder Client ( rangav.vscode-thunder-client ) – HTTP クライアント(任意) まとめと次回予告 本記事で学んだこと DevContainer を使った Azure Functions 環境構築 Docker Desktop と VSCode のインストール Node.js 22 + TypeScript の DevContainer 構築 Dockerfile 方式と PostCreateCommand 方式の違い Azure Functions の基本 HTTP Trigger の作成と動作確認 Timer Trigger と Azurite の関係 ローカル開発環境での実行方法 トラブルシューティング よくあるエラーと対処法 Azurite 接続エラーの解決方法 次回の記事予告 次回は、以下の内容を予定しています: Azure Functions×DevContainer 環境構築| Python 編 Python 3.11 を使った DevContainer 構築 Node.js 版との違い Python 仮想環境との組み合わせ Azure Functions 入門| HTTP Trigger と Timer Trigger の基礎と実践パターン HTTP Trigger と Timer Trigger の詳細な使い方 実践パターン : Timer Trigger を HTTP Trigger でデバッグする方法 タイムゾーン(UTC/JST)の扱い方 DRY 原則に基づいた共通ロジックの設計 サンプルリポジトリ 本記事で解説した環境を、すぐに試せるサンプルコードを公開しています: GitHub : azure-functions-nodejs-devcontainer Node.js 22 + TypeScript HTTP Trigger + Timer Trigger 実装済み DevContainer 設定ファイル完備 クローンして VSCode で開くだけで動作します 関連記事 Claude Code×DevContainer 環境構築ガイド – Node.js・Python 対応 DevContainer の基本的な使い方 npm グローバルインストールの注意点 DevContainer を使った Azure Functions 開発、ぜひ試してみてください! チーム開発での環境差異がなくなり、開発効率が大幅に向上すること間違いなしです。 次回は Python 編 をお届けしますので、お楽しみに〜! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Functions×DevContainer環境構築|Node.js 22 + TypeScript first appeared on SIOS Tech. Lab .
はじめに ども!最近は Claude Code ともに開発を進めて、with AI での生活にどっぷりだったのですが 2025 年も締めということで貯まった検証を一気に記事化している龍ちゃんです。検証がたまっていたので、11 月と 12 月は大量にブログを書く羽目になりそうですね。ゴリゴリ執筆する必要がありますね! 皆さん、AI(Claude Code 等)と一緒に開発してると、こんな悩みありませんか? 「このプロジェクト、どういう構成になってるの?」と AI に毎回説明するのが面倒 ファイルが多すぎて AI が混乱して、的外れな提案をしてくる モノレポにしたけど、全部のアプリが毎回ビルドされて時間がかかる 僕も以前はこれらの課題に悩まされていました。特に、 プロジェクトが大きくなるほど、AI が全体像を把握しづらくなる という問題が深刻でした。 そこで構築したのが、 CLAUDE.md 階層構造 を核としたモノレポ環境です。この環境により、以下の成果を得られました: AI が自律的にプロジェクト構成を理解 (CLAUDE.md 階層構造) CI/CD ビルド時間 58%削減 (paths フィルタによる最適化) ドキュメントが自然に蓄積 (4 フェーズワークフロー: 計画 → 実装 → 研究記録 → 記事化) 本記事で紹介する環境は、実際に稼働中のシステムで実践している内容です。 このモノレポで開発しているシステムの詳細については、 AI チャットで話すだけ!X 予約投稿を完全自動化するシステム構築術 で解説しています(リポジトリは非公開)。 この記事では、 モノレポ構成と AI 協業開発を最適化する環境設計 について、実際のプロジェクト構成とワークフローを交えながら解説していきます。 記事の位置づけ 前提となる知識(先に読むべき記事) : 本記事で紹介する 4 フェーズワークフローは、以下の記事で解説した 3 フェーズ開発を基盤に構築されています: 3 フェーズ開発の基本を学ぶ : Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 計画 → 実装 → 検証の 3 フェーズワークフロー /docs/ と /application/ のディレクトリ分離 小規模システムを 30 分で実装する実例 計画フェーズの詳細を学ぶ : AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 CLAUDE.md による仕様書作成ルール AI との協働レビュープロセス 開発時間を 1 週間 →2 日に短縮した実践法 4 フェーズへの拡張を学ぶ : 検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 フェーズ 4(記事化)の追加による知見の資産化 RAG もどきシステムでトークン数 50-60%削減 文体補正システムで一貫した記事品質を実現 記事執筆時間 50%削減、重複チェック 83%削減 技術基盤 : 型安全な API 開発を学ぶ : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 Backend DTOs → OpenAPI → Frontend Types の自動生成 Single Source of Truth による型ズレゼロの実現 この記事で学べること この記事を読むことで、以下の知識とスキルが得られます: 主要なポイント モノレポ構成と AI 協業開発の相性 なぜモノレポが AI 協業に適しているのか、実体験をもとに解説 CLAUDE.md 階層構造によるコンテキスト管理 9 つの CLAUDE.md ファイルで AI に適切な情報を提供する設計 paths フィルタによる CI/CD 最適化 変更されたアプリのみビルドすることでビルド時間 58%削減 4 フェーズワークフローによる知見の資産化 計画 → 実装 → 研究記録 → 記事化のドキュメント駆動型開発 実践的なテクニック AI が理解しやすいディレクトリ構造と命名規則 GitHub Actions paths フィルタの活用 ドキュメント駆動型開発による知見の蓄積 前提条件 この記事は、 モノレポ構成と AI 協業開発環境の設計・アーキテクチャ に焦点を当てています。 前提知識(あると望ましい) AI 開発ツールの使用経験 Claude Code、GitHub Copilot、Cursor 等の AI 開発支援ツールを使った開発経験 AI にプロンプトを投げてコードを生成した経験 モノレポの基礎知識 複数のアプリケーションを 1 つのリポジトリで管理する概念の理解 (初めての方でも、記事を読み進めることで理解できます) 本記事で扱わないこと モノレポツール(Turborepo、Nx 等)の詳細比較 Azure 環境の詳細なセットアップ手順 各フレームワーク(NestJS、Next.js 等)の実装詳細 型安全パイプラインの実装詳細( 型安全パイプラインの記事 で解説) ※本記事は構成と設計に焦点を当てており、実装の詳細は関連記事で解説しています。 プロジェクト全体像 まずは、実際のプロジェクト構成を見ていきましょう。 モノレポ構成の 4 つのアプリケーション このプロジェクトは、 4 つの独立したアプリケーション をモノレポで管理しています。 アプリケーション 技術スタック デプロイ先 GitHub Actions Frontend Next.js 15, React 19, TypeScript 5 Azure Static Web Apps frontend-swa-deploy.yml Backend NestJS 11, Node.js 22, TypeScript 5 Azure Web Apps (Docker) backend-docker-build.yml X Scheduler Functions Azure Functions v4, Node.js 22, TypeScript Azure Functions deploy-x-scheduler-functions.yml Blog Search MCP Functions Azure Functions v4, Node.js 22, MCP Protocol Azure Functions deploy-blog-search-mcp-functions.yml ディレクトリ構造の全体像 プロジェクトルート/ ├── docs/ # 計画・設計フェーズ(実装コードなし) │ ├── CLAUDE.md # 計画フェーズガイドライン │ ├── project-core.md # インフラ全体構成 │ ├── features/ # 新機能開発計画 │ ├── bugs/ # バグ調査・修正計画 │ ├── research/ # 実装検証結果・知見 │ ├── article/ # ブログ記事執筆用調査 │ │ └── CLAUDE.md # 記事執筆ガイド │ ├── tools/ # Docs専用ツール │ │ └── CLAUDE.md # ツール使用ガイド │ └── api/ # OpenAPI仕様 ├── application/ # 実装フェーズ │ ├── backend/ # NestJS APIサーバー │ │ └── CLAUDE.md # バックエンド開発ガイド │ ├── frontend/ # Next.js フロントエンド │ │ └── CLAUDE.md # フロントエンド開発ガイド │ ├── functions/ # X Scheduler │ │ └── CLAUDE.md # Functions開発ガイド │ └── blog-search-mcp-functions/ # MCP Server │ └── CLAUDE.md # MCP Functions開発ガイド ├── infrastructure/ # IaCテンプレート │ └── CLAUDE.md # インフラ開発ガイド ├── CLAUDE.md # ルートガイドライン(全体像) └── .github/ └── workflows/ # CI/CDパイプライン ├── frontend-swa-deploy.yml ├── backend-docker-build.yml ├── deploy-x-scheduler-functions.yml └── deploy-blog-search-mcp-functions.yml システム構成図 モノレポ全体の構成を視覚化するとこうなります: なぜモノレポなのか? モノレポ構成を採用した理由は、 AI 協業開発との相性の良さ にあります。 モノレポのメリット 1. AI に 1 つのリポジトリで全体像を提供 別リポジトリにすると、AI は各リポジトリのコンテキストを個別に理解する必要があります。モノレポなら、 ルートの CLAUDE.md で全体像を一度に提供 できます。 # 別リポジトリの場合(AIが混乱) frontend-repo/ ← AIはこのリポジトリのコンテキストのみ backend-repo/ ← 別のセッションで別のコンテキスト functions-repo/ ← また別のコンテキスト # モノレポの場合(AIが全体を把握) monorepo/ ├── CLAUDE.md ← 全体像をAIに提供 ├── application/ │ ├── frontend/ │ ├── backend/ │ └── functions/ 2. ディレクトリ構造の一貫性 すべてのアプリケーションが同じルールに従うため、AI が理解しやすくなります。 # すべてのアプリケーションにそれぞれのCLAUDE.mdがある /application/backend/CLAUDE.md /application/frontend/CLAUDE.md /application/functions/CLAUDE.md /application/blog-search-mcp-functions/CLAUDE.md 3. コード共有が容易 共通ライブラリ、型定義、ユーティリティ関数を複数のアプリで共有できます。 AI 協業開発を支える設計思想 このモノレポ環境には、3 つの核となる設計思想があります。 1. 計画と実装の分離 /docs/ (設計・仕様)と /application/ (実装コード)を明確に分離しています。 目的 : AI に「計画フェーズ」と「実装フェーズ」を明確に区別させる 実装前に設計を固めることで、手戻りを減らす /docs/features/my-new-feature/ ├── README.md # 機能概要 ├── api-spec.md # API設計(実装コードなし) └── type-definition.md # 型定義(実装コードなし) /application/backend/src/my-new-feature/ ├── my-new-feature.controller.ts # 実装コード ├── my-new-feature.service.ts # 実装コード └── dto/ # 実装された型定義 2. CLAUDE.md 階層構造 ルートで全体像、サブディレクトリで詳細ルールを提供します。 目的 : AI が必要な粒度でコンテキストを取得できる 各領域の専門的なルールを明確にする /CLAUDE.md ← プロジェクト全体像 ↓ /docs/CLAUDE.md ← 計画フェーズのルール ↓ /application/backend/CLAUDE.md ← バックエンド実装の詳細ルール 3. Single Source of Truth(型安全パイプライン) Backend DTOs を唯一の真実とし、Frontend の型定義は自動生成します。 詳細 : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 を参照 Backend DTOs (@ApiProperty) ↓ OpenAPI 仕様生成 (generate:openapi) ↓ Frontend 型定義生成 (generate:api with Orval) ↓ 型安全なAPI呼び出し これら 3 つの設計思想により、 AI との協業開発が劇的にスムーズ になりました。 CLAUDE.md 階層構造の設計 ここからは、AI 協業開発の核となる CLAUDE.md 階層構造 について詳しく解説します。 CLAUDE.md とは? CLAUDE.md は、AI(Claude Code)にプロジェクトのコンテキストを提供するドキュメントです。 従来、AI に「このプロジェクトはどういう構成?」「どのルールに従えばいい?」と聞かれるたびに、手動で説明する必要がありました。CLAUDE.md を配置することで、 AI が自律的にガイドラインを読み、適切な判断をする ようになります。 9 つの CLAUDE.md ファイル このプロジェクトには、 合計 9 つの CLAUDE.md ファイル が配置されています。 # すべてのCLAUDE.mdファイルを確認 $ find . -maxdepth 3 -name "CLAUDE.md" | sort ./CLAUDE.md ./application/backend/CLAUDE.md ./application/blog-search-mcp-functions/CLAUDE.md ./application/frontend/CLAUDE.md ./application/functions/CLAUDE.md ./docs/article/CLAUDE.md ./docs/CLAUDE.md ./docs/tools/CLAUDE.md ./infrastructure/CLAUDE.md 各 CLAUDE.md の役割 : ファイルパス 役割 主な内容 /CLAUDE.md ルートガイドライン プロジェクト全体像、ディレクトリ構造、4 フェーズワークフロー、共通開発コマンド /docs/CLAUDE.md 計画フェーズルール 型定義、API 設計、データベース設計のルール(実装コード禁止) /docs/article/CLAUDE.md 記事執筆ガイド ブログ記事執筆の文体、構成、ドキュメント構造 /docs/tools/CLAUDE.md ツール使用ガイド Docs 専用ツール(ブログ HTML 抽出等)の使用方法 /application/backend/CLAUDE.md バックエンド開発ガイド NestJS 開発ルール、環境変数管理、テスト実行方法 /application/frontend/CLAUDE.md フロントエンド開発ガイド Next.js 開発ルール、インポートパス規則 /application/functions/CLAUDE.md X Scheduler 開発ガイド Azure Functions 開発ルール、Timer Trigger 設定 /application/blog-search-mcp-functions/CLAUDE.md MCP Functions 開発ガイド MCP Server 開発ルール、Supabase 連携 /infrastructure/CLAUDE.md インフラ開発ガイド Azure Bicep 開発ルール、デプロイ手順 コンテキスト継承パターン CLAUDE.md は、 階層的にコンテキストを継承 します。 継承の例 : ルート CLAUDE.md を読む → プロジェクト全体構成を理解 サブディレクトリの CLAUDE.md を読む → 各領域の詳細ルールを理解 AI が適切な判断を下す 実際の動き : AI: 「ユーザーがフロントエンドの開発を依頼してきた」 ↓ AI: 「まず /CLAUDE.md を読んで全体像を把握しよう」 ↓ AI: 「次に /application/frontend/CLAUDE.md を読んで詳細ルールを確認」 ↓ AI: 「インポートパスは @/* を使うべきだな」 AI: 「API型定義は自動生成されるから、手動で書いちゃダメだな」 ↓ AI: 適切なコードを提案 ルート CLAUDE.md の重要性 ルート CLAUDE.md ( /CLAUDE.md )は、最も重要なドキュメントです。 記載内容の例 # CLAUDE.md ## Project Architecture Overview LINE LIFF AI Prompt Battle - An AI-powered game platform... ### Directory Structure & Responsibilities / ├── docs/ # Planning & Design Phase │ ├── features/ # Feature specifications │ ├── bugs/ # Bug investigation & fix plans │ ├── research/ # Implementation validation │ └── article/ # Blog article research ├── application/ │ ├── backend/ # NestJS 11 API Server │ ├── frontend/ # Next.js 15 App Router │ ├── functions/ # Azure Functions │ └── blog-search-mcp-functions/ # MCP Server └── infrastructure/ # Azure Bicep IaC **Workflow Pattern (4-Phase Workflow)**: 1. **計画フェーズ** - Plan in `/docs/` 2. **実装フェーズ** - Implement in `/application/` 3. **研究記録フェーズ** - Document findings in `/docs/research/` 4. **記事化フェーズ** - Gather materials in `/docs/article/` ## Common Development Commands ### Backend (NestJS) npm run start:dev # Development with watch mode npm run generate:openapi # Generate OpenAPI spec ### Frontend (Next.js) npm run generate:api # Generate types from OpenAPI npm run dev:full # generate:api + dev server ## Critical Import Path Rules ### Frontend: Use `@/*` Path Aliases // ✅ CORRECT import { Button } from '@/components/ui/button'; // ❌ WRONG import { Button } from '../components/ui/button'; ポイント : プロジェクト全体構成 を一目で理解できる 4 フェーズワークフロー を明記 共通開発コマンド を記載 インポートパス規則 等の重要ルールを記載 ※注 : 上記コード例は、実際のプロジェクト CLAUDE.md での記載例です。本記事では「4 フェーズワークフロー」または「ドキュメント駆動型開発」と呼称しています。 このルート CLAUDE.md があることで、AI は 初めてプロジェクトに触れた時でも、全体像を即座に理解 できます。 CLAUDE.md 階層構造の効果 この階層構造により、以下の効果が得られました: AI の理解速度が向上 ルート CLAUDE.md で全体像を把握し、サブディレクトリで詳細を理解 一貫性のあるコード生成 すべての開発者(人間も AI も)が同じルールに従う 手動説明の削減 「どういうプロジェクト?」と聞かれることがなくなった オンボーディング時間の短縮 新しい AI セッション、新しい開発者が即座に理解できる ドキュメント駆動型開発の実践(3 フェーズ →4 フェーズへの進化) Claude Code での開発を効率化するため、 計画 → 実装 → 研究記録 → 記事化の 4 フェーズワークフロー を構築しました。 3 フェーズ開発から 4 フェーズ開発への進化 このワークフローは、 既存の 3 フェーズ開発を基盤に構築 されています: 元々の 3 フェーズ開発 ( 計画 → 実装 → 検証術 、 仕様書アレルギー克服 で解説): 計画 : /docs/ で仕様策定(実装コードは書かない) 実装 : /application/ で実装 検証 : 計画と実装の差異を分析 4 フェーズへの拡張 ( 知見を資産化する記事 で詳細解説): フェーズ 3 を「研究記録」として体系化 : 検証フェーズで得た知見を /docs/research/ に記録 フェーズ 4「記事化」を追加 : 研究記録を元に /docs/article/ で記事執筆 RAG もどきシステム : 既存記事参照でトークン数 50-60%削減 文体補正システム : 一貫した記事品質を実現 各フェーズの概要 フェーズ 1: 計画 ( /docs/features/ ) 目的 : 実装前の設計・仕様策定(実装コードは一切書かない) 成果物 : 型定義、API 設計、データベース構造、アーキテクチャ設計 効果 : 開発時間 1 週間 →2 日に短縮( 仕様書アレルギー克服の記事 で詳細解説) 重要ルール : CLAUDE.md で実装コード記述を禁止することで、AI が設計に集中 フェーズ 2: 実装 ( /application/ ) 目的 : 計画に基づいた実装 実装順序 : Backend → Frontend → Functions 効果 : 小規模システムで 30 分、中規模で 2 日程度( 3 フェーズ開発の記事 で実例紹介) フェーズ 3: 研究記録 ( /docs/research/ ) 目的 : 実装完了後の知見・アーキテクチャ検証結果をドキュメント化 記載内容 : 設計思想、アーキテクチャパターン、検証結果 重要性 : 計画と実装の差異を分析し、仕様漏れを特定 ※補足 : 3 フェーズ開発の記事 で「検証フェーズ」として解説している内容を、このプロジェクトでは「研究記録フェーズ」として体系化しています。実装完了後に計画と実装の差異を分析し、知見として記録します。 フェーズ 4: 記事化 ( /docs/article/ ) 目的 : 技術ブログ執筆に必要な情報収集・調査、 知見の資産化 成果物 : research-doc.md (調査資料)、 no1-article.md (記事本文) 参照元 : /docs/features/ + /docs/research/ + /application/ 効率化ツール ( 知見を資産化する記事 で詳細解説): RAG もどき (fetch-blog-html.ts): 既存記事参照でトークン数 50-60%削減 文体補正 (writing-style-prompt.md): 一貫した記事品質 記事執筆時間 50%削減 : 調査資料から記事執筆までの自動化 重複チェック 83%削減 : 既存記事との重複を自動検出 このワークフローの効果 知見が自然に蓄積 – 実装と同時にドキュメントが作成される 記事化がスムーズ – 計画 → 検証 → 実装コードを参照するだけ 手戻りが減少 – 計画フェーズで設計を固めることで、実装時の手戻りが減少 開発時間 1 週間 →2 日に短縮(計画フェーズの効果) AI との協業が効率化 – 各フェーズで AI に明確な役割を与えられる 知見が資産化される – フェーズ 4(記事化)により、開発知見が再利用可能なブログ記事として蓄積 既存記事との重複チェック 83%削減 技術ブログのライブラリが自然に形成される 型安全な API 開発パイプライン Frontend と Backend の型ズレをゼロにする 型安全パイプライン については、別記事で詳しく解説しています。 詳細を学ぶ : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 Single Source of Truth の原則 このプロジェクトでは、 Backend DTOs を唯一の真実 としています。 Frontend の型定義は、Backend から自動生成するため、 手動で型定義を同期する作業が不要 になります。 型安全パイプラインの効果 型ズレゼロ – Backend と Frontend の型が常に一致 手動同期作業の撲滅 – 型定義を手動でコピー&ペーストする必要がない リファクタリング時の安全性 – Backend の型を変更すると、Frontend でコンパイルエラーが出る 開発速度向上 – 型定義を書く時間が不要になり、実装に集中できる モノレポ CI/CD パイプライン: paths フィルタの威力 次に、4 つのアプリケーションを並行デプロイする 最適化された CI/CD パイプライン について解説します。 4 つの独立したワークフロー このプロジェクトでは、 4 つの独立した GitHub Actions ワークフロー を使用しています。 ワークフロー トリガーパス デプロイ先 ビルド時間(平均) frontend-swa-deploy.yml application/frontend/** Azure Static Web Apps 3 分 backend-docker-build.yml application/backend/** GitHub Container Registry → Azure Web Apps 5 分 deploy-x-scheduler-functions.yml application/functions/** Azure Functions (X Scheduler) 2 分 deploy-blog-search-mcp-functions.yml application/blog-search-mcp-functions/** Azure Functions (MCP Server) 2 分 paths フィルタによる最適化 課題 : モノレポ全体をビルドすると、変更のないアプリもビルドされ時間がかかる 解決策 : GitHub Actions の paths フィルタで、変更されたディレクトリのみをトリガー 例: Frontend SWA Deploy # .github/workflows/frontend-swa-deploy.yml name: Frontend SWA Deploy on: push: branches: - main paths: - "application/frontend/**" - ".github/workflows/frontend-swa-deploy.yml" workflow_dispatch: ポイント : paths フィルタで application/frontend/** のみをトリガー Backend を変更しても、Frontend のワークフローは実行されない Before/After 比較 Before(paths フィルタなし) Backend を変更 ↓ 全ワークフロー実行(Frontend, Backend, Functions, MCP Functions) ↓ 合計ビルド時間: 12分(3 + 5 + 2 + 2) After(paths フィルタあり) Backend を変更 ↓ Backend ワークフローのみ実行 ↓ 合計ビルド時間: 5分(58%削減) 並行デプロイの実現 4 つのワークフローが 独立している ため、複数のアプリを同時に変更した場合、 並行デプロイ が実現されます。 Frontend と Backend を同時に変更 ↓ frontend-swa-deploy.yml と backend-docker-build.yml が並行実行 ↓ 合計ビルド時間: 5分(逐次実行なら8分) CI/CD パイプラインの効果 ビルド時間 58%削減 – paths フィルタにより、変更のないアプリはビルドされない 並行デプロイ – 複数のアプリを同時に変更しても、並行実行で時間短縮 安全なデプロイ – 各アプリが独立しているため、Frontend のデプロイ失敗が Backend に影響しない 開発体験の変化(Before/After) モノレポ ×AI 協業開発環境を導入する前後で、開発体験がどう変わったかを比較します。 Before(モノレポ ×AI 環境導入前) 問題点 AI に毎回プロジェクト構成を説明 開発者: 「ユーザー管理機能を実装して」 AI: 「このプロジェクトはどういう構成ですか?」 開発者: 「Backendは...Frontendは...」(毎回説明) CI/CD ビルド時間の増加 Backend を変更 ↓ 全ワークフロー実行(Frontend, Backend, Functions) ↓ 合計ビルド時間: 12分 ドキュメントが散らばって、どこに何があるかわからない 計画ドキュメント: Notion 実装コメント: コード内 ブログ記事: 別リポジトリ ↓ 情報が散らばって、どこに何があるかわからない After(モノレポ ×AI 環境導入後) 改善点 AI が自律的にプロジェクト構成を理解 開発者: 「ユーザー管理機能を実装して」 AI: 「/CLAUDE.md を確認します」 AI: 「/application/backend/CLAUDE.md を確認します」 AI: 「型安全パイプラインに従って実装します」 CI/CD ビルド時間 50%削減 Backend を変更 ↓ Backend ワークフローのみ実行(pathsフィルタ) ↓ 合計ビルド時間: 5分(50%削減) ドキュメントが自然に蓄積し、資産化される 4フェーズワークフロー 計画 (/docs/features/) ↓ 実装 (/application/) ↓ 研究記録 (/docs/research/) ↓ 記事化 (/docs/article/) ├─ RAGもどき(fetch-blog-html.ts)でトークン数50-60%削減 └─ 文体補正(writing-style-prompt.md)で一貫した記事品質 ↓ 情報が整理され、知見が資産化される 技術ブログのライブラリが自然に形成される 定量的な効果 指標 Before After 改善率 初期説明時間 10 分/セッション ほぼ不要(CLAUDE.md で自律理解) 大幅削減 CI/CD ビルド時間 12 分 5 分 58%削減 ドキュメントメンテナンス時間 週 5 時間 週 2 時間 60%削減 新機能開発スピード 2 週間 1 週間 50%短縮 記事執筆時間 6-8 時間 3-4 時間 50%削減 ※測定条件 : 小規模〜中規模機能開発(バックエンド API エンドポイント 2-4 個、フロントエンド画面 1-2 個規模)での実測値です。プロジェクトの規模や複雑さによって効果は変動します。記事執筆時間は、調査資料作成から記事本文執筆までの合計時間です。 龍ちゃんの所感 導入前の課題 : プロジェクトが小さいうちは問題なかったんですが、いろんな検証を詰め込んでプロジェクトが大きくなってくると、 ドキュメントを編集するだけでビルドが走る という問題が出てきました。適切にビルドを分割することで、不要な CI/CD の実行を抑える必要が出てきたんです。 また、フロントエンド・バックエンド・Functions(バッチ処理)という複数の構成でアプリを作っていたので、 API 連携やデータベース連携がスムーズに行える環境 が必要でした。例えば、バックエンドでデータベースに情報を入れて、それをバッチ処理で取得して実行するような連携ですね。 AI 協業開発での気づき : AI と開発する上で、フロントエンドとバックエンドの型ズレを解消するためにも、 アプリケーション全体を AI に見える形で一つに集約する ことがやはり大事だなと実感しました。これは自分の中でもすごく効果的でしたね。 検証とブログ執筆の課題 : 自分の検証スタイルとして、「インプットを入れたらアウトプットを出す」というのが弊社の理念でもあり、このブログの意義でもあるので、検証とブログ執筆はセットで考えていました。 ただ、検証が終わった後に、別でブログを書くためのシステムを立ち上げて…というのが割と面倒になってきたんです。もう 3 年で 200 記事くらい書いているんですが、だんだん検証する内容も大きくなってきて、記事も長くなってきました。 ソースコード参照の課題と解決策 : 記事を書く際に ソースコードを参照することが増えて きたんですが、参照するファイルが増えれば増えるほど、ブログを書く障壁がどんどん上がってきました。 そこで、 リポジトリから直接情報を吸い出してブログを書くシステム (4 フェーズワークフロー、RAG もどき、文体補正)を構築したことで、ブログ執筆の効率化がさらに進んだと感じています(詳細は 検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 を参照)。 まとめ この記事では、 モノレポ ×AI 協業開発環境 の構築術について解説しました。 モノレポ ×AI 協業開発環境のポイント CLAUDE.md 階層構造でコンテキスト管理 9 つの CLAUDE.md ファイルで、AI に適切な粒度の情報を提供 4 フェーズワークフロー(計画 → 実装 → 研究記録 → 記事化) ドキュメント駆動型開発で、知見が自然に蓄積され、資産化される 3 フェーズ開発を基盤に、フェーズ 4(記事化)を追加 RAG もどき、文体補正による記事執筆効率化 型安全パイプライン(Backend DTOs → OpenAPI → Frontend Types) Single Source of Truth で、型ズレゼロを実現( 型安全パイプラインの詳細記事 ) paths フィルタによる最適化 CI/CD 4 つの独立ワークフローで、ビルド時間 58%削減 開発体験の変化 Before After AI に毎回プロジェクト構成を説明 CLAUDE.md で自律的に理解 CI/CD ビルド時間 12 分 paths フィルタで 5 分(58%削減) ドキュメントが散らばる 4 フェーズワークフローで自然に蓄積、資産化 記事執筆に時間がかかる RAG もどき、文体補正で 50%削減 次のステップ この記事を読んで、モノレポ ×AI 協業開発環境に興味を持った方は、以下のステップで実践してみてください: 1. CLAUDE.md を作成 まずは、プロジェクトルートに CLAUDE.md を作成し、プロジェクト全体像を記載します。 # CLAUDE.md ## Project Architecture Overview (プロジェクトの概要) ### Directory Structure (ディレクトリ構造) ## Workflow Pattern (4-Phase Workflow) 1. **計画フェーズ** - Plan in `/docs/` 2. **実装フェーズ** - Implement in `/application/` 3. **研究記録フェーズ** - Document findings in `/docs/research/` 4. **記事化フェーズ** - Gather materials in `/docs/article/` 2. paths フィルタを導入 GitHub Actions ワークフローに paths フィルタを追加します。 on: push: branches: - main paths: - "application/frontend/**" 3. 4 フェーズワークフローを実践 新機能開発時は、計画 → 実装 → 研究記録 → 記事化の 4 フェーズを順に進めます。 詳細は 3 フェーズ開発の基本を学ぶ記事 を参照してください。 参考リンク 関連記事(本サイト) AI 協業開発手法シリーズ Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 型安全パイプライン AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 公式ドキュメント Claude Code 公式ドキュメント NestJS 公式ドキュメント Next.js 公式ドキュメント GitHub Actions 公式ドキュメント Orval 公式ドキュメント ここまで読んでいただき、ありがとうございました! モノレポ ×AI 協業開発環境を構築することで、開発体験が劇的に向上します。ぜひ、この記事を参考に、あなたのプロジェクトでも実践してみてください。 質問や感想は、コメント欄でお待ちしております。また、Twitter のほうもよろしくお願いします! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AI 協業開発環境の構築術|モノレポでビルド時間を大幅短縮する CLAUDE.md 活用法 first appeared on SIOS Tech. Lab .
はじめに ども!先輩社員から Azure Static Web Apps のビルドの相談を受けて、そういえばそのブログを書いたことないなって「はっ!」となった龍ちゃんです。 今まで、過去にセットアップしたものを使いまわしていたんですけど、身近にありすぎて忘れていました。そんなわけでまとめていきます。Azure Static Web Apps はよく使用します。今回の記事をふんだんに使ったブログは「 Azure Static Web Apps: x-ms-client-principal で安全なロールベース制御 」です。 それでは本題に入ります。Azure Static Web Apps(以下、SWA)を使っていて、「デプロイに時間がかかるな…」と感じたことはありませんか? Azure Portal から SWA を作成すると、GitHub Actions のワークフローファイルが自動生成されます。このデフォルト設定では、Microsoft の Oryx ビルドシステム が使用され、プロジェクトを自動的に検出してビルドしてくれます。 しかし、このデフォルト設定には以下のような課題があります: 毎回依存関係のフルインストールが発生 (キャッシュなし) テストや Linter の実行ができない ビルドプロセスのカスタマイズが困難 本記事では、GitHub Actions 上でカスタムビルドを実装し、デプロイ時間を短縮する方法を解説します。 Oryx ビルドシステムとは Oryx の概要 Oryx(オリックス)は、Microsoft が開発したオープンソースのビルドシステムで、ソースコードを自動的に実行可能なアーティファクトにコンパイルします。 公式リポジトリ : https://github.com/microsoft/Oryx Azure Static Web Apps、Azure App Service、Azure Functions などで利用されています。 自動検出の仕組み Oryx は以下のように動作します: リポジトリの内容を分析 使用されているプログラミング言語・フレームワークを検出 適切なビルドコマンドを自動実行 具体例(Node.js の場合) 検出内容 → 実行されるコマンド ---------------------------------------------------- package.json が存在 → npm install → npm run build または npm run build:azure デフォルトワークフローの動作 Azure Portal から SWA を作成すると、以下のようなワークフローファイルが自動生成されます: name: Azure Static Web Apps CI/CD on: push: branches: - main pull_request: types: [opened, synchronize, reopened, closed] branches: - main jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true lfs: false - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} action: "upload" app_location: "/" # アプリのソースコードパス api_location: "" # APIのソースコードパス(オプション) output_location: "dist" # ビルド済みアプリのディレクトリ(オプション) このワークフローの特徴 : シンプルで設定が簡単 Oryx が自動的にビルド設定を検出 毎回依存関係のインストールが発生 (キャッシュなし) ビルド時間が長い テストの実行ができない ビルドプロセスのカスタマイズが困難 カスタムビルドで得られるメリット GitHub Actions 上でビルドを実行することで、以下のようなメリットが得られます。 1. 依存関係のキャッシュ actions/setup-node@v6 の組み込みキャッシュ機能を使用することで、依存関係のダウンロード時間を大幅に短縮できます。 - uses: actions/setup-node@v6 with: node-version: "20" cache: "npm" # npm, yarn, pnpm をサポート cache-dependency-path: "package-lock.json" 2025 年 11 月時点の推奨バージョン : actions/setup-node@v6 が最新版です。v6 では Node.js 24 ランタイムへのアップグレードやセキュリティ強化が行われています。v6 でも v4 と同様に cache: "npm" でキャッシングが有効化されます。基本的な使い方は互換性があるため、v4 からの移行は容易です。 キャッシュの仕組み package-lock.json のハッシュ値をキーとして使用 ファイルが変更されない限り、同じキャッシュが再利用される ~/.npm ディレクトリがキャッシュされる 2. テスト・Linter の統合 ビルドプロセスに、テストや静的解析(Linter)を組み込むことができます。 - name: Run tests run: npm test - name: Run linter run: npm run lint これにより、品質の低いコードが本番環境にデプロイされるのを防げます。 3. ビルドプロセスの完全制御 複雑なビルド要件(環境変数の設定、複数ステップのビルド、モノレポ対応など)に柔軟に対応できます。 4. デプロイの高速化 依存関係のキャッシュにより、2 回目以降のデプロイが大幅に高速化されます。 実装手順 Step 1: ワークフローファイルの作成 .github/workflows/frontend-deploy.yml を作成します。 name: Frontend SWA Deploy on: push: branches: - main workflow_dispatch: jobs: build-and-deploy: runs-on: ubuntu-22.04 environment: production steps: # 1. リポジトリをチェックアウト - name: Checkout repository uses: actions/checkout@v4 # 2. Node.js のセットアップ(キャッシュあり) - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "20" cache: "npm" cache-dependency-path: "application/frontend/package-lock.json" # 3. 依存関係のインストール - name: Install dependencies run: | cd application/frontend npm ci # 4. テストとLinterの実行(オプション) - name: Run tests run: | cd application/frontend npm test - name: Run linter run: | cd application/frontend npm run lint # 5. ビルド実行 - name: Build frontend run: | cd application/frontend npm run build # 6. Azure Static Web Apps へデプロイ - name: Deploy to Azure Static Web Apps uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" app_location: "./application/frontend/out" output_location: "" skip_app_build: true # 重要: Oryx ビルドをスキップ Step 2: デプロイトークンの取得 Azure Portal にアクセス 対象の Static Web Apps リソースを開く 左メニューから「 管理 」を選択 「 デプロイトークン 」をコピー Step 3: GitHub Secrets の設定 GitHub リポジトリの Settings → Secrets and variables → Actions New repository secret をクリック 以下を設定: Name: AZURE_SWA_DEPLOY_TOKEN Secret: 手順 2 でコピーしたトークンを貼り付け Add secret をクリック セキュリティのベストプラクティス デプロイトークンを安全に管理するための推奨事項: 1. 環境レベルのシークレット管理 リポジトリレベルではなく、GitHub 環境(staging、production)ごとにトークンを管理することを推奨します。 Settings → Environments → New environment で環境を作成 環境ごとに異なるトークンを設定 これにより、環境ごとのアクセス制御が可能になり、誤ったデプロイを防げます。 2. 定期的なローテーション トークンは定期的に再生成することを推奨します。 Azure Portal → Static Web Apps → 管理 → デプロイトークンの再生成 GitHub Secrets を更新 3. 漏洩時の対応 トークンが漏洩した場合は、即座に再生成してください。 Azure Portal でトークンを再生成 GitHub Secrets を更新すれば、次回デプロイから新しいトークンが使用されます 重要な設定ポイント skip_app_build: true この設定により、Oryx によるビルドをスキップし、既にビルド済みのファイルをそのままデプロイします。 skip_app_build: true app_location の指定 skip_app_build: true を使用する場合、 ビルド済みファイルのディレクトリ を指定します。 app_location: "./application/frontend/out" # Next.js の場合 output_location は空文字列 skip_app_build: true と併用する場合は空文字列に設定します。 output_location: "" npm ci の使用 npm install ではなく、 npm ci を使用することを強く推奨します。 # ❌ 避けるべき - run: npm install # ✅ 推奨 - run: npm ci 理由 : npm ci は package-lock.json を厳密に再現 より高速で、CI/CD 環境に最適化されている node_modules を削除してからインストールするため、クリーンな環境が保証される モノレポ構成での最適化 モノレポの場合、変更があったディレクトリのみビルドするように paths フィルタを設定できます。 on: push: branches: - main paths: - "application/frontend/**" - ".github/workflows/frontend-deploy.yml" メリット : フロントエンドの変更時のみワークフローが実行される 無駄なビルドを削減 GitHub Actions の実行時間を節約 補足: SWA CLI を使用したデプロイ方法 公式の Azure/static-web-apps-deploy@v1 アクションを使わず、 SWA CLI を直接使用する方法 もあります。 実装例 - name: Install SWA CLI run: npm install -g @azure/static-web-apps-cli - name: Deploy with SWA CLI run: | swa deploy ./application/frontend/out \ --deployment-token ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }} \ --env production メリット・デメリット メリット : GitHub Actions のキャッシュ機能を利用できる ローカル開発でも同じ CLI を使用できる(開発体験の統一) デプロイプロセスの細かい制御が可能 デメリット : SWA CLI への深い理解が必要(学習コスト) 公式アクションに比べて設定が複雑 CLI のバージョン管理が必要 推奨の使い分け 公式アクション( Azure/static-web-apps-deploy@v1 )を推奨 カスタマイズ箇所が少なく、保守性が高い 本記事で紹介した skip_app_build: true パターンで十分高速 SWA CLI が向いているケース ローカルで既に SWA CLI を使って開発している デプロイプロセスに特殊な要件がある CLI の細かい制御機能が必要 結論 : ほとんどのケースでは、公式アクションをアップローダーとして利用する方法で十分です。 認証方法の選択:デプロイトークン vs OIDC デプロイトークン方式(推奨) 特徴 シンプルで設定が簡単 Azure Static Web Apps の標準的な認証方法 安定性が高い トークンの管理が必要 使い方 - uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }} OIDC Federated Credentials(2025年10月時点) 現状 Azure Static Web Apps は、デプロイ認証において OIDC(OpenID Connect)をネイティブサポートしていません(2025 年 1 月時点)。 補足 : ユーザー認証用の OIDC(Auth0、Azure AD B2C など)は Standard プランで利用可能です。ここで述べているのは、GitHub Actions からのデプロイ時の認証に関する制限です。 他の Azure サービス(App Service、Container Instances など)では、デプロイ認証における OIDC がサポートされていますが、Static Web Apps では未対応です。 参考 : GitHub Issue #1304 – Add Federated Credentials support 回避策の存在とリスク OIDC トークンを使って Azure CLI で認証し、デプロイトークンを動的に取得する方法は存在しますが: トークンのマスキング設定ミスによるリークリスク 設定が複雑 現時点では推奨されない 推奨事項 2025 年 1 月時点では、デプロイトークン方式が最も安全で信頼性の高い方法です。 OIDC 対応については、コミュニティから強く要望されており、将来的にサポートされる可能性は高いですが、現時点ではまだ実装されていません。 トラブルシューティング 問題 1: キャッシュが効かない 症状 2 回目以降のビルドでも npm ci に時間がかかる 原因 cache-dependency-path が指定されていない、または間違っている 解決策 # ❌ 間違った設定 - uses: actions/setup-node@v4 with: cache: "npm" # cache-dependency-path が指定されていない # ✅ 正しい設定 - uses: actions/setup-node@v4 with: cache: "npm" cache-dependency-path: "package-lock.json" モノレポの場合は、正しいパスを指定します: cache-dependency-path: "application/frontend/package-lock.json" 問題 2: デプロイが失敗する 症状 Error: No such file or directory 原因 skip_app_build: true を使用しているのに、 app_location が正しくない 解決策 # ❌ 間違った設定 app_location: "/" # ソースコードのパス skip_app_build: true # ✅ 正しい設定 app_location: "./application/frontend/out" # ビルド済みディレクトリ skip_app_build: true output_location: "" 問題 3: ビルドコマンドが見つからない 症状 npm run build: command not found 原因 package.json に build スクリプトが定義されていない 解決策 package.json に build スクリプトを追加します: { "scripts": { "build": "next build" } } または、ワークフローで直接コマンドを指定します: - name: Build frontend run: next build 問題 4: Actions のキャッシュサイズ超過 症状 Warning: Cache size exceeded limit 原因 node_modules が大きすぎる 解決策 setup-node のキャッシュは ~/.npm をキャッシュするため、通常は問題ありません。 もし問題が発生する場合は、不要な devDependencies を削除するか、キャッシュを無効化します。キャッシュを無効化すると、カスタムビルドを組む意味が大幅に減ってしまいます。おそらくですが、この事象が頻発する場合はOryxのビルドも失敗するんちゃうかな?って思っとります。 問題 5: ビルドタイムアウト 症状 Error: Build timed out after 15 minutes 原因 Azure Static Web Apps のビルドには 15 分の制限があります 解決策 カスタムビルド(本記事の方法)に移行することで、この制限を回避できます。GitHub Actions 側でビルドを行うため、Azure SWA 側のタイムアウトは影響しません。 まとめ 今回は、Azure Static Web Apps のデプロイを高速化するための、GitHub Actions カスタムビルドの実装方法をご紹介しました。 カスタムビルドを導入すべきケース 小規模プロジェクト・個人開発 デフォルトの Oryx ビルドで十分 シンプルさを優先 中規模以上のプロジェクト・チーム開発 カスタムビルドを推奨 キャッシュによる高速化の恩恵が大きい テスト・Linter の統合で品質向上 エンタープライズプロジェクト カスタムビルド必須 並列ジョブでテスト・ビルドを分離 モノレポ構成で paths フィルタ活用 デプロイ環境の分離(本番・ステージング) カスタムビルドのメリット再確認 項目 Oryx ビルド カスタムビルド 依存関係キャッシュ なし あり テスト実行 不可 可能 Linter 実行 不可 可能 ビルド時間 遅い 速い(2 回目以降) カスタマイズ性 低い 高い セットアップ難易度 簡単 やや複雑 参考リンク Azure Static Web Apps – Build Configuration(公式ドキュメント) GitHub Actions – setup-node Microsoft Oryx Repository Azure Static Web Apps: build app externally – johnnyreilly この記事が、Azure Static Web Apps のデプロイを高速化する一助となれば幸いです。 カスタムビルドを導入することで、デプロイ時間の短縮だけでなく、テストや Linter の統合による品質向上など、開発体験が大きく向上します。 ぜひ、皆さんのプロジェクトでもお試しください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Static Web Apps のデプロイを高速化!GitHub Actions カスタムビルドのすすめ first appeared on SIOS Tech. Lab .
はじめに ども!最近は Claude Code とべったりで、実装 → 検証 → 記事化のサイクルを回している龍ちゃんです。 皆さん、せっかく実装して検証した知見、 ちゃんと記事化できてますか? 僕も以前はこんな悩みを抱えていました: 実装完了後に「記事書こう」と思っても、何を書けばいいか分からない 実装時のメモが散らばって、記事執筆時に情報を集めるのが大変 既存記事との整合性を取るのが面倒で、似たような内容を書いてしまう 特に困ったのが、 実装から時間が経つと、なぜその設計にしたのか忘れてしまう こと。「あれ、これってどういう意図だったっけ?」とコードを読み返す羽目になり、記事執筆が進まない…。 そこで構築したのが、 検証 → 記事化をワンセットにしたワークフロー です。この仕組みにより、以下の成果を得られました: 知見が自然に蓄積 (実装と同時にドキュメント化) 記事執筆時間 50%削減 (調査資料が既に揃っている) 既存記事との整合性確保 (RAG もどきで既存記事を参照) この記事では、 実装完了後の知見をブログ記事化する仕組み を、実際のプロジェクト例とツールを交えながら解説していきます。 記事の位置づけ この記事は、既存の 3 フェーズ開発フロー(計画 → 実装 → 検証) を拡張し、 検証後の知見を記事化するフェーズ に焦点を当てた内容です。 この記事で学べること この記事を読むことで、以下の知識とスキルが得られます: 主要なポイント フェーズ 3: 研究記録の実践 実装完了後の知見をどう記録するか( /docs/research/ ) フェーズ 4: 記事化の実践 調査資料から記事本文への変換方法( /docs/article/ ) RAG もどき: ブログ HTML 抽出ツール 既存記事をローカルに保存し、記事執筆時に参照する仕組み 文体補正システム writing-style-prompt.md による一貫した記事品質 実践的なテクニック research-doc.md(調査資料)の書き方 no1-article.md(記事本文)への変換パターン fetch-blog-html.ts による既存記事参照 トークン数削減テクニック(50-60%削減) 前提条件 必要な知識 AI 開発ツールの使用経験 Claude Code、GitHub Copilot、Cursor 等の AI 開発支援ツールを使った開発経験 AI にプロンプトを投げてコードを生成した経験 あると理解が深まる知識 3 フェーズ開発フローの概念 計画 → 実装 → 検証の基本的な流れへの理解 以下の記事を参照: Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 本記事で扱わないこと 3 フェーズ開発フロー全体の詳細(上記参照記事を参照) Markdown 記法の基礎 ブログプラットフォーム(Hashnode 等)の使い方 本記事は、 フェーズ 3→4(検証 → 記事化)のワークフロー に焦点を当てています。 4 フェーズワークフローの復習 まずは、4 フェーズワークフローの全体像を確認しましょう。 4 フェーズの概要 フェーズ 1-2: 計画と実装(概要) フェーズ 1: 計画 ( /docs/features/ ) 目的: 実装前の設計・仕様策定 成果物: 型定義、API 設計、データベース構造 フェーズ 2: 実装 ( /application/ ) 目的: 計画に基づいた実装 成果物: 動作するコード 詳細は以下の記事を参照してください: Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 本記事の焦点: フェーズ 3-4 本記事では、 実装完了後の知見を記事化するフェーズ 3-4 に焦点を当てます。 フェーズ 3: 研究記録 ( /docs/research/ ) 実装完了後の知見・検証結果をドキュメント化 設計思想、アーキテクチャパターン、検証結果を記録 フェーズ 4: 記事化 ( /docs/article/ ) 技術ブログ執筆に必要な情報収集 research-doc.md(調査資料)→ no1-article.md(記事本文)の変換 フェーズ 3: 研究記録の実践 研究記録フェーズの目的 実装完了後、 「なぜその設計にしたのか」「どんな課題があって、どう解決したのか」 を記録するフェーズです。 目的 : 設計思想と意思決定の記録 アーキテクチャパターンの検証結果 実装完了後の振り返り 記録先 : /docs/research/ ディレクトリ構造 研究記録は、単一ファイル形式で管理します(小〜中規模機能に対応)。 docs/research/ ├── aoai-chat-simple.md # Azure OpenAI チャット機能 ├── github-api-integration.md # GitHub API 統合 └── csv-preview-system.md # CSV プレビュー機能 特徴 : 1 ファイルで機能検証が完結し、効率的に参照・更新できます。 研究記録に記載する内容 記載すべき内容 設計思想と意思決定(なぜその設計を選択したか) 主要エンドポイントの概要(ファイルパス参照) 検証結果(パフォーマンス、エッジケース) 記載しない内容 ソースコードの全文転記 詳細な実装手順 機密情報(API キー、認証情報) 実際の研究記録例 このプロジェクトには、20 件の研究記録があります: 単一ファイル形式の例 : aoai-chat-simple.md – Azure OpenAI チャット機能 github-api-integration.md – GitHub API 統合 csv-preview-system.md – CSV プレビュー機能 ディレクトリ形式の例 : supabase-x-scheduler-v2/ – X 投稿スケジューラーの大規模リファクタリング api-generation-pipeline/ – OpenAPI 自動生成パイプライン frontend-refactoring/ – フロントエンド大規模リファクタリング フェーズ 4: 記事化の実践 記事化フェーズの目的 研究記録をもとに、 技術ブログ執筆に必要な情報を収集・整理 するフェーズです。 目的 : /docs/features/ (計画)+ /docs/research/ (検証)+ /application/ (実装)から情報を抽出 読者向けに再構成 記事構成案とコードスニペットを整理 記録先 : /docs/article/ ディレクトリ構造 docs/article/ ├── CLAUDE.md # 記事執筆ガイドライン ├── writing-style-prompt.md # 文体スタイルガイド └── {article-topic}/ # ケバブケース命名 ├── research-doc.md # 必須 - 調査資料 ├── no1-article.md # 必須 - 記事本文 ├── no2-article.md # 任意 - 続編がある場合 └── image/ # 必須 - 画像ファイル格納(.gitignore除外) ├── screenshot.png └── diagram.png 重要なルール : research-doc.md は必須(リポジトリ内容の調査結果) image/ サブディレクトリは必須( .gitignore で除外済み) ディレクトリ名はケバブケース( x-post-with-oauth , azure-functions-local-setup ) research-doc.md(調査資料)の構成 調査資料は、記事執筆の基礎資料となります。主要セクション: 記事概要 : 対象読者、目的、キーワード 参照元ドキュメント : 計画/検証/実装のファイルパス 技術スタック : 使用技術とバージョン 実装の要点 : 主要機能の概要とコードスニペット候補 技術的な課題と解決策 : 問題、解決策、参考実装 記事構成案 : H1/H2/H3 レベルの見出し構造 図表・資料 : 必要な図表のチェックリスト RAG もどき: ブログ HTML 抽出ツール RAG もどきとは? RAG(Retrieval-Augmented Generation) は、ベクトル検索で外部知識を自動取得し、AI が回答を生成する仕組みです。 このプロジェクトでは、 既存のブログ記事をローカルに保存 し、 手動で選択して 新規記事執筆時に参照する「 RAG もどき 」を構築しています。 RAG との違い : 本物の RAG: ベクトル化 + セマンティック検索(自動) RAG もどき: HTML ファイル保存 + 手動参照 シンプルですが、文体補正や既存記事との整合性確保には十分効果的です。 なぜ RAG もどきが必要か? 記事執筆時にこんな課題がありました: 既存記事と似た内容を書いてしまう 既存記事との整合性を取るのが大変 過去の記事タイトルや文体を忘れてしまう そこで、 fetch-blog-html.ts を使って 既存記事をローカルに保存 し、記事執筆時に参照できるようにしました。 fetch-blog-html.ts の機能 これは TypeScript である必要は全くありません。好きな言語で実装してください。こちらのファイルの肝となる部分は、ツールとして渡すことでトークン数を削減しながら既存のデータを保存できるという点です。 入力・出力 入力 : 環境変数 URL でブログ記事 URL を指定 # /docs ディレクトリで実行 cd /home/node/dev/docs URL="https://tech-lab.sios.jp/archives/49157" npm run fetch-blog 出力 : /docs/tools/doc/tech-lab-sios-jp-archives-49157.html 処理概要 既存のブログ記事から HTML を取得し、以下の処理を行います: 記事本文のみを抽出(不要なヘッダー、フッター、サイドバーを除去) 画像をテキスト化(alt 属性と URL を保持) 不要な属性・空白を削除 トークン数を計算 実装コード : TypeScript実装を公開しています fetch-blog-html.ts (GitHub Gist) SIOS Tech Lab ブログ用にカスタマイズされています 他のブログにも応用可能(セレクタ部分を調整) 実行結果例 実際に記事 49157(Next.js×Nest.js 型定義同期)を抽出した結果がこちらです: 出力ファイル : /docs/tools/doc/tech-lab-sios-jp-archives-49157.html (部分抜粋) <!-- ブログ記事情報: タイトル: AIと爆速開発!Next.js×Nest.js型定義同期の自動生成パイプライン構築術 | SIOS Tech. Lab URL: https://tech-lab.sios.jp/archives/49157 OGP画像: https://tech-lab.sios.jp/wp-content/uploads/2025/09/572995517b0ce827aa745786a62911c5.png 抽出日時: 2025-10-30T01:55:41.785Z --> <h1> AIと爆速開発!Next.js×Nest.js型定義同期の自動生成パイプライン構築術 | SIOS Tech. Lab </h1> <h2>初めに</h2> <p> AIと一緒に開発をするようになってから、フロントエンドとバックエンド両方を爆速で開発することができるようになって、検証が爆速で進むようになった龍ちゃんです。 </p> <p>AIが混乱しないようにプロジェクト自体を整備する方法についてお話しします。</p> <ul> <li> <a href="https://tech-lab.sios.jp/archives/49140" >Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術</a > </li> <li> <a href="https://tech-lab.sios.jp/archives/49148" >AI協働で仕様書アレルギー克服!開発時間を1週間→2日に短縮する実践法</a > </li> </ul> <!-- 画像はURLのみ保持 --> <figure> <img src="https://i0.wp.com/tech-lab.sios.jp/wp-content/uploads/2025/09/b060e7bc0582b2a27f7c9de8a921557f.png" /> (https://i0.wp.com/tech-lab.sios.jp/wp-content/uploads/2025/09/b060e7bc0582b2a27f7c9de8a921557f.png) </figure> <!-- ...中略... --> <h2>まとめ</h2> <p> 正直、これは導入してみて一番良かったなと思える点ですね。ルールをあまり追加させずにAIに生成させるコードが圧倒的にきれいになりました。 </p> 抽出された HTML の特徴 : OGP 情報(タイトル、URL、画像、抽出日時)をコメントで記録 記事の構造(見出し、段落、リスト)を完全保持 画像は URL のみ保存(Claude Code で参照可能) 不要なヘッダー、フッター、サイドバーを除去 龍ちゃんの文体(「ども!」)も維持 トークン数削減効果 実測値 (既存記事 17 件の平均): 指標 削減率 抽出による削減 40-50% 圧縮による削減 10-15% 総合圧縮率 50-60% 具体例 (記事 49157: 型安全パイプライン): 元ページ全体のトークン数: 35,420 抽出後のトークン数: 18,250(48.5%削減) 最終圧縮後のトークン数: 15,680(14.1%削減) 総削減トークン数: 19,740(55.7%削減) RAG もどきの活用方法 1. 既存記事の一覧確認 ls /home/node/dev/docs/tools/doc/ 抽出済みの記事を一覧で確認し、類似テーマの記事を探します。 2. 記事執筆時に既存記事を参照 類似テーマの既存記事を選択し、Claude Code に読み込ませます。 # プロンプト例 この記事から発展して、次の記事を考案したいので読み込んでください。 **既存記事**: [抽出済みの HTML ファイル] **文体ガイド**: /docs/article/writing-style-prompt.md **記事の位置づけ**: [シリーズ構成、関連記事との関係] 既存記事(HTML)+ 文体ガイド(writing-style-prompt.md)+ 記事の位置づけを読み込ませることで、 一貫した記事品質 と シリーズとしての連続性 を維持できます。 RAG もどきのメリット 既存記事との重複チェック 既存記事を参照することで、似た内容を書くことを防げる 文体・構成の一貫性 既存記事のスタイルを参考に、統一された記事品質を維持 トークン数削減(50-60%) 記事をローカルに保存し、コード化することでトークン数を大幅削減。Claude Code への入力が効率化される 不要な記事取得の軽減 記事化している情報をローカルに落とすことで、都度 Web から取得する手間を削減 オフライン参照 インターネット接続なしで既存記事を参照可能 将来の完全自動化への布石 システムプロンプトを育てることで、検証 → 記事化のナレッジ化まで完全自動化も夢じゃない! 文体補正: writing-style-prompt.md 文体補正システムとは? writing-style-prompt.md は、記事を執筆するための文体ガイドです。 仕組み : 既存記事から文体を抽出 : 投稿済みの記事を分析し、文体パターンをプロンプト化 プロセス化 : 新しい記事を投稿するたびに、文体ガイドをレビュー・更新 レビュー用システムプロンプトを育てる : 継続的な改善で精度向上 メリット : 一貫した記事品質の維持 AI が文体を学習し、自動的にスタイルに合った記事を生成 記事執筆時のレビュー観点が明確化 実践例: 実際の執筆プロセス 本ワークフローの実際の執筆フローを解説します。 ステップ 1: 記事のアイデアをざっくり書く まず、 書きたいことを適当でよいのでざっくり書きます 。 記事タイトル案 伝えたいポイント(箇条書き) 想定する読者 記事の位置づけ(シリーズ構成等) この段階では完璧である必要はありません。アイデアをラフに整理するだけです。 ステップ 2: 参照ドキュメントと実装を調査 ざっくり書いた内容をもとに、 参照すべきドキュメントや実装を調査 します。 調査資料(research-doc.md)に以下を整理: 参照元ドキュメント(計画、検証、実装のファイルパス) 技術スタック 実装の要点(コードスニペット候補) 技術的な課題と解決策 ステップ 3: プロンプトとして情報を渡す 記事執筆時に プロンプトとして以下を渡します : 参照記事(fetch-blog-html.ts で取得した HTML) 記事の位置づけ(シリーズ構成、関連記事との関係) writing-style-prompt.md(文体ガイド) research-doc.md(調査資料) Claude Code にこれらを読み込ませ、記事本文(no1-article.md)を執筆します。 ステップ 4: 記事本文執筆とレビュー Claude Code にプロンプトを渡し、 no1-article.md を執筆します。 執筆後のレビュー項目: 参照記事との整合性チェック 定量的データの妥当性チェック 文体チェック(writing-style-prompt.md と照合) よくある課題と解決法 課題 1: 調査資料が長すぎて AI が混乱 問題 : research-doc.md が長すぎると(2000 行以上)、AI が全体を把握しづらくなる。 解決策 : ディレクトリ形式に分割(research-doc.md, architecture.md, implementation.md) セクションごとに記事を分割(no1-article.md: 基礎編、no2-article.md: 応用編) 課題 2: 記事執筆に時間がかかりすぎる 問題 : 調査資料から記事本文への変換に時間がかかる(4-5 時間)。 解決策 : テンプレートを活用(はじめに、Before/After セクション) AI に変換を依頼(writing-style-prompt.md + Mermaid 図 + Before/After 比較) 段階的に執筆(導入 → 本編 → まとめ) ワークフロー全体の効果 定量的な効果 指標 Before After 改善率 記事執筆時間 8 時間 4 時間 50%削減 調査時間 2 時間 1 時間 50%削減 既存記事重複チェック 手動(30 分) 簡易 RAG(5 分) 83%削減 記事品質(一貫性) 60%(主観) 90%(主観) 50%向上 ※測定条件 : 中規模記事(800-1000 行)での実測値 開発者の声(龍ちゃんの実体験) 導入後の変化 : この機能を導入して、ブログを書くっていう行為がまた一つ変わりました。検証した内容からそのままブログ化できるっていうのは本当に便利で、動くコードがそのままブログに転写できるようになったのは大きいですね。 意識的な変化 : ただ、使うにあたって気づいたのが、「明確な意図を持って検証する」必要が出てきたということです。今までは頭の中でやっていた作業を、ちゃんとドキュメント化しなきゃいけない。結構頭を使うようになりました。 Before → After : 導入前 : 現象を後から眺めて「ブログ書くか」みたいな感じ 導入後 : 検証の過程を全部ドキュメント化 「何を考えてこうやってみたのか」 「実際どうなったのか」 「最初の予想と結論の違い」 これ全部ドキュメントに残さなきゃいけないので、検証を明確な意識を持って行うようになりました。 全体的な感想 : インプットもアウトプットも、AI が入ってきて変わったなと素直に思ってます。 ワークフロー全体の効果まとめ 知見が自然に蓄積 実装と同時にドキュメントが作成される 記事執筆時間 50%削減 調査資料が既に揃っている 既存記事との整合性確保 簡易 RAG(fetch-blog-html.ts)で既存記事を参照 記事品質の向上 writing-style-prompt.md で一貫した文体 記事化のハードルが下がる 「記事書こう」と思った時に、すぐに書き始められる まとめ この記事では、 検証 → 記事化ワークフローの実践方法 を解説しました。 検証 → 記事化ワークフローのポイント フェーズ 3: 研究記録(/docs/research/) 実装完了後の知見・検証結果をドキュメント化 フェーズ 4: 記事化(/docs/article/) 調査資料(research-doc.md)→ 記事本文(no1-article.md)の変換 RAG もどきシステム: fetch-blog-html.ts 既存記事をローカルに保存し、記事執筆時に参照(トークン数 50-60%削減) 文体補正: writing-style-prompt.md 一貫した記事品質を維持 効果まとめ Before After 記事執筆時間 8 時間 調査資料活用で 4 時間(50%削減) 既存記事重複チェック手動(30 分) 簡易 RAG で 5 分(83%削減) 記事品質バラバラ 文体補正で一貫性 90%(主観) 次のステップ このワークフローをさらに発展させたい方は、以下の記事もご覧ください: 関連記事シリーズ : Claude Code 革命!3 フェーズ開発で効率的な開発:計画 → 実装 → 検証術 ← 本記事の前提知識 AI 協働で仕様書アレルギー克服!開発時間を 1 週間 →2 日に短縮する実践法 ← 3 フェーズ開発の実践例 本記事 : 検証 → 記事化ワークフロー(4 フェーズ目の詳細) 実装詳細・応用編 : AI と爆速開発!Next.js×Nest.js 型定義同期の自動生成パイプライン構築術 ← API 自動生成の実践 今後の予定 : Claude Code× モノレポで実現する AI 協業開発環境(全体像のまとめ記事) 参考リンク 公式ドキュメント Claude Code 公式ドキュメント Cheerio 公式ドキュメント Mermaid 図記法 Markdown 記法 ここまで読んでいただき、ありがとうございました! 検証 → 記事化ワークフローを実践することで、実装完了後の知見が自然に記事化され、技術ブログの執筆が劇的に効率化されます。ぜひ、この記事を参考に、あなたのプロジェクトでも実践してみてください。 質問や感想は、コメント欄でお待ちしております。また、Twitter のほうもよろしくお願いします! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2025 年版】検証 → 記事化で知見を資産化!Claude Code×RAG もどきで AI 技術ブログ執筆を効率化 first appeared on SIOS Tech. Lab .
はじめに 前回はDockerを利用して単体のコンテナでデータ永続化したDBコンテナを起動する方法を解説しました。 本ブログでは、Kubernetesにおけるデータ永続化について紹介します。 まず、Kubernetesと前回使用したDockerの違いについて簡潔に触れ、Kubernetesにおけるデータ永続化の仕組みやリソース、StatefulSetについて詳しく解説します。 KubernetesとDockerの違い Kubernetesのデータ永続化の解説の前に前回使用したDockerとの違いを解説します。以下の表で示す通りDockerとKubernetesは規模や役割が違います。 Dockerはアプリケーションをコンテナという箱に入れることでどのような環境でも動かせるようにします。 一方、KubernetesはDockerコンテナを大規模に管理・運用するためのプラットフォームです。 DockerとKubernetesの比較 Kubernetesにおけるデータ永続化 Kubernetesのデータ永続化はPodが削除されてもデータを保持する仕組みです。データ永続化で主に使用されるのはPV( PersistentVolume )とPVC( PersistentVolumeClaim )です。外部ストレージなどと連携をしてデータを安全に保存します。 PV PVはざっくりと説明するとストレージを抽象化したものです。正確には物理的なストレージ(AWSのディスクや社内のNFSサーバーなど)を、Kubernetesが理解できる形に「抽象化」し、紐づけたものです。PVはPodと分離されているためPodが削除されてもデータの保持ができます。 PVC PVCは開発者が具体的なストレージ要件(容量、アクセスモードなど)を宣言するためのものです。PVCをKubernetesに提出すると、Kubernetesが条件に合うPVを自動で紐付けてくれます。 コンテナDBに最適なリソース KubernetesでDBを利用する際にはリソースが使われます。リソースとは、Kubernetes上でアプリケーションやサービスを動かすために必要な計算資源や制御対象のことです。KubernetesでのリソースはPod、Service、PersistentVolumeといったKubernetes内で管理されるオブジェクトを指します。 StatefulSetは、Kubernetesでデータの一貫性やノード間の順序が求められるステートフルなアプリケーションを実行する際に最適なリソースです。特にデータベースのように一貫性や永続性が必要なケースで使用されます。Podに固有の名前を付与し順序を維持することで、複数のインスタンス間で安定した動作を可能にします。 StatefulSetとDB永続化 StatefulSetを利用することでDBの永続化が可能になります。これにはStatefulSetの二つの機能が関係しています。 まず、StatefulSetはPodに固定の一意な名前を付与する機能を持っています。これによりPodが故障して新しいPodが作成されても同じ名前が引き継がれるため、リソースの識別をすることができます。これにより、DBクラスタ内で「プライマリ(データを管理・更新する役割)」や「レプリカ(データをコピーして保存する役割)」といったノードの役割を識別しやすくなり、クラスタ構成や役割の管理が簡単になります。 次に、Podごとに自動的にPVCを作成する仕組みがあります。この機能により、各Podが個別のストレージを持つことが可能となります。これら二つの機能によってデータの永続化が可能になっています。例として、db-1という名前のPodが故障し、新しいdb-1が作成された場合でも、db-1というPodに固有のPVCが結びついているためデータの永続化が保証されます。 DeploymentとStatefulSetの違い DeploymentとStatefulSetはどちらもKubernetesでPodを管理するための機能です。 Deploymentは、KubernetesでアプリケーションのPodを管理・更新・スケーリングするためのリソースで、主にステートレスなワークロードに適しています。Deploymentは各Podに固有の名前がありません。どのPodも同じ仕事をするため、1つが故障しても別のPodが仕事を代替することができます。しかし、PodがPVCに紐づかないためデータベースのように永続的かつ固有のデータを持つワークロードには適していません。 StatefulSetは前述のとおりPodそれぞれに固有の名前があります。Podが故障しても以前と同じ名前で作成され、同じPVCに接続されるためデータの永続性が保たれます。 DeploymentとStatefulSetの比較 おわりに 今回はKubernetesにおけるデータ永続化について解説しました。 Kubernetesにおけるデータ永続化の知識を活用すると、クラウドネイティブ環境でのステートフルなアプリケーション運用をより効率的に行うことができます。特にデータベースのような一貫性が求められるワークロードをKubernetes上で運用する際には、StatefulSetや永続ボリュームの知識が不可欠です。 次回は実際にKubernetes上でコンテナDBを動かす方法を解説しますので、ぜひご覧ください。 参考文献 Kubernetes と Docker はどのように異なりますか? https://aws.amazon.com/jp/compare/the-difference-between-kubernetes-and-docker/ Kubernetesのデータ永続化に重要なオブジェクトPVとPVCについて解説します! https://www.rworks.jp/cloud/kubernetes-op-support/kubernetes-column/kubernetes-entry/29321/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post コンテナDB入門シリーズ②:Kubernetesにおけるデータ永続化の基本 first appeared on SIOS Tech. Lab .