# Round 12 — Tower J05-J09 · The 5W Parallel Agents

**Date**: 2026-04-30
**Time**: 1.5h budget
**Status**: ✅ COMPLETE
**Round Type**: Tower Floors

---

## 🎯 הקומות בהן עוסק סבב זה

הקומה הקסומה. **5 agents במקביל** מחלצים 5 ה-Wי (Who/What/When/Where/Why). זה ה-streaming heart של ה-press flow — המשתמש רואה 5 שדות מתמלאים בו זמנית. המגדל **מאיץ** כי כל W הוא agent עצמאי.

| # | Station | Field | Latency target |
|---|---|---|---|
| **J05** | Who Extractor | אנשים, גופים, מקומות יעד | <1.5s |
| **J06** | What Extractor | פעולה, אירוע, תוצאה | <1.5s |
| **J07** | When Extractor | זמן ISO + relative + recurring | <1.5s |
| **J08** | Where Extractor | מקום פיזי + נ.צ + region | <1.5s |
| **J09** | Why Extractor | סיבה, מטרה, הקשר | <1.5s |

**Critical insight**: מכיוון ש-5 agents רצים **במקביל**, ה-total latency הוא max(j05..j09), לא sum. זה הבדל בין 7.5 שניות → 1.5 שניות. **5x speedup**.

---

## 🏗️ ארכיטקטורת הריצה במקביל

```
                    [J04 Desk Router output]
                              ⬇
                  ┌───────────┴───────────┐
                  ▼                       ▼
         asyncio.gather()         (Python 3.13 native)
                  │
        ┌─────────┼─────────┬──────────┬──────────┐
        ▼         ▼         ▼          ▼          ▼
       [J05]    [J06]    [J07]      [J08]      [J09]
        Who     What     When       Where      Why
         │       │         │          │          │
         ▼       ▼         ▼          ▼          ▼
      Gemini  Gemini    Gemini     Gemini     Gemini
      Flash   Flash     Flash      Flash      Flash
      ~800ms  ~1000ms   ~600ms     ~700ms     ~1200ms
         │       │         │          │          │
         └───────┴─────────┼──────────┴──────────┘
                           ▼
                    Aggregated W5 object
                    + 5w_done event
```

---

## 🤖 J05 · Who Extractor

### Output schema
```json
{
  "primary": [
    {
      "name": "משרד הביטחון",
      "type": "organization",
      "role": "speaker",
      "confidence": 0.98
    },
    {
      "name": "יואב גלנט",
      "type": "person",
      "role": "subject",
      "title": "שר הביטחון",
      "confidence": 0.94
    }
  ],
  "secondary": [
    { "name": "ארה\"ב", "type": "country", "role": "context", "confidence": 0.81 }
  ],
  "raw_entities": [...]
}
```

### Why "primary" + "secondary"?
- **Primary** = מי הנושא של ההודעה (max 3 entities)
- **Secondary** = מי מוזכר בהקשר (יכול להיות 10+)
- ה-Writer (J13-J16) משתמש ב-primary לכותרת + secondary לגוף

### A2UI streaming
ה-LLM מחזיר structured output, אבל לא מחכים לסוף. כל entity שמופק → emit immediate:

```jsonl
{"version":"v0.9","updateDataModel":{"surfaceId":"...","path":"/w5/who/primary/0","value":{"name":"משרד הביטחון","type":"organization"}}}
{"version":"v0.9","updateDataModel":{"surfaceId":"...","path":"/w5/who/primary/1","value":{"name":"יואב גלנט","type":"person"}}}
```

ה-`FiveWBar` component מציג אנימציית typewriter כל פעם שערך מוכנס לארגז `/w5/who/*`.

---

## ⚙️ J06 · What Extractor

### Output schema
```json
{
  "action": "אישרה רכישה",
  "object": "12 סוללות כיפת ברזל",
  "result": "תקציב 3.2 מיליארד ש\"ח",
  "verb_tense": "past_simple",
  "implications": [
    "הגדלה משמעותית של מערך הגנה אווירית",
    "שיתוף פעולה אסטרטגי עם רפאל"
  ],
  "confidence": 0.92
}
```

### Why specific structure?
- **action + object + result** = זהו "5W של ה-What" — אילו פעולות נעשו
- **implications** = כותב יודע איך לפתח (J13-J16)
- **verb_tense** = SEO + grammar consistency

---

## 📅 J07 · When Extractor

ה-W הקשה ביותר. צריך:

### Output schema
```json
{
  "primary_event": {
    "iso": "2026-04-30T22:00:00+03:00",
    "natural": "אמש בשעות הערב",
    "precision": "hour"
  },
  "publication": {
    "iso": "2026-04-30T23:30:00+03:00",
    "embargo_until": null
  },
  "timeline": [
    { "event": "החלטה התקבלה", "when": "2026-04-29" },
    { "event": "פורסמה", "when": "2026-04-30T22:00:00+03:00" }
  ],
  "future_events": [],
  "is_recurring": false,
  "confidence": 0.96
}
```

### הקושי — Hebrew time normalization
```
"אמש"        → today - 1 day, evening (~20:00-22:00)
"בערב הקרוב" → tomorrow ~20:00
"השבוע"      → ISO week + 7 day window
"ביום ב' הקרוב" → next Monday
"ערב יום הזיכרון" → Yom HaZikaron eve (Hebrew calendar lookup)
```

נדרשת **lookup table** + LLM hybrid:
1. Library: `hebcal` (Hebrew calendar)
2. Library: `dateutil.parser` (general)
3. LLM fallback for ambiguous cases

### A2UI emission
```json
{"version":"v0.9","updateDataModel":{"surfaceId":"...","path":"/w5/when","value":{"iso":"2026-04-30T22:00:00+03:00","natural":"אמש בשעות הערב","precision":"hour"}}}
```

ה-FiveWBar מציג את "natural" אבל שומר ב-data את "iso" לJSON-LD.

---

## 📍 J08 · Where Extractor

### Output schema
```json
{
  "location": {
    "name": "תל אביב",
    "type": "city",
    "country": "IL",
    "region": "Tel Aviv District",
    "coordinates": { "lat": 32.0853, "lng": 34.7818 },
    "wikidata_id": "Q33935",
    "confidence": 0.99
  },
  "all_locations": [
    { "name": "ירושלים", "type": "city", ... },
    { "name": "וושינגטון", "type": "city", "country": "US", ... }
  ],
  "is_remote_event": false
}
```

### Two-stage approach
1. **NER (spaCy)** — fast, identifies all location strings
2. **Geocoder (Nominatim/OSM)** — converts strings to lat/lng + region

ה-LLM **לא** מחזיר coordinates ישירות (hallucination risk).

### A2UI emission
```json
{"version":"v0.9","updateDataModel":{"surfaceId":"...","path":"/w5/where","value":{"name":"תל אביב","coordinates":{"lat":32.08,"lng":34.78}}}}
{"version":"v0.9","updateComponents":{"surfaceId":"...","components":[
  {"id":"map-thumb","component":"MapThumbnail","center":{"lat":32.08,"lng":34.78},"zoom":11}
]}}
```

ה-`MapThumbnail` component מציג מפה קטנה (Mapbox static API). זה מ-**Round 23** (Google Maps as Jason).

---

## 🤔 J09 · Why Extractor (הקשה ביותר)

### Output schema
```json
{
  "stated_reason": "להתחזק לאור איומים מאיראן",
  "implicit_reasons": [
    "תקציב הביטחון השנתי דורש הצדקה ציבורית",
    "ועדה ביטחונית לחוצה לאחר תקיפה אחרונה"
  ],
  "context_links": [
    { "topic": "מתיחות עם איראן", "weight": 0.9 },
    { "topic": "פרשת חוזי רכש", "weight": 0.4 }
  ],
  "confidence": 0.78
}
```

### Why low confidence acceptable?
"Why" הוא הכי עמום. לפעמים אין reason ב-text — צריך להוציא מההקשר. לכן:
- **stated** — מה שכתוב מפורשות
- **implicit** — מה שה-LLM מסיק (סומן ברור!)
- **context_links** — קישורים לסיפורים קודמים

ה-Writer יכול להחליט אם להזכיר את ה-implicit (לחשיבה ביקורתית) או לא.

---

## 📊 Streaming Strategy — איך ה-UI מתעדכן

המקרה הקסום: **5 agents מתעדכנים במקביל**. ה-5w_partial events מגיעים מבולבלים מבחינת זמן:

```
t=0.5s:  J07 done → "When: אמש"
t=0.7s:  J08 partial → "Where: תל אבי..."
t=0.8s:  J05 partial → "Who: משרד הב..."
t=0.9s:  J08 done → "Where: תל אביב"
t=1.0s:  J05 done → "Who: משרד הביטחון, יואב גלנט"
t=1.1s:  J06 partial → "What: אישרה רכ..."
t=1.3s:  J06 done → "What: אישרה רכישה של 12 סוללות..."
t=1.5s:  J09 done → "Why: לאור איומים מאיראן"
t=1.5s:  5w_done event fires
```

ה-FiveWBar מציג כל field בtypewriter — נראה כאילו 5 anim במקביל. **אנושיות מסוף**.

---

## 🔌 A2UI Catalog Entries — J05-J09

```json
{
  "components": {
    "FiveWBar": {
      "type": "object",
      "description": "5 cells displaying 5W extraction in real-time",
      "properties": {
        "fields": {
          "type": "array",
          "items": { "type": "string", "enum": ["who","what","when","where","why"] }
        },
        "values": {
          "type": "object",
          "properties": {
            "who":   { "$ref": "#/$defs/DynamicString" },
            "what":  { "$ref": "#/$defs/DynamicString" },
            "when":  { "$ref": "#/$defs/DynamicString" },
            "where": { "$ref": "#/$defs/DynamicString" },
            "why":   { "$ref": "#/$defs/DynamicString" }
          }
        },
        "streaming": { "type": "boolean", "default": true },
        "states": {
          "type": "object",
          "description": "Per-field state for typewriter cursor",
          "properties": {
            "who":   { "type": "string", "enum": ["idle","streaming","done","error"] },
            "what":  { "type": "string", "enum": ["idle","streaming","done","error"] },
            "when":  { "type": "string", "enum": ["idle","streaming","done","error"] },
            "where": { "type": "string", "enum": ["idle","streaming","done","error"] },
            "why":   { "type": "string", "enum": ["idle","streaming","done","error"] }
          }
        }
      }
    }
  }
}
```

---

## 💰 Cost Analysis — 5W Parallel

| Station | Tokens in | Tokens out | Cost per call | Concurrency |
|---|---|---|---|---|
| J05 Who | ~500 | ~150 | $0.00015 | parallel |
| J06 What | ~500 | ~150 | $0.00015 | parallel |
| J07 When | ~500 | ~100 | $0.00012 | parallel |
| J08 Where | ~500 | ~100 | $0.00012 | parallel |
| J09 Why | ~500 | ~200 | $0.00018 | parallel |
| **Sum** | ~2,500 | ~700 | **$0.00072** | |

**Latency**: max(0.8, 1.0, 0.6, 0.7, 1.2) = **1.2 sec** (not 4.3 sec sequential).

ב-100K הודעות ביום:
- **Daily cost**: $72
- **Monthly**: $2,160

---

## ✅ Closure

- [x] J05-J09 ארכיטקטורה מקבילית מתועדת
- [x] schemas של 5 השדות מוגדרים
- [x] Hebrew time + geocoding workflows מוסברים
- [x] FiveWBar streaming pattern מתועד
- [x] Cost + latency חושבו

✅ **Round 12 closed. 9/54 floors documented.**

---

## 🛣️ Next: Round 13 — J10-J12 (Vector + FactCheck + Tone)
