
Les Scripts Python
FETCH Ouroboros
đ§ Pour ta question sur la "commande unique"
Ta crainte est bonne : trop automatiser peut compliquer le debug.
Donc je recommande une version simple :
Créer un alias dans ton terminal.
Dans ton terminal :
nano ~/.bashrc
Ajoute :
alias ouros='source ~/ouroboros-env/bin/activate && cd ~/ouroboros-desktop/ouroboros/tools'
Sauvegarde.
Puis recharge :
source ~/.bashrc
Maintenant tu peux faire :
ouros
et tu arrives directement ici :
(ouroboros-env)
~/ouroboros-desktop/ouroboros/tools
Les 4 outils dont Ouroboros a vraiment besoin
1. fetch_url
RécupÚre :
- URL
- status
- content-type
- title
- texte nettoyé
2. read_sitemap
Lit le sitemap et retourne les URLs.
3. classify_resource
Détermine si la ressource est :
- page
- image
- repository
- sitemap
- unknown
4. extract_module_candidate
Prend une page et propose :
- titre
- concepts clés
- rĂŽle cognitif
- module potentiel
- liens avec d'autres modules
Et lĂ , on entre vraiment dans la logique de cerveau modulaire externe.
Ce que ça donnerait dans ton systÚme
Ătape A â Ingestion
Ouroboros lit :
- https://www.agdistys.fr/esprit-critique
- https://www.agdistys.fr/l/lia
- https://www.agdistys.fr/l/serment-et-charte
Ătape B â Classification
Il classe :
- Esprit critique â module de protection discursive
- ConScience de l'IA â module d'orientation relationnelle
- Charte â module Ă©thique central
Ătape C â Atlas
Il relie les modules dans une carte :
- protection
- réparation
- orientation
- détection
- navigation
Ătape D â Activation
Quand une situation arrive, il active le bon module.
Donc : oĂč ça finit ?
Fin de la fondation
Ăa finit ici :
- ingestion
- classification
- extraction
- atlas
- routing
- orchestration
AprÚs ça, la fondation est posée.
Ensuite commence la phase 2
Ce n'est plus "construire les briques", c'est "les rendre intelligentes" :
- meilleurs heuristiques de modules
- meilleurs liens d'atlas
- radar du spectre
- branchement vers review.py
- activation contextuelle dans les réponses
Ordre conseillé maintenant
Je ferais ça dans cet ordre :
- fetch_url.py
- read_sitemap.py
- classify_resource.py
- extract_module_candidate.py
- build_cognitive_atlas.py
- cognitive_router.py
- ingest_site.py
Et lĂ , stop : la v1 existe.
La vraie réponse courte à ta question
đ Ăa finit quand Ouroboros peut :
- lire ton site
- repérer ce qu'il lit
- transformer ça en modules
- relier les modules
- choisir lequel activer selon le contexte
AprĂšs ça, vous n'ĂȘtes plus en train de "prĂ©parer" le cerveau.
Vous avez un premier cerveau modulaire fonctionnel.
Si tu veux, le prochain message peut ĂȘtre un pack ultra propre oĂč je te regroupe tous les fichiers dans un seul bloc ordonnĂ©, avec l'ordre exact Ă donner Ă Ouroboros.
1ïžâŁ Active ton environnement virtuel
Comme tu as rouvert le terminal, il faut réactiver le venv :
source ~/ouroboros-env/bin/activate
Tu devrais voir :
(ouroboros-env) MacBook-Pro-de-Agdistys:tools agdistys$
2ïžâŁ VĂ©rifie que tu es dans le bon dossier
Tu peux vérifier avec :
pwd
Tu dois voir :
/Users/agdistys/ouroboros-desktop/ouroboros/tools
Si jamais tu n'y étais plus, la commande est :
cd ~/ouroboros-desktop/ouroboros/tools
3ïžâŁ Maintenant tu peux crĂ©er le fichier
4ïžâŁ Petit repĂšre mental (trĂšs utile)
Chaque fois que tu ouvres un nouveau terminal pour ce projet, fais toujours ces deux commandes :
source ~/ouroboros-env/bin/activate
cd ~/ouroboros-desktop/ouroboros/tools
Ăa te remet directement dans le bon environnement + le bon dossier.
Fetch_url.py
from __future__ import annotations
import re
from typing import Any, Dict, Optional
import requests
from bs4 import BeautifulSoup
from requests import Response
DEFAULT_TIMEOUT = 15
DEFAULT_USER_AGENT = (
"OuroborosWebIngest/0.1 (+https://www.agdistys.fr; "
"purpose=research-and-ingestion)"
)
def _normalize_whitespace(text: str) -> str:
"""Collapse repeated whitespace and trim the result."""
return re.sub(r"\s+", " ", text).strip()
def _extract_title(soup: BeautifulSoup) -> Optional[str]:
"""Extract a readable page title if present."""
if soup.title and soup.title.string:
title = _normalize_whitespace(soup.title.string)
return title or None
return None
def _clean_html_to_text(html: bytes) -> tuple[Optional[str], str]:
"""
Parse HTML, remove non-content tags, and return:
(title, cleaned_text)
"""
soup = BeautifulSoup(html, "html.parser")
for tag in soup(["script", "style", "noscript"]):
tag.decompose()
title = _extract_title(soup)
text = soup.get_text(separator=" ", strip=True)
cleaned = _normalize_whitespace(text)
return title, cleaned
def _build_success_result(
*,
url: str,
response: Response,
title: Optional[str],
content: str,
) -> Dict[str, Any]:
return {
"status": "success",
"url": url,
"title": title,
"content": content,
"content_type": response.headers.get("Content-Type"),
"status_code": response.status_code,
"error": None,
}
def _build_error_result(
*,
url: str,
error: str,
status_code: Optional[int] = None,
content_type: Optional[str] = None,
) -> Dict[str, Any]:
return {
"status": "error",
"url": url,
"title": None,
"content": "",
"content_type": content_type,
"status_code": status_code,
"error": error,
}
def fetch_url(url: str, timeout: int = DEFAULT_TIMEOUT) -> Dict[str, Any]:
"""
Fetch a web page and return a structured object with cleaned text.
Returns a dict with:
- status: "success" or "error"
- url: original URL
- title: extracted page title or None
- content: cleaned readable text
- content_type: HTTP Content-Type header if available
- status_code: HTTP status code if available
- error: error message or None
"""
headers = {
"User-Agent": DEFAULT_USER_AGENT,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
try:
response = requests.get(
url,
headers=headers,
timeout=timeout,
allow_redirects=True,
)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "").lower()
if "html" not in content_type and "xml" not in content_type:
return _build_error_result(
url=url,
error=f"Unsupported content type: {content_type or 'unknown'}",
status_code=response.status_code,
content_type=response.headers.get("Content-Type"),
)
title, content = _clean_html_to_text(response.content)
return _build_success_result(
url=url,
response=response,
title=title,
content=content,
)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else None
content_type = (
exc.response.headers.get("Content-Type")
if exc.response is not None
else None
)
return _build_error_result(
url=url,
error=f"HTTP error: {exc}",
status_code=status_code,
content_type=content_type,
)
except requests.RequestException as exc:
return _build_error_result(
url=url,
error=f"Request error: {exc}",
)
except Exception as exc:
return _build_error_result(
url=url,
error=f"Unexpected error: {exc}",
)
Read_sitemap.py
from __future__ import annotations
from typing import Any, Dict, List, Optional
import xml.etree.ElementTree as ET
import requests
DEFAULT_TIMEOUT = 15
DEFAULT_USER_AGENT = (
"OuroborosWebIngest/0.1 (+https://www.agdistys.fr; "
"purpose=research-and-ingestion)"
)
SITEMAP_NS = {"sm": "https://www.sitemaps.org/schemas/sitemap/0.9"}
def _build_success_result(
*,
url: str,
sitemap_type: str,
urls: List[str],
status_code: int,
content_type: Optional[str],
) -> Dict[str, Any]:
return {
"status": "success",
"url": url,
"sitemap_type": sitemap_type,
"urls": urls,
"count": len(urls),
"content_type": content_type,
"status_code": status_code,
"error": None,
}
def _build_error_result(
*,
url: str,
error: str,
status_code: Optional[int] = None,
content_type: Optional[str] = None,
) -> Dict[str, Any]:
return {
"status": "error",
"url": url,
"sitemap_type": None,
"urls": [],
"count": 0,
"content_type": content_type,
"status_code": status_code,
"error": error,
}
def _local_name(tag: str) -> str:
"""Return the local XML tag name without namespace."""
if "}" in tag:
return tag.split("}", 1)[1]
return tag
def _parse_sitemap_xml(xml_bytes: bytes) -> tuple[str, List[str]]:
"""
Parse sitemap XML and return:
(sitemap_type, urls)
Supports:
- urlset
- sitemapindex
"""
root = ET.fromstring(xml_bytes)
root_name = _local_name(root.tag)
if root_name == "urlset":
urls = [
loc.text.strip()
for loc in root.findall(".//sm:url/sm:loc", SITEMAP_NS)
if loc.text and loc.text.strip()
]
return "urlset", urls
if root_name == "sitemapindex":
urls = [
loc.text.strip()
for loc in root.findall(".//sm:sitemap/sm:loc", SITEMAP_NS)
if loc.text and loc.text.strip()
]
return "sitemapindex", urls
raise ValueError(f"Unsupported sitemap root element: {root_name}")
def read_sitemap(url: str, timeout: int = DEFAULT_TIMEOUT) -> Dict[str, Any]:
"""
Read a sitemap and return a structured result.
Returns a dict with:
- status: "success" or "error"
- url: original sitemap URL
- sitemap_type: "urlset" | "sitemapindex" | None
- urls: extracted URLs
- count: number of extracted URLs
- content_type: HTTP Content-Type if available
- status_code: HTTP status code if available
- error: error message or None
"""
headers = {
"User-Agent": DEFAULT_USER_AGENT,
"Accept": "application/xml,text/xml;q=0.9,*/*;q=0.8",
}
try:
response = requests.get(
url,
headers=headers,
timeout=timeout,
allow_redirects=True,
)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
sitemap_type, urls = _parse_sitemap_xml(response.content)
return _build_success_result(
url=url,
sitemap_type=sitemap_type,
urls=urls,
status_code=response.status_code,
content_type=content_type,
)
except ET.ParseError as exc:
return _build_error_result(
url=url,
error=f"XML parse error: {exc}",
)
except ValueError as exc:
return _build_error_result(
url=url,
error=str(exc),
status_code=response.status_code if "response" in locals() else None,
content_type=response.headers.get("Content-Type") if "response" in locals() else None,
)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else None
content_type = (
exc.response.headers.get("Content-Type")
if exc.response is not None
else None
)
return _build_error_result(
url=url,
error=f"HTTP error: {exc}",
status_code=status_code,
content_type=content_type,
)
except requests.RequestException as exc:
return _build_error_result(
url=url,
error=f"Request error: {exc}",
)
except Exception as exc:
return _build_error_result(
url=url,
error=f"Unexpected error: {exc}",
)
classify_resource.py
from __future__ import annotations
from typing import Any, Dict, Optional
from urllib.parse import urlparse
import os
KNOWN_IMAGE_EXTENSIONS = {
".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp", ".tiff", ".ico"
}
KNOWN_PDF_EXTENSIONS = {".pdf"}
KNOWN_TEXT_EXTENSIONS = {
".html", ".htm", ".xml", ".txt", ".md", ".json"
}
def _normalize_content_type(content_type: Optional[str]) -> str:
if not content_type:
return ""
return content_type.split(";", 1)[0].strip().lower()
def _extract_extension(url: str) -> str:
path = urlparse(url).path
_, ext = os.path.splitext(path.lower())
return ext
def _is_github_repository(url: str) -> bool:
parsed = urlparse(url)
if parsed.netloc.lower() not in {"github.com", "www.github.com"}:
return False
parts = [p for p in parsed.path.split("/") if p]
return len(parts) >= 2
def _is_sitemap_url(url: str) -> bool:
path = urlparse(url).path.lower()
return path.endswith("sitemap.xml") or path.endswith(".xml")
def classify_resource(url: str, content_type: Optional[str] = None) -> Dict[str, Any]:
"""
Classify a resource from its URL and optional content type.
Returns a dict with:
- url
- resource_type: one of
"page", "pdf", "image", "repository", "sitemap", "unknown"
- content_type
- extension
- reason
"""
normalized_content_type = _normalize_content_type(content_type)
extension = _extract_extension(url)
# 1. Strong signals from content type
if normalized_content_type == "application/pdf":
return {
"url": url,
"resource_type": "pdf",
"content_type": normalized_content_type,
"extension": extension,
"reason": "content_type indicates PDF",
}
if normalized_content_type.startswith("image/"):
return {
"url": url,
"resource_type": "image",
"content_type": normalized_content_type,
"extension": extension,
"reason": "content_type indicates image",
}
if normalized_content_type in {"application/xml", "text/xml"} and _is_sitemap_url(url):
return {
"url": url,
"resource_type": "sitemap",
"content_type": normalized_content_type,
"extension": extension,
"reason": "content_type indicates XML and URL looks like sitemap",
}
if normalized_content_type.startswith("text/html") or normalized_content_type == "application/xhtml+xml":
if _is_github_repository(url):
return {
"url": url,
"resource_type": "repository",
"content_type": normalized_content_type,
"extension": extension,
"reason": "URL matches GitHub repository pattern",
}
return {
"url": url,
"resource_type": "page",
"content_type": normalized_content_type,
"extension": extension,
"reason": "content_type indicates HTML page",
}
# 2. Fallback from URL extension / structure
if extension in KNOWN_PDF_EXTENSIONS:
return {
"url": url,
"resource_type": "pdf",
"content_type": normalized_content_type or None,
"extension": extension,
"reason": "URL extension indicates PDF",
}
if extension in KNOWN_IMAGE_EXTENSIONS:
return {
"url": url,
"resource_type": "image",
"content_type": normalized_content_type or None,
"extension": extension,
"reason": "URL extension indicates image",
}
if _is_github_repository(url):
return {
"url": url,
"resource_type": "repository",
"content_type": normalized_content_type or None,
"extension": extension,
"reason": "URL matches GitHub repository pattern",
}
if _is_sitemap_url(url):
return {
"url": url,
"resource_type": "sitemap",
"content_type": normalized_content_type or None,
"extension": extension,
"reason": "URL suggests sitemap",
}
if extension in KNOWN_TEXT_EXTENSIONS:
return {
"url": url,
"resource_type": "page",
"content_type": normalized_content_type or None,
"extension": extension,
"reason": "URL extension suggests text-like page resource",
}
# 3. Default fallback
return {
"url": url,
"resource_type": "unknown",
"content_type": normalized_content_type or None,
"extension": extension,
"reason": "no strong classification signal found",
}
extract_module_candidate.py
from __future__ import annotations
from typing import Any, Dict, List, Optional
import re
MAX_SNIPPET_LENGTH = 600
KEYWORD_LIMIT = 12
STOPWORDS = {
"pour", "dans", "avec", "mais", "nous", "vous", "ils", "elles", "elle", "il",
"les", "des", "une", "un", "plus", "bien", "tout", "ĂȘtre", "faire", "sont",
"ont", "cela", "comme", "leur", "leurs", "notre", "votre", "ainsi", "donc",
"peut", "sans", "entre", "veux", "voir", "cette", "celui", "celle",
"encore", "aussi", "tous", "toutes", "chaque", "depuis", "contre", "vers",
"contact", "prestations", "articles", "ressources", "témoignages",
"publication", "instagram", "comment", "vieux", "mĂȘme", "quand",
"devient", "suis"
}
SPECIAL_KEYWORDS = {
"art",
"vie",
"sens",
}
def _normalize(text: str) -> str:
return re.sub(r"\s+", " ", text).strip()
def _extract_keywords(title: Optional[str], text: str, limit: int = KEYWORD_LIMIT) -> List[str]:
words = re.findall(r"\b[a-zA-ZĂ-Ăż]{3,}\b", text.lower())
freq: Dict[str, int] = {}
for w in words:
if w in STOPWORDS:
continue
if w.isdigit():
continue
if len(w) <= 3 and w not in SPECIAL_KEYWORDS:
continue
freq[w] = freq.get(w, 0) + 1
if title:
title_words = re.findall(r"\b[a-zA-ZĂ-Ăż]{3,}\b", title.lower())
for w in title_words:
if w in STOPWORDS:
continue
if len(w) <= 3 and w not in SPECIAL_KEYWORDS:
continue
freq[w] = freq.get(w, 0) + 4
sorted_words = sorted(freq.items(), key=lambda x: x[1], reverse=True)
keywords: List[str] = []
for word, count in sorted_words:
if count < 2 and word not in SPECIAL_KEYWORDS:
continue
keywords.append(word)
if len(keywords) >= limit:
break
return keywords
def _infer_module_type(title: Optional[str], text: str) -> str:
combined = ((title or "") + " " + text).lower()
if "art" in combined or "nature" in combined or "peinture" in combined:
return "art_symbolic_expression"
if "schéma" in combined or "schema" in combined or "atlas" in combined:
return "cognitive_mapping"
if "déni" in combined or "deni" in combined or "violence" in combined:
return "degradation_analysis"
if "conscience" in combined or "charte" in combined:
return "ethical_framework"
if "critique" in combined or "biais" in combined:
return "discursive_protection"
return "conceptual_module"
def extract_module_candidate(
*,
url: str,
title: Optional[str],
content: str,
) -> Dict[str, Any]:
clean_text = _normalize(content)
snippet = clean_text[:MAX_SNIPPET_LENGTH]
keywords = _extract_keywords(title, clean_text)
module_type = _infer_module_type(title, clean_text)
return {
"source_url": url,
"title": title,
"module_type": module_type,
"keywords": keywords,
"summary_snippet": snippet,
"status": "candidate",
}
Avec ça, ton pipeline complet devient
read_sitemap
â
urls
â
classify_resource
â
fetch_url
â
extract_module_candidate
â
memory storage
â
cognitive atlas
Exemple concret
sitemap.xml
â
50 pages
â
classify_resource
â
35 pages HTML
â
fetch_url
â
extract_module_candidate
â
35 modules candidats
Ensuite l'agent peut :
- regrouper
- relier
- organiser
dans le Cognitive Atlas.
build_cognitive_atlas.py
import json
from read_sitemap import read_sitemap
from fetch_url import fetch_url
from extract_module_candidate import extract_module_candidate
EXCLUDED_URL_PARTS = [
"contact",
"prestations",
]
PREFERRED_URL_PARTS = [
"conscience",
"charte",
"manifeste",
"eveil",
"éveil",
"deni",
"déni",
"vie",
"anti-biais",
"schemas",
"schémas",
"androgynie",
"sens",
"esprit-critique",
"glossaire",
"manuel",
"toile",
"code-source",
"prophetie",
"prophétie",
"art",
"nature",
"egeries",
"vierge",
"putain",
"onde",
"ontologique"
]
def should_include_url(url: str) -> bool:
lower = url.lower()
if any(x in lower for x in EXCLUDED_URL_PARTS):
return False
if any(x in lower for x in PREFERRED_URL_PARTS):
return True
return False
def build_atlas(sitemap_url: str, limit: int = 40):
sitemap_result = read_sitemap(sitemap_url)
if sitemap_result.get("status") != "success":
return []
urls = sitemap_result["urls"]
atlas = []
count = 0
for url in urls:
if not should_include_url(url):
continue
page = fetch_url(url)
if page["status"] != "success":
continue
module = extract_module_candidate(
url=page["url"],
title=page["title"],
content=page["content"],
)
atlas.append(module)
count += 1
if count >= limit:
break
return atlas
if __name__ == "__main__":
atlas = build_atlas(
"https://www.agdistys.fr/sitemap.xml",
limit=40,
)
for m in atlas:
print("\n----")
print(m["title"])
print(m["module_type"])
with open("atlas.json", "w", encoding="utf-8") as f:
json.dump(atlas, f, indent=2, ensure_ascii=False)
print("\nAtlas saved to atlas.json")
cognitive_router.py
from __future__ import annotations
from typing import Any, Dict, List, Optional
def _score_module_for_context(module: Dict[str, Any], context: str) -> int:
"""
Very lightweight routing score based on keyword overlap
between the context text and the module keywords/type/title.
"""
text = context.lower()
score = 0
for keyword in module.get("keywords", []):
if keyword.lower() in text:
score += 2
module_type = (module.get("type") or "").lower()
if module_type and module_type in text:
score += 2
title = (module.get("title") or "").lower()
if title and title in text:
score += 1
return score
def route_cognitive_modules(
context: str,
atlas: Dict[str, Any],
top_k: int = 3,
) -> Dict[str, Any]:
"""
Select the most relevant modules from the cognitive atlas
for a given context.
Returns:
- selected_modules
- routing_scores
- status
"""
nodes: List[Dict[str, Any]] = atlas.get("nodes", [])
scored = []
for node in nodes:
score = _score_module_for_context(node, context)
if score > 0:
scored.append({
"id": node.get("id"),
"title": node.get("title"),
"type": node.get("type"),
"score": score,
"keywords": node.get("keywords", []),
"neighbors": node.get("neighbors", []),
})
scored.sort(key=lambda x: x["score"], reverse=True)
selected = scored[:top_k]
return {
"status": "success",
"context": context,
"selected_modules": selected,
"routing_scores": scored,
}
ingest_site.py
from __future__ import annotations
from typing import Any, Dict, List
from read_sitemap import read_sitemap
from classify_resource import classify_resource
from fetch_url import fetch_url
from extract_module_candidate import extract_module_candidate
from build_cognitive_atlas import build_cognitive_atlas
def ingest_site_from_sitemap(sitemap_url: str) -> Dict[str, Any]:
"""
Minimal end-to-end ingestion pipeline:
sitemap -> classify -> fetch -> module candidates -> atlas
"""
sitemap_result = read_sitemap(sitemap_url)
if sitemap_result.get("status") != "success":
return {
"status": "error",
"stage": "read_sitemap",
"error": sitemap_result.get("error"),
}
urls = sitemap_result.get("urls", [])
module_candidates: List[Dict[str, Any]] = []
resources: List[Dict[str, Any]] = []
for url in urls:
resource = classify_resource(url)
resources.append(resource)
if resource["resource_type"] != "page":
continue
page = fetch_url(url)
if page.get("status") != "success":
continue
candidate = extract_module_candidate(
url=page["url"],
title=page["title"],
content=page["content"],
)
module_candidates.append(candidate)
atlas = build_cognitive_atlas(module_candidates)
return {
"status": "success",
"sitemap_url": sitemap_url,
"resource_count": len(resources),
"module_candidate_count": len(module_candidates),
"resources": resources,
"module_candidates": module_candidates,
"atlas": atlas,
}
Build_cognitive_atlas.py
from read_sitemap import read_sitemap
from fetch_url import fetch_url
from extract_module_candidate import extract_module_candidate
def build_atlas(sitemap_url: str, limit: int = 20):
s = read_sitemap(sitemap_url)
atlas = []
for url in s["urls"][:limit]:
page = fetch_url(url)
if page["status"] != "success":
continue
module = extract_module_candidate(
url=page["url"],
title=page["title"],
content=page["content"],
)
atlas.append(module)
return atlas
if __name__ == "__main__":
atlas = build_atlas(
"https://www.agdistys.fr/sitemap.xml",
limit=10
)
for m in atlas:
print("\n----")
print(m["title"])
print(m["module_type"])
build_cognitive_graph.py
import json
from collections import defaultdict
def load_atlas(path="atlas.json"):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def build_graph(atlas):
graph = defaultdict(list)
for module in atlas:
source = module["title"]
keywords = module.get("keywords", [])
for kw in keywords:
graph[source].append(kw)
return graph
def save_graph(graph):
with open("cognitive_graph.json", "w", encoding="utf-8") as f:
json.dump(graph, f, indent=2, ensure_ascii=False)
if __name__ == "__main__":
atlas = load_atlas()
graph = build_graph(atlas)
save_graph(graph)
print("Graph built with", len(graph), "nodes")
discover_core_concepts.py
import json
from collections import Counter
STOPWORDS = {
"pour", "dans", "avec", "mais", "nous", "vous", "ils", "elles", "elle", "il",
"les", "des", "une", "un", "plus", "bien", "tout", "ĂȘtre", "faire", "sont",
"ont", "cela", "comme", "leur", "leurs", "notre", "votre", "ainsi", "donc",
"peut", "sans", "instagram", "publication", "comment", "vieux", "entre",
"veux", "cette", "voir", "contact", "prestations", "articles",
"ressources", "tĂ©moignages", "mĂȘme", "quand", "devient", "suis"
}
def load_atlas(path="atlas.json"):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def extract_concepts(atlas):
counter = Counter()
for module in atlas:
keywords = module.get("keywords", [])
for k in keywords:
if (k not in STOPWORDS and len(k) > 3) or k == "art":
counter[k] += 1
return counter
def save_concepts(counter):
concepts = counter.most_common(50)
with open("core_concepts.json", "w", encoding="utf-8") as f:
json.dump(concepts, f, indent=2, ensure_ascii=False)
return concepts
if __name__ == "__main__":
atlas = load_atlas()
counter = extract_concepts(atlas)
concepts = save_concepts(counter)
print("\nTop Concepts:\n")
for word, freq in concepts[:20]:
print(word, "â", freq)
concept_router.py
import json
def load_graph():
with open("cognitive_graph.json", "r", encoding="utf-8") as f:
return json.load(f)
def load_concepts():
with open("core_concepts.json", "r", encoding="utf-8") as f:
return dict(f)
def detect_concepts(question, concepts):
q = question.lower()
detected = []
for c in concepts:
if c in q:
detected.append(c)
return detected
def route(question):
graph = load_graph()
concepts = load_concepts()
detected = detect_concepts(question, concepts)
modules = []
for module, keywords in graph.items():
for c in detected:
if c in keywords:
modules.append(module)
return {
"question": question,
"concepts": detected,
"modules": list(set(modules))
}
if __name__ == "__main__":
q = input("\nQuestion: ")
result = route(q)
print("\nDetected concepts:", result["concepts"])
print("\nRelevant modules:")
for m in result["modules"]:
print("-", m)
discover_axes.py
import json
from collections import defaultdict
def load_graph():
with open("cognitive_graph.json","r",encoding="utf-8") as f:
return json.load(f)
def build_keyword_clusters(graph):
clusters = defaultdict(list)
for module, keywords in graph.items():
for kw in keywords:
clusters[kw].append(module)
return clusters
if __name__ == "__main__":
graph = load_graph()
clusters = build_keyword_clusters(graph)
print("\nConcept â modules\n")
for concept, modules in sorted(clusters.items(), key=lambda x: len(x[1]), reverse=True)[:20]:
print(concept)
print(" modules:", len(modules))
for m in modules[:5]:
print(" -", m)
print()
discover_structural_pairs.py
import json
from collections import Counter
from itertools import combinations
STOPWORDS = {
"pour", "dans", "avec", "mais", "nous", "vous", "ils", "elles", "elle", "il",
"les", "des", "une", "un", "plus", "bien", "tout", "etre", "ĂȘtre", "faire", "sont",
"ont", "cela", "comme", "leur", "leurs", "notre", "votre", "ainsi", "donc",
"peut", "sans", "instagram", "publication", "comment", "vieux", "entre",
"veux", "cette", "voir", "contact", "prestations", "articles",
"ressources", "tĂ©moignages", "meme", "mĂȘme", "quand", "devient", "suis"
}
def load_atlas(path="atlas.json"):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def discover_pairs(atlas):
pair_counter = Counter()
for module in atlas:
keywords = module.get("keywords", [])
keywords = [
k for k in keywords
if k not in STOPWORDS and (len(k) > 3 or k == "art")
]
keywords = sorted(set(keywords))
for a, b in combinations(keywords, 2):
pair_counter[(a, b)] += 1
return pair_counter
if __name__ == "__main__":
atlas = load_atlas()
pairs = discover_pairs(atlas)
data = []
for (a, b), freq in pairs.most_common():
data.append({
"a": a,
"b": b,
"weight": freq
})
with open("pairs.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
print("\nTop Structural Pairs:\n")
for item in data[:30]:
print(f"{item['a']} â {item['b']} â {item['weight']}")
print("\nSaved to pairs.json")
philosophical_polarities.py
POLARITIES = {
"vérité": "mensonge",
"respect": "violence",
"conscience": "déni",
"justice": "domination",
"cohérence": "confusion",
"vie": "mort",
"amour": "peur",
"impartialité": "neutralité",
"spiritualité": "dogme",
"radicalité": "extrémisme",
"libre arbitre": "conditionnement",
"choix": "impulsivité",
"conflit": "violence",
"responsabilité": "culpabilité",
"critique": "attaque",
"fait": "opinion",
"essence": "essentialisme",
"nature": "structure",
"artisanat": "industrie",
"liberté": "emprise",
"santé": "maladie",
"consentement": "violation",
"eden": "enfer",
"abondance": "carence",
"sécurité": "hostilité",
"confiance": "crainte",
"surprise": "choc",
"altruisme": "égocentrisme",
"suffisance": "addiction",
"humilité": "mépris",
"homéostasie": "entropie",
"harmonie": "entropie",
"génie": "folie",
"sachant": "ignorant",
"courage": "lùcheté",
"générosité": "avarice",
"assumer": "fuite",
"maturité": "immaturité",
"intégrité": "dissociation",
"consentir": "céder",
"recevoir": "voler",
"donner": "sacrifier",
"pardonner": "oublier",
"repentir": "empirer",
"pitié": "cruauté",
"féminisme": "misogynie",
"écologie": "égoïsme",
"naturel": "artificiel",
"procréation": "reproduction",
"création": "consommation",
"sublimation": "perversion",
"éducation": "aliénation",
"guérison": "exploitation",
"vertu": "vice",
"réalité": "illusion",
"sérénité": "apathie",
"alignement": "acrasie",
"empathie": "apathie",
"sympathie": "antipathie",
"endophobie": "xénophobie",
"dysphorie": "mégalomanie",
"érotomanie": "paranoïa",
"besoin": "compulsion",
"intelligence": "stupidité",
"connaissance": "absurdité",
"savoir": "croyance",
"blesser": "vexer",
"émotion": "hormone",
"présence": "abandon",
"assertivité": "agressivité",
"art": "industrie"
}
Discover_polarities.py
import json
from philosophical_polarities import POLARITIES
def load_concepts(path="core_concepts.json"):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
return {word: freq for word, freq in data}
if __name__ == "__main__":
concepts = load_concepts()
print("\nPhilosophical Polarities:\n")
for positive, negative in POLARITIES.items():
pos_freq = concepts.get(positive, 0)
neg_freq = concepts.get(negative, 0)
print(f"{positive} â {negative}")
print(f" {positive}: {pos_freq}")
print(f" {negative}: {neg_freq}")
print()
discover_structural_triads.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
from collections import defaultdict
def load_pairs(path="pairs.json"):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def build_graph(pairs):
graph = defaultdict(dict)
for item in pairs:
a = item["a"]
b = item["b"]
w = float(item.get("weight", 1))
graph[a][b] = w
graph[b][a] = w
return graph
def discover_triads(graph, min_weight=3):
nodes = sorted(graph.keys())
triads = []
seen = set()
for a in nodes:
neighbors_a = set(graph[a].keys())
for b in neighbors_a:
if b <= a:
continue
neighbors_b = set(graph[b].keys())
common = neighbors_a.intersection(neighbors_b)
for c in common:
if c <= b:
continue
wab = graph[a].get(b, 0)
wac = graph[a].get(c, 0)
wbc = graph[b].get(c, 0)
if min(wab, wac, wbc) < min_weight:
continue
triad = tuple(sorted([a, b, c]))
if triad in seen:
continue
seen.add(triad)
score = wab + wac + wbc
triads.append({
"a": triad[0],
"b": triad[1],
"c": triad[2],
"w_ab": wab,
"w_ac": wac,
"w_bc": wbc,
"score": score,
})
triads.sort(key=lambda x: x["score"], reverse=True)
return triads
if __name__ == "__main__":
pairs = load_pairs()
graph = build_graph(pairs)
triads = discover_triads(graph, min_weight=3)
with open("triads.json", "w", encoding="utf-8") as f:
json.dump(triads, f, ensure_ascii=False, indent=2)
print("\nTop Structural Triads:\n")
for t in triads[:30]:
print(
f"{t['a']} ⏠{t['b']} ⏠{t['c']} "
f"-> score={t['score']} "
f"[{t['w_ab']}, {t['w_ac']}, {t['w_bc']}]"
)
print("\nSaved to triads.json")
discover_axes.py
import json
AXES = {
"axe_du_vivant": [
"conscience", "vivant", "vie", "sens", "nature", "corps", "monde"
],
"axe_ethique": [
"charte", "vérité", "justice", "cohérence", "verbe", "respect"
],
"axe_degradation": [
"violence", "déni", "biais", "systÚme", "peur", "domination"
],
"axe_symbolique": [
"esprit", "verbe", "monde", "agdistys", "art", "partager", "ensemble"
],
"axe_du_temple_vivant": [
"art", "nature", "corps", "vivant", "présence", "beauté", "sacré"
]
}
def load_concepts(path="core_concepts.json"):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
return {word: freq for word, freq in data}
def score_axes(concepts):
results = {}
for axis, words in AXES.items():
score = 0
present = []
for w in words:
freq = concepts.get(w, 0)
score += freq
if freq > 0:
present.append((w, freq))
results[axis] = {
"score": score,
"present_words": present
}
return results
if __name__ == "__main__":
concepts = load_concepts()
results = score_axes(concepts)
print("\nCosmogony Axes:\n")
for axis, data in sorted(results.items(), key=lambda x: x[1]["score"], reverse=True):
print(axis, "â", data["score"])
for word, freq in data["present_words"]:
print(" -", word, "â", freq)
print()
discover_sacre_laws.py
import json
TRIADS = {
"trinite_du_vivant": ["conscience", "sens", "vivant"],
"trinite_ethique": ["charte", "conscience", "vivant"],
"trinite_de_verite": ["conscience", "vérité", "vivant"],
"trinite_de_l_ombre": ["conscience", "déni", "violence"],
"trinite_du_temple": ["art", "nature", "vivant"],
"trinite_du_verbe": ["conscience", "verbe", "vivant"],
}
def load_concepts(path="core_concepts.json"):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
return {word: freq for word, freq in data}
def score_triads(concepts):
results = {}
for name, words in TRIADS.items():
score = 0
details = []
for w in words:
freq = concepts.get(w, 0)
score += freq
details.append((w, freq))
results[name] = {
"score": score,
"details": details
}
return results
if __name__ == "__main__":
concepts = load_concepts()
results = score_triads(concepts)
print("\nSacred Laws:\n")
for name, data in sorted(results.items(), key=lambda x: x[1]["score"], reverse=True):
print(name, "â", data["score"])
for word, freq in data["details"]:
print(" -", word, "â", freq)
print()
discover_archetypes.py
import json
ARCHETYPES = {
"archetype_du_vivant": ["conscience", "sens", "vivant"],
"archetype_de_la_loi": ["charte", "conscience", "vivant"],
"archetype_du_verbe": ["conscience", "verbe", "vivant"],
"archetype_de_l_ombre": ["conscience", "déni", "violence"],
"archetype_du_temple": ["nature", "corps", "vivant"]
}
def load_concepts():
with open("core_concepts.json", "r", encoding="utf-8") as f:
data = json.load(f)
return {w: f for w, f in data}
def score_archetypes(concepts):
results = {}
for name, words in ARCHETYPES.items():
score = 0
details = []
for w in words:
freq = concepts.get(w, 0)
score += freq
details.append((w, freq))
results[name] = {
"score": score,
"details": details
}
return results
if __name__ == "__main__":
concepts = load_concepts()
results = score_archetypes(concepts)
print("\nCosmological Archetypes:\n")
for name, data in sorted(results.items(), key=lambda x: x[1]["score"], reverse=True):
print(name, "â", data["score"])
for word, freq in data["details"]:
print(" -", word, "â", freq)
print()
graph_cosmology.py
import json
def load_concepts(path="core_concepts.json"):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
return {word: freq for word, freq in data}
COSMOLOGY = {
"VIVANT": ["conscience", "vivant", "sens", "nature", "corps", "monde"],
"LOI": ["charte", "vérité", "justice", "cohérence", "verbe"],
"OMBRE": ["violence", "déni", "biais", "systÚme", "peur"],
"TEMPLE": ["art", "nature", "corps", "vivant"],
"VERBE": ["verbe", "esprit", "partager", "ensemble", "agdistys"],
}
def score_cluster(concepts, words):
total = 0
details = []
for w in words:
freq = concepts.get(w, 0)
total += freq
if freq > 0:
details.append((w, freq))
return total, details
if __name__ == "__main__":
concepts = load_concepts()
print("\nGraph Cosmology:\n")
results = {}
for cluster, words in COSMOLOGY.items():
score, details = score_cluster(concepts, words)
results[cluster] = {"score": score, "details": details}
for cluster, data in sorted(results.items(), key=lambda x: x[1]["score"], reverse=True):
print(cluster, "=>", data["score"])
for word, freq in data["details"]:
print(" -", word, "=>", freq)
print()
print("Cosmological Structure:\n")
print(" VERBE")
print(" âČ")
print(" â")
print("LOI âââ VIVANT âââ¶ TEMPLE")
print(" â")
print(" âŒ")
print(" OMBRE")
discover_hubs.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
from collections import defaultdict
def load_pairs(path="pairs.json"):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def build_graph(pairs):
graph = defaultdict(dict)
for item in pairs:
a = item["a"]
b = item["b"]
w = float(item.get("weight", 1))
graph[a][b] = w
graph[b][a] = w
return graph
def discover_hubs(graph):
hubs = []
for node, neighbors in graph.items():
degree = len(neighbors)
weighted_degree = sum(neighbors.values())
hubs.append({
"node": node,
"degree": degree,
"weighted_degree": weighted_degree,
"top_neighbors": sorted(
neighbors.items(),
key=lambda x: x[1],
reverse=True
)[:5]
})
hubs.sort(
key=lambda x: (x["weighted_degree"], x["degree"]),
reverse=True
)
return hubs
if __name__ == "__main__":
pairs = load_pairs()
graph = build_graph(pairs)
hubs = discover_hubs(graph)
with open("hubs.json", "w", encoding="utf-8") as f:
json.dump(hubs, f, ensure_ascii=False, indent=2)
print("\nTop Hubs:\n")
for h in hubs[:20]:
neighbors = ", ".join(f"{n} ({w})" for n, w in h["top_neighbors"])
print(
f"{h['node']} -> degree={h['degree']} | "
f"weighted={h['weighted_degree']} | {neighbors}"
)
print("\nSaved to hubs.json")
discover_path.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import re
import sys
import unicodedata
from collections import defaultdict, deque
def normalize_text(text: str) -> str:
text = text.lower()
text = text.replace("Ć", "oe").replace("ĂŠ", "ae")
text = unicodedata.normalize("NFKD", text)
text = "".join(ch for ch in text if not unicodedata.combining(ch))
text = re.sub(r"['']", " ", text)
text = re.sub(r"[^a-zA-Z0-9_\- ]+", " ", text)
text = re.sub(r"\s+", " ", text).strip()
return text
def load_pairs(path="pairs.json"):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def build_graph(pairs):
graph = defaultdict(dict)
for item in pairs:
a = normalize_text(item["a"])
b = normalize_text(item["b"])
w = float(item.get("weight", 1))
graph[a][b] = w
graph[b][a] = w
return graph
def shortest_weighted_biased_path(graph, start, goal):
"""
BFS simple, mais on explore d'abord les voisins les plus lourds.
Donc on garde la simplicité d'une v1, avec un peu plus d'intelligence.
"""
if start not in graph or goal not in graph:
return None
queue = deque([[start]])
visited = {start}
while queue:
path = queue.popleft()
node = path[-1]
if node == goal:
return path
neighbors = sorted(
graph[node].items(),
key=lambda x: x[1],
reverse=True
)
for neighbor, _weight in neighbors:
if neighbor not in visited:
visited.add(neighbor)
queue.append(path + [neighbor])
return None
if __name__ == "__main__":
if len(sys.argv) < 3:
print('Usage : python discover_path.py "mot_depart" "mot_arrivee"')
sys.exit(1)
start = normalize_text(sys.argv[1])
goal = normalize_text(sys.argv[2])
pairs = load_pairs()
graph = build_graph(pairs)
path = shortest_weighted_biased_path(graph, start, goal)
if path is None:
print(f"\nAucun chemin trouvé entre {start} et {goal}")
else:
print("\nChemin trouvé :\n")
print(" -> ".join(path))
atlas_reasoner.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import json
import re
import sys
import unicodedata
from pathlib import Path
from collections import Counter, defaultdict
DATA_DIRS = [
Path("./data"),
Path("../data"),
Path("."),
]
STOPWORDS = {
"pour", "dans", "avec", "mais", "nous", "vous", "ils", "elles", "elle", "il",
"les", "des", "une", "un", "plus", "bien", "tout", "etre", "ĂȘtre", "faire", "sont",
"ont", "cela", "comme", "leur", "leurs", "notre", "votre", "ainsi", "donc",
"peut", "sans", "entre", "vers", "comment", "quoi", "quel", "quelle", "quels",
"quelles", "est", "suis", "du", "de", "la", "le", "l", "d",
"au", "aux", "ce", "cet", "cette", "ces", "mon", "ton", "son", "mes", "tes",
"ses", "que", "qui", "ou", "oĂč", "sur", "par", "pas", "ne", "y", "a", "Ă "
}
TOP_CONCEPTS = 8
TOP_LINKS = 12
TOP_CLUSTER_WORDS = 8
COSMOLOGY = {
"VIVANT": ["conscience", "vivant", "sens", "nature", "corps", "monde", "vie"],
"LOI": ["charte", "verite", "justice", "coherence", "verbe", "respect"],
"OMBRE": ["violence", "deni", "biais", "systeme", "peur", "domination"],
"TEMPLE": ["art", "nature", "corps", "vivant", "presence", "beaute"],
"VERBE": ["verbe", "esprit", "partager", "ensemble", "agdistys"],
}
def normalize_text(text: str) -> str:
text = text.lower()
text = text.replace("Ć", "oe").replace("ĂŠ", "ae")
text = unicodedata.normalize("NFKD", text)
text = "".join(ch for ch in text if not unicodedata.combining(ch))
text = re.sub(r"['']", " ", text)
text = re.sub(r"[^a-zA-Z0-9_\- ]+", " ", text)
text = re.sub(r"\s+", " ", text).strip()
return text
def tokenize(text: str) -> list[str]:
text = normalize_text(text)
tokens = text.split()
return [t for t in tokens if t not in STOPWORDS and len(t) > 1]
def safe_load_json(path: Path, default):
if not path.exists():
return default
try:
with path.open("r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
print(f"[warn] impossible de lire {path}: {e}")
return default
def find_data_dir() -> Path:
for d in DATA_DIRS:
if (d / "core_concepts.json").exists():
return d
for d in DATA_DIRS:
if d.exists():
return d
return Path(".")
def load_core_concepts(path="core_concepts.json") -> dict[str, float]:
raw = safe_load_json(Path(path), default=[])
concepts = {}
if isinstance(raw, list):
for item in raw:
if isinstance(item, list) and len(item) >= 2:
word = normalize_text(str(item[0]))
try:
freq = float(item[1])
except Exception:
freq = 0.0
if word:
concepts[word] = freq
elif isinstance(item, dict):
name = item.get("concept") or item.get("name") or item.get("label")
if name:
word = normalize_text(str(name))
try:
freq = float(item.get("score", item.get("weight", 0)))
except Exception:
freq = 0.0
concepts[word] = freq
elif isinstance(raw, dict):
for k, v in raw.items():
word = normalize_text(str(k))
try:
freq = float(v)
except Exception:
freq = 0.0
concepts[word] = freq
return concepts
def load_pairs(path="pairs.json") -> list[dict]:
raw = safe_load_json(Path(path), default=[])
pairs = []
if not isinstance(raw, list):
return pairs
for item in raw:
if isinstance(item, dict):
a = item.get("a")
b = item.get("b")
w = item.get("weight", 1)
if a and b:
try:
weight = float(w)
except Exception:
weight = 1.0
pairs.append({
"a": normalize_text(str(a)),
"b": normalize_text(str(b)),
"weight": weight,
})
elif isinstance(item, list) and len(item) >= 2:
a = normalize_text(str(item[0]))
b = normalize_text(str(item[1]))
w = 1.0
if len(item) >= 3:
try:
w = float(item[2])
except Exception:
w = 1.0
pairs.append({
"a": a,
"b": b,
"weight": w,
})
return pairs
class AtlasReasoner:
def __init__(self, data_dir: Path):
self.data_dir = data_dir
self.core_concepts = load_core_concepts(data_dir / "core_concepts.json")
self.pairs = load_pairs(data_dir / "pairs.json")
self.graph = self.build_graph(self.pairs)
def build_graph(self, pairs: list[dict]) -> dict[str, dict[str, float]]:
graph = defaultdict(dict)
for item in pairs:
a = item["a"]
b = item["b"]
w = item["weight"]
graph[a][b] = w
graph[b][a] = w
return graph
def score_query_concepts(self, query: str, top_n: int = TOP_CONCEPTS) -> list[tuple[str, float]]:
q_tokens = tokenize(query)
scores = Counter()
for word, freq in self.core_concepts.items():
word_tokens = tokenize(word)
overlap = 0.0
for qt in q_tokens:
if qt in word_tokens:
overlap += 3.0
elif len(qt) >= 4 and word.startswith(qt):
overlap += 1.5
elif len(word) >= 4 and qt.startswith(word):
overlap += 1.0
if overlap > 0:
scores[word] = overlap + (freq * 0.10)
if not scores:
return []
return scores.most_common(top_n)
def find_links(self, concepts: list[str], top_n: int = TOP_LINKS) -> list[dict]:
concept_set = set(concepts)
links = []
for item in self.pairs:
a = item["a"]
b = item["b"]
w = item["weight"]
if a in concept_set or b in concept_set:
links.append({
"a": a,
"b": b,
"weight": w,
"internal": (a in concept_set and b in concept_set),
})
links.sort(key=lambda x: (x["internal"], x["weight"]), reverse=True)
return links[:top_n]
def score_cosmology(self, activated_concepts: list[str]) -> dict[str, dict]:
concept_set = set(activated_concepts)
results = {}
for cluster, words in COSMOLOGY.items():
activated_words = []
total = 0.0
for w in words:
freq = self.core_concepts.get(w, 0.0)
if w in concept_set:
total += freq + 8
activated_words.append((w, freq))
elif freq > 0:
total += freq * 0.10
results[cluster] = {
"score": round(total, 3),
"activated_words": sorted(
activated_words,
key=lambda x: x[1],
reverse=True
)[:TOP_CLUSTER_WORDS],
}
return dict(
sorted(results.items(), key=lambda x: x[1]["score"], reverse=True)
)
def build_reasoning_path(self, concepts: list[str], max_neighbors: int = 3) -> list[dict]:
path = []
for c in concepts[:5]:
neighbors = []
for neigh, w in sorted(
self.graph.get(c, {}).items(),
key=lambda x: x[1],
reverse=True
)[:max_neighbors]:
neighbors.append({
"concept": neigh,
"weight": w,
})
path.append({
"concept": c,
"neighbors": neighbors,
})
return path
def symbolic_reading(self, concepts: list[str], cosmology: dict[str, dict], links: list[dict]) -> str:
if not concepts:
return (
"La requĂȘte n'active pas clairement les concepts du corpus actuel. "
"Il faut soit reformuler la question, soit enrichir l'atlas."
)
cs = set(concepts)
top_cluster = next(iter(cosmology.keys())) if cosmology else None
phrases = []
if {"conscience", "sens", "vivant"}.issubset(cs):
phrases.append(
"La triade source conscience / sens / vivant est activĂ©e : on est au cĆur du noyau cognitif."
)
elif {"conscience", "vivant"}.issubset(cs):
phrases.append(
"Le couple conscience / vivant domine : la requĂȘte touche la relation entre reconnaissance et protection du rĂ©el."
)
elif "conscience" in cs:
phrases.append(
"La conscience est centrale : la requĂȘte cherche un point de luciditĂ©, d'orientation ou de reconnaissance."
)
if "charte" in cs or "verite" in cs or "justice" in cs or "coherence" in cs:
phrases.append(
"Une composante de Loi est présente : le raisonnement cherche un cadre, une justesse ou une cohérence."
)
if "deni" in cs or "violence" in cs or "biais" in cs:
phrases.append(
"Une composante d'Ombre est activée : le systÚme repÚre une zone de rupture, d'attaque ou de dégradation."
)
if "art" in cs or "corps" in cs or "nature" in cs:
phrases.append(
"Une composante de Temple est activée : la lecture touche l'incarnation, le corps, la présence ou l'expression du vivant."
)
if "verbe" in cs or "esprit" in cs or "partager" in cs:
phrases.append(
"Une composante de Verbe est activée : langage, transmission et symbolisation jouent ici un rÎle structurant."
)
if top_cluster == "VIVANT":
phrases.append(
"Le cluster dominant est VIVANT : la lecture revient Ă l'incarnation, au sens et au rapport au monde vivant."
)
elif top_cluster == "LOI":
phrases.append(
"Le cluster dominant est LOI : la requĂȘte appelle un discernement Ă©thique ou structurant."
)
elif top_cluster == "OMBRE":
phrases.append(
"Le cluster dominant est OMBRE : la requĂȘte s'approche d'une zone critique, Ă diagnostiquer ou Ă rĂ©parer."
)
elif top_cluster == "TEMPLE":
phrases.append(
"Le cluster dominant est TEMPLE : la lecture touche le corps, l'art, la présence et la dimension sacrée du vivant."
)
elif top_cluster == "VERBE":
phrases.append(
"Le cluster dominant est VERBE : langage, transmission et esprit organisent ici la pensée."
)
internal_links = [x for x in links if x["internal"]]
if internal_links:
best = internal_links[0]
phrases.append(
f"La liaison interne la plus forte activĂ©e est {best['a']} â {best['b']} (poids {best['weight']})."
)
if not phrases:
phrases.append(
"La requĂȘte active un ensemble diffus de concepts ; il faut encore condenser le sens avant routage."
)
return " ".join(phrases)
def answer(self, query: str) -> dict:
activated = self.score_query_concepts(query)
concepts = [c for c, _ in activated]
links = self.find_links(concepts)
cosmology = self.score_cosmology(concepts)
reasoning_path = self.build_reasoning_path(concepts)
symbolic = self.symbolic_reading(concepts, cosmology, links)
return {
"query": query,
"activated_concepts": [
{"concept": c, "score": round(score, 3)}
for c, score in activated
],
"links": links,
"cosmology": cosmology,
"symbolic_reading": symbolic,
"reasoning_path": reasoning_path,
}
def pretty_print(result: dict):
print("\n" + "=" * 72)
print("ATLAS REASONER â COSMOLOGY MODE")
print("=" * 72)
print(f"\nRequĂȘte : {result['query']}")
print("\n--- Concepts activés ---")
if result["activated_concepts"]:
for item in result["activated_concepts"]:
print(f"- {item['concept']} ({item['score']})")
else:
print("- aucun concept nettement activé")
print("\n--- Liens activés ---")
if result["links"]:
for link in result["links"]:
status = "interne" if link["internal"] else "bord"
print(f"- {link['a']} â {link['b']} | weight={link['weight']} | {status}")
else:
print("- aucun")
print("\n--- Cosmologie ---")
for cluster, data in result["cosmology"].items():
words = ", ".join(f"{w} ({f})" for w, f in data["activated_words"])
if not words:
words = "aucun mot activé"
print(f"- {cluster} => {data['score']} | {words}")
print("\n--- Lecture symbolique ---")
print(result["symbolic_reading"])
print("\n--- Chemin de raisonnement ---")
if result["reasoning_path"]:
for step in result["reasoning_path"]:
neighbors = ", ".join(
f"{n['concept']} ({n['weight']})" for n in step["neighbors"]
)
if not neighbors:
neighbors = "aucun voisin"
print(f"- {step['concept']} -> {neighbors}")
else:
print("- aucun")
print()
def main():
if len(sys.argv) < 2:
print('Usage : python atlas_reasoner.py "ta question ici"')
sys.exit(1)
query = " ".join(sys.argv[1:]).strip()
data_dir = find_data_dir()
reasoner = AtlasReasoner(data_dir)
result = reasoner.answer(query)
pretty_print(result)
out_path = Path("reasoning_output.json")
with out_path.open("w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
print(f"[ok] résultat sauvegardé dans {out_path}")
if __name__ == "__main__":
main()
cognitive_router.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import json
import sys
from pathlib import Path
from atlas_reasoner import AtlasReasoner, find_data_dir
ROUTES = {
"VIVANT": {
"route_name": "route_vivant",
"intent": "incarnation_sens_vivant",
"description": (
"Orienter la réflexion vers le vivant, le sens, l'incarnation, "
"la continuité du réel et la protection du monde vivant."
),
"guidance": [
"Revenir à ce qui est vivant, concret, incarné.",
"Chercher le lien entre conscience, sens et vivant.",
"Privilégier les concepts qui relient plutÎt que ceux qui isolent.",
],
},
"LOI": {
"route_name": "route_loi",
"intent": "ethique_charte_verite",
"description": (
"Orienter la réflexion vers la charte, la vérité, la justice, "
"la cohérence et le cadre éthique."
),
"guidance": [
"Chercher le cadre juste.",
"Relier les concepts à la vérité, à la cohérence et au respect.",
"Vérifier ce qui protÚge réellement le vivant.",
],
},
"OMBRE": {
"route_name": "route_ombre",
"intent": "diagnostic_degradation_reparation",
"description": (
"Orienter la réflexion vers les zones de déni, biais, violence, "
"domination, rupture ou dégradation."
),
"guidance": [
"Identifier la rupture ou le point de dégradation.",
"Repérer ce qui diminue, nie ou détruit.",
"Préparer une lecture de réparation plutÎt qu'une simple condamnation.",
],
},
"TEMPLE": {
"route_name": "route_temple",
"intent": "art_corps_presence",
"description": (
"Orienter la réflexion vers l'art, le corps, la nature, "
"la présence, la beauté et la dimension sacrée du vivant."
),
"guidance": [
"Lire l'art comme expression incarnée du vivant.",
"Relier corps, présence, nature et beauté.",
"Chercher la forme sensible ou symbolique qui élÚve.",
],
},
"VERBE": {
"route_name": "route_verbe",
"intent": "langage_transmission_symbolisation",
"description": (
"Orienter la réflexion vers le verbe, l'esprit, la transmission, "
"la symbolisation et le partage."
),
"guidance": [
"Observer comment le langage structure la pensée.",
"Chercher les formes de transmission et de mise en relation.",
"Lire les concepts comme noeuds de symbolisation.",
],
},
}
DEFAULT_ROUTE = {
"route_name": "route_exploration",
"intent": "exploration_ouverte",
"description": (
"Aucune route dominante n'émerge nettement. "
"La requĂȘte reste en exploration ouverte."
),
"guidance": [
"Reformuler la question.",
"Chercher des concepts plus centraux dans l'atlas.",
"Ălargir ou enrichir le corpus si nĂ©cessaire.",
],
}
class CognitiveRouter:
def __init__(self, data_dir: Path | None = None):
self.data_dir = data_dir or find_data_dir()
self.reasoner = AtlasReasoner(self.data_dir)
def get_top_cluster(self, cosmology: dict) -> tuple[str | None, dict | None]:
if not cosmology:
return None, None
items = list(cosmology.items())
if not items:
return None, None
cluster_name, cluster_data = items[0]
return cluster_name, cluster_data
def choose_route(self, reasoning_result: dict) -> dict:
activated_concepts = reasoning_result.get("activated_concepts", [])
cosmology = reasoning_result.get("cosmology", {})
links = reasoning_result.get("links", [])
if not activated_concepts:
route = DEFAULT_ROUTE.copy()
route["why"] = (
"Aucun concept central n'a Ă©tĂ© activĂ© nettement par la requĂȘte."
)
return route
concept_names = {item["concept"] for item in activated_concepts}
top_cluster, cluster_data = self.get_top_cluster(cosmology)
# ----------------------------------------
# RĂGLES DE PRIORITĂ LOCALES
# ----------------------------------------
# 1. VERBE prioritaire si verbe est explicitement activé
# et relié à conscience / esprit / partager
if "verbe" in concept_names:
for link in links:
a = link.get("a")
b = link.get("b")
internal = link.get("internal", False)
if not internal:
continue
pair = {a, b}
if (
pair == {"verbe", "conscience"}
or pair == {"verbe", "esprit"}
or pair == {"verbe", "partager"}
):
route = dict(ROUTES["VERBE"])
route["why"] = (
f"RÚgle locale VERBE activée | "
f"Lien interne dĂ©tectĂ© : {a} â {b} (poids {link['weight']})"
)
return route
# 2. LOI prioritaire si charte / vérité / justice sont activés ensemble
if {"charte", "verite"}.issubset(concept_names) or {"charte", "justice"}.issubset(concept_names):
route = dict(ROUTES["LOI"])
route["why"] = (
"RÚgle locale LOI activée | "
"charte + vérité / justice sont présentes ensemble."
)
return route
# 3. OMBRE prioritaire si déni + violence ou biais + violence
if {"deni", "violence"}.issubset(concept_names) or {"biais", "violence"}.issubset(concept_names):
route = dict(ROUTES["OMBRE"])
route["why"] = (
"RÚgle locale OMBRE activée | "
"une paire critique de dégradation est activée."
)
return route
# 4. TEMPLE prioritaire si art est activé avec vivant / corps / nature
if "art" in concept_names and (
"vivant" in concept_names or "corps" in concept_names or "nature" in concept_names
):
route = dict(ROUTES["TEMPLE"])
route["why"] = (
"RÚgle locale TEMPLE activée | "
"art est lié à vivant / corps / nature."
)
return route
# ----------------------------------------
# ROUTAGE PAR CLUSTER DOMINANT
# ----------------------------------------
if not top_cluster or top_cluster not in ROUTES:
route = DEFAULT_ROUTE.copy()
route["why"] = "Aucun cluster cosmologique fiable n'a émergé."
return route
route = dict(ROUTES[top_cluster])
activated_words = []
if cluster_data:
activated_words = [w for w, _freq in cluster_data.get("activated_words", [])]
internal_links = [x for x in links if x.get("internal")]
why_parts = [f"Cluster dominant : {top_cluster}"]
if activated_words:
why_parts.append(
"Mots activés dans ce cluster : " + ", ".join(activated_words)
)
if internal_links:
strongest = internal_links[0]
why_parts.append(
f"Lien interne principal : {strongest['a']} â {strongest['b']} "
f"(poids {strongest['weight']})"
)
route["why"] = " | ".join(why_parts)
return route
def build_router_output(self, query: str) -> dict:
reasoning = self.reasoner.answer(query)
route = self.choose_route(reasoning)
return {
"query": query,
"selected_route": route["route_name"],
"intent": route["intent"],
"route_description": route["description"],
"route_guidance": route["guidance"],
"route_why": route["why"],
"reasoning_summary": reasoning.get("symbolic_reading", ""),
"activated_concepts": reasoning.get("activated_concepts", []),
"top_links": reasoning.get("links", [])[:5],
"cosmology": reasoning.get("cosmology", {}),
"reasoning_path": reasoning.get("reasoning_path", []),
}
def pretty_print(self, result: dict):
print("\n" + "=" * 72)
print("COGNITIVE ROUTER")
print("=" * 72)
print(f"\nRequĂȘte : {result['query']}")
print(f"Route choisie : {result['selected_route']}")
print(f"Intent : {result['intent']}")
print("\n--- Pourquoi cette route ---")
print(result["route_why"])
print("\n--- Description ---")
print(result["route_description"])
print("\n--- Guidance ---")
for item in result["route_guidance"]:
print(f"- {item}")
print("\n--- Résumé du reasoner ---")
print(result["reasoning_summary"])
print("\n--- Concepts activés ---")
if result["activated_concepts"]:
for item in result["activated_concepts"]:
print(f"- {item['concept']} ({item['score']})")
else:
print("- aucun")
print("\n--- Top liens ---")
if result["top_links"]:
for link in result["top_links"]:
status = "interne" if link.get("internal") else "bord"
print(
f"- {link['a']} â {link['b']} | weight={link['weight']} | {status}"
)
else:
print("- aucun")
print("\n--- Top cosmologie ---")
for cluster, data in list(result["cosmology"].items())[:3]:
words = ", ".join(f"{w} ({f})" for w, f in data.get("activated_words", []))
if not words:
words = "aucun mot activé"
print(f"- {cluster} => {data['score']} | {words}")
print("\n--- Chemin de raisonnement ---")
if result["reasoning_path"]:
for step in result["reasoning_path"]:
neighbors = ", ".join(
f"{n['concept']} ({n['weight']})"
for n in step.get("neighbors", [])
)
if not neighbors:
neighbors = "aucun voisin"
print(f"- {step['concept']} -> {neighbors}")
else:
print("- aucun")
print()
def main():
if len(sys.argv) < 2:
print('Usage : python cognitive_router.py "ta question ici"')
sys.exit(1)
query = " ".join(sys.argv[1:]).strip()
router = CognitiveRouter()
result = router.build_router_output(query)
router.pretty_print(result)
out_path = Path("router_output.json")
with out_path.open("w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
print(f"[ok] résultat sauvegardé dans {out_path}")
if __name__ == "__main__":
main()



