MemoDokümantasyon
TR

Arka Uç Mimarisi

Memo arka ucu, merkezi bir App orkestratörü etrafında 29 paket halinde yapılandırılmış monolitik bir Go uygulamasıdır. Yalnızca standart kütüphanenin http.ServeMux'u kullanılarak localhost:8090 üzerinden düz bir REST + SSE API'si sunar.

Giriş Noktası

// main.go
func main() {
    port := flag.Int("port", 8090, "HTTP server port")
    flag.Parse()
    app, _ := app.New()
    app.Start()
    webserver.ListenAndServe(app, *port)
}

app.New() tüm alt sistemleri başlatır. webserver.ListenAndServe yolları kaydeder ve HTTP sunucusunu başlatır.

Merkezi Orkestratör: internal/app/

App yapısı arka ucun beynidir:

type App struct {
    cfg        *config.Config
    store      *memory.Store
    provider   *provider.Router
    conductor  *orchestra.Conductor
    executor   *agent.Executor
    syncer     *cloudsync.Drive
    waclient   *whatsapp.Client
    calendar   *calendar.Store
    sessions   *sessions.Manager
    identity   *identity.Manager
    skills     *skill.Manager
    mood       *mood.Engine
    db         *database.DB
    // ...
}

App üzerindeki ana yöntemler:

Yöntem Dosya Amaç
Chat() internal/app/chat.go Tam sohbet işlem hattı — prompt oluşturma, hafıza erişimi, sağlayıcı yönlendirme, akış
ChatStream() internal/app/llm.go Token düzeyinde SSE akış sarmalayıcısı
RememberExchange() internal/app/memory.go Konuşma turunu RAG'de sakla
SearchMemory() internal/app/memory.go RRF ile hibrit arama
ExecuteAgent() internal/app/agent.go Araç çağırma işlem hattı
RunOrchestra() internal/app/orchestra.go Çoklu-model paralel yürütme

Köprü Deseni

internal/webserver/ paketi, HTTP işleyicilerini App'ten ayırmak için bir köprü deseni kullanır:

// internal/webserver/bridge.go

type AppBridge interface {
    Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
    ChatStream(ctx context.Context, req ChatRequest, ch chan<- SSEEvent) error
    // ... ~90 yöntem imzası
}

type FullBridge struct {
    app *app.App
}

func (b *FullBridge) Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
    return b.app.Chat(ctx, req)
}

İşleyiciler yalnızca köprü arayüzünü bilir — asla somut App'i değil. Bu, işleyicileri sahte köprülerle test etmeyi ve HTTP koduna dokunmadan gelecekteki mimari değişiklikleri mümkün kılar.

API Katmanı: internal/webserver/

Yol Kaydı

// internal/webserver/server.go
func ListenAndServe(app *app.App, port int) error {
    mux := http.NewServeMux()
    bridge := &FullBridge{app: app}
    registerFlutterRoutes(mux, bridge)
    return http.ListenAndServe(fmt.Sprintf(":%d", port), mux)
}

Yol Kategorileri

Önek Sayı Amaç
/api/chat 3 Mesaj gönder, yanıt akışı, durdur
/api/memory/ 8 Ara, hatırla, unut, dışa aktar, içe aktar, analitik, hata ayıkla, ayarlar
/api/providers/ 10 Her sağlayıcı türü için CRUD + test
/api/models/ 6 Keşfet, indir, listele, sil, HuggingFace kataloğu
/api/agent/ 4 Yürüt, izinler, işlem hattı durumu
/api/orchestra/ 5 Çalıştır, yapılandır, roller, durum
/api/calendar/ 5 CRUD etkinlikler, ayarlar, hatırlatıcılar
/api/sync/ 6 Gönder, çek, durum, OAuth, ayarlar
/api/whatsapp/ 8 Eşleştir, durum, mesajlar, gönder, kişiler, ara
/api/skills/ 6 Listele, al, yükle, kaldır, etkinleştir
/api/config/ 4 Al, ayarla, dışa aktar, içe aktar
/api/mood/ 3 Puan, ayarlar, aç/kapat
/api/proactive/ 4 Ayarlar, öneriler, geçmiş, tetikle
/api/sessions/ 4 Listele, al, sil, yeniden adlandır
/api/llama/ 5 Başlat, durdur, durum, GPU bilgisi, modeller
/api/system/ 3 Sürüm, sağlık, kapat

SSE Akışı

Sohbet ve ajan yanıtları Sunucu-Gönderilen Olaylar (SSE) kullanır:

// internal/webserver/handlers_flutter.go
func handleChatStream(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    flusher, _ := w.(http.Flusher)
    eventCh := make(chan SSEEvent, 100)

    go bridge.ChatStream(r.Context(), req, eventCh)

    for event := range eventCh {
        fmt.Fprintf(w, "data: %s\n\n", event.JSON())
        flusher.Flush()
    }
}

Olaylar şunları içerir: token, thinking, tool_executing, tool_result, memory_retrieved, error, done.

Sağlayıcı Sistemi: internal/provider/

Sağlayıcı yönlendiricisi birden çok LLM arka ucunu yönetir:

// internal/provider/router.go
type Router struct {
    providers map[string]Provider  // sağlayıcı adına göre anahtarlanmış
    order     []string             // öncelik sırası (yedekleme zinciri)
    mu        sync.RWMutex
}

type Provider interface {
    Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
    ChatStream(ctx context.Context, req ChatRequest, ch chan<- TokenEvent) error
    Health(ctx context.Context) bool
}

Desteklenen Sağlayıcılar

Sağlayıcı Dosya API Türü
llama.cpp llama.go Yerel, OpenAI-uyumlu
openai openai.go Bulut, OpenAI API
gemini gemini.go Bulut, Gemini API
claude claude.go Bulut, Anthropic API
grok grok.go Bulut, xAI API
groq groq.go Bulut, Groq API
openrouter openrouter.go Bulut, OpenRouter API
ollama ollama.go Yerel, Ollama API
opencode-zen opencode_zen.go Bulut, OpenCode Zen API
opencode-go opencode_go.go Bulut, OpenCode Go API

Yedekleme Zinciri

func (r *Router) Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
    for _, name := range r.order {
        provider := r.providers[name]
        if !provider.Enabled() {
            continue
        }
        resp, err := provider.Chat(ctx, req)
        if err == nil {
            return resp, nil
        }
        provider.RecordFailure()
        // Sonraki sağlayıcıya geç
    }
    return nil, ErrAllProvidersFailed
}

3 ardışık başarısızlıktan sonra bir sağlayıcı otomatik devre dışı bırakılır. Bir arka plan sağlık kontrolü goroutine'i devre dışı sağlayıcıları yoklar ve düzeldiklerinde yeniden etkinleştirir.

Hafıza Deposu: internal/memory/

// internal/memory/store.go
type Store struct {
    db         *sql.DB
    embedModel string
    mu         sync.RWMutex
}

func (s *Store) Search(ctx context.Context, query string, topK int) ([]Memory, error)
func (s *Store) Store(ctx context.Context, exchange Exchange) error
func (s *Store) Forget(ctx context.Context, pattern string) (int, error)
func (s *Store) Export(ctx context.Context) ([]byte, error)
func (s *Store) Import(ctx context.Context, data []byte) error

storeMu (sync.RWMutex) eşzamanlı hafıza yeniden başlatmaya karşı koruma sağlar. Uygun kilidi tutmadan asla depoyu okumayın veya yazmayın.

Ajan Yürütücü: internal/agent/

// internal/agent/executor.go
type Executor struct {
    tools       map[string]Tool
    permissions *PermissionPolicy
    sandbox     *Sandbox
    pipeline    *Pipeline
}

func (e *Executor) Execute(ctx context.Context, task string, events chan<- AgentEvent) (string, error)

Korumalı Alan Kuralları

  • Yol sınırı kontrollerinden önce filepath.EvalSymlinks ile sembolik bağlar çözülür
  • Yazma işlemleri izin verilen dizin listesine göre kontrol edilir
  • run_command 60sn zaman aşımıyla bash -c üzerinden çalışır
  • Dosya okumaları proje diziniyle sınırlıdır
  • WriteFile içerik üst sınırı: 10 MB

Orkestra Şefi: internal/orchestra/

// internal/orchestra/conductor.go
type Conductor struct {
    roles      map[string]Role  // 8 rol
    chiefModel string
}

type Role struct {
    Name         string   // planner, frontend, backend, ...
    Provider     string
    Model        string
    SystemPrompt string
    Enabled      bool
}

Şef:

  1. Kullanıcı görevini ayrıştırma için şef modele gönderir
  2. Alt görevleri etkin rollere paralel olarak dağıtır (rol başına goroutine)
  3. Sonuçları toplar, sentez için şefe gönderir
  4. İlerleme olaylarını akışlar (orchestra:role_started, orchestra:role_completed)

Veritabanı Katmanı: internal/database/

// internal/database/db.go
type DB struct {
    Write chan func(*sql.DB)  // Serileştirilmiş yazma kuyruğu
    Read  *sql.DB             // Eşzamanlı okumalar
}

Tüm SQLite yazmaları, database is locked hatalarını önlemek için Write kanalından geçer. Okumalar Read tanıtıcısında eşzamanlı olarak gerçekleşebilir. Bu, gömme yazmalarının ve FTS indeks güncellemelerinin atomik olması gereken hafıza deposu için özellikle önemlidir.

Yaşam Döngüsü

Başlatma:
  config.Load() → db.Open() → provider.Init() → memory.Init()
  → agent.Init() → cloudsync.Init() → whatsapp.Init()
  → calendar.Init() → mood.Init() → webserver.ListenAndServe()

Kapatma:
  webserver.Shutdown() [yeni istekleri kabul etmeyi durdur]
  → whatsapp.Stop() → cloudsync.Stop() → calendar.Stop()
  → mood.Stop() → memory.Close() → db.Close()


Kapatmada önce web sunucusu durmalıdır. Alt sistemleri web sunucusundan önce durdurmak, uçuş halindeki HTTP isteklerinin nil/geçersiz kaynaklara erişmesine ve çökmesine neden olur.

Ana Tasarım Kuralları

  1. Harici HTTP yönlendirici yok — yalnızca http.ServeMux
  2. Köprü deseni — işleyiciler asla doğrudan App'e referans vermez
  3. context.Context ilk parametre olarak — asla yapı alanlarında saklanmaz (yaşam döngüsü goroutine'leri hariç)
  4. Paylaşılan durum için sync.RWMutex — saparsanız nedenini belgeleyin
  5. SQLite yazmaları serileştirilmişdatabase.DB.Write kanalını kullanın
  6. CGO gerekli — tüm Go komutları için CGO_ENABLED=1
  7. Atomik yazmalar — tüm yapılandırma/veri kalıcılığı için geçici dosya + yeniden adlandırma