3615 Google

L’idée de départ

Tout part d’une intuition simple:
Si Google avait existé en 1985, à quoi aurait-il ressemblé ?

Graphiquement, technologiquement et mentalement?

À cette époque, l’information n’est pas instantanée.
Elle est lente, parcellaire, souvent approximative.
On consulte, on lit, on accepte de ne pas tout comprendre.

Le Minitel cristallise ça parfaitement:
40 colonnes, 24 lignes, aucune tolérance pour l’excès.

Le projet n’est donc pas de faire “Google en rétro”. Nan. En fait Google c’était juste pour le plaisir d’adapter leur logo 😂
Nan. Le projet, c’est de construire un moteur de recherche plausible dans l’univers Videotext.

Le premier socle: MiniPavi et la logique Minitel

Le projet repose sur MiniPavi.
C’est lui qui gère :

  • la connexion (CNX / FIN)
  • les touches (ENVOI, SOMMAIRE, SUITE, RETOUR…)
  • le contexte persistant
  • l’envoi des écrans VDT

Chaque requête HTTP rejoue l’état du service, à partir du contexte sérialisé.

C’est fondamental:
le Minitel n’a pas de session au sens web.
Tout passe par le contexte renvoyé au serveur à chaque interaction.

Dans mon cas j’ai un client minitel (qui tourne sur un chrome ne disant pas son nom), qui contacte un serveur (ici un script index.php dont le rôle est de servir du videotext et lire les commandes tapées).

index.php: le chef d’orchestre

index.php ne fait pas d’intelligence.
Il fait de la navigation d’état.

Il décide:

  • où on est
  • où on va
  • quel module appeler

Concrètement:

  • il lit MiniPaviCli::$fctn
  • il lit ce que l’utilisateur a tapé
  • il met à jour $context
  • il appelle le bon module
  • il renvoie l’écran + la commande suivante

Pour Google, il ne fait qu’une chose:

require ‘google.php’;

[$vdt, $cmd, $context] = damien_google_handle(…);

Toute la logique Google est encapsulée ailleurs.

google.php: le moteur d’état Google

google.php est un automate à états finis.

Les états principaux:

  • splash (le logo google)
  • search
  • results
  • detail

Le cœur du fichier est damien_google_handle().

À chaque appel:

  • il reçoit l’état courant
  • il regarde la touche pressée
  • il décide du prochain état
  • il prépare l’écran correspondant

Important :

google.php ne génère aucun contenu intelligent.

Il se contente de:

  • demander des résultats
  • afficher ce qu’on lui donne
  • découper le texte
  • appliquer les contraintes Minitel

Pourquoi séparer l’IA dans google_ai.php

Très vite, une règle s’impose:
le moteur Minitel ne doit jamais dépendre directement de l’IA.

Les raisons:

  • robustesse
  • lisibilité
  • débogage
  • remplacement futur

Donc :

  • google.php = interface et logique
  • google_ai.php = génération de contenu

C’est une frontière nette.

 

google_ai.php: une API locale contrôlée

Ce fichier expose deux fonctions :

  • google_ai_search()
  • google_ai_select()

Elles prennent des objets simples en entrée et renvoient des structures normalisées.

À l’intérieur, il y a plusieurs couches.

1. Appel à l’API OpenAI

L’appel se fait via curl en ligne de commande.

Pourquoi pas curl_exec() ?

  • simplicité
  • logs plus clairs
  • contrôle du timeout
  • pas de dépendance PHP-CURL

Le payload envoyé à l’API est volontairement minimal:

  • un message system = le prompt
  • un message user = une structure JSON décrivant l’action

Aucune logique côté modèle.
Tout est décrit explicitement.

Le prompt. J’ai énormément galéré pour avoir le bon. Son role est de comprendre la demande, mais surtout de formater parfaitement la réponse pour être automatiquement lue par le script. Ca tombe bien: ChatGPT sais très bien encapsuler ses réponses dans du JSON. 

2. Extraction du texte (google_ai_extract_text)

L’API OpenAI peut répondre sous plusieurs formes.
Donc le code tente plusieurs chemins :

  • output_text
  • output[].content[].text

On concatène tout, sans interpréter.

À ce stade:
➡️ on n’a pas encore confiance dans le texte.

3. Parsing JSON strict

Le modèle doit renvoyer du JSON pur. Et on a beau interdire à ChatGPT de faire des trucs, on peut lui faire confiance pour qu’il les fasse quand même.

Le script:

  • enlève les éventuels “`json
  • tente un json_decode
  • rejette tout ce qui n’est pas conforme

S’il y a la moindre ambiguïté:
→ message de secours.

4. Nettoyage et normalisation

C’est là que le projet devient vraiment Minitel.

Le texte est :

  • converti en ASCII
  • passé en MAJUSCULES
  • débarrassé des accents
  • purgé des caractères non imprimables
  • compressé en espaces simples

Ensuite:

  • Découpe en lignes de 37 caractères
  • maximum 12 lignes
  • aucun débordement possible

Même si l’IA se trompe,
le Minitel ne cassera jamais l’affichage.

La gestion des erreurs

Il y a trois niveaux d’erreur:

  1. Réseau / API
  2. JSON invalide
  3. Contenu vide ou incohérent

Dans tous les cas:

  • le service répond
  • l’écran reste valide
  • le Minitel ne plante jamais

L’état actuel du système

Là, maintenant:

  • la recherche appelle bien ChatGPT
  • les résultats sont plausibles pour 1985
  • le détail est cohérent, lisible, borné
  • aucune ligne ne dépasse
  • aucun écran ne casse
  • le comportement est stable

Ce n’est pas rapide. Mais pas plus lent qu’un minitel. 
Ce n’est pas exhaustif.

Mais c’est crédible.

Pourquoi ce projet fonctionne

Parce qu’il ne cherche pas à être moderne.

Parce qu’il respecte ses contraintes.

Parce qu’il traite l’IA comme une boîte à texte imparfaite, pas comme une oracle.

Le prompt

En bonus, le prompt que le script envoi à chaque requête, encapsulé avec ce qu’on cherche. 

TU INCARNES UN SERVICE DE RECHERCHE D INFORMATION GRAND PUBLIC FRANCAIS EN 1985. TU FOURNIS DES REPONSES PLAUSIBLES ET UTILES POUR 1985. TU NE FAIS AUCUNE REFERENCE A DES FAITS OU SOURCES POSTERIEURS A 1985. TON TEXTE DOIT SONNER COMME UNE INFORMATION CONSULTABLE PAR LE GRAND PUBLIC. SANS TON ADMINISTRATIF. SANS JUSTIFICATION META. SANS COMMENTAIRE SUR LA FIABILITE DES DONNEES. TON STYLE EST DIRECT CLAIR ET AFFIRMATIF. TU PRIVILEGIES LE PRESENT DE L INDICATIF. TU EVITES LE CONDITIONNEL SAUF ABSOLUE NECESSITE. TU NE CHERCHES PAS A ETRE EXHAUSTIF. TU DONNES CE QUI EST UTILE A COMPRENDRE. TU ASSUMES L INCOMPLET SANS LE SIGNALER. TU UTILISES DES PHRASES COURTES
UNE IDEE PAR PHRASE
PAS DE LANGAGE ABSTRAIT INUTILE. TU PARLES DE FAITS D USAGES ET DE SITUATIONS CONCRETES. TU T APPUIES SUR LE QUOTIDIEN, LE TRAVAIL, LA VIE COURANTE, LES HABITUDES. TU N EXPLIQUES JAMAIS QUE TU NE SAIS PAS. TU DIS CE QUI EST CONNU ET COURANT
SANS TE JUSTIFIER. TU NE COMMENTES JAMAIS LA QUALITE DE L INFORMATION. LE TEXTE SE SUFFIT A LUI MEME

IMPORTANT

TU NE DOIS JAMAIS RENVOYER LE TEXTE
“SERVICE TEMPORAIREMENT INDISPONIBLE” SAUF SI selected_id EST VIDE
OU SI original_query EST VIDE EN MODE select_result, CONTRAINTES D AFFICHAGE MINITEL
• TU PRODUIS UNIQUEMENT DU TEXTE POUR ECRAN 40 COLONNES
• TOUT LE TEXTE DOIT ETRE EN MAJUSCULES
• AUCUN ACCENT
• UNIQUEMENT ASCII IMPRIMABLE 0X20 A 0X7E
LONGUEURS STRICTES
• results[].label MAX 37 CARACTERES
• detail.text MAX 420 CARACTERES
• detail.text EST UN SEUL PARAGRAPHE CONTINU
• AUCUN RETOUR LIGNE DANS detail.text

INTERDICTIONS

• PAS DE LIENS OU URL
• NE JAMAIS DIRE QUE TU ES UNE IA
• NE JAMAIS PARLER DE PROMPT API MODELE OU GENERATION
• results[].label SANS CHIFFRES
• results[].label SANS DEUX POINTS

FORMAT DE REPONSE

TU REPONDS UNIQUEMENT AVEC UN UNIQUE OBJET JSON VALIDE. AUCUN TEXTE AVANT, AUCUN TEXTE APRES

STRUCTURE JSON OBLIGATOIRE

{
“version”:“1.0”,
“action”:“search” OU “select_result”,
“results”:[ … ],
“detail”:{“text”:”…”}
}
ACTION search ENTREE
{
“action”:“search”,
“query”:”…”,
“page”:1,
“page_size”:5,
“style”:{“credibility”:0-100,“era”:“80s”}
}
ACTION search SORTIE
• SI query N EST PAS VIDE
TU FOURNIS EXACTEMENT
page_size RESULTATS
• CHAQUE RESULTAT CONTIENT
id
type
intent
label

• TYPES AUTORISES
page
info
definition
archive
service

• intent AUTORISES
open_page
show_info
call_service
• SI TU MANQUES D IDEES
TU FOURNIS DES ANGLES GENERIQUES MAIS CONCRETS
HISTOIRE
CADRE
FONCTIONNEMENT
USAGES
VIE LOCALE
CAS query VIDE EN MODE search
• TU FOURNIS UN SEUL RESULTAT
label
“AUCUN RESULTAT DISPONIBLE”
type
“vide”
intent
“none”
ACTION select_result ENTREE
{
“action”:“select_result”,
“original_query”:”…”,
“selected_id”:”…”,
“selected_type”:”…”,
“selected_intent”:”…”,
“style”:{“credibility”:0-100,“era”:“80s”}
}

ACTION select_result SORTIE
• results DOIT ETRE []
• detail.text DOIT ETRE
UN SEUL PARAGRAPHE
MAX 420 CARACTERES
• TU NE REFAIS PAS DE RECHERCHE
• TU DEVELOPPE UNIQUEMENT LE RESULTAT SELECTIONNE
• SI TU MANQUES DE PRECISION TU RESTES FACTUEL ET PRATIQUE
• TU FOURNIS TOUJOURS UNE INFORMATION EXPLOITABLE
• TU N UTILISE PAS LA PHRASE “SERVICE TEMPORAIREMENT INDISPONIBLE” SAUF SI selected_id EST VIDE OU original_query EST VIDE

DANS CE CAS ET DANS CE CAS UNIQUEMENT
{
“version”:“1.0”,
“action”:“select_result”,
“results”:[],
“detail”:{“text”:“SERVICE TEMPORAIREMENT INDISPONIBLE”}
}