メインコンテンツまでスキップ

スキル—River Reviewの心臓部

スキルとは

スキルとは、チームの知識を再利用可能でバージョン管理されたコードレビューパターンとして体系化したものです。

スキルは以下のように捉えることができます。

  • プレイブック—チームが従うレビュー手順を文書化したもの
  • ツール—自動的に実行されるレビューロジック
  • アーティファクト—バージョン管理され、テスト可能で、共有可能な資産

スキルは暗黙知(「普段Xを確認している」)を明示的かつ自動化されたチェックに変換し、毎回一貫して実行します。

スキルが重要な理由

問題: 暗黙知への依存

従来のコードレビューは人間の記憶と経験に依存しています。

  • 「SQLインジェクションを確認したか?」
  • 「エラーハンドリングの検証を忘れずに」
  • 「アクセシビリティ監査を忘れないように」

この結果、以下の問題が生じます。

  • 一貫性の欠如—レビューの質が担当者に依存する
  • 知識の喪失—エキスパートが離れると知識も消える
  • スケーリングの困難—すべてを徹底的にレビューすることが難しい

解決策: 資産としてのスキル

スキルは暗黙知を明示的かつ実行可能にします。

# Before: 「SQLインジェクションを確認すること」
# After: バージョン管理されテスト可能なスキル
id: security-basic
name: Baseline Security Checks
version: 0.1.0

利点は以下のとおりです。

  • 再現性—毎回同じチェックを実行する
  • 改善可能—フィクスチャーでテストし改良できる
  • 共有可能—チーム間でスキルを交換できる
  • スケーラブル—人的ボトルネックなしに全PRをレビューできる

スキルの構造

スキルは主に以下の5つのコンポーネントで構成されます。最小構成ではスキル定義の.mdファイルのみで動作し、評価用アセットは必要に応じて同梱できます。

1. メタデータ(skill.yaml)

スキルの内容と実行タイミングを宣言します。

id: security-basic # 一意の識別子
name: Baseline Security Checks # 人間が読める名前
version: 0.1.0 # セマンティックバージョン
description: Detects common vulnerabilities

# 起動条件
phase: midstream # SDLCフェーズ
applyTo: # ファイルパターン
- 'src/**/*.ts'
- 'src/**/*.js'

# 入出力
inputContext: [diff] # 入力要件
outputKind: [findings] # 出力タイプ

# 最適化ヒント
modelHint: cheap # コスト/精度バランス
severity: major # 検出の重要度

2. プロンプト(prompt/)

自然言語によるレビューロジックです。

prompt/system.md—役割を設定:

You are a security-focused code reviewer.
Your task is to identify common vulnerabilities.

Focus on:

- SQL injection
- XSS vulnerabilities
- Hardcoded secrets

prompt/user.md—コードに適用:

Review this diff for security issues:

{{diff}}

Report findings using this format:
**Finding:** [description]
**Fix:** [suggestion]
**Severity:** [info|minor|major|critical]

3. フィクスチャー(fixtures/)

テスト用のサンプル入力です。

fixtures/01-sql-injection.md:

+const query = `SELECT * FROM users WHERE id = ${userId}`;
+const result = await db.query(query);

4. ゴールデン出力(golden/)

各フィクスチャーに対する期待結果です。

golden/01-sql-injection.md:

**Finding:** SQL injection vulnerability in user lookup
**Fix:** Use parameterized queries: `db.query('SELECT * FROM users WHERE id = ?', [userId])`
**Severity:** major

5. 評価設定(eval/)

スキルのテスト方法です。

eval/promptfoo.yaml:

prompts:
- file://../prompt/system.md
- file://../prompt/user.md

tests:
- vars:
diff: file://../fixtures/01-sql-injection.md
assert:
- type: contains
value: 'SQL injection'
- type: similar
value: file://../golden/01-sql-injection.md
threshold: 0.7

スキルのライフサイクル

詳細は スキルのライフサイクル も参照してください。

1. 作成

# インタラクティブなスキャフォールディング
npm run create:skill

# 手動作成
cp -r scripts/templates/skill skills/my-skill

2. 実装

プロンプトを編集してレビュー知識をエンコードします。

# prompt/system.md

You are a React code reviewer checking for common pitfalls.

Rules:

1. State updates must be immutable
2. Event handlers should be memoized
3. Lists must have stable keys

3. テスト

フィクスチャーとゴールデン出力を追加します。

# テストケース追加
echo "..." > fixtures/01-state-mutation.md
echo "..." > golden/01-state-mutation.md

# 評価実行
cd skills/my-skill
npx promptfoo eval

4. 検証

スキルがスキーマ要件を満たしていることを確認します。

npm run validate:skill-yaml

5. デプロイ

以下の条件を満たすとスキルが自動的に起動します。

  • フェーズが一致(upstream/midstream/downstream)
  • ファイルパターンが一致(applyToグロブ)
  • 必要なコンテキストが利用可能(inputContext

デプロイ手順は不要です。リポジトリにコミットするだけで完了します。

6. 改善

スキルの効果をモニタリングし反復改善します。

# 回帰テスト実行
npx promptfoo eval

# 結果に基づきプロンプトを更新
# エッジケースのフィクスチャーを追加
# skill.yamlのバージョンをバンプ

最初のスキルを作成する

ステップバイステップの例

TypeScriptのnullチェック漏れを検出するスキルを作成します。

1. スキャフォールド:

npm run create:skill

# 入力:
# ID: typescript-nullcheck
# Name: TypeScript Null Safety
# Phase: midstream
# Files: src/**/*.ts

2. システムプロンプトを記述:

# prompt/system.md

You are a TypeScript code reviewer focused on null safety.

Check for:

- Optional chaining (?.) vs explicit null checks
- Nullish coalescing (??) vs || for defaults
- Non-null assertions (!) without validation

3. ユーザープロンプトを記述:

# prompt/user.md

Review this TypeScript code for null safety issues:

{{diff}}

For each issue, provide:

- **Issue:** What's wrong
- **Line:** Line number
- **Fix:** Corrected code
- **Severity:** minor|major

4. フィクスチャーを追加:

# fixtures/01-unsafe-property-access.md

+function getName(user) {
+ return user.profile.name;
+}

5. ゴールデン出力を追加:

# golden/01-unsafe-property-access.md

**Issue:** Unsafe property access without null check
**Line:** 2
**Fix:** Use optional chaining: `return user?.profile?.name;`
**Severity:** major

6. 評価を設定:

# eval/promptfoo.yaml
prompts:
- file://../prompt/system.md
- file://../prompt/user.md

providers:
- id: openai:gpt-4o
config:
temperature: 0.1

tests:
- description: Unsafe property access
vars:
diff: file://../fixtures/01-unsafe-property-access.md
assert:
- type: contains
value: 'optional chaining'
- type: llm-rubric
value: |
Score 1 if the output correctly identifies the null safety issue
and suggests optional chaining. Score 0 otherwise.

7. テスト:

cd skills/midstream/typescript-nullcheck
npx promptfoo eval

# eval/results.jsonで結果を確認

8. コミット:

git add skills/typescript-nullcheck
git commit -m "feat: add TypeScript null safety skill"

以上でスキルはTypeScriptのPRに対して自動的に実行されます。

スキル設計の原則

1. 単一責任

  • 悪い例: 「コード品質、セキュリティ、パフォーマンスをチェック」
  • 良い例: 「SQLインジェクション脆弱性を検出」

1つのスキルに1つの関心事を持たせます。焦点を絞ったスキルには以下の利点があります。

  • テストが容易
  • 理解が容易
  • 保守が容易

2. 明確な契約

入出力を明示的に宣言します。

inputContext: [diff, fullFile] # diffとファイル全体の両方が必要
outputKind: [findings, metrics] # findingsとmetricsを出力

これにより以下が実現します。

  • スマートなスキル選択(コンテキストが利用可能な場合のみ実行)
  • より良いエラーメッセージ(コンテキスト不足時はスキップ、失敗ではない)
  • 将来の最適化(依存関係に基づく並列実行)

3. フィクスチャーによるテスト

すべてのスキルに以下を用意します。

  • 少なくとも2つのフィクスチャー(ハッピーパス + エッジケース)
  • 対応するゴールデン出力
  • 動作を検証するアサーション
fixtures/
01-happy-path.md → golden/01-happy-path.md
02-edge-case-null.md → golden/02-edge-case-null.md
03-false-positive.md → golden/03-false-positive.md

4. 慎重なバージョニング

セマンティックバージョニングを使用します。

  • パッチ(0.1.1): タイポ修正、例の改善
  • マイナー(0.2.0): 新しいチェック追加、スコープ拡大
  • メジャー(1.0.0): 出力フォーマット変更、互換性の破壊

5. 適切なモデルヒント

コスト/精度のバランスを選択します。

modelHint: cheap # 高速な構文チェック、スタイルガイド
modelHint: balanced # 標準的なコードレビュー
modelHint: high-accuracy # セキュリティ監査、アーキテクチャレビュー

cheapスキルが最初に実行され(高速フィードバック)、high-accuracyが最後に実行されます(徹底的な分析)。

高度な概念

入力コンテキスト

スキルはさまざまな入力タイプを要求できます。

inputContext:
- diff # Gitのdiff(最速、コンテキスト最小)
- fullFile # ファイル全体
- tests # 関連テストファイル
- adr # アーキテクチャ決定記録
- commitMessage # コンテキストとしてのコミットメッセージ

フレームワークが要求されたコンテキストを提供するか、スキルをスキップします。

出力タイプ

スキルはさまざまな出力を生成できます。

outputKind:
- findings # 標準的なレビューコメント
- questions # 著者への確認質問
- metrics # 定量的な計測
- actions # 自動修正の提案

出力フォーマッターが宣言された種類に応じて適応します。

依存関係

スキルは必要なツールを宣言できます。

dependencies:
- test_runner # テスト実行が必要
- coverage_report # カバレッジデータが必要
- custom:sonarqube # カスタムツール連携

フレームワークがスキル実行前に利用可能性を検証します。

スキルの合成(将来)

注意: これは将来の設計コンセプトであり、現在は実装されていません。skill: プレフィックスは現時点で有効な依存関係の値ではありません。有効な値は DependencyEnum に定義された文字列(例: test_runnercoverage_report)と custom:<name> 形式のみです。

スキルが他のスキルを参照できるようになると、以下が実現します。

  • 段階的チェック(基本 → 高度)
  • スキルの再利用(セキュリティスキルが認証スキルを呼び出す)
  • ワークフローオーケストレーション

評価の哲学

評価の目的

スキルはコードです。他のコードと同様にテストが必要です。

評価は以下の問いに答えます。

  • 動作するか?(回帰テスト)
  • 効果的か?(品質測定)
  • 一貫しているか?(再現性チェック)

評価戦略

1. 完全一致(厳密)

assert:
- type: contains
value: 'SQL injection'

用途: 特定の技術用語、必須フレーズです。

2. 類似度(柔軟)

assert:
- type: similar
value: file://../golden/01-expected.md
threshold: 0.7 # 70%の意味的類似度

用途: 自然言語出力、表現のバリエーションです。

3. LLM-as-Judge(ルーブリック)

assert:
- type: llm-rubric
value: |
Score 1 if the output identifies the vulnerability
and provides a concrete fix. Score 0 otherwise.

用途: 複雑な基準、主観的な品質です。

ベストプラクティス: 複数のアサーションを組み合わせます。

assert:
- type: contains
value: 'SQL injection' # 問題への言及が必須
- type: llm-rubric
value: 'Suggests parameterized queries' # 修正案が必須
- type: similar
value: file://../golden/01.md
threshold: 0.6 # 期待出力に類似すべき

継続的な評価

定期的に評価を実行します。

# コミット前
npm run eval:fixtures

# CIで(オプション—APIキーが必要)
# .github/workflows/skills-and-tests.ymlを参照

スキルの品質をコード品質と同様に扱い、マージ前にテストします。

ベストプラクティス

推奨事項

  • 小さく始める—1つのシンプルなスキルは1つの複雑なスキルより優れている
  • 徹底的にテストする—エッジケースや誤検知のフィクスチャーを追加する
  • 意図を文書化する—スキルが何をするかだけでなく、なぜ存在するかを説明する
  • 慎重にバージョニングする—破壊的変更にはメジャーバージョンバンプが必要
  • フィードバックに基づき反復する—評価結果を活用して改善する

避けるべきこと

  • 曖昧にしない—「コード品質をチェック」は範囲が広すぎる
  • フィクスチャーを省略しない—テストされていないスキルはドリフトする
  • 固有値をハードコードしない—ファイルパス・名前には変数を使用する
  • 誤検知を無視しない—防止するためのフィクスチャーを追加する
  • 重要度を忘れない—ユーザーが検出結果を優先順位付けできるようにする

レジストリからの例

セキュリティスキル

# skills/midstream/security-basic/SKILL.md(frontmatter)
id: security-basic
name: Baseline Security Checks
phase: midstream
applyTo: ['src/**/*.{ts,js}']
inputContext: [diff]
modelHint: cheap
severity: major

ユースケース: すべてのPRに対する高速セキュリティスキャンです。

アーキテクチャスキル

# skills/upstream/adr-decision-quality/SKILL.md
id: adr-decision-quality
name: ADR Decision Quality
phase: upstream
applyTo: ['docs/adr/*.md']
inputContext: [fullFile, adr]
modelHint: high-accuracy
severity: info

ユースケース: アーキテクチャ決定の徹底的なレビューです。

テストカバレッジスキル

# skills/downstream/coverage-gap/SKILL.md
id: coverage-gap
name: Test Coverage Gap Detection
phase: downstream
applyTo: ['src/**/*.{ts,js}']
inputContext: [diff, tests, coverage_report]
modelHint: balanced
severity: minor

ユースケース: テストされていないコードパスの特定です。

レガシーフォーマットからの移行

既存のMarkdownベースのスキルがある場合は段階的に移行します。

# 1. レジストリフォーマットのスキルを作成
npm run create:skill

# 2. プロンプト内容をコピー
cp skills/midstream/old-skill.md \
skills/new-skill/prompt/system.md

# 3. メタデータを追加
# skills/new-skill/skill.yamlを編集

# 4. フィクスチャーを追加
# skills/new-skill/fixtures/*.mdを作成

# 5. テスト
cd skills/new-skill
npx promptfoo eval

# 6. 旧スキルを廃止
# (新スキルが検証されるまで保持)

リソース

次のステップ

  1. 既存スキルを探索するskills/ディレクトリで例を確認する
  2. 最初のスキルを作成するnpm run create:skillを使用する
  3. フィクスチャーでテストする—テストケースを追加しpromptfoo evalを実行する
  4. チームと共有する—スキルはプロジェクト間で共有可能

スキルはRiver Reviewの心臓部です。チームの知識を再現可能でテスト可能な資産としてエンコードすることで、チームと共に成長するコードレビューの専門知識ライブラリを構築します。