15. Tree-of-Thoughts sur de vrais problemes de recherche

Navigation : Index | << Précédent | Suivant >>

Phase 3 de l’epic #2926 — pont entre NB-12 (ToT demontre sur le 24-game), NB-13/NB-14 (routeur + memoire agentiques) et les series Search (CSP) / Sudoku.

Ou ToT bat le single-shot le plus nettement

Cadrage editorial (fidelite au papier source). Le ToT de Yao et al. (2023) met un LLM dans la boucle de recherche : il genere et evalue lui-même les pensées candidates (etat evaluator LLM, beam search de largeur b). Ce notebook adapte l’idee de ToT – recherche structuree sur un arbre de pensées – a un CSP déterministe ou la “pensee” = une affectation lettre->chiffre et l’“evaluation” = la contrainte arithmetique exacte de la colonne. Le LLM n’est donc pas dans la boucle de recherche (voir NB-12 pour le ToT LLM-guide sur le 24-game, et Exercice 2 ci-dessous pour un prototype LLM-guide). Cette distinction est explicite tout au long du notebook.

NB-12 demontre Tree-of-Thoughts sur le 24-game (combinatoire numérique). Mais c’est sur les problemes de satisfaction de contraintes exacts (CSP) – cryptarithmetic, Sudoku, N-queens – que l’ecart entre single-shot (un LLM qui devine la solution en un appel) et recherche structuree (ToT : developper des etats, evaluer, elaguer, backtrack) est le plus grand. Un LLM est mauvais au calcul arithmetique exact et aux contraintes d’unicite : il hallucine des chiffres, viole la règle “chaque lettre = un chiffre distinct”.

Tree-of-Thoughts (ToT) (Yao et al., 2023) generalise le chain-of-thought (Wei et al., 2022) et la self-consistency (Wang et al., 2022) en explorant un arbre d’etats ou chaque noeud est une pensee candidate, evaluee puis elaguee plutot que suivie en ligne droite.

Ide centrale de ce notebook : sur un CSP, ToT = recherche structuree sur l’espace des affectations. Chaque “pensee” = affecter une (ou plusieurs) lettre(s) a un chiffre ; l’evaluation = verifier la contrainte de colonne (+ retenue) ; le backtrack = abandonner la branche inconsistante. C’est exactement le backtracking + propagation des solveurs CSP formels (series Search / Sudoku, OR-Tools, Choco).

Plan

  1. Single-shot : le LLM resout un cryptarithme en un appel -> on mesure sa fiabilite (basse).
  2. ToT : recherche structuree (DFS colonne par colonne + retenue) -> solution garantie.
  3. Comparaison ToT vs single-shot (le livrable Phase 3).
  4. Pont Search/Sudoku : c’est du CSP, les LLM sont faibles sur les CSP exacts -> solveurs formels.

0. Setup — même infrastructure que NB-12 a NB-14

%pip install -q openai python-dotenv

import os, re, json, itertools
from pathlib import Path
from openai import OpenAI
from dotenv import load_dotenv

_env_path = None
_current = Path.cwd()
for _i in range(10):
    if (_current / ".env").exists():
        _env_path = _current / ".env"; break
    if _current.name in ("GenAI", "MyIA.AI.Notebooks"):
        break
    _current = _current.parent
if _env_path is None:
    for _cand in (Path.cwd() / "MyIA.AI.Notebooks" / "GenAI" / ".env",
                  Path.cwd() / "GenAI" / ".env"):
        if _cand.exists():
            _env_path = _cand; break
if _env_path is not None:
    load_dotenv(_env_path); print(f".env charge depuis : {_env_path.parent.name}/{_env_path.name}")

FAST_MODEL = os.getenv("OPENAI_MODEL_FAST", "meta-llama/llama-3.3-70b-instruct")
BIG_MODEL = os.getenv("OPENAI_MODEL_BIG", "openai/gpt-5-nano")
BATCH_MODE = os.getenv("BATCH_MODE", "true").lower() in ("1", "true", "yes")
client = OpenAI(api_key=os.getenv("OPENROUTER_API_KEY"),
                base_url=os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1"))
print(f"FAST_MODEL={FAST_MODEL} | BATCH_MODE={BATCH_MODE}")
Note: you may need to restart the kernel to use updated packages.
.env charge depuis : GenAI/.env
FAST_MODEL=meta-llama/llama-3.3-70b-instruct | BATCH_MODE=False
def chat(prompt, system=None, model=FAST_MODEL, temperature=0.0, max_tokens=1000):
    messages = ([{"role": "system", "content": system}] if system else []) \
               + [{"role": "user", "content": prompt}]
    try:
        resp = client.chat.completions.create(model=model, messages=messages,
                                              temperature=temperature, max_tokens=max_tokens)
        return resp.choices[0].message.content or ""
    except Exception as exc:
        print(f"  [chat] erreur : {exc}")
        return ""

_ping = chat("Reponds uniquement par : OK", max_tokens=10)
print("Ping :", repr(_ping[:40]) if _ping else "ECHEC")
Ping : 'OK'

1. Single-shot — le LLM resout-il un cryptarithme en un appel ?

On demande au LLM de resoudre CROSS + ROADS = DANGER (cryptarithme moins celebre que SEND+MORE=MONEY, pour eviter la memorisation) en un seul appel, puis on verifie la proposition (unicite des chiffres + validite arithmetique). On repete pour mesurer la fiabilite – un LLM est typiquement peu fiable sur ce genre de tâche exacte.

PUZZLES = {
    "SEND+MORE=MONEY": (["SEND", "MORE"], "MONEY"),
    "CROSS+ROADS=DANGER": (["CROSS", "ROADS"], "DANGER"),
}

def valider_affectation(affect, addends, result):
    """Verifie qu'une affectation lettre->chiffre est valide : unicite, pas de 0 en tete,
    et l'egalite arithmetique est vraie. Renvoie (ok, detail)."""
    if not affect:
        return False, "affectation vide"
    lettres = set(ch for w in addends + [result] for ch in w)
    if set(affect.keys()) != lettres:
        return False, f"lettres manquantes/supplementaires ({set(affect.keys())} vs {lettres})"
    digits = list(affect.values())
    if len(set(digits)) != len(digits):
        return False, "chiffres non distincts"
    for w in addends + [result]:
        if affect[w[0]] == 0:
            return False, f"zero en tete ({w[0]})"
    try:
        v = lambda w: int("".join(str(affect[ch]) for ch in w))
        ok = sum(v(w) for w in addends) == v(result)
        return ok, ("correct" if ok else "egalite fausse")
    except Exception as exc:
        return False, f"erreur eval : {exc}"

def extraire_affectation(reponse):
    """Extrait lettre->chiffre d'une reponse LLM (tolere JSON ou 'S=9' ligne par ligne)."""
    affect = {}
    for lettre, chiffre in re.findall(r'([A-Za-z])\s*[:=]\s*([0-9])', reponse or ""):
        affect[lettre.upper()] = int(chiffre)
    return affect

def single_shot(addends, result, model=FAST_MODEL, temperature=0.7):
    """Le LLM resout en un appel. Renvoie (affect, ok, detail)."""
    puzzle = " + ".join(addends) + " = " + result
    prompt = (f"Resous le cryptarithme {puzzle}. Chaque lettre = un chiffre distinct (0-9). "
              f"Une lettre en tete de mot ne vaut jamais 0. "
              f"Reponds avec UNE affectation par ligne au format 'LETTRE=chiffre' (ex: S=9).")
    rep = chat(prompt, model=model, temperature=temperature, max_tokens=400)
    affect = extraire_affectation(rep)
    ok, detail = valider_affectation(affect, addends, result)
    return affect, ok, detail, rep

print("single_shot + validateur definis.")
single_shot + validateur definis.
# Demo single-shot sur CROSS+ROADS=DANGER, repete N fois (BATCH_MODE -> petit N).
N_RUNS = 3 if BATCH_MODE else 8
addends, result = PUZZLES["CROSS+ROADS=DANGER"]
succes = 0
print(f"Single-shot sur CROSS+ROADS=DANGER ({N_RUNS} essais, FAST_MODEL={FAST_MODEL})")
print("-" * 60)
for i in range(N_RUNS):
    affect, ok, detail, rep = single_shot(addends, result, temperature=0.8)
    succes += int(ok)
    print(f"  essai {i+1} : {detail:24s} | reponse LLM : {(rep[:50]).strip()!r}")
print("-" * 60)
print(f"Fiabilite single-shot : {succes}/{N_RUNS} = {100*succes/N_RUNS:.0f}%")
print("=> Le LLM est peu fiable sur ce CSP exact (calcul arithmetique + unicite).")
Single-shot sur CROSS+ROADS=DANGER (8 essais, FAST_MODEL=meta-llama/llama-3.3-70b-instruct)
------------------------------------------------------------
  essai 1 : egalite fausse           | reponse LLM : 'R=5\nO=2\nC=1\nS=9\nD=8\nA=7\nN=4\nG=6\nE=3'
  essai 2 : affectation vide         | reponse LLM : 'Pour résoudre ce cryptarithme, nous devons trouver'
  essai 3 : lettres manquantes/supplementaires ({'R', 'S'} vs {'N', 'O', 'S', 'E', 'G', 'C', 'R', 'D', 'A'}) | reponse LLM : 'Pour résoudre ce cryptarithme, commençons par les'
  essai 4 : egalite fausse           | reponse LLM : 'R=4\nO=1\nS=9\nC=3\nD=6\nA=0\nN=8\nG=7\nE=5'
  essai 5 : lettres manquantes/supplementaires ({'O', 'S', 'E', 'C', 'R', 'D', 'A'} vs {'N', 'O', 'S', 'E', 'G', 'C', 'R', 'D', 'A'}) | reponse LLM : 'Je vais essayer de résoudre le cryptarithme étape'
  essai 6 : lettres manquantes/supplementaires ({'N', 'O', 'B', 'S', 'E', 'G', 'C', 'R', 'D', 'A'} vs {'N', 'O', 'S', 'E', 'G', 'C', 'R', 'D', 'A'}) | reponse LLM : 'Voici la solution du cryptarithme :\n\nC = 1\nR = 8\nO'
  essai 7 : lettres manquantes/supplementaires ({'R', 'S'} vs {'N', 'O', 'S', 'E', 'G', 'C', 'R', 'D', 'A'}) | reponse LLM : 'Pour résoudre ce cryptarithme, nous allons procéde'
  essai 8 : egalite fausse           | reponse LLM : 'R=5\nO=2\nC=1\nS=9\nD=8\nA=7\nN=4\nG=6\nE=3'
------------------------------------------------------------
Fiabilite single-shot : 0/8 = 0%
=> Le LLM est peu fiable sur ce CSP exact (calcul arithmetique + unicite).

Lecture : pourquoi le LLM echoue a 0/8 – anatomie des modes d’echec

Le resultat 0/8 n’est pas un echec uniforme : les 8 essais se repartissent en quatre modes d’echec distincts, chacun revelateur d’une limitation fondamentale des LLM autoregressifs sur les problemes combinatoires exacts.

Mode d’echec Essais Ce que fait le LLM Limitation revelee
Egalite fausse 1, 4, 8 Produit une affectation complete et bien formee (9 lettres, chiffres uniques, pas de 0 en tete)… mais l’arithmetique est fausse Le LLM n’a aucun verificateur interne : il genere une reponse plausible, sans pouvoir la tester. C’est le piege le plus dangereux, car la sortie a l’air valide.
Affectation vide 2 Se lance dans une prose explicative (« Pour resoudre ce cryptarithme, nous devons… ») sans jamais produire l’affectation demandee Le LLM discute au lieu de resoudre : il optimise la fluidite du texte, non la production d’une structure contrainte.
Lettres manquantes/supplementaires 3, 5, 7 Donne une affectation partielle ou sur-complete (jeu de lettres != celui du puzzle) Le LLM ne suit pas l’ensemble exact des variables du probleme : il perd des lettres ou en ajoute au fil de la generation.
Hallucination de lettre 6 Invente une lettre « B » qui n’appartient pas a CROSS+ROADS=DANGER Hallucination structurelle : le modele fabrique des elements absents du contexte, d’autant plus sur un puzzle moins connu (anti-memorisation).

La lecon generale. Ces quatre modes pointent vers une cause unique : un LLM predit le prochain token le plus plausible, il ne resout pas de contraintes. Resoudre un cryptarithme exige trois choses qu’aucune prediction de token ne garantit – (1) du calcul arithmetique exact (retenues de colonne), (2) le suivi d’un ensemble precis de variables (les lettres du puzzle), et (3) une verification (l’egalite est-elle vraie ?). Le LLM n’a ni l’un, ni l’autre, ni le troisieme de facon fiable : il « devine » une reponse qui ressemble a une solution sans pouvoir la controler.

Transition vers Tree-of-Thoughts. C’est precisement ce diagnostic qui motive la section suivante : au lieu de demander au LLM une solution en un coup (et de subir ces quatre modes d’echec), on externalise la recherche et la verification dans un algorithme explicite – un backtracking colonne par colonne avec propagation de retenue. Cet algorithme est deterministe et exact : il trouve l’unique solution (cf. sortie suivante : 96233+62513=158746) ou prouve l’absence, sans aucune hallucination ni derive arithmetique. Le LLM etait le mauvais outil pour le calcul exact ; le CSP, lui, est fait pour cela.

2. Tree-of-Thoughts — recherche structuree garantie correcte

On implemente le cryptarithme comme une recherche Tree-of-Thoughts : un arbre dont les noeuds sont des affectations partielles lettre->chiffre. On developpe colonne par colonne (de la droite, les unites, vers la gauche), on essaie les chiffres disponibles, on verifie la contrainte de colonne + la retenue, et on backtrack si inconsistante.

C’est du backtracking + propagation – le paradigme CSP classique (Russell & Norvig, 2020, ch. 6 ; series Search / Sudoku de ce depot). Pour le cryptarithme, la recherche est déterministe et exacte : elle trouve la solution (unique ici) ou prouve l’absence, sans hallucination. Le LLM n’intervient pas dans la recherche elle-même – c’est honnete (G.2) : sur un CSP exact, la structure de recherche apporte la valeur, pas le LLM (voir Exercice 2 pour un ToT LLM-guide).

def resoudre_cryptarithme(addends, result, find_all=False):
    """Tree-of-Thoughts : DFS colonne par colonne (droite->gauche) avec propagation de retenue.
    Chaque 'pensee' = affecter les lettres non-affectees d'une colonne a des chiffres
    disponibles ; evaluation = contrainte de colonne (somme % 10 == chiffre resultat) ;
    backtrack si inconsistante. Renvoie la liste des solutions (dicts lettre->chiffre)."""
    words = list(addends) + [result]
    leading = {w[0] for w in words}                 # lettres de tete (jamais 0)
    ncol = max(len(w) for w in words)
    solutions, assign, used = [], {}, set()

    def dfs(col, carry):
        if col == ncol:                             # toutes colonnes traitees
            if carry == 0:
                solutions.append(dict(assign))
                return not find_all                 # True = stop (premiere solution)
            return False
        # lettres de cette colonne (addends puis resultat)
        ac = [w[-(col + 1)] for w in addends if col < len(w)]
        rc = result[-(col + 1)] if col < len(result) else None
        col_letters = list(dict.fromkeys(ac + ([rc] if rc else [])))
        unassigned = [ch for ch in col_letters if ch not in assign]
        avail = [d for d in range(10) if d not in used]
        for combo in itertools.permutations(avail, len(unassigned)):
            nouveau, ok = {}, True
            for ch, d in zip(unassigned, combo):
                if ch in leading and d == 0:        # pas de 0 en tete
                    ok = False; break
                nouveau[ch] = d
            if not ok:
                continue
            assign.update(nouveau); used.update(combo)
            s = sum(assign[ch] for ch in ac) + carry
            nouveau_carry = s // 10
            # contrainte de colonne : le chiffre des unites de la somme == resultat
            if (rc is None) or (rc in assign and s % 10 == assign[rc]):
                if dfs(col + 1, nouveau_carry):
                    return True
            used.difference_update(combo)           # backtrack
            for ch in unassigned:
                assign.pop(ch, None)
        return False

    dfs(0, 0)
    return solutions

# Demo : ToT resout les deux cryptarithmes (deterministe, garanti correct).
for nom, (addends, result) in PUZZLES.items():
    sols = resoudre_cryptarithme(addends, result)
    print(f"\n{nom} : {len(sols)} solution(s) trouvee(s) par ToT")
    for s in sols[:2]:
        ok, detail = valider_affectation(s, addends, result)
        v = lambda w: int("".join(str(s[ch]) for ch in w))
        print(f"  {s} | {detail} | {v(addends[0])}+{v(addends[1])}={v(result)}")

SEND+MORE=MONEY : 1 solution(s) trouvee(s) par ToT
  {'D': 7, 'E': 5, 'Y': 2, 'N': 6, 'R': 8, 'O': 0, 'S': 9, 'M': 1} | correct | 9567+1085=10652

CROSS+ROADS=DANGER : 1 solution(s) trouvee(s) par ToT
  {'S': 3, 'R': 6, 'D': 1, 'E': 4, 'O': 2, 'A': 5, 'G': 7, 'N': 8, 'C': 9} | correct | 96233+62513=158746

3. Comparaison ToT vs single-shot (livrable Phase 3)

On compare sur le même puzzle : fiabilite (taux de succes) et garantie de correction. ToT est déterministe et garantit la solution ; single-shot depend du modèle/du tirage.

def benchmark(addends, result, n_runs=3):
    """Compare single-shot (N tirages) vs ToT (deterministe) sur un puzzle."""
    nom = " + ".join(addends) + " = " + result
    # Single-shot
    ok_ss = 0
    for _ in range(n_runs):
        _, ok, _, _ = single_shot(addends, result, temperature=0.8)
        ok_ss += int(ok)
    # ToT
    sols = resoudre_cryptarithme(addends, result)
    tot_correct = len(sols) >= 1 and valider_affectation(sols[0], addends, result)[0]
    print(f"\n{nom}")
    print(f"  Single-shot : {ok_ss}/{n_runs} reussis ({100*ok_ss/n_runs:.0f}%) -- non garanti")
    print(f"  ToT         : {'GARANTI correct' if tot_correct else 'AUCUNE solution'} -- deterministe")
    return {"puzzle": nom, "single_shot_pct": 100*ok_ss/n_runs, "tot_garanti": tot_correct}

N = 3 if BATCH_MODE else 6
resultats = [benchmark(add, res, n_runs=N) for add, res in PUZZLES.values()]
print("\n=> Conclusion : sur les CSP exacts, la recherche structuree (ToT) garantit la "
      "solution la ou le single-shot est peu fiable. C'est l'ecart max de valeur de ToT.")

SEND + MORE = MONEY
  Single-shot : 3/6 reussis (50%) -- non garanti
  ToT         : GARANTI correct -- deterministe

CROSS + ROADS = DANGER
  Single-shot : 0/6 reussis (0%) -- non garanti
  ToT         : GARANTI correct -- deterministe

=> Conclusion : sur les CSP exacts, la recherche structuree (ToT) garantit la solution la ou le single-shot est peu fiable. C'est l'ecart max de valeur de ToT.

4. Pont vers les series Search / Sudoku

Ce qu’on vient de faire – backtracking avec propagation de contraintes – c’est exactement le paradigme CSP (Constraint Satisfaction Problem) des series Search (CSP, Mixed) et Sudoku de ce depot. Le cryptarithme est un CSP : variables = lettres, domaines = {0..9}, contraintes = “toutes différentes” + egalite arithmetique colonne par colonne.

  • La serie Search formalise CSP / backtracking / forward-checking / AC-3, avec des solveurs dedies (Python, parfois OR-Tools ou Choco pour les grandes instances).
  • La serie Sudoku applique le même paradigme a des grilles (variables = cases, contraintes = ligne/colonne/carre).

Pourquoi un LLM echoue la ou la recherche reussit ? Les LLM sont entraines a predire du texte, pas a respecter des contraintes exactes (unicite, arithmetique). Sur un CSP, la recherche exhaustive elaguee est complete et correcte par construction ; le LLM, lui, “devine”. D’ou le pont : pour les CSP, un solveur formel battra toujours un LLM single-shot (et c’est ce que demontre cette comparaison).

Exercice 1 te fait ajouter de la propagation (forward-checking) ; Exercice 3 te fait resoudre une grille de Sudoku partielle avec la même boucle de recherche.

5. Travaux pratiques

Les exercices sont a completer (convention C.1 : pas d’erreur volontaire).

Exercice 1 : propagation de contraintes (forward-checking / AC-3 leger)

La recherche actuelle elague quand une colonne echoue. Ajoute de la propagation : des qu une lettre est affectee, retire sa valeur des domaines des lettres encore libres (forward- checking), et coupe une branche des qu’un domaine devient vide.

Indice : remplace avail = [d for d in range(10) if d not in used] par un suivi de domaines par lettre ; a chaque affectation, reduis les domaines ; backtrack = restaurer.

def resoudre_avec_propagation(addends, result, find_all=False):
    """Exercice 1 : ToT + forward-checking (propagation de contraintes)."""
    # TODO etudiant : adapter resoudre_cryptarithme avec un suivi de domaines par lettre
    # et elaguer une branche des qu'un domaine devient vide (forward-checking).
    return None

_r = resoudre_avec_propagation(["SEND", "MORE"], "MONEY")
print(f"Exercice 1 - forward-checking : {'implemente' if _r is not None else 'a completer'}")
Exercice 1 - forward-checking : a completer

Exercice 2 : ToT LLM-guide (le LLM propose / classe les branches)

La recherche actuelle est purement combinatoriale (pas de LLM). Implemente un ToT ou le LLM propose des affectations candidates pour la colonne courante et les classe (les plus prometteuses d’abord), la recherche verifiant ensuite la contrainte. Compare le nombre de noeuds expliores vs le ToT combinatoire pur.

Indice : a chaque colonne, demande au LLM “propose 3 affectations pour D,E,Y telles que D+E finisse par Y” ; essaie-les dans l’ordre ; backtrack si aucune ne valide.

def tot_llm_guide(addends, result, model=FAST_MODEL):
    """Exercice 2 : ToT ou le LLM propose/classe les branches de recherche."""
    # TODO etudiant : a chaque colonne, appeler le LLM pour proposer des candidats,
    # les essayer dans l'ordre prometteur, verifier la contrainte, backtracker.
    return None

_r2 = tot_llm_guide(["SEND", "MORE"], "MONEY")
print(f"Exercice 2 - ToT LLM-guide : {'implemente' if _r2 is not None else 'a completer'}")
Exercice 2 - ToT LLM-guide : a completer

Exercice 3 (avance) : Sudoku partiel (pont serie Sudoku)

Adapte la boucle de recherche (DFS + contraintes + backtrack) a une grille de Sudoku partielle : variables = cases vides, domaines = {1..9}, contraintes = ligne/colonne/carre. Resous une grille facile et compare avec un solveur dedie de la serie Sudoku.

Indice : represente la grille comme un dict (ligne, col) -> valeur ; contraintes = ligne, colonne, bloc 3x3 ; DFS sur les cases vides dans l’ordre. Pont vers la serie Sudoku.

def resoudre_sudoku(grille):
    """Exercice 3 : DFS + contraintes (ligne/colonne/carre) sur une grille Sudoku partielle.
    `grille` = dict {(ligne, col): valeur} pour les cases connues (1-9)."""
    # TODO etudiant : DFS sur les cases vides, essai 1..9, verif ligne/colonne/carre, backtrack.
    return None

print(f"Exercice 3 - Sudoku : {'implemente' if False else 'a completer'}")
Exercice 3 - Sudoku : a completer

6. Conclusion et suite

On a etendu Tree-of-Thoughts (NB-12, 24-game) aux vrais problemes de recherche : cryptarithmetic (SEND+MORE=MONEY, CROSS+ROADS=DANGER) via DFS colonne-par-colonne avec propagation de retenue. La comparaison ToT vs single-shot montre l’ecart maximal de valeur de la recherche structuree : sur un CSP exact, ToT garantit la solution, le single-shot est peu fiable.

Limites honnetes (G.2) : - Le LLM n’apporte rien a la recherche exacte : sur le cryptarithme, la recherche combinatoire est déterministe et optimale ; un LLM “guide” (Exercice 2) ajoute du bruit/cout. La valeur de ToT ici est la structure de recherche, pas l’LLM. - Echelle : la DFS colonne-par-colonne est efficace pour quelques mots, mais sur de grands CSP (Sudoku difficile, emploi du temps), il faut de la propagation (AC-3, forward- checking, Exercice 1) voire un solveur dedie (OR-Tools, Choco). - Single-shot peut reussir par chance/memorisation : sur SEND+MORE=MONEY (celebre), un grand modèle peut avoir memorise la solution. C’est pourquoi la demo principale utilise CROSS+ROADS=DANGER (moins celebre).

Suite de l’epic #2926 : - Phase 4 — courbes de scaling Snell 2024 (quand echantillonner large vs chercher profond). - Phase 5 — hand-rolled vs modèles a raisonnement natif (gpt-5-thinking), cout-normalise. - Phase 6 — plugin Semantic Kernel (pont series SemanticKernel).

References academiques : - Yao, S. et al. (2023) - Tree of Thoughts: Deliberate Problem Solving with Large Language Models. arXiv:2305.10601. (Framework ToT original, LLM-guided.) - Wei, J. et al. (2022) - Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. arXiv:2201.11903. (Precurseur CoT.) - Wang, X. et al. (2022) - Self-Consistency Improves Chain of Thought Reasoning in Language Models. arXiv:2203.11171. (Self-consistency, voisin de ToT.) - Snell, C. et al. (2024) - Scaling LLM Test-Time Compute Optimally can be More Effective than Scaling Model Parameters. (Scaling test-time, Phase 4.) - Russell, S. & Norvig, P. (2020) - Artificial Intelligence: A Modern Approach (4e ed.), ch. 6. (Backtracking + propagation CSP.)

Ressources internes : series Search (CSP) / Sudoku de ce depot ; NB-12 (moteurs ToT LLM-guide), NB-13 (routeur), NB-14 (memoire).

Retour au sommet