Construire des Agents IA qui Utilisent Réellement vos Modèles Excel

D'Excel à Agent IA en 30 Minutes

Votre feuille de calcul Excel a évolué pendant des années. Elle gère les cas particuliers, implémente des règles métier complexes et incarne une connaissance approfondie du domaine. Maintenant, vous voulez qu'un agent IA l'utilise.

La plupart des tutoriels vous diront de "simplement exporter en CSV" ou "reconstruire en Python." Nous allons faire quelque chose de mieux : donner à votre agent IA un accès direct au moteur de calcul d'Excel.

Ce que Nous Construisons

Un agent IA de service client qui peut :

  • Calculer des devis précis en utilisant votre Excel de tarification
  • Vérifier les dates de livraison avec votre modèle logistique
  • Appliquer des remises basées sur des règles métier complexes
  • Gérer les cas particuliers exactement comme votre équipe le fait

Tout en utilisant vos fichiers Excel existants. Aucune reconstruction requise.

Prérequis

# Vous aurez besoin de :
npm install langchain @langchain/openai
# ou
pip install langchain openai

# Et un compte SpreadAPI (le niveau gratuit fonctionne)
# Inscrivez-vous sur https://spreadapi.io

Étape 1 : Préparer votre Excel pour l'IA

Votre Structure Excel

PricingModel.xlsx
├── Entrées
│   ├── B2: Code Produit
│   ├── B3: Quantité
│   ├── B4: Type de Client
│   └── B5: Région
├── Calculs (Cachés de l'IA)
│   ├── Formules VLOOKUP complexes
│   ├── Matrices de remises
│   └── Règles métier
└── Sorties
    ├── E10: Prix de Base
    ├── E11: Montant de Remise
    ├── E12: Prix Final
    └── E13: Date de Livraison

Téléchargement vers SpreadAPI

  1. Se connecter au Tableau de Bord SpreadAPI
  2. Créer un nouveau service appelé "pricing-model"
  3. Télécharger votre Excel
  4. Définir l'interface :
{
  "inputs": {
    "productCode": "B2",
    "quantity": "B3",
    "customerType": "B4",
    "region": "B5"
  },
  "outputs": {
    "basePrice": "E10",
    "discount": "E11",
    "finalPrice": "E12",
    "deliveryDate": "E13"
  }
}

Étape 2 : Créer l'Agent IA

Agent de Base avec Function Calling

import { ChatOpenAI } from '@langchain/openai';
import { SpreadAPITool } from './spreadapi-tool';

// Définir l'outil de calcul Excel
const pricingTool = {
  name: "calculate_pricing",
  description: "Calculer des prix précis en utilisant le modèle de tarification de l'entreprise. Utilisez ceci chaque fois que vous devez proposer des prix ou vérifier des remises.",
  parameters: {
    type: "object",
    properties: {
      productCode: {
        type: "string",
        description: "Code produit (ex. 'PRO-001')"
      },
      quantity: {
        type: "number",
        description: "Nombre d'unités"
      },
      customerType: {
        type: "string",
        enum: ["standard", "premium", "enterprise"],
        description: "Type de compte client"
      },
      region: {
        type: "string",
        enum: ["US", "EU", "APAC"],
        description: "Région du client"
      }
    },
    required: ["productCode", "quantity", "customerType", "region"]
  },
  execute: async (params) => {
    // Appeler SpreadAPI
    const response = await fetch('https://api.spreadapi.io/v1/services/pricing-model/execute', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.SPREADAPI_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ inputs: params })
    });
    
    const result = await response.json();
    return result.outputs;
  }
};

// Créer l'agent IA
const model = new ChatOpenAI({
  modelName: "gpt-4",
  temperature: 0
});

const tools = [pricingTool];
const modelWithTools = model.bind({ tools });

Implémenter la Logique de l'Agent

class CustomerServiceAgent {
  constructor(model, tools) {
    this.model = model;
    this.tools = tools;
    this.conversation = [];
  }
  
  async respond(userMessage) {
    // Ajouter le message utilisateur à la conversation
    this.conversation.push({
      role: 'user',
      content: userMessage
    });
    
    // Obtenir la réponse IA avec des appels d'outils potentiels
    const response = await this.model.invoke(this.conversation);
    
    // Gérer les appels d'outils
    if (response.tool_calls && response.tool_calls.length > 0) {
      for (const toolCall of response.tool_calls) {
        const tool = this.tools.find(t => t.name === toolCall.name);
        
        if (tool) {
          // Exécuter le calcul Excel
          const result = await tool.execute(toolCall.arguments);
          
          // Ajouter le résultat de l'outil à la conversation
          this.conversation.push({
            role: 'tool',
            content: JSON.stringify(result),
            tool_call_id: toolCall.id
          });
        }
      }
      
      // Obtenir la réponse finale après l'exécution des outils
      const finalResponse = await this.model.invoke(this.conversation);
      this.conversation.push({
        role: 'assistant',
        content: finalResponse.content
      });
      
      return finalResponse.content;
    }
    
    // Aucun appel d'outil nécessaire
    this.conversation.push({
      role: 'assistant',
      content: response.content
    });
    
    return response.content;
  }
}

Étape 3 : Patterns Prêts pour la Production

Pattern 1 : Agent Multi-Outils

// Ajouter plusieurs outils basés sur Excel
const tools = [
  {
    name: "calculate_pricing",
    description: "Calculer les prix des produits et les remises",
    spreadapiService: "pricing-model",
    execute: spreadapiExecutor("pricing-model")
  },
  {
    name: "check_inventory",
    description: "Vérifier la disponibilité des produits et les délais de livraison",
    spreadapiService: "inventory-tracker",
    execute: spreadapiExecutor("inventory-tracker")
  },
  {
    name: "calculate_shipping",
    description: "Calculer les coûts d'expédition et les dates de livraison",
    spreadapiService: "logistics-calculator",
    execute: spreadapiExecutor("logistics-calculator")
  }
];

// Fonction d'aide pour l'exécution SpreadAPI
function spreadapiExecutor(serviceName) {
  return async (params) => {
    const response = await fetch(
      `https://api.spreadapi.io/v1/services/${serviceName}/execute`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.SPREADAPI_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ inputs: params })
      }
    );
    
    if (!response.ok) {
      throw new Error(`Échec du calcul Excel : ${response.statusText}`);
    }
    
    const result = await response.json();
    return result.outputs;
  };
}

Pattern 2 : Agent Conscient du Contexte

class ContextAwareAgent {
  constructor() {
    this.customerContext = {};
    this.calculationCache = new Map();
  }
  
  async handleQuery(query, customerId) {
    // Charger le contexte client
    if (!this.customerContext[customerId]) {
      this.customerContext[customerId] = await this.loadCustomerData(customerId);
    }
    
    const context = this.customerContext[customerId];
    
    // Améliorer le prompt avec le contexte
    const enhancedPrompt = `
      Informations Client :
      - Type : ${context.customerType}
      - Région : ${context.region}
      - Historique d'achat : ${context.totalPurchases} commandes
      
      Requête Utilisateur : ${query}
      
      Instructions :
      - Utilisez l'outil calculate_pricing pour tous les devis de prix
      - Appliquez automatiquement le type de client approprié
      - Considérez leur région pour les calculs d'expédition
    `;
    
    return await this.respond(enhancedPrompt);
  }
  
  async loadCustomerData(customerId) {
    // Charger depuis votre base de données
    return {
      customerType: 'enterprise',
      region: 'US',
      totalPurchases: 47
    };
  }
}

Pattern 3 : Validation et Gestion d'Erreurs

class RobustAgent {
  async executeToolSafely(tool, params) {
    try {
      // Valider les entrées avant l'envoi à Excel
      const validation = this.validateInputs(tool.name, params);
      if (!validation.valid) {
        return {
          error: `Entrée invalide : ${validation.message}`,
          suggestion: validation.suggestion
        };
      }
      
      // Vérifier le cache d'abord
      const cacheKey = `${tool.name}:${JSON.stringify(params)}`;
      if (this.cache.has(cacheKey)) {
        return this.cache.get(cacheKey);
      }
      
      // Exécuter avec timeout
      const result = await Promise.race([
        tool.execute(params),
        new Promise((_, reject) => 
          setTimeout(() => reject(new Error('Timeout de calcul')), 5000)
        )
      ]);
      
      // Mettre en cache les résultats réussis
      this.cache.set(cacheKey, result);
      
      // Valider la sortie
      if (result.finalPrice < 0) {
        return {
          error: 'Résultat de calcul invalide',
          suggestion: 'Veuillez vérifier le code produit et la quantité'
        };
      }
      
      return result;
      
    } catch (error) {
      console.error('Échec de l\'exécution de l\'outil:', error);
      
      // Réponse de secours
      return {
        error: 'Impossible de calculer en ce moment',
        suggestion: 'Veuillez réessayer ou contacter le support',
        reference: error.message
      };
    }
  }
  
  validateInputs(toolName, params) {
    if (toolName === 'calculate_pricing') {
      if (params.quantity < 1) {
        return {
          valid: false,
          message: 'La quantité doit être au moins 1',
          suggestion: 'Veuillez spécifier une quantité valide'
        };
      }
      
      if (!params.productCode.match(/^[A-Z]{3}-\d{3}$/)) {
        return {
          valid: false,
          message: 'Format de code produit invalide',
          suggestion: 'Les codes produit doivent être comme ABC-123'
        };
      }
    }
    
    return { valid: true };
  }
}

Étape 4 : Capacités Avancées de l'Agent

Capacité 1 : Calculs Multi-Étapes

const complexWorkflowTool = {
  name: "quote_with_options",
  description: "Générer un devis complet avec plusieurs options de produits",
  execute: async (params) => {
    const { products, customerType, region } = params;
    
    // Calculer les prix pour chaque produit
    const quotes = await Promise.all(
      products.map(async (product) => {
        const pricing = await spreadapiExecutor('pricing-model')({
          productCode: product.code,
          quantity: product.quantity,
          customerType,
          region
        });
        
        const shipping = await spreadapiExecutor('logistics-calculator')({
          productCode: product.code,
          quantity: product.quantity,
          region,
          expedited: product.expedited || false
        });
        
        return {
          product: product.code,
          quantity: product.quantity,
          pricing,
          shipping,
          total: pricing.finalPrice + shipping.cost
        };
      })
    );
    
    // Calculer la remise groupée si applicable
    if (quotes.length > 1) {
      const bundleResult = await spreadapiExecutor('bundle-calculator')({
        products: products.map(p => p.code),
        quantities: products.map(p => p.quantity),
        customerType
      });
      
      return {
        individualQuotes: quotes,
        bundleDiscount: bundleResult.discount,
        bundleTotal: bundleResult.total
      };
    }
    
    return { quotes };
  }
};

Capacité 2 : Explications et Raisonnement

class ExplainableAgent {
  async respondWithExplanation(query) {
    const response = await this.model.invoke([
      {
        role: 'system',
        content: `Vous êtes un agent de service client utile.
        Lors de l'utilisation d'outils de tarification, expliquez toujours :
        1. Quels facteurs ont affecté le prix
        2. Quelles remises ont été appliquées
        3. Pourquoi c'est la meilleure option pour le client`
      },
      {
        role: 'user',
        content: query
      }
    ]);
    
    // Traiter les appels d'outils et ajouter des explications
    if (response.tool_calls) {
      const explanations = [];
      
      for (const toolCall of response.tool_calls) {
        const result = await this.executeTool(toolCall);
        
        // Générer une explication basée sur les résultats
        if (toolCall.name === 'calculate_pricing') {
          const discount = result.basePrice - result.finalPrice;
          const discountPercent = (discount / result.basePrice * 100).toFixed(1);
          
          explanations.push({
            calculation: toolCall.name,
            explanation: `
              Prix de base : ${result.basePrice}€
              ${discount > 0 ? `Remise appliquée : ${discount}€ (${discountPercent}%)` : 'Aucune remise applicable'}
              Prix final : ${result.finalPrice}€
              Livraison avant le : ${result.deliveryDate}
            `
          });
        }
      }
      
      // Obtenir la réponse finale avec explications
      const finalResponse = await this.model.invoke([
        ...this.conversation,
        {
          role: 'system',
          content: `Incluez ces détails de calcul dans votre réponse : ${JSON.stringify(explanations)}`
        }
      ]);
      
      return finalResponse.content;
    }
    
    return response.content;
  }
}

Capacité 3 : Comparaison de Scénarios

const scenarioTool = {
  name: "compare_scenarios",
  description: "Comparer différents scénarios d'achat pour trouver la meilleure option",
  execute: async (params) => {
    const scenarios = [
      {
        name: "Achat Individuel",
        params: {
          quantity: params.quantity,
          customerType: params.customerType
        }
      },
      {
        name: "Achat en Gros",
        params: {
          quantity: params.quantity * 3,
          customerType: params.customerType
        }
      },
      {
        name: "Contrat Annuel",
        params: {
          quantity: params.quantity * 12,
          customerType: 'enterprise' // Mise à niveau automatique
        }
      }
    ];
    
    const results = await Promise.all(
      scenarios.map(async (scenario) => {
        const pricing = await spreadapiExecutor('pricing-model')({
          ...params,
          ...scenario.params
        });
        
        return {
          scenario: scenario.name,
          totalQuantity: scenario.params.quantity,
          unitPrice: pricing.finalPrice / scenario.params.quantity,
          totalPrice: pricing.finalPrice,
          savings: (params.quantity * (pricing.basePrice / scenario.params.quantity)) - pricing.finalPrice
        };
      })
    );
    
    // Trouver la meilleure option
    const bestOption = results.reduce((best, current) => 
      current.unitPrice < best.unitPrice ? current : best
    );
    
    return {
      scenarios: results,
      recommendation: bestOption,
      potentialSavings: results[0].totalPrice - bestOption.totalPrice
    };
  }
};

Étape 5 : Déploiement et Surveillance

Configuration de Production

// config/agent.js
export const agentConfig = {
  model: {
    name: process.env.MODEL_NAME || 'gpt-4',
    temperature: 0,
    maxTokens: 1000,
    timeout: 30000
  },
  
  spreadapi: {
    baseUrl: process.env.SPREADAPI_URL || 'https://api.spreadapi.io/v1',
    apiKey: process.env.SPREADAPI_KEY,
    timeout: 5000,
    retries: 3
  },
  
  caching: {
    ttl: 300, // 5 minutes
    maxSize: 1000
  },
  
  monitoring: {
    logLevel: process.env.LOG_LEVEL || 'info',
    metricsEnabled: true,
    tracingEnabled: process.env.NODE_ENV === 'production'
  }
};

Surveillance et Analytiques

class MonitoredAgent {
  constructor(config) {
    this.metrics = {
      totalRequests: 0,
      toolCalls: {},
      errors: {},
      responseTime: []
    };
  }
  
  async handleRequest(query) {
    const startTime = Date.now();
    const requestId = generateRequestId();
    
    try {
      console.log(`[${requestId}] Traitement de la requête :`, query);
      
      const response = await this.agent.respond(query);
      
      const duration = Date.now() - startTime;
      this.metrics.responseTime.push(duration);
      this.metrics.totalRequests++;
      
      console.log(`[${requestId}] Terminé en ${duration}ms`);
      
      // Envoyer aux analytiques
      await this.sendAnalytics({
        requestId,
        duration,
        toolsUsed: this.agent.lastToolCalls,
        success: true
      });
      
      return response;
      
    } catch (error) {
      const errorType = error.name || 'Inconnu';
      this.metrics.errors[errorType] = (this.metrics.errors[errorType] || 0) + 1;
      
      console.error(`[${requestId}] Erreur :`, error);
      
      await this.sendAnalytics({
        requestId,
        error: error.message,
        success: false
      });
      
      throw error;
    }
  }
  
  getMetrics() {
    const avgResponseTime = 
      this.metrics.responseTime.reduce((a, b) => a + b, 0) / 
      this.metrics.responseTime.length;
    
    return {
      totalRequests: this.metrics.totalRequests,
      averageResponseTime: avgResponseTime,
      toolUsage: this.metrics.toolCalls,
      errorRate: Object.values(this.metrics.errors).reduce((a, b) => a + b, 0) / 
                 this.metrics.totalRequests
    };
  }
}

Pièges Courants et Solutions

Piège 1 : Surcharger l'Agent

//  Mauvais : Donner trop de liberté à l'agent
const badPrompt = "Aidez le client avec tout ce dont il a besoin";

//  Bon : Limites et capacités claires
const goodPrompt = `Vous êtes un agent de service client spécialisé dans :
1. La tarification des produits (utilisez l'outil calculate_pricing)
2. La disponibilité des stocks (utilisez l'outil check_inventory)
3. Les estimations d'expédition (utilisez l'outil calculate_shipping)

Pour les autres demandes, expliquez poliment ce avec quoi vous pouvez aider.`;

Piège 2 : Ne Pas Gérer les Erreurs Excel

//  Gestion d'erreurs robuste
const toolWithErrorHandling = {
  execute: async (params) => {
    try {
      const result = await spreadapiCall(params);
      
      // Valider les résultats Excel
      if (result.outputs.error) {
        return {
          success: false,
          error: 'Erreur de calcul dans Excel',
          details: result.outputs.error,
          suggestion: 'Veuillez vérifier le code produit et réessayer'
        };
      }
      
      return { success: true, ...result.outputs };
      
    } catch (error) {
      if (error.status === 422) {
        return {
          success: false,
          error: 'Paramètres d\'entrée invalides',
          suggestion: 'Veuillez vérifier le format de votre code produit'
        };
      }
      
      throw error; // Re-lancer les erreurs inattendues
    }
  }
};

Piège 3 : Ignorer les Performances

//  Optimisation des performances
class OptimizedAgent {
  constructor() {
    this.cache = new LRUCache({ max: 500, ttl: 1000 * 60 * 5 });
    this.batchQueue = [];
    this.batchTimer = null;
  }
  
  async calculatePricing(params) {
    // Vérifier le cache d'abord
    const cacheKey = JSON.stringify(params);
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }
    
    // Grouper les demandes similaires
    return new Promise((resolve) => {
      this.batchQueue.push({ params, resolve });
      
      if (!this.batchTimer) {
        this.batchTimer = setTimeout(() => this.processBatch(), 50);
      }
    });
  }
  
  async processBatch() {
    const batch = this.batchQueue.splice(0, 50); // Traiter jusqu'à 50 à la fois
    
    const results = await spreadapiExecutor('pricing-model').batch(
      batch.map(item => item.params)
    );
    
    results.forEach((result, index) => {
      const { params, resolve } = batch[index];
      this.cache.set(JSON.stringify(params), result);
      resolve(result);
    });
    
    this.batchTimer = null;
  }
}

Tester votre Agent

// test/agent.test.js
describe('Agent de Service Client', () => {
  let agent;
  
  beforeEach(() => {
    agent = new CustomerServiceAgent();
  });
  
  test('devrait calculer les prix avec précision', async () => {
    const response = await agent.respond(
      "Quel est le prix pour 100 unités de PRO-001 pour un client entreprise aux États-Unis ?"
    );
    
    expect(response).toContain('prix');
    expect(response).toMatch(/[0-9,]+€/);
  });
  
  test('devrait gérer les codes produit invalides', async () => {
    const response = await agent.respond(
      "Prix pour le produit XYZ"
    );
    
    expect(response).toContain('code produit valide');
  });
  
  test('devrait comparer les scénarios quand demandé', async () => {
    const response = await agent.respond(
      "Devrais-je acheter 10 unités maintenant ou attendre une commande en gros ?"
    );
    
    expect(response).toContain('scénario');
    expect(response).toContain('recommandation');
  });
});

Liste de Contrôle de Mise en Production

  • [ ] Modèles Excel téléchargés vers SpreadAPI
  • [ ] Mappages entrée/sortie définis
  • [ ] Clés API stockées de manière sécurisée
  • [ ] Prompt système de l'agent affiné
  • [ ] Descriptions d'outils claires et spécifiques
  • [ ] Gestion d'erreurs implémentée
  • [ ] Stratégie de mise en cache en place
  • [ ] Surveillance et journalisation configurées
  • [ ] Limitation de débit activée
  • [ ] Couverture de test > 80%
  • [ ] Tests de charge terminés
  • [ ] Réponses de secours définies
  • [ ] Documentation mise à jour

Prochaines Étapes

  1. Commencer Simple : Un modèle Excel, un outil, agent basique
  2. Ajouter de l'Intelligence : Conscience du contexte, workflows multi-étapes
  3. Monter en Échelle : Multiples modèles, mise en cache, surveillance
  4. Optimiser : Réglage des performances, optimisation des coûts

Prêt à construire votre agent IA ? Commencez avec SpreadAPI

Questions ? Exemples ? Écrivez-nous à hello@airrange.io

Articles Connexes

Explorez plus de guides d'API Excel et d'intégration IA :