gists

Cognee についての対話メモ

このリポジトリは何してる?

Cognee は、生データを永続的な知識グラフに変換するオープンソース AI メモリプラットフォーム。従来の RAG(検索拡張生成)を、ベクトル検索・グラフ DB・LLM によるエンティティ抽出を組み合わせた ECL(Extract, Cognify, Load)パイプラインで置き換える。

主要フロー: add()(データ取り込み)→ cognify()(エンティティ/関係を抽出してグラフ構築)→ search()(多様な検索戦略で問い合わせ)→ memify()(グラフを追加文脈で拡充)

特徴:


インデックス化が要るってことですね

はい。生データをそのまま検索するのではなく、事前に インデックス化(Cognee では cognify() が必要。具体的には:

  1. チャンク分割 - ドキュメントを処理単位に分割
  2. エンティティ/関係抽出 - LLM で「誰が・何を・どうした」を構造化(知識グラフ化)
  3. 埋め込み生成 - ベクトル DB に格納(意味検索用)
  4. グラフ DB に格納 - ノードとエッジの関係性を保持

従来の RAG が「チャンク + ベクトル検索」だけなのに対し、Cognee はここに 知識グラフ層 を追加することで、エンティティ間の関係をたどった検索(GRAPH_COMPLETION など)を可能にしているのが差別化ポイント。


どういうときに使うん?PKM?それとも DB を持つような製品向け?

両方ありえるが、設計思想的には 後者(AI エージェント/製品に組み込む長期メモリ基盤) 寄り。

本命ユースケース(製品組み込み)

PKM 的な使い方も可能だが…

見分け方

「複数ユーザー・データセット分離・API で外から叩く」要件があれば製品向け、単に自分のメモを検索したいだけなら Obsidian+RAG の方が軽い。


どういうグラフにするの?ノードとエッジは?

ノード(Node)

LLM がテキストから抽出する 名前付きエンティティ が基本単位。フィールド:

type は固定ではなく LLM が動的に命名(大文字)。典型例:

加えて、ドキュメント構造由来のノードも作られる:

エッジ(Edge)

(source_node_id, target_node_id, relationship_name) の三つ組。

LLM 抽出の関係例(自由形式で動的に生成):

構造的に固定の関係(コード上で定義済み):

イメージ

Document ──has_chunk──> DocumentChunk ──mentions──> Entity(Einstein, PERSON)
                                                        │
                                                        └─developed─> Entity(Relativity, CONCEPT)

→「ドキュメント構造の層 + LLM 抽出の意味ネットワーク層」が重なったグラフ。検索時は両層をまたいで辿れるのが肝。


LLM はどうやって抽出してるの?たぶんプロンプトとかで指示してんだよね?

そう。プロンプト + 構造化出力強制の合わせ技。中核は 3 点。

1. システムプロンプト(ルール指示)

generate_graph_prompt.txt が全体方針を指示:

2. Pydantic スキーマで出力形式を縛る

プロンプトだけだと JSON がブレるので、KnowledgeGraph モデル(nodes: List[Node], edges: List[Edge])を response_model として渡す。

content_graph = await LLMGateway.acreate_structured_output(
    content, system_prompt, response_model, **kwargs
)

内部は Instructor(または BAML)ライブラリがスキーマを tool_call/json_schema に変換し、LLM の出力を自動バリデーション。違反したら再試行。

3. チャンク単位で並列抽出 → 統合

extract_graph_from_data.py で全チャンクに対し asyncio.gather で並列実行。その後:

選べるプロンプトバリエーション

LLM_CONFIG.graph_prompt_path で切替可、custom_prompt 引数で完全上書きも可能。


あまり多様性がない、典型的なオントロジーになりそう。Cosense の [Cogneeのエッジ] みたいな自由度はないよね?

その通り。

Cosense との根本差

  Cosense Cognee
ノード化の主体 ユーザー[foo] と書けば即ノード) LLM(抽出アルゴリズムが判断)
粒度の自由度 任意(思いつき・造語 OK) エンティティ扱いできる名詞句に限定
無型(ページは全部等価) PERSON/CONCEPT 等に分類される
関係 双方向リンク(暗黙) 有向 + 名前付き(works_at 等)

Cosense の「[Cogneeのエッジ] と書けばそれがページ化される」は、ユーザーの意図でノードを立てる モデル。Cognee は逆で、プロンプトに「Person のような基本型に寄せろ」「あまり細かい型を作るな」と明示指示があるので、むしろ 多様性を抑えて収束させる設計 になっている。

さらに収束を強める要素

→ 結果として、Wikipedia 的な「固有名詞 + 明確な関係」グラフ に収束しがち。Cosense の「造語や概念ラベルが自由に飛び交う有機的ネット」にはならない。

抜け道

ここで「[brackets] で囲まれたテキストを無条件でノード化しろ」というプロンプト + スキーマを書けば、Cosense 風の挙動を再現することは可能。ただし Cognee の想定ユースケースではない。

要は、Cognee は 「曖昧な文章から機械的に構造を取り出す」側、Cosense は 「人間の連想をそのまま残す」側 で、思想が正反対。


Cognee だと 1 万ノードには耐えられなそうだね、いや可能か?

1 万ノードは 余裕で可能。むしろボトルネックは別の場所にある。

ストレージ層は 1 万ノード程度では問題なし

デフォルトバックエンド:

1 万ノード + 関連エッジ・ベクトルは、むしろ「小規模」の部類。Neo4j/PostgreSQL に切り替えれば数億ノードまで伸ばせる。

本当のボトルネックは 3 つ

1. Cognify 時の LLM コスト(最大の壁)

1 万エンティティを抽出するには、その背後に 数千〜数万チャンク があり、各チャンクで LLM 呼び出しが発生。extract_graph_from_data.pyasyncio.gather で並列化しているが:

2. オントロジー解決の fuzzy マッチ(O(N²) リスク)

既存エンティティとの類似度マッチが 1 万ノード規模だと効いてくる。retrieve_existing_edgesexpand_with_nodes_and_edges の実装次第では、ノード数に対して線形以上に遅くなる可能性あり。

3. 検索時のグラフ走査

GRAPH_COMPLETION 系は関連ノードを辿って LLM に渡すので、深さ制御 をしないと関連ノードが爆発してコンテキスト溢れを起こす。

現実的な目安

「耐えられない」のは ストレージじゃなくて LLM 課金と cognify 時間 の方、というのが正確な答え。


どちらかというとデータ量の多さに耐えるよりも、リッチなグラフをつくることでパフォーマンスとセマンティック検索の便宜を図る意図?

その理解が正確。Cognee は 「取り込み時に高コストを払って、検索時の質を稼ぐ」 アーキテクチャ。

設計思想の証拠

  1. cognify 段階で LLM を大量消費 してまでエンティティ・関係・要約・カテゴリを多層で持つ → ただ検索が速くなるだけならこんな前処理は不要
  2. 検索タイプが 14 種類GRAPH_COMPLETION/TRIPLET/CHUNKS/TEMPORAL/CYPHER…)→ 同じデータを多角的に引く前提
  3. オントロジー解決 でエンティティを正規化 → 純粋なベクトル検索なら不要な手間
  4. ドキュメント層 + エンティティ層の二重グラフ →「チャンクに触れずに概念レベルで辿る」ことを可能にする

トレードオフが明確

  ベクトル RAG Cognee
取り込みコスト 低(埋め込みのみ) 高(LLM 抽出 × チャンク数)
スケール上限 データ量 LLM 予算
強み 「似た文を探す」 「概念の関係を辿る」

なので「10 億ドキュメントの全文検索基盤」ではなく、「中規模コーパスを濃密な意味ネットワークに変換して AI エージェントに与える」 のが本命用途。