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 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 theLearningEngine
with 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 theDataStorage
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 theLearningEngine
and 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 theLearningEngine
andDataStorage
, and perform any other necessary cleanup. The providedCoroutineScope
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 thecom.karl.core.data.DataSource
interface. ItsobserveInteractionData
method is called by theKarlContainer
during initialization. The application's implementation is then responsible for observing relevant user actions and asynchronously passing newInteractionData
objects 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 aPrediction
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:
-
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
KarlInstruction
s that might modify the prediction process (e.g., a minimum confidence threshold). -
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.