9.1 KiB
Research Findings: OCR & AI Extraction Prompt Management
Date: 2026-06-17 Feature: 238-ocr-ai-prompt-separation
Research Areas
1. Concurrent Edit Handling Strategy
Decision: ใช้ optimistic locking ด้วย version/timestamp field ใน ai_prompts table
Rationale:
- ลด lock contention ใน database - ไม่ block admin คนอื่น
- User experience ดีกว่า - แจ้งเตือนแทนการ block
- ง่ายต่อการ implement กับ TypeORM @VersionColumn
- สอดคล้องกับ pattern ที่ใช้ใน LCBP3-DMS อยู่แล้ว (ADR-002 document numbering)
Alternatives considered:
- Pessimistic locking (SELECT FOR UPDATE): ไม่เลือกเพราะอาจ block admin คนอื่นนาน, ไม่เหมาะกับ admin UI
- Last-write-wins: ไม่เลือกเพราะเสี่ยงสูญเสียการแก้ไขของ admin คนแรกโดยไม่รู้ตัว
- Operational Transform (like Google Docs): ไม่เลือกเพราะ complex เกินความจำเป็น, prompt editing ไม่ต้องการ real-time collaboration
⚠️ สถานะของจริง (codebase ปัจจุบัน):
@VersionColumn({ name: 'version' })ถูกเพิ่มใน entity แล้ว (delta 2026-06-15) — คอลัมน์ DB มีแล้ว- แต่
activate()ของจริง ใช้ pessimistic_write lock ใน transaction และ ไม่รับexpectedVersionจึงยังไม่มี HTTP 409 flow @VersionColumnปัจจุบันทำงานตอนsave()เท่านั้น (ดัก lost update ระหว่าง read→write)
ถ้าจะทำ flow optimistic 409 ตาม spec ต้องแก้เพิ่ม:
- แก้ signature
activate(promptType, versionNumber, userId, expectedVersion)ให้รับexpectedVersion - เทียบ
versionก่อน save → ถ้าไม่ตรงโยน BusinessException/409 พร้อม current data - หมายเหตุ: อ้าง ADR-002 จริงๆใช้ Redlock/pessimistic — คำว่า "สอดคล้อง pattern ADR-002" ใน rationale จึงคลาดเคลื่อน
2. Sidecar System Prompt Parameter Format
Decision: Sidecar รับ systemPrompt ผ่าน multipart/form-data field 'systemPrompt'
Rationale:
- สอดคล้องกับรูปแบบที่ sidecar รับ file upload อยู่แล้ว
- ไม่ต้องเปลี่ยน content-type หรือ endpoint structure
- ง่ายต่อการ integrate กับ existing form data
Alternatives considered:
- JSON payload with base64 PDF: ไม่เลือกเพราะต้องเปลี่ยน endpoint structure มาก, file size limit ของ JSON
- Separate endpoint: ไม่เลือกเพราะซับซ้อนเกินไป, ควรรวมอยู่ใน /ocr-upload
✅ ยืนยันแล้ว (จาก app.py จริง): OCR engine ใช้ prepare_ocr_messages(pdf_path, task_type="structure", page_num=N) จาก typhoon_ocr ซึ่งคืน messages array ที่มี user message เดียว โดย messages[0]["content"] เป็น list (image + prompt). โค้ดปัจจุบันที่ process_ocr() (app.py:194-203) inject ข้อความเพิ่มได้สำเร็จอยู่แล้ว ด้วยการ messages[0]["content"].append({"type": "text", "text": ...}) (DMS tags) → ใช้ pattern เดียวกันนี้กับ systemPrompt ได้ทันที
Decision (ปรับให้ตรงของจริง): inject systemPrompt ด้วยการ append text item เข้า messages[0]["content"] — ไม่ insert {"role":"system"} แยก (typhoon OCR เป็น single-message format; system role แยกยังไม่พิสูจน์ และเสี่ยงกระทบ structured extraction)
Implementation approach (ยืนยันตาม app.py):
# process_ocr() — เพิ่มพารามิเตอร์ system_prompt และ append ก่อน DMS tags
def process_ocr(pdf_path, page_num=1, options_override={}, system_prompt: Optional[str] = None) -> str:
messages = prepare_ocr_messages(pdf_path, task_type="structure", page_num=page_num)
if system_prompt:
messages[0]["content"].append({"type": "text", "text": system_prompt})
# DMS tags injection เดิม (ยังคงไว้)
messages[0]["content"].append({"type": "text", "text": "Additionally: ..."})
# ...payload เดิม → /v1/chat/completions
# /ocr-upload — รับ systemPrompt แล้วส่งต่อ (ต้อง X-API-Key)
@app.post("/ocr-upload", dependencies=[Depends(get_api_key)])
def ocr_upload(file=File(...), engine=Form("auto"), systemPrompt: Optional[str] = Form(None), ...):
# ต้อง thread systemPrompt → _process_pdf_doc(...) → process_ocr(..., system_prompt=systemPrompt)
...
หมายเหตุ: ต้อง thread
systemPromptผ่าน_process_pdf_doc()(เพิ่มพารามิเตอร์) ไปยังprocess_ocr()ด้วย เพราะปัจจุบัน_process_pdf_docไม่รับ systemPrompt
3. Default OCR System Prompt Content
Decision: ใช้ hardcoded default minimal system prompt
Content: "Extract all text from this PDF page accurately."
Rationale:
- Simple and language-agnostic
- ทำงานได้กับทุกประเภทเอกสาร (Thai/English/mixed)
- ไม่มี bias ต่อ specific document type
- Vision model (np-dms-ocr) ถูกฝึกมาให้เข้าใจ instruction แบบนี้อยู่แล้ว
Alternatives considered:
- No default (fail fast): ไม่เลือกเพราะจะทำให้ OCR ใช้ไม่ได้ถ้าลืมสร้าง prompt หรือ database error
- Complex multi-language prompt: ไม่เลือกเพราะอาจทำให้ model confused, minimal prompt มีประสิทธิภาพดีกว่า
- Template with placeholders: ไม่เลือกเพราะ OCR system prompt ไม่มี context อื่นให้ inject
4. Placeholder Validation for AI Extraction Prompt
Decision: ตรวจสอบ required placeholders ตอน save (backend validation)
Required placeholders:
{{ocr_text}}- mandatory (OCR text to extract from){{master_data_context}}- optional (project/contract context)
Rationale:
- ป้องกัน runtime error เมื่อ prompt ถูกใช้
- ให้ admin รู้ทันทีว่า template ไม่ถูกต้อง
- สอดคล้องกับ ADR-007 error handling strategy
Validation approach:
validateTemplate(template: string, promptType: string): ValidationResult {
if (promptType === 'ocr_extraction') {
if (!template.includes('{{ocr_text}}')) {
return { valid: false, error: 'Template must include {{ocr_text}} placeholder' };
}
}
// ocr_system has no required placeholders
return { valid: true };
}
5. AI Prompts Table Schema Compatibility
Decision: ใช้ schema ที่มีอยู่แล้วจาก ADR-029, เพิ่มแค่ @VersionColumn
Existing fields (sufficient):
prompt_type(string): รองรับ 'ocr_system', 'ocr_extraction'version_number(int): Version trackingtemplate(text): Prompt contentcontext_config(json): Metadatais_active(tinyint(1)): Active flag (MariaDB ส่งกลับเป็น 0/1)created_at(datetime): Timestampcreated_by(int FK → users.user_id): Creator — ไม่ใช่ created_by_public_id
คอลัมน์จริงที่ต้องระวัง:
version(int, @VersionColumn): มีอยู่แล้ว (delta2026-06-15-fix-ai-prompts-columns.sql)created_byเป็น INT FK → users(user_id) — ไม่ใช่created_by_public_id; seed ต้องใช้(SELECT user_id FROM users WHERE username='superadmin')
SQL delta (idempotent — version มีแล้ว):
ALTER TABLE ai_prompts ADD COLUMN IF NOT EXISTS `version` INT NOT NULL DEFAULT 1;
Summary
ทุก research area มีทางเลือกที่ชัดเจนและสอดคล้องกับ:
- LCBP3-DMS patterns (optimistic locking, backend validation)
- ADR-007 error handling strategy
- ADR-029 dynamic prompt management
- ADR-037 unified prompt management UX
ไม่มี technical blockers พร้อม proceed ไป Phase 1 design