docs(voyage): mark stage C complete + record empirical fixes
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 10s

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 20:16:13 +00:00
parent 8a815ecff5
commit d12cdb1fad

View File

@@ -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 המקורי (תכנון, לרפרנס)
### הבעיה שהוא פותר