Skip to content

Commit 3be36e6

Browse files
RMANOVclaude
andcommitted
fix(search): address code review — vec_sync in delete_observations, docstring, constants
- Add vec_sync after fts_sync in delete_observations (stale embedding bug) - Update search_nodes docstring to reflect hybrid search behavior - Extract MAX_OBS_FOR_EMBEDDING constant (was magic number 20) - Log vec_remove_entity failures instead of silently swallowing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5826be7 commit 3be36e6

2 files changed

Lines changed: 14 additions & 7 deletions

File tree

server.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,11 @@ def delete_observations(deletions: list[dict[str, Any]]) -> str:
356356
)
357357
deleted += cur.rowcount
358358
_fts_sync(conn, eid)
359+
if _VEC_AVAILABLE:
360+
try:
361+
_vec_sync(conn, eid)
362+
except Exception:
363+
pass
359364

360365
logger.info("delete_observations: %d deleted", deleted)
361366
return json.dumps({"deleted": deleted})
@@ -414,11 +419,12 @@ def read_graph() -> str:
414419

415420
@mcp.tool()
416421
def search_nodes(query: str, project: str | None = None) -> str:
417-
"""Search the knowledge graph using FTS5 BM25-ranked full-text search.
422+
"""Search the knowledge graph using hybrid BM25 + semantic search.
418423
419-
Returns matching entities with their observations, ranked by relevance.
420-
Applies multi-signal re-ranking (recency, project affinity, graph proximity,
421-
observation richness, canonical facts, session context).
424+
When sqlite-vec is installed, combines FTS5 keyword matching with vector
425+
cosine similarity via Reciprocal Rank Fusion. Falls back to FTS5-only
426+
otherwise. Results are re-ranked with 6 contextual signals (recency,
427+
project affinity, graph proximity, richness, canonical facts, session).
422428
"""
423429
fts_q = _fts_query(query)
424430
with _get_conn() as conn:

vec_search.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
_model: SentenceTransformer | None = None
4747
_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
4848
EMBEDDING_DIM = 384
49+
MAX_OBS_FOR_EMBEDDING = 20 # MiniLM-L6-v2 has 256-token limit; cap observations to fit
4950

5051

5152
def _get_model() -> SentenceTransformer:
@@ -108,7 +109,7 @@ def init_vec_table(conn: sqlite3.Connection) -> bool:
108109

109110
def _entity_text(name: str, entity_type: str, observations: list[str]) -> str:
110111
"""Compose the text to embed for an entity."""
111-
obs_str = ". ".join(observations[:20])
112+
obs_str = ". ".join(observations[:MAX_OBS_FOR_EMBEDDING])
112113
return f"{name} ({entity_type}): {obs_str}"
113114

114115

@@ -159,8 +160,8 @@ def vec_remove_entity(conn: sqlite3.Connection, entity_id: int) -> None:
159160
try:
160161
if load_vec(conn):
161162
conn.execute("DELETE FROM entity_embeddings WHERE rowid = ?", (entity_id,))
162-
except Exception:
163-
pass
163+
except Exception as e:
164+
logger.debug("vec_remove_entity(%d) failed: %s", entity_id, e)
164165

165166

166167
# ── Vector search ──────────────────────────────────────────────────────

0 commit comments

Comments
 (0)