# Quy Trình 01A — Điều Phối Editor Hiệu Đính OCR (Sổ Tay Mahavihara)

> **Dành cho Architect** — thiết lập & giám sát cron job điều phối editor
> Cập nhật: 2026-06-10 | Phiên bản: **4.2** (cache-hit optimized + rules A→H + notes tối ưu)
> **Dự án:** Sổ Tay Mahavihara (book 011) — 41 trang, 14 batches

---

## I. THÔNG SỐ DỰ ÁN

| Thông số | Giá trị |
|----------|---------|
| Project path | `/home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara` |
| Source files | `cleaned_md/` (14 files, đã post-process header/footer) |
| Raw OCR | `extracted/` (RAW từ GCV) |
| Tổng batches | **14** (output-1-to-3 → output-40-to-41) |
| Tổng dung lượng | ~93 KB |
| Model editor | `google/gemini-3-flash-preview` |
| Model monitor | `deepseek/deepseek-v4-flash` |
| Cost ước tính | ~$0.02 (14 batches × 2 calls × ~23K token) |
| OCR Checklist DB | `data/ocr-checklist-vec.db` |
| Guide | `guide/03A-Hieu-Dinh-Myanmar-OCR.md` |

### Batch list

| # | Batch name | Pages | Size (bytes) |
|---|-----------|-------|-------------|
| 1 | `output-1-to-3` | 1–3 | 6,563 |
| 2 | `output-4-to-6` | 4–6 | 9,147 |
| 3 | `output-7-to-9` | 7–9 | 8,418 |
| 4 | `output-10-to-12` | 10–12 | 8,032 |
| 5 | `output-13-to-15` | 13–15 | 8,956 |
| 6 | `output-16-to-18` | 16–18 | 5,441 |
| 7 | `output-19-to-21` | 19–21 | 6,323 |
| 8 | `output-22-to-24` | 22–24 | 6,844 |
| 9 | `output-25-to-27` | 25–27 | 6,764 |
| 10 | `output-28-to-30` | 28–30 | 5,008 |
| 11 | `output-31-to-33` | 31–33 | 5,771 |
| 12 | `output-34-to-36` | 34–36 | 5,838 |
| 13 | `output-37-to-39` | 37–39 | 7,378 |
| 14 | `output-40-to-41` | 40–41 | 2,441 |

---

## II. NGUYÊN LÝ CỐT LÕI

```
┌─────────────────────────────────────────────────────────────┐
│                  KIẾN TRÚC TỐI ƯU V4.2                       │
│                                                              │
│  EDITOR AI (2 LLM CALLS — ~23K TOKEN)                        │
│  ├─ PHASE 1 (1 call): exec python → chọn batch + source     │
│  │                    + đọc GUIDE (03A) + SQLite patterns    │
│  └─ PHASE 2 (1 call): write+write+exec, TẤT CẢ CÙNG LÚC    │
│                       KHÔNG tạo text report riêng            │
│                                                              │
│  TOKEN: 434K → ~32K → ~23K (GIẢM 95%)                       │
│  lightContext: true → bỏ system prompt files                 │
│  Guide (03A) in từ exec → single source of truth             │
│  Notes: chỉ ghi lỗi thực sự (không format) → tiết kiệm output│
└─────────────────────────────────────────────────────────────┘
```

### 🔥 Cache Hit Strategy (V4.2)

Prompt cron **không chứa rules** — rules nằm trong 03A guide, được Phase 1 đọc và in ra qua exec output. Điều này:
- **Single source of truth** — chỉ cần sửa 03A, không cần đồng bộ prompt
- **Không phá cache** — guide được in qua exec output (variable), không nằm trong static prefix
- **Vẫn 2 API calls** — guide + source + patterns được in cùng 1 lần exec

```
┌──────────────────────────────────────────┐
│  CRON MESSAGE (STATIC — CACHE HIT 100%)  │
│  ┌────────────────────────────────────┐  │
│  │ Phase 1 script + Phase 2 hướng dẫn │  │
│  │ → KHÔNG chứa rules (rules ở 03A)   │  │
│  │ → Rất ngắn (~3K)                   │  │
│  └────────────────────────────────────┘  │
│         ↓ Turn 1: exec python            │
│  ┌────────────────────────────────────┐  │
│  │ EXEC OUTPUT (VARIABLE)              │  │
│  │ → GUIDE (~10K) + source + patterns  │  │
│  │ → Token mới mỗi batch               │  │
│  └────────────────────────────────────┘  │
│         ↓ Turn 2: write + write + exec   │
└──────────────────────────────────────────┘
```

| Lần chạy | Turn 1 input | Turn 2 input mới | Tổng token mới |
|----------|-------------|-----------------|---------------|
| Lần 1 | ~3K (prompt gọn) | ~10K (guide) + ~10K (source+patterns) = ~20K | ~23K |
| Lần 2+ | ~0 (cache hit) | ~10K (guide) + ~10K (source) = ~20K | ~20K |

---

## III. KIẾN TRÚC

```
┌──────────────────────────────────────────────────────┐
│                 ARCHITECT (giám sát)                  │
│  • 1 cron job ocr-processor (every 15min)             │
│  • 1 cron job ocr-monitor (every 30min)               │
│  • Cập nhật SQLite-vec DB khi Checklist thay đổi      │
│  • QA hậu kiểm (script apply-marks.py)               │
└──────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────┐
│         EDITOR AI (Gemini Flash, 2 LLM CALLS)         │
│  ┌──────────────────────────────────────────┐        │
│  │ PHASE 1: exec python                      │        │
│  │   • Chọn batch pending                    │        │
│  │   • Đọc source từ cleaned_md/             │        │
│  │   • SQLite-vec lookup patterns            │        │
│  │   → In source + patterns vào output       │        │
│  └──────────────────────────────────────────┘        │
│  ┌──────────────────────────────────────────┐        │
│  │ PHASE 2: ALL TOOLS IN 1 RESPONSE          │        │
│  │   • write → edited/gemini-flash/{B}.md    │        │
│  │   • write → edited-notes/{B}-notes.md     │        │
│  │   • exec → python update progress         │        │
│  │   ⚠️ KHÔNG tạo text report riêng!         │        │
│  └──────────────────────────────────────────┘        │
│  ⚡ 2 LLM CALLS ~23K token, ~$0.008/batch             │
└──────────────────────────────────────────────────────┘
```

---

## IV. KHỞI TẠO DỰ ÁN

### 1. Tạo progress-ocr.json

Trước khi chạy pipeline, cần tạo file tiến độ với 14 batches:

```bash
cd /home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara

python3 << 'PYEOF'
import json
batches = []
fnames = [
    "output-1-to-3", "output-4-to-6", "output-7-to-9",
    "output-10-to-12", "output-13-to-15", "output-16-to-18",
    "output-19-to-21", "output-22-to-24", "output-25-to-27",
    "output-28-to-30", "output-31-to-33", "output-34-to-36",
    "output-37-to-39", "output-40-to-41"
]
for fn in fnames:
    batches.append({
        "name": fn,
        "status": "pending",
        "qa_passed": False,
        "notes_missing": True,
        "output_file": None,
        "file_size": 0,
        "started_at": None,
        "completed_at": None
    })
data = {
    "project": "So Tay Mahavihara",
    "model": "google/gemini-3-flash-preview",
    "total_batches": 14,
    "batches": batches
}
with open("progress-ocr.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)
print(f"✅ progress-ocr.json: {len(batches)} batches")
PYEOF
```

### 2. Tạo thư mục output

```bash
mkdir -p /home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara/edited/gemini-flash
mkdir -p /home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara/edited-notes/gemini-flash
```

---

## V. THIẾT LẬP CRON JOBS

### Cron 1: `ocr-processor-so-tay-mahavihara` (Editor — mỗi 15 phút, 2 LLM calls)

```bash
openclaw cron add \
  --name ocr-processor-so-tay-mahavihara \
  --every 15m \
  --session isolated \
  --agent editor \
  --model google/gemini-3-flash-preview \
  --light-context true \
  --thinking off \
  --message 'Bạn là Editor Agent hiệu đính OCR Myanmar — TỐI ƯU 2 LLM CALLS.

═══════════════════
QUY TẮC — XEM GUIDE (in từ Phase 1)
═══════════════════

→ Toàn bộ quy tắc hiệu đính nằm trong file 03A-Hieu-Dinh-Myanmar-OCR.md
→ Phase 1 sẽ in nội dung guide ra cùng với source + patterns
→ Editor đọc guide từ Phase 1 output, không cần rules inline trong prompt này

═══════════════════
PHASE 1: CHẠY SCRIPT NÀY (1 LLM call)
═══════════════════

```python
import json, os, sqlite3, glob
from datetime import datetime, timezone, timedelta
PROJ = "/home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara"
tz = timezone(timedelta(hours=7))

with open(f"{PROJ}/guide/03A-Hieu-Dinh-Myanmar-OCR.md") as f: guide = f.read()

with open(f"{PROJ}/progress-ocr.json") as f: data = json.load(f)
edited = {os.path.basename(x).replace(".md","") for x in glob.glob(f"{PROJ}/edited/gemini-flash/*.md")}
notes = {os.path.basename(x).replace("-notes.md","") for x in glob.glob(f"{PROJ}/edited-notes/gemini-flash/*.md")}
for b in data["batches"]:
    if b["status"] == "in_progress":
        bn = b["name"]
        if bn in edited and bn in notes:
            b["status"] = "done"; b["completed_at"] = datetime.now(tz).isoformat()
            print("RECOVER:" + bn)
        else:
            started = datetime.fromisoformat(b.get("started_at","2000-01-01T00:00:00+07"))
            if (datetime.now(tz)-started).total_seconds() > 900:
                b["status"] = "pending"; print("STUCK:" + bn)
            else: print("SKIP:" + bn); exit()
        with open(f"{PROJ}/progress-ocr.json","w") as f: json.dump(data,f,indent=2,ensure_ascii=False)
target = None
for b in data["batches"]:
    bn = b["name"]
    if b["status"] == "pending" or (bn not in edited or bn not in notes):
        target = b; break
if target is None:
    print("ALL_DONE")
    exit()
target["status"] = "in_progress"; target["started_at"] = datetime.now(tz).isoformat()
BATCH = target["name"]; print("BATCH:" + BATCH)
with open(f"{PROJ}/progress-ocr.json","w") as f: json.dump(data,f,indent=2,ensure_ascii=False)

# Đọc source từ cleaned_md (đã clean header/footer)
with open(f"{PROJ}/cleaned_md/{BATCH}.md") as f: source = f.read()

conn = sqlite3.connect("/home/tuan-nguyen/.openclaw/workspace/data/ocr-checklist-vec.db")
cur = conn.cursor()
cur.execute("SELECT find_text, replace_text, note FROM patterns WHERE is_mechanical=1 ORDER BY id LIMIT 30")
mech = cur.fetchall()
cur.execute("SELECT find_text, replace_text, note, category FROM patterns WHERE is_mechanical=0 ORDER BY id LIMIT 60")
sem = cur.fetchall()
conn.close()
print(f"GUIDE:{len(guide)} chars")
print(f"PATTERNS:{len(mech)} mech + {len(sem)} semantic")
print("---GUIDE_START---")
print(guide)
print("---GUIDE_END---")
print("---SOURCE_START---")
print(source)
print("---SOURCE_END---")
print("---PATTERNS_START---")
for f_text, r_text, note, cat in sem:
    print(f"  [{cat}] {f_text} -> {r_text} | {note}")
print("---PATTERNS_END---")
```

═══════════════════
PHASE 2: 1 RESPONSE — GỌI TẤT CẢ TOOL CÙNG LÚC
═══════════════════

⚠️ PHASE NÀY PHẢI HOÀN THÀNH TRONG ĐÚNG 1 RESPONSE.
   GỌI TẤT CẢ TOOL SONG SONG TRONG CÙNG 1 LƯỢT.
   KHÔNG gọi tuần tự (gọi tool → chờ kết quả → gọi tool tiếp).

⚠️ Sau khi gọi tool, thêm 1 DÒNG TEXT NGẮN để xác nhận.
   Ví dụ: "Đã hiệu đính xong {BATCH}: N lỗi."

⚠️ Nếu Phase 1 in "ALL_DONE": KHÔNG làm gì hết.

Khi Phase 1 in "BATCH:{tên_batch}":
   → Đọc GUIDE (giữa ---GUIDE_START--- và ---GUIDE_END---) — đây là toàn bộ quy tắc hiệu đính
   → Đọc SOURCE (giữa ---SOURCE_START--- và ---SOURCE_END---) — ĐÂY LÀ VĂN BẢN CẦN HIỆU ĐÍNH. CHỈ LÀM VIỆC VỚI TEXT NÀY.
   → Đọc PATTERNS (giữa ---PATTERNS_START--- và ---PATTERNS_END---)
   → Áp dụng GUIDE + patterns để HIỆU ĐÍNH (sửa lỗi OCR, gộp dòng, format) — KHÔNG DỊCH, KHÔNG VIẾT LẠI BẰNG PALI

   → SAU ĐÓ, GỌI ĐỒNG THỜI TẤT CẢ CÁC TOOL SAU TRONG 1 RESPONSE:

   [TOOL 1] write:
     path: /home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara/edited/gemini-flash/{BATCH}.md
     content: toàn bộ file đã hiệu đính (ngắt đoạn + bold Pāli, GIỮ NGUYÊN TIẾNG MYANMAR)

   [TOOL 2] write:
     path: /home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara/edited-notes/gemini-flash/{BATCH}-notes.md
     content: bảng notes markdown (CHỈ ghi lỗi thay đổi ký tự, không ghi formatting)
     | # | Trang | Lỗi (OCR) | Đã sửa thành | Loại |
     |---|-------|-----------|-------------|------|
     | 1 | X | `chữ_sai` | `chữ_đúng` | Pali/Trợ từ/Số đếm/Nhiễu |
     ⚠️ KHÔNG ghi: gộp dòng, thêm dòng trống, bold ** (thuần format, không đổi ký tự)

   [TOOL 3] exec python (thay BATCH bằng tên thật từ output Phase 1):
     import json
     from datetime import datetime, timezone, timedelta
     PROJ = "/home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara"
     BATCH = "{BATCH}"
     with open(f"{PROJ}/progress-ocr.json") as f: data = json.load(f)
     for b in data["batches"]:
         if b["name"] == BATCH: b["status"] = "done"; b["completed_at"] = datetime.now(timezone(timedelta(hours=7))).isoformat(); break
     with open(f"{PROJ}/progress-ocr.json","w") as f: json.dump(data,f,indent=2,ensure_ascii=False)
     print("OK")

   → SAU TOOL, thêm 1 dòng text ngắn xác nhận hoàn thành.' \
  --timeout-seconds 600 \
  --delivery "announce,telegram,412242443"
```

**Cấu trúc payload quan trọng:**
```json
{
  "lightContext": true,
  "timeoutSeconds": 600,
  "model": "google/gemini-3-flash-preview",
  "thinking": "off"
}
```

> ⚠️ **`thinking: "off"` là bắt buộc** — Google API mặc định bật reasoning mode, khiến output tokens tăng 2-3 lần (9K→4.5K sau khi tắt) mà không cải thiện chất lượng hiệu đính. Tiết kiệm ~$0.017/batch.

---

### Cron 2: `ocr-monitor-so-tay-mahavihara` (Architect — mỗi 30 phút)

```bash
openclaw cron add \
  --name ocr-monitor-so-tay-mahavihara \
  --every 30m \
  --session isolated \
  --agent architect \
  --model deepseek/deepseek-v4-flash \
  --light-context true \
  --message '## OCR Monitor — So Tay Mahavihara

### 1. Check progress
```python
import json, glob
PROJ = "/home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara"
with open(f"{PROJ}/progress-ocr.json") as f: data = json.load(f)
from collections import Counter
c = Counter(b["status"] for b in data["batches"])
n = len(glob.glob(f"{PROJ}/edited-notes/gemini-flash/*.md"))
total = data["total_batches"]
print(f"Done={c.get("done",0)}/{total} InProg={c.get("in_progress",0)} Pend={c.get("pending",0)} Notes={n}")
```

### 2. Detect stuck / pipeline dead
- in_progress > 20min + no output → STUCK → reset
- in_progress == 0 + pending > 0 + idle > 20min → PIPELINE DEAD

### 3. Actions
- STUCK: reset → pending
- PIPELINE DEAD: enable processor
- ALL DONE: disable processor cron + self-disable ocr-monitor

### 4. Self-disable khi ALL DONE
When done == total_batches (in data["batches"]) and pending == 0 and in_progress == 0:
  1. Use cron tool: find job "ocr-processor-so-tay-mahavihara" → update enabled=false
  2. Use cron tool: find job "ocr-monitor-so-tay-mahavihara" → update enabled=false
  3. Report "✅ Pipeline complete — all crons disabled."

### 5. Report 1 dòng' \
  --timeout-seconds 120 \
  --delivery "announce,telegram,412242443"
```

> ⚠️ Cron monitor hiện đang disabled — chỉ enable khi pipeline đang chạy.

---

## VI. TẠO PROGRESS FILE & OUTPUT DIRS

Trước khi enable cron, architect cần chạy các lệnh khởi tạo ở Mục IV để tạo `progress-ocr.json` và thư mục `edited/gemini-flash/`, `edited-notes/gemini-flash/`.

---

## VII. SQLITE-VEC OCR CHECKLIST

**Database:** `/home/tuan-nguyen/.openclaw/workspace/data/ocr-checklist-vec.db`

| Category | Mô tả | Số lượng |
|----------|-------|----------|
| Pali | Thuật ngữ Pāli | ~80 |
| CoHoc | Lỗi dấu phụ/spacing/ký tự | ~180 |
| SaiNghia | Từ sai nghĩa | ~30 |
| Khac | Lỗi khác | ~20 |

Database này dùng chung cho tất cả dự án OCR Myanmar. Rebuild khi OCR-Checklist thay đổi.

---

## VIII. GIÁM SÁT CHI PHÍ

| Phiên bản | Token/batch | 14 batch | LLM calls/batch | Chi phí |
|-----------|------------|----------|-----------------|---------|
| V4.2 (guide in exec) | ~23K | ~322K | 2 | **~$0.11** |

**Chi tiết:**
- Mỗi batch: ~23K token (input ~13K + output ~10K)
- 14 batches × $0.008/batch = $0.11
- Giá model: google/gemini-3-flash-preview: $0.50/1M input, $3.00/1M output

---

## IX. TỔNG KẾT

```
V4.2 — SỔ TAY MAHAVIHARA (14 BATCHES)

  EDITOR AI (2 LLM CALLS)               ARCHITECT
  ┌──────────────────────────┐         ┌──────────────┐
  │ PHASE 1: exec python     │         │ Giám sát 30ph │
  │  • Chọn batch + source   │         │ QA hậu kiểm   │
  │  • Đọc GUIDE (03A)       │         │ Cập nhật DB   │
  │  • SQLite patterns       │         │ Fix sự cố     │
  ├──────────────────────────┤         └──────────────┘
  │ PHASE 2: 1 RESPONSE      │
  │  • write edited file     │
  │  • write notes (chỉ lỗi) │
  │  • exec update progress  │
  │  ⚠️ KHÔNG text report!   │
  │ GỌI TẤT CẢ CÙNG LÚC      │
  │ ~23K token, ~$0.008/batch│
  └──────────────────────────┘
```

> **Thay đổi chính cho Sổ Tay Mahavihara:**
> 1. Project path: `/home/tuan-nguyen/.openclaw/workspace/011-so-tay-mahavihara`
> 2. Source dir: `cleaned_md/` (đã post-process header/footer) thay vì `extracted/`
> 3. 14 batches, ~93 KB total
> 4. Chi phí ước tính: ~$0.11 cho toàn bộ pipeline

> 📖 **File liên quan:**
> - `scripts/apply-marks.py` — Script QA hậu kiểm (từ dự án 003, có thể dùng lại)
> - `scripts/merge-lines.py` — Script gộp dòng bị OCR cắt ngang
> - `data/ocr-checklist-vec.db` — SQLite-vec database (dùng chung)
> - `progress-ocr.json` — Tiến độ pipeline (cần tạo)
> - `guide/03A-Hieu-Dinh-Myanmar-OCR.md` — Quy tắc hiệu đính
