SK-1-Fundamentals : Introduction a Semantic Kernel

Navigation : Index | 02-Functions >>


Objectifs d’apprentissage

A la fin de ce notebook, vous saurez : 1. Installer et configurer Semantic Kernel pour Python 2. Comprendre le rôle du Kernel comme orchestrateur central 3. Ajouter des services LLM (OpenAI, Azure, Hugging Face) 4. Charger et utiliser des plugins (prompts templates) 5. Créer des fonctions sémantiques inline 6. Gerer un chat conversationnel avec historique

Prerequis

  • Python 3.10+
  • Cle API OpenAI (ou Azure OpenAI)
  • Fichier .env configure

Duree estimee : 45 minutes


Sommaire

Section Contenu Concepts cles
1 Installation pip install semantic-kernel
2 Configuration .env, Kernel, Services
3 Premier Kernel Initialisation, service LLM
4 Plugins Chargement depuis fichiers
5 Fonctions inline Prompts dynamiques
6 Chat Historique, KernelArguments

Qu’est-ce que Semantic Kernel ? Un SDK Microsoft open-source pour integrer des LLMs dans vos applications. Il orchestre les appels aux modèles, gere les plugins, et permet de créer des agents intelligents.

# Installation de Semantic Kernel (si nécessaire)

# Vérification de la version installée
from semantic_kernel import __version__
print(f"Semantic Kernel version : {__version__}")
Semantic Kernel version : 1.42.0

📌 Importation des bibliothèques nécessaires

Dans cette cellule, nous allons importer les modules principaux.

import os
import json
from dotenv import load_dotenv
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.connectors.ai.open_ai import OpenAIChatPromptExecutionSettings
from semantic_kernel.functions import KernelArguments  # Correction de l'import
from semantic_kernel.prompt_template import PromptTemplateConfig
from semantic_kernel.prompt_template.input_variable import InputVariable
print("Imports OK : Kernel, OpenAIChatCompletion, KernelArguments, PromptTemplateConfig, InputVariable")
Imports OK : Kernel, OpenAIChatCompletion, KernelArguments, PromptTemplateConfig, InputVariable

📁 2. Chargement des paramètres de configuration

📌 Lecture des paramètres depuis un fichier .env ou JSON

Le fichier de configuration .env doit contenir les clés nécessaires pour accéder aux services OpenAI/Azure OpenAI.

💡 Vérifiez que vous avez bien créé un fichier .env dans le même dossier que ce notebook avec ces valeurs :

GLOBAL_LLM_SERVICE="OpenAI"
OPENAI_API_KEY="sk-..."
OPENAI_CHAT_MODEL_ID="gpt-5-mini"

👉 Nous allons maintenant charger ces paramètres en Python :

# Chargement du fichier .env
load_dotenv()

# Récupération des clés API et du modèle
llm_service = os.getenv("GLOBAL_LLM_SERVICE", "OpenAI")
api_key = os.getenv("OPENAI_API_KEY")
model_id = os.getenv("OPENAI_CHAT_MODEL_ID", "gpt-5-mini")

# Vérification
print(f"Service sélectionné : {llm_service}")
print(f"Modèle utilisé : {model_id}")
Service sélectionné : OpenAI
Modèle utilisé : gpt-5.2
# Importer le Kernel depuis Semantic Kernel
from semantic_kernel import Kernel

# Créer une instance du Kernel
kernel = Kernel()
print("Kernel initialisé avec succès.")
Kernel initialisé avec succès.

Interpretation : Chargement de la configuration et initialisation du Kernel

Les deux cellules précédentes ont respectivement charge les variables d’environnement et instancie le Kernel Semantic Kernel. Ces deux étapes sont preconditionnees l’une par l’autre :

Étape Action Résultat
Lecture .env load_dotenv() + os.getenv() Recuperation de la cle API et du modèle
Initialisation Kernel Kernel() Orchestrateur central pret a recevoir des services

Le Kernel est actuellement “nu” : il ne possede pas encore de service LLM. La cellule suivante va verifier la connectivite avec l’API OpenAI avant d’ajouter le service ChatCompletion au Kernel.

import os
from dotenv import load_dotenv
from openai import AsyncOpenAI
import asyncio

load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
# Récupère la valeur du .env et nettoie les espaces
base_url_from_env = os.getenv("OPENAI_BASE_URL", "").strip()

# Détermine explicitement l'URL finale
if base_url_from_env:
    final_base_url = base_url_from_env
    print(f"Utilisation de l'URL du .env : {final_base_url}")
else:
    final_base_url = "https://api.openai.com/v1" # <<< URL par défaut explicite
    print(f"Utilisation de l'URL par défaut : {final_base_url}")

model_id = os.getenv("OPENAI_CHAT_MODEL_ID", "gpt-5-mini")
print(f"Utilisation du modèle : {model_id}")
print(f"Clé API utilisée : {'Oui' if api_key else 'Non'}")

async def test_connection():
    try:
        if not api_key:
            print("ERREUR : Clé API non définie.")
            return

        # Utilise final_base_url déterminé ci-dessus
        client = AsyncOpenAI(api_key=api_key, base_url=final_base_url)
        response = await client.chat.completions.create(
            model=model_id,
            messages=[{"role": "user", "content": "Say hello!"}],
            timeout=20
        )
        print("Connexion réussie !")
        print("Réponse :", response.choices[0].message.content)
    except Exception as e:
        print(f"ERREUR lors du test de connexion : {type(e).__name__} - {e}")
        import traceback
        traceback.print_exc()

# Exécuter le test
await test_connection()
Utilisation de l'URL par défaut : https://api.openai.com/v1
Utilisation du modèle : gpt-5.2
Clé API utilisée : Oui
Connexion réussie !
Réponse : Hello!

Configuration du service LLM

Nous devons connecter notre Kernel à un service de Chat Completion.
Pour cet exemple, nous allons utiliser OpenAI. Si vous préférez Azure OpenAI, adaptez le code en conséquence (voir la documentation).

La configuration se fait via l’ajout d’un service au Kernel.
Assurez-vous que votre fichier .env contient votre clé API.

# Importation du service OpenAI pour le Chat Completion
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

# Ajout du service "default" au Kernel
kernel.add_service(OpenAIChatCompletion(service_id="default"))
print("Service OpenAI ajouté au Kernel.")
Service OpenAI ajouté au Kernel.

Interprétation : Services LLM dans Semantic Kernel

Configuration effectuée : Ajout du service OpenAI au Kernel avec l’identifiant “default”.

Aspect Détail Signification
Service ID “default” Identifiant unique pour référencer ce service lors des invocations
Type OpenAIChatCompletion Service de complétion chat (vs embedding, image, etc.)
Configuration automatique Via .env api_key et model_id chargés depuis les variables d’environnement
Flexibilité Multi-services possibles Un Kernel peut avoir plusieurs services (OpenAI, Azure, Ollama, etc.)

Points clés :

  1. Un Kernel peut avoir plusieurs services - Permet de basculer entre différents LLMs selon le contexte (ex: GPT-4 pour les tâches complexes, GPT-3.5 pour les tâches simples)
  2. Le service_id est crucial - Si non spécifié lors d’une invocation, “default” est utilisé
  3. Configuration centralisée - Le Kernel gère automatiquement les appels API, retry logic, et logging
  4. Abstraction du fournisseur - Le même code fonctionne avec OpenAI, Azure, Hugging Face, etc. en changeant juste le service

Note technique : La méthode add_service() enregistre le service dans le registre interne du Kernel. Lors d’une invocation, le Kernel sélectionne automatiquement le service approprié basé sur le service_id ou utilise le service par défaut.

Utiliser un Plugin de Prompt

Semantic Kernel permet de charger des prompt plugins stockés sur disque.
Dans cet exemple, nous chargerons le plugin “FunPlugin” qui contient, par exemple, une fonction pour générer une blague.

Les fichiers du plugin (le prompt et sa configuration) sont stockés dans le répertoire prompt_template_samples/.
Nous allons charger ce plugin et invoquer la fonction “Joke” pour générer une blague sur un sujet donné.

# Chemin correct vers les plugins
plugins_directory = "./prompt_template_samples/"

# Vérifier si le dossier du plugin existe avant de charger
if os.path.exists(os.path.join(plugins_directory, "FunPlugin")):
    fun_plugin = kernel.add_plugin(parent_directory=plugins_directory, plugin_name="FunPlugin")
    joke_function = fun_plugin["Joke"]
    print("Plugin chargé avec succès.")
else:
    print("⚠️ Le plugin FunPlugin est introuvable. Vérifiez le chemin et assurez-vous qu'il est bien présent.")


# Invoquer la fonction pour générer une blague sur un thème donné
# Pour ce faire, nous utilisons des KernelArguments (ici, seul l'input est nécessaire)
from semantic_kernel.functions import KernelArguments

print(joke_function)

# Exemple : générer une blague sur "time travel to dinosaur age" avec un style "super silly"
joke_response = await kernel.invoke(joke_function, KernelArguments(input="time travel to dinosaur age", style="super silly"))
print("Blague générée :", joke_response)
Plugin chargé avec succès.
metadata=KernelFunctionMetadata(name='Joke', plugin_name='FunPlugin', description='Generate a funny joke', parameters=[KernelParameterMetadata(name='input', description='Joke subject', default_value='', type_='', is_required=True, type_object=None, schema_data={'type': 'object', 'description': 'Joke subject'}, include_in_function_choices=True), KernelParameterMetadata(name='style', description='Give a hint about the desired joke style', default_value='', type_='', is_required=True, type_object=None, schema_data={'type': 'object', 'description': 'Give a hint about the desired joke style'}, include_in_function_choices=True)], is_prompt=True, is_asynchronous=True, return_parameter=KernelParameterMetadata(name='return', description='The completion result', default_value=None, type_='FunctionResult', is_required=True, type_object=None, schema_data=None, include_in_function_choices=True), additional_properties=None) invocation_duration_histogram=<opentelemetry.metrics._internal.instrument._ProxyHistogram object at 0x0000023B5E2E4190> streaming_duration_histogram=<opentelemetry.metrics._internal.instrument._ProxyHistogram object at 0x0000023B5CCE2780> prompt_template=KernelPromptTemplate(prompt_template_config=PromptTemplateConfig(name='Joke', description='Generate a funny joke', template="WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE TOPIC BELOW\n\nJOKE MUST BE:\n- G RATED\n- WORKPLACE/FAMILY SAFE\nNO SEXISM, RACISM OR OTHER BIAS/BIGOTRY\nAVOID Daddy's jokes and puns, be funny, be deep, provocative, for real !\n\nBE CREATIVE AND FUNNY. I WANT TO LAUGH.\nIncorporate the style suggestion, if provided: {{$style}}\n+++++\n\n{{$input}}\n+++++\n", template_format='semantic-kernel', input_variables=[InputVariable(name='input', description='Joke subject', default='', is_required=True, json_schema='', allow_dangerously_set_content=False), InputVariable(name='style', description='Give a hint about the desired joke style', default='', is_required=True, json_schema='', allow_dangerously_set_content=False)], allow_dangerously_set_content=False, execution_settings={'default': PromptExecutionSettings(service_id=None, extension_data={}, function_choice_behavior=None)}), allow_dangerously_set_content=False) prompt_execution_settings={'default': PromptExecutionSettings(service_id=None, extension_data={}, function_choice_behavior=None)}
Blague générée : I built my time machine at the kitchen table because that’s where all serious science happens in my family. My sister kept walking by, seeing wires everywhere, and asking the same question: “Is it safe?”

I told her, “It’s safer than my search history.”

The plan was simple: jump back to the dinosaur age, take one photo, come back, finally silence the group chat that keeps saying, “If time travel is real, why hasn’t anyone visited us?” I even packed responsibly: granola bars, a first-aid kit, and a tiny notebook labeled IMPORTANT DISCOVERIES, like I was about to change history instead of immediately forgetting why I walked into a room.

I hit the button.

Reality folded like a cheap lawn chair.

And I landed in the dinosaur age with the confidence of someone who has never had to make eye contact with a creature that could treat them like a snack-sized opinion.

It was beautiful. Huge ferns. A sky so clear it looked like someone had just wiped the lens. The air had this warm, swampy smell that made me understand, spiritually, why people invented deodorant.

Then the ground started doing that thing where it decides to become a drum.

Thump. Thump. Thump.

I turned slowly, like a person who has watched documentaries and still learned nothing.

Behind me: a T. rex.

Not “movie monster” scary. Worse. Real-life scary. Like a forklift with teeth, standing there with the calm authority of something that has never been asked to “circle back” or “touch base.”

It looked at me.

I looked at it.

My brain offered exactly one plan: “Act natural.”

So I stood up straighter, nodded politely, and did what any reasonable adult does when confronted by an apex predator: I pulled out my notebook and pretended I was here for research.

The T. rex took a step closer.

I raised a hand like I was directing traffic. “Hi. Hello. Great to meet you. I’m just here to—”

It lowered its head and sniffed me.

And in that moment I realized something profound: I had packed granola bars.

I slowly reached into my bag, took one out, unwrapped it with the careful reverence of someone defusing a bomb, and held it out like an offering to the gods of poor planning.

The T. rex stared at the granola bar.

Then it opened its mouth.

And instead of roaring or trying to eat me, it did something I was not prepared for.

It sneezed.

Not a cute little sneeze. A prehistoric sneeze. A sneeze with weather in it. A sneeze that arrived with its own wind advisory.

I was instantly coated in dinosaur mucus—warm, swampy, existentially discouraging dinosaur mucus. My notebook slapped shut in my hand like it was quitting.

The T. rex blinked, looked mildly offended—like *I* had startled *it*—and then wandered off with the energy of someone who just coughed in a meeting and decided to retire.

I stood there, dripping, trying to process the fact that I had time-traveled across millions of years and my first contact with the distant past was… being sneezed on by history.

Then I heard a new sound.

A chorus of smaller thumps.

I turned and saw a line of little dinosaurs—raptors, I think—peeking out from behind the ferns. They weren’t charging. They were… watching. Their heads tilted in sync like a committee.

One stepped forward, stared at the granola bar still in my hand, and made a noise that sounded exactly like a tiny judgmental cough.

And suddenly it hit me.

I wasn’t in danger.

I was being evaluated.

By dinosaurs.

Like I was at some ancient reptile networking event and I had shown up wearing the wrong badge.

I offered the granola bar again.

The raptor sniffed it, took the tiniest bite, and immediately spit it out with theatrical disgust. Another one tried. Same reaction. The whole group began making these annoyed little chirps, like: *What is this? Why is it dry? Why does it taste like disappointment and responsibility?*

Then one of them hopped closer, looked me dead in the eye, and—no joke—pushed the granola bar back toward me with its snout, like a waiter returning a meal.

And I understood, completely and spiritually, that I had been rejected by dinosaurs for my snack choices.

I did the only dignified thing left.

I bowed.

“I respect your feedback,” I said, because apparently my survival strategy is “customer service voice.” “We will take this under advisement.”

The raptors, satisfied that they had delivered their critique, lost interest and dispersed into the ferns like a tiny, scaly review panel moving on to destroy another product.

I stood there, covered in T. rex sneeze, holding my rejected granola bar, and for the first time I thought, with total clarity: *If I stay here, I will become an extremely humiliating footnote.*

So I sprinted back to my time machine.

Which, because I built it on the kitchen table, required me to kick it twice for it to start.

I hit the return button.

Reality folded again.

I landed back in my kitchen, dripping, hair stuck to my forehead, smelling like I had wrestled a swamp.

My sister walked in, took one look at me, and said, very calmly, “So. Was it safe?”

I looked her in the eye and said, “I have traveled through time and been reviewed negatively by dinosaurs.”

She paused. “Did you at least get a photo?”

I pulled out my phone.

It was open to the camera.

It had one picture.

A perfect, crystal-clear, high-resolution shot of my thumb covering the lens.

And, faintly in the background, the T. rex mid-sneeze, looking like it was about to end my whole era.

My sister stared at it for a long time and finally said, “So… you went back sixty-five million years… to get slimed and take a thumb picture.”

I nodded.

She nodded back, like she was updating her internal file on me.

Then she said, “Next time, bring something better than granola.”

And that’s how I learned the most humbling truth of time travel:

You can rewrite history, but you cannot impress a dinosaur with a snack that tastes like office morale.

Exercice 1 : Analyse de sentiment via fonction sémantique

Objectif : Créer une fonction sémantique qui analyse le sentiment d’un texte et retourne un résultat structure avec le sentiment dominant, un score de confiance et une justification courte.

L’analyse de sentiment est un cas d’usage classique des LLMs. Vous allez créer une fonction qui prend un texte en entree et retourne une analyse structuree (positif/negatif/neutre + score).

Indices : - # Étape 1 : Ecrire un template qui demande au LLM d’analyser le sentiment et de formater la reponse - # Étape 2 : Specifier le format de sortie attendu dans le prompt (ex: “SENTIMENT: positif | CONFIANCE: 0.8 | RAISON: …”) - # Étape 3 : Tester avec 3 textes de sentiments différents - # Indice : Demander explicitement un format structure dans le prompt force le LLM a etre consistent

# Exercice 3 : Analyseur de sentiment

sentiment_prompt = """
# TODO etudiant : ecrire le template d'analyse de sentiment
# Le template doit utiliser {{$input}} et demander un format structure
Analyse le sentiment.
"""

# TODO etudiant : configurer le PromptTemplateConfig
sentiment_config = PromptTemplateConfig(
    template=sentiment_prompt,
    name="sentiment",
    template_format="semantic-kernel",
    input_variables=[
        InputVariable(name="input", description="Texte a analyser", is_required=True),
    ],
)

# TODO etudiant : ajouter au kernel et tester
# sentiment_function = kernel.add_function(
#     function_name="sentiment",
#     plugin_name="sentimentPlugin",
#     prompt_template_config=sentiment_config
# )

# Textes de test avec sentiments differents
test_texts = [
    "J'adore ce nouveau framework ! Il est incroyablement bien concu et facile a utiliser.",
    "Le service etait tres decevant. Je ne recommande pas du tout ce produit.",
    "La reunion d'hier a dure 2 heures. Nous avons discute du budget pour le prochain trimestre.",
]

# TODO etudiant : decommentez apres implementation
# for text in test_texts:
#     result = await kernel.invoke(sentiment_function, KernelArguments(input=text))
#     print(f"Texte : {text[:60]}...")
#     print(f"Analyse : {result}\n")

print("Exercice a completer : implementez le template et testez les 3 textes")
Exercice a completer : implementez le template et testez les 3 textes

Interprétation : Plugins et Prompt Templates

Sortie obtenue : Blague générée sur le voyage dans le temps à l’ère des dinosaures avec style “super silly”.

Aspect Valeur Signification
Plugin FunPlugin Collection de fonctions liées (Joke, Story, etc.)
Fonction Joke Prompt template avec variables {input} et {style}
Variables d’entrée input=“time travel…”, style=“super silly” Paramètres injectés dans le template
Configuration prompt Aucune dans config.json (execution_settings vide) Ni max_tokens ni temperature ne sont fixés par le plugin — la génération utilise les réglages par défaut du service (visible dans la sortie : extension_data={})
Format de sortie Texte libre (blague) Le LLM génère du contenu non structuré

Architecture d’un Prompt Template :

┌─────────────────────────────────────────────┐
│           FunPlugin/Joke/                   │
│  ┌────────────────────────────────────────┐ │
│  │ skprompt.txt (template)                │ │
│  │  "Write a joke about `{$input}`        │ │
│  │   with style `{$style}`"               │ │
│  └────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────┐ │
│  │ config.json (exécution settings)       │ │
│  │  { execution_settings: {} }            │ │
│  └────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
         ↓
   kernel.invoke(joke_function, args)
         ↓
   Prompt rempli → Service LLM → Résultat

Points clés :

  1. Les plugins sont des dossiers structurés - Chaque fonction a son propre sous-dossier avec skprompt.txt et config.json
  2. Les variables sont injectées avec {$nom} - Syntaxe Semantic Kernel pour les placeholders
  3. La température contrôle la créativité - 0.9 est élevé (très créatif), 0.0 serait déterministe
  4. Les metadata sont extraits automatiquement - KernelFunctionMetadata décrit la fonction (nom, paramètres, description)

Avantage des plugins : Séparation du code Python et des prompts. Les prompts peuvent être modifiés par des non-développeurs (prompt engineers) sans toucher au code.

Schema : du plugin a l’invocation

Le plugin (template skprompt.txt + reglages config.json) est invoque par le kernel, qui remplit le prompt, appelle le service LLM et renvoie le résultat.

flowchart TD
    subgraph P["FunPlugin/Joke/"]
      T["skprompt.txt (template)"]
      C["config.json (exécution settings)"]
    end
    P --> I["kernel.invoke(joke_function, args)"]
    I --> F["Prompt rempli"]
    F --> L["Service LLM"]
    L --> Res["Résultat"]

Définir une fonction sémantique en ligne

Outre l’utilisation de plugins stockés sur disque, il est possible de définir des fonctions sémantiques directement dans votre code Python.
Cette approche est particulièrement utile pour : - Générer dynamiquement des prompts en fonction du contexte - Prototyper rapidement des idées sans créer de fichiers séparés

Dans cet exemple, nous allons créer une fonction qui résume un texte donné en quelques mots (TL;DR).

from semantic_kernel.connectors.ai.open_ai import OpenAIChatPromptExecutionSettings
from semantic_kernel.prompt_template import PromptTemplateConfig
from semantic_kernel.prompt_template.input_variable import InputVariable

# Définition du prompt
tldr_prompt = """
{{$input}}

Donne-moi un résumé en 5 mots ou moins.
"""

# Configuration de l'exécution (temperature non supportée par les modèles de raisonnement)
execution_settings = OpenAIChatPromptExecutionSettings(
    service_id="default",
    ai_model_id=model_id,
)

# Configuration du prompt template
tldr_template_config = PromptTemplateConfig(
    template=tldr_prompt,
    name="tldr",
    template_format="semantic-kernel",
    input_variables=[InputVariable(name="input", description="Texte à résumer", is_required=True)],
    execution_settings=execution_settings,
)

# Ajout de la fonction au Kernel
tldr_function = kernel.add_function(function_name="tldrFunction", plugin_name="tldrPlugin", prompt_template_config=tldr_template_config)

# Exécution de la fonction
async def run_tldr():
    input_text = "Demo était une poétesse grecque ancienne connue pour un unique poème gravé sur le Colosse de Memnon."
    tldr_summary = await kernel.invoke(tldr_function, KernelArguments(input=input_text))
    print("Résumé (TL;DR) :", tldr_summary)

# Lancer la fonction
await run_tldr()
Résumé (TL;DR) : Poétesse grecque, poème sur Memnon.

Exercice 2 : Générateur de titres avec contrôle de temperature

Objectif : Créer une fonction sémantique qui genere des titres creatifs pour un article, et experimenter avec le paramètre temperature pour observer son impact sur la creativite.

La temperature est le paramètre le plus important pour contrôler le comportement d’un LLM : 0 = déterministe, 1 = creatif, 2 = très aleatoire. Vous allez créer un générateur de titres et tester plusieurs valeurs de temperature.

Indices : - # Étape 1 : Créer un template qui genere 3 propositions de titre pour un article donne - # Étape 2 : Configurer les execution_settings avec temperature variable - # Étape 3 : Tester avec temperature 0.0, 0.5 et 1.5 et comparer les résultats - # Indice : OpenAIChatPromptExecutionSettings(service_id="default", temperature=0.0) pour le contrôle

# Exercice 2 : Generateur de titres avec temperature variable

title_prompt = """
# TODO etudiant : ecrire un template qui genere 3 propositions de titre
# pour l'article decrit dans {{$input}}
Genere des titres.
"""

# TODO etudiant : creer une fonction pour chaque niveau de temperature
async def generate_titles_with_temperature(article_topic: str, temperature: float) -> str:
    """
    Genere des titres avec une temperature donnee.
    
    # Etape 1 : Creer les execution_settings avec la temperature specifiee
    # Etape 2 : Configurer le PromptTemplateConfig avec ces settings
    # Etape 3 : Invoquer la fonction et retourner le resultat
    
    # Indice : Creez une nouvelle fonction pour chaque temperature
    # car les execution_settings sont lies a la fonction
    """
    # TODO etudiant : implementer la generation avec temperature
    return "Exercice a completer"  # TODO etudiant : retourner les titres generes

# Test avec differentes temperatures
topic = "L'impact de l'intelligence artificielle sur l'education superieure"

# TODO etudiant : decommentez apres implementation
# for temp in [0.0, 0.5, 1.5]:
#     print(f"\n--- Temperature : {temp} ---")
#     titles = await generate_titles_with_temperature(topic, temp)
#     print(titles)

print("Exercice a completer : testez differentes valeurs de temperature")
Exercice a completer : testez differentes valeurs de temperature

Interprétation : Fonctions Sémantiques Inline

Sortie obtenue : Résumé (TL;DR) : Poétesse grecque, poème sur Memnon.

Aspect Valeur Signification
Méthode de définition Inline (dans le code) Pas de fichiers externes, tout défini en Python
Template String Python avec {$input} Prompt défini directement dans une variable
Configuration PromptTemplateConfig Metadata + exécution settings embarqués
Avantage Dynamique et flexible Permet de générer des prompts programmatiquement
Température 0.7 Équilibre entre créativité et cohérence

Comparaison Plugin vs Inline :

Critère Plugin (fichiers) Fonction Inline
Modification Par prompt engineers Par développeurs
Versioning Git + fichiers séparés Code source uniquement
Réutilisation Facile (import plugin) Requiert copier-coller
Génération dynamique Difficile Très facile
Maintenance Prompts centralisés Dispersés dans le code

Cas d’usage idéaux pour inline :

  1. Prototypage rapide - Tester une idée sans créer de structure de fichiers
  2. Prompts générés dynamiquement - Construire des templates basés sur le contexte runtime
  3. Fonctions jetables - Utilitaires ponctuels non réutilisés
  4. Logique métier complexe - Quand le prompt dépend fortement du code environnant

Points clés :

  1. PromptTemplateConfig centralise tout - Template, variables, exécution settings dans un seul objet
  2. InputVariable définit les paramètres - Équivalent des metadata dans les plugins
  3. add_function() enregistre la fonction - Utilisable ensuite avec kernel.invoke() comme un plugin
  4. Même API d’invocation - kernel.invoke() fonctionne identiquement pour plugins et fonctions inline

Best practice : Utilisez les plugins pour les prompts stables et réutilisables, et les fonctions inline pour l’expérimentation et les cas dynamiques.

Chat interactif avec le Kernel

Semantic Kernel offre également la possibilité de créer des interactions de type chatbot.
Nous allons configurer une fonction de chat qui utilise des Kernel Arguments pour conserver l’historique de la conversation.

L’objectif est de simuler une conversation où l’utilisateur envoie un message, le bot y répond, et l’historique est mis à jour à chaque échange.

from semantic_kernel.contents import ChatHistory

# Initialiser l'historique
chat_history = ChatHistory()
chat_history.add_system_message("Vous êtes un chatbot utile et vous fournissez des recommandations de livres.")

# Définition du prompt de chat
chat_prompt = """
{{$history}}
User: {{$user_input}}
ChatBot:
"""

# Configuration de l'exécution (temperature non supportée par les modèles de raisonnement)
chat_exec_settings = OpenAIChatPromptExecutionSettings(
    service_id="default",
    ai_model_id=model_id,
)

# Configuration du chat template
chat_template_config = PromptTemplateConfig(
    template=chat_prompt,
    name="chat",
    template_format="semantic-kernel",
    input_variables=[
        InputVariable(name="user_input", description="Message de l'utilisateur", is_required=True),
        InputVariable(name="history", description="Historique de la conversation", is_required=True),
    ],
    execution_settings=chat_exec_settings,
)

# Ajout au Kernel
chat_function = kernel.add_function(function_name="chat", plugin_name="chatPlugin", prompt_template_config=chat_template_config)

# Fonction asynchrone pour le chat
async def chat(input_text: str):
    print(f"Utilisateur : {input_text}")
    response = await kernel.invoke(chat_function, KernelArguments(
        user_input=input_text, 
        history=str(chat_history),
        allow_dangerously_set_content=True
    ))
    print(f"ChatBot : {response}")
    chat_history.add_user_message(input_text)
    chat_history.add_assistant_message(str(response))

# Exécution des exemples
await chat("Salut, je cherche des suggestions de livres sur la philosophie antique.")
await chat("Peux-tu m'en recommander quelques-uns ?")
Utilisateur : Salut, je cherche des suggestions de livres sur la philosophie antique.
ChatBot : Voici quelques excellentes suggestions de livres (en français) pour découvrir et approfondir la philosophie antique, selon différents niveaux :

## Pour débuter (clair et accessible)
- **Pierre Hadot — *Qu’est-ce que la philosophie antique ?***  
  Une très bonne porte d’entrée : la philosophie comme “manière de vivre”, avec des repères historiques.
- **Jean-François Pradeau (dir.) — *Histoire de la philosophie* (tome sur l’Antiquité, selon l’édition)**  
  Vue d’ensemble structurée, utile pour se situer (présocratiques, Socrate, Platon, Aristote, écoles hellénistiques).

## Les grands textes (indispensables)
- **Platon — *Apologie de Socrate*, *Criton*, *Phédon***  
  Idéal pour comprendre Socrate, la justice, la mort, et la naissance de la philosophie morale.
- **Aristote — *Éthique à Nicomaque***  
  Un classique sur la vertu, le bonheur (eudaimonia) et la vie bonne.
- **Épictète — *Manuel* (et *Entretiens* si vous voulez aller plus loin)**  
  Stoïcisme très concret, orienté pratique.
- **Marc Aurèle — *Pensées pour moi-même***  
  Stoïcisme introspectif, accessible, souvent très actuel.
- **Épicure — *Lettre à Ménécée* (et *Maximes capitales*)**  
  Texte bref et central pour comprendre l’éthique épicurienne.
- **Lucrèce — *De la nature (De rerum natura)***  
  Épicurisme en poésie : fascinant, plus exigeant, mais majeur.
- **Sénèque — *Lettres à Lucilius* (sélections possibles)**  
  Une entrée très riche dans le stoïcisme romain.

## Pour approfondir (commentaires et interprétations)
- **Pierre Hadot — *La Citadelle intérieure*** (sur Marc Aurèle)  
  Superbe analyse du stoïcisme comme exercice spirituel.
- **Michel Onfray — *L’Antiquité* (dans sa contre-histoire, selon volumes)**  
  Accessible et stimulant (plus “interprétation” que manuel académique).

## Si vous aimez les présocratiques
- **Jean-Paul Dumont — *Les Présocratiques*** (selon édition/anthologie)  
  Pour découvrir Héraclite, Parménide, etc., avec fragments et explications.

Si vous me dites :
1) votre niveau (débutant / déjà lu Platon-Aristote),  
2) ce qui vous attire le plus (éthique, politique, métaphysique, stoïcisme/épicurisme),  
3) si vous préférez **textes originaux** ou **essais explicatifs**,  
je peux vous proposer une liste plus ciblée (5–8 titres) avec un ordre de lecture.
Utilisateur : Peux-tu m'en recommander quelques-uns ?
ChatBot : Oui — voici une petite sélection (8 titres) très solides pour commencer et progresser, avec un bon équilibre entre **textes** et **livres d’introduction** :

1) **Pierre Hadot — *Qu’est-ce que la philosophie antique ?***  
   Le meilleur point d’entrée : comprend les écoles (stoïciens, épicuriens, sceptiques…) et l’idée de la philosophie comme art de vivre.

2) **Platon — *Apologie de Socrate* (souvent avec *Criton*)**  
   Court, captivant, central pour comprendre Socrate, la justice et la naissance de la démarche philosophique.

3) **Aristote — *Éthique à Nicomaque***  
   Un grand classique sur le bonheur, la vertu, le choix, et la “vie bonne” (un peu plus dense, mais incontournable).

4) **Épictète — *Manuel* (Enchiridion)**  
   Stoïcisme très pratique, facile à lire par fragments, et immédiatement applicable.

5) **Marc Aurèle — *Pensées pour moi-même***  
   Notes personnelles d’un empereur stoïcien : introspectif, très accessible, souvent étonnamment actuel.

6) **Épicure — *Lettre à Ménécée* + *Maximes capitales***  
   Très court : parfait pour comprendre rapidement l’épicurisme (désir, plaisir, peur de la mort, tranquillité).

7) **Sénèque — *Lettres à Lucilius* (en sélection)**  
   Le stoïcisme “romain” en lettres : concret, moral, parfois très beau.

8) **Pierre Hadot — *La Citadelle intérieure*** (sur Marc Aurèle)  
   Pour approfondir : un excellent guide de lecture et une interprétation forte des exercices spirituels stoïciens.

Si tu me dis ce que tu préfères (plutôt **stoïcisme**, **Platon/Aristote**, ou une **vue d’ensemble**) et ton niveau, je te propose un **ordre de lecture sur 4–6 semaines** avec 5–6 titres max.

Exercice 3 : Fonction sémantique de traduction multilingue

Objectif : Créer une fonction sémantique inline qui traduit un texte dans une langue cible specifiee, en utilisant PromptTemplateConfig et KernelArguments.

Vous avez vu comment créer des fonctions sémantiques inline (TLDR) et comment passer des arguments dynamiques (KernelArguments). Vous allez maintenant combiner ces deux concepts pour créer un traducteur configurable.

Indices : - # Étape 1 : Définir un template avec deux variables {$input} (texte) et {$language} (langue cible) - # Étape 2 : Configurer PromptTemplateConfig avec les InputVariable correspondants - # Étape 3 : Invoquer la fonction avec différentes langues cibles (espagnol, allemand, japonais) - # Indice : Inspirez-vous du template TLDR mais ajoutez la variable {$language}

# Exercice 1 : Traducteur multilingue

translate_prompt = """
# TODO etudiant : ecrire le template de prompt avec {{$input}} et {{$language}}
Traduis le texte suivant.
"""

# TODO etudiant : configurer PromptTemplateConfig avec les bonnes input_variables
translate_config = PromptTemplateConfig(
    template=translate_prompt,
    name="translate",
    template_format="semantic-kernel",
    input_variables=[
        # TODO etudiant : ajouter InputVariable pour "input" et "language"
    ],
)

# TODO etudiant : ajouter la fonction au kernel
# translate_function = kernel.add_function(
#     function_name="translate",
#     plugin_name="translatePlugin",
#     prompt_template_config=translate_config
# )

# Test (decommentez apres implementation)
# test_text = "Semantic Kernel est un SDK pour integrer des LLMs dans vos applications."
# for lang in ["espagnol", "allemand", "japonais"]:
#     result = await kernel.invoke(
#         translate_function,
#         KernelArguments(input=test_text, language=lang)
#     )
#     print(f"{lang} : {result}")

print("Exercice a completer : implementez le template, la config et l'invocation")
Exercice a completer : implementez le template, la config et l'invocation

Interprétation : Gestion de l’Historique de Conversation

Sortie obtenue : Conversation multi-tours avec recommandations de livres sur la philosophie antique.

Aspect Valeur Signification
Historique ChatHistory() Structure pour conserver tous les messages échangés
Message système “Vous êtes un chatbot…” Définit le rôle et le comportement du bot
Messages utilisateur add_user_message() Ajoutés après chaque tour de conversation
Messages assistant add_assistant_message() Ajoutés après chaque réponse du LLM
Injection dans prompt {$history} L’historique complet est passé à chaque invocation

Architecture du Chat avec Historique :

┌─────────────────────────────────────────────────┐
│         ChatHistory (état persistant)           │
│  ┌───────────────────────────────────────────┐  │
│  │ [System] "Vous êtes un chatbot..."       │  │
│  │ [User] "Salut, je cherche..."            │  │
│  │ [Assistant] "Salut ! La philosophie..."  │  │
│  │ [User] "Peux-tu m'en recommander..."     │  │
│  │ [Assistant] "Bien sûr ! Voici..."        │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘
         ↓ À chaque tour
   Prompt: `{$history}` + `{$user_input}`
         ↓
   Service LLM (avec contexte complet)
         ↓
   Réponse → Ajoutée à l'historique

Flux de conversation :

Tour Entrée utilisateur Historique passé au LLM Action
1 “Salut, je cherche…” [System] Réponse + ajout user/assistant à historique
2 “Peux-tu m’en recommander…” [System] + [U1] + [A1] Réponse avec contexte du tour 1
3+ Messages suivants [System] + tous les tours précédents Contexte cumulatif

Points clés :

  1. L’historique est cumulatif - Chaque tour a accès à tous les messages précédents, permettant des références (“ces livres”, “comme tu l’as dit”)
  2. Le message système est crucial - Définit le comportement global du bot (personnalité, domaine d’expertise, contraintes)
  3. La longueur de contexte est limitée - Attention aux conversations très longues (dépassement de max_tokens ou context window)
  4. str(chat_history) sérialise l’historique - Converti en texte pour injection dans le prompt template

Limitations et solutions :

Problème Impact Solution
Contexte trop long Dépassement max_tokens Implémenter sliding window (garder N derniers messages)
Coût API croissant Chaque tour envoie tout l’historique Utiliser summarization ou compression
Perte de focus Le bot peut dériver du sujet initial Ré-injecter le message système périodiquement

Note technique : ChatHistory peut être sérialisé/désérialisé pour persister les conversations entre sessions (ex: sauvegarder en JSON ou base de données).

Conclusion

Resume des concepts

Concept Description Code cle
Kernel Orchestrateur central SK Kernel()
Service Connexion LLM kernel.add_service(OpenAIChatCompletion(...))
Plugin Collection de fonctions kernel.add_plugin(parent_directory=..., plugin_name=...)
Fonction sémantique Prompt template execute kernel.add_function(prompt_template_config=...)
KernelArguments Paramètres dynamiques KernelArguments(input=..., style=...)
ChatHistory Historique conversation ChatHistory() + add_user_message()

Points cles a retenir

  1. Le Kernel est le coeur de SK - Il orchestre services, plugins et fonctions
  2. Les plugins sont modulaires - Chargeables depuis fichiers ou définis inline
  3. KernelArguments permet le passage dynamique - Variables injectees dans les templates
  4. ChatHistory preserve le contexte - Essentiel pour les conversations multi-tours

Pour aller plus loin

Notebook suivant Contenu
02-Functions Function Calling moderne, Memory, Groundedness
03-Agents ChatCompletionAgent, AgentGroupChat

Navigation : Index | 02-Functions >>

Retour au sommet