Core Concepts

This section delves into the fundamental architectural components and operational principles that define Project KARL. Understanding these core concepts is essential for effectively integrating and leveraging KARL's capabilities for on-device, privacy-first AI.

The KARL Container Model

At the heart of Project KARL lies the "Container" model, encapsulated by the KarlContainer interface. This is the primary abstraction through which applications interact with the KARL system for a specific user or context.

What is a Container? (Analogy: The AI's sandbox/personal space)

Think of a KarlContainer as a dedicated, isolated environment or a "personal AI sandbox" for each user (or a distinct context within an application). It's where an individual instance of KARL's adaptive intelligence lives and evolves. Each container operates independently, ensuring that the learning and personalization for one user do not influence another. This container holds the user-specific learned model, manages its state, and orchestrates the flow of interaction data for learning and the generation of predictions.

The "composability" aspect refers to how this container is designed to be an integrable component within a larger application structure, potentially with a visual representation (as seen in :karl-compose-ui) that gives users a tangible sense of the AI's presence and boundaries.

Data Isolation & Boundaries

A key design principle of the KarlContainer is strict data isolation. The container defines a clear boundary for the AI's operational scope. Data fed into a specific container for learning (via the DataSource) is used exclusively by the LearningEngine within that container. Similarly, the learned state (model weights, parameters) managed by the DataStorage implementation is partitioned per container, typically identified by a unique user ID.

This isolation ensures that the AI's adaptation is hyper-personalized and that there is no cross-contamination of learned patterns between different users or contexts, reinforcing the privacy-first approach.

Container Lifecycle (Creation, Loading, Saving, Resetting)

Each KarlContainer instance goes through a defined lifecycle, managed by the application:

  • Creation & Configuration: A container is typically created using the KarlAPI.forUser(userId).build() builder pattern, where implementations for LearningEngine, DataStorage, and DataSource, along with a CoroutineScope, are provided.

  • Initialization (initialize()): After creation, the container must be explicitly initialized. During this phase, it attempts to load any previously saved state for the user from DataStorage, initializes the LearningEngine with this state (or as a new model if no state exists), and starts observing new interaction data from the DataSource.

  • Operation: Once initialized, the container actively processes incoming InteractionData (triggering trainStep() in the LearningEngine) and can provide predictions (via getPrediction()).

  • Saving State (saveState()): The application is responsible for periodically calling saveState() or triggering it at appropriate times (e.g., on application exit, after significant learning) to persist the LearningEngine's current learned state via the DataStorage implementation.

  • Loading State: This happens automatically during the initialize() phase if a previously saved state exists for the user ID.

  • Resetting (reset()): The container can be reset, which typically involves clearing its learned state in the LearningEngine and deleting associated data from DataStorage, effectively returning the AI to its initial "blank slate" condition for that user.

  • Releasing (release()): When a container is no longer needed (e.g., user logs out, application closes), release() should be called to stop data observation, release resources held by the LearningEngine and DataStorage, and perform any other necessary cleanup. The provided CoroutineScope should also be cancelled by the application at this point.

Data Handling within the Container

KARL's effectiveness relies on how interaction data is structured, provided, and managed.

Data Format & Input Layer InteractionData

The primary unit of information for KARL is the com.karl.core.models.InteractionData data class. Applications must transform raw user actions into this format. It typically contains:

  • userId: String: Identifies the user this interaction belongs to.

  • type: String: A category for the interaction (e.g., "button_click", "command_executed", "setting_changed").

  • details: Map: A flexible map to hold specific metadata about the interaction (e.g., mapOf("button_id" to "save", "context" to "editor")). This should contain relevant features for learning but **must avoid sensitive user content** to uphold privacy principles.

  • timestamp: Long: The time of the interaction, crucial for sequential learning and recency considerations.

The LearningEngine implementation is responsible for feature extraction from this InteractionData to create suitable input vectors for its internal ML model.

Local Storage Mechanism

The com.karl.core.data.DataStorage interface defines the contract for persisting the KarlContainer's state (represented by KarlContainerState, which primarily holds the serialized AI model) and, optionally, a history of InteractionData if the LearningEngine needs to query past interactions for context during prediction or training.

Implementations like :karl-room (using AndroidX Room and SQLite) are responsible for the actual database operations. A critical aspect of any DataStorage implementation for KARL is data encryption at rest. While KARL's core doesn't mandate a specific encryption method, it is highly recommended that implementations use robust encryption mechanisms (e.g., SQLCipher with Room/SQLite, platform-specific secure storage APIs) to protect the locally stored AI state and interaction metadata.

The Input/Ouput API

The interaction between the application and the KarlContainer is well-defined:

  • Input (DataSource): The application implements the com.karl.core.data.DataSource interface. Its observeInteractionData method is called by the KarlContainer during initialization. The application's implementation is then responsible for observing relevant user actions and asynchronously passing new InteractionData objects to the container via the provided callback.

  • Output (KarlContainer.getPrediction()): When the application requires an AI-driven suggestion, it calls karlContainer.getPrediction(). This method internally queries the LearningEngine (which might use context from DataStorage) and returns a Prediction object or null.

The Learning Process Explained LearningEngine

The com.karl.core.api.LearningEngine interface encapsulates the actual machine learning model and its adaptation logic.

Incremental Learning Loop (trainStep() concept)

KARL primarily employs incremental (or online) learning. When new InteractionData is received by the KarlContainer (from the DataSource), it typically triggers the LearningEngine.trainStep(data: InteractionData) method. Inside this method, the engine:

  1. Extracts relevant features from the InteractionData.

  2. Performs a small update to its internal model parameters based on this single new data point (or a small recent batch).

This allows the model to continuously adapt to the user's evolving behavior in near real-time without requiring offline retraining on large historical datasets. The trainStep method is designed to be asynchronous, returning a Job, so it doesn't block the data ingestion flow.

No Pre-Training: Starting From Scratch

A defining characteristic of KARL is that its models typically start "from scratch" for each user – meaning they are initialized with random or default parameters and have no prior knowledge. The intelligence is built up purely from the individual user's interactions within the application. This "blank slate" approach is fundamental to its privacy model (no external data is used to prime the model for a user) and ensures that personalization is truly individual, albeit requiring a "warm-up" period for the AI to become effective.

How Learning Adapts Over Time

As more InteractionData points are processed via trainStep(), the LearningEngine's internal model refines its parameters. This adaptation could mean:

  • Learning common sequences of actions.

  • Identifying frequently used features or settings.

  • Understanding correlations between different types of interactions.

  • Adjusting the probability of certain predictions based on recent behavior.

The specific adaptation depends on the model architecture (e.g., MLP, RNN) chosen within the LearningEngine implementation and the feature engineering performed on the InteractionData.

The Inference Process (predict() concept)

Inference is the process of using the learned model to generate predictions or suggestions.

Getting Suggestions/Predictions from KARL

When an application needs an AI-driven output, it calls karlContainer.getPrediction(). This, in turn, calls the LearningEngine.predict(contextData: List, instructions: List<:KarlInstruction>) method. The LearningEngine:

  1. May use the optional contextData (e.g., a short history of recent interactions fetched from DataStorage) to inform the current prediction.

  2. Applies its internal learned model to the current context/input features.

  3. Considers any active KarlInstructions that might modify the prediction process (e.g., a minimum confidence threshold).

  4. Returns a Prediction object or null if no confident suggestion can be made.

This process is designed to be fast and efficient for on-device execution.

Understanding the Output Format

The output of a successful inference is a com.karl.core.models.Prediction data class, which typically contains:

  • suggestion: String: The primary suggestion (e.g., a predicted command, a recommended item, a UI element to highlight). The format can be a simple string or a more structured representation depending on the use case.

  • confidence: Float: A value (often between 0.0 and 1.0) indicating the model's confidence in this particular suggestion.

  • type: String: A category for the prediction (e.g., "next_command_suggestion", "ui_personalization", "content_recommendation").

  • metadata: Map?: Optional additional data related to the prediction (e.g., alternative suggestions, reasons for the prediction if explainability is implemented).

The application then uses this structured Prediction object to enhance the user experience.