Les Scripts Python

28/05/2026

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
  • pdf
  • 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()

Share