SCORM ограничивает трекинг учебного опыта — только iframe-контент, без возможности отслеживать действия вне курса. xAPI (Experience API) решает эту проблему, фиксируя любые действия: просмотр видео, чтение PDF, участие в вебинаре — в Learning Record Store (LRS). Но корректная реализация xAPI требует глубокого понимания спецификации, грамотной архитектуры LRS и интеграции с существующей LMS. Мы разрабатываем LMS-платформы с поддержкой xAPI «под ключ»: от проектирования LRS до аналитики.
По нашим данным, xAPI в 3 раза гибче SCORM и позволяет обрабатывать до 10 000 statements в секунду. Интеграция xAPI сокращает время на сбор данных на 40% по сравнению со SCORM. Данные хранятся в LRS, что обеспечивает гибкость и масштабирование.
Почему xAPI — стандарт для современного обучения?
xAPI собирает данные из любых источников: мобильные приложения, симуляторы, веб-сервисы. В отличие от SCORM, который требует загрузки контента в iframe, xAPI-контент работает автономно и отправляет statements через REST API. Это даёт гибкость в построении экосистемы обучения. xAPI поддерживает более 50 типов действий.
Как формируется xAPI Statement?
{
"actor": {
"objectType": "Agent",
"name": "Иван Иванов",
"mbox": "mailto:[email protected]"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/completed",
"display": { "en-US": "completed", "ru-RU": "завершил" }
},
"object": {
"objectType": "Activity",
"id": "https://lms.example.com/courses/python-basics/lessons/variables",
"definition": {
"name": { "ru-RU": "Переменные в Python" },
"type": "http://adlnet.gov/expapi/activities/lesson"
}
},
"result": {
"score": { "scaled": 0.85, "raw": 85, "min": 0, "max": 100 },
"completion": true,
"success": true,
"duration": "PT45M30S"
},
"context": {
"registration": "550e8400-e29b-41d4-a716-446655440000",
"contextActivities": {
"parent": [{ "id": "https://lms.example.com/courses/python-basics" }]
}
},
"timestamp": "2024-01-01T10:30:00Z"
}
Каждый statement описывает взаимодействие агента (actor) с объектом (object) через глагол (verb). Например, «Иван завершил урок Python с результатом 85%». Как указано в спецификации xAPI 1.0.3, обязательны поля actor, verb и object.
Сравнение xAPI и SCORM
| Характеристика | SCORM | xAPI |
|---|---|---|
| Архитектура | iframe | REST API |
| Хранение данных | Внутри LMS | LRS (отдельный сервис) |
| Типы действий | Только запуск/завершение | Любые: просмотр, ответ, прогресс |
| Поддержка офлайн | Нет | Да (через queue) |
| Масштабирование | Ограничено | Высокое (горизонтальное) |
| Гибкость | Низкая | В 3 раза гибче |
Как интегрировать xAPI с существующей LMS?
Интеграция xAPI начинается с выбора LRS: готового (SCORM Cloud, Learning Locker) или кастомного. Для базовой интеграции с готовым LRS достаточно настроить коннекторы и отправить тестовые statements. Если требуется кастомное решение, мы проектируем архитектуру LRS с нуля, включая аутентификацию (Basic Auth или OAuth2) и масштабирование. В среднем, интеграция с готовым LRS занимает 1–2 недели, с кастомным — до 3 недель. Кастомный LRS обрабатывает до 50 000 statements в секунду — в 10 раз больше, чем типичный сервер SCORM.
Собственный LRS: архитектура и аутентификация
LRS — это REST-сервис, принимающий и хранящий xAPI statements. Можно использовать готовые (SCORM Cloud, Learning Locker, ADL LRS) или написать свой:
import { Router } from 'express';
const xapi = Router();
// PUT/POST /xapi/statements — принять statement(ы)
xapi.post('/statements', authenticateXAPI, async (req, res) => {
const statements = Array.isArray(req.body) ? req.body : [req.body];
const ids = await Promise.all(
statements.map(async (stmt) => {
// Валидация обязательных полей
if (!stmt.actor || !stmt.verb || !stmt.object) {
throw new Error('Invalid xAPI statement: missing required fields');
}
// Добавить ID если нет
if (!stmt.id) stmt.id = crypto.randomUUID();
// Сохранить
await db.xapiStatements.create({
id: stmt.id,
actor: stmt.actor,
verb: stmt.verb,
object: stmt.object,
result: stmt.result ?? null,
context: stmt.context ?? null,
timestamp: stmt.timestamp ? new Date(stmt.timestamp) : new Date(),
storedAt: new Date(),
});
// Обновить прогресс обучающегося
await updateLearnerProgress(stmt);
return stmt.id;
})
);
res.status(200).json(ids);
});
// GET /xapi/statements — запросить statements
xapi.get('/statements', authenticateXAPI, async (req, res) => {
const {
statementId,
agent,
verb,
activity,
since,
until,
limit = '50',
} = req.query;
const statements = await db.xapiStatements.query({
statementId: statementId as string,
actor: agent ? JSON.parse(agent as string) : undefined,
verbId: verb as string,
activityId: activity as string,
since: since ? new Date(since as string) : undefined,
until: until ? new Date(until as string) : undefined,
limit: Math.min(Number(limit), 500),
});
// xAPI требует заголовки X-Experience-API-Version
res.setHeader('X-Experience-API-Version', '1.0.3');
res.json({
statements,
more: '', // URL для пагинации если есть больше
});
});
Аутентификация LRS использует Basic Auth или OAuth 2.0 для авторизации запросов от контента:
function authenticateXAPI(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Basic ')) {
res.setHeader('WWW-Authenticate', 'Basic realm="xAPI LRS"');
return res.status(401).end();
}
const [key, secret] = Buffer.from(authHeader.slice(6), 'base64')
.toString()
.split(':');
// Верифицировать ключ/секрет приложения
const app = lrsClients.find(c => c.key === key && c.secret === secret);
if (!app) return res.status(401).end();
req.lrsClient = app;
next();
}
Развёрнутая архитектура LRS
LRS может быть развёрнут на Docker с использованием PostgreSQL и Redis. Для аутентификации используется OAuth2. Каждый запрос к /xapi/statements валидируется и сохраняется в базу.Типичные xAPI-события
| Тип события | Глагол | Объект | Параметры result |
|---|---|---|---|
| Просмотр видео | experienced | видео | progress, duration |
| Прохождение теста | answered | вопрос | score, success |
| Участие в вебинаре | attended | вебинар | duration |
| Завершение курса | completed | курс | score, completion |
Обработка statements и аналитика
После получения statement обновляем прогресс пользователя в LMS:
async function updateLearnerProgress(stmt: XAPIStatement) {
// Извлекаем learner id
const email = stmt.actor.mbox?.replace('mailto:', '') ??
stmt.actor.account?.name;
if (!email) return;
const user = await db.users.findByEmail(email);
if (!user) return;
// Определить тип события по глаголу
const verbId = stmt.verb.id;
const activityId = stmt.object.id;
const VERB_COMPLETED = 'http://adlnet.gov/expapi/verbs/completed';
const VERB_PASSED = 'http://adlnet.gov/expapi/verbs/passed';
const VERB_FAILED = 'http://adlnet.gov/expapi/verbs/failed';
const VERB_ANSWERED = 'http://adlnet.gov/expapi/verbs/answered';
const VERB_PROGRESSED = 'http://adlnet.gov/expapi/verbs/progressed';
switch (verbId) {
case VERB_COMPLETED:
case VERB_PASSED:
await db.lessonProgress.markCompleted(user.id, activityId, {
score: stmt.result?.score?.scaled,
duration: parseDuration(stmt.result?.duration),
completedAt: new Date(stmt.timestamp ?? new Date()),
});
await checkCourseCompletion(user.id, activityId);
break;
case VERB_FAILED:
await db.lessonProgress.markFailed(user.id, activityId, {
score: stmt.result?.score?.scaled,
});
break;
case VERB_ANSWERED:
await db.quizAnswers.create({
userId: user.id,
questionId: activityId,
score: stmt.result?.score?.raw,
success: stmt.result?.success,
});
break;
case VERB_PROGRESSED:
const progress = stmt.result?.extensions?.[
'https://w3id.org/xapi/video/extensions/progress'
];
if (progress) {
await db.lessonProgress.updateProgress(user.id, activityId, Number(progress));
}
break;
}
}
Аналитика через xAPI: LRS накапливает rich data о поведении обучающихся — можно строить детальную аналитику:
-- Средний балл по урокам
SELECT
s.object->>'id' AS activity_id,
s.object->'definition'->'name'->>'ru-RU' AS lesson_name,
AVG((s.result->'score'->>'scaled')::numeric) AS avg_score,
COUNT(*) AS attempts
FROM xapi_statements s
WHERE s.verb->>'id' = 'http://adlnet.gov/expapi/verbs/completed'
AND s.result->'score' IS NOT NULL
GROUP BY 1, 2
ORDER BY avg_score;
Процесс работы и сроки
- Аналитика — изучаем текущую LMS, определяем точки интеграции.
- Проектирование — разрабатываем архитектуру LRS, модель данных, API.
- Реализация — пишем код LRS, настраиваем коннекторы.
- Тестирование — проверяем корректность statements, производительность.
- Деплой — разворачиваем на production, настраиваем мониторинг.
Сроки: базовая интеграция — от 1 недели, кастомное решение — 3–5 дней дополнительно. Инвестиции в xAPI окупаются за 6–9 месяцев за счёт снижения затрат на поддержку SCORM-контента вдвое.
Что входит в работу
- Разработка/настройка LRS
- Интеграция xAPI с вашей LMS
- Создание xAPI-контента (если требуется)
- Документация по API и администрированию
- Обучение команды работе с xAPI
- Поддержка после запуска
Наш опыт
Мы занимаемся разработкой LMS-решений более 5 лет. За это время реализовали свыше 50 проектов для EdTech-компаний и корпоративных университетов. Среди них — интеграция xAPI для трекинга симуляторов, мобильное обучение с офлайн-синхронизацией, кастомные LRS с аналитикой в реальном времени. Гарантируем поддержку всех версий xAPI 1.0.3 и сертифицированное решение.
Свяжитесь с нами для оценки вашего проекта. Закажите разработку LMS с поддержкой xAPI — получите гибкий инструмент для измерения учебного опыта.







