HIP-6: Per-User Fine-Tuning Architecture for Personalized AI. Status Draft. Hanzo's own standard — read this before implementing against it.
This proposal defines Hanzo's per-user fine-tuning architecture where every user interaction automatically creates and updates a personalized model fork. Each user owns their unique AI model that learns from their interactions, with all training data and model evolution stored on an immutable ledger. This eliminates domain-specific pre-training in favor of continuous per-user adaptation.
class UserModelLifecycle:
def __init__(self, user_id: str, base_model: str = "HMM-32B"):
# Every user starts with base model fork
self.model = fork_base_model(base_model, user_id)
self.training_history = []
self.priors = PersonalPriors()
def interact(self, input: UserInput) -> ModelOutput:
# 1. Generate response
output = self.model.generate(input)
# 2. Create training record
training_op = TrainingOp(
input=input,
output=output,
timestamp=now(),
gradient=compute_gradient(input, output)
)
# 3. Update model (real-time)
self.model.apply_gradient(training_op.gradient)
# 4. Record on ledger
ledger.record(self.user_id, training_op)
# 5. Update priors
self.priors.update(training_op)
return output
pub struct ModelFork {
// Unique per user
pub fork_id: ForkId,
pub user_id: UserId,
// Base model reference (not copied)
pub base_model: ModelReference,
pub fork_height: u64,
// User-specific layers (LoRA-style)
pub user_layers: UserAdapterLayers,
// Personalization state
pub personalization: PersonalizationState,
}
pub struct UserAdapterLayers {
// Low-rank adaptation layers
pub attention_adapters: Vec<LoRAAdapter>,
pub ffn_adapters: Vec<LoRAAdapter>,
// User-specific embeddings
pub user_embeddings: PersonalEmbeddings,
// Learned preferences
pub preference_weights: PreferenceMatrix,
}
class RealtimeFineTuning:
def apply_gradient(self, gradient: Gradient):
# Immediate model update (no batching)
with torch.no_grad():
# Update LoRA adapters only (efficient)
for adapter in self.user_layers:
adapter.weight += self.lr * gradient[adapter.name]
# Update user embeddings
self.user_embeddings.update(gradient.embeddings)
# Adjust preference matrix
self.preference_weights *= (1 + gradient.preferences)
def compute_gradient(self, input: Tensor, output: Tensor) -> Gradient:
# Compute gradient from single interaction
loss = self.compute_loss(input, output)
gradient = torch.autograd.grad(loss, self.user_layers.parameters())
# Apply privacy noise (differential privacy)
gradient = add_dp_noise(gradient, epsilon=1.0)
return gradient
A fork is stored as its delta from the base, at one bit per weight — the sign of the change — plus a scale factor. Reconstruction is the base plus the sign matrix times the scale, so a node holds one copy of the base model and one small delta per user rather than one model per user.
class BitDeltaCompressor:
"""One bit per weight: the sign of the delta, plus a scale factor"""
def compress(self, base_model, fine_tuned_model):
# Compute weight deltas
delta = fine_tuned_model - base_model
# 1-bit quantization
sign_matrix = torch.sign(delta)
scale_factor = torch.abs(delta).mean()
# Pack bits efficiently
packed = self.pack_bits(sign_matrix)
return {
'bits': packed,
'scale': scale_factor,
'metadata': {...}
}
def decompress(self, base_model, bitdelta):
# Unpack bits
sign_matrix = self.unpack_bits(bitdelta['bits'])
# Reconstruct model
delta = sign_matrix * bitdelta['scale']
return base_model + delta
message UserTrainingRecord {
// User and model identifiers
string user_id = 1;
string fork_id = 2;
uint64 interaction_number = 3;
// Interaction data (encrypted)
bytes encrypted_input = 4;
bytes encrypted_output = 5;
// Model update
bytes encrypted_gradient = 6;
float learning_rate = 7;
// Feedback and metrics
UserFeedback feedback = 8;
PerformanceMetrics metrics = 9;
// Priors update
PriorDelta prior_update = 10;
// Cryptographic proof
bytes zkp_proof = 11;
bytes signature = 12;
}
Where a user's training runs is the user's choice, not a platform default. Four tiers, weakest guarantee first; a user's tier is a property of the fork and applies to every update made to it.
tier_0:
name: Cloud Training
location: Hanzo servers
encryption: TLS
suitable_for: Non-sensitive data
tier_1:
name: Edge Training
location: User's edge device
encryption: Local only
suitable_for: Personal data
tier_2:
name: TEE Training
location: Confidential compute
encryption: Hardware-backed
suitable_for: Sensitive enterprise
tier_3:
name: Homomorphic Training
location: Encrypted computation
encryption: FHE
suitable_for: Maximum privacy
Tier 3 in code — training and inference without ever holding the plaintext:
class PrivateUserModel:
def __init__(self, user_id: str):
# All user data encrypted with user's key
self.encryption_key = derive_key(user_id)
self.model = EncryptedModel(base_model, self.encryption_key)
def private_inference(self, input: str) -> str:
# Inference in encrypted space (FHE)
encrypted_input = encrypt(input, self.encryption_key)
encrypted_output = self.model.forward_encrypted(encrypted_input)
output = decrypt(encrypted_output, self.encryption_key)
return output
def secure_training(self, interaction: Interaction):
# Training without decryption
encrypted_gradient = compute_encrypted_gradient(interaction)
self.model.apply_encrypted_update(encrypted_gradient)
# Zero-knowledge proof of training
proof = generate_training_proof(interaction, encrypted_gradient)
ledger.record_with_proof(self.user_id, proof)
class ModelEvolution:
def __init__(self):
self.generations = []
self.current_generation = 0
def advance_generation(self, user_models: List[UserModel]):
# Aggregate learnings from all users (privacy-preserving)
aggregated_priors = self.aggregate_priors(user_models)
# Create next generation base model
next_gen_model = self.evolve_base_model(
current_model=self.base_model,
priors=aggregated_priors,
fitness_scores=self.compute_fitness(user_models)
)
# Record generation advancement
self.generations.append(Generation(
number=self.current_generation + 1,
model=next_gen_model,
priors=aggregated_priors,
timestamp=now()
))
self.current_generation += 1
def aggregate_priors(self, user_models: List[UserModel]) -> Priors:
# Federated aggregation without seeing user data
encrypted_priors = [m.get_encrypted_priors() for m in user_models]
aggregated = homomorphic_sum(encrypted_priors)
return aggregated
interface UserModelAPI {
// Create user's personal model
async createUserModel(userId: string): Promise<ModelId>;
// Interact with personal model
async chat(
userId: string,
message: string,
options?: ChatOptions
): Promise<Response>;
// Get training history
async getTrainingHistory(
userId: string,
auth: AuthProof
): Promise<TrainingRecord[]>;
// Export user's model
async exportModel(
userId: string,
format: ExportFormat
): Promise<EncryptedModel>;
// Import model from another platform
async importModel(
userId: string,
model: EncryptedModel,
proof: OwnershipProof
): Promise<ModelId>;
}
# Per-user storage requirements
user_model:
base_reference: 8 bytes # Pointer to base model
lora_adapters: 100 MB # User-specific layers
embeddings: 10 MB # Personal embeddings
training_history: 1 MB/month # Compressed history
total: ~111 MB + growth
# Computation requirements
inference:
base_model: Shared across users (cached)
user_adaptation: +5% overhead
latency: <100ms additional
training:
gradient_computation: 10ms per interaction
model_update: 5ms (LoRA only)
ledger_recording: 20ms
total: ~35ms per interaction