[Bug] Reflection V1: large single-cluster merges cause partial deprecated_by writes with no compensation, no per-merge source cap, and no prompt/language configuration
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 35/100
Línea de trabajo
Start with everos/memory/reflection/orchestrator.py, especially _deprecate_lance_episodes, _deprecate_lance_facts, _deprecate, and _detect_orphans; then trace how reflect_episodes.py invokes EpisodeReflector.areflect. Review the write timeout in everos/core/persistence/lancedb/repository.py and the prompt in everalgo/user_memory/prompts/en/reflect.py. The issue lists three possible improvements; agree on scope first, then verify deprecation consistency, oversized-cluster handling, or configurable prompt/language behavior for the chosen change.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
Three related defects in Reflection V1 (reflect_episodes) that compound on large clusters:
- Partial deprecation with no compensation — the per-episode LanceDB
deprecated_byupdates run as N concurrent writes; when some exceed the write-lock deadline under load, the run is reported failed while the already-applied updates persist. Sources are left pointingdeprecated_byat a merged entry that may be invalid or already removed, with no rollback or reconcile pass. - No per-cluster source cap — a merge consumes all members of a cluster (observed
source_count=228→ one narrative). Only_MAX_CLUSTERS_PER_RUN = 10is bounded. Oversized merges produce narratives whose cascade upsert exceeds the 15 s write-lock deadline, starving the same run's deprecate updates (feedback loop, see below). - No prompt/language entry point — the reflector prompt is hard-wired to the upstream English prompt; merged output language is left to the model (observed mixed Chinese/English/French, occasionally garbage), with no
[reflection]config to inject instructions.
Environment
- EverOS
1.3.1, everalgo-core0.3.0, LanceDB index - LLM:
mimo-v2.5via an OpenAI-compatible proxy that does support strict structured outputs — the merge LLM call succeeds, so this is not a provider-shape failure
Observed behavior
Manual trigger over an existing 228-member cluster:
reflection_merged ... cluster_id=cl_bb3f91313016 mode=init source_count=228
lancedb_write_lock_deadline_exceeded ... op=update table=episode budget_seconds=15.0 acquired=False (repeated xN)
reflection_deprecate_failed ... cluster_id=cl_bb3f91313016
After reflection_deprecate_failed, LanceDB inspection showed 179–297 episode rows (and thousands of atomic_fact rows) with deprecated_by already written pointing at the merged entry — deprecation partially applied before failing, and nothing reconciles the leftovers. Concurrently cascade_worker_recoverable logged VectorStoreBusyError: upsert on table 'episode' exceeded its 15s write-lock deadline while indexing the same oversized narrative.
Root cause
Timeout mechanism (measured). The storm is queue-wait starvation behind the single per-table lock, not slow individual ops:
lancedb_write_lock_deadline_exceeded: 3516×acquired=Falsevs 21×acquired=True→ overwhelmingly "did not acquire the lock within 15 s" while someone else held it.- Lock bodies are long on memory-bound hosts:
lancedb_write_lock_slow_holdshows routine 8–16 s holds, and one maintenance op held a table lock 260.5 s (op=rebuild_indexes table=agent_case) on 2.4 GB RAM + swap. LanceDB writes, table-handle resolution and embedding are slow under swap churn. - The cascade worker itself cannot index the merged narrative — its upsert (+embed) exceeds 15 s, so it both fails and holds/contends the same lock the deprecations need.
- Feedback loop: the giant markdown entry → cascade reprocesses it (upsert + downstream
extract_atomic_facts) → more writes to the same table, while the run's N per-row deprecate updates queue behind them. The partial residuals keep cascade busy, so the storm continues for minutes afterreflect_episodesstops — observed ~100–220table=episodedeadline events/min from 06:01→06:51 (UTC+8), including a stretch while the strategy was disabled, until the oversized markdown entries were removed.
Code-level defects (everos/memory/reflection/orchestrator.py unless noted):
_deprecate_lance_episodes(L838) builds oneupdate()coroutine per source episode viaasyncio.gather(...)withoutreturn_exceptions(same for_deprecate_lance_factsL868 per parent id). Each update runs under the hard-coded_WRITE_TIMEOUT_SECONDS = 15.0(everos/core/persistence/lancedb/repository.py:37).- On failure,
_deprecate(L642) catchesAppError, logsreflection_deprecate_failed(L693), returnsNone— already-applied updates remain, no compensation._detect_orphans(L505) only detects merged-entries-whose-sources-were-not-deprecated, not the inverse. reflect_episodes.pybuildsEpisodeReflector(llm=...)and the orchestrator callsareflect(algo_episodes)with no prompt; the prompt is hard-coded (English) ineveralgo/user_memory/prompts/en/reflect.py, and no[reflection]section exists ineveros/config/default.toml.- Cluster size is governed only by
[clustering] threshold = 0.65 / time_window_days = 7.0; reflection has no per-merge member cap.
Reproduction
- Any cluster large enough that its merged narrative's cascade upsert exceeds ~15 s (memory-bound host, big cluster).
POST /api/v2/ome/trigger{"name":"reflect_episodes","timeout":180,"force":true}.- Observe
reflection_merged source_count=N→lancedb_write_lock_deadline_exceededstorm →reflection_deprecate_failed; queryepisode/atomic_fact— a non-empty subset carriesdeprecated_bytoward the (possibly removed) merged entry. - No later
reflect_episodesrun repairs these rows; cascade reconcile only rebuilds tables from markdown whose md changed, so the residuals can stay deprecated indefinitely.
Impact
- Searchable memory silently loses rows (
deprecated_by IS NULLis the default filter) that point at failed/invalid merged episodes — data effectively hidden with dangling references. - One large cluster can drive hundreds of
lancedb_write_lock_deadline_exceededwarnings per minute on every weekly run. - Merged episodes are stored in model-chosen (mixed) languages, not controllable by the operator.
Suggested fixes (independently useful)
- Transactional / compensating deprecation (L838/L868): batch the deprecate into a single atomic
updateoverentry_id IN (...), or revert the already-writtendeprecated_byon_deprecatefailure, or extend orphan detection to flag (and auto-revert) "source deprecated toward a merged entry that does not exist / was never committed". - Per-merge source cap / oversized-cluster policy: add
[reflection] max_sources_per_merge(skip or truncate clusters beyond N) so the merged narrative stays inside the write-lock budget, breaking the lock feedback loop. - Prompt & language configuration: expose
[reflection] prompt/prompt_language, plumbed throughreflect_episodes.py→EpisodeReflector.areflect(..., prompt=...)(the algo API already acceptsprompt), so operators can pin output language without patching site-packages.
Related hardening already merged: #385 (deadline must cover table-handle resolution), #392 (stall class), #379/#384/#390. Related open: #316 (asyncio deadlock between cascade + extract + LanceDB write), #398 (run_record inflation). This issue documents what we hit with the 1.2.2/1.3.1 hardening in place — the remaining gaps are in the reflection orchestration layer above those primitives.
- Lenguaje dominante
- Python
- Estrellas
- 13.3k
- Forks
- 926
- Merge medio
- 22 h 56 min
- PR fusionados (30 d)
- 27
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de EverMind-AI/EverOS
-
Dificultad 2/5 Medio día Aptitud para principiantes 62/100
EverMind-AI/EverOS#414 ·
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 4/5 3-5 días Aptitud para principiantes 35/100
EverMind-AI/EverOS#537 ·
Los mantenedores suelen responder en 1 día
-
[Bug]: 同一 EverOS 账号下,不同 AI 客户端的记忆无法共享(user 隔离问题) / Cannot share memory between two AI clients under the same EverOS account (user isolation)Posiblemente ocupada @dani1005 la tomó hace 7 días. Abiertobug
Dificultad 4/5 1-2 días Aptitud para principiantes 48/100
EverMind-AI/EverOS#528 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
memorize(): make delivery resumable per memcell so retries and replays are idempotentPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
EverMind-AI/EverOS#516 ·
Los mantenedores suelen responder en 1 día
-
[Bug]: add/flush returns 500 every time when an extraction reply quotes code with an unmatched `}` (everalgo brace-counting JSON extractor)Posiblemente ocupada @Kendrick-Song la tomó hace 10 días. Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
EverMind-AI/EverOS#515 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de EverMind-AI/EverOS
Issues similares
-
Support .aac audio uploadsPosiblemente ocupada @julien731 la tomó hoy. Abiertofeature
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
nimblehq/audio-transcriber#153 ·
-
help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
antonyrag/ragleap-core#1062 ·
-
needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
gke-labs/kube-agents#4321 ·
Los mantenedores suelen responder en 1 día
-
enhancement help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Git-Hospital/githealthcheck#61 ·
Los mantenedores suelen responder en 1 día
-
changelog investigate
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
ramnes/notion-sdk-py#433 ·