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.
Refer to Project KARL's dokka docs
for comprehensive information on KARL Modules.
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 forLearningEngine,DataStorage, andDataSource, along with aCoroutineScope, 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 fromDataStorage, initializes theLearningEnginewith this state (or as a new model if no state exists), and starts observing new interaction data from theDataSource. -
Operation: Once initialized, the container actively processes incoming
InteractionData(triggeringtrainStep()in theLearningEngine) and can provide predictions (viagetPrediction()). -
Saving State (
saveState()): The application is responsible for periodically callingsaveState()or triggering it at appropriate times (e.g., on application exit, after significant learning) to persist theLearningEngine's current learned state via theDataStorageimplementation. -
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 theLearningEngineand deleting associated data fromDataStorage, 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 theLearningEngineandDataStorage, and perform any other necessary cleanup. The providedCoroutineScopeshould 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 thecom.karl.core.data.DataSourceinterface. ItsobserveInteractionDatamethod is called by theKarlContainerduring initialization. The application's implementation is then responsible for observing relevant user actions and asynchronously passing newInteractionDataobjects to the container via the provided callback. -
Output (
KarlContainer.getPrediction()): When the application requires an AI-driven suggestion, it callskarlContainer.getPrediction(). This method internally queries theLearningEngine(which might use context fromDataStorage) and returns aPredictionobject 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:
-
Extracts relevant features from the
InteractionData. -
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 method. The LearningEngine:
-
May use the optional
contextData(e.g., a short history of recent interactions fetched fromDataStorage) to inform the current prediction. -
Applies its internal learned model to the current context/input features.
-
Considers any active
KarlInstructions that might modify the prediction process (e.g., a minimum confidence threshold). -
Returns a
Predictionobject 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.