Skip to content

Testing Module

Модуль автоматизованого тестування AI-агентів через LLM-діалоги з оцінкою якості.

Архітектура

mermaid
graph TB
    Client["Клієнт"]
    TestingAPI["Testing API"]
    DB["PostgreSQL"]
    LLM["LLM Provider (Groq/Claude)"]

    Client -->|CRUD + Run| TestingAPI
    TestingAPI -->|TypeORM| DB
    TestingAPI -->|Tester + Agent + Evaluator| LLM

Як це працює

Testing Module симулює діалог між двома LLM:

  1. Tester LLM — грає роль дзвінка (персона з ціллю і поведінкою)
  2. Agent LLM — агент інтеграції (використовує agentPrompt)
  3. Evaluator LLM — оцінює транскрипт по 5 критеріям (0-100 балів)

Сутності

Test Personas (Персони)

Профіль дзвінка для симуляції. Описує хто дзвонить, яку ціль переслідує, які обмеження поведінки.

sql
CREATE TABLE test_personas (
    id UUID PRIMARY KEY,
    name VARCHAR NOT NULL,
    persona TEXT NOT NULL,
    goal TEXT NOT NULL,
    constraints TEXT,
    tags TEXT[],
    example_messages JSONB,
    integration_id UUID REFERENCES integrations(id),
    owner_id UUID NOT NULL REFERENCES users(id),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
)

Test Runs (Запуски тестів)

Запуск набору персон проти агента інтеграції.

sql
CREATE TABLE test_runs (
    id UUID PRIMARY KEY,
    integration_id UUID NOT NULL REFERENCES integrations(id),
    owner_id UUID NOT NULL REFERENCES users(id),
    status VARCHAR DEFAULT 'pending',
    trigger VARCHAR DEFAULT 'manual',
    agent_prompt_snapshot TEXT,
    persona_ids JSONB,
    score_threshold INTEGER DEFAULT 70,
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    summary JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
)

Test Results (Результати)

Результат тесту кожної персони: транскрипт, оцінка, критерії.

sql
CREATE TABLE test_results (
    id UUID PRIMARY KEY,
    run_id UUID NOT NULL REFERENCES test_runs(id),
    persona_id UUID REFERENCES test_personas(id),
    status VARCHAR DEFAULT 'pending',
    score INTEGER,
    evaluation JSONB,
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    duration INTEGER,
    error TEXT,
    logs JSONB,
    transcript JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
)

Test Schedules (Розклади)

Автоматичний запуск тестів по cron-розкладу.

sql
CREATE TABLE test_schedules (
    id UUID PRIMARY KEY,
    name VARCHAR NOT NULL,
    integration_id UUID NOT NULL REFERENCES integrations(id),
    cron_expression VARCHAR NOT NULL,
    is_active BOOLEAN DEFAULT true,
    last_run_at TIMESTAMPTZ,
    next_run_at TIMESTAMPTZ,
    owner_id UUID NOT NULL REFERENCES users(id),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
)

Enums

TestStatusEnum (для результатів)

typescript
enum TestStatusEnum {
	PENDING = "pending",
	RUNNING = "running",
	PASSED = "passed",
	FAILED = "failed",
	ERROR = "error",
	CANCELLED = "cancelled"
}

TestRunStatusEnum (для запусків)

typescript
enum TestRunStatusEnum {
	PENDING = "pending",
	RUNNING = "running",
	COMPLETED = "completed",
	FAILED = "failed",
	CANCELLED = "cancelled"
}

TestRunTriggerEnum

typescript
enum TestRunTriggerEnum {
	MANUAL = "manual",
	SCHEDULED = "scheduled",
	CI_CD = "ci_cd"
}

API Endpoints

Personas /api/testing/personas

POST /api/testing/personas/generate

AI-генерація персон на основі agentPrompt інтеграції. Генерує 5-8 різноманітних персон (happy path, rude, edge case, objection).

Request:

http
POST /api/testing/personas/generate
Authorization: Bearer <token>
Content-Type: application/json

{
  "integrationId": "integration-uuid"
}

Response (201 Created):

json
{
	"data": {
		"personas": [
			{
				"id": "persona-uuid",
				"name": "Задоволений клієнт",
				"persona": "Ввічливий клієнт, який хоче записатися на прийом",
				"goal": "Успішно забронювати час",
				"constraints": "Говорити чемно, відповідати на запитання",
				"tags": ["good_flow", "booking"],
				"integrationId": "integration-uuid",
				"isActive": true
			}
		]
	}
}

GET /api/testing/personas

Список персон з пагінацією та фільтрами.

http
GET /api/testing/personas?integrationId=xxx&tags=rude
Authorization: Bearer <token>

POST /api/testing/personas

Створити персону вручну.

http
POST /api/testing/personas
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Грубий клієнт",
  "persona": "Роздратований клієнт, який хоче повернення коштів",
  "goal": "Отримати повернення коштів",
  "constraints": "Використовувати грубу мову, перебивати",
  "tags": ["rude", "refund"],
  "integrationId": "integration-uuid"
}

GET /api/testing/personas/:id

Отримати персону по ID.

PATCH /api/testing/personas/:id

Оновити персону.

DELETE /api/testing/personas/:id

Видалити персону.


Runs /api/testing/runs

POST /api/testing/runs/quick

Quick Test — один клік до результату. Автоматично генерує персони (якщо немає) і запускає тести.

Request:

http
POST /api/testing/runs/quick
Authorization: Bearer <token>
Content-Type: application/json

{
  "integrationId": "integration-uuid"
}

Response (201 Created):

json
{
	"data": {
		"id": "run-uuid",
		"integrationId": "integration-uuid",
		"status": "pending",
		"trigger": "manual",
		"personaIds": ["persona-1", "persona-2", "persona-3"],
		"scoreThreshold": 70
	}
}

POST /api/testing/runs

Запустити тести з вибраними персонами. Якщо personaIds порожній — використовує всі активні персони інтеграції.

Request:

http
POST /api/testing/runs
Authorization: Bearer <token>
Content-Type: application/json

{
  "integrationId": "integration-uuid",
  "personaIds": ["persona-1", "persona-2"],
  "scoreThreshold": 80
}

GET /api/testing/runs

Список запусків з фільтрами по integrationId та status.

http
GET /api/testing/runs?integrationId=xxx&status=completed
Authorization: Bearer <token>

GET /api/testing/runs/:id

Отримати запуск по ID.

GET /api/testing/runs/:id/results

Отримати детальні результати запуску з транскриптами та оцінками.

Response (200 OK):

json
{
	"data": {
		"id": "run-uuid",
		"integrationId": "integration-uuid",
		"status": "completed",
		"summary": {
			"total": 3,
			"passed": 2,
			"failed": 1,
			"error": 0
		},
		"results": [
			{
				"id": "result-uuid",
				"personaId": "persona-uuid",
				"status": "passed",
				"score": 85,
				"evaluation": {
					"score": 85,
					"passed": true,
					"criteria": [
						{ "name": "Goal Achievement", "score": 90, "comment": "..." },
						{ "name": "Persona Handling", "score": 80, "comment": "..." },
						{ "name": "Tone & Professionalism", "score": 85, "comment": "..." },
						{ "name": "Information Accuracy", "score": 88, "comment": "..." },
						{ "name": "Conversation Flow", "score": 82, "comment": "..." }
					],
					"reasoning": "...",
					"suggestions": ["..."]
				},
				"transcript": [
					{ "role": "tester", "content": "Привіт, я хочу записатися", "timestamp": "..." },
					{ "role": "agent", "content": "Вітаю! Підкажіть, на який день?", "timestamp": "..." }
				],
				"duration": 15000
			}
		]
	}
}

POST /api/testing/runs/:id/cancel

Скасувати запуск (через AbortController).

DELETE /api/testing/runs/:id

Видалити запуск та всі пов'язані результати.


Schedules /api/testing/schedules

GET /api/testing/schedules

Список розкладів з фільтром по integrationId.

POST /api/testing/schedules

Створити розклад. Автоматично реєструє cron-джоб.

http
POST /api/testing/schedules
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Daily Regression",
  "integrationId": "integration-uuid",
  "cronExpression": "0 9 * * *"
}

PATCH /api/testing/schedules/:id

Оновити розклад. Автоматично оновлює cron-джоб.

DELETE /api/testing/schedules/:id

Видалити розклад. Автоматично зупиняє cron-джоб.

POST /api/testing/schedules/:id/toggle

Увімкнути/вимкнути розклад.


Chats (Інтерактивне тестування)

Ручне тестування агента через чат.

POST   /api/integrations/:integrationId/chats     # Створити чат
GET    /api/integrations/:integrationId/chats     # Список чатів
GET    /api/chats/:chatId                         # Отримати чат
DELETE /api/chats/:chatId                         # Видалити чат
GET    /api/chats/:chatId/messages                # Повідомлення чату
POST   /api/chats/:chatId/messages                # Надіслати повідомлення

Environment Variables

bash
# LLM Provider (для діалогів та оцінки)
GROQ_API_KEY=<your-groq-api-key>
# або
ANTHROPIC_API_KEY=<your-anthropic-api-key>

Повний приклад

typescript
import axios from "axios";

const api = axios.create({
	baseURL: "http://localhost:3000/api",
	headers: { Authorization: `Bearer ${token}` }
});

async function testIntegration() {
	// Варіант 1: Quick Test (один клік)
	const { data: run } = await api.post("/testing/runs/quick", {
		integrationId: "my-integration-uuid"
	});
	console.log("Test run started:", run.data.id);

	// Варіант 2: Покроково
	// 2a. Згенерувати персони
	await api.post("/testing/personas/generate", {
		integrationId: "my-integration-uuid"
	});

	// 2b. Запустити тести
	const { data: customRun } = await api.post("/testing/runs", {
		integrationId: "my-integration-uuid",
		scoreThreshold: 80
	});

	// 3. Дочекатися завершення та отримати результати
	let status = "pending";
	while (status === "pending" || status === "running") {
		await new Promise((resolve) => setTimeout(resolve, 5000));
		const { data: updated } = await api.get(`/testing/runs/${customRun.data.id}`);
		status = updated.data.status;
	}

	// 4. Отримати детальні результати
	const { data: results } = await api.get(`/testing/runs/${customRun.data.id}/results`);
	console.log("Results:", results.data);

	// 5. Створити розклад для регресії
	await api.post("/testing/schedules", {
		name: "Daily Regression",
		integrationId: "my-integration-uuid",
		cronExpression: "0 9 * * *"
	});
}

Додатково