Skip to content
Назад в блог

ИИ-поддержка клиентов: Как собрать RAG-бота с бесшовной передачей менеджеру

12 мин. чтения
ИИ-поддержка клиентов: Как собрать RAG-бота с бесшовной передачей менеджеру

Внедрение больших языковых моделей (LLM) вроде ChatGPT для ответов на вопросы клиентов — один из самых популярных способов снижения нагрузки на техподдержку. Однако стандартные ИИ-модели имеют два критических недостатка для корпоративного использования: у них нет доступа к вашим закрытым данным (инструкции, цены, API) и они склонны придумывать недостоверные факты («галлюцинировать»).

Для решения этих проблем в продакшене используется архитектура RAG (Retrieval-Augmented Generation). Сначала бот ищет ответ в локальной базе знаний компании, извлекает релевантные текстовые фрагменты и передает их в качестве контекста в LLM. Модель формулирует ответ строго на основе предоставленных документов.

Для обеспечения премиального клиентского сервиса система должна содержать протокол эскалации на оператора. Если ИИ не находит ответа в базе, диалог мгновенно переключается на человека.

В этой статье мы соберем полноценный RAG-цикл на Node.js, настроим поиск в PostgreSQL с расширением pgvector и опишем логику переключения диалога.

Архитектура работы RAG-бота поддержки

[Пользователь спрашивает: "Как настроить Stripe?"]
                       │
                       v
     (Генерация векторного эмбеддинга)
     [text-embedding-3-small] (1536 dim)
                       │
                       v
     (Векторный поиск в PostgreSQL)
     SELECT * FROM match_kb_docs(vector, threshold = 0.75)
                       │
                       +-----------------------+
                       |                       |
            (Контекст НАЙДЕН)         (Контекст НЕ НАЙДЕН / Низкий скор)
                       │                       │
                       v                       v
     [Формируем промпт с текстом KB]   [Ставим сессии статус human_handling]
                       │                       │
                       v                       v
     [Отправляем запрос в gpt-4o-mini]  [Оповещаем менеджеров в Slack/CRM]
                       │                       │
                       v                       v
     [Отправляем ИИ-ответ клиенту]     [Отправляем: "Перевожу на менеджера"]

1. Настройка базы данных Pgvector

Включите расширение vector в вашей БД PostgreSQL и создайте таблицу для хранения фрагментов документации. Количество измерений (dimensions) вектора должно совпадать с выбранной моделью эмбеддингов (1536 для OpenAI text-embedding-3-small).

-- Включаем поддержку векторного расширения
CREATE EXTENSION IF NOT EXISTS vector;

-- Создаем таблицу Базы Знаний
CREATE TABLE kb_documents (
  id SERIAL PRIMARY KEY,
  content TEXT NOT NULL,
  embedding VECTOR(1536) NOT NULL,
  category VARCHAR(50) DEFAULT 'general',
  created_at TIMESTAMP DEFAULT NOW()
);

-- Индексируем векторную колонку (используем HNSW для высокой скорости на масштабе)
CREATE INDEX kb_hnsw_idx ON kb_documents USING hnsw (embedding vector_cosine_ops);

Создадим SQL-функцию поиска документов по косинусному расстоянию:

CREATE OR REPLACE FUNCTION match_kb_docs(
  query_embedding VECTOR(1536),
  match_threshold FLOAT,
  match_count INT
)
RETURNS TABLE (id INT, content TEXT, similarity FLOAT)
LANGUAGE plpgsql AS $$
BEGIN
  RETURN QUERY
  SELECT kb.id, kb.content, 1 - (kb.embedding <=> query_embedding) AS similarity
  FROM kb_documents kb
  WHERE 1 - (kb.embedding <=> query_embedding) > match_threshold
  ORDER BY kb.embedding <=> query_embedding
  LIMIT match_count;
END;
$$;

2. Реализация векторного поиска на Node.js

Напишем сервис, который будет обращаться к API OpenAI для генерации вектора из вопроса пользователя и осуществлять поиск по базе PostgreSQL:

import { OpenAI } from 'openai';
import pg from 'pg';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });

export async function getEmbedding(text: string): Promise<number[]> {
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: text.replace(/\n/g, ' '),
  });
  return response.data[0].embedding;
}

export async function findRelevantContext(queryText: string): Promise<string> {
  const queryVector = await getEmbedding(queryText);
  const sql = `SELECT * FROM match_kb_docs($1::vector, 0.75, 3)`;
  
  const client = await pool.connect();
  try {
    const result = await client.query(sql, [JSON.stringify(queryVector)]);
    if (result.rows.length === 0) {
      return '';
    }
    // Объединяем найденные статьи в один текстовый блок
    return result.rows.map(row => row.content).join('\n---\n');
  } finally {
    client.release();
  }
}

3. Системный промпт против галлюцинаций ИИ

Чтобы модель не придумывала несуществующие тарифы и правила, промпт должен жестко запретить генерацию ответов при отсутствии информации в контексте:

export async function generateAIResponse(userQuestion: string, context: string): Promise<string> {
  if (!context) {
    return 'ERROR_UNRESOLVED';
  }

  const systemPrompt = `
Вы — оператор техподдержки TeleGo.io.
Отвечайте на вопросы пользователя, используя ТОЛЬКО предоставленный контекст.
Правила:
1. Опирайтесь исключительно на предоставленные факты. Не придумывайте ничего от себя.
2. Если в контексте нет ответа на вопрос или он не связан с темой, ответьте строго одним словом: "ERROR_UNRESOLVED".
3. Пишите вежливо, в деловом стиле.

Контекст:
---
${context}
---
`;

  const response = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userQuestion }
    ],
    temperature: 0.0, // Нулевая температура гарантирует стабильный результат
  });

  return response.choices[0].message.content || 'ERROR_UNRESOLVED';
}

4. Логика маршрутизации и передача человеку

При получении сообщения в Telegram проверяем, не общается ли пользователь с живым оператором. Если нет — запускаем RAG-цикл. Если ИИ вернул ERROR_UNRESOLVED — переключаем статус чата.

import TelegramBot from 'node-telegram-bot-api';

const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN!, { polling: true });

bot.on('message', async (msg) => {
  const chatId = msg.chat.id;
  const userText = msg.text;

  if (!userText) return;

  // 1. Получаем статус сессии из БД
  const session = await db.getChatSession(chatId);

  // Если чат переключен на человека, пересылаем сообщения в Slack/панель менеджеров
  if (session?.status === 'human_handling') {
    await forwardToManagers(chatId, msg);
    return;
  }

  // 2. Выполняем RAG-поиск
  try {
    const context = await findRelevantContext(userText);
    const aiResponse = await generateAIResponse(userText, context);

    // 3. Проверка на эскалацию
    if (aiResponse.trim() === 'ERROR_UNRESOLVED') {
      // Обновляем статус сессии в базе данных
      await db.updateChatSession(chatId, { status: 'human_handling' });
      
      // Оповещаем менеджеров через вебхук
      await notifySupportStaff(chatId, userText);
      
      await bot.sendMessage(
        chatId, 
        "К сожалению, я не нашел ответ в документации. Я переключил этот чат на службу поддержки. Специалист ответит вам в ближайшее время."
      );
      return;
    }

    // 4. Отправляем ИИ-ответ пользователю
    await bot.sendMessage(chatId, aiResponse);

  } catch (error) {
    console.error('Ошибка в цикле RAG:', error);
    await bot.sendMessage(chatId, "Произошла ошибка. Перевожу диалог на оператора.");
    await db.updateChatSession(chatId, { status: 'human_handling' });
  }
});

В моем проекте TeleGo.io мы создали аналогичную RAG-систему для обработки частых вопросов пользователей по настройкам ботов и платежам. Если пользователь задает сложный технический вопрос, на который нет готового ответа, сессия автоматически переводится на панель оператора.

Этот процесс эскалации становится еще эффективнее, если связать его с Интеграцией Telegram-ботов с CRM для автоматического логирования событий. А саму панель общения оператора с клиентами можно удобно разместить прямо внутри Telegram Web App.

Источники и документация

  • OpenAI Embeddings — генерация векторов, модель text-embedding-3-small из примера
  • pgvector — расширение PostgreSQL для векторного поиска
  • Telegram Bot API — приём сообщений и long polling

Внедрение RAG-архитектуры с автоматическим переключением на оператора кратно снижает затраты на поддержку: типовые вопросы закрывает ИИ, а люди подключаются только к сложным диалогам — без потери качества сервиса.

Если вы хотите разработать умного ИИ-бота для техподдержки, настроить векторный поиск Pgvector или интегрировать чаты с AmoCRM/Bitrix24, ознакомьтесь с моей услугой Создания Telegram-ботов под ключ или запишитесь на Консультацию.

Частые вопросы

Чем RAG-бот отличается от «просто ChatGPT в боте»?

RAG-бот отвечает строго по вашей базе знаний: сначала находит релевантные фрагменты документации через векторный поиск, затем передаёт их модели как контекст. Это устраняет главные проблемы «голой» LLM — галлюцинации и незнание ваших цен, инструкций и API.

Что происходит, если бот не знает ответа?

Срабатывает протокол эскалации: если векторный поиск не нашёл релевантного контекста или модель вернула маркер неуверенности, сессия помечается как human_handling, менеджеры получают уведомление, а все следующие сообщения клиента пересылаются живому оператору.

Сколько стоит эксплуатация такого бота?

Две статьи расходов: генерация эмбеддингов при индексации базы знаний (разовая и копеечная) и вызовы LLM на каждый вопрос — оплата по фактическому использованию API. На типовом потоке вопросов это заметно дешевле ставки оператора поддержки.