A la fin de ce notebook, vous saurez : 1. Créer un ChatCompletionAgent simple avec instructions 2. Integrer des plugins avec Function Calling automatique 3. Orchestrer plusieurs agents via AgentGroupChat 4. Définir des stratégies de terminaison personnalisees 5. Comprendre les bases d’OpenAIAssistantAgent (Code Interpreter, File Search)
Prerequis
Python 3.10+
Notebooks 01 et 02 completes
Cle API OpenAI configuree (.env)
Duree estimee : 55 minutes
Sommaire
Section
Contenu
Concepts cles
1
Installation
semantic-kernel, imports
2
Agent Simple
ChatCompletionAgent, instructions, invoke
3
Agent + Plugins
MenuPlugin, FunctionChoiceBehavior.Auto()
4
Group Chat
AgentGroupChat, TerminationStrategy
5
OpenAIAssistantAgent
Code Interpreter, File Search, Threads
6
Conclusion
Resume, exercices, navigation
Qu’est-ce que l’Agent Framework ? Introduit avec SK 1.0, il permet de créer des agents autonomes capables de raisonner, utiliser des outils, et collaborer entre eux. C’est l’evolution naturelle des plugins vers des entites plus intelligentes.
# ============================# Bloc 1 : Installation semantic-kernel et imports# ============================# A n'executer qu'une foisimport asyncioimport loggingimport osfrom dotenv import load_dotenv# Chargement du fichier .env (cles API)load_dotenv("../.env")# Verification de la configurationapi_key = os.getenv("OPENAI_API_KEY")model_id = os.getenv("OPENAI_CHAT_MODEL_ID", "gpt-5")print(f"Configuration chargee:")print(f" - API Key: {'OK'if api_key else'MANQUANTE'}")print(f" - Modele: {model_id}")print("semantic-kernel installe.")
Configuration chargee:
- API Key: OK
- Modele: gpt-5.2
semantic-kernel installe.
1. Installation et imports
Cette cellule installe le SDK Semantic Kernel et importe les modules necessaires pour créer des agents conversationnels.
Concepts cles : - ChatCompletionAgent : Un agent base sur un modèle de chat completion - AgentGroupChat : Orchestrateur pour faire collaborer plusieurs agents - ChatHistory : Historique de conversation partage
Bloc 2 : Simple Agent (Parrot)
Nous créons un agent tout simple, qui répète le message de l’utilisateur sur le ton d’un pirate.
import loggingfrom semantic_kernel import Kernelfrom semantic_kernel.agents import ChatCompletionAgentfrom semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletionfrom semantic_kernel.contents import ChatHistoryAGENT_NAME ="Parrot"AGENT_INSTRUCTIONS ="You are a helpful parrot that repeats the user message in a pirate voice, then ends with 'Arrr!'"# Création du Kernelkernel = Kernel()# On suppose que vous avez défini ou récupéré des clés d'API :# kernel.add_service(OpenAIChatCompletion(...)) ou AzureChatCompletion(...)kernel.add_service(OpenAIChatCompletion(service_id="agent"))agent = ChatCompletionAgent( kernel=kernel, name=AGENT_NAME, instructions=AGENT_INSTRUCTIONS)user_inputs = ["Fortune favors the bold.","I came, I saw, I conquered.","Practice makes perfect.",]asyncdef simple_agent_demo(): chat_history = ChatHistory()# On ajoute les instructions de l'agent en tant que 'developer' ou 'system' chat_history.add_developer_message(AGENT_INSTRUCTIONS)for user_input in user_inputs: chat_history.add_user_message(user_input)print(f"# User: '{user_input}'")asyncfor content in agent.invoke(chat_history):# CORRECTION DÉFINITIVE : Toujours utiliser add_assistant_message# L'API SemanticKernel agents retourne des objets incompatibles avec add_message()ifhasattr(content, 'content'):# Extraire le contenu et le convertir en string content_str =str(content.content) if content.content elsestr(content) chat_history.add_assistant_message(content_str)print(f"# Agent - {content.name or AGENT_NAME}: '{content_str}'")else:# Fallback: convertir tout l'objet en string content_str =str(content) chat_history.add_assistant_message(content_str)print(f"# Agent - {AGENT_NAME}: '{content_str}'")await simple_agent_demo()
# User: 'Fortune favors the bold.'
# Agent - Parrot: 'Fortune be favorin’ the bold, matey. Arrr!'
# User: 'I came, I saw, I conquered.'
# Agent - Parrot: 'I came, I saw, I conquered, matey. Arrr!'
# User: 'Practice makes perfect.'
# Agent - Parrot: 'Practice be makin’ perfect, matey. Arrr!'
Exercice 1 : Agent avec personnalite parametrable
Objectif : Créer une fonction qui genere un agent ChatCompletionAgent avec une personnalite configurable, et tester 3 personnalites différentes sur la même question.
L’agent Parrot utilise des instructions fixees en dur. En production, on veut généralement créer des agents dynamiquement avec différentes personnalites (support client, expert technique, tuteur, etc.) sans dupliquer le code.
Indices : - # Étape 1 : Définir create_personality_agent(name, personality, domain) qui retourne un agent configure - # Étape 2 : Configurer le Kernel et le service dans la fonction (ou les recevoir en paramètre) - # Étape 3 : Tester avec 3 personnalites : “formel”, “humoristique”, “pedagogique” - # Indice : Injecter la personnalite dans les instructions de l’agent
def create_personality_agent(name: str, personality: str, domain: str="technologie") -> ChatCompletionAgent:""" Cree un agent avec une personnalite parametrable. # Etape 1 : Creer un Kernel avec un service OpenAI # Etape 2 : Construire les instructions en combinant personnalite et domaine # Etape 3 : Retourner un ChatCompletionAgent configure # Indice : Utilisez un template d'instructions comme : # f"Tu es un assistant specialise en {domain}. Tu reponds de maniere {personality}." Args: name: Nom de l'agent personality: Style de reponse (formel, humoristique, pedagogique...) domain: Domaine d'expertise Returns: ChatCompletionAgent configure """# TODO etudiant : implementer la creation d'agent parametrable agent_kernel = Kernel() agent_kernel.add_service(OpenAIChatCompletion(service_id=f"personality_{name}")) instructions =f"Tu es un assistant."# TODO etudiant : personnaliser avec personality et domainreturn ChatCompletionAgent( kernel=agent_kernel, name=name, instructions=instructions )# Test : creez 3 agents avec des personnalites differentespersonalities = {"FormelBot": "formel et professionnel","FunBot": "humoristique et decontracte","ProfBot": "pedagogique avec des exemples concrets",}test_question ="Explique-moi ce qu'est Semantic Kernel en 2 phrases."for agent_name, personality in personalities.items(): agent = create_personality_agent(agent_name, personality, domain="IA et Semantic Kernel")print(f"--- {agent_name} (personnalite: {personality}) ---")# TODO etudiant : invoquer l'agent avec la question de test# history = ChatHistory()# history.add_user_message(test_question)# async for content in agent.invoke(history):# print(content.content if hasattr(content, 'content') else str(content))print("Exercice a completer\n")
--- FormelBot (personnalite: formel et professionnel) ---
Exercice a completer
--- FunBot (personnalite: humoristique et decontracte) ---
Exercice a completer
--- ProfBot (personnalite: pedagogique avec des exemples concrets) ---
Exercice a completer
Interprétation : Anatomie d’un Agent Simple
Sortie obtenue : L’agent Parrot transforme 3 proverbes en voix de pirate avec la signature “Arrr!”
Aspect
Valeur
Signification
Instructions
“repeat in a pirate voice”
Définit la personnalité de l’agent
Invocation
agent.invoke(chat_history)
Traite les messages de manière asynchrone
Streaming
async for content in ...
Réception progressive des tokens
Historique
ChatHistory()
Maintient le contexte conversationnel
Points clés :
Séparation des responsabilités : Le Kernel fournit les services (modèle LLM), l’agent ajoute la logique comportementale
Instructions = System Message : Les instructions sont injectées comme message développeur, invisibles pour l’utilisateur
Asynchrone par défaut : SK utilise async/await pour toutes les opérations I/O (appels API)
Historique explicite : Contrairement à l’API OpenAI brute, SK gère l’historique via ChatHistory
Note technique : La correction add_assistant_message() est nécessaire car invoke() retourne des objets StreamingChatMessageContent incompatibles avec add_message() générique.
Exécution de l’agent Parrot
L’agent “Parrot” illustre le cas le plus simple : - Instructions système : Definissent le comportement (repeter en voix de pirate) - Invocation : Chaque message utilisateur est transforme par l’agent - Historique : Les reponses sont ajoutees a ChatHistory pour le contexte
Point technique : La méthode invoke() est asynchrone et retourne un flux (streaming) de contenus.
Bloc 3 : Agent simple avec Plugins
Exemple de plugin MenuPlugin, et agent unique qui répond en utilisant ces fonctions.
import asynciofrom typing import TYPE_CHECKING, Annotatedfrom semantic_kernel import Kernelfrom semantic_kernel.agents import ChatCompletionAgentfrom semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletionfrom semantic_kernel.functions import KernelArguments, kernel_functionfrom semantic_kernel.contents import ChatHistoryclass MenuPlugin:"""Plugin pour gérer un menu"""@kernel_function(description="Liste les specials")def get_specials(self) -> Annotated[str, "Describes specials"]:# print function callprint("get_specials called")return"Special Soup: Clam Chowder\nSpecial Salad: Cobb Salad\nSpecial Drink: Chai Tea"@kernel_function(description="Donne le prix d'un item")def get_item_price(self, menu_item: Annotated[str, "nom de l'item"]) ->str:# print function callprint("get_item_price called")return"$9.99"# Créer kernelkernel2 = Kernel()# Ajout du pluginkernel2.add_plugin(MenuPlugin(), plugin_name="menu")# Ajout du servicekernel2.add_service(OpenAIChatCompletion(service_id="agent2"))# On configure l'auto function-callingsettings2 = kernel2.get_prompt_execution_settings_from_service_id(service_id="agent2")from semantic_kernel.connectors.ai import FunctionChoiceBehaviorsettings2.function_choice_behavior = FunctionChoiceBehavior.Auto()AGENT2_NAME ="Host"AGENT2_INSTRUCTIONS ="Answer questions about the menu."agent2 = ChatCompletionAgent( kernel=kernel2, name=AGENT2_NAME, instructions=AGENT2_INSTRUCTIONS, arguments=KernelArguments(settings=settings2),)asyncdef plugin_agent_demo(): chat_history = ChatHistory() user_msgs = ["Hello","What is the special soup?","What does it cost?","Thanks", ]for user_input in user_msgs: chat_history.add_user_message(user_input)print(f"# User: '{user_input}'") agent_name =None full_response =""# Accumulateur pour la réponse complèteasyncfor content in agent2.invoke_stream(chat_history):ifnot agent_name: agent_name = content.name or AGENT2_NAMEprint(f"# {agent_name}: '", end="")# CORRECTION: Gestion appropriée du StreamingChatMessageContentifhasattr(content, 'content'):# Conversion sécurisée du contenu content_str =str(content.content) if content.content else""# Accumulation de la réponse full_response += content_str# Affichage incrémentalif content_str:print(content_str, end="", flush=True)print("'")# Ajout sécurisé du message complet à l'historiqueif full_response: chat_history.add_assistant_message(full_response)else:# Si pas de contenu, ajouter un message par défaut chat_history.add_assistant_message("[No response generated]")await plugin_agent_demo()
# User: 'Hello'
# Host: 'Hello! I can help with the menu—are you looking for today’s specials or the price of a specific item?'
# User: 'What is the special soup?'
# Host: 'get_specials called
The special soup is **Clam Chowder**.'
# User: 'What does it cost?'
# Host: 'get_item_price called
The **Clam Chowder** costs **$9.99**.'
# User: 'Thanks'
# Host: 'You’re welcome!'
Exercice 2 : Plugin de recherche simulate avec Function Calling
Objectif : Créer un plugin ResearchPlugin avec une fonction de recherche simulee, puis configurer un agent qui l’utilise automatiquement via Function Calling.
L’exercice precede demontre comment le MenuPlugin est appele automatiquement. Vous allez reproduire ce pattern avec un plugin de recherche qui simule des résultats pour un agent “Researcher”.
Indices : - # Étape 1 : Créer une classe ResearchPlugin avec une méthode search decoree de @kernel_function - # Étape 2 : Ajouter le plugin au kernel et configurer FunctionChoiceBehavior.Auto() - # Étape 3 : Créer un agent ChatCompletionAgent avec ce kernel et tester une question de recherche - # Indice : Simulez les résultats avec un dictionnaire {sujet: "Résultats simules pour..."}
class ResearchPlugin:""" Plugin de recherche simulant des resultats. # Etape 1 : Definir la methode search() avec @kernel_function et une description claire # Etape 2 : Utiliser un parametre `topic` annote avec Annotated[str, "sujet de recherche"] # Etape 3 : Retourner une string simulant des resultats de recherche """@kernel_function(description="Recherche des informations sur un sujet") # TODO etudiant : completerdef search(self, topic: Annotated[str, "Sujet de la recherche"]) ->str:""" Simule une recherche et retourne des resultats. # Indice : Retournez une string realiste simulant des resultats de recherche # Indice : Incluez le topic dans les resultats pour que l'agent puisse les exploiter """# TODO etudiant : implementer la recherche simuleereturn"Exercice a completer"# TODO etudiant : retourner des resultats simules# Test du plugin seulresearch = ResearchPlugin()print(research.search("intelligence artificielle"))# TODO etudiant : creer le kernel, ajouter le plugin, configurer l'agent# kernel_research = Kernel()# kernel_research.add_service(OpenAIChatCompletion(service_id="research"))# kernel_research.add_plugin(ResearchPlugin(), plugin_name="research")# settings = kernel_research.get_prompt_execution_settings_from_service_id("research")# settings.function_choice_behavior = FunctionChoiceBehavior.Auto()# # researcher = ChatCompletionAgent(# kernel=kernel_research,# name="Researcher",# instructions="Tu es un chercheur qui utilise l'outil de recherche pour repondre.",# arguments=KernelArguments(settings=settings),# )# Test avec une questionprint("Exercice a completer : configurez l'agent Researcher avec le plugin")
Exercice a completer
Exercice a completer : configurez l'agent Researcher avec le plugin
Interprétation : Function Calling Automatique (ReAct Pattern)
Sortie obtenue : L’agent Host répond aux questions sur le menu en appelant automatiquement les bonnes fonctions du plugin
Question
Fonction appelée
Résultat
“What is the special soup?”
get_specials()
Liste des spéciaux → extraction “Clam Chowder”
“What does it cost?”
get_item_price("Clam Chowder")
“$9.99” (contexte conservé)
“Hello” / “Thanks”
Aucune
Réponse conversationnelle directe
Le graphe Mermaid de la cellule suivante détaille l’architecture du pattern ReAct : première passe du LLM, appel de MenuPlugin.get_specials(), puis synthèse de la réponse.
Comparaison avec les Plugins simples :
Aspect
Plugin sans Agent
Agent + Plugin
Appel de fonction
Manuel (kernel.invoke("plugin-func"))
Automatique (LLM décide)
Extraction paramètres
Développeur spécifie
LLM extrait du contexte
Contexte conversationnel
Absent
Conservé dans ChatHistory
Raisonnement
Aucun
LLM raisonne avant d’agir
Points clés :
FunctionChoiceBehavior.Auto() : Active le mode “tool use” du LLM (équivalent à tools dans l’API OpenAI brute)
Inférence de paramètres : Le LLM extrait “Clam Chowder” du contexte précédent pour get_item_price()
Transparence : Les appels de fonction sont invisibles pour l’utilisateur final
Streaming multi-phases : Le flux contient à la fois les tool calls et la réponse textuelle finale
Note technique : Le pattern invoke_stream() + accumulation de full_response permet d’afficher progressivement la réponse tout en maintenant l’historique complet.
Le diagramme ci-dessous rend cette sequence d’appel de fonction sous forme de graphe : le LLM detecte qu’il faut un outil, l’invoque, puis synthetise la reponse finale a partir du résultat.
flowchart TD
U(["User : What is the special soup ?"]) --> L1["LLM (gpt-5)<br/>1. Analyse 2. Identifie get_specials() 3. Tool call"]
L1 --> P["MenuPlugin.get_specials()<br/>retourne : Special Soup: Clam Chowder..."]
P --> L2["LLM (gpt-5)<br/>4. Synthetise la reponse"]
L2 --> A(["The special soup is..."])
classDef llm fill:#fff3cd,stroke:#b8860b,color:#5c4400
classDef tool fill:#cfe2ff,stroke:#084298,color:#052c65
classDef io fill:#d1e7dd,stroke:#0f5132,color:#0a3622
class L1,L2 llm
class P tool
class U,A io
Lecture. Le function calling se deroule en deux passes du LLM autour d’un appel d’outil : a la première passe, le modèle analyse la question et decide d’appeler get_specials() (il ne repond pas directement) ; le plugin s’execute et renvoie une donnee factuelle ; a la seconde passe, le LLM integre ce résultat pour formuler une reponse en langage naturel. C’est ce cycle raisonner -> agir -> observer -> repondre qui distingue un agent d’un simple appel de completion.
Analyse des appels de fonction automatiques
Dans l’exécution ci-dessus, observez la sequence d’appels :
Le paramètre FunctionChoiceBehavior.Auto() permet a l’agent de : - Analyser la question de l’utilisateur - Determiner quelle fonction appeler - Extraire les paramètres (ex: “Clam Chowder”) - Integrer le résultat dans sa reponse
C’est la base du ReAct pattern (Reasoning + Acting).
Exécution de l’agent avec plugins
Le plugin MenuPlugin expose des fonctions que l’agent peut appeler automatiquement : - get_specials() : Retourne les plats du jour - get_item_price() : Retourne le prix d’un article
Function Calling Auto : Le paramètre FunctionChoiceBehavior.Auto() permet a l’agent de decider lui-même quand appeler ces fonctions en fonction du contexte de la conversation.
Observez les appels get_specials called et get_item_price called dans la sortie.
Bloc 4 : Group Chat
Exemple d’un chat groupé : un agent CopyWriter, un agent ArtDirector, etc. On utilise la AgentGroupChat.
import asynciofrom semantic_kernel.agents import AgentGroupChat, ChatCompletionAgentfrom semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletionfrom semantic_kernel.contents import AuthorRole, ChatMessageContentfrom semantic_kernel.agents.strategies import TerminationStrategyfrom semantic_kernel import Kernelclass ApprovalTerminationStrategy(TerminationStrategy):asyncdef should_agent_terminate(self, agent, history):return"approved"in history[-1].content.lower()# On crée un kernel par agent, ou le même kernel + service differencié.def create_kernel_for(name): k = Kernel()# on admet qu'on a paramétré un service openAI. k.add_service(OpenAIChatCompletion(service_id=name))return kREVIEWER_NAME ="ArtDirector"REVIEWER_INSTRUCTIONS ="You are an art director. If the copy is good, say 'Approved'. Otherwise, propose improvements."reviewer_agent = ChatCompletionAgent( kernel=create_kernel_for(REVIEWER_NAME), name=REVIEWER_NAME, instructions=REVIEWER_INSTRUCTIONS,)COPYWRITER_NAME ="CopyWriter"COPYWRITER_INSTRUCTIONS ="You are a copywriter. Provide short but strong marketing copy."writer_agent = ChatCompletionAgent( kernel=create_kernel_for(COPYWRITER_NAME), name=COPYWRITER_NAME, instructions=COPYWRITER_INSTRUCTIONS,)group_chat = AgentGroupChat( agents=[reviewer_agent, writer_agent], termination_strategy=ApprovalTerminationStrategy(agents=[reviewer_agent], maximum_iterations=6))asyncdef group_chat_demo(): user_msg ="I need a slogan for a new line of electric bikes"await group_chat.add_chat_message(ChatMessageContent(role=AuthorRole.USER, content=user_msg))print(f"# User: '{user_msg}'")asyncfor content in group_chat.invoke():# Gestion sécurisée du contenu pour éviter ContentInitializationErrorifhasattr(content, 'content') and content.content:print(f"# Agent - {content.name or'*'}: '{content.content}'")else:print(f"# Agent - {content.name or'*'}: '{str(content)}'")print(f"# IS COMPLETE: {group_chat.is_complete}")await group_chat_demo()
# User: 'I need a slogan for a new line of electric bikes'
# Agent - ArtDirector: 'Charge Your Ride.
Options if you want alternates:
- Ride Far. Ride Electric.
- Power the Pedal.
- Silent Speed, Serious Range.
- Plug In. Go Anywhere.
- Clean Power. Pure Fun.
To tailor it, tell me: commuter vs. adventure, premium vs. budget, and your key differentiator (range, speed, design, light weight).'
# Agent - CopyWriter: 'Charge Your Ride.
More slogan options:
- Ride Far. Ride Electric.
- Power the Pedal.
- Silent Speed, Serious Range.
- Plug In. Go Anywhere.
- Clean Power. Pure Fun.
- Pedal Less. Live More.
- City Ready. Trail Hungry.
- Range That Keeps Up.
Tell me your vibe (commuter/adventure), price point (premium/value), and your #1 edge (range, speed, weight, design), and I’ll tailor 10 punchy picks.'
# Agent - ArtDirector: 'Not approved — “Charge Your Ride” is catchy, but it’s generic and could fit any electric product. It doesn’t signal bikes, benefit, or brand personality.
Improvements (more ownable, e-bike specific):
- **Electric, Meet Effortless.**
- **Go Far. Feel Fresh.**
- **Make Every Mile Easy.**
- **Commute, Upgraded.**
- **More Miles. Less Sweat.**
- **Your City. Supercharged.**
- **Power When You Want It.**
- **Quiet Power. Big Range.**
Tell me: commuter vs. trail, premium vs. value, and your standout (range/weight/speed/design), and I’ll tighten these into a single best-line direction.'
# IS COMPLETE: True
Exercice 3 : Stratégie de terminaison par mot-cle amelioree
Objectif : Implementer une stratégie de terminaison KeywordTerminationStrategy qui arrete le group chat quand un mot-cle spécifique est detecte dans le dernier message d’un agent designe.
La ApprovalTerminationStrategy utilisee precedemment est basique et peut echouer si le mot exact n’apparait pas. Vous allez créer une version plus robuste qui supporte plusieurs mots-cles et insensibilite a la casse.
Indices : - # Étape 1 : Heriter de TerminationStrategy et implementer should_agent_terminate - # Étape 2 : Verifier si le dernier message contient l’un des mots-cles (case-insensitive) - # Étape 3 : Limiter la verification aux agents specifies dans agents (comme ApprovalTerminationStrategy) - # Indice : any(kw in history[-1].content.lower() for kw in self.keywords)
class KeywordTerminationStrategy(TerminationStrategy):""" Strategie de terminaison basee sur des mots-cles configurables. # Etape 1 : Definir __init__ avec une liste de keywords (ex: ["approved", "done", "final"]) # Etape 2 : Implementer should_agent_terminate() qui verifie le dernier message # Etape 3 : Convertir le contenu en minuscules avant la comparaison """# SK strategies sont des modeles pydantic : keywords doit etre un champ declare,# passe via super().__init__ (l'assignation directe self.keywords est rejetee). keywords: list[str] =Nonedef__init__(self, keywords: list[str] =None, **kwargs):super().__init__(keywords=keywords or ["approved"], **kwargs)# TODO etudiant : personnaliser les mots-cles par defautasyncdef should_agent_terminate(self, agent, history) ->bool:""" Verifie si le dernier message contient un mot-cle. # Indice : Acceder au dernier message avec history[-1].content # Indice : Convertir en minuscules avec .lower() """# TODO etudiant : implementer la verificationreturnFalse# TODO etudiant : remplacer par la logique de detection# Test unitaire de la strategiestrategy = KeywordTerminationStrategy( keywords=["approved", "done", "finalise"], agents=[], # sera configure avec les vrais agents maximum_iterations=10)# Simulation avec des messages fictifsfrom semantic_kernel.contents import ChatMessageContenttest_messages = [ ChatMessageContent(role=AuthorRole.ASSISTANT, content="Le slogan est pret."), ChatMessageContent(role=AuthorRole.ASSISTANT, content="APPROVED - version finale."),]result =await strategy.should_agent_terminate(agent=None, history=test_messages)print(f"Terminaison detectee : {result}")print("Exercice a completer")
Terminaison detectee : False
Exercice a completer
Interprétation : Orchestration Multi-Agents et Stratégies
Sortie obtenue : Dialogue itératif entre CopyWriter (propose des slogans) et ArtDirector (critique), mais incomplet (is_complete=False)
Itération
Agent
Message
État
1
ArtDirector
“Ride the Future: Electrify Your Journey”
Suggestion initiale
2
CopyWriter
“Unleash the Power of Green: Ride Beyond”
Proposition
3
ArtDirector
(répète slogan #1)
Problème de contexte
4
CopyWriter
“Unchain Your Commute…”
Nouvelle proposition
5
ArtDirector
“I’m looking for organic skincare”
Dérapage (hors sujet)
6
CopyWriter
“Purely You: Embrace Nature’s Glow”
Répond au dérapage
Analyse du problème de terminaison :
ApprovalTerminationStrategy attend "approved" dans le message
↓
Aucun message ne contient "approved"
↓
Maximum 6 itérations atteint → STOP (sans validation)
↓
is_complete = False (terminaison par limite, pas par succès)
Limite d’itérations cruciale : Sans maximum_iterations, risque de boucle infinie si le mot-clé n’apparaît jamais
Prompt engineering pour terminaison : Les instructions de l’ArtDirector devraient explicitement mentionner “Say ‘Approved’ when satisfied”
Dérapage de contexte : L’itération 5 montre que les agents peuvent perdre le fil (problème connu avec les longs historiques)
is_complete vs succès : False signifie que la stratégie n’a pas validé, pas nécessairement un échec fonctionnel
Améliorations possibles :
# Instructions plus explicitesREVIEWER_INSTRUCTIONS ="""You are an art director. Review the copywriter's slogan. If it's good, respond with exactly 'APPROVED'.Otherwise, suggest ONE specific improvement."""# Stratégie hybrideclass HybridTerminationStrategy(TerminationStrategy):asyncdef should_agent_terminate(self, agent, history): last_msg = history[-1].content.lower()# Condition 1: Mot-clé expliciteif"approved"in last_msg:returnTrue# Condition 2: LLM juge l'objectif atteint judgment =await llm_judge(history, objective="Create bike slogan")return judgment =="SUCCESS"
Cas d’usage production : Le notebook 10-NotebookMaker implémente un système 3-agents (Admin, Coder, Reviewer) avec stratégies robustes pour éviter ces problèmes.
Bloc 5 : OpenAIAssistantAgent (Apercu)
L’API Assistants d’OpenAI offre des capacites avancees que ChatCompletionAgent n’a pas nativement :
from semantic_kernel.agents.open_ai import OpenAIAssistantAgent# Creation de l'agent avec Code Interpreteragent =await OpenAIAssistantAgent.create( kernel=kernel, name="DataAnalyst", instructions="Tu es un analyste de données. Utilise Python pour les calculs.", enable_code_interpreter=True, enable_file_search=True)# Creation d'un thread (session persistante)thread =await agent.create_thread()# Ajout d'un fichier pour analyseawait agent.add_file(thread_id=thread.id, file_path="data.csv")# Invocation avec contexte fichierresponse =await agent.invoke(thread_id=thread.id, message="Analyse ce CSV et genere un graphique")
Points cles : - enable_code_interpreter=True : L’agent peut executer du Python - enable_file_search=True : L’agent peut rechercher dans les fichiers - thread : Session persistante cote OpenAI (pas de ChatHistory local) - Les fichiers sont uploades vers OpenAI et indexes automatiquement
Note : Cette API necessite un compte OpenAI avec acces aux Assistants (payant). Pour des alternatives locales, voir les Vector Stores dans le notebook 05.
Exécution du Group Chat
Le AgentGroupChat orchestre plusieurs agents qui collaborent : - ArtDirector : Valide ou demande des ameliorations du texte - CopyWriter : Propose du contenu marketing
Stratégie de terminaison : La classe ApprovalTerminationStrategy arrete la conversation quand l’ArtDirector dit “approved”. Cela illustre comment définir des conditions d’arret personnalisees.
La limite maximum_iterations=6 protege contre les boucles infinies.
Conclusion
Resume des concepts
Concept
Description
Code cle
ChatCompletionAgent
Agent de base SK
ChatCompletionAgent(kernel, name, instructions)
Instructions
Personnalite de l’agent
Message système definissant le comportement
Plugins + Agent
Outils pour l’agent
FunctionChoiceBehavior.Auto()
AgentGroupChat
Orchestration multi-agents
AgentGroupChat(agents, termination_strategy)
TerminationStrategy
Condition d’arret
ApprovalTerminationStrategy, custom
OpenAIAssistantAgent
API Assistants
Code Interpreter, File Search, Threads
Points cles a retenir
Un agent = Kernel + Instructions - Le Kernel fournit les capacites, les instructions definissent le comportement
Function Calling automatique - FunctionChoiceBehavior.Auto() permet a l’agent de decider quand utiliser les outils
AgentGroupChat pour la collaboration - Plusieurs agents peuvent dialoguer et iterer
Stratégies configurables - Sélection et terminaison sont personnalisables
OpenAIAssistantAgent pour les cas avances - Code Interpreter et RAG integres
Exercice suggere
Créez un groupe de 2 agents : - Researcher : Recherche des informations (simule avec un plugin) - Writer : Redige un article base sur les recherches
## Squelette de departclass ResearchPlugin:@kernel_function(description="Recherche sur un sujet")def search(self, topic: str) ->str:returnf"Résultats de recherche pour: {topic}..."## A vous de créer les agents et le group chat !
1. Agent simple (Parrot)
→ Comprendre Instructions + Kernel + invoke()
2. Agent + Plugins (Menu)
→ Maîtriser FunctionChoiceBehavior.Auto()
3. AgentGroupChat (CopyWriter + ArtDirector)
→ Orchestrer des agents, gérer les stratégies
4. OpenAIAssistantAgent (Code Interpreter)
→ Cas avancés : code exécution, RAG intégré
5. Production (NotebookMaker)
→ Système robuste 3+ agents avec gestion d'erreurs
6. Comparaison avec d’autres frameworks
Framework
Philosophie
Forces
Faiblesses
Semantic Kernel
“Kernel-centric” (services partagés)
Intégration .NET, plugins réutilisables
Moins mature qu’AutoGen
AutoGen
“Agent-centric” (agents autonomes)
Patterns multi-agents éprouvés, communauté active
Principalement Python
LangGraph
“Graph-centric” (workflows as graphs)
Contrôle fin, debugging visuel
Courbe d’apprentissage
CrewAI
“Rôle-centric” (agents = métiers)
Abstractions haut niveau
Moins flexible
Positionnement SK : Meilleur choix pour écosystème Microsoft (.NET, Azure), plugins partagés entre agents, et migration progressive depuis des applications SK existantes.