Agentive
AIエージェント活用

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%以下

ベストプラクティス

  1. 小さく始める: まず手動でケースを記録し、パターンが見えてきたら自動化する
  2. 定期的な棚卸し: 月次で使用頻度の低い知識をアーカイブまたは削除する
  3. フィードバックループ: パターンが使用されるたびに成功/失敗を記録し、信頼度を更新する
  4. コンテキスト長の管理: グラフから注入する知識量がコンテキストウィンドウを圧迫しないよう制限する

関連記事

A

Agentive 編集部

AIエージェントを実際に使い倒す個人開発者。サイト制作の自動化を実践しながら、その知見を発信しています。