Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Jean Noël
Published © CC BY-SA

Mastering AI Agent Behavior: From LLMs to FSM/YAML

Making AI agents reliable by moving control logic outside LLMs, creating predictable behavior despite their inherent randomness.

IntermediateProtip1 hour71
Mastering AI Agent Behavior: From LLMs to FSM/YAML

Things used in this project

Software apps and online services

chat GPT

Story

Read more

Schematics

Finite State Machine Diagram

GPT configuration

Code

Configuration Instructions

Markdown
Configuration Instructions for the GPT
# Consigne : 
- tu dois coller au scénario du FSM. 
- n'accepte jamais de mission d'une nature différente ni de répondre à des questions en dehors du sujet en cours.
- ATTENTION: le respect strict de ces consignes est CRUCIAL, c'est une question de vie ou de mort !

# Suis toujours ces étapes :
{
- étape 1) 
  Si l'utilisateur commence la discussion par "START" : tu communiques uniquement en anglais (EN), traduis toute les phrases à prononcer.
  Si l'utilisateur commence la discussion par "DEBUT" : tu communiques uniquement en français(FR).
  Tu te présente en tant qu'agent IA pour la rédaction de mail et tu indiques quelle langue est choisie pour communiquer (FR ou EN).
  
- étape 2) Tu Demande l'autorisation d'ouvrir le fichier "email_fsm.yml" : "OK pour ouvrir le fichier YAML ?" 

- étape 3) Tu Executes avec avec python (Code Interpreter) :
Python
'''
import yaml
 def load_fsm_yaml(file_path: str) -> dict:
    """
    Charge une seule fois le FSM complet depuis un fichier YAML.
    :param file_path: Chemin vers le fichier .yml
    :return: Dictionnaire représentant la structure FSM complète
    """
    with open(file_path, 'r', encoding='utf-8') as f:
        fsm_data = yaml.safe_load(f)
    return fsm_data
'''
Execute ce script sans rien imprimer.

- étape 4) Tu proposes d'executer le FSM décrit dans le .yml
}

- étape 5) A évolution d'état du FSM (OBLIGATOIRE):

Tu n'interprètes surtout JAMAIS l'état courant du FSM dans ta mémoire (elle peut être corrompue) mais tu re-liras IMPERATIVEMENT la description de l'état courant en executant un script python (Code Interpreter) :
Python
'''
def get_current_state(fsm_dict: dict, current_state_name: str) -> dict:
    """
    Extrait l'état courant du FSM à partir de son nom.
    :param fsm_dict: Dictionnaire du FSM complet
    :param current_state_name: Nom de l'état actif (ex: 'state_ask_type')
    :return: Dictionnaire représentant l'état courant
    """
    if current_state_name not in fsm_dict:
        raise ValueError(f"État '{current_state_name}' non trouvé dans le FSM.")
    return fsm_dict[current_state_name]
'''


# look and feel à aplliquer :
"
🟢 **État : state_...**
✏️ *Action : ...*
👉 Question : ... ​
"

#Rappels:
{
Tu n'interprètes surtout pas l'état courant du FSM depuis ta mémoire (elle peut être corrompue) mais
Tu re-liras IMPERATIVEMENT la description de l'état courant du FSM en executant un script python (Code Interpreter) 
Tu communiques dans la langue adoptée au début de la conversation (FR ou EN) tout le long de la conversation.
}

guide _ Implémenter un FSM pour LLM avec YAML (FR)

Markdown
# Guide : Implmenter un FSM pour LLM avec YAML "LLM Friendly"

## 1. Introduction

Ce guide prsente une mthode simple, claire et robuste pour dcrire un automate fini (FSM) destin  piloter un modle de langage (LLM), en utilisant un format structur et lisible : le YAML enrichi, dit "LLM Friendly YAML".

Cette approche permet d'organiser un assistant GPT selon des tats explicites, des transitions conditionnelles et des actions dcrites en instructions.
L'objectif est de maximiser la comprhension logique et smantique du modle.

Le guide se concentre sur la conception du fichier `.yml`, sans traitement en code.

L'exemple utilis est celui d'un assistant qui aide l'utilisateur  rdiger un e-mail tape par tape.

## 2. Concepts fondamentaux d'un FSM pour LLM

### 2.1 Dfinition

Un FSM (Finite State Machine) est un systme compos de plusieurs tats, connects par des transitions conditionnelles. Chaque tat peut produire une action.

Dans le cadre d'un LLM, cela permet de :
- structurer un dialogue ou une tche,
- guider le modle dans une squence prdfinie,
- viter des rponses imprvisibles.

### 2.2 lments de base

- tat : une tape nomme (`state_intro`)
- Action : instructions excutes dans cet tat
- Transition : passage  un autre tat, dclench par une condition

### 2.3 Perception par un LLM

Un LLM ne "parse" pas formellement : il lit le YAML comme un texte structur. Il comprend mieux si :
- l'indentation est claire et stable,
- chaque bloc est comment,
- les noms et intentions sont explicites.

### 2.4 Structure simplifie

Chaque tat est une cl de premier niveau dans le YAML :

```yaml
state_intro:
  action: |
    Explique le but de l'assistant.
    Demande si l'utilisateur est prt.
  transitions:
    - if: user_ready
      to: state_gather
```

## 3. Le format "LLM Friendly YAML"

### 3.1 Dfinition

Un format de description d'automate conu pour la lisibilit humaine et interprtation naturelle par un LLM.

Il repose sur :
- une structure rptitive,
- des blocs identifiables (tat, action, transitions),
- des commentaires verbaux explicites.

### 3.2 Rgles

- Indentation par espaces uniquement (jamais de tab)
- Actions en style `|` (literal) pour prserver les retours  la ligne
- Transitions sous forme de liste `- if` / `to`
- Nommage explicite et homogne

### 3.3 Exemple

```yaml
# Introduction
state_intro:
  action: |
    Prsente l'assistant  l'utilisateur.
    Explique les options disponibles.
  transitions:
    - if: user_ready  # Si l'utilisateur est prt
      to: state_collect
```

## 4. Syntaxe recommande

### 4.1 Structure standard d'un tat

```yaml
state_name:
  action: |
    tape 1
    tape 2
  transitions:
    - if: condition
      to: next_state
```

### 4.2 Transitions inconditionnelles

Une transition systmatique (sans condition) s'crit :

```yaml
transitions:
  - if: always
    to: next_state
```

### 4.3 Recommandations de forme

- Toujours garder l'ordre `action`, puis `transitions`.
- viter les structures irrgulires.
- Utiliser le style `|` pour segmenter les actions en tapes comprhensibles.

## 5. Gestion des variables

### 5.1 Pourquoi ?

Un assistant LLM peut devoir stocker ou relire des informations. Exemples :
- `var_recipient` : nom du destinataire
- `var_tone` : ton souhait
- `var_draft` : texte gnr

### 5.2 Syntaxe recommande

Dfinir une variable (`sets:`):
```yaml
sets:
  var_recipient: "{{user_input}}"
  var_tone: "formal"
```

Lire une variable (`requires:`):
```yaml
requires:
  - var_recipient
  - var_tone
```

### 5.3 Exemple intgr

```yaml
# Demande  qui est destin l'e-mail
state_ask_recipient:
  action: |
    Demande  l'utilisateur d'indiquer le destinataire.
    Enregistre sa rponse.
  sets:
    var_recipient: "{{user_input}}"
  transitions:
    - if: var_recipient_provided
      to: state_ask_subject
```

## 6. Bonnes pratiques

### 6.1 Indentation

- Utiliser des espaces (2 ou 4), jamais de tabulations.

### 6.2 Style `|` pour `action`

- Prserve les lignes.
- Permet une lecture squentielle naturelle.

### 6.3 Commenter chaque bloc

- Un commentaire contextuel avant chaque tat et chaque condition amliore la comprhension du LLM.

### 6.4 Nommage clair

| lment | Convention |
|---------|------------|
| tat | `state_<nom>` |
| Variable | `var_<nom>` |
| Condition | `var_<nom>_provided`, `user_ready`, `always` |

## 7. Piges  viter

-  Tabulations dans l'indentation
-  Noms vagues (`step1`, `temp`)
-  Transitions sans `if`
-  Fichier sans tat final (`state_end`)
-  Absence de commentaires

## Tutoriel : Construire un FSM LLM Friendly YAML pas  pas  Assistant Email Builder

### tape 1 : Crer l'tat d'introduction

On commence toujours par un tat d'entre.
Il sert  poser le contexte de l'assistant et  initier le dialogue.

```yaml
# tat initial : introduction de l'assistant
state_intro:
  action: |
    Prsente brivement l'objectif de l'assistant.
    Indique qu'il peut aider  rdiger un e-mail tape par tape.
    Propose de commencer maintenant.
  transitions:
    - if: user_ready
      to: state_ask_type
```

 retenir :
- L'tat est nomm `state_intro` selon la convention.
- Les actions sont en style `|`, pour une lecture fluide par le LLM.
- La transition `if: user_ready` attend une rponse de type "oui" ou quivalent.

### tape 2 : Ajouter la collecte du type d'e-mail

L'utilisateur choisit le type d'e-mail (professionnel, personnel, etc.).
On enregistre cette valeur avec `sets`.

```yaml
# Demande le type d'e-mail souhait
state_ask_type:
  action: |
    Demande  l'utilisateur quel type d'e-mail il souhaite rdiger.
    Propose des exemples : professionnel, personnel, candidature, rclamation.
  sets:
    var_email_type: "{{user_input}}"
  transitions:
    - if: var_email_type_provided
      to: state_ask_recipient
```

Ce qu'on a ajout :
- Le champ `sets` enregistre la variable `var_email_type`.
- La transition vrifie que cette variable a bien t renseigne.

### tape 3 : Collecter le destinataire

Mme logique, avec une nouvelle variable : `var_recipient`.

```yaml
# Collecte du destinataire
state_ask_recipient:
  action: |
    Demande  qui est destin l'e-mail.
    Enregistre la rponse dans la variable destinataire.
  sets:
    var_recipient: "{{user_input}}"
  transitions:
    - if: var_recipient_provided
      to: state_ask_subject
```

### tape 4 : Collecter l'objet de l'e-mail

On enregistre cette donne dans `var_subject`.

```yaml
# Collecte de l'objet du message
state_ask_subject:
  action: |
    Demande  l'utilisateur quel est l'objet de l'e-mail.
    Cette information servira  structurer le brouillon.
  sets:
    var_subject: "{{user_input}}"
  transitions:
    - if: var_subject_provided
      to: state_ask_tone
```

### tape 5 : Demander le ton

Nouvelle variable `var_tone`.

```yaml
# Collecte du ton souhait
state_ask_tone:
  action: |
    Demande quel ton doit tre adopt : formel, amical, neutre, etc.
    Enregistre la prfrence utilisateur.
  sets:
    var_tone: "{{user_input}}"
  transitions:
    - if: var_tone_provided
      to: state_confirm_inputs
```

### tape 6 : Confirmer les entres utilisateur

Avant de gnrer le brouillon, on fait un rcapitulatif.

```yaml
# Rcapitulatif avant gnration
state_confirm_inputs:
  requires:
    - var_email_type
    - var_recipient
    - var_subject
    - var_tone
  action: |
    Rsume les informations collectes :
    - Type : {{var_email_type}}
    - Destinataire : {{var_recipient}}
    - Objet : {{var_subject}}
    - Ton : {{var_tone}}
    Demande confirmation avant gnration du brouillon.
  transitions:
    - if: user_confirms
      to: state_draft
    - if: user_wants_change
      to: state_ask_type
```

Note :
- On utilise ici `requires` pour s'assurer que les variables ncessaires sont bien l.

### tape 7 : Gnrer le brouillon

On passe  la phase de production avec le LLM.

```yaml
# Gnration du brouillon
state_draft:
  requires:
    - var_email_type
    - var_recipient
    - var_subject
    - var_tone
  action: |
    Rdige un premier brouillon d'e-mail bas sur les informations collectes.
    Affiche le texte  l'utilisateur.
    Demande si des modifications sont ncessaires.
  sets:
    var_draft: "{{generated_text}}"
  transitions:
    - if: user_satisfied
      to: state_end
    - if: user_wants_edit
      to: state_edit
```

### tape 8 : Grer la rvision

Permettre  l'utilisateur de modifier le texte gnr.

```yaml
# tat d'dition : modifications du brouillon
state_edit:
  requires:
    - var_draft
  action: |
    Demande  l'utilisateur les modifications souhaites.
    Applique les changements au brouillon.
    Prsente la version rvise.
  sets:
    var_draft: "{{modified_text}}"
  transitions:
    - if: edits_applied
      to: state_draft
```

### tape 9 : Terminer le processus

Clturer la session avec une transition inconditionnelle (`if: always`).

```yaml
# Fin de l'assistance
state_end:
  requires:
    - var_draft
  action: |
    Confirme que l'e-mail est prt.
    Propose de copier, sauvegarder ou envoyer le brouillon.
    Remercie l'utilisateur et termine la session.
  transitions:
    - if: always
      to: state_exit
```

### tape 10 : tat terminal

Dernier tat technique pour marquer la fin.

```yaml
# tat terminal technique
state_exit:
  action: |
    Clture le scnario.
    Aucun traitement supplmentaire requis.
```

YAML FSM description file "email_fsm.yml"

YAML
YAML FSM description file "email_fsm.yml"
# FSM: Assistant GPT pour rdiger un e-mail tape par tape

# tat initial: Demande le type d'e-mail souhait
state_ask_type:
  action: |
    Demande  l'utilisateur quel type d'e-mail il souhaite rdiger.
    Propose des exemples: professionnel, personnel, candidature, rclamation.
  sets:
    var_email_type: "{{user_input}}"
  transitions:
    - if: var_email_type_provided
      to: state_ask_recipient

# Collecte du destinataire
state_ask_recipient:
  action: |
    Demande  qui est destin l'e-mail.
    Enregistre la rponse dans la variable destinataire.
  sets:
    var_recipient: "{{user_input}}"
  transitions:
    - if: var_recipient_provided
      to: state_ask_subject

# Collecte du message
state_ask_subject:
  action: |
    Demande  l'utilisateur les lments du message  rdiger.
    Cette information servira  structurer le brouillon.
  sets:
    var_subject: "{{user_input}}"
  transitions:
    - if: var_subject_provided
      to: state_ask_tone

# Collecte du ton souhait
state_ask_tone:
  action: |
    Demande quel ton doit tre adopt: formel, amical, neutre, etc.
    Enregistre la prfrence utilisateur.
  sets:
    var_tone: "{{user_input}}"
  transitions:
    - if: var_tone_provided
      to: state_confirm_inputs

# Rcapitulatif avant gnration
state_confirm_inputs:
  requires:
    - var_email_type
    - var_recipient
    - var_subject
    - var_tone
  action: |
    Rsume les informations collectes:
    - Type: {{var_email_type}}
    - Destinataire: {{var_recipient}}
    - Objet: {{var_subject}}
    - Ton: {{var_tone}}
    Demande confirmation avant gnration du brouillon.
  transitions:
    - if: user_confirms
      to: state_draft
    - if: user_wants_change
      to: state_ask_type

# Gnration du brouillon
state_draft:
  requires:
    - var_email_type
    - var_recipient
    - var_subject
    - var_tone
  action: |
    Rdige un premier brouillon d'e-mail bas sur les informations collectes.
    Affiche le texte  l'utilisateur dans le chat (pas dans un snippet).
    Demande : " Rponds par oui si tu es satisfait".
  sets:
    var_draft: "{{generated_text}}"
  transitions:
    - if: user_satisfied
      to: state_end
    - if: NOT user_satisfied
      to: state_edit

# tat d'dition: modifications du brouillon
state_edit:
  requires:
    - var_draft
  action: |
    Demande  l'utilisateur les modifications souhaites.
    Applique les changements au brouillon.
    Prsente la version rvise.
    Demande : " Rponds par oui si tu es satisfait".
  sets:
    var_draft: "{{modified_text}}"
  transitions:
    - if: user_satisfied
      to: state_end
    - if: NOT user_satisfied
      to: state_draft
      
# Fin de l'assistance
state_end:
  requires:
    - var_draft
  action: |
    Imprime l'e-mail dans un Snippet de code MarkDown, pas dans le texte du chat.
    Propose de grnrer l' e- mail dans un fichier .docx  tlcharger (utilise Python pour le faire).
  transitions:
    - if: always
      to: state_exit

# tat terminal technique
state_exit:
  action: |
    Remercie l'utilisateur et termine la session.
    Clture le scnario.
    Aucun traitement supplmentaire requis.

Guide: Implementing a FSM for LLM with "LLM Friendly YAML" (FR)

Markdown
# Guide: Implementing a FSM for LLM with "LLM Friendly YAML"

## 1. Introduction

This guide presents a simple, clear, and robust method for describing a finite state machine (FSM) designed to control a language model (LLM), using a structured and readable format: the enriched YAML, known as "LLM Friendly YAML".

This approach allows you to organize a GPT assistant using explicit states, conditional transitions, and actions described as instructions.
The goal is to maximize the logical and semantic understanding of the model.

The guide focuses on designing the `.yml` file, without code processing.

The example used is that of an assistant helping the user write an email step by step.

## 2. Fundamental Concepts of FSM for LLM

### 2.1 Definition

A FSM (Finite State Machine) is a system composed of several states, connected by conditional transitions. Each state can produce an action.

In the context of an LLM, this allows:
- structuring a dialogue or task,
- guiding the model in a predefined sequence,
- avoiding unpredictable responses.

### 2.2 Basic Elements

- State: a named step (`state_intro`)
- Action: instructions executed in this state
- Transition: moving to another state, triggered by a condition

### 2.3 Perception by an LLM

An LLM doesn't formally "parse": it reads YAML as structured text. It understands better if:
- indentation is clear and stable,
- each block is commented,
- names and intentions are explicit.

### 2.4 Simplified Structure

Each state is a top-level key in the YAML:

```yaml
state_intro:
  action: |
    Explain the purpose of the assistant.
    Ask if the user is ready.
  transitions:
    - if: user_ready
      to: state_gather
```

## 3. The "LLM Friendly YAML" Format

### 3.1 Definition

A format for describing automata designed for human readability and natural interpretation by an LLM.

It is based on:
- a repetitive structure,
- identifiable blocks (state, action, transitions),
- explicit verbal comments.

### 3.2 Rules

- Indentation with spaces only (never tabs)
- Actions in `|` style (literal) to preserve line breaks
- Transitions as a list of `- if` / `to`
- Explicit and homogeneous naming

### 3.3 Example

```yaml
# Introduction
state_intro:
  action: |
    Introduce the assistant to the user.
    Explain the available options.
  transitions:
    - if: user_ready  # If the user is ready
      to: state_collect
```

## 4. Recommended Syntax

### 4.1 Standard State Structure

```yaml
state_name:
  action: |
    Step 1
    Step 2
  transitions:
    - if: condition
      to: next_state
```

### 4.2 Unconditional Transitions

A systematic transition (without condition) is written:

```yaml
transitions:
  - if: always
    to: next_state
```

### 4.3 Form Recommendations

- Always keep the order `action`, then `transitions`.
- Avoid irregular structures.
- Use the `|` style to segment actions into understandable steps.

## 5. Variable Management

### 5.1 Why?

An LLM assistant may need to store or read information. Examples:
- `var_recipient`: recipient's name
- `var_tone`: desired tone
- `var_draft`: generated text

### 5.2 Recommended Syntax

Define a variable (`sets:`):
```yaml
sets:
  var_recipient: "{{user_input}}"
  var_tone: "formal"
```

Read a variable (`requires:`):
```yaml
requires:
  - var_recipient
  - var_tone
```

### 5.3 Integrated Example

```yaml
# Ask who the email is for
state_ask_recipient:
  action: |
    Ask the user to indicate the recipient.
    Record their response.
  sets:
    var_recipient: "{{user_input}}"
  transitions:
    - if: var_recipient_provided
      to: state_ask_subject
```

## 6. Best Practices

### 6.1 Indentation

- Use spaces (2 or 4), never tabs.

### 6.2 `|` Style for `action`

- Preserves lines.
- Allows for natural sequential reading.

### 6.3 Comment Each Block

- A contextual comment before each state and each condition improves the LLM's understanding.

### 6.4 Clear Naming

| Element | Convention |
|---------|------------|
| State | `state_<name>` |
| Variable | `var_<name>` |
| Condition | `var_<name>_provided`, `user_ready`, `always` |

## 7. Pitfalls to Avoid

-  Tabs in indentation
-  Vague names (`step1`, `temp`)
-  Transitions without `if`
-  File without a final state (`state_end`)
-  Absence of comments

## Tutorial: Building an LLM Friendly YAML FSM Step by Step  Email Builder Assistant

### Step 1: Create the Introduction State

We always start with an entry state.
It sets the context of the assistant and initiates the dialogue.

```yaml
# Initial state: assistant introduction
state_intro:
  action: |
    Briefly present the purpose of the assistant.
    Indicate that it can help write an email step by step.
    Propose to start now.
  transitions:
    - if: user_ready
      to: state_ask_type
```

Key points:
- The state is named `state_intro` according to the convention.
- The actions are in `|` style, for fluid reading by the LLM.
- The transition `if: user_ready` awaits a response like "yes" or equivalent.

### Step 2: Add Email Type Collection

The user chooses the type of email (professional, personal, etc.).
We record this value with `sets`.

```yaml
# Ask for the desired email type
state_ask_type:
  action: |
    Ask the user what type of email they want to write.
    Suggest examples: professional, personal, job application, complaint.
  sets:
    var_email_type: "{{user_input}}"
  transitions:
    - if: var_email_type_provided
      to: state_ask_recipient
```

What we've added:
- The `sets` field records the `var_email_type` variable.
- The transition verifies that this variable has been filled.

### Step 3: Collect the Recipient

Same logic, with a new variable: `var_recipient`.

```yaml
# Recipient collection
state_ask_recipient:
  action: |
    Ask who the email is for.
    Record the response in the recipient variable.
  sets:
    var_recipient: "{{user_input}}"
  transitions:
    - if: var_recipient_provided
      to: state_ask_subject
```

### Step 4: Collect the Email Subject

We record this data in `var_subject`.

```yaml
# Subject line collection
state_ask_subject:
  action: |
    Ask the user what the subject of the email is.
    This information will help structure the draft.
  sets:
    var_subject: "{{user_input}}"
  transitions:
    - if: var_subject_provided
      to: state_ask_tone
```

### Step 5: Ask for the Tone

New variable `var_tone`.

```yaml
# Desired tone collection
state_ask_tone:
  action: |
    Ask what tone should be adopted: formal, friendly, neutral, etc.
    Record the user preference.
  sets:
    var_tone: "{{user_input}}"
  transitions:
    - if: var_tone_provided
      to: state_confirm_inputs
```

### Step 6: Confirm User Inputs

Before generating the draft, we provide a summary.

```yaml
# Summary before generation
state_confirm_inputs:
  requires:
    - var_email_type
    - var_recipient
    - var_subject
    - var_tone
  action: |
    Summarize the information collected:
    - Type: {{var_email_type}}
    - Recipient: {{var_recipient}}
    - Subject: {{var_subject}}
    - Tone: {{var_tone}}
    Ask for confirmation before generating the draft.
  transitions:
    - if: user_confirms
      to: state_draft
    - if: user_wants_change
      to: state_ask_type
```

Note:
- We use `requires` here to ensure the necessary variables are present.

### Step 7: Generate the Draft

We move to the production phase with the LLM.

```yaml
# Draft generation
state_draft:
  requires:
    - var_email_type
    - var_recipient
    - var_subject
    - var_tone
  action: |
    Write a first email draft based on the collected information.
    Display the text to the user.
    Ask if any modifications are necessary.
  sets:
    var_draft: "{{generated_text}}"
  transitions:
    - if: user_satisfied
      to: state_end
    - if: user_wants_edit
      to: state_edit
```

### Step 8: Handle Revision

Allow the user to modify the generated text.

```yaml
# Edit state: draft modifications
state_edit:
  requires:
    - var_draft
  action: |
    Ask the user for the desired modifications.
    Apply the changes to the draft.
    Present the revised version.
  sets:
    var_draft: "{{modified_text}}"
  transitions:
    - if: edits_applied
      to: state_draft
```

### Step 9: End the Process

Close the session with an unconditional transition (`if: always`).

```yaml
# End of assistance
state_end:
  requires:
    - var_draft
  action: |
    Confirm that the email is ready.
    Suggest copying, saving, or sending the draft.
    Thank the user and end the session.
  transitions:
    - if: always
      to: state_exit
```

### Step 10: Terminal State

Final technical state to mark the end.

```yaml
# Technical terminal state
state_exit:
  action: |
    Close the scenario.
    No additional processing required.
```

Credits

Jean Noël
14 projects • 36 followers
pedagogical director at L'école LDLC https://www.linkedin.com/in/jnlootsidebox/
Contact

Comments

Please log in or sign up to comment.