All guides
Intermediate-AdvancedSelf-pacedFree GuideSection 32/33

Intégration IA avancée

Learn to build and check a web application with development tools and AI assistants. Practise design, coding and validation.

The full lessons for this course are currently available in French. Use the section menu to navigate the French course.

💡

Free content, available without an account. Tools and models change: use the current model guide to check their versions. Models and 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.

Quick Check

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

Learn AI, from prompts to agents

15 free guidesQuizzes, diagrams and workshopsNo account needed

Your progress

Progress is self-reported and stored in your browser. The final completion record is not a qualification or an independent assessment.

View progress and completion record

Learn AI, from prompts to agents

15 free guidesQuizzes, diagrams and workshopsNo account needed

One practical AI workflow in your inbox

Methods, resources and new exercises. Free, unsubscribe at any time.