Updating NICE Cognigy.AI Intent Patterns via API with Python

Updating NICE Cognigy.AI Intent Patterns via API with Python

What You Will Build

  • This script updates Cognigy.AI intent patterns, validates complexity constraints, triggers atomic retraining, verifies model performance, and deploys to NICE CXone.
  • It uses the Cognigy.AI v1 REST API for intent management, retraining, and project deployment.
  • The implementation is written in Python 3.10 using requests and pydantic for schema validation and retry logic.

Prerequisites

  • Cognigy.AI tenant with API access enabled
  • OAuth2 client credentials or API key with scopes: intent:write, retrain:trigger, project:deploy, webhook:read
  • Python 3.10+ runtime
  • External dependencies: pip install requests pydantic typing-extensions
  • Target Cognigy.AI API version: v1

Authentication Setup

Cognigy.AI authenticates API requests using Bearer tokens issued via the tenant authentication endpoint. The token must be attached to every subsequent request. Cache the token and refresh before expiration to avoid 401 interruptions.

import requests
from typing import Optional
import time

class CognigyAuth:
    def __init__(self, base_url: str, api_key: str, api_secret: str):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.api_secret = api_secret
        self.token: Optional[str] = None
        self.token_expiry: float = 0.0

    def get_token(self) -> str:
        if self.token and time.time() < self.token_expiry - 30:
            return self.token

        url = f"{self.base_url}/api/v1/auth/token"
        payload = {
            "grant_type": "client_credentials",
            "client_id": self.api_key,
            "client_secret": self.api_secret,
            "scope": "intent:write retrain:trigger project:deploy webhook:read"
        }

        headers = {"Content-Type": "application/json"}
        response = requests.post(url, json=payload, headers=headers, timeout=10)
        response.raise_for_status()

        data = response.json()
        self.token = data["access_token"]
        self.token_expiry = time.time() + data["expires_in"] - 60
        return self.token

Implementation

Step 1: Initialize Client and Validate Constraints

The Cognigy.AI API enforces intent complexity limits and structural constraints. Validate the incoming payload against intent_constraints and maximum_pattern_complexity before sending it to the platform. This prevents 400 Bad Request failures caused by overly dense patterns or unsupported entity mappings.

from pydantic import BaseModel, Field, field_validator
from typing import List, Dict, Any

class IntentPatternPayload(BaseModel):
    pattern_ref: str
    intent_matrix: Dict[str, Any]
    retrain_directive: str
    intent_constraints: Dict[str, Any]
    maximum_pattern_complexity: int = Field(le=500, ge=1)
    patterns: List[str]

    @field_validator("retrain_directive")
    @classmethod
    def validate_directive(cls, v: str) -> str:
        allowed = {"full", "incremental", "skip"}
        if v not in allowed:
            raise ValueError(f"retrain_directive must be one of {allowed}")
        return v

    @field_validator("patterns")
    @classmethod
    def validate_pattern_count(cls, v: List[str]) -> List[str]:
        if len(v) > 100:
            raise ValueError("Intent patterns exceed maximum allowed count of 100")
        return v

The validation step ensures the payload matches Cognigy.AI schema expectations. The maximum_pattern_complexity field caps token density, and the retrain_directive restricts training behavior to supported modes.

Step 2: Construct Payload and Evaluate Confidence Thresholds

Entity extraction accuracy depends on pattern density and entity overlap. Calculate an expected confidence threshold before submission. This step evaluates token distribution and entity annotation frequency to predict classification stability.

import re
import math

def calculate_confidence_threshold(patterns: List[str], entity_annotations: Dict[str, int]) -> float:
    total_tokens = sum(len(re.split(r"\s+", p)) for p in patterns)
    total_entities = sum(entity_annotations.values())
    entity_density = total_entities / max(total_tokens, 1)
    
    base_confidence = 0.85
    density_penalty = min(entity_density * 0.1, 0.15)
    threshold = base_confidence - density_penalty
    return round(max(threshold, 0.65), 3)

def build_payload(
    pattern_ref: str,
    patterns: List[str],
    entity_annotations: Dict[str, int],
    retrain_directive: str = "incremental"
) -> Dict[str, Any]:
    threshold = calculate_confidence_threshold(patterns, entity_annotations)
    
    payload_dict = {
        "pattern_ref": pattern_ref,
        "intent_matrix": {
            "confidence_threshold": threshold,
            "entity_extraction_enabled": True,
            "fallback_strategy": "semantic_similarity"
        },
        "retrain_directive": retrain_directive,
        "intent_constraints": {
            "max_response_time_ms": 150,
            "min_confidence_score": threshold,
            "allow_overlap": False
        },
        "maximum_pattern_complexity": min(sum(len(p.split()) for p in patterns), 500),
        "patterns": patterns
    }
    
    # Validate against Pydantic model
    return IntentPatternPayload(**payload_dict).model_dump()

The confidence threshold calculation adjusts based on entity density. High entity density reduces the threshold to prevent false negatives during classification. The intent_matrix configures fallback behavior and extraction toggles.

Step 3: Execute Atomic PUT and Verify Format

Send the validated payload to the Cognigy.AI intent endpoint using an atomic HTTP PUT operation. Verify the response schema matches the expected structure. Implement exponential backoff for 429 rate limit responses.

import logging
from functools import wraps

logger = logging.getLogger("cognigy_updater")

def retry_on_rate_limit(max_retries: int = 3, base_delay: float = 1.0):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            delay = base_delay
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except requests.exceptions.HTTPError as e:
                    if e.response.status_code == 429 and attempt < max_retries:
                        logger.warning(f"Rate limited on attempt {attempt + 1}. Retrying in {delay}s")
                        time.sleep(delay)
                        delay *= 2
                    else:
                        raise
        return wrapper
    return decorator

class CognigyIntentUpdater:
    def __init__(self, auth: CognigyAuth, intent_id: str, project_id: str):
        self.auth = auth
        self.intent_id = intent_id
        self.project_id = project_id
        self.base_url = auth.base_url
        self.session = requests.Session()
        self.session.headers.update({
            "Content-Type": "application/json",
            "Accept": "application/json"
        })

    @retry_on_rate_limit(max_retries=3)
    def update_intent(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        url = f"{self.base_url}/api/v1/intents/{self.intent_id}"
        headers = {"Authorization": f"Bearer {self.auth.get_token()}"}
        
        logger.info(f"PUT {url}")
        logger.debug(f"Request Body: {payload}")
        
        response = self.session.put(url, json=payload, headers=headers, timeout=30)
        
        if response.status_code == 401:
            raise PermissionError("Invalid or expired authentication token")
        if response.status_code == 403:
            raise PermissionError("Insufficient scopes: require intent:write")
        if response.status_code == 404:
            raise ValueError(f"Intent {self.intent_id} not found")
            
        response.raise_for_status()
        
        # Format verification
        data = response.json()
        required_keys = {"id", "pattern_ref", "status", "updated_at"}
        if not required_keys.issubset(data.keys()):
            raise ValueError("Response schema mismatch: missing required fields")
            
        logger.info(f"Intent updated successfully. Status: {data['status']}")
        return data

The PUT operation targets /api/v1/intents/{intent_id}. The retry decorator handles 429 responses with exponential backoff. Format verification ensures the platform returned a valid intent object before proceeding.

Step 4: Run Overlap and Degradation Validation Pipelines

Before triggering retraining, validate the updated patterns against overlapping intent definitions and simulate model degradation metrics. This prevents classification collisions during CXone scaling.

def check_overlapping_patterns(new_patterns: List[str], existing_patterns: List[str]) -> List[str]:
    overlaps = []
    new_tokens = [set(p.lower().split()) for p in new_patterns]
    existing_tokens = [set(p.lower().split()) for p in existing_patterns]
    
    for i, new_set in enumerate(new_tokens):
        for j, existing_set in enumerate(existing_tokens):
            intersection = new_set.intersection(existing_set)
            if len(intersection) > 0 and len(intersection) / len(new_set) > 0.6:
                overlaps.append(f"Pattern {i} overlaps 60%+ with existing pattern {j}: {intersection}")
    return overlaps

def verify_model_degradation(
    baseline_confidence: float,
    updated_confidence: float,
    threshold: float = 0.05
) -> bool:
    degradation = baseline_confidence - updated_confidence
    is_degraded = degradation > threshold
    if is_degraded:
        logger.warning(f"Model degradation detected: confidence dropped by {degradation:.3f}")
    return not is_degraded

The overlap check compares token intersections between new and existing patterns. A 60% intersection triggers a warning to prevent intent confusion. The degradation verification compares baseline and updated confidence scores. A drop exceeding 0.05 flags the update for review.

Step 5: Trigger Retrain, Deploy, and Sync Webhooks

After validation, trigger the retrain directive, wait for completion, deploy to the target project, and synchronize with an external NLU monitor via webhook callbacks. Track latency and success rates for governance.

import json
from datetime import datetime, timezone

class CognigyIntentUpdater:
    # ... previous methods ...

    @retry_on_rate_limit(max_retries=3)
    def trigger_retrain(self, directive: str) -> Dict[str, Any]:
        url = f"{self.base_url}/api/v1/intents/{self.intent_id}/retrain"
        headers = {"Authorization": f"Bearer {self.auth.get_token()}"}
        payload = {"directive": directive}
        
        logger.info(f"POST {url}")
        response = self.session.post(url, json=payload, headers=headers, timeout=60)
        response.raise_for_status()
        return response.json()

    @retry_on_rate_limit(max_retries=3)
    def deploy_project(self) -> Dict[str, Any]:
        url = f"{self.base_url}/api/v1/projects/{self.project_id}/deploy"
        headers = {"Authorization": f"Bearer {self.auth.get_token()}"}
        
        logger.info(f"POST {url}")
        response = self.session.post(url, json={"force": False}, headers=headers, timeout=60)
        response.raise_for_status()
        return response.json()

    def sync_external_monitor(self, event_data: Dict[str, Any]) -> None:
        # Simulate webhook payload generation for external NLU monitor
        webhook_payload = {
            "event_type": "intent_updated",
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "intent_id": self.intent_id,
            "metrics": event_data,
            "status": "deployed"
        }
        
        # In production, POST to your external monitor endpoint
        logger.info(f"Webhook sync payload generated: {json.dumps(webhook_payload, indent=2)}")
        # requests.post(EXTERNAL_MONITOR_URL, json=webhook_payload, timeout=10)

    def execute_full_pipeline(self, payload: Dict[str, Any], existing_patterns: List[str], baseline_confidence: float) -> Dict[str, Any]:
        start_time = time.time()
        audit_log = {
            "intent_id": self.intent_id,
            "project_id": self.project_id,
            "start_time": datetime.now(timezone.utc).isoformat(),
            "steps": [],
            "success": False
        }

        try:
            # Step 1: Validate overlaps
            overlaps = check_overlapping_patterns(payload["patterns"], existing_patterns)
            if overlaps:
                raise ValueError(f"Overlap validation failed: {overlaps}")
            
            # Step 2: Update intent
            update_result = self.update_intent(payload)
            audit_log["steps"].append({"step": "update_intent", "status": "success"})

            # Step 3: Verify degradation
            updated_confidence = payload["intent_matrix"]["confidence_threshold"]
            if not verify_model_degradation(baseline_confidence, updated_confidence):
                raise RuntimeError("Model degradation threshold exceeded")
            audit_log["steps"].append({"step": "degradation_check", "status": "success"})

            # Step 4: Trigger retrain
            retrain_result = self.trigger_retrain(payload["retrain_directive"])
            audit_log["steps"].append({"step": "retrain", "status": "success", "job_id": retrain_result.get("job_id")})

            # Step 5: Deploy
            deploy_result = self.deploy_project()
            audit_log["steps"].append({"step": "deploy", "status": "success", "version": deploy_result.get("version")})

            latency = time.time() - start_time
            audit_log["latency_ms"] = round(latency * 1000, 2)
            audit_log["success"] = True
            audit_log["end_time"] = datetime.now(timezone.utc).isoformat()

            # Step 6: Sync webhook
            self.sync_external_monitor({
                "latency_ms": audit_log["latency_ms"],
                "retrain_job": retrain_result.get("job_id"),
                "deploy_version": deploy_result.get("version")
            })

            logger.info(f"Pipeline completed in {audit_log['latency_ms']}ms")
            return audit_log

        except Exception as e:
            audit_log["error"] = str(e)
            audit_log["end_time"] = datetime.now(timezone.utc).isoformat()
            logger.error(f"Pipeline failed: {e}")
            raise

The pipeline executes sequentially: overlap validation, atomic PUT, degradation check, retrain trigger, deployment, and webhook sync. Latency and success rates are captured in the audit log for governance reporting.

Complete Working Example

import logging
import time
import requests
from typing import List, Dict, Any, Optional
from pydantic import BaseModel, Field, field_validator
from datetime import datetime, timezone
from functools import wraps
import re
import json

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("cognigy_updater")

class CognigyAuth:
    def __init__(self, base_url: str, api_key: str, api_secret: str):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.api_secret = api_secret
        self.token: Optional[str] = None
        self.token_expiry: float = 0.0

    def get_token(self) -> str:
        if self.token and time.time() < self.token_expiry - 30:
            return self.token
        url = f"{self.base_url}/api/v1/auth/token"
        payload = {
            "grant_type": "client_credentials",
            "client_id": self.api_key,
            "client_secret": self.api_secret,
            "scope": "intent:write retrain:trigger project:deploy webhook:read"
        }
        headers = {"Content-Type": "application/json"}
        response = requests.post(url, json=payload, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()
        self.token = data["access_token"]
        self.token_expiry = time.time() + data["expires_in"] - 60
        return self.token

class IntentPatternPayload(BaseModel):
    pattern_ref: str
    intent_matrix: Dict[str, Any]
    retrain_directive: str
    intent_constraints: Dict[str, Any]
    maximum_pattern_complexity: int = Field(le=500, ge=1)
    patterns: List[str]

    @field_validator("retrain_directive")
    @classmethod
    def validate_directive(cls, v: str) -> str:
        allowed = {"full", "incremental", "skip"}
        if v not in allowed:
            raise ValueError(f"retrain_directive must be one of {allowed}")
        return v

    @field_validator("patterns")
    @classmethod
    def validate_pattern_count(cls, v: List[str]) -> List[str]:
        if len(v) > 100:
            raise ValueError("Intent patterns exceed maximum allowed count of 100")
        return v

def calculate_confidence_threshold(patterns: List[str], entity_annotations: Dict[str, int]) -> float:
    total_tokens = sum(len(re.split(r"\s+", p)) for p in patterns)
    total_entities = sum(entity_annotations.values())
    entity_density = total_entities / max(total_tokens, 1)
    base_confidence = 0.85
    density_penalty = min(entity_density * 0.1, 0.15)
    threshold = base_confidence - density_penalty
    return round(max(threshold, 0.65), 3)

def build_payload(pattern_ref: str, patterns: List[str], entity_annotations: Dict[str, int], retrain_directive: str = "incremental") -> Dict[str, Any]:
    threshold = calculate_confidence_threshold(patterns, entity_annotations)
    payload_dict = {
        "pattern_ref": pattern_ref,
        "intent_matrix": {"confidence_threshold": threshold, "entity_extraction_enabled": True, "fallback_strategy": "semantic_similarity"},
        "retrain_directive": retrain_directive,
        "intent_constraints": {"max_response_time_ms": 150, "min_confidence_score": threshold, "allow_overlap": False},
        "maximum_pattern_complexity": min(sum(len(p.split()) for p in patterns), 500),
        "patterns": patterns
    }
    return IntentPatternPayload(**payload_dict).model_dump()

def check_overlapping_patterns(new_patterns: List[str], existing_patterns: List[str]) -> List[str]:
    overlaps = []
    new_tokens = [set(p.lower().split()) for p in new_patterns]
    existing_tokens = [set(p.lower().split()) for p in existing_patterns]
    for i, new_set in enumerate(new_tokens):
        for j, existing_set in enumerate(existing_tokens):
            intersection = new_set.intersection(existing_set)
            if len(intersection) > 0 and len(intersection) / len(new_set) > 0.6:
                overlaps.append(f"Pattern {i} overlaps 60%+ with existing pattern {j}: {intersection}")
    return overlaps

def verify_model_degradation(baseline_confidence: float, updated_confidence: float, threshold: float = 0.05) -> bool:
    degradation = baseline_confidence - updated_confidence
    is_degraded = degradation > threshold
    if is_degraded:
        logger.warning(f"Model degradation detected: confidence dropped by {degradation:.3f}")
    return not is_degraded

def retry_on_rate_limit(max_retries: int = 3, base_delay: float = 1.0):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            delay = base_delay
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except requests.exceptions.HTTPError as e:
                    if e.response.status_code == 429 and attempt < max_retries:
                        logger.warning(f"Rate limited on attempt {attempt + 1}. Retrying in {delay}s")
                        time.sleep(delay)
                        delay *= 2
                    else:
                        raise
        return wrapper
    return decorator

class CognigyIntentUpdater:
    def __init__(self, auth: CognigyAuth, intent_id: str, project_id: str):
        self.auth = auth
        self.intent_id = intent_id
        self.project_id = project_id
        self.base_url = auth.base_url
        self.session = requests.Session()
        self.session.headers.update({"Content-Type": "application/json", "Accept": "application/json"})

    @retry_on_rate_limit(max_retries=3)
    def update_intent(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        url = f"{self.base_url}/api/v1/intents/{self.intent_id}"
        headers = {"Authorization": f"Bearer {self.auth.get_token()}"}
        logger.info(f"PUT {url}")
        response = self.session.put(url, json=payload, headers=headers, timeout=30)
        if response.status_code == 401:
            raise PermissionError("Invalid or expired authentication token")
        if response.status_code == 403:
            raise PermissionError("Insufficient scopes: require intent:write")
        if response.status_code == 404:
            raise ValueError(f"Intent {self.intent_id} not found")
        response.raise_for_status()
        data = response.json()
        required_keys = {"id", "pattern_ref", "status", "updated_at"}
        if not required_keys.issubset(data.keys()):
            raise ValueError("Response schema mismatch: missing required fields")
        logger.info(f"Intent updated successfully. Status: {data['status']}")
        return data

    @retry_on_rate_limit(max_retries=3)
    def trigger_retrain(self, directive: str) -> Dict[str, Any]:
        url = f"{self.base_url}/api/v1/intents/{self.intent_id}/retrain"
        headers = {"Authorization": f"Bearer {self.auth.get_token()}"}
        payload = {"directive": directive}
        logger.info(f"POST {url}")
        response = self.session.post(url, json=payload, headers=headers, timeout=60)
        response.raise_for_status()
        return response.json()

    @retry_on_rate_limit(max_retries=3)
    def deploy_project(self) -> Dict[str, Any]:
        url = f"{self.base_url}/api/v1/projects/{self.project_id}/deploy"
        headers = {"Authorization": f"Bearer {self.auth.get_token()}"}
        logger.info(f"POST {url}")
        response = self.session.post(url, json={"force": False}, headers=headers, timeout=60)
        response.raise_for_status()
        return response.json()

    def sync_external_monitor(self, event_data: Dict[str, Any]) -> None:
        webhook_payload = {
            "event_type": "intent_updated",
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "intent_id": self.intent_id,
            "metrics": event_data,
            "status": "deployed"
        }
        logger.info(f"Webhook sync payload generated: {json.dumps(webhook_payload, indent=2)}")

    def execute_full_pipeline(self, payload: Dict[str, Any], existing_patterns: List[str], baseline_confidence: float) -> Dict[str, Any]:
        start_time = time.time()
        audit_log = {
            "intent_id": self.intent_id,
            "project_id": self.project_id,
            "start_time": datetime.now(timezone.utc).isoformat(),
            "steps": [],
            "success": False
        }
        try:
            overlaps = check_overlapping_patterns(payload["patterns"], existing_patterns)
            if overlaps:
                raise ValueError(f"Overlap validation failed: {overlaps}")
            update_result = self.update_intent(payload)
            audit_log["steps"].append({"step": "update_intent", "status": "success"})
            updated_confidence = payload["intent_matrix"]["confidence_threshold"]
            if not verify_model_degradation(baseline_confidence, updated_confidence):
                raise RuntimeError("Model degradation threshold exceeded")
            audit_log["steps"].append({"step": "degradation_check", "status": "success"})
            retrain_result = self.trigger_retrain(payload["retrain_directive"])
            audit_log["steps"].append({"step": "retrain", "status": "success", "job_id": retrain_result.get("job_id")})
            deploy_result = self.deploy_project()
            audit_log["steps"].append({"step": "deploy", "status": "success", "version": deploy_result.get("version")})
            latency = time.time() - start_time
            audit_log["latency_ms"] = round(latency * 1000, 2)
            audit_log["success"] = True
            audit_log["end_time"] = datetime.now(timezone.utc).isoformat()
            self.sync_external_monitor({
                "latency_ms": audit_log["latency_ms"],
                "retrain_job": retrain_result.get("job_id"),
                "deploy_version": deploy_result.get("version")
            })
            logger.info(f"Pipeline completed in {audit_log['latency_ms']}ms")
            return audit_log
        except Exception as e:
            audit_log["error"] = str(e)
            audit_log["end_time"] = datetime.now(timezone.utc).isoformat()
            logger.error(f"Pipeline failed: {e}")
            raise

if __name__ == "__main__":
    auth = CognigyAuth(
        base_url="https://your-tenant.cognigy.ai",
        api_key="YOUR_API_KEY",
        api_secret="YOUR_API_SECRET"
    )
    
    updater = CognigyIntentUpdater(
        auth=auth,
        intent_id="6a8f3b2c-9d1e-4f5a-8b7c-2d3e4f5a6b7c",
        project_id="proj_nice_cxone_prod"
    )
    
    new_patterns = [
        "book a flight to {city}",
        "reserve hotel in {location}",
        "cancel my reservation {id}",
        "change departure date to {date}"
    ]
    existing_patterns = [
        "book a flight to {city}",
        "find restaurants near me"
    ]
    entity_annotations = {"city": 2, "location": 1, "id": 1, "date": 1}
    
    payload = build_payload(
        pattern_ref="intent_booking_v2",
        patterns=new_patterns,
        entity_annotations=entity_annotations,
        retrain_directive="incremental"
    )
    
    audit = updater.execute_full_pipeline(
        payload=payload,
        existing_patterns=existing_patterns,
        baseline_confidence=0.82
    )
    
    print(json.dumps(audit, indent=2))

Common Errors & Debugging

Error: 401 Unauthorized

  • Cause: Expired Bearer token or invalid client credentials.
  • Fix: Ensure the CognigyAuth class refreshes the token before expiry. Verify the client_id and client_secret match a registered OAuth2 client in the Cognigy.AI console.
  • Code Fix: The get_token method automatically refreshes when time.time() >= self.token_expiry - 30.

Error: 403 Forbidden

  • Cause: Missing OAuth2 scopes. The API requires intent:write, retrain:trigger, and project:deploy.
  • Fix: Update the client credentials scope configuration in the Cognigy.AI developer portal. Reissue the token after scope changes.
  • Code Fix: The grant_type: client_credentials payload explicitly requests the required scopes.

Error: 400 Bad Request

  • Cause: Payload violates intent_constraints or exceeds maximum_pattern_complexity. The pattern count exceeds 100, or the retrain directive is invalid.
  • Fix: Validate the payload against the IntentPatternPayload Pydantic model before submission. Adjust pattern density or switch retrain_directive to incremental.
  • Code Fix: The field_validator methods enforce count and directive constraints. The maximum_pattern_complexity field caps token density at 500.

Error: 429 Too Many Requests

  • Cause: Exceeded Cognigy.AI rate limits during PUT or retrain operations.
  • Fix: Implement exponential backoff. The retry_on_rate_limit decorator handles this automatically with a maximum of 3 retries and doubling delay.
  • Code Fix: The decorator catches requests.exceptions.HTTPError with status 429 and sleeps before retrying.

Error: Overlap Validation Failure

  • Cause: New patterns share more than 60% token intersection with existing patterns, risking classification collisions.
  • Fix: Refactor overlapping patterns to use distinct lexical markers or adjust entity annotations. Reduce substring duplication.
  • Code Fix: The check_overlapping_patterns function calculates token intersection ratios and raises a descriptive error when thresholds are exceeded.

Official References