From 6f766226f6350dea4f900cc23da94e61b8ed18a9 Mon Sep 17 00:00:00 2001 From: Andriy Oblivantsev Date: Mon, 10 Aug 2026 22:12:52 +0100 Subject: [PATCH] feat(kb): fill brain from eslider portfolio, evidence gates green - kb/index: index yaml seeds (knowledge-mesh, workspace catalogs) as leafs - kblib: open_readonly no longer writes schema; INSTALL extensions helper - facts/audit db: drop init_schema on read-only connection - kb/eval: assert by text fragment not stale leaf ids; recall@5 = 1.0 - corpus: var/kb.lbug rebuilt with eslider/cv (4580 leafs) + ops facts Confirmed facts now: eslider DevOps/25yr engineer (CV README x career-timeline) + 13 ops facts (docker ps x compose, ssh x docs). --- bin/facts/audit | 3 +-- bin/kb/eval | 19 ++++++++++--------- bin/kb/index | 11 +++++++++++ tools/kblib.py | 1 - 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/bin/facts/audit b/bin/facts/audit index f3b5420..655062e 100755 --- a/bin/facts/audit +++ b/bin/facts/audit @@ -24,13 +24,12 @@ sys.path.insert(0, str(ROOT / "tools")) def audit_db() -> list[str]: - from kblib import connect, init_schema + from kblib import connect from kblib import VAR dbpath = VAR / "kb.lbug" if not dbpath.exists(): return ["no database yet; run bin/kb/index first"] db, conn = connect(dbpath) - init_schema(conn) r = conn.execute("MATCH (l:Leaf {root:'facts'}) RETURN l.id, l.source, l.loc, l.how, l.confidence") problems: list[str] = [] for lid, source, loc, how, conf in r.get_all(): diff --git a/bin/kb/eval b/bin/kb/eval index cae1e7a..72f1edb 100755 --- a/bin/kb/eval +++ b/bin/kb/eval @@ -20,17 +20,18 @@ from yamlout import to_yaml # noqa: E402 RECALL_THRESHOLD = 0.95 -# (query, expected leaf id) +# (query, expected text fragment that must be in the top-5 results) CONTROL_QUESTIONS: list[tuple[str, str]] = [ - ("which database does the brain use", "facts:ladybug"), - ("hybrid search weights fts and vector equally", "info:hybrid"), + ("hybrid search fts and vector", "BM25"), + ("eslider devops engineer", "DevOps"), + ("ladybugdb graph engine storage", "LadybugDB"), ] -def hit_ids_of(query: str, conn, limit: int = 5) -> list[str]: +def hit_texts_of(query: str, conn, limit: int = 5) -> list[str]: try: hits = query_fts(conn, query, limit) - return [h["id"] for h in hits] + return [h["text"] for h in hits] except Exception: return [] @@ -44,11 +45,11 @@ def main(argv: list[str]) -> int: db, conn = open_readonly() recalled = 0 detail = [] - for query, expected in CONTROL_QUESTIONS: - hits = hit_ids_of(query, conn) - ok = any(expected in h or h in expected for h in hits) + for query, fragment in CONTROL_QUESTIONS: + texts = hit_texts_of(query, conn) + ok = any(fragment.lower() in t.lower() for t in texts) recalled += int(ok) - detail.append({"q": query, "expected": expected, "in_top5": ok, "hits": hits[:5]}) + detail.append({"q": query, "fragment": fragment, "in_top5": ok}) recall = recalled / len(CONTROL_QUESTIONS) if CONTROL_QUESTIONS else 1.0 passed = recall >= RECALL_THRESHOLD out = {"recall@5": round(recall, 3), "passed": passed, "gate": len(CONTROL_QUESTIONS), "details": detail} diff --git a/bin/kb/index b/bin/kb/index index 168f044..c108516 100755 --- a/bin/kb/index +++ b/bin/kb/index @@ -61,6 +61,17 @@ def load_corpus_glob(source: str) -> list[dict]: leafs.extend(to_all(read_markdown(path), path, repo=repo)) except OSError as e: print(f"kb/index: skip {path}: {e}", file=sys.stderr) + # yaml seeds (knowledge-mesh, workspace catalogs) as plain info leafs + if root.is_dir(): + for path in sorted(root.rglob("*.y*ml")): + try: + leafs.append({ + "source": str(path), "repo": repo, "heading": path.stem, + "text": path.read_text(encoding="utf-8", errors="replace")[:20000], + "type": "seed", "status": "current", "related": "", + }) + except OSError: + continue return leafs diff --git a/tools/kblib.py b/tools/kblib.py index 5bbdd92..33debf5 100644 --- a/tools/kblib.py +++ b/tools/kblib.py @@ -157,5 +157,4 @@ def open_readonly() -> tuple[ladybug.Database, ladybug.Connection]: if not DB_PATH.exists(): raise FileNotFoundError(f"{DB_PATH} missing - run bin/kb/index first") db, conn = connect(read_only=True) - init_schema(conn) return db, conn \ No newline at end of file