# חיפוש סמנטי — ארכיטקטורה ומימוש

## עיקרון-על: Embeddings דרך שכבת ה-BRAIN

האפליקציה **לא** פונה ישירות ל-OpenRouter/OpenAI. כל בקשות ה-embedding עוברות דרך
שירות ה-BRAIN (‏brain-mcp), שכבר מכיל `OpenRouterEmbeddingProvider`
(‏`brain_mcp/embed/provider.py`):

```
App (embedding-client.ts)
   │  POST {model, input[]}          (OpenAI-compatible contract)
   ▼
BRAIN embedding service  ──►  https://openrouter.ai/api/v1/embeddings
   model: openai/text-embedding-3-small · 1536 dims · ~$0.02/1M tokens
```

יתרונות הריכוז ב-BRAIN: ניהול מפתחות במקום אחד, מדידת עלויות, caching משותף,
והחלפת מודל בנקודה אחת. ‏Fallback ישיר ל-OpenRouter קיים רק לחירום
(`OPENROUTER_API_KEY`), עם אזהרה בלוג.

### משתני סביבה
| משתנה | תפקיד |
|---|---|
| `EMBEDDING_SERVICE_URL` | כתובת שירות ה-BRAIN (למשל `http://brain-host:8760/embed`) |
| `EMBEDDING_MODEL` | ברירת מחדל `openai/text-embedding-3-small` (1536d) |
| `OPENROUTER_API_KEY` | ‏fallback חירום בלבד |
| `CONSTRAINT_MODEL` | מודל לניתוח שאילתות (ברירת מחדל claude-haiku) |

## צינור החיפוש (5 שלבים)

1. **טקסט מאוחד** (`unified-text.ts`) — שם, תיאור, מרכיבים (כולל קבוצות), הוראות,
   תגיות, תזונה, אלרגנים, זמן וקושי — מוזן לוקטור אחד. נשמר ב-`recipe_embeddings.unified_text`.
2. **Embedding בעדכון** (`embedding-worker.ts`) — תור pg-boss על אותו PostgreSQL;
   ‏`singletonKey` מאחד עריכות רצופות; ‏retry עם backoff.
3. **ניתוח שאילתה** (`query-constraints.ts`) — LLM עם סכמת zod מחלץ:
   אלרגנים להוצאה ("בלי גלוטן"), מרכיבים עם/בלי, "עד 30 דקות", קושי, תזונה —
   ו-`cleanedQuery` לחיפוש הסמנטי.
4. **שאילתת pgvector** (`search-recipes.ts`) — ‏cosine ‏(`<=>`) על אינדקס HNSW,
   משולב עם **מסננים קשיחים ב-SQL**. ⚠️ אלרגנים הם `AND NOT (allergens && ...)`
   — הסרה מוחלטת, לעולם לא רק הורדת ציון.
5. **Re-ranking + סיבת התאמה** — ציון משוקלל:
   `0.55·semantic + 0.15·keyword(trigram) + 0.15·rating + 0.15·popularity`,
   ולכל תוצאה משפט: "מתאים כי הוא ללא גלוטן ומוכן בתוך 25 דקות".

## טיפול בעברית
- ‏embedding רב-לשוני (text-embedding-3-small מטפל בעברית היטב) — החיפוש הסמנטי
  עובד גם כשהמתכון באנגלית והשאילתה בעברית.
- התאמת מילות מפתח: ‏pg_trgm ‏(trigram) — עמיד לניקוד חלקי ולאותיות סופיות.
- מילון `ingredients.aliases` מכיל וריאציות כתיב ("עגבניה"/"עגבנייה").

## בטיחות
המידע על אלרגנים מוצג עם אזהרה קבועה: אינו תחליף לבדיקת רכיבים בפועל.
כשל בחילוץ אילוצים ⇒ החיפוש ממשיך סמנטית אבל ה-UI מציג שלא הופעל סינון אלרגנים.
