session-learning について
amplihack のセッション学習スキルです。会話記録から知見を抽出し、後のセッションで関連する内容を提示します。知見はカテゴリ別の YAML に保存し、意味検索ではなくキーワードで照合します。
主な機能
- 01セッション終了時の知見抽出
- 02開始時の関連知見の注入
- 03カテゴリ別の YAML 保存
- 04コマンドによる知見管理
- 05フック失敗時も継続する設計
活用シーン
- 01デバッグ時に過去の解決策を参照
- 02プロジェクトのワークフロー知見を再利用
- 03保存した知見の確認や検索
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ rysweet/ amplihack --skill session-learningインストール後、Claude Code で/session-learningを入力して読み込まれたか確認します。
同梱ファイルREADME.md
詳細
セッション学習スキル
目的
このスキルは、次の方法でセッションをまたいだ学習を実現します。
- Stop フックでセッションのトランスクリプトから学びを抽出します
- 学びを構造化された YAML 形式(
~/.amplihack/.claude/data/learnings/)で保存します - タスクの類似性に基づき、SessionStart で関連する過去の学びを注入します
/amplihack:learnings機能で学びを管理します
設計思想
徹底的にシンプルなアプローチ:
- 学習カテゴリごとに YAML ファイルを 1 つ使用します(セッションごとではありません)
- 関連性の判定にはシンプルなキーワードマッチングを使用します(複雑な ML は使用しません)
- 既存の DISCOVERIES.md/PATTERNS.md を補完します。置き換えるものではありません
- フェイルセーフ:セッションの開始や終了を妨げることはありません
学習カテゴリ
学びは次の 5 つのカテゴリに保存されます。
| カテゴリ | ファイル | 目的 |
|---|---|---|
| errors | errors.yaml | エラーパターンとその解決策 |
| workflows | workflows.yaml | ワークフローに関する知見と近道 |
| tools | tools.yaml | ツールの使用パターンと注意点 |
| architecture | architecture.yaml | 設計上の決定とトレードオフ |
| debugging | debugging.yaml | デバッグ戦略と根本原因 |
YAML スキーマ
各学習ファイルは次の構造に従います。
# .claude/data/learnings/errors.yaml
category: errors
last_updated: "2025-11-25T12:00:00Z"
learnings:
- id: "err-001"
created: "2025-11-25T12:00:00Z"
keywords:
- "import"
- "module not found"
- "circular dependency"
summary: "Circular imports cause 'module not found' errors"
insight: |
When module A imports from module B and module B imports from module A,
Python raises ImportError. Solution: Move shared code to a third module
or use lazy imports.
example: |
# Bad: circular import
# utils.py imports from models.py
# models.py imports from utils.py
# Good: extract shared code
# shared.py has common functions
# both utils.py and models.py import from shared.py
confidence: 0.9
times_used: 3
このスキルを使用するタイミング
自動使用(フック経由):
- セッション終了時:トランスクリプトから学びを抽出します
- セッション開始時:プロンプトのキーワードに基づいて関連する学びを注入します
手動使用:
- 学びを表示または管理したい場合
- デバッグ中に過去の解決策を確認したい場合
- プロジェクト固有のパターンを把握するためにオンボーディングする場合
学びの抽出プロセス
ステップ 1:セッションのトランスクリプトを分析する
セッション終了時に、次の内容を確認します。
- エラーパターン:発生したエラーとその解決方法
- ワークフローに関する知見:うまくいった手順、またはうまくいかなかった手順
- ツールに関する発見:ツールを効果的に使う新しい方法
- アーキテクチャ上の決定:設計上の選択とその根拠
- デバッグ戦略:根本原因分析のパターン
ステップ 2:構造化された学びを抽出する
重要な知見ごとに、次の処理を行います。
- カテゴリとタイムスタンプに基づいて一意の ID を生成します
- コンテキストからキーワードを抽出します(関連性の高い用語を 3~5 個)
- 1 文の要約を作成します
- 説明を添えて詳しい知見を記述します
- 該当する場合はコード例を含めます
- 信頼度スコアを割り当てます(0.5~1.0)
ステップ 3:既存の学びと統合する
- キーワードの重複を使って学びの重複を確認します
- 類似する学びが見つかった場合(キーワード一致率が 60% 超)、信頼度を更新します
- それ以外の場合は、新しい学びをカテゴリファイルに追加します
学びの注入プロセス
ステップ 1:タスクのキーワードを抽出する
セッション開始時のプロンプトから、次の内容を抽出します。
- 技術用語(言語、フレームワーク、ツール)
- 問題を示す語句(error、fix、debug、implement)
- ドメインキーワード(api、database、auth など)
ステップ 2:関連する学びを探す
各学習カテゴリについて、次の処理を行います。
- YAML から学びを読み込みます
- タスクとのキーワードの重複度を計算します
overlap_score * confidence * recency_weightで順位付けします- 関連性が最も高い学びを上位 3 件選択します
ステップ 3:コンテキストを注入する
関連する学びをコンテキストとして整形します。
## Past Learnings Relevant to This Task
### [Category]: [Summary]
## [Insight with example if helpful]
使用例
例 1:自動抽出
Session: Debugging circular import issue in Neo4j module
Duration: 45 minutes
Resolution: Moved shared types to separate file
Extracted Learning:
- Category: errors
- Keywords: [import, circular, neo4j, type]
- Summary: Circular imports in Neo4j types cause ImportError
- Insight: When Neo4jNode imports from connection.py which imports
Node types, move types to separate types.py module
- Example: types.py with dataclasses, connection.py imports from types.py
例 2:自動注入
Session Start Prompt: "Fix the import error in the memory module"
Matched Learnings:
1. errors/err-001: "Circular imports cause 'module not found' errors" (85% match)
2. debugging/dbg-003: "Use `python -c` to isolate import issues" (60% match)
Injected Context:
## Past Learnings Relevant to This Task
### Errors: Circular imports cause 'module not found' errors
When module A imports from module B and B imports from A, Python raises
ImportError. Solution: Move shared code to a third module or use lazy imports.
---
例 3:手動管理
User: Show me what I've learned about testing
Claude (using this skill):
1. Reads .claude/data/learnings/workflows.yaml
2. Filters learnings with keywords containing "test"
3. Displays formatted list with summaries and examples
キーワードマッチングアルゴリズム
シンプルで効果的なマッチングです。
def calculate_relevance(task_keywords: set, learning_keywords: set) -> float:
"""Calculate relevance score between 0 and 1."""
if not task_keywords or not learning_keywords:
return 0.0
# Count overlapping keywords
overlap = task_keywords & learning_keywords
# Score: overlap / min(task, learning) to not penalize short queries
return len(overlap) / min(len(task_keywords), len(learning_keywords))
統合ポイント
Stop フックとの統合
Stop フックは、このスキルを呼び出して学びを抽出できます。
- トランスクリプトを解析し、重要なイベントを特定します
- エラーパターン、解決策、知見を識別します
- 適切なカテゴリの YAML に保存します
- 抽出の概要を記録します
Session Start フックとの統合
Session Start フックは、関連する学びを注入できます。
- 初期プロンプトを解析してキーワードを抽出します
- 各カテゴリから一致する学びを探します
- コンテキスト注入用に整形します
- セッションのコンテキストに含めます
/amplihack:learnings コマンドを使用する場合
学習管理用のコマンドインターフェイス:
/amplihack:learnings show [category]- 学習内容を表示/amplihack:learnings search <query>- すべてのカテゴリを対象に検索/amplihack:learnings add- 学習内容を手動で追加/amplihack:learnings stats- 学習統計を表示
品質ガイドライン
抽出する場合
次の場合に学習内容を抽出します。
- 問題の解決に10分以上かかった
- 一見してわかりにくいツールの動作を発見した
- 広く応用できるパターンを見つけた
- トレードオフを伴うアーキテクチャ上の決定を行った
抽出しない場合
次の場合は抽出を見送ります。
- 問題が単純なタイプミスや構文エラーだった
- 解決方法がすでに DISCOVERIES.md または PATTERNS.md に記載されている
- 再利用するには知見がプロジェクト固有すぎる
- 確信度が低い(<0.5)
学習内容の品質チェックリスト
- キーワードが具体的で検索しやすい
- 要約が明確な一文になっている
- 知見で「何を」だけでなく「なぜ」を説明している
- 例が最小限で、そのまま実行できる
- 確信度が実際の確かさを反映している
ファイルの場所
.claude/
data/
learnings/
errors.yaml # Error patterns and solutions
workflows.yaml # Workflow insights
tools.yaml # Tool usage patterns
architecture.yaml # Design decisions
debugging.yaml # Debugging strategies
_stats.yaml # Usage statistics (auto-generated)
既存システムとの比較
| 機能 | DISCOVERIES.md | PATTERNS.md | セッション学習 |
|---|---|---|---|
| 形式 | Markdown | Markdown | YAML |
| 対象ユーザー | 人間 | 人間 | エージェントと人間 |
| 保存場所 | 単一ファイル | 単一ファイル | カテゴリごとのファイル |
| マッチング | 手動で読み込み | 手動で読み込み | キーワードベースの自動処理 |
| 注入 | 手動 | 手動 | 自動 |
| 範囲 | 重要な発見 | 実証済みのパターン | 役立つあらゆる知見 |
併用方法:
- 重要で十分に文書化された発見には DISCOVERIES.md を使用します
- 実証済みで再利用可能なコード付きのパターンには PATTERNS.md を使用します
- 今後のセッションに役立つちょっとした知見には Session Learning を使用します
エラー処理
YAML の解析エラー
学習ファイルが破損したり無効になったりした場合:
import yaml
from pathlib import Path
def safe_load_learnings(filepath: Path) -> dict:
"""Load learnings with graceful error handling."""
try:
content = filepath.read_text()
data = yaml.safe_load(content)
if not isinstance(data, dict) or "learnings" not in data:
print(f"Warning: Invalid structure in {filepath}, using empty learnings")
return {"category": filepath.stem, "learnings": []}
return data
except yaml.YAMLError as e:
print(f"Warning: YAML error in {filepath}: {e}")
# Create backup before recovery
backup = filepath.with_suffix(".yaml.bak")
filepath.rename(backup)
print(f"Backed up corrupted file to {backup}")
return {"category": filepath.stem, "learnings": []}
except Exception as e:
print(f"Warning: Could not read {filepath}: {e}")
return {"category": filepath.stem, "learnings": []}
ファイルが見つからない場合
learnings ディレクトリが存在しない場合は、作成します。
def ensure_learnings_directory():
"""Create learnings directory and empty files if missing."""
learnings_dir = Path(".claude/data/learnings")
learnings_dir.mkdir(parents=True, exist_ok=True)
categories = ["errors", "workflows", "tools", "architecture", "debugging"]
for cat in categories:
filepath = learnings_dir / f"{cat}.yaml"
if not filepath.exists():
filepath.write_text(f"category: {cat}\nlearnings: []\n")
フェイルセーフの原則
学習システムは、フェイルセーフ設計に従います。
- セッションの開始を妨げない:注入に失敗しても、セッションは通常どおり続行します
- セッションの終了を妨げない:抽出に失敗しても、セッションは通常どおり終了します
- 警告をログに記録して処理を続行する:エラーはログに記録され、例外は発生しません
- 変更前にバックアップを作成する:破損したファイルは保持されます
フックとの統合
Stop フック:学習内容の抽出
Stop フックに学習内容の抽出を追加します。
# .claude/tools/amplihack/hooks/stop_hook.py
async def extract_session_learnings(transcript: str, session_id: str):
"""Extract learnings from session transcript at stop."""
from pathlib import Path
import yaml
from datetime import datetime
# Only extract if session was substantive (not just a quick question)
if len(transcript) < 1000:
return
# Use Claude to extract insights (simplified example)
extraction_prompt = f"""
Analyze this session transcript and extract any reusable learnings.
Categories:
- errors: Error patterns and solutions
- workflows: Process improvements
- tools: Tool usage insights
- architecture: Design decisions
- debugging: Debug strategies
For each learning, provide:
- category (one of the above)
- keywords (3-5 searchable terms)
- summary (one sentence)
- insight (detailed explanation)
- example (code if applicable)
- confidence (0.5-1.0)
Transcript:
{transcript[:5000]} # Truncate for token limits
"""
# ... call Claude to extract ...
# ... parse response and add to appropriate YAML files ...
def on_stop(session_data: dict):
"""Stop hook entry point."""
# ... other stop hook logic ...
# Extract learnings (non-blocking)
try:
import asyncio
asyncio.create_task(
extract_session_learnings(
session_data.get("transcript", ""),
session_data.get("session_id", "")
)
)
except Exception as e:
print(f"Learning extraction failed (non-blocking): {e}")
セッション開始フック:学習内容の注入
セッション開始フックに学習内容の注入を追加します。
# .claude/tools/amplihack/hooks/session_start_hook.py
def inject_relevant_learnings(initial_prompt: str) -> str:
"""Find and format relevant learnings for injection."""
from pathlib import Path
import yaml
learnings_dir = Path(".claude/data/learnings")
if not learnings_dir.exists():
return ""
# Extract keywords from prompt
prompt_lower = initial_prompt.lower()
task_keywords = set()
for word in prompt_lower.split():
if len(word) > 3: # Skip short words
task_keywords.add(word.strip(".,!?"))
# Find matching learnings
matches = []
for yaml_file in learnings_dir.glob("*.yaml"):
if yaml_file.name.startswith("_"):
continue # Skip _stats.yaml
try:
data = yaml.safe_load(yaml_file.read_text())
for learning in data.get("learnings", []):
learning_keywords = set(k.lower() for k in learning.get("keywords", []))
overlap = task_keywords & learning_keywords
if overlap:
score = len(overlap) * learning.get("confidence", 0.5)
matches.append((score, learning))
except Exception:
continue
# Return top 3 matches
matches.sort(key=lambda x: x[0], reverse=True)
if not matches:
return ""
context = "## Past Learnings Relevant to This Task\n\n"
for score, learning in matches[:3]:
context += f"### {learning.get('summary', 'Insight')}\n"
context += f"{learning.get('insight', '')}\n\n"
return context
def on_session_start(session_data: dict) -> dict:
"""Session start hook entry point."""
initial_prompt = session_data.get("prompt", "")
# Inject relevant learnings
try:
learning_context = inject_relevant_learnings(initial_prompt)
if learning_context:
session_data["injected_context"] = learning_context
except Exception as e:
print(f"Learning injection failed (non-blocking): {e}")
return session_data
制限事項
- キーワードマッチングは完璧ではありません - 関連する学習内容を見落としたり、無関係な内容にマッチしたりする場合があります
- 意味的な理解は行いません - 表現が異なるものの概念的に似ている知見にはマッチできません
- 保存先はローカルです - 学習内容はマシン間で同期されません
- 手動での整理が必要です - 古い学習内容や誤った学習内容は定期的に確認する必要があります
今後の改善
必要に応じて、以下を検討してください。
- より適切なマッチングのための埋め込みベースの類似度判定
- git によるマシン間同期
- 時間の経過に伴う確信度の自動低下
- グラフベースの学習関係を実現するための Neo4j との統合
成功指標
有効性を追跡します。
- 注入率:関連する学習内容が注入されたセッションの割合
- 利用率:注入された学習内容が問題解決に役立った頻度
- 増加率:週ごとの新しい学習内容の数
- 品質:学習内容の関連性に関するユーザーのフィードバック
コメント
Markdown 対応