我叫阿杰,是一名独立开发者。去年双十一我帮朋友运营的潮流电商社区 Discord 服务器遇到了大麻烦——促销日同时在线 3000 人,客服完全扛不住。我花了两个周末,用 HolyShehe AI 的 API 给服务器做了一个智能客服机器人,支持多轮对话上下文记忆,还能调用数据库查询订单、自动生成促销码。今天把完整方案分享出来,包含我踩过的坑和调试经验。

一、项目背景与方案选型

当时面临的核心问题是:

我的解决方案是做一个 Discord AI 助手,核心需求:

需求清单:
├── 多轮对话:记住用户上一轮问过的商品
├── 工具调用:查订单、查库存、生成优惠码
├── 上下文管理:区分不同用户的会话
└── 成本控制:日均 5000 次调用,月预算 < ¥200

为什么选 HolyShehe AI?对比了一圈:

二、环境准备与项目初始化

先创建项目结构:

mkdir discord-ai-bot && cd discord-ai-bot
npm init -y
npm install discord.js openai-zod-functions dotenv

项目结构如下:

discord-ai-bot/
├── index.js           # 主入口
├── services/
│   ├── chat.js        # AI 对话服务
│   ├── tools.js       # 工具函数定义
│   └── history.js     # 会话历史管理
├── .env               # 环境变量
└── package.json

三、HolyShehe AI API 接入配置

HolyShehe AI 的接口完全兼容 OpenAI 格式,改个 base_url 就行。立即注册 获取你的 API Key。

# .env 文件
DISCORD_TOKEN=你的Discord机器人Token
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
HOLYSHEEP_BASE_URL=https://api.holysheep.ai/v1

核心服务封装(services/chat.js):

const OpenAI = require('openai');

const client = new OpenAI({
  apiKey: process.env.HOLYSHEEP_API_KEY,
  baseURL: process.env.HOLYSHEEP_BASE_URL,
});

const MODEL = 'gpt-4.1'; // 支持 gpt-4.1 / claude-sonnet-4.5 / deepseek-v3.2

// 可用工具列表
const AVAILABLE_TOOLS = [
  {
    type: 'function',
    function: {
      name: 'query_order',
      description: '查询用户订单状态',
      parameters: {
        type: 'object',
        properties: {
          order_id: { type: 'string', description: '订单ID' },
          user_id: { type: 'string', description: '用户Discord ID' },
        },
        required: ['order_id'],
      },
    },
  },
  {
    type: 'function',
    function: {
      name: 'generate_coupon',
      description: '为用户生成限时优惠码',
      parameters: {
        type: 'object',
        properties: {
          discount_type: { 
            type: 'string', 
            enum: ['percentage', 'fixed'],
            description: '折扣类型' 
          },
          amount: { type: 'number', description: '折扣值' },
        },
        required: ['discount_type', 'amount'],
      },
    },
  },
  {
    type: 'function',
    function: {
      name: 'check_stock',
      description: '查询商品库存',
      parameters: {
        type: 'object',
        properties: {
          product_id: { type: 'string', description: '商品ID' },
        },
        required: ['product_id'],
      },
    },
  },
];

async function chatWithAI(userId, messages) {
  const response = await client.chat.completions.create({
    model: MODEL,
    messages: messages,
    tools: AVAILABLE_TOOLS,
    tool_choice: 'auto',
    temperature: 0.7,
  });

  const assistantMessage = response.choices[0].message;
  
  // 返回 token 使用量(用于成本计算)
  const usage = {
    input: response.usage.prompt_tokens,
    output: response.usage.completion_tokens,
    total: response.usage.total_tokens,
  };

  return { message: assistantMessage, usage };
}

module.exports = { chatWithAI, AVAILABLE_TOOLS };

四、工具调用函数实现

这里定义真正的业务逻辑(services/tools.js):

// 模拟数据库操作
const mockDatabase = {
  orders: new Map(),
  stock: new Map(),
  coupons: new Map(),
};

// 初始化模拟数据
mockDatabase.stock.set('SNEAKER-001', { name: '限量球鞋', stock: 23 });
mockDatabase.stock.set('HOODIE-002', { name: '联名卫衣', stock: 156 });

async function queryOrder(orderId, userId) {
  // 实际项目这里查数据库
  const order = mockDatabase.orders.get(orderId);
  
  if (!order) {
    return {
      success: false,
      message: 未找到订单 ${orderId},请确认订单号是否正确。,
    };
  }
  
  if (order.userId !== userId) {
    return {
      success: false,
      message: '订单信息不匹配,请联系客服核实。',
    };
  }
  
  return {
    success: true,
    order_id: order.id,
    status: order.status,
    amount: order.amount,
    items: order.items,
    create_time: order.createTime,
  };
}

async function generateCoupon(discountType, amount) {
  const code = PROMO${Date.now().toString(36).toUpperCase()};
  
  mockDatabase.coupons.set(code, {
    type: discountType,
    amount: amount,
    createTime: new Date().toISOString(),
  });
  
  const desc = discountType === 'percentage' 
    ? ${amount}% 折扣 
    : ¥${amount} 直减;
  
  return {
    success: true,
    coupon_code: code,
    description: desc,
    expire_time: '24小时后过期',
  };
}

async function checkStock(productId) {
  const product = mockDatabase.stock.get(productId);
  
  if (!product) {
    return {
      success: false,
      message: 未找到商品 ${productId},
    };
  }
  
  return {
    success: true,
    product_id: productId,
    product_name: product.name,
    stock: product.stock,
    available: product.stock > 0,
  };
}

// 工具调度器
const TOOL_HANDLERS = {
  query_order: queryOrder,
  generate_coupon: generateCoupon,
  check_stock: checkStock,
};

module.exports = { TOOL_HANDLERS };

五、会话历史管理

多轮对话的核心是上下文管理,我用的是内存 + 定期清理方案(services/history.js):

class ConversationHistory {
  constructor(maxHistory = 20) {
    this.history = new Map(); // userId -> messages[]
    this.maxHistory = maxHistory;
  }

  getHistory(userId) {
    if (!this.history.has(userId)) {
      this.history.set(userId, []);
    }
    return this.history.get(userId);
  }

  addMessage(userId, role, content) {
    const messages = this.getHistory(userId);
    messages.push({ role, content, timestamp: Date.now() });
    
    // 超过上限,删除最早的 5 条
    if (messages.length > this.maxHistory) {
      messages.splice(0, 5);
    }
  }

  addSystemPrompt(userId, prompt) {
    const messages = this.getHistory(userId);
    // 确保系统提示在开头
    const systemIndex = messages.findIndex(m => m.role === 'system');
    if (systemIndex === -1) {
      messages.unshift({ role: 'system', content: prompt, timestamp: Date.now() });
    } else {
      messages[systemIndex].content = prompt;
    }
  }

  clearHistory(userId) {
    this.history.delete(userId);
  }

  // 定时清理,释放内存
  cleanup() {
    const now = Date.now();
    const MAX_AGE = 30 * 60 * 1000; // 30分钟无活动清理
    
    for (const [userId, messages] of this.history.entries()) {
      const lastMessage = messages[messages.length - 1];
      if (lastMessage && (now - lastMessage.timestamp) > MAX_AGE) {
        this.history.delete(userId);
      }
    }
  }
}

module.exports = ConversationHistory;

六、主程序入口

整合所有模块(index.js):

require('dotenv').config();
const { Client, GatewayIntentBits } = require('discord.js');
const { chatWithAI, AVAILABLE_TOOLS } = require('./services/chat');
const { TOOL_HANDLERS } = require('./services/tools');
const ConversationHistory = require('./services/history');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

const conversationHistory = new ConversationHistory(20);
const MAX_TOOL_CALLS = 3; // 防止无限循环

client.once('ready', () => {
  console.log(🤖 Bot 已上线: ${client.user.tag});
  // 每10分钟清理过期会话
  setInterval(() => conversationHistory.cleanup(), 10 * 60 * 1000);
});

client.on('messageCreate', async (message) => {
  // 忽略机器人消息和 DM(可按需修改)
  if (message.author.bot || !message.guild) return;
  if (!message.content.startsWith('!ai ')) return;

  const userId = message.author.id;
  const userInput = message.content.slice(4).trim();
  
  // 设置系统提示
  if (!conversationHistory.getHistory(userId).find(m => m.role === 'system')) {
    conversationHistory.addSystemPrompt(userId, `你是电商潮流社区的智能客服助手。
能力:查订单状态、查库存、生成优惠码。
风格:年轻、专业、简洁。
限制:不透露你是AI,不提供虚假信息。`);
  }

  // 添加用户消息
  conversationHistory.addMessage(userId, 'user', userInput);
  
  await message.channel.sendTyping();

  try {
    let messages = conversationHistory.getHistory(userId);
    let toolCallCount = 0;

    while (toolCallCount < MAX_TOOL_CALLS) {
      const { message: aiMessage, usage } = await chatWithAI(userId, messages);
      
      // 记录 AI 响应
      messages.push(aiMessage);
      conversationHistory.addMessage(userId, 'assistant', 
        typeof aiMessage.content === 'string' 
          ? aiMessage.content 
          : JSON.stringify(aiMessage.content)
      );

      // 无工具调用,直接返回
      if (!aiMessage.tool_calls || aiMessage.tool_calls.length === 0) {
        await message.reply(aiMessage.content || '处理完成');
        break;
      }

      // 执行工具调用
      for (const toolCall of aiMessage.tool_calls) {
        const toolName = toolCall.function.name;
        const args = JSON.parse(toolCall.function.arguments);
        
        console.log(🔧 执行工具: ${toolName}, args);
        
        const handler = TOOL_HANDLERS[toolName];
        if (!handler) {
          messages.push({
            role: 'tool',
            tool_call_id: toolCall.id,
            content: 错误:未找到工具 ${toolName},
          });
          continue;
        }

        const result = await handler(...Object.values(args), userId);
        
        messages.push({
          role: 'tool',
          tool_call_id: toolCall.id,
          content: JSON.stringify(result),
        });
      }

      toolCallCount++;
    }

    // 超出工具调用限制
    if (toolCallCount >= MAX_TOOL_CALLS) {
      await message.reply('⚠️ 请求处理复杂,请稍后再试或联系人工客服。');
    }

  } catch (error) {
    console.error('❌ 处理失败:', error);
    await message.reply(⚠️ 系统错误:${error.message});
  }
});

client.login(process.env.DISCORD_TOKEN);

七、部署与成本估算

我把这个 bot 部署在 2核1G 的学生机上,24小时运行。给大家算一笔账:

促销日峰值可能到 2 万次调用,用 DeepSeek V3.2 也才 ¥12 左右,比雇一个临时客服便宜多了。

八、常见报错排查

这个项目我踩了三个大坑,记录一下:

错误1:401 Unauthorized - API Key 无效

# 错误日志
Error: 401 Unauthorized
{
  "error": {
    "message": "Invalid API key provided",
    "type": "invalid_request_error",
    "code": "invalid_api_key"
  }
}

排查步骤

1. 检查 .env 文件是否正确加载 node -e "require('dotenv').config(); console.log(process.env.HOLYSHEEP_API_KEY)" 2. 确认 API Key 没有前后的空格 echo $HOLYSHEEP_API_KEY 3. 检查 base_url 是否正确(很多人漏掉这个) 应该是: https://api.holysheep.ai/v1 不是: https://api.holysheep.ai 或其他路径

错误2:429 Rate Limit - 请求频率超限

# 错误日志
Error: 429 Too Many Requests
{
  "error": {
    "message": "Rate limit exceeded for...",
    "type": "rate_limit_error",
    "retry_after": 5
  }
}

解决方案:添加限流中间件

const rateLimiter = new Map(); const RATE_LIMIT = { maxRequests: 10, perSeconds: 60 }; async function checkRateLimit(userId) { const now = Date.now(); const key = rateLimiter.get(userId) || { count: 0, resetAt: now + RATE_LIMIT.perSeconds * 1000 }; if (now > key.resetAt) { key.count = 0; key.resetAt = now + RATE_LIMIT.perSeconds * 1000; } key.count++; rateLimiter.set(userId, key); if (key.count > RATE_LIMIT.maxRequests) { throw new Error(请求过于频繁,请 ${Math.ceil((key.resetAt - now) / 1000)} 秒后重试); } }

错误3:tool_call_id 为空导致死循环

# 问题现象
Bot 反复调用同一个工具,陷入死循环

根因

工具返回结果的 tool_call_id 没有正确传递给下一轮

修复代码

// 在添加工具结果时,确保包含 tool_call_id messages.push({ role: 'tool', tool_call_id: toolCall.id, // ← 这个字段必须有 content: JSON.stringify(result), }); // 同时限制工具调用次数 const MAX_TOOL_CALLS = 3; let toolCallCount = 0; // 在循环开始处检查 if (++toolCallCount >= MAX_TOOL_CALLS) break;

错误4:上下文超出 token 限制

# 错误日志
Error: 400 Bad Request
{
  "error": {
    "message": "This model's maximum context length is 128000 tokens",
    "type": "invalid_request_error",
    "param": "messages",
    "code": "context_length_exceeded"
  }
}

解决方案:压缩历史消息

function compressHistory(messages, maxTokens = 60000) { let totalTokens = 0; const compressed = []; // 从最新的消息往前遍历 for (let i = messages.length - 1; i >= 0; i--) { const msg = messages[i]; const estimatedTokens = Math.ceil(msg.content.length / 4); totalTokens += estimatedTokens; if (totalTokens > maxTokens) break; compressed.unshift(msg); } // 保留系统提示 const systemPrompt = messages.find(m => m.role === 'system'); return systemPrompt ? [systemPrompt, ...compressed] : compressed; }

九、总结

这个方案帮我朋友把促销日客服响应时间从 3 分钟降到了 5 秒以内,同时减少了 70% 的人工干预。最关键的几点:

  1. 工具调用:让 AI 决定何时查询数据库,而不是把所有数据都塞给模型
  2. 历史管理:防止 token 爆炸,同时支持跨轮次的上下文
  3. 成本控制:选对模型很重要,DeepSeek V3.2 在非复杂场景下完全够用

HolyShehe AI 的国内直连和微信充值对我这种个人开发者特别友好,不用折腾信用卡和代理。建议先从免费额度开始测试,觉得 ok 再充值。

👉 免费注册 HolyShehe AI,获取首月赠额度

完整代码我放在 GitHub 了:holysheep/discord-ai-bot,有问题可以提 issue。