43 KiB
ADR-023A: Unified AI Architecture — Model Revision (gemma4:e2b, 2-Model Stack)
Status: Accepted Date: 2026-05-15 Decision Makers: Development Team, System Architect, Security Team, AI Integration Lead Supersedes Revision: ADR-023 v1.1 (2026-05-14) Related Documents:
- Glossary
- Data Dictionary
- Legacy Data Migration Plan
- n8n Migration Setup Guide
- ADR-016: Security & Authentication
- ADR-019: Hybrid Identifier Strategy
- RAG Implementation Guide v1.1.2
- ADR-023: Unified AI Architecture (Base)
หมายเหตุ: ADR-023A เป็นสำเนาอัปเดตของ ADR-023 v1.1 โดยปรับปรุงชุดโมเดล AI เพื่อให้ใช้งาน VRAM ≤ 8GB ได้อย่างมีเสถียรภาพ ลดจาก 3 โมเดล (gemma4:9b + Typhoon Local + nomic-embed-text) เหลือ 2 โมเดล (gemma4:e2b + nomic-embed-text) โดย gemma4:e2b ทำหน้าที่ครอบคลุมทั้ง General Inference และ OCR Post-processing/Extraction แทน Typhoon Local
🎯 Gap Analysis & Purpose
ปิด Gap จากเอกสาร:
- Product Vision v1.8.5 - Section 3.1, 3.2, 3.3: ความต้องการยกระดับประสิทธิภาพการนำเข้าเอกสารเก่าและการจัดการเอกสารใหม่ด้วย AI Intelligence
- เหตุผล: การทำงานด้วยมือ (Manual) มีต้นทุนเวลาสูงและเกิดความผิดพลาดได้ง่าย จำเป็นต้องมี AI ช่วยตรวจสอบและจัดหมวดหมู่โดยอัตโนมัติ
- Security Requirements & Risk Assessment - Section 3.1, 4.2: ความเสี่ยงด้านข้อมูลรั่วไหลและการเข้าถึงฐานข้อมูลโดยตรงจากเซอร์วิส AI
- เหตุผล: ข้อมูลเอกสารก่อสร้างท่าเรือแหลมฉบัง เฟส 3 เป็นความลับระดับสูง (Confidential) ต้องมีขอบเขตความปลอดภัย (AI Boundary) ที่รัดกุม
แก้ไขความขัดแย้งและการกระจายตัวของเอกสาร:
- ADR-017, ADR-017B, ADR-018, ADR-020, ADR-022 มีความทับซ้อนในเชิงสถาปัตยกรรมและข้อกำหนด
- การตัดสินใจนี้ช่วยแก้ไขโดย: ยุบรวมข้อกำหนดทั้งหมดเข้าสู่ร่มใหญ่ฉบับเดียว (Consolidation) เพื่อลดปัญหา Revision Drift และทำให้การทบทวนสถาปัตยกรรม (Review Cycle) เป็นไปอย่างสอดคล้องกันทั้งระบบ
Context and Problem Statement
โครงการ LCBP3-DMS มีความต้องการประยุกต์ใช้ AI ในการเพิ่มประสิทธิภาพการบริหารจัดการเอกสารวิศวกรรมโยธาขนาดใหญ่ โดยเผชิญกับโจทย์และความท้าทายหลัก 5 ด้าน:
- Legacy Document Migration: เอกสาร PDF เก่ากว่า 20,000 ฉบับ ต้องนำเข้าระบบพร้อมตรวจสอบความสอดคล้องกับ Metadata ใน Excel
- Real-time Ingestion & Classification: เอกสารใหม่ที่ผู้ใช้อัปโหลดต้องการการสกัด Metadata และจัดหมวดหมู่แบบเรียลไทม์เพื่อลดภาระงานกรอกข้อมูล
- Conversational Retrieval (RAG): Full-text search บน MariaDB ไม่เข้าใจบริบท (Semantic) และการตัดคำภาษาไทย ทำให้สืบค้นข้อมูลเชิงลึกได้ยาก
- Data Confidentiality & Privacy: ห้ามส่งข้อมูลความลับออกนอกเครือข่ายองค์กรไปยัง Cloud AI Provider (เช่น OpenAI, Google)
- System Stability & Isolation: การรัน AI Inference ใช้ทรัพยากรสูง (GPU VRAM/CPU) ไม่ควรรันร่วมกับ Production Server หลัก (QNAP NAS) เพื่อไม่ให้กระทบประสิทธิภาพของระบบ
Decision Drivers
- Zero Trust & Physical Isolation: AI ต้องถูกปฏิบัติเสมือน Untrusted Component รันแยกต่างหากบน Admin Desktop เท่านั้น
- RFA-First Approach: มุ่งเน้นกระบวนการเอกสาร RFA (Request for Approval) ซึ่งซับซ้อนที่สุดเป็นแกนหลัก
- Data Integrity & Human-in-the-Loop: ข้อมูลจาก AI ต้องผ่านการทวนสอบและยืนยันโดยมนุษย์ก่อน Commit ลงฐานข้อมูลจริงเสมอ
- Multi-tenant Isolation: ต้องแยกขอบเขตข้อมูลของแต่ละโครงการอย่างเด็ดขาดในระดับ Vector Database Payload Filter
- Cost Effectiveness: ประมวลผลภายในองค์กร (On-Premises) เพื่อหลีกเลี่ยงค่าใช้จ่ายแบบ Pay-per-use
- Two-Phase Storage Governance: ควบคุมการย้ายไฟล์ทุกขั้นตอนผ่าน
StorageServiceเพื่อให้สแกนไวรัสและเก็บ Audit Log ได้ครบถ้วน - GPU VRAM Budget ≤ 8GB: โมเดลทั้งหมดต้องโหลดพร้อมกันภายใน RTX 2060 Super 8GB ได้โดยไม่เกิด OOM (Out-of-Memory)
Considered Options
Option 1: Fragmented AI Subsystems (แยกระบบ AI ตาม Use Case)
Pros:
- ออกแบบและพัฒนาง่ายในระยะสั้น แต่ละส่วนไม่พึ่งพากัน Cons:
- ❌ เกิด Code Duplication สูง
- ❌ มาตรฐานความปลอดภัยและการควบคุมสิทธิ์ไม่สม่ำเสมอ
- ❌ บำรุงรักษายากเมื่อมีการเปลี่ยนโมเดลหรือโครงสร้าง Prompt
Option 2: Cloud AI Platform Integration
Pros:
- โมเดลมีความฉลาดแม่นยำสูงมาก ไม่ต้องลงทุนและบำรุงรักษา Hardware Cons:
- ❌ ผิดข้อกำหนดด้าน Data Privacy อย่างรุนแรงสำหรับเอกสาร Confidential
- ❌ ค่าใช้จ่ายสูงมากเมื่อต้องประมวลผลเอกสารเก่ากว่า 20,000 ฉบับและรองรับ RAG
Option 3: 3-Model Stack (gemma4:9b + Typhoon Local + nomic-embed-text)
เหตุผลที่ไม่เลือก:
- ❌ Typhoon Local (~4GB VRAM) + gemma4:9b (~5.5GB VRAM) = ~9.5GB → เกิน RTX 2060 Super 8GB ทำให้เกิด GPU Swap และลดความเสถียร
- ❌ Ollama ไม่สลับโมเดลได้ฉับพลัน หากโหลดพร้อมกัน VRAM เต็มแน่นอน
- ❌ ต้องจัดการ Routing Logic (เลือกว่างานไหนใช้โมเดลไหน) เพิ่ม Complexity
Option 4: Unified 2-Model Stack (gemma4:e2b + nomic-embed-text) ⭐ SELECTED
| โมเดล | ขนาด (VRAM โดยประมาณ) | หน้าที่ |
|---|---|---|
gemma4:e2b |
~2GB (Q4) | General Inference + OCR Post-processing + Extraction + RAG Q&A |
nomic-embed-text |
~0.3GB | Embedding 768-dim สำหรับ Qdrant |
| รวม | ~2.3GB | เผื่อ headroom ~5.7GB สำหรับ KV Cache และ context window ขนาดใหญ่ (8K tokens) |
Pros:
- ✅ VRAM ≤ 8GB อย่างมีเสถียรภาพ: โมเดลทั้ง 2 โหลดพร้อมกันได้ มี headroom เพียงพอสำหรับ KV Cache ขนาดใหญ่
- ✅ Single Model ลด Routing Complexity: gemma4:e2b ครอบคลุมทุก Use Case (OCR clean-up, Extraction, RAG, Classification) ผ่าน Prompt Engineering ที่แตกต่างกัน
- ✅ BullMQ Sequential Queue: การใช้โมเดลเดียวทำให้ Queue ทำงานได้ตรงไปตรงมา — ไม่มีปัญหา Worker ต้องสลับโมเดลระหว่างงาน
- ✅ GPU Overload Prevention ตาม ADR-023: สอดคล้องกับนโยบายที่กำหนดไว้แต่เดิม
- ✅ gemma4:e2b Q4: quantization Q4 ประหยัด VRAM มากที่สุด (~2GB) พร้อม context window 8K tokens ขนาดใหญ่
Cons:
- ❌ ไม่มี Typhoon Local ซึ่งถูก Fine-tune มาสำหรับภาษาไทยโดยเฉพาะ — ต้องพึ่ง Prompt Engineering บน gemma4:e2b แทน
Decision Outcome
Chosen Option: Option 4 — Unified 2-Model Stack (gemma4:e2b + nomic-embed-text)
Rationale
การลด Model Stack จาก 3 → 2 โมเดลช่วยให้ VRAM Budget ≤ 8GB อย่างมีเสถียรภาพ โดย gemma4:e2b สามารถทำหน้าที่แทน Typhoon Local ได้ผ่าน Prompt Engineering เนื่องจาก gemma4 architecture รองรับ Multimodal Context ได้ดี และ Q4 quantization ประหยัด VRAM มากที่สุด BullMQ Sequential Queue ทำงานได้ตรงไปตรงมามากขึ้นเมื่อมีโมเดลเดียว ลด complexity ของ Job Routing
🔍 Impact Analysis
Affected Components
| Component | Level | Impact Description | Required Action |
|---|---|---|---|
| Security Layer | 🔴 High | บังคับใช้ขอบเขตการเชื่อมต่อผ่าน API Gateway เท่านั้น | เพิ่ม permissions ai.suggest, ai.rag_query, ai.migration_manage, ai.audit_log_delete และ Assign ตาม Role Matrix ด้านล่าง |
| Backend (NestJS) | 🔴 High | สร้าง AiModule เป็นศูนย์กลางควบคุม Pipeline และ RAG |
พัฒนา Gateway Services และ Validation Layers |
| Database | 🔴 High | ตารางจัดเก็บประวัติการทวนสอบและสถานะเวกเตอร์ | สร้าง migration_review_queue และ ai_audit_logs (แยก table, ไม่ใช่ Compliance — เป็น AI Development Feedback Log) |
| Frontend (Next.js) | 🟡 Medium | หน้าจอแสดงผลลัพธ์จาก AI พร้อมค่า Confidence | พัฒนา Reusable Form Components และ Dashboard |
| Infrastructure | 🔴 High | การตั้งค่า Admin Desktop (Desk-5439) สำหรับ AI | ติดตั้ง Ollama, Qdrant, n8n, PaddleOCR, PyThaiNLP |
Cross-Module Dependencies
graph TB
subgraph QNAP["🖥️ QNAP NAS (Production Host)"]
BE[NestJS Backend API]
N8N[n8n Workflow Orchestrator]
end
subgraph DESK["🖥️ Desk-5439 (AI Isolation Host)"]
OLLAMA["Ollama\ngemma4:e2b\n+ nomic-embed-text"]
QDRANT[Qdrant Vector Store]
NLP[PaddleOCR + PyThaiNLP]
end
BE --"HTTP API"--> N8N
N8N --"Ollama REST API"--> OLLAMA
N8N --"Qdrant REST API"--> QDRANT
N8N --"HTTP"--> NLP
N8N --"DMS API (MariaDB update)"--> BE
BE --"RAG Query"--> QDRANT
BE --"LLM Inference"--> OLLAMA
📋 Version Dependency Matrix
| ADR | Version | Dependency Type | Affected Version(s) | Implementation Status |
|---|---|---|---|---|
| ADR-023A | 1.2 | Model Revision | v1.9.0+ | ✅ Active |
| ADR-023 | 1.1 | Base Architecture | v1.9.0+ | ✅ Active (superseded by 023A for model config) |
| ADR-016 | 2.0 | Governs | v1.8.0+ | ✅ Active |
| ADR-019 | 1.5 | Governs | v1.8.0+ | ✅ Active |
Implementation Details (ข้อกำหนดเชิงลึกรายหมวด)
1. Security Isolation Policy (ขอบเขตความปลอดภัย)
- Physical Isolation: เซอร์วิส AI ทั้งหมด (Ollama, Qdrant, PaddleOCR) ต้องรันบน Admin Desktop (Desk-5439) ที่มี GPU RTX 2060 Super 8GB เท่านั้น ห้ามรันบน QNAP NAS หลัก
- No Direct DB/Storage Access: เครื่อง AI Host ห้ามมีการเชื่อมต่อฐานข้อมูล MariaDB หรือเมาท์ Storage ปลายทางโดยตรง การอ่าน/เขียนข้อมูลทั้งหมดต้องทำผ่าน DMS Backend API
- Validation Layer: Backend ต้องตรวจสอบความถูกต้องของ Output จาก AI (Schema, System Enum, Confidence Threshold) ก่อนบันทึกลงฐานข้อมูลเสมอ
AI RBAC Permission Matrix
Permission ใหม่ที่ต้องเพิ่มใน
lcbp3-v1.9.0-seed-permissions.sql(module:ai, ID range: 181-190)
| Permission | คำอธิบาย | Superadmin (1) | Org Admin (2) | Document Control (3) | Editor (4) | Viewer (5) |
|---|---|---|---|---|---|---|
ai.suggest |
รับ AI Suggestion เมื่อสร้าง/แก้ไขเอกสาร | ✅ | ✅ | ✅ | ❌ | ❌ |
ai.rag_query |
ใช้ RAG Q&A สืบค้นเอกสาร | ✅ | ✅ | ✅ | ❌ | ❌ |
ai.migration_manage |
จัดการ Migration Batch (Review/Import/Reject) | ✅ | ✅ | ✅ | ❌ | ❌ |
ai.audit_log_delete |
Hard Delete ai_audit_logs |
✅ | ❌ | ❌ | ❌ | ❌ |
2. Core Infrastructure & Models
นโยบาย: เอกสารทั้งหมดใน LCBP3 จัดชั้นเป็น INTERNAL — AI Inference ทั้งหมดต้องรันภายใน Physical Isolation Boundary บน Desk-5439 เท่านั้น ห้ามใช้ Cloud AI Provider โดยเด็ดขาด
2.1 Model Stack & Dynamic Thai-Specialized Models (T041, US2, US3)
ระบบประมวลผลพื้นฐานจะรันด้วยชุด 2-Model Stack ที่ประหยัด VRAM เป็นหลัก และเปิดให้โหลดสลับไปประมวลผลด้วยโมเดลภาษาไทยเฉพาะทางประสิทธิภาพสูง (High-Performance Thai Specialized Models) ได้แบบ Dynamic ภายใต้การควบคุมของ VRAM Monitor เพื่อไม่ให้เกิด VRAM OOM:
ชุดประมวลผลหลัก (Baseline 2-Model Stack):
| โมเดล | Role | VRAM (โดยประมาณ) | หมายเหตุ |
|---|---|---|---|
gemma4:e2b |
General Inference + OCR Post-processing + Extraction + RAG Q&A | ~2GB (Q4) + ~0.2GB (KV Cache) | Q4 quantization; Context window 8K tokens; Parameters 2.1B |
nomic-embed-text |
Embedding 768-dim → Qdrant | ~0.3GB | สร้าง Semantic Vector สำหรับ Hybrid Search |
| รวม (peak) | ~2.5GB | เผื่อ headroom ~5.5GB — มั่นใจสูง เพราะ context window ขนาดใหญ่ (8K tokens) |
โมเดลภาษาไทยเฉพาะทางที่เป็นทางเลือก (Dynamic Thai Specialized Models):
| โมเดลทางเลือก | Role | VRAM (โดยประมาณ) | การจำกัดความเสี่ยง VRAM OOM |
|---|---|---|---|
scb10x/typhoon-ocr-3b |
OCR ภาษาไทยใน OCR Sandbox | ~3.5GB | ตั้งค่า "keep_alive": 0 (unload ทันทีหลังเสร็จสิ้น) + เช็ค VRAM ว่างต้อง ≥ 4000MB (มิฉะนั้นห้ามรันและ Fallback ไป Tesseract อัตโนมัติใน 5 วินาที) |
scb10x/typhoon2.1-gemma3-4b |
LLM สำหรับสกัดข้อมูลและจัดหมวดหมู่เอกสาร | ~4.5GB | ตั้งค่า "keep_alive": 0 + ตรวจสอบ capacity โดย VramMonitorService ก่อนอนุญาตให้เปลี่ยนโมเดลหลัก |
- Orchestrator: ใช้ n8n เป็นตัวควบคุม Flow Migration Phase เท่านั้น (trigger batch, monitor progress, handle retry ระดับ batch) — ห้าม n8n เรียก Ollama หรือ PaddleOCR โดยตรง
- Job Executor: ทุก AI Inference (OCR, Extraction, Embedding, RAG) ต้องผ่าน BullMQ บน NestJS เท่านั้น — n8n call
POST /api/ai/jobsเพื่อ queue job แล้ว poll ผลผ่านGET /api/ai/jobs/:jobId
Migration Flow:
n8n → POST /api/ai/jobs (DMS API) → BullMQ (ai-batch)
→ Worker: PaddleOCR / Ollama บน Desk-5439
→ n8n poll GET /api/ai/jobs/:jobId → ได้ผล → POST /api/ai/migration/review
Real-time Flow (User Upload):
NestJS Controller → BullMQ (ai-batch) → Worker → ai_audit_logs
เหตุผล: การ inference ทั้งหมดผ่าน BullMQ ทำให้ RBAC, ADR-007 Error Handling และ
ai_audit_logsครอบคลุมทุก job โดยอัตโนมัติ — ถ้า n8n bypass BullMQ จะเกิด audit gap
- LLM Engine: ใช้ Ollama บน Desk-5439 รันโมเดล
gemma4:e2bสำหรับงานทั้งหมด ได้แก่ General Inference, OCR Post-processing, Metadata Extraction, Classification และ RAG Q&A - Embedding Model: ใช้
nomic-embed-textรันผ่าน Ollama บน Desk-5439 สำหรับแปลงเวกเตอร์ 768-มิติ - OCR & NLP: ใช้ PaddleOCR สกัดข้อความจาก Scanned PDF และใช้ PyThaiNLP ตัดคำ/เตรียมข้อความภาษาไทย — ทั้งคู่รันบน Desk-5439
- ❌ Typhoon Local: ไม่ใช้ — ถูกแทนที่โดย
gemma4:e2bเพื่อรักษา VRAM Budget - ❌ Typhoon Cloud API: ไม่ใช้ —
rag/typhoon.service.tsต้องถูก Remove ออกจาก Codebase (Dead Code + Security Risk)
2.2 BullMQ Queue Architecture (GPU Overload Prevention)
นโยบาย: ใช้ 2 Queues แยกอิสระ เพื่อป้องกัน RAG Q&A ถูก Block โดย Batch Jobs และป้องกัน VRAM Overflow บน RTX 2060 Super 8GB ทั้งสอง Queue มี concurrency = 1 เสมอ
Phase Context (กำหนดลักษณะการใช้งานแต่ละช่วง)
| Phase | ช่วงเวลา | Volume | หมายเหตุ |
|---|---|---|---|
| Migration Phase | Pre-launch (ก่อนเปิดให้ User ทั่วไป) | ~20,000 ฉบับ (Batch ครั้งเดียว) | รัน ai-batch เต็มแรง ไม่มี User แย่งคิว — queue contention = 0 |
| Production Phase | หลัง Go-live | ~50 ฉบับ/วัน | Volume ต่ำมาก ทั้ง 2 queues แทบไม่มีงานค้าง |
Queue 1: ai-realtime (Interactive User Requests)
Queue: ai-realtime (BullMQ)
concurrency: 1
defaultJobOptions:
attempts: 3
backoff: { type: 'exponential', delay: 3000 }
| Job Type | โมเดลที่ใช้ | SLA Target |
|---|---|---|
rag-query |
gemma4:e2b |
p95 < 10s (นับตั้งแต่ dequeue) |
ai-suggest |
gemma4:e2b |
p95 < 8s |
Queue 2: ai-batch (Background Processing)
Queue: ai-batch (BullMQ)
concurrency: 1
defaultJobOptions:
attempts: 3
backoff: { type: 'exponential', delay: 5000 }
| Job Type | โมเดลที่ใช้ | Priority |
|---|---|---|
ocr-postprocess |
gemma4:e2b |
Normal |
metadata-extract |
gemma4:e2b |
Normal |
embed-document |
nomic-embed-text |
Low |
⚠️ GPU Constraint: แม้จะแยก 2 Queue แต่ Ollama Worker บน Desk-5439 มี GPU เดียว — หาก
ai-realtimeและai-batchรัน Job พร้อมกัน VRAM อาจเต็ม ให้ตั้งค่าai-batchpause อัตโนมัติเมื่อai-realtimeมี active job (ผ่าน BullMQ Event hooks:active/completed)
3. Legacy Data Migration (การนำเข้าข้อมูลเก่า)
3.0 Ingestion Workflow Overview
┌─────────────────────────────────────────────────────────────────┐
│ 1.1 LEGACY INGESTION (Pre-launch, Migration Phase) │
│ │
│ Admin วางไฟล์ใน Folder │
│ → n8n UI: Admin กด "Run Migration Workflow" ด้วยตนเอง │
│ → n8n → POST /api/ai/jobs → BullMQ (ai-batch) │
│ → OCR/Extract → migration_review_queue (PENDING) │
│ → DMS Frontend /admin/ai-migration: Admin Approve/Reject │
│ → IMPORTED → AUTO: queue embed-document (ai-batch) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 1.2 NEW DOCUMENT INGESTION (Production Phase, ~50/วัน) │
│ │
│ User Upload ผ่านช่องทางปกติ (RFA / Correspondence form) │
│ → Two-Phase Upload: Temp storage + ClamAV scan │
│ → User Submit → DMS commit (temp → permanent) │
│ → AUTO (parallel): │
│ ├─ queue ocr-postprocess + metadata-extract (ai-batch)│
│ │ → AI Suggestion แสดงบนฟอร์ม │
│ │ → Human confirm Metadata │
│ └─ queue embed-document (ai-batch, Low priority) │
│ → Qdrant indexed (background, ไม่บล็อก User) │
│ │
│ ช่วง gap (commit → embed เสร็จ): ค้นหาผ่าน DB search ปกติได้ │
│ ไม่ต้อง real-time — ยอมรับได้ │
└─────────────────────────────────────────────────────────────────┘
RAG Embedding Trigger: AUTO เสมอ หลัง commit ทันที — ห้าม Manual trigger เพื่อป้องกัน Qdrant index ไม่สมบูรณ์
- Legacy: trigger หลัง Admin Approve (
PENDING→IMPORTED)- New doc: trigger ทันทีหลัง Two-Phase commit สำเร็จ (parallel กับ AI Suggestion — ไม่รอ Human confirm)
- Gap period (ระหว่าง commit → embed เสร็จ): ใช้ MariaDB full-text search แทน — ยอมรับได้ ไม่ต้อง real-time
3.1 Frontend UI Scope (DMS Frontend vs n8n UI)
| งาน | UI ที่ใช้ | หมายเหตุ |
|---|---|---|
| Trigger Migration Batch | n8n Workflow UI | Admin กด Run ใน n8n — ไม่มีปุ่มใน DMS Frontend |
| Review migration_review_queue | DMS Frontend /admin/ai-migration |
Approve / Reject + แก้ไข Metadata |
| Monitor job progress | DMS Frontend (BullMQ dashboard หรือ job status API) | GET /api/ai/jobs/:jobId |
| AI Suggestion on new doc | DMS Frontend (form inline) | แสดงบนฟอร์ม RFA/Correspondence |
- Staging Area: ข้อมูลที่ประมวลผลผ่าน n8n จะถูกส่งเข้าตาราง
migration_review_queueเสมอ - Record Lifecycle: Record ใน
migration_review_queueไม่ถูกลบ หลัง Import — เปลี่ยนstatusเป็นIMPORTEDเก็บไว้ตลอดเพื่อ Debug และตรวจสอบ Batch ย้อนหลัง- Status transitions:
PENDING→IMPORTED|PENDING→REJECTED
- Status transitions:
- Confidence Threshold Policy (กำหนดผ่าน
.env— ไม่ Hardcode, ไม่มี Admin UI):AI_THRESHOLD_HIGH=0.85และis_valid = true\rightarrowสถานะPENDING(พร้อม Import)AI_THRESHOLD_MID=0.60ถึงAI_THRESHOLD_HIGH-0.01\rightarrowสถานะPENDING(ไฮไลต์เตือน Admin)- ต่ำกว่า
AI_THRESHOLD_MIDหรือis_valid = false\rightarrowสถานะREJECTED - การเปลี่ยนค่าต้อง Restart service และมีร่องรอยใน deployment log
- Threshold Recalibration Policy:
- ค่าเริ่มต้น (0.85/0.60) ถูกกำหนดในยุค gemma4:9b — ใช้เป็น baseline สำหรับ Migration Phase แรก
- หลัง import เอกสารชุดแรก 100–500 ฉบับ: ทบทวนค่า threshold โดยดูจาก
ai_audit_logs(confidence_scoredistribution) เปรียบเทียบกับ Admin override rate - เกณฑ์ปรับ: ถ้า REJECTED rate > 30% หรือ Admin override rate > 40% ให้ปรับลด threshold ลง และบันทึกการเปลี่ยนแปลงใน Review History ของ ADR นี้
- ผู้รับผิดชอบ: AI Integration Lead ร่วมกับ Document Control Team
- Idempotency Header: บังคับส่ง
Idempotency-Key: <doc_number>:<batch_id>ป้องกันบันทึกซ้ำ - Two-Phase Storage: ไฟล์ถูกอัปโหลดเป็น Temp (
is_temporary = true) และย้ายเข้า Storage จริงเมื่อเรียก API Commit เท่านั้น
4. Smart Classification & Real-time Ingestion
4.1 PDF Input Limit (Hard Constraint)
กฎ: ส่ง PDF เข้า gemma4:e2b ได้ สูงสุด 5 หน้าแรกเท่านั้น สำหรับงาน Summarization, Classification และ Tagging (เพิ่มจาก 3 เพราะ context window 8K tokens) ⚠️ ข้อยกเว้น: งาน
embed-document(RAG) ใช้เอกสารทั้งฉบับ — ดู Section 5
เหตุผล:
- หน้าปก + หน้าที่ 1–4 ของเอกสารวิศวกรรมมักมีข้อมูลหลักครบ (Document Title, Drawing No., Discipline, Project Code, Revision)
- Context window 8K tokens รองรับ ~5 หน้า → VRAM peak ≤ ~2.5GB ตามที่ออกแบบไว้
- ป้องกัน Job ใช้เวลานานเกิน SLA
Implementation Note:
n8n PDF Pre-processor:
extract_pages: [1, 2, 3, 4, 5] ← hard limit, ห้ามเปลี่ยนโดยไม่ review ADR
fallback: ถ้า PDF < 5 หน้า → ใช้ทั้งหมด
4.2 PDF Type Auto-Detection (OCR Routing)
กฎ: ระบบต้อง detect อัตโนมัติ ว่า PDF มี selectable text หรือไม่ ก่อนเลือก pipeline — ห้ามให้ User เลือกเอง
PDF Upload
└─ n8n: ตรวจสอบ text layer (PyMuPDF: page.get_text())
├─ มีข้อความ (len > threshold) → Fast Path: text parser โดยตรง
└─ ไม่มีข้อความ / image-only → Slow Path: PaddleOCR → PyThaiNLP
| Path | เงื่อนไข | เครื่องมือ | เวลาโดยประมาณ |
|---|---|---|---|
| Fast Path | extracted_chars > 100 ต่อหน้า |
PyMuPDF text parser | < 1s |
| Slow Path | extracted_chars ≤ 100 ต่อหน้า |
PaddleOCR + PyThaiNLP | 5–30s/หน้า |
หมายเหตุ: threshold
100 charsป้องกัน PDF ที่มี text layer แต่ข้อมูลน้อยมาก (เช่น มีแค่ watermark) ถูก route ไป Fast Path ผิด — ปรับค่าได้ผ่าน.env: OCR_CHAR_THRESHOLD=100
- Enum Enforcement: ฟิลด์หมวดหมู่และประเภทเอกสารที่สกัดได้ ต้องนำไปทวนสอบกับ Master Data (
GET /api/meta/categories) เสมอ ห้ามให้ AI สร้างประเภทเอกสารขึ้นมาเองโดยพลการ - Human Override: นำเสนอผลลัพธ์บนหน้าจอ RFA/Correspondence ให้ผู้ใช้กดยืนยันหรือแก้ไขก่อนบันทึก
5. Hybrid Retrieval-Augmented Generation (RAG)
5.1 Document Embedding Scope (แตกต่างจาก Classification)
กฎ:
embed-documentต้องฝัง Vector จาก เอกสารทั้งฉบับ (ไม่ใช่แค่ 3 หน้า) เพื่อให้ RAG ค้นหาเนื้อหาจากทุกหน้าได้
เหตุผลที่ไม่กระทบ VRAM:
nomic-embed-textประมวลผล chunk ทีละชิ้น (stateless) — ไม่ต้องโหลดเอกสารทั้งฉบับพร้อมกัน- VRAM peak ของ embed job = ขนาด 1 chunk (~512 tokens) เท่านั้น ≈ negligible
Chunking Strategy:
Chunk size: 512 tokens
Overlap: 64 tokens ← รักษา context ข้ามขอบ chunk
Unit: paragraph-aware (ตัดที่ paragraph boundary ก่อน token boundary)
Max chunks/doc: ไม่จำกัด — ขึ้นกับความยาวเอกสาร
Qdrant Payload ต่อ chunk:
{
"document_public_id": "<uuid>",
"project_public_id": "<uuid>",
"page_number": 12,
"chunk_index": 5
}
5.2 RAG Pipeline
- Hybrid Search Strategy: ผสานคะแนน Vector Similarity (0.7) + Keyword Exact Match (0.3) และผ่าน Score-based Re-ranking
- Multi-tenant Isolation: บังคับใช้ Qdrant Payload Filter กำหนด
project_public_idเป็นเงื่อนไขในการสืบค้น ทุกครั้ง — enforce ผ่านQdrantServicewrapper ที่กำหนดprojectPublicId: stringเป็น required parameter (ไม่มี optional fallback) ดังนี้:
// ✅ Required contract — ห้ามเปลี่ยน signature โดยไม่ review ADR
@Injectable()
export class QdrantService {
async search(
projectPublicId: string, // required — compile-time enforcement
vector: number[],
topK: number = 5,
): Promise<QdrantSearchResult[]> {
return this.client.search('documents', {
vector,
limit: topK,
filter: {
must: [{ key: 'project_public_id', match: { value: projectPublicId } }],
},
});
}
async upsert(
projectPublicId: string, // required
chunks: DocumentChunk[],
): Promise<void> { ... }
// ❌ ห้าม expose rawSearch() หรือ method ที่ไม่บังคับ filter
}
- LLM สำหรับ RAG Q&A: ใช้ Local Ollama (
gemma4:e2b) บน Desk-5439 เท่านั้น — ไม่มี Cloud Fallback เนื่องจากเอกสารทั้งหมดจัดชั้นเป็น INTERNAL - Context Window สำหรับ RAG: ส่ง top-K chunks (K=5) เข้า gemma4:e2b ≈ 5 × 512 = ~2,560 tokens — อยู่ในขีดจำกัด VRAM (8K tokens)
- Performance Target:
p95 < 10sสำหรับการตอบคำถามผ่าน Local LLM (นับตั้งแต่ dequeue จากai-realtime)
6. ai_audit_logs — AI Development Feedback Log
วัตถุประสงค์: บันทึก AI Suggestion + การตัดสินใจของมนุษย์เพื่อใช้วิเคราะห์และปรับปรุงคุณภาพโมเดล AI — ไม่ใช่ Compliance Audit Trail Compliance จริงๆ ถูกบันทึกอยู่ใน
audit_logsแล้ว (Human Confirm Action)
- Key Columns:
document_public_id,model_name,ai_suggestion_json,human_override_json,confidence_score,confirmed_by_user_id,created_at - Retention: ตลอดอายุโครงการ (~5-10 ปี) — Admin สามารถ Hard Delete ได้ผ่าน Frontend เพื่อจัดการ Test Data และ Storage
- RBAC: เฉพาะ Role
SYSTEM_ADMINเท่านั้นที่ลบได้ — การลบทุกครั้งต้องบันทึกในaudit_logs(action: 'AI_AUDIT_LOG_DELETED') - ห้าม Merge: ต้องเป็น Table แยกจาก
audit_logsเพื่อให้ Query ด้วย Typed Columns ได้ (เช่นWHERE confidence_score < 0.85)
Consequences
Positive
- ✅ มีมาตรฐานสถาปัตยกรรม AI ที่เป็นหนึ่งเดียว ง่ายต่อการอ้างอิงและตรวจสอบสิทธิ์
- ✅ ปลอดภัยสูงสุดตามหลักการ Zero Trust ป้องกันฐานข้อมูลและไฟล์ระบบจากความเสี่ยงของเซอร์วิสภายนอก
- ✅ รองรับเอกสารภาษาไทยได้อย่างแม่นยำผ่านกระบวนการ NLP เฉพาะทาง
- ✅ ควบคุมการใช้งานทรัพยากรได้อย่างมีประสิทธิภาพ ไม่รบกวน Production NAS
- ✅ VRAM Budget เสถียร: 2-Model Stack ใช้ ~4.5GB จาก 8GB (peak) — มี headroom ~3.5GB; ยืนยันโดย PDF 3-page hard limit
- ✅ BullMQ Sequential Queue ลด Complexity: ไม่มี Logic เลือกโมเดล Worker ทำงานได้ตรงไปตรงมา
Negative
- ❌ ระบบมีความซับซ้อนในการตั้งค่าและเชื่อมต่อเครือข่ายระหว่าง NAS และ Admin Desktop
- ❌ มี Overhead ในการดูแลรักษาเครื่อง Desktop (GPU Temperature, Service Uptime)
- ❌ ไม่มี Typhoon Local ที่ Fine-tune ภาษาไทยโดยเฉพาะ — ต้องใช้ Prompt Engineering ชดเชย
⚠️ Unvalidated Assumptions (ข้อสมมติที่ยังไม่ได้ทดสอบ)
การตัดสินใจเปลี่ยนจาก Typhoon Local → gemma4:e4b Q8_0 อาศัยเหตุผล VRAM เป็นหลัก ยังไม่มีหลักฐานเชิงคุณภาพ รองรับ
| Assumption | ความเสี่ยง | Validation Plan |
|---|---|---|
| gemma4:e4b Q8_0 + Prompt Engineering ชดเชยคุณภาพ Typhoon Local ได้ | OCR Post-processing และ Metadata Extraction ภาษาไทยอาจด้อยกว่า → Confidence Score ต่ำ → เพิ่มภาระ Admin Review | ทดสอบด้วย Sample 50–100 ฉบับจากเอกสารจริง เปรียบเทียบ Accuracy ก่อน Go-live |
| PyThaiNLP Pre-processing เพียงพอชดเชยความขาด Thai Fine-tuning | ข้อความที่ตัดคำไม่ถูกต้องอาจทำให้โมเดลเข้าใจผิด | วัด Word Error Rate บน OCR output จริง ก่อน Production |
ข้อตกลง: หากทดสอบแล้วพบ Accuracy ต่ำกว่า 80% สำหรับ Thai Metadata Extraction ให้พิจารณา Option เพิ่มเติม เช่น Quantized Typhoon ขนาดเล็กหรือ LoRA Adapter บน gemma4:e4b
Mitigation Strategies
- Graceful Degradation (Desk-5439 ออฟไลน์): DMS Core ยังทำงานได้ปกติทุก Feature — เฉพาะ AI Features ถูก Disable ชั่วคราว:
- Backend ตรวจสอบ Health Check ของ Desk-5439 ทุก 60 วินาที ผ่าน
/healthendpoint ของ Ollama และ Qdrant - เมื่อ Desk-5439 ออฟไลน์ → set
AI_AVAILABLE = falseใน Redis Cache - Frontend แสดง Global Banner: "⚠️ ระบบ AI ไม่พร้อมใช้งานชั่วคราว กรุณากรอกข้อมูลด้วยตนเอง"
- AI Classification form fields แสดงผล แต่ AI Suggestion ถูก hide — User กรอกเองได้ปกติ
- RAG Q&A endpoint return
503 Service Unavailableพร้อม error message ที่อ่านเข้าใจได้
- Backend ตรวจสอบ Health Check ของ Desk-5439 ทุก 60 วินาที ผ่าน
- GPU Overload Prevention: ใช้ BullMQ จัดคิวงาน AI แบบ Sequential (concurrency = 1) เพื่อไม่ให้ VRAM 8GB โอเวอร์โหลด — ดูรายละเอียดใน Section 2.2
- Thai Language Quality: ใช้ PyThaiNLP สำหรับ Pre-processing (word segmentation) ก่อนส่งให้ gemma4:e4b เพื่อรักษาคุณภาพข้อความภาษาไทย
🔄 Review Cycle & Maintenance
Review Schedule
- Next Review: 2026-11-15 (6 months from revision)
- Review Type: Scheduled Core Architecture Review
- Reviewers: System Architect, Security Lead, AI Integration Lead
Review History
| Version | Date | Changes | Status |
|---|---|---|---|
| 1.0 | 2026-05-14 | ยุบรวมและแทนที่ ADR-017, 017B, 018, 020, 022 เป็นฉบับเดียว | ✅ Superseded |
| 1.1 | 2026-05-14 | Grilling Session: (1) ล็อค Local-only AI บน Desk-5439 ทั้งหมด (2) แยก Typhoon Local vs Cloud (3) ลบ Typhoon Cloud API ออก (4) กำหนด ai_audit_logs เป็น Development Feedback Log ไม่ใช่ Compliance (5) เพิ่ม Admin Hard Delete Policy |
✅ Superseded by 023A |
| 1.2 | 2026-05-15 | ADR-023A: เปลี่ยน Model Stack 3→2 (ลบ Typhoon Local, เปลี่ยน gemma4:9b → gemma4:e4b Q8_0), เพิ่ม BullMQ Queue Policy Table, เพิ่ม VRAM Budget breakdown | ✅ Active |
| 1.3 | 2026-05-30 | บันทึกการรองรับ Typhoon OCR-3B และ typhoon2.1-gemma3-4b แบบ Dynamic พร้อมระบบ VRAM capacity check และ Tesseract fallback | ✅ Active |
Related ADRs (อดีตเอกสารที่ถูกแทนที่)
- ADR-017: Ollama Data Migration Architecture — Superseded
- ADR-017B: AI Document Classification — Superseded
- ADR-018: AI Boundary Policy — Superseded
- ADR-020: AI Intelligence Integration Architecture — Superseded
- ADR-022: Retrieval-Augmented Generation (RAG) System — Superseded