> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Du texte brut à l'interface dynamique
- URL: https://www.sfeir.dev/ia/du-texte-brut-a-linterface-dynamique/
- Published: 2026-07-21T09:23:51.000Z
- Updated: 2026-07-21T09:23:51.000Z
- Description: Transformez vos agents conversationnels en interfaces interactives ! Découvrez comment LangChain DeepAgents et un middleware intelligent permettent de générer formulaires, boutons et cartes à la volée pour une expérience utilisateur optimale.
- Author: Abdeladel MAJDEB
- Tags: agents IA, IA, langchain, Angular, deepagents, middleware

*Les agents conversationnels se limitent souvent à des échanges textuels fastidieux. Et si votre agent pouvait générer des formulaires, boutons et cartes interactives à la volée ? Découvrez comment nous avons transformé l'expérience utilisateur de notre agent grâce à* [*LangChain DeepAgents.*](https://www.sfeir.dev/au-dela-du-prompt-developper-des-applications-autonomes-avec-deep-agents-de-langchain/)

### Introduction

Imaginons un cas concret. Un utilisateur veut créer un ticket dans Jira via un chatbot. Sans [Skills](https://www.sfeir.dev/ia/les-skills-donner-un-vrai-metier-a-votre-agent-ia/) ni structure, voici ce qui se passe réellement :

```markdown
Utilisateur : @jira je veux créer un ticket
Agent       : Bonjour, oui bien sûr ! Veuillez renseigner les informations suivantes :
              * Résumé : de quoi parle le ticket
              * Description : description détaillée du ticket
              * Priorité : quelle priorité

```

**Le problème ?** Cette réponse est incomplète et incorrecte :

1. **L'agent n'a pas le `project_key`** à utiliser – impossible de créer le ticket sans cette information, à moins que cette information ait été fournie dans la demande initiale (**`@jira je veux créer un ticket dans le projet X`**),
2. **Des champs obligatoires manquent** : environnement, assigné, labels...
3. **L'agent présume qu'il faut créer une User Story** sans demander le type de ticket
4. **Les champs requis varient selon le type** : un Bug nécessite un environnement, pas une Story

Résultat : des tickets impossibles à créer, des erreurs API, et une frustration côté utilisateur.

[DeepAgents : comment LangChain révolutionne les agents IA avec le Context EngineeringVos agents LangChain perdent le fil après cinq étapes ? Harrison Chase a analysé Claude Code pour comprendre pourquoi certains agents réussissent. La réponse : l’architecture, pas le modèle. Voici DeepAgents, le passage du Function Engineering au Context Engineering.![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/icon/plume-point-909253be-01c4-41f3-826d-621c6f81623c.png)sfeir.dev - Le média incontournable pour les passionnés de tech et d'intelligence artificielleFedi CHERIF![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/thumbnail/Gemini_Generated_Image_bnqklmbnqklmbnqk-1-7f63041c-c2df-42ba-98ee-9ba49e205180.png)](https://www.sfeir.dev/deepagents-comment-langchain-revolutionne-les-agents-ia-avec-le-context-engineering/)

Suite à la migration vers le framework DeepAgents, pilotée par [Fedi CHERIF](https://www.sfeir.dev/author/fedi/), il est désormais possible d'introduire des Skills pour structurer et affiner le comportement de nos agents. C'est cette brique qui a ouvert la porte à une refonte de l'expérience utilisateur.

### L'architecture existante

Avant cette évolution, l'agent Jira fonctionnait dans un pattern **`agent-as-tool`** au sein de [LangGraph](https://www.sfeir.dev/ia/kesaco-langgraph/), mais sans guidage structuré ni transformation UI :

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/07/image-6-1.png)

L'agent répondait en texte brut, sans validation côté client ni structuration des échanges. La qualité dépendait entièrement du LLM.

---

## La solution : Skills + middleware GenerateUI

En m'appuyant sur DeepAgents, j'ai introduit trois briques complémentaires :

1. Des **Skills** en Markdown pour structurer le comportement de l'agent
2. Un **middleware GenerateUI** qui transforme automatiquement les réponses texte en composants d'interface en format JSON
3. Un **moteur de rendu dynamique** côté Angular capable d'afficher des composants UI

Avec ces ajouts, le même flux devient structuré et complet :

```markdown
Utilisateur : @jira je veux créer un ticket
Agent       : Dans quel projet ? (texte libre)
```

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/07/Capture-d-e--cran-2026-04-30-132017-1-2.png)

```markdown
Utilisateur : Nom du projet: X
Agent       : Quel type ? → [Bug] [Story] [Task] [Business Epic]  (boutons)
```

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/07/Capture-d-e--cran-2026-04-30-132549-2-1-1-1-1.png)

```markdown
Utilisateur : clique sur [Bug]
Agent       : Formulaire → résumé*, environnement*, description, priorité, assigné, labels
```

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/07/Capture-d-e--cran-2026-04-30-131548-1-1-2-1.png)

```markdown
Utilisateur : remplit et soumet
Agent       : ✅ Ticket X-123 créé 
```

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/07/image-7-1.png)

**Les bénéfices :**

- **Découverte progressive** : **`project_key`** puis type de ticket
- **Validation du type** : l'agent récupère les types disponibles via l'API Jira
- **Champs adaptés au type** : un Bug demande l'environnement, pas une Story
- **Validation côté client** : aucun champ obligatoire oublié
- **Interface claire** : formulaire complet en une seule fois

Voici l'architecture résultante :

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/07/image-5-1.png)

Les trois sections suivantes détaillent chacune de ces briques.

## Skills — guider l'agent avec des instructions structurées

DeepAgents permet de charger des Skills via un simple paramètre :

```python
from deepagents import create_deep_agent
agent = create_deep_agent(
    ...
    skills=["skills/"],  # Charge tous les .md du dossier
    backend=FilesystemBackend(
                root_dir=str(Path(__file__).parent), virtual_mode=True
            ),
)
```

Chaque fichier Markdown du dossier skills/ devient une instruction structurée, activée automatiquement quand sa description correspond à l'intention de l'utilisateur.

### Anatomie du Skill create-issue

Voici le Skill le plus complexe — la création de ticket Jira — dans son intégralité structurelle :

```markdown
---
name: create-issue
description: Use these instructions whenever the user wants to create,
  add, or open a new Jira issue or ticket — even if no details have been
  provided yet. Covers discovery, type validation, form collection,
  and creation. Do NOT collect information yourself — follow these steps.
---

## STEP 1 — Discovery

### 1a — Project key
- Si `project_key` inconnu → demander, puis STOP.

### 1b — Issue type
- Appeler `jira_get_issue_types(project_key)` pour récupérer les types.
- Si `issue_type` inconnu → lister les types et demander, puis STOP.

## STEP 2 — Type Validation
- Normaliser et vérifier que le type choisi existe dans la liste.

## STEP 3 — Collect Form Data
- **Bug** → champs requis : résumé + environnement.
  Champs optionnels : description, priorité, assigné, labels.
- **Autre type** → champ requis : résumé uniquement.
- Présenter TOUS les champs en un seul message. STOP.

## STEP 4 — Create the Issue
- Appeler `jira_create_issue` avec tous les champs collectés.
- Afficher le résultat : clé, résumé, statut, lien.

## STEP 5 — Post-Creation
- Proposer la suite : "Créer un autre ticket ?"

## Rules
- Ne JAMAIS deviner project_key ou issue_type.
- Ne JAMAIS sauter la discovery.
- `environment` requis pour Bug uniquement.
- Présenter tous les champs en une seule fois (pas un par un).

```

Les points STOP sont la clé. Sans eux, le LLM a tendance à "anticiper" la réponse de l'utilisateur et à enchaîner les étapes sans attendre. Avec les STOP, chaque étape de découverte devient un vrai point de synchronisation homme-machine.

Le Skill garantit aussi que le formulaire présente **tous les champs en une seule fois** — requis et optionnels. C'est cette réponse-formulaire que le middleware GenerateUI va ensuite transformer en composant form interactif.

## Le middleware GenerateUI : du texte au JSON

### Rôle et déclenchement

Le middleware GenerateUiMiddleware est le pont entre le monde textuel des agents et le monde graphique du frontend. Il s'insère dans le pipeline via le paramètre middleware de create\_deep\_agent :

```python
from deepagents import create_deep_agent
orchestrator = create_deep_agent(
    ...
    middleware=[GenerateUiMiddleware()],
)
```

Il implémente la méthode **`aafter_agent`** : un hook qui s'exécute **après** chaque tour d'agent.

```python
from langchain.agent.middleware import AgentMiddleware

CLASSIFY_SYSTEM = (
    "You receive the final message that an agent is about to show to a user\n",
    "Decide which UI component best wraps it and extract the required parameters \n\n",
    "Decision Table:\n"
    ...
)

Class UIDecision(BaseModel):
  kind: Literal["form", "buttons", "card", ...] = Field(...)
  fields: list[FormField] | None = Field(...)
  ...

class GenerateUiMiddleware(AgentMiddleware):
    async def aafter_agent():
        llm = define_llm(...).with_structured_output(UIDecision)
        decision = await llm.ainvoke([CLASSIFY_SYSTEM, jira_agent_respose])
```

Le middleware extrait le texte de la réponse IA, puis le soumet au LLM avec un prompt de classification. Le with\_structured\_output force le LLM à retourner un objet conforme au schéma Pydantic **UIDecision**.

### La table de décision

Le prompt contient des règles explicites pour choisir le bon composant :

| Situation détectée dans le message            | Composant  | kind    |
| --------------------------------------------- | ---------- | ------- |
| Demande de remplir des valeurs                | Formulaire | form    |
| ≤6 choix fixes proposés                       | Boutons    | buttons |
| Un seul résultat structuré (ticket, document) | Carte      | card    |
| Confirmation simple ou question libre         | Aucun      | ""      |

### Le schéma UIDecision

Le modèle Pydantic UIDecision porte la décision. Son champ kind détermine quel type de composant sera rendu (form, buttons ou vide). Chaque kind active un sous-ensemble de champs (fields, options, title/content, cards) — le LLM n'a qu'à remplir les bons, le schéma Pydantic s'occupe de la validation.

### Exemples de JSON générés

**Formulaire** — quand l'agent demande les champs pour créer un bug :

```json
{
    "kind": "form",
    "payload": {
        "fields": [
            {"id": "summary", "label": "Résumé", "type": "text", "required": true,
             "placeholder": "Titre court du problème"},
            {"id": "description", "label": "Description", "type": "textarea",
             "required": false},
            {"id": "priority", "label": "Priorité", "type": "select",
             "required": false, "options": ["High", "Medium", "Low"]},
            {"id": "assignee", "label": "Assigné", "type": "text", "required": false},
            {"id": "labels", "label": "Labels", "type": "text", "required": false,
             "placeholder": "label1, label2"}
        ],
        "submitLabel": "Créer le ticket"
    }
}
```

## Frontend Angular : rendu dynamique

### Le composant orchestrateur : DynamicUiComponent

Côté Angular, le contrat JSON est reflété par une **union discriminée** TypeScript sur le champ kind. Un unique composant DynamicUiComponent fait le dispatch vers le bon sous-composant via un @switch :

```html
@switch (uiComponent.kind) {
    @case ('buttons') {
        <app-ui-buttons [config]="uiComponent.payload" [disabled]="disabled" (action)="onAction($event)" />
    }
    @case ('form') {
        <app-ui-form [config]="uiComponent.payload" [disabled]="disabled" (action)="onAction($event)" />
    }
    @case ('card') {
        <app-ui-card [config]="uiComponent.payload" />
    }
}
```

### Les sous-composants

Chaque sous-composant est un **standalone component** Angular qui reçoit son payload typé :

- **UiButtonsComponent** : rend des boutons PrimeNG en flexbox. Au clic, émet la value du bouton.
- **UiFormComponent** : construit dynamiquement un FormGroup (Reactive Forms) à partir du tableau fields. Gère la validation (required, email), les types de champs (text, select, textarea, checkbox) et formate la soumission en texte Markdown.
- **UiCardComponent** : affiche un PrimeNG Card avec un titre, du contenu Markdown (via ngx-markdown) et une image optionnelle.

### Gestion des actions utilisateur

Quand l'utilisateur interagit avec un composant UI (clic bouton, soumission formulaire), l'action est renvoyée comme un message humain à l'agent. Par exemple, le formulaire formate ses valeurs :

```text
Résumé: Bug login
Environnement: Production
Priorité: High
```

L'agent reçoit ce texte et poursuit son workflow — le Skill passe au STEP suivant.

## Retour d'expérience et perspectives

### Résultats observés

**Découverte structurée et fiable** : Le workflow en étapes (project → type → champs adaptés) élimine les erreurs de création. L'agent ne devine plus, il découvre méthodiquement les informations nécessaires.

**Champs complets et contextuels** : Grâce aux Skills, l'agent présente tous les champs requis et optionnels en fonction du type de ticket. Fini les tickets incomplets ou les champs manquants.

**Validation côté client** : Le formulaire Angular valide les champs obligatoires avant soumission. Aucune erreur API silencieuse, aucun ticket créé avec des données incomplètes.

**Gain de temps mesurable** : La création d'un ticket passe d'une interaction incertaine et souvent incomplète à un processus guidé en 4 étapes claires, avec validation à chaque niveau.

### Améliorations futures

- **Autres agents spécialisés** : Confluence, SharePoint et Teams sont les prochains candidats.
- **Nouveaux composants UI** : modales de confirmation, steppers multi-étapes, composants de visualisation de données.
- **Enrichissement des Skills** : ajout de validations métier (ex: vérifier que l'assigné existe dans Jira avant soumission), suggestions intelligentes basées sur l'historique.

## Conclusion : une approche généralisable

Le pattern **Skills + middleware UI** n'est pas spécifique à Jira. Tout agent qui manipule des données structurées peut en bénéficier : un agent RH qui collecte des demandes de congés, un agent IT qui gère des incidents, un agent commercial qui qualifie des leads, ou encore un agent support qui crée des tickets d'assistance.

**La recette reste la même :**

1. **Écrire des Skills** pour structurer le workflow de l'agent et guider sa découverte
2. **Laisser le middleware** transformer automatiquement les réponses en composants UI
3. **Ajouter les composants Angular** nécessaires (si les 5 existants ne suffisent pas)

**L'élégance de cette architecture ?** Aucun agent n'est modifié. Aucun prompt n'est pollué par du JSON UI. La séparation des responsabilités est totale : les agents pensent en langage naturel, le middleware traduit en interface, Angular affiche.

Cette transformation prouve qu'on peut **allier la puissance des LLM et l'ergonomie des interfaces modernes** sans compromis. Les agents conversationnels ne sont plus condamnés au texte brut – ils peuvent désormais offrir une expérience visuelle riche, guidée et fiable.

[Au-delà du Prompt : développer des applications autonomes avec Deep Agents de LangChainDe Paris à Bruxelles, les entreprises cherchent à dépasser le stade des simples chatbots. En direct de nos practices IA régionales, voici comment le framework Deep Agents de LangChain transforme radicalement l’industrialisation des agents autonomes et la gestion des tâches complexes au long cours.![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/icon/plume-point-132b4f9f-f7ea-4e0a-a3b3-df4141ba44c4.png)sfeir.dev - Le média incontournable pour les passionnés de tech et d'intelligence artificielleCharlotte Ryssen (Coumont)![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/thumbnail/Gemini_Generated_Image_sajvkksajvkksajv-1-82181372-f658-4b35-8761-69c2b7f5d991.png)](https://www.sfeir.dev/au-dela-du-prompt-developper-des-applications-autonomes-avec-deep-agents-de-langchain/)