Building AI Agents That Actually Use Your Excel Models

From Excel to AI Agent in 30 Minutes

Your Excel spreadsheet has evolved over years. It handles edge cases, implements complex business rules, and embodies deep domain knowledge. Now you want an AI agent to use it.

Most tutorials will tell you to "just export to CSV" or "rebuild in Python." We're going to do something better: give your AI agent direct access to Excel's calculation engine.

What We're Building

A customer service AI agent that can:

  • Calculate accurate quotes using your pricing Excel
  • Check delivery dates with your logistics model
  • Apply discounts based on complex business rules
  • Handle edge cases exactly like your team does

All using your existing Excel files. No rebuilding required.

Prerequisites

# You'll need:
npm install langchain @langchain/openai
# or
pip install langchain openai

# And a SpreadAPI account (free tier works)
# Sign up at https://spreadapi.io

Step 1: Prepare Your Excel for AI

Your Excel Structure

PricingModel.xlsx
├── Inputs
│   ├── B2: Product Code
│   ├── B3: Quantity
│   ├── B4: Customer Type
│   └── B5: Region
├── Calculations (Hidden from AI)
│   ├── Complex VLOOKUP formulas
│   ├── Discount matrices
│   └── Business rules
└── Outputs
    ├── E10: Base Price
    ├── E11: Discount Amount
    ├── E12: Final Price
    └── E13: Delivery Date

Upload to SpreadAPI

  1. Log into SpreadAPI Dashboard
  2. Create new service called "pricing-model"
  3. Upload your Excel
  4. Define interface:
{
  "inputs": {
    "productCode": "B2",
    "quantity": "B3",
    "customerType": "B4",
    "region": "B5"
  },
  "outputs": {
    "basePrice": "E10",
    "discount": "E11",
    "finalPrice": "E12",
    "deliveryDate": "E13"
  }
}

Step 2: Create the AI Agent

Basic Agent with Function Calling

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

// Define the Excel calculation tool
const pricingTool = {
  name: "calculate_pricing",
  description: "Calculate accurate pricing using the company pricing model. Use this whenever you need to quote prices or check discounts.",
  parameters: {
    type: "object",
    properties: {
      productCode: {
        type: "string",
        description: "Product code (e.g., 'PRO-001')"
      },
      quantity: {
        type: "number",
        description: "Number of units"
      },
      customerType: {
        type: "string",
        enum: ["standard", "premium", "enterprise"],
        description: "Type of customer account"
      },
      region: {
        type: "string",
        enum: ["US", "EU", "APAC"],
        description: "Customer's region"
      }
    },
    required: ["productCode", "quantity", "customerType", "region"]
  },
  execute: async (params) => {
    // Call 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;
  }
};

// Create the AI agent
const model = new ChatOpenAI({
  modelName: "gpt-4",
  temperature: 0
});

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

Implementing the Agent Logic

class CustomerServiceAgent {
  constructor(model, tools) {
    this.model = model;
    this.tools = tools;
    this.conversation = [];
  }
  
  async respond(userMessage) {
    // Add user message to conversation
    this.conversation.push({
      role: 'user',
      content: userMessage
    });
    
    // Get AI response with potential tool calls
    const response = await this.model.invoke(this.conversation);
    
    // Handle tool calls
    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) {
          // Execute the Excel calculation
          const result = await tool.execute(toolCall.arguments);
          
          // Add tool result to conversation
          this.conversation.push({
            role: 'tool',
            content: JSON.stringify(result),
            tool_call_id: toolCall.id
          });
        }
      }
      
      // Get final response after tool execution
      const finalResponse = await this.model.invoke(this.conversation);
      this.conversation.push({
        role: 'assistant',
        content: finalResponse.content
      });
      
      return finalResponse.content;
    }
    
    // No tool calls needed
    this.conversation.push({
      role: 'assistant',
      content: response.content
    });
    
    return response.content;
  }
}

Step 3: Production-Ready Patterns

Pattern 1: Multi-Tool Agent

// Add multiple Excel-based tools
const tools = [
  {
    name: "calculate_pricing",
    description: "Calculate product pricing and discounts",
    spreadapiService: "pricing-model",
    execute: spreadapiExecutor("pricing-model")
  },
  {
    name: "check_inventory",
    description: "Check product availability and lead times",
    spreadapiService: "inventory-tracker",
    execute: spreadapiExecutor("inventory-tracker")
  },
  {
    name: "calculate_shipping",
    description: "Calculate shipping costs and delivery dates",
    spreadapiService: "logistics-calculator",
    execute: spreadapiExecutor("logistics-calculator")
  }
];

// Helper function for SpreadAPI execution
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(`Excel calculation failed: ${response.statusText}`);
    }
    
    const result = await response.json();
    return result.outputs;
  };
}

Pattern 2: Context-Aware Agent

class ContextAwareAgent {
  constructor() {
    this.customerContext = {};
    this.calculationCache = new Map();
  }
  
  async handleQuery(query, customerId) {
    // Load customer context
    if (!this.customerContext[customerId]) {
      this.customerContext[customerId] = await this.loadCustomerData(customerId);
    }
    
    const context = this.customerContext[customerId];
    
    // Enhance prompt with context
    const enhancedPrompt = `
      Customer Information:
      - Type: ${context.customerType}
      - Region: ${context.region}
      - Purchase History: ${context.totalPurchases} orders
      
      User Query: ${query}
      
      Instructions:
      - Use the calculate_pricing tool for any price quotes
      - Apply appropriate customer type automatically
      - Consider their region for shipping calculations
    `;
    
    return await this.respond(enhancedPrompt);
  }
  
  async loadCustomerData(customerId) {
    // Load from your database
    return {
      customerType: 'enterprise',
      region: 'US',
      totalPurchases: 47
    };
  }
}

Pattern 3: Validation and Error Handling

class RobustAgent {
  async executeToolSafely(tool, params) {
    try {
      // Validate inputs before sending to Excel
      const validation = this.validateInputs(tool.name, params);
      if (!validation.valid) {
        return {
          error: `Invalid input: ${validation.message}`,
          suggestion: validation.suggestion
        };
      }
      
      // Check cache first
      const cacheKey = `${tool.name}:${JSON.stringify(params)}`;
      if (this.cache.has(cacheKey)) {
        return this.cache.get(cacheKey);
      }
      
      // Execute with timeout
      const result = await Promise.race([
        tool.execute(params),
        new Promise((_, reject) => 
          setTimeout(() => reject(new Error('Calculation timeout')), 5000)
        )
      ]);
      
      // Cache successful results
      this.cache.set(cacheKey, result);
      
      // Validate output
      if (result.finalPrice < 0) {
        return {
          error: 'Invalid calculation result',
          suggestion: 'Please check product code and quantity'
        };
      }
      
      return result;
      
    } catch (error) {
      console.error('Tool execution failed:', error);
      
      // Fallback response
      return {
        error: 'Unable to calculate at this time',
        suggestion: 'Please try again or contact support',
        reference: error.message
      };
    }
  }
  
  validateInputs(toolName, params) {
    if (toolName === 'calculate_pricing') {
      if (params.quantity < 1) {
        return {
          valid: false,
          message: 'Quantity must be at least 1',
          suggestion: 'Please specify a valid quantity'
        };
      }
      
      if (!params.productCode.match(/^[A-Z]{3}-\d{3}$/)) {
        return {
          valid: false,
          message: 'Invalid product code format',
          suggestion: 'Product codes should be like ABC-123'
        };
      }
    }
    
    return { valid: true };
  }
}

Step 4: Advanced Agent Capabilities

Capability 1: Multi-Step Calculations

const complexWorkflowTool = {
  name: "quote_with_options",
  description: "Generate a complete quote with multiple product options",
  execute: async (params) => {
    const { products, customerType, region } = params;
    
    // Calculate pricing for each product
    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
        };
      })
    );
    
    // Calculate bundle discount if 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 };
  }
};

Capability 2: Explanations and Reasoning

class ExplainableAgent {
  async respondWithExplanation(query) {
    const response = await this.model.invoke([
      {
        role: 'system',
        content: `You are a helpful customer service agent.
        When using pricing tools, always explain:
        1. What factors affected the price
        2. Any discounts applied
        3. Why this is the best option for the customer`
      },
      {
        role: 'user',
        content: query
      }
    ]);
    
    // Process tool calls and add explanations
    if (response.tool_calls) {
      const explanations = [];
      
      for (const toolCall of response.tool_calls) {
        const result = await this.executeTool(toolCall);
        
        // Generate explanation based on results
        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: `
              Base price: $${result.basePrice}
              ${discount > 0 ? `Discount applied: $${discount} (${discountPercent}%)` : 'No discount applicable'}
              Final price: $${result.finalPrice}
              Delivery by: ${result.deliveryDate}
            `
          });
        }
      }
      
      // Get final response with explanations
      const finalResponse = await this.model.invoke([
        ...this.conversation,
        {
          role: 'system',
          content: `Include these calculation details in your response: ${JSON.stringify(explanations)}`
        }
      ]);
      
      return finalResponse.content;
    }
    
    return response.content;
  }
}

Capability 3: Scenario Comparison

const scenarioTool = {
  name: "compare_scenarios",
  description: "Compare different purchase scenarios to find the best option",
  execute: async (params) => {
    const scenarios = [
      {
        name: "Single Purchase",
        params: {
          quantity: params.quantity,
          customerType: params.customerType
        }
      },
      {
        name: "Bulk Purchase",
        params: {
          quantity: params.quantity * 3,
          customerType: params.customerType
        }
      },
      {
        name: "Annual Contract",
        params: {
          quantity: params.quantity * 12,
          customerType: 'enterprise' // Automatic upgrade
        }
      }
    ];
    
    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
        };
      })
    );
    
    // Find best option
    const bestOption = results.reduce((best, current) => 
      current.unitPrice < best.unitPrice ? current : best
    );
    
    return {
      scenarios: results,
      recommendation: bestOption,
      potentialSavings: results[0].totalPrice - bestOption.totalPrice
    };
  }
};

Step 5: Deployment and Monitoring

Production Configuration

// 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'
  }
};

Monitoring and Analytics

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}] Processing query:`, query);
      
      const response = await this.agent.respond(query);
      
      const duration = Date.now() - startTime;
      this.metrics.responseTime.push(duration);
      this.metrics.totalRequests++;
      
      console.log(`[${requestId}] Completed in ${duration}ms`);
      
      // Send to analytics
      await this.sendAnalytics({
        requestId,
        duration,
        toolsUsed: this.agent.lastToolCalls,
        success: true
      });
      
      return response;
      
    } catch (error) {
      const errorType = error.name || 'Unknown';
      this.metrics.errors[errorType] = (this.metrics.errors[errorType] || 0) + 1;
      
      console.error(`[${requestId}] Error:`, 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
    };
  }
}

Common Pitfalls and Solutions

Pitfall 1: Overloading the Agent

// ❌ Bad: Giving agent too much freedom
const badPrompt = "Help the customer with anything they need";

// ✅ Good: Clear boundaries and capabilities
const goodPrompt = `You are a customer service agent specializing in:
1. Product pricing (use calculate_pricing tool)
2. Inventory availability (use check_inventory tool)
3. Shipping estimates (use calculate_shipping tool)

For other requests, politely explain what you can help with.`;

Pitfall 2: Not Handling Excel Errors

// ✅ Robust error handling
const toolWithErrorHandling = {
  execute: async (params) => {
    try {
      const result = await spreadapiCall(params);
      
      // Validate Excel results
      if (result.outputs.error) {
        return {
          success: false,
          error: 'Calculation error in Excel',
          details: result.outputs.error,
          suggestion: 'Please verify product code and try again'
        };
      }
      
      return { success: true, ...result.outputs };
      
    } catch (error) {
      if (error.status === 422) {
        return {
          success: false,
          error: 'Invalid input parameters',
          suggestion: 'Please check your product code format'
        };
      }
      
      throw error; // Re-throw unexpected errors
    }
  }
};

Pitfall 3: Ignoring Performance

// ✅ Performance optimization
class OptimizedAgent {
  constructor() {
    this.cache = new LRUCache({ max: 500, ttl: 1000 * 60 * 5 });
    this.batchQueue = [];
    this.batchTimer = null;
  }
  
  async calculatePricing(params) {
    // Check cache first
    const cacheKey = JSON.stringify(params);
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }
    
    // Batch similar requests
    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); // Process up to 50 at once
    
    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;
  }
}

Testing Your Agent

// test/agent.test.js
describe('Customer Service Agent', () => {
  let agent;
  
  beforeEach(() => {
    agent = new CustomerServiceAgent();
  });
  
  test('should calculate pricing accurately', async () => {
    const response = await agent.respond(
      "What's the price for 100 units of PRO-001 for an enterprise customer in the US?"
    );
    
    expect(response).toContain('price');
    expect(response).toMatch(/\$[0-9,]+/);
  });
  
  test('should handle invalid product codes', async () => {
    const response = await agent.respond(
      "Price for product XYZ"
    );
    
    expect(response).toContain('valid product code');
  });
  
  test('should compare scenarios when asked', async () => {
    const response = await agent.respond(
      "Should I buy 10 units now or wait for a bulk order?"
    );
    
    expect(response).toContain('scenario');
    expect(response).toContain('recommendation');
  });
});

Go Live Checklist

  • [ ] Excel models uploaded to SpreadAPI
  • [ ] Input/output mappings defined
  • [ ] API keys securely stored
  • [ ] Agent system prompt refined
  • [ ] Tool descriptions clear and specific
  • [ ] Error handling implemented
  • [ ] Caching strategy in place
  • [ ] Monitoring and logging configured
  • [ ] Rate limiting enabled
  • [ ] Test coverage > 80%
  • [ ] Load testing completed
  • [ ] Fallback responses defined
  • [ ] Documentation updated

Next Steps

  1. Start Simple: One Excel model, one tool, basic agent
  2. Add Intelligence: Context awareness, multi-step workflows
  3. Scale Up: Multiple models, caching, monitoring
  4. Optimize: Performance tuning, cost optimization

Ready to build your AI agent? Get started with SpreadAPI

Questions? Examples? Email us at hello@airrange.io

P.S. - Every AI agent needs accurate calculations. Your Excel models already have them. It's time to connect the dots.

Explore more Excel API and AI integration guides: