Isi-APP Docs fonctionnelles
Toutes les docs
Markdown brut
Isi-IA — Assistance IA sur les Tickets
Actif tickets functional Revu le 2026-04-17 tickets/ticket-ai.md

Isi-IA — Assistance IA sur les Tickets

Document à destination des admins DSI, chefs de projet, et développeurs.


PARTIE 1 — Guide Fonctionnel

1.1 Présentation

Isi-IA est le module d'assistance intelligente intégré au module Tickets. Il permet à un technicien de dialoguer en temps réel avec un agent IA pendant le traitement d'un ticket.

L'agent IA peut :

  • Analyser le contenu du ticket et proposer des pistes de résolution
  • Répondre aux questions du technicien dans le contexte du ticket
  • Suggérer de clôturer le ticket (si le problème est résolu)
  • Suggérer d'escalader le ticket au niveau supérieur (si le problème dépasse le périmètre)

L'assistance IA est optionnelle et configurable par sujet de ticket.


1.2 Configuration par sujet de ticket

L'activation d'Isi-IA se fait au niveau du sujet de ticket (Listdata, référentiel 810098).

Champ blhelpai

Valeur Comportement
off (ou absent) Isi-IA masqué — aucun panneau affiché
on Isi-IA disponible — le technicien décide manuellement d'activer l'IA
auto Isi-IA automatique — la conversation démarre dès la création du ticket

Champ idassistant (optionnel)

Permet d'associer un agent IA spécifique à ce sujet de ticket. Si non renseigné, l'agent de fallback ticket-support-it est utilisé.

L'agent IA peut être configuré avec :

  • Un prompt système personnalisé (contexte métier, instructions spécifiques)
  • Un champ ai_context sur le sujet pour injecter des informations contextuelles supplémentaires (procédures internes, liens vers la documentation, etc.)
  • Un modèle IA spécifique (ex : Mistral Large, GPT-4o)

1.3 Comportement en situation réelle

Mode manuel (blhelpai = on)

  1. Le technicien ouvre un ticket
  2. Le panneau "Isi-IA" est visible avec un bouton Activer Isi-IA
  3. En cliquant, la conversation démarre : le contenu du ticket est envoyé automatiquement à l'agent IA
  4. L'IA répond dans les secondes qui suivent (indicateur de chargement animé pendant la génération)
  5. Le technicien peut envoyer des messages supplémentaires (max 2 000 caractères, 50 messages/minute)

Mode automatique (blhelpai = auto)

  1. Dès qu'un ticket est créé sur ce sujet, TicketV4Helper::checkAndCreateAiAnswer() crée automatiquement la conversation et dispatch le job IA
  2. Le technicien ouvre le ticket et voit déjà la réponse de l'IA (ou l'indicateur de chargement si la réponse n'est pas encore prête)

Actions suggérées par l'IA

Après chaque réponse, l'IA peut inclure une suggestion d'action dans son message. Le panneau affiche alors un bouton de confirmation :

Suggestion IA Affichage Action si confirmée
Clôturer Bandeau vert + bouton "Confirmer la clôture" Ticket fermé (tpstate=C, tpresolve=R, tpclose=ai), redirection
Escalader Bandeau jaune + bouton "Confirmer l'escalade" nblevel incrémenté, redirection

La suggestion peut être ignorée en cliquant Continuer la discussion.


1.4 Bonnes pratiques

  • Prompts système : rédiger en français, indiquer le rôle de l'agent ("Tu es un agent de support IT pour l'entreprise X"), le périmètre du sujet, et les instructions de clôture/escalade
  • Contexte spécifique (ai_context) : insérer les procédures internes, les codes d'erreur connus, les contacts à escalader
  • Modèle : Mistral Large pour les sujets complexes nécessitant un raisonnement approfondi, Mistral Small pour les sujets simples (plus rapide, moins coûteux)
  • Escalade : bien définir dans le prompt quand l'IA doit proposer une escalade (ex : "Si le problème concerne le réseau physique, demande une escalade")
  • Ne pas activer auto sur des sujets à volume très élevé sans avoir d'abord testé en mode on

PARTIE 2 — Documentation Technique

2.1 Architecture

[Ticket créé / Technicien clique "Activer Isi-IA"]
        │
        ▼
TicketAiChat (Livewire)
  └── enableAi() / sendMessage()
        │  créé AiConversation + AiMessage(user)
        │  dispatch(ProcessTicketAiResponseJob)
        ▼
ProcessTicketAiResponseJob (Queue)
  ├── Résout l'assistant (avec provider + model)
  ├── Construit l'historique (20 derniers messages)
  ├── Construit le system prompt
  ├── Appelle AiManager->provider()->billable(false)->chat()
  ├── Détecte les tags <<TICKET:CLOSE>> / <<TICKET:ESCALATE>>
  └── Sauvegarde AiMessage(assistant) avec jsmetadata
        │
        ▼
TicketAiChat::refreshConversation() (wire:poll.3s quand $isLoading)
  └── syncLoadingState() → désactive le loader quand la réponse arrive

2.2 Composant Livewire — TicketAiChat

Fichier : app/Livewire/Ticketv4/TicketAiChat.php Vue : resources/views/livewire/ticketv4/ticket-ai-chat.blade.php Balise : <livewire:ticketv4.ticket-ai-chat :idask="$ticket->idask"/>

Propriétés

Propriété Type Description
$idask int ID du ticket
$ticket Ticket Instance du ticket
$listdata Listdata\|null Sujet de ticket (config IA)
$conversation AiConversation\|null Conversation en cours
$message string Saisie de l'utilisateur
$isLoading bool Indicateur de génération en cours

Méthodes publiques

Méthode Description
mount(int $idask) Charge ticket + listdata + conversation existante
enableAi() Crée la conversation et dispatch le premier job IA
sendMessage() Valide, rate-limite, sauvegarde le message, dispatch le job
refreshConversation() Polling : détecte si la réponse est arrivée
closeTicket() Clôture le ticket avec tpclose=ai, redirige
escalateTicket() Incrémente nblevel, redirige

État de chargement (syncLoadingState)

$isUserMessage = $lastMessage && $lastMessage->tprole === AiMessage::ROLE_USER;
$isTimedOut = $lastMessage && $lastMessage->created_at->diffInMinutes(now()) > 5;
$this->isLoading = $isUserMessage && !$isTimedOut;

Si le job IA ne répond pas en 5 minutes, le loader est masqué automatiquement pour ne pas bloquer l'interface.

Rate limiter

$rateLimitKey = 'ai-message:{userId}:{assistantId}';
// 50 tentatives par fenêtre de 60 secondes

2.3 Job — ProcessTicketAiResponseJob

Fichier : app/Jobs/Ticket/ProcessTicketAiResponseJob.php Queue : synchrone ou default selon la config Timeout : 180 secondes | Tries : 2

public function __construct(
    public AiConversation $conversation,
    public Ticket $ticket,
    public Listdata $listdata,
) {}

Flux d'exécution

  1. Charge l'assistant via $conversation->assistant()->with(['provider', 'model'])->first()
  2. Si pas d'assistant ou pas de provider → retour silencieux
  3. Construit l'historique (20 derniers messages, ordre chronologique)
  4. Construit le system prompt (txsystemprompt + ai_context du listdata)
  5. Appelle AiManager :
    $ai->provider($assistant->provider->lbslug)
        ->billable(false)
        ->model($assistant->model->lbmodel)   // si model défini
        ->system($systemPrompt)               // si non vide
        ->temperature($config['temperature']) // si configuré
        ->maxTokens($config['max_tokens'])    // si configuré
        ->chat($history)
  6. Détecte les tags dans la réponse (voir §2.6)
  7. Sauvegarde AiMessage(ROLE_ASSISTANT) avec jsmetadata

Note sur billable(false)

L'appel est marqué billable(false) : la consommation de tokens est financée par ISI et non imputée au tenant. Cela utilise la credential "par défaut" du provider (configurée manuellement par l'admin ISI via /admin/ai/credentials). La méthode ->systemCredential() a été abandonnée (provoquait des erreurs 401 en l'absence de clés .env).


2.4 Mode automatique (blhelpai = auto)

Déclenché dans TicketV4Helper::checkAndCreateAiAnswer() lors de la création d'un ticket.

Fichier : app/Helpers/TicketV4Helper.php

// Flux simplifié
$assistant = $this->resolveAssistantForTicket($ticket, $listdata);
if ($assistant) {
    $conversation = AiConversation::create([...]);
    AiMessage::create(['tprole' => ROLE_USER, 'txcontent' => titre + contenu]);
    dispatch(new ProcessTicketAiResponseJob($conversation, $ticket, $listdata));
} else {
    // Fallback legacy : GenerateAiTicketResponseJob
    dispatch(new GenerateAiTicketResponseJob($ticket));
}

Le fallback GenerateAiTicketResponseJob est conservé pour les tickets dont le sujet n'a pas d'assistant IA configuré (compatibilité ascendante).


2.5 Résolution de l'assistant (resolveAssistant)

protected function resolveAssistant(): ?AiAssistant
{
    // 1. Assistant spécifique du sujet
    if ($this->listdata?->idassistant) {
        $assistant = AiAssistant::find($this->listdata->idassistant);
        if ($assistant && $assistant->blactive) {
            return $assistant;
        }
    }
    // 2. Fallback global
    return AiAssistant::ticketSupportIT();
}

AiAssistant::ticketSupportIT() retourne l'agent avec lbslug = 'ticket-support-it' et _id IS NULL (agent global, non rattaché à un tenant).


2.6 Convention de clôture/escalade dans le prompt

L'agent IA peut déclencher des actions en incluant un tag spécial en fin de réponse :

Tag Action
<<TICKET:CLOSE>> Suggère la clôture du ticket
<<TICKET:ESCALATE>> Suggère l'escalade

Détection (insensible à la casse) :

if (preg_match('/<<TICKET:(CLOSE|ESCALATE)>>/i', $content, $matches)) {
    $ticketAction = strtolower($matches[1]); // 'close' ou 'escalate'
    $content = trim(preg_replace('/<<TICKET:(CLOSE|ESCALATE)>>/i', '', $content));
}

Le tag est supprimé du contenu avant sauvegarde. L'action est stockée dans jsmetadata['ticket_action'] du AiMessage.

Exemple de prompt système recommandé :

Tu es un agent de support IT de niveau 1. Aide le technicien à résoudre les problèmes courants.

Si la solution proposée a été appliquée et confirmée, termine ton message par <<TICKET:CLOSE>>.
Si le problème dépasse le niveau 1 (réseau physique, serveur, AD), termine ton message par <<TICKET:ESCALATE>>.

2.7 Construction du prompt système

protected function buildSystemPrompt(AiAssistant $assistant): string
{
    $parts = [];

    if ($assistant->txsystemprompt) {
        $parts[] = $assistant->txsystemprompt;   // Prompt de l'assistant
    }
    if ($this->listdata->ai_context) {
        $parts[] = 'Contexte spécifique : ' . $this->listdata->ai_context; // Contexte du sujet
    }

    return implode("\n\n", $parts);
}

2.8 Modèles et tables

Table ai_conversations

Colonne Type Description
id bigint PK
_id string Tenant ID
idassistant bigint FK → ai_assistants
user_id bigint Utilisateur ayant démarré la conversation
lbtitle string Titre (ex: "Ticket #42 — Problème VPN")
model_type string Classe du modèle lié (ex: App\Models\Ticket)
model_id bigint ID du modèle lié (ex: idask du ticket)
created_at timestamp

Relation polymorphique :

// AiConversation
public function conversable(): MorphTo
{
    return $this->morphTo('model');
}

// Ticket
public function aiConversations(): MorphMany
{
    return $this->morphMany(AiConversation::class, 'model', 'model_type', 'model_id', 'idask');
}

Note : La clé locale idask est explicite car le modèle Ticket n'utilise pas id comme PK.

Table ai_messages

Colonne Type Description
id bigint PK
idconversation bigint FK → ai_conversations
tprole string user ou assistant
txcontent text Contenu du message
jsmetadata json Métadonnées (tokens, model, ticket_action…)
created_at timestamp

Constantes :

AiMessage::ROLE_USER      // 'user'
AiMessage::ROLE_ASSISTANT // 'assistant'

Structure jsmetadata (messages assistant) :

{
    "provider": "mistral",
    "model": "mistral-large-latest",
    "prompt_tokens": 450,
    "completion_tokens": 120,
    "total_tokens": 570,
    "ticket_action": "close"   // null si aucune action suggérée
}

Table _listdata (sujets de tickets)

Colonne Type Description
blhelpai string off / on / auto
idassistant bigint|null FK → ai_assistants (assistant spécifique)
ai_context text|null Contexte injecté dans le system prompt

2.9 Référence des fichiers

Rôle Fichier
Composant Livewire app/Livewire/Ticketv4/TicketAiChat.php
Vue Blade resources/views/livewire/ticketv4/ticket-ai-chat.blade.php
Job IA async app/Jobs/Ticket/ProcessTicketAiResponseJob.php
Helper tickets (mode auto) app/Helpers/TicketV4Helper.php
Modèle conversation app/Models/Ai/AiConversation.php
Modèle message app/Models/Ai/AiMessage.php
Modèle assistant app/Models/Ai/AiAssistant.php
Service IA app/Services/Ai/AiManager.php
Seeder agents tickets database/seeders/AiTicketAssistantSeeder.php
Seeder providers database/seeders/AiSystemCredentialSeeder.php
Migration polymorphique database/migrations/2026_04_10_190451_add_model_type_model_id_to_ai_conversations.php
Migration listdata database/migrations/..._add_idassistant_ai_context_to_listdata.php
Page ticket (intégration) resources/views/ticketv4/show.blade.php
Tests tests/Feature/TicketAiChatTest.php