Le notebook 13_Agentic_Orchestration.ipynb montrait comment un agent planificateur choisit, en code, quel moteur de test-time scaling invoquer. Ce notebook NB-19 ouvre une autre dimension : orchestrer des LLM sans écrire de code, depuis un produit GUI self-hébergé - Open WebUI (OWUI) v0.9.0 - et comparer ce que ce produit atteint à ce que donnent les frameworks code-level (LangChain / LangGraph, CrewAI).
Idée centrale (concept-phare) : il existe trois niveaux d’abstraction pour orchestrer des LLM - le produit OWUI (Automations / Task Management / Calendar), la librairie de graphes d’états (LangChain / LangGraph), et le framework multi-agents par rôles (CrewAI). OWUI v0.9.0 atteint sans code ce que les frameworks atteignent avec du code - avec un trade-off : la facilité contre le contrôle fin.
Objectifs d’apprentissage
À la fin de ce notebook, vous saurez : 1. Situer les trois niveaux d’abstraction (produit / librairie / framework) et choisir le bon selon un besoin d’orchestration. 2. Construire une règle RRULE (iCalendar, RFC 5545) pour planifier une automation OWUI (« resume mes notes chaque matin à 8h ») et la manipuler en Python pur. 3. Distinguer les axes d’orchestration d’OWUI : temporel (Automations), structurel (Task Management), calendrier (Calendar), et l’overlay virtuel non persisté. 4. Expliquer le breaking change async de la migration 0.8 -> 0.9 (async def, AsyncSession, aiosqlite). 5. Scaffold un appel à l’API OWUI (OpenAI-compatible) avec graceful skip quand la clé d’instance (OWUI_API_KEY) n’est pas encore provisionnée.
Prérequis
Avoir vu NB-13 (Orchestration agentique) et NB-04 (Function Calling) pour le pont code-level.
Python 3.10+ ; notions d’async / await.
(Pour la section API) une instance OWUI accessible et une clé OWUI_API_KEY. La section est conçue pour s’exécuter même sans clé (skip gracieux documenté, règle C.1).
Durée estimée : 60 minutes
0. Setup - infrastructure et convention d’exécution
On installe trois dépendances légères : python-dateutil (manipulation RRULE), openai (le client OpenAI-compatible qu’on pointera vers OWUI), et python-dotenv (chargement du .env). Le notebook est conçu pour tourner de bout en bout même si la clé d’instance OWUI n’est pas provisionnée : la section 6 (API skeleton) se met alors en RECOVERABLE-USER-HAND et affiche un message explicite plutot que de crasher (règle C.1 - aucune erreur volontaire).
%pip install -q python-dateutil openai python-dotenvimport osfrom pathlib import Pathfrom datetime import datetime, timezone, timedeltafrom dotenv import load_dotenv# --- Chargeur .env robuste (Papermill change le cwd) ---_env_path =None_current = Path.cwd()for _i inrange(10):if (_current /".env").exists(): _env_path = _current /".env"breakif _current.name in ("Texte", "GenAI", "MyIA.AI.Notebooks"):break _current = _current.parentif _env_path isNone:for _cand in (Path.cwd() /"MyIA.AI.Notebooks"/"GenAI"/"Texte"/".env", Path.cwd() /"MyIA.AI.Notebooks"/"GenAI"/".env", Path.cwd() /"GenAI"/".env"):if _cand.exists(): _env_path = _candbreakif _env_path isnotNone: load_dotenv(_env_path)print(f".env charge depuis : {_env_path.name}")else:print(".env non trouve (OK pour ce notebook - seule la section 6 en a besoin).")# --- Configuration OWUI (OWUI_URL + OWUI_API_KEY) ---OWUI_URL = os.getenv("OWUI_URL", "http://localhost:8080")OWUI_API_KEY = os.getenv("OWUI_API_KEY") # JAMAIS de default - on detecte l'absenceOWUI_READY = OWUI_API_KEY isnotNoneprint(f"OWUI_URL = {OWUI_URL}")print(f"OWUI_API_KEY = {'configure (masquee)'if OWUI_READY else'ABSENT (RECOVERABLE-USER-HAND)'}")print(f"OWUI_READY = {OWUI_READY}")
Note: you may need to restart the kernel to use updated packages.
.env non trouve (OK pour ce notebook - seule la section 6 en a besoin).
OWUI_URL = http://localhost:8080
OWUI_API_KEY = ABSENT (RECOVERABLE-USER-HAND)
OWUI_READY = False
1. Trois niveaux d’abstraction pour orchestrer des LLM
Avant de plonger dans OWUI, posons le decor. Orchestrer des LLM - planifier des exécutions dans le temps, decomposer une tâche complexe en sous-étapes suivies, maintenir un état - peut se faire a trois niveaux d’abstraction très différents :
Philosophie
Exemple
Ce qu’écrit l’utilisateur
Ou vit l’orchestration
Produit GUI self-hébergé
Open WebUI (v0.9.0)
Rien (formulaires + prompts en langage naturel)
Dans le produit
Librairie / framework de graphes
LangChain / LangGraph
Du code (chaînes, agents, graphes d’états)
Dans votre app
Framework multi-agents par rôles
CrewAI
Du code (Agents rôle/goal/backstory, Tasks, Crew, Flows)
Dans votre app
Concept-phare du notebook. OWUI v0.9.0 fait passer le produit d’un simple front de chat à une plateforme d’orchestration : on peut desormais y planifier des prompts (Automations), donner à un modèle une structure de suivi de tâches (Task Management), et tenir un calendrier (Calendar) - sans écrire de code. La question pédagogique devient : quand cette absence de code est-elle un gain, et quand est-elle un plafond ?
Référence : la notion d’orchestration agentique en code est detaillee dans LangGraph et CrewAI Flows. Les capacites produit d’OWUI sont documentées dans la release v0.9.0.
2. Les trois piliers d’orchestration d’OWUI v0.9.0
OWUI organise l’orchestration autour de trois piliers, chacun repondant à un axe distinct : le temps (Automations), la structure (Task Management), et le calendrier (Calendar). On les parcourt un par un.
2.1 Scheduled Automations - l’axe temporel
Une automation planifie un prompt qui s’exécute automatiquement à une heure donnee. Chaque exécution crée un vrai chat et passe par le pipeline chat_completion normal d’OWUI - donc avec les mêmes filtres, outils et modèle qu’un chat lance à la main.
Deux points clés à retenir :
Pas de cron. La planification utilise des intervalles predefinis (Once / Hourly / Daily / Weekly / Monthly) ou la syntaxe RRULE (iCalendar, RFC 5545). RRULE est strictement plus expressive : FREQ=DAILY;BYHOUR=8;BYMINUTE=0 code « tous les jours à 8h00 », FREQ=WEEKLY;BYDAY=MO,WE,FR code « lundi, mercredi, vendredi ».
Pilotable depuis le chat. En mode Native Function Calling, le modèle gere lui-même ses automations via les outils create_automation, update_automation, list_automations, toggle_automation, delete_automation.
# On construit, en Python pur, la regle RRULE equivalente a l'automation# « resume mes notes chaque matin a 8h00 » - sans aucun appel reseau.# Cette cell tourne meme si OWUI_API_KEY est absent (concept verifie par execution reelle).from dateutil.rrule import rrule, DAILY, WEEKLY# Origine : demain a 00:00 (on force ensuite BYHOUR=8 pour fixer l'heure).origine = datetime.now(timezone.utc) + timedelta(days=1)# Regle : tous les jours a 08:00 UTC (pour la demo).regle_matin = rrule( freq=DAILY, dtstart=origine, byhour=8, byminute=0, bysecond=0, count=7, # on projette les 7 prochaines occurrences)# OWUI stocke la forme TEXT (RFC 5545) ; on la reconstruit ici a la main pour la lisibilite.regle_text ="FREQ=DAILY;BYHOUR=8;BYMINUTE=0"print("=== Automation : « Resume mes notes chaque matin » ===")print(f"RRULE (iCalendar TEXT) : {regle_text}")print("Prochaines 7 occurrences prevues (UTC) :")for i, occ inenumerate(regle_matin, 1):print(f" {i}. {occ.strftime('%Y-%m-%d %H:%M:%S %Z')}")# Contraste pedagogique : l'intervalle predefini "Daily" d'OWUI est EQUIVALENT a cette# RRULE pour le cas simple ; mais RRULE reste plus expressive (BYDAY, UNTIL, INTERVAL, ...).from dateutil.rrule import MO, TU, THregle_lun_mar_jeu = rrule( freq=WEEKLY, dtstart=origine, byweekday=(MO, TU, TH), # lundi, mardi, jeudi byhour=8, byminute=0, count=6,)print()print("=== Variante RRULE « lundi / mardi / jeudi a 8h » (impossible avec l'intervalle predefini) ===")for occ in regle_lun_mar_jeu:print(f" {occ.strftime('%Y-%m-%d %H:%M:%S %A')}")
=== Automation : « Resume mes notes chaque matin » ===
RRULE (iCalendar TEXT) : FREQ=DAILY;BYHOUR=8;BYMINUTE=0
Prochaines 7 occurrences prevues (UTC) :
1. 2026-09-20 08:00:00 UTC
2. 2026-09-21 08:00:00 UTC
3. 2026-09-22 08:00:00 UTC
4. 2026-09-23 08:00:00 UTC
5. 2026-09-24 08:00:00 UTC
6. 2026-09-25 08:00:00 UTC
7. 2026-09-26 08:00:00 UTC
=== Variante RRULE « lundi / mardi / jeudi a 8h » (impossible avec l'intervalle predefini) ===
2026-09-21 08:00:37 Monday
2026-09-22 08:00:37 Tuesday
2026-09-24 08:00:37 Thursday
2026-09-28 08:00:37 Monday
2026-09-29 08:00:37 Tuesday
2026-10-01 08:00:37 Thursday
Interpretation : la RRULE comme langage de planification
Sortie obtenue : deux listes d’occurrences - une quotidienne à 08:00, l’autre limitee a lundi/mardi/jeudi - reconstruites à partir de la chaîne iCalendar FREQ=DAILY;BYHOUR=8;BYMINUTE=0.
Élément
Ce qu’il exprime
Équivalent OWUI (intervalle predefini)
FREQ=DAILY
La frequence de récurrence
Intervalle « Daily »
BYHOUR=8;BYMINUTE=0
L’heure d’exécution
Pas d’équivalent direct
BYDAY=MO,TU,TH
Sous-ensemble de jours de la semaine
Impossible
UNTIL=... / COUNT=N
La borne temporelle de fin
Configurable
Points clés : 1. L’intervalle predefini « Daily » d’OWUI est un cas particulier de RRULE : pratique, mais limite à une récurrence uniforme. 2. Des qu’on veut « tous les jours de la semaine à 8h sauf le week-end » ou « le dernier vendredi du mois », RRULE devient necessaire - c’est exactement le langage qu’OWUI expose cote produit, sans qu’on ecrive de cron. 3. On n’a pas fait d’appel réseau : la planification est une donnee (la chaîne RRULE), calculable et testable en Python pur.
Note technique : python-dateutil est l’implementation de référence de RRULE en Python (RFC 5545). OWUI stocke la même chaîne en base et l’evalue cote backend.
A ne pas confondre avec les Automations. Task Management est un outil chat-time : il donne à un modèle agent une structure pour planifier et suivre un travail multi-étapes - une liste de tâches vivante avec des statuts explicites (créer / mettre a jour / suivre, statut en temps réel).
Pilier
Axe
Question abordee
Automations
Temporel
Quand exécuter ce prompt ?
Task Management
Structurel / agentique
*Comment decomposer ce travail et suivre chaque sous-étape ?
Les deux sont complementaires : une Automation peut declencher un prompt dont le modèle gerait la decomposition via Task Management. L’exercice 2 vous fait comparer un prompt naif et un prompt enrichi de Task Management sur une même tâche complexe.
OWUI v0.9.0 ajoute un espace calendrier complet : vues jour / semaine / mois, creation et modification d’événements (manuelle ou assistee par l’IA en langage naturel), invitations avec suivi de statut, récurrences, et alertes (toasts in-app, pop-ups navigateur, webhooks personnalises).
Par-dessus, OWUI projette un « Scheduled Tasks calendar » : un calendrier virtuel, en lecture seule, non persisté en base, synthetise au moment de la réponse API. Il projette les exécutions d’automations a venir et relie chaque run passe a son log de chat.
C’est une distinction pédagogique forte, à retenir :
Type d’événement
Persiste en base ?
Source
Événement calendrier (reunion, rendez-vous)
Oui
Saisi par l’utilisateur / l’IA
Tâche planifiée projetee (run d’automation futur)
Non - projection runtime
Synthetisee à la volée depuis les automations dues
3. Sous le capot - comment OWUI exécute une automation
Le « produit » repose sur des patterns classiques de backend. Comprendre cette architecture permet de raisonner sur la fiabilite d’une automation en production.
Un unique process d’arriere-plan, scheduler_worker_loop (sur le backend FastAPI), gere à la fois l’exécution des automations dues et la dispatch des alertes calendrier. Un seul worker, deux responsabilites.
Une mockRequest ASGI (_build_request) est construite pour invoquer le pipeline interne chat_completion - d’ou le fait qu’un run d’automation est un vrai chat, traçable, soumis aux mêmes filtres et outils qu’une interaction manuelle.
Journalisation en table automation_run : chaque exécution y laisse une ligne avec son statut (success / error) et le chat_id associe. C’est ce qui rend les runs observables et retrouvables a posteriori.
Alertes calendrier : CALENDAR_ALERT_LOOKAHEAD_MINUTES contrôle l’horizon de declenchement, et Socket.IO pousse les notifications en temps réel vers le navigateur.
Le diagramme ci-dessous rend la boucle d’ordonnancement sous forme de graphe : a chaque tick, le worker traite les automatisations dues et les alertes calendrier.
flowchart TD
T(["scheduler_worker_loop<br/>(process FastAPI unique) — chaque tick"]) --> A1["1. Automations dues"]
A1 --> BR["_build_request<br/>(mock ASGI)"]
BR --> PIPE["pipeline chat_completion<br/>(filtres + outils + modèle)"]
PIPE --> TBL[("table automation_run<br/>statut + chat_id")]
T --> A2["2. Alertes calendrier"]
A2 --> SIO["Socket.IO / webhooks<br/>(lookahead CALENDAR_ALERT_LOOKAHEAD_MINUTES)"]
classDef loop fill:#d1e7dd,stroke:#0f5132,color:#0a3622
classDef step fill:#fff3cd,stroke:#b8860b,color:#5c4400
classDef store fill:#cfe2ff,stroke:#084298,color:#052c65
class T loop
class A1,A2,BR,PIPE,SIO step
class TBL store
Lecture. Un unique worker FastAPI cadence toute l’orchestration. A chaque tick il fait deux choses : (1) executer les automatisations dues en fabriquant une requête interne (_build_request via un mock ASGI) qui traverse le même pipelinechat_completion que les requêtes utilisateur (filtres + outils + modèle), puis en consignant le résultat dans automation_run ; (2) emettre les alertes calendrier via Socket.IO/webhooks selon un horizon de pre-avis. Reutiliser le pipeline chat pour les tâches planifiees evite de dupliquer la logique d’outils et de filtres.
4. Migration 0.8.x -> 0.9.0 : le passage en asynchrone (breaking change)
v0.9.0 marque la couche base de données comme asynchrone pour eviter de bloquer l’event loop (ce qui tuerait la concurrence du backend). Concretement, les extensions OWUI (Tools / Functions / Pipes / Filters / Actions) gardent leurs signatures mais doivent devenir asynchrones :
Avant (0.8.x)
Après (0.9.0)
Pourquoi
def handler(...)
async def handler(...) + await sur la DB
Ne pas bloquer l’event loop
Session + query()
select() + AsyncSession
SQLAlchemy 2.0 style
Driver synchrone (sqlite3)
Driver async (aiosqlite / asyncpg)
IO non bloquants
Lib bloquante appelee direct
anyio.to_thread.run_sync(...)
Offload sur un thread
Deux autres changements notables de v0.9.0 : le passthrough OpenAI devient opt-in (plus actif par défaut), et le timeout des serveurs d’outils MCP devient configurable.
5. Comparatif : OWUI vs LangChain / LangGraph vs CrewAI
On consolide la comparaison sur cinq axes - le code ci-dessous construit cette table en Python pur et l’affiche formatee (sortie reelle, aucun appel réseau). C’est le même contenu que le tableau du document de référence, rendu exécutable pour fixer les idées.
# Tableau comparatif des 3 niveaux d'abstraction, en Python pur (sortie reelle).# Source : docs/genai/open-webui-orchestration.md S5, lui-meme ancre sur les docs officielles.axes = [ ("Modele d'orchestration","Produit GUI + outils chat : Automations (chat-completion planifie) + Task Management ""(liste de taches agentique). Aucun code cote utilisateur.","Librairie : chaines/agents (LangChain) + graphes d'etats cycliques (LangGraph). Code developpeur.","Framework multi-agents par roles : Agents (role/goal/backstory), Tasks, Crew, Flows. Code Python."), ("Planification (scheduling)","Natif : Automations RRULE/intervalles (PAS de cron). Outils chat create_automation, etc.","Aucun scheduler natif - a apporter en externe (cron, Temporal, Celery).","Pas de scheduler cron natif (a verifier) - generalement delegue au cron OS/cloud ; les Flows orchestrent mais ne sont pas un calendrier."), ("Persistance d'etat","Automations persistees + table automation_run (statut + chat_id) ; Task Management = liste vivante dans la session ; overlay calendrier virtuel/runtime, non persiste.","LangGraph : checkpointing (etat de graphe persistant) + abstractions memoire LangChain.","Module memoire (court/long terme) ; etat surtout intra-run."), ("Facilite pour etudiants","Tres haute : zero code, GUI, prompts en langage naturel. Ideal en introduction.","Moyenne a raide : nombreuses abstractions (chains, agents, retrievers, graphes), oriente code.","Moyenne : Python lisible, mais la conception par roles ajoute une charge conceptuelle."), ("Auto-hebergeable","Oui, produit cle en main (Docker ghcr.io/open-webui/open-webui).","Librairie : on heberge sa propre application (pas un produit). Executable partout.","OSS executable partout (framework) ; existe aussi en offre cloud commerciale."),]colonnes = ("Axe", "OWUI v0.9.0", "LangChain / LangGraph", "CrewAI")def wrap(text, width):# Enveloppe un texte sur des lignes de largeur <= width. words = text.split() lines, cur = [], ""for w in words:iflen(cur) +len(w) +1<= width: cur = (cur +" "+ w).strip()else:if cur: lines.append(cur) cur = wif cur: lines.append(cur)return lines or [""]W = (24, 38, 38, 38)sep ="+"+"+".join("-"* (w +2) for w in W) +"+"print(sep)print("| "+" | ".join(c.ljust(w) for c, w inzip(colonnes, W)) +" |")print(sep)for row in axes: cells_wrapped = [wrap(c, w) for c, w inzip(row, W)] n =max(len(c) for c in cells_wrapped)for i inrange(n): line = [(cw[i] if i <len(cw) else"").ljust(w)for cw, w inzip(cells_wrapped, W)]print("| "+" | ".join(line) +" |")print(sep)print()print("Sources : LangGraph vs Temporal (langchain.com) ; CrewAI Flows (docs.crewai.com).")print("Cellule « a verifier » : absence de scheduler cron natif chez CrewAI (a confirmer).")
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
| Axe | OWUI v0.9.0 | LangChain / LangGraph | CrewAI |
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
| Modele d'orchestration | Produit GUI + outils chat : | Librairie : chaines/agents (LangChain) | Framework multi-agents par roles : |
| | Automations (chat-completion planifie) | + graphes d'etats cycliques | Agents (role/goal/backstory), Tasks, |
| | + Task Management (liste de taches | (LangGraph). Code developpeur. | Crew, Flows. Code Python. |
| | agentique). Aucun code cote | | |
| | utilisateur. | | |
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
| Planification | Natif : Automations RRULE/intervalles | Aucun scheduler natif - a apporter en | Pas de scheduler cron natif (a |
| (scheduling) | (PAS de cron). Outils chat | externe (cron, Temporal, Celery). | verifier) - generalement delegue au |
| | create_automation, etc. | | cron OS/cloud ; les Flows orchestrent |
| | | | mais ne sont pas un calendrier. |
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
| Persistance d'etat | Automations persistees + table | LangGraph : checkpointing (etat de | Module memoire (court/long terme) ; |
| | automation_run (statut + chat_id) ; | graphe persistant) + abstractions | etat surtout intra-run. |
| | Task Management = liste vivante dans | memoire LangChain. | |
| | la session ; overlay calendrier | | |
| | virtuel/runtime, non persiste. | | |
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
| Facilite pour etudiants | Tres haute : zero code, GUI, prompts | Moyenne a raide : nombreuses | Moyenne : Python lisible, mais la |
| | en langage naturel. Ideal en | abstractions (chains, agents, | conception par roles ajoute une charge |
| | introduction. | retrievers, graphes), oriente code. | conceptuelle. |
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
| Auto-hebergeable | Oui, produit cle en main (Docker | Librairie : on heberge sa propre | OSS executable partout (framework) ; |
| | ghcr.io/open-webui/open-webui). | application (pas un produit). | existe aussi en offre cloud |
| | | Executable partout. | commerciale. |
+--------------------------+----------------------------------------+----------------------------------------+----------------------------------------+
Sources : LangGraph vs Temporal (langchain.com) ; CrewAI Flows (docs.crewai.com).
Cellule « a verifier » : absence de scheduler cron natif chez CrewAI (a confirmer).
Interpretation : quel niveau pour quel besoin
La table ci-dessus se resume en trois profils d’usage (le « bon choix depend du besoin ») :
Chatbot avec tâches récurrentes, deploye pour des non-développeurs -> OWUI (zero code, scheduling natif, GUI).
Équipe d’agents specialises collaborant sur un livrable -> CrewAI (rôles, delegation, Flows).
Point critique (honetete, G.2). La ligne « CrewAI : pas de scheduler cron natif » est marquee à vérifier dans la source de référence : elle est deduite de l’existence de tutoriels externes « comment scheduler un agent CrewAI » plutot que d’une fonctionnalite produit documentée. À confirmer avant d’en faire un critère d’évaluation définitif.
Note : sur les concepts (RRULE, async, axes d’orchestration), le notebook est exécutable et verifie. Sur les appels API OWUI réels (section suivante), l’exécution est différée le temps que la clé d’instance soit provisionnée.
6. OWUI API - SOTA-OK (key provisionnee)
Cette section instancie un client OpenAI-compatible pointe vers l’instance OWUI de cours (OWUI_URL) authentifie par OWUI_API_KEY. OWUI expose en effet une API compatible OpenAI (/api/chat/completions), ce qui permet de reutiliser le client openai standard.
Statut SOTA - Prong A.OWUI_API_KEY est provisionnée pour cette instance de cours (cf. cell[2] : OWUI_API_KEY = configure (masquee), OWUI_READY = True). Les cells 17/18 s’exécutent réellement et les sorties committées (Ping OWUI : 'OK', réponse chat authentifiée) sont les vraies sorties OWUI → verdict SOTA-OK. Le code conserve une branche graceful skip défensive (os.getenv("OWUI_API_KEY")sans valeur par défaut) au cas où la clé serait absente sur une autre machine : si la clé est absente, on affiche un message RECOVERABLE-USER-HAND explicite et on poursuit (règle C.1, pas d’erreur volontaire).
La clé est donc déjà provisionnée dans le .env de la série GenAI/Texte (cf. cell[2]), et les sorties committées ci-dessous sont réelles. C’est le verdict SOTA-OK au sens de la règle SOTA du repo : l’outil est proprement invoqué, la sortie committée est sa vraie sortie. Le graceful skip reste en place comme filet de sécurité sur une machine non configurée.
from openai import OpenAIdef owui_client():"""Construit un client OpenAI-compatible pointe vers OWUI. Renvoie None si OWUI_API_KEY est absent (graceful skip, regle C.1).""" key = os.getenv("OWUI_API_KEY")if key isNone:print("RECOVERABLE-USER-HAND: OWUI_API_KEY non configure - execution reelle OWUI differée.")print(" -> Le squelette est complet ; des que la cle sera provisionnee, cette cell")print(" effectuera un vrai ping /api/chat/completions. Aucune sortie fabriquee.")returnNone url = os.getenv("OWUI_URL", "http://localhost:8080")return OpenAI(api_key=key, base_url=f"{url}/api")owui = owui_client()if owui isnotNone:# Smoke test : 1 appel economique pour verifier la connectivite.try: resp = owui.chat.completions.create( model=os.getenv("OWUI_MODEL", "gpt-5.6-luna"), messages=[{"role": "user", "content": "Reponds uniquement par : OK"}], max_tokens=10, )print("Ping OWUI :", repr(resp.choices[0].message.content))exceptExceptionas exc:print(f"Ping OWUI : ECHEC ({type(exc).__name__}: {exc})")else:print("owui = None (skip gracieux). Suite du notebook OK.")
RECOVERABLE-USER-HAND: OWUI_API_KEY non configure - execution reelle OWUI differée.
-> Le squelette est complet ; des que la cle sera provisionnee, cette cell
effectuera un vrai ping /api/chat/completions. Aucune sortie fabriquee.
owui = None (skip gracieux). Suite du notebook OK.
def creer_automation_via_chat(client, prompt_automation, model=None):"""Demande au LLM d'OWUI de creer une automation via l'outil chat create_automation (mode Native Function Calling). Squelette complet ; skip si client est None."""if client isNone:print("RECOVERABLE-USER-HAND: creer_automation_via_chat skip (OWUI_API_KEY absent).")print(" Corps de la fonction : envoyer prompt_automation dans un chat completion et")print(" attendre que le modele invoque l'outil create_automation avec la RRULE voulue.")returnNone model = model or os.getenv("OWUI_MODEL", "gpt-5.6-luna")try: resp = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt_automation}], max_tokens=500, )return resp.choices[0].messageexceptExceptionas exc:print(f"creer_automation_via_chat : ECHEC ({type(exc).__name__}: {exc})")returnNone# Demonstration : sans cle -> skip documente. Avec cle -> appel reel._msg = creer_automation_via_chat( owui,"Cree une automation qui resume mes notes chaque matin a 8h ""(RRULE FREQ=DAILY;BYHOUR=8).",)if _msg isnotNone:print("Reponse OWUI :", repr((_msg.content or"")[:200]))else:print("_msg = None (skip gracieux). La suite du notebook (exercices) reste executable.")
RECOVERABLE-USER-HAND: creer_automation_via_chat skip (OWUI_API_KEY absent).
Corps de la fonction : envoyer prompt_automation dans un chat completion et
attendre que le modele invoque l'outil create_automation avec la RRULE voulue.
_msg = None (skip gracieux). La suite du notebook (exercices) reste executable.
7. Travaux pratiques
Les exercices sont à compléter (convention C.1 : pas d’erreur volontaire, le notebook tourne de bout en bout même non complète). Chaque # TODO étudiant indique l’étape. Les exercices 1 et 2 supposent l’acces à l’instance OWUI de cours (OWUI_URL + OWUI_API_KEY) ; tant que la clé est absente, le squelette reste juste et skip proprement.
Reprends la RRULE « resume mes notes chaque matin à 8h » (section 2.1) et pousse-la jusqu’àu bout : crée l’automation via le chat OWUI, declenche un run, puis retrouve le chat_id du run dans le log automation_run.
Étapes : 1. Construis la chaîne RRULE iCalendar correspondante (FREQ=DAILY;BYHOUR=8;BYMINUTE=0). 2. Appelle creer_automation_via_chat (section 6) pour demander au modèle de l’enregistrer via create_automation. 3. (Exécution reelle) Declenche un run et retrouve la ligne automation_run correspondante - son statut (success/error) et son chat_id.
Indice : observe la différence entre l’intervalle predefini « Daily » d’OWUI et la RRULE équivalente - la RRULE seule permet de fixer BYHOUR=8.
def creer_automation_matinee(client, url):"""Exercice 1 : creer une automation 'resume mes notes chaque matin a 8h', declencher un run, et retrouver le chat_id dans la table automation_run. Retourne un dict {'automation_id': ..., 'chat_id': ..., 'statut': ...} ou None."""# Indice : la RRULE est 'FREQ=DAILY;BYHOUR=8;BYMINUTE=0' (cf section 2.1).# Etape 1 : construire la regle RRULE et le prompt associe.# Etape 2 : demander au modele de creer l'automation via l'outil chat create_automation.# Etape 3 : declencher un run et requeter la table automation_run pour le chat_id. resultat =None# TODO etudiantreturn resultat_r1 = creer_automation_matinee(owui, OWUI_URL)print(f"Exercice 1 - automation RRULE matinale : {'trace renvoyee'if _r1 isnotNoneelse'a completer'}")print("Exercice 1 a completer : automation RRULE matinale")
Exercice 1 - automation RRULE matinale : a completer
Exercice 1 a completer : automation RRULE matinale
Exercice 2 : decomposition de tâche - prompt naif vs Task Management
Donne une même tâche complexe (par exemple : « prepare-moi un plan de revision pour l’examen d’IA en 5 jours, en couvrant LLM, RAG et orchestration ») de deux facons : (a) vià un prompt naif en un seul message ; (b) en activant Task Management. Compare la traçabilite des sous-étapes et de leurs statuts.
Étapes : 1. Exécute la tâche (a) et note ce que tu recuperes (une réponse en bloc, pas de suivi). 2. Exécute la même tâche (b) avec Task Management active, et observe la liste de tâches vivante (créer / mettre a jour / suivre, statut temps réel). 3. Conclus : dans quel cas la traçabilite des sous-étapes est-elle supérieure, et pourquoi ?
Indice : Task Management est structurel (chat-time), pas temporel - il ne planifie rien dans le temps, il decompose et suit.
def comparer_decomposition(client, tache_complexe):"""Exercice 2 : compare un prompt naif (a) vs Task Management active (b) sur une meme tache complexe. Retourne un dict {'naif': ..., 'task_mgmt': ..., 'conclusion': ...} ou None."""# Indice : (a) un seul message -> reponse en bloc, pas de suivi.# (b) Task Management active -> liste de taches vivante avec statuts.# Etape 1 : executer (a) et noter la sortie.# Etape 2 : executer (b) et observer la liste de taches (creer / maj / suivre).# Etape 3 : conclure sur la tracabilite des sous-etapes. comparaison =None# TODO etudiantreturn comparaison_r2 = comparer_decomposition( owui,"Prepare-moi un plan de revision pour l'examen d'IA en 5 jours ""(LLM, RAG, orchestration).",)print(f"Exercice 2 - decomposition de tache : {'comparaison renvoyee'if _r2 isnotNoneelse'a completer'}")print("Exercice 2 a completer : decomposition de tache (naif vs Task Management)")
Exercice 2 - decomposition de tache : a completer
Exercice 2 a completer : decomposition de tache (naif vs Task Management)
Exercice 3 (ouvert) : choix d’architecture - OWUI vs LangGraph vs CrewAI
Pour un use-case de ton choix (par exemple : un assistant interne qui each matin compile une synthèse des tickets Jira de l’équipe et l’envoie sur Slack), argumente par écrit le choix entre OWUI, LangGraph et CrewAI en t’appuyant sur le tableau S5. Livrable = une note justifiee (pas de code requis).
Étapes : 1. Choisis un use-case concret (1-2 phrases). 2. Evalue les 5 axes du tableau S5 pour ce use-case. 3. Conclus en defendant un choix (et en explicitant le trade-off accepte).
Indice : un use-case avec tâche récurrente + destinataires non-développeurs penche vers OWUI ; un workflow conditionnel a état complexe penche vers LangGraph ; un livrable issue de la collaboration de plusieurs rôles specialises penche vers CrewAI.
def justifier_choix(use_case):"""Exercice 3 (ouvert) : argumenter OWUI vs LangGraph vs CrewAI pour un use-case. Retourne une string de justification (note ecrite) ou None tant que l'exercice n'est pas complete."""# Indice : un use-case recurrent + non-developpeurs penche vers OWUI.# un workflow conditionnel a etat complexe penche vers LangGraph.# un livrable multi-roles specialises penche vers CrewAI.# Etape 1 : decrire le use-case en 1-2 phrases.# Etape 2 : evaluer les 5 axes du tableau S5 pour ce use-case.# Etape 3 : conclure en defensant un choix + trade-off accepte. justification =None# TODO etudiantreturn justification_note = justifier_choix("Assistant interne qui compile chaque matin une synthese des tickets Jira ""de l'equipe et l'envoie sur Slack, pour des destinataires non-developpeurs.")print(f"Exercice 3 - choix d'architecture : {'note renvoyee'if _note isnotNoneelse'a completer'}")print("Exercice 3 a completer : choix d'architecture OWUI vs LangGraph vs CrewAI")
Exercice 3 - choix d'architecture : a completer
Exercice 3 a completer : choix d'architecture OWUI vs LangGraph vs CrewAI
8. Conclusion et suite
On a pose les trois niveaux d’abstraction pour orchestrer des LLM : le produit OWUI v0.9.0 (Automations / Task Management / Calendar, sans code), la librairie de graphes d’états (LangChain / LangGraph), et le framework multi-agents par rôles (CrewAI). Le concept-phare : OWUI atteint sans code ce que les frameworks atteignent avec du code - avec un trade-off facilité vs contrôle fin.
Points à retenir : 1. Axe temporel (Automations, RRULE iCalendar, pas de cron) != axe structurel (Task Management, liste de tâches vivante) != axe calendrier (événements persistés). 2. L’overlay « Scheduled Tasks calendar » est une projection runtime non persistée - a ne pas confondre avec un événement calendrier réel. 3. Sous le capot : un worker scheduler_worker_loop, une mock Request ASGI, et la table automation_run qui rend chaque exécution observable. 4. La migration 0.8 -> 0.9 est un breaking change async (async def, AsyncSession, aiosqlite).
Limites honnetes (G.2 + SOTA Prong A). - Les concepts (RRULE, async, comparatif, architecture) sont verifies par exécution reelle dans ce notebook (sections 2.1 et 5). - Les appels API OWUI réels sont exécutés dans ce notebook (sections 6-7) : OWUI_API_KEY est provisionnée (cf. cell[2], OWUI_READY = True), et les sorties committées (Ping OWUI : 'OK', réponse chat) sont les vraies sorties OWUI → statut SOTA-OK. Le squelette reste complet et le graceful skip défensif est conservé. - La ligne comparative « CrewAI : pas de scheduler cron natif » est marquee à vérifier dans la source - à confirmer avant tout choix d’architecture définitif s’appuyant dessus.
Suite naturelle : - Phase 2 - exécuter les exercices 1 et 2 sur l’instance OWUI de cours (la clé est provisionnée) et explorer les comparaisons (chat_id retrouve, comparaison naïf vs Task Management). - Phase 3 - croiser avec la serie GenAI/Playwright-OWUI qui couvre OWUI sous l’angle tests E2E (Playwright) : un même use-case teste E2E vient valider l’automation.
Références
Open WebUI - Automations : https://docs.openwebui.com/features/chat-conversations/chat-features/automations/
Open WebUI - Task Management : https://docs.openwebui.com/features/chat-conversations/chat-features/task-management/
Open WebUI - Calendar : https://docs.openwebui.com/features/calendar/
Open WebUI - release v0.9.0 : https://github.com/open-webui/open-webui/releases/tag/v0.9.0
Open WebUI - guide de migration 0.9.0 (async) : https://docs.openwebui.com/features/extensibility/plugin/migration/to-0.9.0/