вот код
// server.js
const express = require('express');
const tf = require('@tensorflow/tfjs-node');
const cors = require('cors');
const bodyParser = require('body-parser');
const path = require('path');

const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(cors());
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public')));

// Данные для обучения (20 пар вопрос-ответ)
const data = [
  {
    "question": "Что такое искусственный интеллект?",
    "answer": "Искусственный интеллект – раздел информатики, посвященный созданию систем, способных выполнять задачи, требующие человеческого интеллекта."
  },
  // ... (оставьте остальные 19 пар вопрос-ответ аналогично)
  {
    "question": "Что такое Великая Китайская стена?",
    "answer": "Великая Китайская стена – серия укреплений, построенных для защиты Китая от нашествий."
  }
];

// Глобальные переменные для модели и токенизатора
let model;
let tokenizer;
const sequenceLength = 20; // Максимальная длина последовательности

/**
 * Функция для очистки текста от пунктуации и приведения к нижнему регистру
 * @param {string} text 
 * @returns {string}
 */
function cleanText(text) {
   return text.replace(/[.,\\/#!$%\\^&\\*;:{}=\\-_`~()?"«»]/g, '').toLowerCase();
}

/**
 * Предобработка данных: токенизация и создание словаря
 */
async function preprocessData() {
  console.log('Подготовка данных...');

  // Объединяем все вопросы и ответы для построения словаря
  const texts = data.flatMap(item => [item.question, item.answer]);

  // Очистка текстов и разбивка на слова
  const cleanedTexts = texts.map(cleanText);
  const wordList = cleanedTexts.flatMap(text => text.split(/\s+/));

  // Создаем уникальный словарь
  const vocab = Array.from(new Set(wordList));

  // Индексирование слов (начиная с 1, 0 зарезервирован для паддинга)
  const wordIndex = {};
  vocab.forEach((word, index) => {
    wordIndex[word] = index + 1;
  });

  // Обратный словарь для декодирования
  const indexWord = {};
  Object.entries(wordIndex).forEach(([word, index]) => {
    indexWord[index] = word;
  });

  // Функция преобразования текста в последовательность индексов
  const textsToSequences = (texts) => {
    return texts.map(text => {
      return cleanText(text).split(/\s+/).map(word => wordIndex[word] || 0);
    });
  };

  tokenizer = {
    wordIndex,
    indexWord,
    textsToSequences
  };
}

/**
 * Создание модели LSTM
 * @param {number} vocabSize - Размер словаря
 */
function createModel(vocabSize) {
  console.log('Создание модели...');

  model = tf.sequential();

  // Слой Embedding для преобразования индексов слов в векторные представления
  model.add(tf.layers.embedding({
    inputDim: vocabSize,
    outputDim: 128,
    inputLength: sequenceLength
  }));

  // Cлой LSTM для обработки последовательностей
  model.add(tf.layers.lstm({
    units: 256,
    returnSequences: false
  }));

  // Полносвязный слой с softmax активацией для классификации
  model.add(tf.layers.dense({
    units: vocabSize,
    activation: 'softmax'
  }));

  // Компиляция модели с использованием функции потерь и оптимизатора
  model.compile({
    loss: 'sparseCategoricalCrossentropy',
    optimizer: tf.train.adam(),
    metrics: ['accuracy']
  });
}

/**
 * Добавление паддинга к последовательности
 * @param {Array<number>} seq - Последовательность индексов
 * @param {number} length - Максимальная длина последовательности
 * @returns {Array<number>} - Паддированная последовательность
 */
function padSequence(seq, length) {
  if (seq.length > length) {
    return seq.slice(seq.length - length); // Обрезаем в начале
  } else if (seq.length < length) {
    return Array(length - seq.length).fill(0).concat(seq); // Добавляем паддинг в начале
  } else {
    return seq;
  }
}

/**
 * Обучение модели на данных
 */
async function trainModel() {
  console.log('Обучение модели...');

  // Извлекаем вопросы и ответы из данных
  const questions = data.map(item => item.question);
  const answers = data.map(item => item.answer);

  // Преобразуем тексты в последовательности индексов
  const questionSequences = tokenizer.textsToSequences(questions);
  const answerSequences = tokenizer.textsToSequences(answers);

  // Преобразуем ответы в классы (каждый уникальный ответ - отдельный класс)
  const uniqueAnswers = Array.from(new Set(answers));
  const answerToClass = {};
  uniqueAnswers.forEach((ans, idx) => {
    answerToClass[ans] = idx;
  });
  const classLabels = answers.map(ans => answerToClass[ans]);

  // Преобразуем вопросы в тензоры
  const xs = tf.tensor2d(questionSequences.map(seq => padSequence(seq, sequenceLength)));

  // Преобразуем метки в тензоры
  const ys = tf.tensor1d(classLabels, 'int32');

  // Обучаем модель
  await model.fit(xs, ys, {
    epochs: 200, // Увеличено количество эпох для лучшего обучения
    batchSize: 4,
    shuffle: true,
    callbacks: {
      onEpochEnd: (epoch, logs) => {
        if (epoch % 10 === 0) { // Логирование каждые 10 эпох
          console.log(`Эпоха ${epoch + 1}/200, Потеря: ${logs.loss.toFixed(4)}, Точность: ${(logs.acc * 100).toFixed(2)}%`);
        }
      }
    }
  });

  // Освобождаем память
  xs.dispose();
  ys.dispose();

  console.log('Обучение завершено.');
}

/**
 * Генерация ответа на заданный вопрос
 * @param {string} question 
 * @returns {string} - Сгенерированный ответ
 */
async function generateAnswer(question) {
  if (!model || !tokenizer) {
    throw new Error('Модель не загружена.');
  }

  // Преобразуем вопрос в последовательность индексов
  const questionSequence = tokenizer.textsToSequences([question])[0];
  const paddedQuestion = padSequence(questionSequence, sequenceLength);
  const inputTensor = tf.tensor2d([paddedQuestion]);

  // Делаем предсказание
  const prediction = model.predict(inputTensor);
  const predictedClass = (await prediction.argMax(-1).data())[0];

  // Получаем ответ по предсказанному классу
  const uniqueAnswers = data.map(item => item.answer);
  const answer = uniqueAnswers[predictedClass] || 'Извините, ответ не найден.';

  // Освобождаем память
  inputTensor.dispose();
  prediction.dispose();

  return answer;
}

// Инициализация и обучение модели при запуске сервера
(async () => {
  await preprocessData();
  const vocabSize = Object.keys(tokenizer.wordIndex).length + 1; // +1 для паддинга
  createModel(vocabSize);
  await trainModel();
})();

// Маршрут для получения ответа
app.post('/api/ask', async (req, res) => {
  const question = req.body.question;
  if (!question) {
    return res.status(400).json({ error: 'Вопрос не предоставлен.' });
  }

  try {
    const answer = await generateAnswer(question);
    res.json({ answer });
  } catch (error) {
    console.error('Ошибка при генерации ответа:', error);
    res.status(500).json({ error: 'Ошибка при генерации ответа.' });
  }
});

// Запуск сервера
app.listen(PORT, () => {
  console.log(`Сервер запущен на порту ${PORT}`);
});
вот ошибка D:\qa-nodejs>node server.js
node:internal/modules/cjs/loader:1340
  return process.dlopen(module, path.toNamespacedPath(filename));
                 ^

Error: The specified module could not be found.
\\?\D:\qa-nodejs\node_modules\@tensorflow\tfjs-node\lib\napi-v8\tfjs_binding.node
    at Module._extensions..node (node:internal/modules/cjs/loader:1340:18)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12)
    at Module.require (node:internal/modules/cjs/loader:1143:19)
    at require (node:internal/modules/cjs/helpers:119:18)
    at Object.<anonymous> (D:\qa-nodejs\node_modules\@tensorflow\tfjs-node\dist\index.js:72:16)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12) {
  code: 'ERR_DLOPEN_FAILED'
}