AIエージェントのナレッジグラフ — Case MemoryとPattern DBの関係性可視化
約6分で読めます
AIエージェントが蓄積するCase Memory(事例記録)とPattern DB(パターンデータベース)は、適切に構造化しなければ知識の死蔵が起こる。ナレッジグラフとして関係性を可視化することで、過去の経験を効率的に検索・再利用し、エージェントの判断品質を継続的に向上させることができる。本記事では、知識の構造化からグラフの構築、クエリの最適化まで実践的に解説する。
Case Memoryの構造設計
Case Memoryは、エージェントが過去に遭遇した問題とその解決策を記録するデータストアである。各ケースを構造化して保存することで、類似状況での検索精度が向上する。
interface CaseMemoryEntry {
id: string;
timestamp: string;
category: string;
tags: string[];
problem: {
description: string;
context: Record<string, unknown>;
errorType?: string;
};
solution: {
approach: string;
steps: string[];
outcome: "success" | "partial" | "failure";
};
metrics: {
resolutionTimeMs: number;
tokensUsed: number;
retryCount: number;
};
relatedCases: string[];
}
class CaseMemoryStore {
private cases: Map<string, CaseMemoryEntry> = new Map();
add(entry: CaseMemoryEntry): void {
this.cases.set(entry.id, entry);
}
findSimilar(problem: string, limit: number = 5): CaseMemoryEntry[] {
const scored = [...this.cases.values()].map((c) => ({
case: c,
similarity: this.cosineSimilarity(problem, c.problem.description),
}));
return scored
.sort((a, b) => b.similarity - a.similarity)
.slice(0, limit)
.map((s) => s.case);
}
private cosineSimilarity(a: string, b: string): number {
// 簡易的なJaccard類似度で代用
const setA = new Set(a.split(/\s+/));
const setB = new Set(b.split(/\s+/));
const intersection = new Set([...setA].filter((x) => setB.has(x)));
const union = new Set([...setA, ...setB]);
return intersection.size / union.size;
}
}
ケースの自動分類
新しいケースが追加されるたびに、AIが自動的にカテゴリとタグを付与する仕組みを実装する。
Pattern DBの設計と運用
Pattern DBは、Case Memoryから抽出された汎用的なパターンを管理するデータベースである。個別のケースから共通パターンを抽出し、再利用可能な形で保存する。
interface PatternEntry {
id: string;
name: string;
description: string;
trigger: {
conditions: string[];
frequency: number;
};
template: {
approach: string;
steps: string[];
caveats: string[];
};
sourceCase: string[];
confidence: number;
lastUsed: string;
useCount: number;
}
function extractPatterns(cases: CaseMemoryEntry[]): PatternEntry[] {
// 類似ケースをクラスタリング
const clusters = clusterCases(cases);
return clusters.map((cluster) => ({
id: generateId(),
name: inferPatternName(cluster),
description: summarizeCluster(cluster),
trigger: {
conditions: extractCommonConditions(cluster),
frequency: cluster.length,
},
template: {
approach: findMostEffectiveApproach(cluster),
steps: mergeSteps(cluster),
caveats: collectCaveats(cluster),
},
sourceCase: cluster.map((c) => c.id),
confidence: calculateConfidence(cluster),
lastUsed: new Date().toISOString(),
useCount: 0,
}));
}
ナレッジグラフの構築
Case MemoryとPattern DBの関係性をグラフ構造で表現する。ノードはケースやパターンを、エッジは関係性(類似、派生、依存)を表す。
interface KnowledgeNode {
id: string;
type: "case" | "pattern" | "category" | "tag";
label: string;
properties: Record<string, unknown>;
}
interface KnowledgeEdge {
source: string;
target: string;
type: "similar_to" | "derived_from" | "depends_on" | "tagged_with" | "belongs_to";
weight: number;
}
class KnowledgeGraph {
private nodes: Map<string, KnowledgeNode> = new Map();
private edges: KnowledgeEdge[] = [];
addNode(node: KnowledgeNode): void {
this.nodes.set(node.id, node);
}
addEdge(edge: KnowledgeEdge): void {
this.edges.push(edge);
}
// 特定ノードから距離N以内の関連ノードを取得
getNeighbors(nodeId: string, depth: number = 2): KnowledgeNode[] {
const visited = new Set<string>();
const queue: Array<{ id: string; currentDepth: number }> = [
{ id: nodeId, currentDepth: 0 },
];
while (queue.length > 0) {
const { id, currentDepth } = queue.shift()!;
if (visited.has(id) || currentDepth > depth) continue;
visited.add(id);
const connected = this.edges
.filter((e) => e.source === id || e.target === id)
.map((e) => (e.source === id ? e.target : e.source));
for (const connId of connected) {
if (!visited.has(connId)) {
queue.push({ id: connId, currentDepth: currentDepth + 1 });
}
}
}
visited.delete(nodeId);
return [...visited].map((id) => this.nodes.get(id)!).filter(Boolean);
}
}
グラフクエリによる知識検索
ナレッジグラフに対するクエリを最適化し、エージェントが適切なタイミングで過去の知識を活用できるようにする。
関連知識の自動注入
async function enrichContextWithKnowledge(
graph: KnowledgeGraph,
currentProblem: string,
maxResults: number = 3
): Promise<string> {
// 問題文からキーワードを抽出
const keywords = extractKeywords(currentProblem);
// キーワードに一致するノードを検索
const matchingNodes = keywords.flatMap((kw) =>
graph.searchNodes(kw)
);
// 関連するパターンとケースを取得
const relatedKnowledge = matchingNodes
.flatMap((node) => graph.getNeighbors(node.id, 2))
.filter((n) => n.type === "pattern" || n.type === "case");
// 重複排除とスコアリング
const unique = [...new Map(relatedKnowledge.map((n) => [n.id, n])).values()];
const topResults = unique.slice(0, maxResults);
// コンテキストとして整形
return topResults
.map((n) => {
if (n.type === "pattern") {
return "関連パターン: " + n.label + "\n" + JSON.stringify(n.properties);
}
return "類似ケース: " + n.label + "\n" + JSON.stringify(n.properties);
})
.join("\n\n");
}
グラフの可視化
蓄積された知識の全体像を把握するため、D3.jsやCytoscape.jsを使ったインタラクティブな可視化を実装する。
可視化のポイント
| 要素 | 表現方法 | 情報 |
|---|---|---|
| ケースノード | 円形 | 解決結果に応じた色分け |
| パターンノード | 六角形 | 信頼度に応じたサイズ |
| カテゴリノード | 四角形 | 含まれるケース数 |
| エッジの太さ | 線の太さ | 関連度の強さ |
| クラスター | 背景色 | 自動グルーピング |
知識の陳腐化対策
時間の経過とともに古い知識が不正確になる問題に対処する。
TTL(Time To Live)の設定
interface KnowledgeWithTTL {
entry: CaseMemoryEntry | PatternEntry;
createdAt: string;
lastValidated: string;
ttlDays: number;
isExpired: boolean;
}
function validateKnowledge(
entries: KnowledgeWithTTL[]
): { valid: KnowledgeWithTTL[]; expired: KnowledgeWithTTL[] } {
const now = Date.now();
const valid: KnowledgeWithTTL[] = [];
const expired: KnowledgeWithTTL[] = [];
for (const entry of entries) {
const lastValidated = new Date(entry.lastValidated).getTime();
const ageInDays = (now - lastValidated) / (1000 * 60 * 60 * 24);
if (ageInDays > entry.ttlDays) {
expired.push({ ...entry, isExpired: true });
} else {
valid.push(entry);
}
}
return { valid, expired };
}
知識グラフの運用メトリクス
| メトリクス | 説明 | 目標値 |
|---|---|---|
| ノード数 | グラフ内の総ノード数 | 継続的増加 |
| 再利用率 | パターンが実際に使用された割合 | 40%以上 |
| 検索ヒット率 | 問題に対して関連知識が見つかった割合 | 70%以上 |
| 解決時間短縮 | 知識活用時と未活用時の解決時間差 | 30%以上短縮 |
| 陳腐化率 | 期限切れの知識の割合 | 20%以下 |
ベストプラクティス
- 小さく始める: まず手動でケースを記録し、パターンが見えてきたら自動化する
- 定期的な棚卸し: 月次で使用頻度の低い知識をアーカイブまたは削除する
- フィードバックループ: パターンが使用されるたびに成功/失敗を記録し、信頼度を更新する
- コンテキスト長の管理: グラフから注入する知識量がコンテキストウィンドウを圧迫しないよう制限する
関連記事
- AIエージェントの自己改善ループ - 継続的改善の仕組み
- AIエージェントのメモリ管理 - 長期記憶と短期記憶の設計
- AIエージェントの継続学習 - 運用中の学習メカニズム
- CLAUDE.mdベストプラクティス - 設定ファイルの最適な構成
A
Agentive 編集部
AIエージェントを実際に使い倒す個人開発者。サイト制作の自動化を実践しながら、その知見を発信しています。