Build a Customer Support AI Agent with OpenAI Responses API

Mar 16, 2026
11 min read
Build a Customer Support AI Agent with OpenAI Responses API

OpenAI Responses API for Customer Support

The OpenAI Assistants API is being deprecated on August 26, 2026, replaced by the Responses API with Conversations API—a simpler, more powerful architecture for building AI agents.

This guide shows how to build a production-ready customer support agent with:

  • RAG (Retrieval-Augmented Generation): Search through help docs, policies, order history
  • Function calling: Create tickets, refund orders, check status
  • Conversation management: Multi-turn context with conversation history
  • Production deployment: Serverless architecture with Redis for state

Migration note: If you have existing Assistants API implementations, migrate before August 26, 2026. This guide uses the new Responses API from the start.

Architecture Overview

ComponentOld (Assistants API)New (Responses API)Benefit
ConfigurationAssistantsPromptsEasier versioning and configuration management
StateThreadsConversationsBetter support for conversation streams
ExecutionRunsResponsesSimpler input/output model with new features
Tools128 tools maxUnlimited prompts/functionsMore flexible tool orchestration

Support Agent System Flow

User Query → Conversation API (context retrieval)
            ↓
         Responses API (RAG search + function calling)
            ↓
         [Search help docs] OR [Call function: create_ticket]
            ↓
         Generate response with context
            ↓
         Update conversation history
            ↓
         Return to user

Project Setup & Dependencies

# Initialize project
mkdir support-agent && cd support-agent
npm init -y

# Install dependencies
npm install openai ioredis express dotenv

# Create .env
echo "OPENAI_API_KEY=sk-..." > .env
echo "REDIS_URL=redis://localhost:6379" >> .env

Project Structure

support-agent/
├── index.js              # Express server
├── agent/
│   ├── responseAgent.js  # Responses API wrapper
│   ├── ragSearch.js      # RAG implementation
│   ├── functions.js      # Function calling tools
│   └── prompts.js        # System prompts
├── data/
│   └── help-docs.json    # Knowledge base
└── .env

RAG (Retrieval-Augmented Generation) searches through help docs before generating responses, grounding answers in your actual documentation.

Knowledge Base Setup

// data/help-docs.json
[
  {
    "id": "refund-policy",
    "title": "Refund Policy",
    "content": "We offer full refunds within 30 days of purchase. No questions asked.",
    "category": "billing",
    "embedding": [0.123, -0.456, ...] // pre-computed OpenAI embedding
  },
  {
    "id": "shipping-times",
    "title": "Shipping Times",
    "content": "Standard shipping: 5-7 business days. Express: 2-3 business days.",
    "category": "shipping"
  }
]
// agent/ragSearch.js
const OpenAI = require('openai');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const helpDocs = require('../data/help-docs.json');

// Compute cosine similarity
function cosineSimilarity(a, b) {
  const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
  const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
  return dotProduct / (magnitudeA * magnitudeB);
}

// Search help docs using embeddings
async function searchDocs(query, topK = 3) {
  // Get query embedding
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: query
  });
  
  const queryEmbedding = response.data[0].embedding;
  
  // Compute similarities
  const scored = helpDocs.map(doc => ({
    ...doc,
    score: cosineSimilarity(queryEmbedding, doc.embedding)
  }));
  
  // Return top K results
  return scored
    .sort((a, b) => b.score - a.score)
    .slice(0, topK)
    .map(doc => ({
      title: doc.title,
      content: doc.content,
      category: doc.category,
      score: doc.score
    }));
}

module.exports = { searchDocs };

Precompute Embeddings (One-Time)

// scripts/embedDocs.js
const OpenAI = require('openai');
const fs = require('fs');
const openai = new OpenAI();

const docs = require('../data/help-docs.json');

async function embedAllDocs() {
  for (const doc of docs) {
    const response = await openai.embeddings.create({
      model: 'text-embedding-3-small',
      input: doc.content
    });
    
    doc.embedding = response.data[0].embedding;
  }
  
  fs.writeFileSync('data/help-docs.json', JSON.stringify(docs, null, 2));
  console.log('Embeddings computed and saved!');
}

embedAllDocs();

Function Calling: Actions Beyond Text

Function calling enables the agent to perform actions: create tickets, process refunds, check order status.

Function Definitions

// agent/functions.js
const functions = [
  {
    name: 'create_ticket',
    description: 'Create a support ticket for complex issues that need human review',
    parameters: {
      type: 'object',
      properties: {
        subject: { type: 'string', description: 'Ticket subject' },
        description: { type: 'string', description: 'Detailed issue description' },
        priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Urgency level' }
      },
      required: ['subject', 'description', 'priority']
    }
  },
  {
    name: 'check_order_status',
    description: 'Check the status of a customer order',
    parameters: {
      type: 'object',
      properties: {
        order_id: { type: 'string', description: 'Order ID (e.g., ORD-12345)' }
      },
      required: ['order_id']
    }
  },
  {
    name: 'process_refund',
    description: 'Process a refund for an order (only if within 30-day policy)',
    parameters: {
      type: 'object',
      properties: {
        order_id: { type: 'string', description: 'Order ID to refund' },
        reason: { type: 'string', description: 'Refund reason' }
      },
      required: ['order_id', 'reason']
    }
  }
];

// Function implementations
async function executeFunction(name, args) {
  switch (name) {
    case 'create_ticket':
      // Call ticketing API (e.g., Zendesk, Intercom)
      return { ticket_id: 'TKT-' + Date.now(), status: 'created' };
    
    case 'check_order_status':
      // Call order management system
      return { order_id: args.order_id, status: 'shipped', tracking: 'TRK123' };
    
    case 'process_refund':
      // Call payment processor
      return { refund_id: 'REF-' + Date.now(), amount: 99.99, status: 'processing' };
    
    default:
      throw new Error(`Unknown function: ${name}`);
  }
}

module.exports = { functions, executeFunction };

Responses API Integration

Response Agent Wrapper

// agent/responseAgent.js
const OpenAI = require('openai');
const { searchDocs } = require('./ragSearch');
const { functions, executeFunction } = require('./functions');
const { systemPrompt } = require('./prompts');

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

class SupportAgent {
  constructor() {
    this.model = 'gpt-4o-2025-01-31';
  }
  
  async respond(userMessage, conversationHistory = []) {
    // RAG: Search help docs
    const relevantDocs = await searchDocs(userMessage);
    const context = relevantDocs.map(d => `${d.title}: ${d.content}`).join('\n');
    
    // Build messages with RAG context
    const messages = [
      { role: 'system', content: systemPrompt + '\n\nKnowledge Base:\n' + context },
      ...conversationHistory,
      { role: 'user', content: userMessage }
    ];
    
    // Call Responses API
    let response = await openai.chat.completions.create({
      model: this.model,
      messages,
      functions,
      function_call: 'auto'
    });
    
    // Handle function calls
    while (response.choices[0].message.function_call) {
      const functionCall = response.choices[0].message.function_call;
      const args = JSON.parse(functionCall.arguments);
      
      console.log(`Calling function: ${functionCall.name}`, args);
      const result = await executeFunction(functionCall.name, args);
      
      // Add function result to messages
      messages.push(response.choices[0].message);
      messages.push({
        role: 'function',
        name: functionCall.name,
        content: JSON.stringify(result)
      });
      
      // Get final response
      response = await openai.chat.completions.create({
        model: this.model,
        messages,
        functions,
        function_call: 'auto'
      });
    }
    
    return {
      message: response.choices[0].message.content,
      usage: response.usage
    };
  }
}

module.exports = { SupportAgent };

System Prompt

// agent/prompts.js
const systemPrompt = `You are a helpful customer support agent for an e-commerce company.

Guidelines:
- Be friendly, professional, and concise
- Always search the knowledge base first before answering
- If you can't find an answer, offer to create a support ticket
- For refunds: verify order is within 30-day policy before processing
- For order status: use check_order_status function with order ID
- Escalate complex issues to human agents via create_ticket

Never:
- Make up information not in the knowledge base
- Promise things outside company policy
- Share customer data with unauthorized parties`;

module.exports = { systemPrompt };

Conversation Management with Redis

// index.js
const express = require('express');
const Redis = require('ioredis');
const { SupportAgent } = require('./agent/responseAgent');

const app = express();
const redis = new Redis(process.env.REDIS_URL);
const agent = new SupportAgent();

app.use(express.json());

// Endpoint: Send message
app.post('/chat', async (req, res) => {
  const { userId, message } = req.body;
  
  // Get conversation history from Redis
  const historyKey = `conv:${userId}`;
  const history = await redis.get(historyKey);
  const conversationHistory = history ? JSON.parse(history) : [];
  
  // Get AI response
  const response = await agent.respond(message, conversationHistory);
  
  // Update conversation history
  conversationHistory.push(
    { role: 'user', content: message },
    { role: 'assistant', content: response.message }
  );
  
  // Keep last 20 messages
  const trimmed = conversationHistory.slice(-20);
  await redis.setex(historyKey, 3600, JSON.stringify(trimmed)); // 1hr TTL
  
  res.json({
    reply: response.message,
    usage: response.usage
  });
});

// Endpoint: Clear conversation
app.post('/clear', async (req, res) => {
  const { userId } = req.body;
  await redis.del(`conv:${userId}`);
  res.json({ status: 'cleared' });
});

app.listen(3000, () => console.log('Support agent on :3000'));

Production Deployment & Best Practices

Serverless Deployment (Vercel)

// api/chat.js (Vercel serverless function)
import { SupportAgent } from '../agent/responseAgent';
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_URL,
  token: process.env.UPSTASH_REDIS_TOKEN
});

const agent = new SupportAgent();

export default async function handler(req, res) {
  const { userId, message } = req.body;
  
  const history = await redis.get(`conv:${userId}`) || [];
  const response = await agent.respond(message, history);
  
  history.push(
    { role: 'user', content: message },
    { role: 'assistant', content: response.message }
  );
  
  await redis.setex(`conv:${userId}`, 3600, JSON.stringify(history.slice(-20)));
  
  res.json({ reply: response.message });
}

Monitoring & Observability

  • Log all queries: Track response times, function calls, errors
  • Track costs: Monitor OpenAI token usage via response.usage
  • User feedback: Add 👍/👎 buttons to improve RAG search
  • Fallback to human: Automatically create ticket if AI fails 3+ times

Performance Optimization

OptimizationImpactImplementation
Caching embeddings90% faster RAG searchPrecompute and store in JSON/database
Streaming responsesBetter UX (typing indicator)Use stream: true in OpenAI API
Lazy function loadingReduced cold startsLoad functions on demand, not at init
Model selectionCost savingsUse GPT-4o-mini for simple queries

FAQs

What's the difference between Assistants API and Responses API?

Assistants API (deprecated Aug 2026) used Assistants + Threads + Runs. Responses API uses Prompts + Conversations + Responses—simpler architecture with better performance and new features like deep research capabilities.

How much does it cost to run a support agent?

GPT-4o costs ~$2.50 per 1M input tokens, $10 per 1M output tokens. Average support query: ~500 input + 200 output tokens = $0.003 per query. At 1,000 queries/day, that's ~$90/month.

Can I use my own knowledge base instead of JSON files?

Yes—replace ragSearch.js with a vector database (Pinecone, Weaviate, Chroma). Store embeddings there and query via API. This scales better for 10K+ documents.

How do I prevent the agent from hallucinating?

Use RAG to ground responses in actual docs. Set clear system prompts: 'Only answer from knowledge base. If uncertain, offer to create a ticket.' Add citations: 'According to our refund policy...'

Should I use function calling or Responses API tools?

Both work—function calling is simpler for basic actions (create ticket, check status). Responses API tools support more complex orchestration (multi-step workflows, conditional logic).

Need an expert team to provide digital solutions for your business?

Book A Free Call

Related Articles & Resources

Dive into a wealth of knowledge with our unique articles and resources. Stay informed about the latest trends and best practices in the tech industry.

View All articles
Get in Touch

Let's build somethinggreat together.

Tell us about your vision. We'll respond within 24 hours with a free AI-powered estimate.

🎁This month only: Free UI/UX Design worth $3,000
Takes just 2 minutes
* How did you hear about us?
or prefer instant chat?

Quick question? Chat on WhatsApp

Get instant responses • Just takes 5 seconds

Response in 24 hours
100% confidential
No commitment required
🛡️100% Satisfaction Guarantee — If you're not happy with the estimate, we'll refine it for free
Propelius Technologies

You bring the vision. We handle the build.

facebookinstagramLinkedinupworkclutch

© 2026 Propelius Technologies. All rights reserved.