Konfiguracja
Wszystkie ustawienia znajdują się w pliku config.toml. Nie ma wartości domyślnych zapisanych na stałe — każdą wartość trzeba jawnie skonfigurować.
Ścieżkę pliku konfiguracji można zmienić za pomocą zmiennej środowiskowej LOOMEM_CONFIG.
Uwierzytelnianie konfiguruje się przez [server].auth_token_env (zobacz [server]) — nie ma osobnej sekcji [auth].
[storage]
[storage]
data_dir = "./data"
vector_enabled = true
| Klucz | Typ | Domyślnie | Opis |
|---|---|---|---|
data_dir |
string | "./data" | Katalog główny RocksDB, Tantivy i embeddingów |
vector_enabled |
bool | true | Włącz wyszukiwanie wektorowe (embeddingi) |
[storage.rocksdb]
[storage.rocksdb]
max_open_files = 1000
compression = "lz4"
write_buffer_size = 67108864
max_write_buffer_number = 3
| Klucz | Typ | Opis |
|---|---|---|
max_open_files |
int | Rozmiar puli deskryptorów plików |
compression |
string | "lz4" lub "snappy" — zalecane LZ4 |
write_buffer_size |
int | Liczba bajtów na bufor zapisu (64 MB) |
max_write_buffer_number |
int | Liczba buforów zapisu |
[storage.tantivy]
[storage.tantivy]
enabled = true
heap_size_mb = 128
| Klucz | Typ | Opis |
|---|---|---|
enabled |
bool | Włącz wyszukiwanie pełnotekstowe BM25 |
heap_size_mb |
int | Pula pamięci zapisu indeksu |
[storage.intent_log]
Dziennik zapisu z wyprzedzeniem zapewniający spójność między magazynami.
[storage.intent_log]
enabled = true
dir = "wal"
max_size_mb = 10
sync_on_write = false
| Klucz | Typ | Opis |
|---|---|---|
enabled |
bool | Włącz WAL |
dir |
string | Katalog WAL (względem data_dir) |
max_size_mb |
int | Rotuj po osiągnięciu tego rozmiaru dziennika |
sync_on_write |
bool | fsync przy każdym zapisie (bezpieczniej, lecz wolniej) |
[search]
[search]
top_k = 10
max_request_top_k = 1000 # HTTP top_k musi mieścić się w zakresie 1..ten limit; MCP ma osobne limity
surprise_boost = 1.5
synonyms_file = "synonyms.toml"
entities_file = "entities.toml"
rerank_enabled = false
rerank_candidates = 20
rerank_model_dir = "models/reranker"
multi_query_enabled = false
stem_polish = true
evidence_fields = false
| Klucz | Typ | Opis |
|---|---|---|
top_k |
int | Domyślna liczba wyników wyszukiwania |
max_request_top_k |
int | Górna granica liczby wyników HTTP (domyślnie 1000); zero lub żądanie powyżej limitu zwraca 400 przed wyszukiwaniem. Limity MCP są oddzielne. |
surprise_boost |
f64 | Mnożnik nowości przy ingest (inspirowany Titans) |
synonyms_file |
string | Ścieżka do mapy rozszerzania synonimów |
entities_file |
string | Ścieżka do słownika encji |
rerank_enabled |
bool | Włącz reranking cross-encoderem (~97 ms/parę) |
rerank_candidates |
int | Przekaż najlepsze N wyników do rerankera |
rerank_model_dir |
string | Katalog modelu ONNX |
multi_query_enabled |
bool | Rozbijaj złożone zapytania na zapytania składowe |
stem_polish |
bool | Włącz stemming języka polskiego |
evidence_fields |
bool | Eksperymentalne, wymaga włączenia (domyślnie false). Przy true każdy wynik /v1/search oraz każdy element GET /api/memories/{id} zawiera dodatkowy obiekt evidence (author, conversation_date, event_date, event_date_provenance, source_id, byte_start, byte_end), a każdy wiersz memory_search zawiera przed wynikiem znacznik dowodu w nawiasach. Przy false odpowiedzi są identyczne bajt po bajcie z odpowiedziami sprzed wprowadzenia flagi. Zobacz dokumentację API. |
[search.cache]
[search.cache]
enabled = true
max_entries = 500
ttl_secs = 300
Semantyczna pamięć podręczna zapytań. Identyczne zapytania w czasie TTL zwracają zapisane wyniki.
[search.graph]
[search.graph]
enabled = true
max_hops = 1
boost_factor = 0.3
max_graph_additions = 3
| Klucz | Typ | Opis |
|---|---|---|
enabled |
bool | Włącz wyszukiwanie wzbogacone grafem |
max_hops |
int | 1 = bezpośredni sąsiedzi, 2 = dwa kroki |
boost_factor |
f64 | Mnożnik wyniku dla elementów znalezionych przez graf (0.0 – 1.0) |
max_graph_additions |
int | Maksymalna liczba wyników wyłącznie z grafu dodanych do wyszukiwania |
[search.hybrid_weights]
[search.hybrid_weights]
vector = 0.6
bm25 = 0.4
Steruje proporcją łączenia wyników. Suma musi wynosić 1.0.
[search.decay]
[search.decay]
l0_lambda = 0.05
l1_lambda = 0.03
Wykładnicze tempo zanikania dla każdego poziomu. Większa wartość = szybsze zanikanie. Okres półtrwania ≈ ln(2) / lambda dni.
| Poziom | Lambda | Przybliżony okres półtrwania |
|---|---|---|
| L0 | 0.05 | ~14 dni |
| L1 | 0.03 | ~23 dni |
[search.complexity]
[search.complexity]
enabled = false
simple_top_k = 3
medium_top_k = 10
complex_top_k = 20
Routing uwzględniający złożoność. Po włączeniu dostosowuje top_k do klasyfikacji złożoności zapytania. Obecnie wyłączony — wszystkie zapytania korzystają z pełnego procesu.
[worker]
[worker.consolidation]
[worker.consolidation]
interval_secs = 300
batch_size = 200
concurrency = 2
timeout_secs = 300
min_chunks_to_consolidate = 3
min_age_secs = 60
prompt_version = 1
consolidation_style = "structured"
similarity_threshold = 0.20
| Klucz | Typ | Opis |
|---|---|---|
interval_secs |
int | Częstotliwość uruchamiania (5 min) |
batch_size |
int | Maksymalna liczba fragmentów na strumień w jednym przebiegu |
concurrency |
int | Równoległe strumienie |
min_chunks_to_consolidate |
int | Pomiń strumienie z mniejszą liczbą fragmentów |
min_age_secs |
int | Konsoliduj tylko starsze fragmenty |
consolidation_style |
string | "structured" = fakty z typem i ustalaniem dat, "observation" = szczegółowe fakty, "summary" = akapit |
similarity_threshold |
f64 | Podobieństwo cosinusowe grupowania tematów przed kompresją (0.0 = jeden na fragment, 1.0 = wszystkie razem) |
[worker.decay_worker]
[worker.decay_worker]
interval_secs = 3600
factor = 0.995
l0_factor = 0.990
l1_factor = 0.995
dormant_threshold = 0.01
access_boost = true
adaptive_enabled = true
adaptive_dampening = 0.5
adaptive_cap = 200
| Klucz | Typ | Opis |
|---|---|---|
interval_secs |
int | Uruchamiaj co godzinę |
l0/l1_factor |
f64 | Godzinowy mnożnik zanikania dla poziomu |
dormant_threshold |
f64 | Wynik poniżej progu oznacza stan uśpienia |
access_boost |
bool | Resetuj wynik po trafieniu wyszukiwania |
adaptive_enabled |
bool | ACT-R: częściej używane elementy zanikają wolniej |
adaptive_dampening |
f64 | Wpływ access_count na zanikanie |
adaptive_cap |
int | Górna granica access_count dla efektu adaptacyjnego |
[worker.compaction]
[worker.compaction]
interval_secs = 3600
timeout_secs = 300
Wyzwalanie kompakcji RocksDB w tle.
[worker.clustering]
[worker.clustering]
interval_secs = 21600
max_iterations = 1000
timeout_secs = 600
Proces grupowania uruchamia k-means na embeddingach L1, aby grupować powiązane wspomnienia.
[scheduler]
[scheduler]
enabled = true
Główny przełącznik wszystkich procesów w tle.
[llm]
[llm]
provider = "openai" # generowanie odpowiedzi (konsolidacja / reflect)
api_key_env = "OPENAI_API_KEY"
embedding_provider = "local" # "local" (ONNX na urządzeniu) lub "openai"
embedding_model = "text-embedding-3-small" # używany, gdy embedding_provider = "openai"
# embedding_model_path = "/abs/path" # katalog modelu lokalnego; domyślnie ~/.loomem/models/multilingual-e5-small
embedding_dim = 384 # MUSI odpowiadać aktywnemu modelowi embeddingów
compression_model = "gpt-4.1-mini"
timeout_secs = 10
fallback_to_regex = true
| Klucz | Typ | Opis |
|---|---|---|
provider |
string | Dostawca generowania tekstu do konsolidacji / reflect ("openai") |
embedding_provider |
string | "local" (ONNX na urządzeniu, bez klucza API) lub "openai". Brak w starszej konfiguracji → traktowany jako "openai" |
api_key_env |
string | Nazwa zmiennej środowiskowej z kluczem API |
embedding_model |
string | Nazwa modelu embeddingów przy embedding_provider = "openai" |
embedding_model_path |
string? | Katalog z model.onnx i tokenizer.json dla lokalnych embeddingów. Brak ustawienia → ~/.loomem/models/multilingual-e5-small |
embedding_dim |
int | Wymiar wektora embeddingu — musi odpowiadać aktywnemu modelowi |
compression_model |
string | Model do konsolidacji, ekstrakcji i dream |
timeout_secs |
int | Limit czasu wywołania API |
fallback_to_regex |
bool | Tylko konsolidacja: kompresja regex po błędzie wywołania API generowania tekstu (nie obejmuje braku klucza) |
Lokalne embeddingi (domyślnie)
Nowe instalacje korzystają z embedding_provider = "local": embeddingi są obliczane na urządzeniu przez model ONNX bez kwantyzacji (domyślnie multilingual-e5-small, 384 wymiary, dobra skuteczność wyszukiwania wielojęzycznego i polskiego) przy użyciu środowiska tract napisanego w Rust. Klucz API nie jest wymagany, a tekst nie opuszcza urządzenia. Obsługa modelu jest w domyślnej kompilacji; pliki modelu pobierzesz poleceniem:
./scripts/fetch-embedding-model.sh # → ~/.loomem/models/multilingual-e5-small
LLM generujący tekst (konsolidacja, ekstrakcja faktów, dream) działa niezależnie. Przy domyślnej konfiguracji (te podsystemy są włączone) i bez OPENAI_API_KEY serwer nie uruchomi się, o ile nie ustawisz LOOMEM_ALLOW_KEYLESS=1. Wtedy memory_ingest może zapisać tekst źródłowy, ale zwraca błąd braku klucza i nie wyodrębnia faktów. Ekstrakcja encji przez LLM jest wyłączona, a dream i konsolidacja kończą się błędem; memory_store i wyszukiwanie semantyczne działają całkowicie lokalnie. Wyłączenie tych podsystemów w config.toml usuwa kontrolę przy starcie, a przy knowledge_extraction.enabled = false memory_ingest zapisuje surową transkrypcję. Aby używać lokalnego LLM do generowania tekstu, skieruj provider do punktu końcowego zgodnego z OpenAI (przyszły etap).
Wymiar embeddingów i ponowne obliczanie
embedding_dim musi odpowiadać aktywnemu modelowi embeddingów (multilingual-e5-small = 384, OpenAI text-embedding-3-small = 1536). Wymiar użyty przy tworzeniu bazy jest zapisany w jej metadanych; w razie niezgodności serwer odmawia uruchomienia, zamiast po cichu mieszać wymiary wektorów. Aby zmienić dostawcę lub model istniejącej bazy, przelicz embeddingi:
loomem-server --reembed # przelicz wektory skonfigurowanym dostawcą (uruchom po zatrzymaniu serwera)
Aby korzystać z API zamiast lokalnych embeddingów, ustaw embedding_provider = "openai" i embedding_dim = 1536, wyeksportuj OPENAI_API_KEY i przelicz embeddingi.
[server]
[server]
host = "127.0.0.1"
port = 3030
auth_token_env = "LOOMEM_AUTH_TOKEN"
| Klucz | Typ | Opis |
|---|---|---|
host |
string | Adres nasłuchiwania (0.0.0.0 w środowisku produkcyjnym) |
port |
int | Port HTTP (zmieniany przez zmienną środowiskową PORT) |
auth_token_env |
string | Nazwa zmiennej środowiskowej zawierającej klucz Bearer API (domyślnie LOOMEM_AUTH_TOKEN). Jeśli zmienna nie jest ustawiona lub jest pusta, uwierzytelnianie jest wyłączone, a serwer działa w lokalnym trybie passthrough |
[resource_guards]
[resource_guards]
max_cpu_cores = 1.0
max_memory_mb = 512
min_disk_space_mb = 1024
llm_timeout_secs = 30
worker_timeout_secs = 120
Kontrole przy uruchamianiu. Serwer odmówi startu, jeśli zasoby są poniżej progów.
[streams] i [namespaces]
[streams]
shared = "001"
[streams.agents]
assistant = { raw = "100", compressed = "101" }
[namespaces]
personal = "100"
work = "110"
streams — identyfikatory surowych i skompresowanych strumieni przypisane agentom. namespaces — mapowanie czytelnej nazwy na ID strumienia, zwracane przez memory_namespaces.
Dane zapisane bez wskazania strumienia trafiają do wbudowanego strumienia domyślnego __user_default__.
[pii]
[pii]
enabled = true
redact_phones = true
redact_emails = true
redact_ids = true
blocklist_file = "pii_blocklist.txt"
audit_log = true
Dane osobowe są usuwane przed każdym wywołaniem API LLM (konsolidacja, ekstrakcja, dream). Oryginalna treść w RocksDB pozostaje bez zmian.
[cost]
[cost]
daily_cap_usd = 15.00
alert_threshold_usd = 10.00
anomaly_multiplier = 3.0
persist = true
| Klucz | Typ | Opis |
|---|---|---|
daily_cap_usd |
f64 | Twardy limit — po jego przekroczeniu nie ma wywołań LLM |
alert_threshold_usd |
f64 | Próg ostrzegawczy |
anomaly_multiplier |
f64 | 3 razy typowy koszt dzienny = alert anomalii |
persist |
bool | Zachowaj liczniki kosztów po ponownym uruchomieniu |
[knowledge_extraction]
[knowledge_extraction]
enabled = true
model = "gpt-4.1-mini"
max_transcript_tokens = 20000
dedup_cosine_threshold = 0.92
contradiction_check = true
contradiction_cosine_min = 0.5
max_facts_per_transcript = 20
max_tokens = 2000
input_chunk_tokens = 0
split_on_truncation = false
Steruje procesem LLM memory_ingest.
| Klucz | Typ | Domyślnie | Opis |
|---|---|---|---|
max_tokens |
int | 2000 | Limit odpowiedzi na wywołanie ekstrakcji. Odpowiedź, która go osiągnie (finish_reason: "length"), jest odrzucana; nigdy nie jest przyjmowana częściowo |
input_chunk_tokens |
int | 0 | Limit wejścia dla jednego wywołania ekstrakcji (4 znaki ≈ 1 token). 0 zachowuje poprzedni proces: jedno wywołanie na transkrypcję do max_transcript_tokens. Inna wartość dzieli transkrypcję na granicach wierszy na fragmenty o maksymalnie min(input_chunk_tokens, max_transcript_tokens) tokenach i wykonuje jedno wywołanie dla każdego |
split_on_truncation |
bool | false | Przy true fragment, którego odpowiedź została obcięta, jest ponownie przetwarzany jako dwie połowy rozdzielone na granicy wiersza (głębokość 2, maksymalnie 4 dodatkowe wywołania na fragment). Przy false obcięty fragment jest odrzucany i zgłaszany bez ponawiania |
Te trzy klucze są dodatkowymi ustawieniami: z powyższymi wartościami domyślnymi ekstrakcja wykonuje te same wywołania co przed ich wprowadzeniem. Obcięta odpowiedź nie jest przyjmowana w żadnym trybie.
[fragments]
Eksperymentalne, wymaga włączenia. Domyślnie wyłączone; istniejące wdrożenia i klienci zachowują działanie identyczne bajt po bajcie.
[fragments]
enabled = false
max_fragment_tokens = 400
prefix_reserve_tokens = 16
provenance_role = "claim"
token_counter = "embedder"
| Klucz | Typ | Domyślnie | Opis |
|---|---|---|---|
enabled |
bool | false | Zapisuj fragmenty źródłowe przy memory_ingest |
max_fragment_tokens |
int | 400 | Limit tokenów tekstu fragmentu mierzony przez token_counter. Musi być dodatni |
prefix_reserve_tokens |
int | 16 | Zapas ponad limit tekstu. Lokalny model embeddingów dodaje do treści dokładnie dwa tokeny specjalne (bez prefiksu passage:); reszta pokrywa różnicę między wybranym licznikiem a tokenizerem modelu embeddingów |
provenance_role |
string | "claim" |
Rola przypisywana każdemu fragmentowi: "claim" (pełna waga), "evidence" (×0.5) lub "cue" (×0.8). Każda inna wartość powoduje błąd wczytania konfiguracji |
token_counter |
string | "embedder" |
"embedder" (domyślny i zalecany: tokenizer załadowanego lokalnego modelu embeddingów, jedyny licznik używający jego limitu), "approx" (ceil(chars / 3), bez modelu) lub "tiktoken" (BPE cl100k_base) — dwa liczniki bez modelu służą do nielokalnych dostawców embeddingów i testów offline. Inna wartość powoduje błąd wczytania konfiguracji |
Po włączeniu memory_ingest zapisuje, zaraz po źródle całej rozmowy i przed ekstrakcją, jeden fragment na turę rozmowy (długie tury są dalej dzielone na granicach akapitów, zdań i białych znaków). Każdy fragment zawiera rolę autora, datę rozmowy i dokładny zakres bajtów w zapisanym źródle. Fragmenty mają od początku status consolidated (nigdy nie są wejściem konsolidacji ani dream), są wykluczone z sekcji recent w memory_context, nie mają własnej daty zdarzenia (wiersz memory_search pokazuje datę źródła observed:/recorded:) i są usuwane wraz ze źródłem. Do wyniku narzędzia dodawane jest zdanie Stored N fragments. (zobacz narzędzia MCP). Zapisy fragmentów są dodatkowe i nigdy nie blokują ekstrakcji faktów.
Walidacja przy uruchomieniu: max_fragment_tokens musi być dodatnie, a przy embedding_provider = "local" suma max_fragment_tokens + prefix_reserve_tokens nie może przekraczać okna lokalnego modelu embeddingów wynoszącego 512 tokenów. enabled = true z token_counter = "embedder" (domyślnie) wymaga embedding_provider = "local" — przy innym dostawcy ustaw jawnie token_counter = "approx" albo "tiktoken", w przeciwnym razie wczytanie konfiguracji zawiedzie. Przy token_counter = "embedder" bez załadowanego modelu lokalnego segmentacja kończy się błędem i tekst memory_ingest o tym informuje (nie ma cichego przejścia na przybliżenie).
provenance_role wpływa na ranking tylko przy wyszukiwaniu łączonym (z włączonymi wektorami, dla zapytań nieprostych). Ścieżka wyłącznie BM25 nie stosuje mnożnika pochodzenia, dlatego dla krótkich zapytań fragmenty claim i evidence otrzymują ten sam wynik.
[entity_extraction]
[entity_extraction]
enabled = true
model = "gpt-4.1-mini"
batch_size = 20
flush_interval_secs = 3
queue_capacity = 200
confidence_threshold = 0.7
max_tokens_per_batch = 2000
Asynchroniczne rozpoznawanie encji przez LLM dla encji spoza słownika.
[contradiction]
[contradiction]
enabled = true
similarity_threshold = 0.70
max_candidates = 5
model = "gpt-4.1-mini"
Gdy pojawia się nowy fakt, LLM sprawdza najlepsze max_candidates podobnych istniejących faktów pod kątem sprzeczności. Wynik: updates (zastępuje), extends (zachowuje oba), none (brak relacji).
[conversation_extraction]
[conversation_extraction]
enabled = true
model = "gpt-4.1-mini"
max_tokens = 2000
confidence_threshold = 0.7
dedup_threshold = 0.80
max_extractions_per_request = 30
[profile]
[profile]
enabled = true
model = "gpt-4.1-mini"
max_chunks = 100
cache_ttl_secs = 3600
max_static_facts = 30
max_recent_items = 15
Synteza profilu użytkownika. Przechowywana w pamięci podręcznej przez cache_ttl_secs.
[retention]
[retention]
soft_delete_days = 30
hard_purge_interval_secs = 86400
| Klucz | Typ | Opis |
|---|---|---|
soft_delete_days |
int | Liczba dni przechowywania miękko usuniętych fragmentów przed trwałym usunięciem |
hard_purge_interval_secs |
int | Częstotliwość uruchamiania procesu usuwania (sekundy) |
Proces trwałego usuwania usuwa przeterminowane, miękko usunięte fragmenty z RocksDB, Tantivy i grafu.
[associator]
[associator]
enabled = true
k_clusters = 0
max_clusters = 50
max_iterations = 100
min_serendipity = 0.1
max_associations = 3
| Klucz | Typ | Opis |
|---|---|---|
enabled |
bool | Włącz silnik nieoczekiwanych skojarzeń ECA i automatyczne grupowanie |
k_clusters |
int | Stała liczba klastrów (0 = automatycznie) |
max_clusters |
int | Górna granica automatycznie określanej liczby klastrów |
min_serendipity |
f64 | Minimalny wynik potrzebny do uwzględnienia skojarzenia |
max_associations |
int | Maksymalna liczba nieoczekiwanych wyników na wyszukiwanie |
[dream]
[dream]
enabled = true
batch_size = 50
min_group_size = 2
model = "gpt-4.1-mini"
cost_cap_usd_per_run = 0.10
| Klucz | Typ | Opis |
|---|---|---|
batch_size |
int | Liczba fragmentów na przebieg dream |
min_group_size |
int | Minimalna liczba fragmentów tematu do połączenia |
cost_cap_usd_per_run |
f64 | Limit bezpieczeństwa na sesję dream |
[memory_generator]
[memory_generator]
enabled = true
max_chunks = 200
max_sections = 20
model = "gpt-4.1-mini"
Steruje wynikiem punktu końcowego generate-memory-md.
Zmienne środowiskowe
| Zmienna | Przeznaczenie | Wymagana |
|---|---|---|
LOOMEM_AUTH_TOKEN |
Klucz Bearer API (nazwa konfigurowana przez server.auth_token_env) |
Nie (bez ustawienia uwierzytelnianie wyłączone — lokalny tryb passthrough) |
OPENAI_API_KEY |
Klucz API LLM (nazwa konfigurowana przez llm.api_key_env) |
Tylko dla generowania tekstu OpenAI (provider = "openai") lub embeddingów OpenAI (embedding_provider = "openai"). Przy lokalnych embeddingach i bez klucza domyślna konfiguracja uruchamia się tylko z LOOMEM_ALLOW_KEYLESS=1, a etapy LLM są niedostępne (memory_store i wyszukiwanie nadal działają). |
LOOMEM_CONFIG |
Ścieżka do pliku konfiguracji | Nie (domyślnie: config.toml) |
PORT |
Nadpisz port serwera | Nie |
SERVER_ORIGIN |
Bazowy URL przekierowania OAuth | Nie (dla MCP Remote) |
LOOMEM_LOG_FORMAT |
"json" dla dzienników strukturalnych |
Nie |
LOOMEM_AT_REST_MASTER_KEY |
Klucz główny włączający szyfrowanie danych w spoczynku (32 bajty w base64) | Nie |
LOOMEM_AT_REST_EXPECT_ENABLED |
Po ustawieniu odmów uruchomienia bez klucza głównego | Nie |
LOOMEM_AMBIENT_CACHE_TTL_SECS |
TTL pamięci podręcznej dla odpowiedzi /v1/ambient |
Nie (domyślnie 60) |
TELEGRAM_BOT_TOKEN, LOOMEM_TELEGRAM_CHAT_ID |
Opcjonalny webhook alertów o kosztach | Nie |
Informacje o szyfrowaniu danych w spoczynku: SECURITY.md — dane w spoczynku.