Tous les guides
Intermédiaire-AvancéÀ votre rythmeGuide gratuitSection 32/33

Intégration IA avancée

Apprenez à construire et vérifier une application web avec des outils de développement et des assistants IA. Pratiquez les étapes de conception, code et validation.

💡

Contenu gratuit, accessible sans compte. Les outils et modèles évoluent : consultez le dossier actuel pour vérifier leurs versions. Modèles et sources

Section 13.7.3 : Intégration IA avancée

🎯 Objectif pédagogique

Intégrer des fonctionnalités IA avancées dans le projet final : streaming de réponses, gestion de contexte conversationnel, monitoring des coûts, et gestion des erreurs IA.


Streaming des réponses IA

Au lieu d'attendre la réponse complète (5-10 secondes), le streaming affiche les tokens au fur et à mesure, comme ChatGPT :

# app/services/streaming_ai_service.rb

class StreamingAiService
  def self.stream_chat(messages, &block)
    OPENAI_CLIENT.chat(
      parameters: {
        model: 'gpt-4',
        messages: messages,
        stream: proc { |chunk, _bytesize|
          delta = chunk.dig('choices', 0, 'delta', 'content')
          block.call(delta) if delta
        }
      }
    )
  end
end
# Avec Action Cable pour le streaming temps réel
# app/channels/chat_stream_channel.rb

class ChatStreamChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat_stream_#{params[:conversation_id]}"
  end

  def ask(data)
    conversation = Conversation.find(params[:conversation_id])
    messages = conversation.messages.map { |m| { role: m.role, content: m.content } }
    messages << { role: 'user', content: data['content'] }

    # Sauvegarder le message utilisateur
    conversation.messages.create!(role: 'user', content: data['content'])

    # Streamer la réponse
    full_response = ''
    StreamingAiService.stream_chat(messages) do |token|
      full_response += token
      ActionCable.server.broadcast(
        "chat_stream_#{conversation.id}",
        { token: token, type: 'stream' }
      )
    end

    # Sauvegarder la réponse complète
    conversation.messages.create!(role: 'assistant', content: full_response)

    ActionCable.server.broadcast(
      "chat_stream_#{conversation.id}",
      { type: 'complete' }
    )
  end
end

Gestion du contexte conversationnel

# app/services/conversation_manager.rb

class ConversationManager
  MAX_CONTEXT_TOKENS = 4000

  def self.build_messages(conversation)
    messages = []

    # System message
    messages << {
      role: 'system',
      content: system_prompt(conversation)
    }

    # Contexte RAG si pertinent
    if conversation.uses_rag?
      context = RagService.get_context(conversation.last_user_message)
      messages << {
        role: 'system',
        content: "Contexte documentaire :\n#{context}"
      }
    end

    # Historique (limité pour respecter la fenêtre de contexte)
    recent_messages = conversation.messages
      .order(created_at: :desc)
      .limit(20)
      .reverse

    recent_messages.each do |msg|
      messages << { role: msg.role, content: msg.content }
    end

    # Tronquer si nécessaire
    truncate_to_token_limit(messages)
  end

  private

  def self.truncate_to_token_limit(messages)
    # Garder le system message et les derniers messages
    while estimate_tokens(messages) > MAX_CONTEXT_TOKENS && messages.length > 2
      messages.delete_at(1)  # Supprimer les plus anciens (garder system)
    end
    messages
  end

  def self.estimate_tokens(messages)
    messages.sum { |m| m[:content].length / 4 }  # Approximation
  end
end

Monitoring des coûts

# app/models/ai_usage.rb

class AiUsage < ApplicationRecord
  belongs_to :user

  # Tracker chaque appel API
  def self.track(user:, model:, input_tokens:, output_tokens:)
    costs = {
      'gpt-4' => { input: 0.03, output: 0.06 },         # par 1K tokens
      'gpt-3.5-turbo' => { input: 0.0015, output: 0.002 },
      'text-embedding-3-small' => { input: 0.00002, output: 0 }
    }

    model_cost = costs[model]
    total_cost = (input_tokens / 1000.0 * model_cost[:input]) +
                 (output_tokens / 1000.0 * model_cost[:output])

    create!(
      user: user,
      model: model,
      input_tokens: input_tokens,
      output_tokens: output_tokens,
      cost_usd: total_cost
    )
  end

  # Limites par utilisateur
  def self.monthly_cost(user)
    where(user: user)
      .where('created_at >= ?', Time.current.beginning_of_month)
      .sum(:cost_usd)
  end
end

Gestion des erreurs IA

# app/services/resilient_ai_service.rb

class ResilientAiService
  MAX_RETRIES = 3
  RETRY_DELAY = 1 # seconde

  def self.chat(prompt, **options)
    retries = 0
    begin
      result = AiService.chat(prompt, **options)
      raise 'Réponse vide' if result.blank?
      result
    rescue Faraday::TooManyRequestsError => e
      # Rate limiting — attendre et réessayer
      retries += 1
      if retries <= MAX_RETRIES
        sleep(RETRY_DELAY * retries)
        retry
      end
      fallback_response('Service temporairement surchargé.')
    rescue Faraday::ServerError => e
      # Erreur serveur OpenAI
      Rails.logger.error("OpenAI server error: #{e.message}")
      fallback_response('Le service IA est temporairement indisponible.')
    rescue JSON::ParserError => e
      Rails.logger.error("Invalid JSON from OpenAI: #{e.message}")
      fallback_response('Erreur de format dans la réponse IA.')
    end
  end

  private

  def self.fallback_response(message)
    "Désolé, #{message} Veuillez réessayer dans quelques instants."
  end
end

Coûts IA en production

Surveillez vos coûts OpenAI dès le début. Un utilisateur qui abuse du chat peut facilement générer 10$/jour en tokens GPT-4. Implémentez des limites quotidiennes/mensuelles par utilisateur et utilisez GPT-3.5-turbo pour les tâches simples (classification, extraction) et GPT-4 uniquement quand la qualité est critique.

Vérification rapide

Pourquoi est-il important de streamer les réponses IA au lieu d'attendre la réponse complète ?

Apprendre l’IA, du premier prompt aux agents

15 guides gratuitsQuiz, schémas et ateliersSans compte

Votre progression

Cette progression est déclarative et conservée dans votre navigateur. L’attestation finale ne constitue pas un diplôme ni une évaluation indépendante.

Voir ma progression et mon attestation

Apprendre l’IA, du premier prompt aux agents

15 guides gratuitsQuiz, schémas et ateliersSans compte

Un cas pratique IA dans votre boîte mail

Méthodes, ressources et nouveaux exercices. Gratuit, avec désinscription à tout moment.