Files
topoteretes--cognee/examples/python/truth_subspace_reranking_demo.py
wehub-resource-sync c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:02:24 +08:00

178 lines
7.5 KiB
Python

"""Truth-Subspace Reranking — runnable demo.
What it shows
-------------
A finished "session" teaches the system a preference (here: the user cares about
*coffee*, not tea). We distill that into deterministic centroid slots, project
every corpus chunk onto those slots, and store the per-chunk coordinates on the
graph node. At query time the HYBRID retriever reads current-epoch coordinates
and nudges ranking toward the learned preference.
The demo retrieves the SAME ambiguous query twice — once with truth weighting OFF
(exact baseline) and once ON — and prints a side-by-side rank diff so you can see
the coffee chunks rise.
Requirements
------------
- An LLM + embeddings provider configured (e.g. LLM_API_KEY in your .env). cognify
and retrieval call the embedding/LLM APIs.
Run it
------
python examples/python/truth_subspace_reranking_demo.py
It uses a dedicated dataset ("truth_subspace_demo") and does NOT prune, so it will
not touch your other cognee data. Re-running is safe (adds are content-addressed,
the subspace build is a full recompute).
"""
import asyncio
import os
import sys
# Run against THIS checkout of cognee even if a different copy is pip-installed,
# so the truth-subspace code in this working tree is the one that executes.
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
import cognee
from cognee.context_global_variables import set_database_global_context_variables
from cognee.modules.data.methods import get_authorized_existing_datasets
from cognee.modules.retrieval.hybrid.results import payload, result_id
from cognee.modules.retrieval.hybrid_retriever import HybridRetriever
from cognee.modules.truth_subspace.build import build_truth_subspace
from cognee.modules.users.methods import get_default_user
DATASET = "truth_subspace_demo"
CORPUS_NODE_SET = ["beverages"]
LESSONS_NODE_SET = ["session_learnings"] # the node set build_truth_subspace reads learnings from
# A small two-theme corpus. Each short doc becomes its own chunk.
CORPUS = [
"Espresso is brewed by forcing hot water through finely ground coffee under high pressure.",
"A pour-over coffee drips a slow stream of hot water over a paper filter of ground coffee.",
"Cold brew coffee steeps coarse coffee grounds in cold water for twelve hours or more.",
"A French press steeps coffee grounds in hot water, then a metal plunger separates them.",
"Green tea is brewed with water below boiling to avoid a bitter, astringent flavor.",
"Black tea is steeped in fully boiling water for three to five minutes before serving.",
"Herbal tisanes are caffeine-free infusions of dried herbs, flowers, and dried fruit.",
"Matcha is a powdered green tea whisked into hot water with a bamboo whisk until frothy.",
]
# What a finished session "learned" about the user. These fill the truth centroid
# slots. They are about coffee, so coffee chunks align more strongly.
LESSONS = [
"The user is a dedicated coffee drinker who cares about espresso extraction and pour-over technique.",
"We learned the user wants coffee brewing recommendations specifically, and is not interested in tea.",
"For this user, coffee details — grind size, water temperature, and bloom time — matter most.",
]
QUERY = "How should I prepare my morning drink at home?"
# Number of chunks to rank.
TOP_K = len(CORPUS)
def _theme(text: str) -> str:
coffee = ("coffee", "espresso", "pour-over", "french press", "cold brew")
return "☕ coffee" if any(w in text.lower() for w in coffee) else "🍵 tea "
def _snippet(text: str, width: int = 62) -> str:
text = " ".join(text.split())
return text if len(text) <= width else text[: width - 1] + "…"
async def ranked_chunks(dataset_obj, query: str, use_truth_weight: bool):
"""Return the hybrid retriever's ranked chunk dicts, within the dataset's DB context."""
async with set_database_global_context_variables(dataset_obj.id, dataset_obj.owner_id):
retriever = HybridRetriever(
chunks_top_k=TOP_K,
entities_top_k=0, # focus the demo on chunk-lane reranking
facts_top_k=0,
node_name=CORPUS_NODE_SET, # rank only the corpus, not the lesson chunks
use_truth_weight=use_truth_weight,
)
objects = await retriever.get_retrieved_objects(query=query)
return objects.get("chunks", [])
def _text(chunk) -> str:
return payload(chunk).get("text", "")
def print_ranking(title: str, chunks: list):
print(f"\n{title}")
print(" " + "-" * 74)
for i, chunk in enumerate(chunks, 1):
text = _text(chunk)
print(f" {i:>2}. {_theme(text)} {_snippet(text)}")
def print_diff(baseline: list, truthful: list):
base_rank = {result_id(c): i for i, c in enumerate(baseline, 1)}
print("\nRANK CHANGE WITH TRUTH WEIGHTING ON (vs baseline)")
print(" " + "-" * 74)
for new_rank, chunk in enumerate(truthful, 1):
cid = result_id(chunk)
old = base_rank.get(cid)
if old is None:
delta = " new"
elif old > new_rank:
delta = f" ↑{old - new_rank}"
elif old < new_rank:
delta = f" ↓{new_rank - old}"
else:
delta = " ="
text = _text(chunk)
print(f" {new_rank:>2}. {_theme(text)} {_snippet(text, 52)}{delta}")
async def main():
print("=" * 78)
print("Truth-Subspace Reranking demo")
print("=" * 78)
# 1) Ingest the corpus and build the knowledge graph.
print(f"\n[1/5] Adding {len(CORPUS)} corpus docs and cognifying (dataset='{DATASET}')…")
await cognee.add(CORPUS, dataset_name=DATASET, node_set=CORPUS_NODE_SET)
await cognee.cognify(datasets=[DATASET])
user = await get_default_user()
datasets = await get_authorized_existing_datasets([DATASET], "write", user)
dataset_obj = datasets[0]
# 2) Baseline ranking — truth weighting OFF (exact current behavior).
print(f"\n[2/5] Baseline retrieval (truth weighting OFF) for:\n{QUERY}”")
baseline = await ranked_chunks(dataset_obj, QUERY, use_truth_weight=False)
print_ranking("BASELINE RANKING", baseline)
# 3) A finished session's learnings → seed the session_learnings node set.
print(f"\n[3/5] Recording {len(LESSONS)} session learnings (favoring coffee)…")
await cognee.add(LESSONS, dataset_name=DATASET, node_set=LESSONS_NODE_SET)
await cognee.cognify(datasets=[DATASET])
# 4) Build the truth subspace: lesson centroids -> coords on every corpus chunk.
print("\n[4/5] Building the truth subspace (build_truth_subspace)…")
result = await build_truth_subspace(dataset=DATASET, session_ids=None, user=user)
print(f" centroid_slots={result['anchors']} nodes_scored={result['nodes_scored']}")
if result["anchors"] == 0 or result["nodes_scored"] == 0:
print(" ⚠ No centroid slots or no scored nodes — truth weighting will be a no-op.")
# 5) Truth-weighted ranking — same query, truth weighting ON.
print("\n[5/5] Retrieval with truth weighting ON for the same query…")
truthful = await ranked_chunks(dataset_obj, QUERY, use_truth_weight=True)
print_ranking("TRUTH-WEIGHTED RANKING", truthful)
print_diff(baseline, truthful)
base_top = _theme(_text(baseline[0])) if baseline else "?"
truth_top = _theme(_text(truthful[0])) if truthful else "?"
print("\n" + "=" * 78)
print(f"Top result moved from {base_top.strip()}{truth_top.strip()}")
print("The learned coffee preference reshaped retrieval ordering. ✔")
print("=" * 78)
if __name__ == "__main__":
asyncio.run(main())