Train Cognigy.AI Entity Extraction Models via REST APIs with Java

Train Cognigy.AI Entity Extraction Models via REST APIs with Java

What You Will Build

A Java utility that constructs, validates, and submits entity training payloads to Cognigy.AI, executes grid search cross-validation, monitors precision and recall metrics, triggers automatic checkpoints, and synchronizes training events with external version control via webhooks. This tutorial uses the Cognigy.AI REST API surface with Java 17 HttpClient and Jackson for JSON processing. The programming language covered is Java.

Prerequisites

  • Cognigy.AI API token with models:write, training:execute, and webhooks:manage scopes
  • Cognigy.AI API version v1
  • Java 17 or higher
  • External dependencies: com.fasterxml.jackson.core:jackson-databind:2.15.2, com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2
  • Base API URL format: https://{your-region}.cognigy.ai/api/v1

Authentication Setup

Cognigy.AI authenticates API requests using Bearer tokens. You must attach the token to the Authorization header on every request. The token must possess the required scopes for model modification and training execution. The following code demonstrates token injection and automatic 401 retry logic.

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class CognigyAuthClient {
    private final HttpClient httpClient;
    private final String apiToken;
    private final String baseUrl;

    public CognigyAuthClient(String baseUrl, String apiToken) {
        this.apiToken = apiToken;
        this.baseUrl = baseUrl;
        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .build();
    }

    public HttpResponse<String> executePost(String path, String jsonPayload) throws Exception {
        String url = baseUrl + path;
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", "Bearer " + apiToken)
                .header("Content-Type", "application/json")
                .header("Accept", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() == 401) {
            throw new SecurityException("Invalid or expired Cognigy.AI token. Verify models:write and training:execute scopes.");
        }
        if (response.statusCode() == 429) {
            String retryAfter = response.headers().firstValue("Retry-After").orElse("30");
            long delay = Long.parseLong(retryAfter);
            System.out.println("Rate limit hit. Waiting " + delay + " seconds.");
            TimeUnit.SECONDS.sleep(delay);
            return executePost(path, jsonPayload);
        }
        if (response.statusCode() >= 500) {
            throw new RuntimeException("Server error " + response.statusCode() + ": " + response.body());
        }
        return response;
    }
}

Implementation

Step 1: Construct and Validate Training Payloads

The training payload must contain entity references, an utterance matrix, and an optimization directive. Cognigy.AI enforces strict schema constraints and maximum dataset size limits. You must validate the payload structure and byte size before submission to prevent ML engine rejection.

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

public class PayloadBuilder {
    private static final long MAX_DATASET_BYTES = 50 * 1024 * 1024; // 50 MB limit
    private static final int MAX_UTTERANCES_PER_ENTITY = 500;
    private final ObjectMapper mapper = new ObjectMapper();

    public String buildTrainingPayload(String modelId, List<Map<String, Object>> entities,
                                       List<String> utterances, String optimizeDirective) throws Exception {
        ObjectNode payload = mapper.createObjectNode();
        payload.put("modelId", modelId);
        
        ObjectNode entitiesNode = mapper.createObjectNode();
        entitiesNode.put("count", entities.size());
        entitiesNode.set("definitions", mapper.valueToTree(entities));
        payload.set("entityReferences", entitiesNode);

        ObjectNode matrixNode = mapper.createObjectNode();
        matrixNode.put("totalUtterances", utterances.size());
        matrixNode.set("data", mapper.valueToTree(utterances));
        payload.set("utteranceMatrix", matrixNode);

        payload.put("optimizationDirective", optimizeDirective);
        payload.put("formatVersion", "1.2");

        String json = mapper.writeValueAsString(payload);
        long byteSize = json.getBytes(StandardCharsets.UTF_8).length;

        if (byteSize > MAX_DATASET_BYTES) {
            throw new IllegalArgumentException("Dataset exceeds maximum size limit of " + (MAX_DATASET_BYTES / 1024 / 1024) + " MB.");
        }
        if (utterances.size() > MAX_UTTERANCES_PER_ENTITY * entities.size()) {
            throw new IllegalArgumentException("Utterance count exceeds ML engine constraint per entity.");
        }
        if (!optimizeDirective.matches("^(accuracy|speed|balanced)$")) {
            throw new IllegalArgumentException("Invalid optimization directive. Must be accuracy, speed, or balanced.");
        }

        return json;
    }
}

Step 2: Submit Training with Cross-Validation and Hyperparameter Grid Search

Cognigy.AI supports atomic training submission with cross-validation fold generation and hyperparameter grid search. You configure these parameters directly in the POST body. The API returns a training job identifier immediately.

import com.fasterxml.jackson.databind.node.ObjectNode;

public class TrainingSubmitter {
    private final CognigyAuthClient client;
    private final ObjectMapper mapper = new ObjectMapper();

    public TrainingSubmitter(CognigyAuthClient client) {
        this.client = client;
    }

    public String submitTraining(String modelId, String basePayloadJson, int cvFolds,
                                  Map<String, List<Integer>> hyperparameterGrid, boolean autoCheckpoint) throws Exception {
        ObjectNode trainingConfig = mapper.readValue(basePayloadJson, ObjectNode.class);
        
        ObjectNode cvConfig = mapper.createObjectNode();
        cvConfig.put("folds", cvFolds);
        cvConfig.put("stratified", true);
        trainingConfig.set("crossValidation", cvConfig);

        ObjectNode gridConfig = mapper.createObjectNode();
        gridConfig.set("parameters", mapper.valueToTree(hyperparameterGrid));
        gridConfig.put("searchMethod", "exhaustive");
        trainingConfig.set("hyperparameterGrid", gridConfig);

        ObjectNode checkpointConfig = mapper.createObjectNode();
        checkpointConfig.put("enabled", autoCheckpoint);
        checkpointConfig.put("intervalEpochs", 5);
        checkpointConfig.put("saveBestOnly", true);
        trainingConfig.set("checkpointStrategy", checkpointConfig);

        String finalPayload = mapper.writeValueAsString(trainingConfig);
        
        var response = client.executePost("/models/" + modelId + "/train", finalPayload);
        ObjectNode result = mapper.readValue(response.body(), ObjectNode.class);
        
        if (!result.has("trainingId")) {
            throw new RuntimeException("Training submission failed. Response: " + response.body());
        }
        
        return result.get("trainingId").asText();
    }
}

Step 3: Monitor Precision, Recall, and Overfitting Prevention

After submission, you must poll the training status endpoint to retrieve validation metrics. Cognigy.AI returns precision, recall, and loss curves. You must verify that validation loss does not diverge from training loss to prevent overfitting.

import java.util.concurrent.TimeUnit;

public class MetricValidator {
    private final CognigyAuthClient client;
    private final ObjectMapper mapper = new ObjectMapper();

    public MetricValidator(CognigyAuthClient client) {
        this.client = client;
    }

    public boolean validateTrainingMetrics(String trainingId, double minPrecision, double minRecall, double maxLossDivergence) throws Exception {
        int maxPolls = 60;
        int pollCount = 0;

        while (pollCount < maxPolls) {
            var response = client.executeGet("/training/" + trainingId + "/status");
            ObjectNode status = mapper.readValue(response.body(), ObjectNode.class);
            String state = status.get("state").asText();

            if ("COMPLETED".equals(state)) {
                ObjectNode metrics = status.with("validationMetrics");
                double precision = metrics.get("precision").asDouble();
                double recall = metrics.get("recall").asDouble();
                double trainLoss = metrics.get("trainLoss").asDouble();
                double valLoss = metrics.get("validationLoss").asDouble();
                double lossDiff = Math.abs(trainLoss - valLoss);

                if (precision < minPrecision || recall < minRecall) {
                    throw new Exception("Model underfitting. Precision: " + precision + ", Recall: " + recall);
                }
                if (lossDiff > maxLossDivergence) {
                    throw new Exception("Overfitting detected. Loss divergence: " + lossDiff);
                }
                return true;
            }

            if ("FAILED".equals(state)) {
                throw new Exception("Training failed: " + status.get("errorMessage").asText());
            }

            TimeUnit.SECONDS.sleep(10);
            pollCount++;
        }
        throw new TimeoutException("Training did not complete within expected timeframe.");
    }
}

Step 4: Webhook Synchronization and Audit Logging

You must register a webhook to synchronize training completion events with external version control systems. The trainer also tracks latency, success rates, and generates structured audit logs for AI governance compliance.

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class TrainingOrchestrator {
    private final CognigyAuthClient client;
    private final PayloadBuilder builder;
    private final TrainingSubmitter submitter;
    private final MetricValidator validator;
    private final ObjectMapper mapper = new ObjectMapper();
    private final AtomicInteger successCount = new AtomicInteger(0);
    private final AtomicInteger failureCount = new AtomicInteger(0);
    private final List<String> auditLogs = new ArrayList<>();

    public TrainingOrchestrator(CognigyAuthClient client) {
        this.client = client;
        this.builder = new PayloadBuilder();
        this.submitter = new TrainingSubmitter(client);
        this.validator = new MetricValidator(client);
    }

    public String executeFullTraining(String modelId, List<Map<String, Object>> entities,
                                      List<String> utterances, String optimizeDirective,
                                      int cvFolds, Map<String, List<Integer>> hyperparameterGrid,
                                      String webhookUrl) throws Exception {
        Instant startTime = Instant.now();
        auditLogs.add("[" + startTime + "] Training initiated for model: " + modelId);

        // Register webhook for VCS sync
        ObjectNode webhookPayload = mapper.createObjectNode();
        webhookPayload.put("url", webhookUrl);
        webhookPayload.put("event", "model_trained");
        webhookPayload.put("active", true);
        webhookPayload.put("secret", "vcs-sync-secret-2024");
        client.executePost("/webhooks", mapper.writeValueAsString(webhookPayload));

        // Build and validate payload
        String payloadJson = builder.buildTrainingPayload(modelId, entities, utterances, optimizeDirective);
        
        // Submit training
        String trainingId = submitter.submitTraining(modelId, payloadJson, cvFolds, hyperparameterGrid, true);
        auditLogs.add("[" + Instant.now() + "] Training job created: " + trainingId);

        // Validate metrics
        boolean valid = validator.validateTrainingMetrics(trainingId, 0.85, 0.80, 0.15);
        
        Instant endTime = Instant.now();
        long latencyMs = java.time.Duration.between(startTime, endTime).toMillis();

        if (valid) {
            successCount.incrementAndGet();
            auditLogs.add("[" + endTime + "] Training completed successfully. Latency: " + latencyMs + "ms");
        } else {
            failureCount.incrementAndGet();
            auditLogs.add("[" + endTime + "] Training validation failed. Latency: " + latencyMs + "ms");
        }

        return trainingId;
    }

    public List<String> getAuditLogs() { return auditLogs; }
    public double getSuccessRate() {
        int total = successCount.get() + failureCount.get();
        return total == 0 ? 0.0 : (double) successCount.get() / total;
    }
}

Complete Working Example

The following class integrates all components into a runnable utility. Replace the placeholder credentials and configuration values with your environment details.

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.Map;

public class CognigyEntityTrainer {
    public static void main(String[] args) {
        String baseUrl = "https://us-east-1.cognigy.ai/api/v1";
        String apiToken = "your_cognigy_api_token_here";
        String modelId = "model_ent_prod_001";
        String webhookUrl = "https://your-vcs-server.com/api/webhooks/cognigy-sync";

        CognigyAuthClient authClient = new CognigyAuthClient(baseUrl, apiToken);
        TrainingOrchestrator orchestrator = new TrainingOrchestrator(authClient);

        // Entity definitions with references
        List<Map<String, Object>> entities = List.of(
            Map.of("entityId", "ent_destination", "type", "classification", "slots", List.of("city", "airport")),
            Map.of("entityId", "ent_date", "type", "temporal", "slots", List.of("departure_date"))
        );

        // Utterance matrix aligned with entity references
        List<String> utterances = List.of(
            "Book a flight to JFK on Monday",
            "I need a ticket to SFO next Friday",
            "Reserve seats to LAX for tomorrow",
            "Schedule departure to ORD on 2024-11-15"
        );

        // Hyperparameter grid search configuration
        Map<String, List<Integer>> hyperGrid = Map.of(
            "epochs", List.of(10, 20, 30),
            "batchSize", List.of(32, 64),
            "learningRateSteps", List.of(5, 10)
        );

        try {
            String trainingId = orchestrator.executeFullTraining(
                modelId, entities, utterances, "accuracy", 5, hyperGrid, webhookUrl
            );
            System.out.println("Training ID: " + trainingId);
            System.out.println("Success Rate: " + orchestrator.getSuccessRate());
            System.out.println("Audit Log:");
            orchestrator.getAuditLogs().forEach(System.out::println);
        } catch (Exception e) {
            System.err.println("Training pipeline failed: " + e.getMessage());
            System.exit(1);
        }
    }
}

Common Errors & Debugging

Error: 400 Bad Request - Schema Validation Failure

  • Cause: The payload contains invalid entity reference formats, missing utterance matrix fields, or unsupported optimization directives.
  • Fix: Verify that entityReferences contains valid entityId and type fields. Ensure utteranceMatrix is a flat list of strings matching the entity schema. Use optimizeDirective values strictly from accuracy, speed, or balanced.
  • Code showing the fix: The PayloadBuilder class enforces regex validation on optimizeDirective and checks utterance count against ML engine constraints before serialization.

Error: 429 Too Many Requests - Rate Limit Cascade

  • Cause: Cognigy.AI enforces per-tenant request limits on training endpoints. Concurrent grid search submissions trigger rate limiting.
  • Fix: Implement exponential backoff or respect the Retry-After header. The CognigyAuthClient.executePost method automatically parses Retry-After and retries the request.
  • Code showing the fix: See the 429 handling block in CognigyAuthClient.

Error: 403 Forbidden - Insufficient OAuth Scopes

  • Cause: The API token lacks models:write or training:execute permissions.
  • Fix: Regenerate the token in the Cognigy.AI admin console with the required scopes assigned to the service account.
  • Code showing the fix: The 401 handler throws a SecurityException with explicit scope requirements.

Error: Overfitting Detected - Loss Divergence Exceeds Threshold

  • Cause: Validation loss diverges from training loss by more than the configured tolerance, indicating the model memorizes training data instead of generalizing.
  • Fix: Increase cross-validation folds, reduce epochs in the hyperparameter grid, or enable early stopping via checkpointStrategy.saveBestOnly.
  • Code showing the fix: The MetricValidator.validateTrainingMetrics method calculates Math.abs(trainLoss - valLoss) and throws an exception when divergence exceeds maxLossDivergence.

Official References