Aniket Raj

SDE · Systems & AI

India

2025-08-06

Project KARL

An open-source Kotlin library for building privacy-first, on-device adaptive AI models.

4 min read

Introduction: The Problem with Personalization

In the modern software landscape, AI-driven personalization is a key differentiator. From suggesting the next action in a developer tool to tailoring a news feed, intelligent features enhance user engagement. However, this intelligence often comes at a steep price: user privacy. The conventional approach requires aggregating vast amounts of user interaction data on centralized cloud servers, creating significant privacy risks, latency issues, and a dependency on network connectivity.

This trade-off inspired a question: Can we build a system that provides deep, adaptive personalization while guaranteeing that a user's data never leaves their device?

Project KARL (Kotlin Adaptive Reasoning Learner) is my answer to that question. It is an open-source, on-device AI framework designed from the ground up to be privacy-first, modular, and deeply integrated with the modern Kotlin ecosystem.

Core Philosophy & Vision

The vision for KARL is to empower developers to embed trustworthy AI directly into their applications. This is built on three foundational pillars:

  1. Privacy by Design (Local-First): All learning, data processing, and state storage occur exclusively on the user's device. There is no data egress by default, eliminating the privacy concerns of cloud-based AI.
  2. True Personalization (Adaptive Learning): KARL models start as a "blank slate" for each user. They learn and adapt incrementally from an individual's specific interactions, creating a uniquely tailored experience rather than applying a generalized, pre-trained model.
  3. Developer-Friendly Modularity: The framework is architected as a set of decoupled modules with clear interfaces, allowing developers to plug in different machine learning backends, storage mechanisms, and UI components.

Technical Architecture: The Composable Container Model

To achieve these goals, I designed a multi-module Kotlin Multiplatform (KMP) architecture centered around the KarlContainer.

The KarlContainer is an isolated, stateful sandbox for each user's AI instance. It orchestrates the interactions between three key pluggable components defined in the :karl-core module:

  • LearningEngine (The Brain): An interface for the machine learning model. It defines the contract for incremental training (trainStep) and inference (predict). The initial reference implementation, :karl-kldl, uses the KotlinDL library to build and manage a simple Multi-Layer Perceptron (MLP) that adapts in real-time.
  • DataStorage (The Memory): An interface for local persistence. It's responsible for saving and loading the KarlContainerState (the serialized model weights and metadata). The reference implementation, :karl-room, leverages AndroidX Room's KMP support with a SQLite backend, using KSP for compile-time code generation.
  • DataSource (The Senses): An interface implemented by the host application. It acts as the bridge, observing user actions, transforming them into anonymized InteractionData objects, and feeding them into the container.

This decoupled design ensures that the core orchestration logic remains independent of the specific ML library or database used, making the system highly extensible.

Key Technical Challenges & Solutions

Building an on-device AI framework, especially in the Kotlin ecosystem, presented several interesting challenges:

1. On-Device Incremental Learning:

  • Challenge: How do you train a model effectively with a stream of single data points without the massive computational resources of the cloud?
  • Solution: I implemented a trainStep function in the KLDLLearningEngine that performs a single, low-latency training pass on the model with each new InteractionData point. This "online learning" approach allows the model to adapt continuously. State is managed using a Mutex to ensure thread-safe updates to the model during concurrent training and prediction requests.

2. State Persistence and Management:

  • Challenge: How do you save and restore the entire learned state of a neural network efficiently on a local device?
  • Solution: The getCurrentState() method in the LearningEngine serializes the model's weights and key metadata into a ByteArray. The RoomDataStorage implementation then persists this binary blob in a local SQLite database. On initialization, this state is loaded back, allowing the AI to resume its learned state across application sessions.

3. Reactive UI Integration:

  • Challenge: How do you display the AI's real-time status and predictions in a modern, declarative UI without blocking the main thread?
  • Solution: The :karl-example-desktop application was built using Jetpack Compose for Desktop. I used Kotlin Coroutines and StateFlow extensively. The application's ViewModel observes the KarlContainer and exposes its state as reactive streams.

4. Build System Complexity (Gradle & KMP):

  • Challenge: Managing a multi-module Kotlin Multiplatform project with complex dependencies like KSP, Compose, and KotlinDL.
  • Solution: Implemented a Gradle Version Catalog (libs.versions.toml) as the single source of truth for all plugin and library versions.

Future Directions & Personal Learnings

This project has been an incredible learning experience in software architecture, on-device machine learning, and modern Kotlin development. Project KARL demonstrates that it is feasible to build intelligent, adaptive systems that respect user privacy by design.

Project Resources

Repoarrow Portalarrow Docsarrow