Testing Module
Модуль автоматизованого тестування AI-агентів через LLM-діалоги з оцінкою якості.
Архітектура
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:
- Tester LLM — грає роль дзвінка (персона з ціллю і поведінкою)
- Agent LLM — агент інтеграції (використовує
agentPrompt) - Evaluator LLM — оцінює транскрипт по 5 критеріям (0-100 балів)
Сутності
Test Personas (Персони)
Профіль дзвінка для симуляції. Описує хто дзвонить, яку ціль переслідує, які обмеження поведінки.
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 (Запуски тестів)
Запуск набору персон проти агента інтеграції.
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 (Результати)
Результат тесту кожної персони: транскрипт, оцінка, критерії.
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-розкладу.
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 (для результатів)
enum TestStatusEnum {
PENDING = "pending",
RUNNING = "running",
PASSED = "passed",
FAILED = "failed",
ERROR = "error",
CANCELLED = "cancelled"
}TestRunStatusEnum (для запусків)
enum TestRunStatusEnum {
PENDING = "pending",
RUNNING = "running",
COMPLETED = "completed",
FAILED = "failed",
CANCELLED = "cancelled"
}TestRunTriggerEnum
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:
POST /api/testing/personas/generate
Authorization: Bearer <token>
Content-Type: application/json
{
"integrationId": "integration-uuid"
}Response (201 Created):
{
"data": {
"personas": [
{
"id": "persona-uuid",
"name": "Задоволений клієнт",
"persona": "Ввічливий клієнт, який хоче записатися на прийом",
"goal": "Успішно забронювати час",
"constraints": "Говорити чемно, відповідати на запитання",
"tags": ["good_flow", "booking"],
"integrationId": "integration-uuid",
"isActive": true
}
]
}
}GET /api/testing/personas
Список персон з пагінацією та фільтрами.
GET /api/testing/personas?integrationId=xxx&tags=rude
Authorization: Bearer <token>POST /api/testing/personas
Створити персону вручну.
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:
POST /api/testing/runs/quick
Authorization: Bearer <token>
Content-Type: application/json
{
"integrationId": "integration-uuid"
}Response (201 Created):
{
"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:
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.
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):
{
"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-джоб.
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
# LLM Provider (для діалогів та оцінки)
GROQ_API_KEY=<your-groq-api-key>
# або
ANTHROPIC_API_KEY=<your-anthropic-api-key>Повний приклад
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 * * *"
});
}Додатково
- Integrations - Управління інтеграціями
- Secrets API - Секрети для інтеграцій
- Authentication - Автентифікація в API