PaceUp
AI destekli koşu koçu (iOS) · kişisel proje
Problem
Kendim de yapay zekadan koşu programı istiyordum. Her seferinde yaşımı, kilomu, mevcut tempomu, haftada kaç gün müsait olduğumu baştan anlatmam gerekiyordu. Karşılığında aldığım şey de bir listeydi. Sorun da oradaydı: program sohbetin içinde metin olarak kalıyor, bir süre sonra geri dönüp bakmıyorsun ve kopuyorsun. PaceUp'ta profilini bir kez giriyorsun; chatbot seni zaten tanıdığı için sonrasında sadece hedefini söylüyorsun — “üç ay sonra yarı maraton koşmak istiyorum” gibi. Onay verdiğin anda program uygulamanın takvimine gün gün yerleşiyor. Antrenmanları oradan işaretliyorsun, uygulama da bir gün öncesinden bildirim atıp hatırlatıyor.
Teknik detaylar
Tek kayıt, dört bağımsız etki
Kullanıcı bir antrenmanı tamamlayıp kaydettiğinde tek bir WorkoutResult oluşuyor, gerisini Django signal'ları hallediyor: antrenman tamamlandı olarak işaretleniyor, programın sayacı ilerliyor, kullanıcının istatistikleri ve streak'i yeniden hesaplanıyor, achievement kontrolü tetikleniyor ve gerekiyorsa bildirim gönderiliyor. Her etki kendi Django app'inde, birbirinden bağımsız duruyor. Kullanıcı kaydı sildiğinde aynı zincir ters yönde çalışıyor, böylece sayaçlar ve streak'ler tutarlı kalıyor.
Aynı anda gelen isteklerde şaşmayan sayaçlar
Program ve kullanıcı sayaçlarını F() expression'ları ile artırıyorum. Yani toplama işlemi Python tarafında oku-değiştir-yaz şeklinde değil, doğrudan veritabanının içinde yapılıyor. Aynı saniyede tamamlanan iki antrenman birbirinin sayacını ezmiyor.
Abonelik doğrulaması iki kez çalışsa da sorun olmuyor
RevenueCat doğrulamasının idempotent olması şart, çünkü aynı satın almayı hem webhook hem uygulamanın kendisi aynı anda onaylamaya çalışabiliyor. Eşleştirmeyi RevenueCat'in app user id'si ve store'un hiç değişmeyen original transaction id'si üzerinden yapıyorum; bu id sonradan bir iade ya da itiraz olduğunda izi sürmek için de kullanılıyor. Son doğrulamanın zaman damgasını da hata ayıklamak için saklıyorum.
Yapmadığı işi yaptım diyemeyen LLM
Agent'ın elindeki tool'ları ikiye ayırdım. UI tool'ları serbest metin döndürmek yerine uygulamada tipi belli bir form açıyor. Gerçekten veritabanına yazan tek bir backend tool'u var: create_workout_plan. Model “programını hazırladım” yazsa bile bu tek başına bir şey ifade etmiyor; program ancak o backend tool'u gerçekten çalıştıysa ortaya çıkıyor.
İki servis, tek auth sistemi
FastAPI servisi, Django'nun ürettiği JWT'leri aynı signing key ile HS256 üzerinden doğruluyor ve aynı PostgreSQL veritabanını okuyor. Böylece ikinci bir login ekranı, token değiş tokuşu ya da senkron tutulması gereken ikinci bir kullanıcı tablosu gerekmiyor. LangGraph'ın checkpointer'ı da sohbetleri servis yeniden başlasa veya deploy alsa bile kaldığı yerden devam ettiriyor.