← Back to blog

Building a Discord Support Agent with Custom Knowledge Base Integration

By Agentis Labs Team

2025-03-10

Building a Discord Support Agent with Custom Knowledge Base Integration

In today's competitive business landscape, providing exceptional customer support is crucial for success. Discord has emerged as a popular platform for community engagement, but manually answering repetitive questions can be time-consuming and inefficient.

Enter AI-powered support agents - intelligent bots that can understand questions, retrieve relevant information from your knowledge base, and deliver accurate responses in real-time. In this comprehensive guide, we'll show you how to build a powerful Discord support agent with custom knowledge base integration using the agentis-framework.

Why Build a Discord Support Agent?

Before diving into the implementation, let's understand the benefits:

  • 24/7 Availability: Support agents are always online, providing immediate assistance regardless of time zone.
  • Consistent Responses: Ensure customers receive accurate and consistent information based on your approved content.
  • Scalability: Handle multiple inquiries simultaneously without increasing support staff.
  • Reduced Workload: Free up human agents to focus on complex issues while the bot handles routine questions.
  • Custom Knowledge: Tailor responses based on your specific product, service, or industry knowledge.

Prerequisites

To follow this tutorial, you'll need:

  1. Node.js (v16 or higher) installed on your system
  2. A Discord account and a registered Discord application/bot
  3. Basic familiarity with JavaScript/TypeScript
  4. An OpenAI API key for embeddings and completions
  5. Your support documentation in text format

Setting Up Your Project

Let's start by creating a new project and installing the necessary dependencies:

mkdir discord-support-agent
cd discord-support-agent
npm init -y
npm install agentis-framework dotenv

Create a .env file to store your API keys and tokens:

OPENAI_API_KEY=your_openai_api_key
DISCORD_BOT_TOKEN=your_discord_bot_token

Building the Core Components

Our Discord support agent will consist of three main components:

  1. Knowledge Base: Stores and retrieves information using vector embeddings
  2. Agent: Processes queries and generates helpful responses
  3. Discord Connector: Interfaces with Discord to receive and send messages

Let's implement each of these components:

1. Creating the Knowledge Base

The heart of our support agent is the knowledge base. It stores your support documentation, FAQs, and other information in a searchable format using vector embeddings.

Create a file named index.js (or index.ts if using TypeScript) and add the following code:

import { KnowledgeBase, EmbeddingService, Agent, AgentRole, DiscordConnector } from 'agentis-framework';
import * as fs from 'fs';
import dotenv from 'dotenv';
import path from 'path';

dotenv.config();

/**
 * Discord Support Agent with Knowledge Base Integration
 * 
 * This script creates a Discord bot support agent that can answer customer questions
 * directly in a Discord server using a knowledge base built from your custom content.
 */
async function main() {
  console.log("Starting Discord Support Agent...");

  // Check for required environment variables
  if (!process.env.OPENAI_API_KEY) {
    console.error('OPENAI_API_KEY is required in your .env file');
    process.exit(1);
  }

  if (!process.env.DISCORD_BOT_TOKEN) {
    console.error('DISCORD_BOT_TOKEN is required in your .env file');
    process.exit(1);
  }

  // Create data directory if it doesn't exist
  const dataDir = path.join(__dirname, 'data');
  if (!fs.existsSync(dataDir)) {
    fs.mkdirSync(dataDir, { recursive: true });
    console.log('Created data directory:', dataDir);
  }

  // Create embedding service
  const embeddingService = new EmbeddingService({
    apiKey: process.env.OPENAI_API_KEY,
    model: 'text-embedding-3-small',
    dimensions: 1536
  });

  // Create knowledge base
  const kb = new KnowledgeBase({
    persistPath: path.join(dataDir, 'support-kb.json'),
    graphPersistPath: path.join(dataDir, 'support-kb-graph.json'),
    embeddingService,
    enableChunking: true,  // Enable document chunking for better retrieval
    chunkSize: 1000,       // Split documents into 1000-character chunks
    chunkOverlap: 200      // 200 character overlap between chunks
  });

  // Initialize knowledge base
  console.log('Initializing knowledge base...');
  await kb.initialize();

  // Load FAQ data if available
  const faqFilePath = path.join(__dirname, 'support-faq.json');
  
  // Import from JSON file if KB is empty and file exists
  if (kb.getStats().faqCount === 0 && fs.existsSync(faqFilePath)) {
    try {
      console.log(`Loading FAQs from ${faqFilePath}...`);
      const faqData = JSON.parse(fs.readFileSync(faqFilePath, 'utf8'));
      
      if (Array.isArray(faqData)) {
        console.log(`Found ${faqData.length} FAQs to import.`);
        await kb.ingestFAQs(faqData);
        console.log('FAQ import completed successfully!');
      } else {
        console.error('The JSON file should contain an array of FAQ objects');
      }
    } catch (error) {
      console.error('Error importing FAQ data:', error);
    }
  }

  // Load documents if KB is empty
  if (kb.getStats().documentCount === 0) {
    const docsDir = path.join(__dirname, 'docs');
    
    if (fs.existsSync(docsDir)) {
      console.log("Loading documents from docs directory...");
      
      // Read all .txt or .md files in the docs directory
      const files = fs.readdirSync(docsDir).filter(file => 
        file.endsWith('.txt') || file.endsWith('.md')
      );
      
      for (const file of files) {
        const filePath = path.join(docsDir, file);
        const content = fs.readFileSync(filePath, 'utf8');
        const title = file.replace(/\.(txt|md)$/, '');
        
        console.log(`Adding document: ${title}`);
        
        await kb.addDocument(
          title,
          content,
          `file://${filePath}`,
          undefined,
          "Documentation",
          ["support", "documentation"]
        );
      }
      
      console.log("Documents added successfully!");
    } else {
      console.log("Docs directory not found. Skipping document import.");
    }
  }

  // Show current stats
  const stats = kb.getStats();
  console.log('\nKnowledge Base Stats:');
  console.log(`- FAQ entries: ${stats.faqCount}`);
  console.log(`- Document entries: ${stats.documentCount}`);
  console.log(`- Chunks: ${stats.chunkCount || 'N/A'}`);
  console.log(`- Categories: ${stats.categories.join(', ')}`);
  console.log(`- Tags: ${stats.tags.join(', ')}`);

  // Create the support agent
  const supportAgent = new Agent({
    name: "Support Assistant",
    role: AgentRole.ASSISTANT,
    personality: {
      traits: ["helpful", "knowledgeable", "professional", "patient"],
      background: "A customer support specialist who provides accurate information based on the company's documentation and policies.",
      voice: "Professional, friendly, and informative. Provides clear explanations and accurate information from the knowledge base."
    },
    goals: [
      "Provide accurate information based on the knowledge base",
      "Help customers understand products and services",
      "Answer questions clearly and concisely",
      "Refer to human support for complex issues beyond the scope of available information"
    ],
    knowledgeBase: kb,
    knowledgeBaseMaxResults: 5,     // Retrieve up to 5 relevant items
    knowledgeBaseThreshold: 0.65    // Minimum relevance score (0-1)
  });

  // Define keywords to monitor for in Discord messages
  const monitorKeywords = [
    "help", "support", "how to", "problem", "issue",
    "question", "guide", "tutorial", "documentation",
    "assistance", "troubleshoot"
    // Add product-specific terms here
  ];

  // Create Discord connector
  const discord = new DiscordConnector({
    token: process.env.DISCORD_BOT_TOKEN,
    prefix: '!help',               // Command prefix for direct commands
    autoReply: true,               // Automatically reply to messages
    monitorKeywords: monitorKeywords,
    pollInterval: 60000,           // Check every minute
    // Optional: Add allowed channels or users if you want to restrict usage
    allowedChannels: process.env.ALLOWED_CHANNELS?.split(',') || [],
  });

  // Add error event handler to catch connector-level errors
  discord.on('error', (error) => {
    console.error('Discord connector error:', error.message);
  });

  // Connect agent to Discord
  try {
    await discord.connect(supportAgent);
    console.log(`Successfully connected to Discord!`);
    
    // Set bot status
    await discord.setStatus('online', 'WATCHING', 'for support questions');
    
    // Get connected guilds (for information)
    const guilds = await discord.getGuilds();
    console.log(`Connected to ${guilds.length} Discord servers:`);
    guilds.forEach(guild => {
      console.log(`- ${guild.name} (${guild.id}) with ${guild.memberCount || 'unknown'} members`);
    });

    console.log('\nBot is now running and ready to answer questions!');
    console.log('Available commands:');
    console.log('  !help <question> - Ask a specific question to the bot');
    console.log('The bot will also respond automatically to:');
    console.log('  - Direct mentions (@Support Assistant)');
    console.log('  - Messages containing keywords about support topics');

    // Keep the process running
    process.on('SIGINT', async () => {
      console.log('Shutting down...');
      await discord.disconnect();
      process.exit(0);
    });

  } catch (error) {
    console.error('Error connecting to Discord:', error);
    process.exit(1);
  }
}

main().catch(error => {
  console.error('Unhandled error:', error);
  process.exit(1);
});

Preparing Your Knowledge Base Content

Creating FAQ Data

Create a file named support-faq.json with your frequently asked questions and answers:

[
  {
    "question": "What is your return policy?",
    "answer": "Our return policy allows for returns within 30 days of purchase. Items must be in original condition with tags attached. Please include your order number with the return.",
    "category": "Returns",
    "tags": ["returns", "policy", "refund"]
  },
  {
    "question": "How do I reset my password?",
    "answer": "To reset your password, visit the login page and click 'Forgot Password'. Enter your email address, and we'll send you a link to create a new password.",
    "category": "Account",
    "tags": ["password", "reset", "account"]
  }
  // Add more FAQs here...
]

Adding Documentation Files

Create a docs directory and add your documentation files. You can use plain text (.txt) or markdown (.md) files:

mkdir docs
touch docs/product-features.md
touch docs/troubleshooting-guide.md

Then add your documentation content to these files.

Document Chunking for Better Retrieval

One of the most powerful features of agentis-framework is its ability to automatically chunk large documents into smaller pieces for more effective retrieval. This is particularly valuable for support documentation, which often contains lengthy guides or manuals.

When you enable chunking with enableChunking: true, the framework will:

  1. Split large documents into smaller chunks of the specified size
  2. Create overlapping sections to maintain context between chunks
  3. Generate embeddings for each chunk separately
  4. Match queries against the most relevant chunks rather than entire documents

This approach significantly improves retrieval accuracy, as it allows the system to find specific information buried deep within lengthy documentation.

Handling Fallback Scenarios

A well-designed support agent should know its limits. Let's enhance our agent to recognize when it doesn't have enough information and to suggest escalation to human support when needed:

// You can add this custom handler to your DiscordConnector setup
discord.on('message', async (message) => {
  // Skip messages from bots or system messages
  if (message.author.bot) return;
  
  // Process the message with the agent
  const result = await supportAgent.run({
    task: `Respond to this Discord message: "${message.content}"`,
    conversation: {
      id: `discord-${message.channelId}`,
      messages: [], // You could load previous messages here
      metadata: { channelId: message.channelId }
    }
  });
  
  // Check confidence level (you can implement this in your Agent class)
  if (result.metadata?.confidenceScore < 0.4) {
    // Low confidence - suggest human support
    await discord.sendMessage(
      message.channelId,
      `I'm not entirely sure about this one. Let me connect you with our human support team. Please use the @support tag or email support@yourcompany.com for further assistance.`
    );
  } else {
    // Confident enough to provide an answer
    await discord.sendMessage(message.channelId, result.response);
  }
});

Deploying Your Discord Support Agent

Running Locally

To run your agent locally for testing:

node index.js

Production Deployment

For production, you should deploy your bot to a reliable server or cloud platform. Here are some options:

  1. Virtual Private Server (VPS): Providers like DigitalOcean, Linode, or AWS EC2
  2. Serverless: AWS Lambda, Google Cloud Functions, or Azure Functions
  3. Container-based: Docker containers on Kubernetes, Google Cloud Run, or AWS ECS

Make sure your deployment has:

  • Process management (PM2 or similar)
  • Environment variable security
  • Error monitoring and logging
  • Automatic restarts in case of failures

Enhancing Your Support Agent

Multi-Provider Model Support

agentis-framework supports multiple AI providers, allowing you to choose the best model for your needs or fall back to alternatives when needed:

// Create a support agent with provider preferences
const supportAgent = new Agent({
  // ... other configuration
  providers: [
    { type: 'anthropic', model: 'claude-3-haiku' },
    { type: 'openai', model: 'gpt-4o' }
  ]
});

Implementing Conversation History

For better context awareness, implement a conversation history store:

// Add conversation store to your project
const conversationStore = new Map();

discord.on('message', async (message) => {
  const conversationId = `discord-${message.channelId}-${message.author.id}`;
  
  // Get or initialize conversation history
  if (!conversationStore.has(conversationId)) {
    conversationStore.set(conversationId, []);
  }
  
  const history = conversationStore.get(conversationId);
  
  // Add user message to history
  history.push({
    role: 'user',
    content: message.content,
    timestamp: Date.now()
  });
  
  // Process with agent using history
  const result = await supportAgent.run({
    task: message.content,
    conversation: {
      id: conversationId,
      messages: history,
      metadata: { channelId: message.channelId }
    }
  });
  
  // Add agent response to history
  history.push({
    role: 'assistant',
    content: result.response,
    timestamp: Date.now()
  });
  
  // Limit history length to prevent context overflow
  if (history.length > 20) {
    history.splice(0, history.length - 20);
  }
  
  // Send response
  await discord.sendMessage(message.channelId, result.response);
});

Adding Analytics and Improvement Loops

Track user interactions to continually improve your support agent:

// Simple analytics tracking
function trackInteraction(query, response, source, successful) {
  const interaction = {
    timestamp: new Date().toISOString(),
    query,
    responseLength: response.length,
    source,
    successful,
  };
  
  // Append to a log file
  fs.appendFileSync(
    path.join(__dirname, 'data', 'interactions.jsonl'),
    JSON.stringify(interaction) + '\n'
  );
}

// Use in your message handler
discord.on('message', async (message) => {
  // ... process message
  
  // Track the interaction
  trackInteraction(
    message.content,
    result.response,
    result.metadata?.knowledgeSource || 'unknown',
    !result.metadata?.fallback
  );
});

Advanced Knowledge Base Techniques

Knowledge Graph Integration

agentis-framework supports knowledge graph capabilities to establish relationships between different pieces of information:

// When adding items to your knowledge base
await kb.addDocument(
  "Product A Features",
  "Detailed feature list...",
  "https://docs.example.com/products/a",
  undefined,
  "Products",
  ["features", "product-a"]
);

await kb.addDocument(
  "Product A Troubleshooting",
  "Common issues and solutions...",
  "https://docs.example.com/products/a/troubleshooting",
  undefined,
  "Troubleshooting",
  ["issues", "product-a"]
);

// Create a relationship between documents
await kb.addRelationship(
  "Product A Features",
  "Product A Troubleshooting",
  "related_to",
  0.9 // Strength of relationship
);

Regular Knowledge Base Updates

Keep your knowledge base current by implementing an update mechanism:

// Function to update knowledge base from documentation repository
async function updateKnowledgeBase() {
  console.log("Updating knowledge base from latest documentation...");
  
  // Pull latest docs from git, CMS, or other source
  // executeSyncCommand('git pull origin main');
  
  const docsDir = path.join(__dirname, 'docs');
  const files = fs.readdirSync(docsDir).filter(file => 
    file.endsWith('.txt') || file.endsWith('.md')
  );
  
  for (const file of files) {
    const filePath = path.join(docsDir, file);
    const content = fs.readFileSync(filePath, 'utf8');
    const title = file.replace(/\.(txt|md)$/, '');
    const stats = fs.statSync(filePath);
    
    // Check if file was modified since last update
    const lastModified = new Date(stats.mtime);
    const existingDoc = await kb.findDocumentByTitle(title);
    
    if (!existingDoc || new Date(existingDoc.updated) < lastModified) {
      console.log(`Updating document: ${title}`);
      await kb.updateOrAddDocument(
        title,
        content,
        `file://${filePath}`,
        undefined,
        "Documentation",
        ["support", "documentation"]
      );
    }
  }
  
  console.log("Knowledge base update completed!");
}

// Schedule regular updates (e.g., once a day)
setInterval(updateKnowledgeBase, 24 * 60 * 60 * 1000);

Best Practices for Discord Support Agents

  1. Clear Introduction: Have your bot introduce itself and explain how to interact with it when it first joins a server.

  2. Response Templates: Use structured response templates for consistency:

    • Clear answer to the question
    • Source of the information
    • Related resources or next steps
    • Option to escalate to human support
  3. Message Formatting: Use Discord's markdown formatting for better readability:

    • Bold text for important information
    • Code blocks for technical instructions
    • Embeds for structured responses
    • Emojis for visual cues
  4. Conversation Management:

    • End conversations gracefully
    • Recognize when to transfer to a human
    • Handle multiple users in the same channel
  5. Monitor and Improve:

    • Track common questions that lack good answers
    • Regularly update your knowledge base
    • Review and refine agent responses based on user feedback

Troubleshooting Common Issues

Knowledge Retrieval Problems

If your agent isn't finding relevant information:

  1. Check your chunking settings - you may need to adjust chunk size
  2. Verify that documents are being properly ingested
  3. Lower the similarity threshold for broader matches
  4. Add more varied tags to improve retrieval

Discord Connection Issues

If you're having trouble connecting to Discord:

  1. Verify your bot token is correct and has the necessary permissions
  2. Check that your bot is invited to the server with the right scopes
  3. Ensure your server isn't blocking bot connections
  4. Look for rate limiting issues in the Discord API

Conclusion

Building a Discord support agent with custom knowledge base integration using agentis-framework opens up powerful possibilities for providing efficient, accurate, and scalable customer support. By leveraging AI to understand questions and retrieve relevant information from your custom knowledge base, you can deliver exceptional support experiences around the clock.

The approach outlined in this guide combines:

  1. Powerful vector embeddings for accurate information retrieval
  2. Automatic document chunking for better results from long-form content
  3. Discord integration for seamless user interactions
  4. Conversation history for contextual understanding
  5. Advanced knowledge management techniques

As you continue to build and refine your support agent, remember that the key to success lies in the quality of your knowledge base content and the ongoing improvement based on real user interactions.

Want to see more examples or get help implementing your own Discord support agent? Check out our documentation at agentislabs.ai or join our Discord community for assistance from our team and other developers building with agentis-framework.


This tutorial is part of our series on building intelligent AI agents for real-world applications. For more guides and examples, visit our blog or GitHub repository.