From d12cdb1fad9c1ed84243a00c39d57c0dfda8f00f Mon Sep 17 00:00:00 2001 From: Chaim Date: Sun, 3 May 2026 20:16:13 +0000 Subject: [PATCH] docs(voyage): mark stage C complete + record empirical fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage C of the voyage-upgrades-plan shipped to production on 2026-05-03. The doc now leads with the final state and the two empirical corrections vs the original plan: 1. Reciprocal Rank Fusion replaces weighted-sum hybrid merge. voyage-3 cosines (~0.4-0.5) systematically outscale voyage-multimodal-3 cosines (~0.20-0.25); a weighted sum lets text dominate even when image is the better signal. RRF is rank-based and robust to scale differences. 2. Chunker now propagates page_number end-to-end (extractor returns per-page offsets, chunker tags each chunk by its first character's page). A retrofit script backfills page_number on existing document_chunks without re-OCR — uses the stored documents.extracted_text plus PyMuPDF direct text reads as page anchors (linear interpolation for OCR-only pages). Production state on cases 8174-24 + 8137-24: 419 page-image embeddings, 819 chunks tagged with page_number, MULTIMODAL_ENABLED=true in Coolify env, hybrid search verified A/B against text-only baseline. The original stage C plan section is retained below for reference. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/voyage-upgrades-plan.md | 68 +++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/docs/voyage-upgrades-plan.md b/docs/voyage-upgrades-plan.md index 042e447..acfba20 100644 --- a/docs/voyage-upgrades-plan.md +++ b/docs/voyage-upgrades-plan.md @@ -163,7 +163,73 @@ queries בשעה במקרה רגיל) — מתחת ל-1% מהמכסה. --- -## שלב C — voyage-multimodal-3.5 (לביצוע בשיחה החדשה) +## שלב C — voyage-multimodal-3 (✅ בוצע 2026-05-03) + +> **תיקון שם המודל מהתכנית המקורית**: השם הסופי הוא +> `voyage-multimodal-3` (לא 3.5). הוצמד לזה ש-POC #3 הריץ. + +### מצב סופי בייצור + +- `MULTIMODAL_ENABLED=true` ב-Coolify env +- Schema V9 ב-DB (document_image_embeddings + precedent_image_embeddings) +- 419 page-image embeddings על 8174-24 (146) + 8137-24 (273) +- 819 text chunks קיבלו page_number (100% retrofit) +- RRF hybrid merge עם boost text+image פעיל + +### שינויים מהתכנית המקורית — שני תיקונים אמפיריים + +1. **Score scaling — Reciprocal Rank Fusion במקום weighted sum.** + ה-cosine של voyage-3 (~0.4-0.5) שיטתית גבוה מ-voyage-multimodal-3 + (~0.20-0.25). A/B ראשון על 7 שאילתות הראה: עם 0.65/0.35 weighted + sum ו-MULTIMODAL_ENABLED=true, **0** image rows הופיעו ב-top-5, + image side פשוט הוצף. עברנו ל-RRF (`rrf_score = w / (k + rank)`) + שעמיד לסקיילים שונים. תוצאה: 5/5 results עם image contribution + בכל שאילתה. + +2. **Page tracking — chunker חדש + retrofit ל-819 chunks קיימים.** + ה-chunker הישן זרק את ה-page_number של chunks. בלעדיו ה-boost + text+image (join על `(document_id, page_number)`) לא יכול לפעול. + נוסף `page_offsets` ל-`extractor.extract_text` (משלשה במקום זוג — + מעודכן ב-6 callers); chunker מקבל אותו ומסמן page לכל chunk לפי + offset של התווים הראשונים שלו. retrofit ל-chunks קיימים + (`scripts/backfill_chunk_pages.py`) עובד **בלי re-OCR** — + משתמש ב-stored extracted_text כמקור (matches existing chunk + content verbatim) ו-PyMuPDF direct text reads כעיגוני page + boundaries; pages סרוקים ללא טקסט ישיר עוברים אינטרפולציה. + +### למה NOT לעשות re-OCR ב-retrofit + +ניסיון ראשון השתמש ב-`extractor.extract_text` להפיק page_offsets +חדשים. תוצאה: 1/29 chunks נמצאו (28 not found), כי OCR של Google +Vision לא דטרמיניסטי — ה-OCR החדש שונה מה-OCR שהפיק את ה-chunks +המקוריים. הגרסה החדשה משתמשת ב-stored `documents.extracted_text` +שמתאים לחלוטין לתוכן ה-chunks. עלות: $0 (לעומת ~$0.0015/page). + +### Files שהשתנו (יחסית למה שהמסמך הזה תיכנן) + +קוד שנכתב/שונה (5 commits, 242f668 → 8a815ec): +- `mcp-server/src/legal_mcp/config.py` — flags MULTIMODAL_* +- `mcp-server/src/legal_mcp/services/extractor.py` — render + page_offsets +- `mcp-server/src/legal_mcp/services/embeddings.py` — embed_images +- `mcp-server/src/legal_mcp/services/db.py` — schema V9 + 4 store/search funcs +- `mcp-server/src/legal_mcp/services/chunker.py` — page tracking +- `mcp-server/src/legal_mcp/services/processor.py` — ingest integration +- `mcp-server/src/legal_mcp/services/precedent_library.py` — same +- `mcp-server/src/legal_mcp/services/hybrid_search.py` — חדש, RRF orchestrator +- `mcp-server/src/legal_mcp/tools/search.py` — wired to hybrid +- `mcp-server/src/legal_mcp/tools/documents.py` + `tools/workflow.py` + `web/app.py` — extract_text triple unpack +- `scripts/multimodal_backfill.py` + `scripts/backfill_chunk_pages.py` — חדשים + +### מה נשאר (deferred) + +- UI thumbnails בתוצאות חיפוש (לא חוסם — דפנה מקבלת page numbers) +- Backfill על שאר הקורפוס (מעבר ל-2 התיקים): לא דחוף, אפשר per-case +- `text_weight` תיאום: כרגע 0.5 (vanilla RRF). אם דפנה תגיד שהיא רואה + יותר מדי image-influence, מעלים ל-0.55-0.6 דרך env בלי deploy. + +--- + +## שלב C המקורי (תכנון, לרפרנס) ### הבעיה שהוא פותר