เมื่อเดือนที่แล้ว ทีมงานของผมเจอปัญหาใหญ่หลวง — ระบบ Content Moderation เดิมที่ใช้ Claude Sonnet ตรวจจับรูปภาพและข้อความแยกกัน เริ่มมีปัญหาเมื่อผู้ใช้งานส่งคอนเทนต์ที่ผสมผสานทั้งสองอย่าง เช่น รูปภาพที่มีข้อความซ้อนอยู่ข้างใน หรือ caption ที่อ้างอิงถึงรูปภาพ ระบบเดิมมองข้าม context ระหว่างรูปภาพและข้อความ ทำให้เนื้อหาที่ไม่เหมาะสมบางส่วนหลุดรอดไปได้

หลังจากทดสอบหลายโมเดล พบว่า Gemini 2.5 Flash ที่มีความสามารถ multi-modal ในตัว สามารถวิเคราะห์รูปภาพและข้อความพร้อมกันได้อย่างมีประสิทธิภาพ แถมราคาถูกกว่า Claude Sonnet 4.5 ถึง 6 เท่า ($2.50 vs $15 ต่อล้าน token) ในบทความนี้จะสอนวิธีพัฒนา API สำหรับตรวจจับเนื้อหาที่ไม่เหมาะสมแบบครบวงจร

ปัญหาจริงที่พบ: Multi-Modal Content หลุดรอดการตรวจจับ

ระบบ Content Moderation แบบเดิมที่ใช้โมเดลเดียวตรวจจับรูปภาพ และอีกโมเดลตรวจจับข้อความ มีข้อจำกัดสำคัญ:

ด้วย Gemini 2.5 Flash multi-modal เราสามารถส่งรูปภาพและข้อความใน request เดียว ระบบจะวิเคราะห์ทั้งสองส่วนพร้อมกัน และส่งผลลัพธ์กลับมาพร้อม confidence score สำหรับแต่ละประเภทเนื้อหา

การตั้งค่า Environment และการติดตั้ง Dependencies

# สร้าง virtual environment (Python 3.10+)
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

ติดตั้ง packages ที่จำเป็น

pip install openai httpx python-dotenv pillow aiofiles

หรือใช้ Poetry

poetry add openai httpx python-dotenv pillow aiofiles

สร้างไฟล์ .env สำหรับเก็บ API key:

# .env
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY

หากยังไม่มี API key สามารถสมัครได้ที่ https://www.holysheep.ai/register

โค้ดพื้นฐาน: Multi-Modal Content Moderation API

import os
import base64
from io import BytesIO
from openai import OpenAI
from PIL import Image
from dotenv import load_dotenv

โหลด environment variables

load_dotenv()

สร้าง client สำหรับ HolySheep AI

⚠️ สำคัญ: base_url ต้องเป็น https://api.holysheep.ai/v1 เท่านั้น

client = OpenAI( api_key=os.getenv("HOLYSHEEP_API_KEY"), base_url="https://api.holysheep.ai/v1" # ไม่ใช่ api.openai.com! ) def encode_image_to_base64(image_path: str) -> str: """แปลงรูปภาพเป็น base64 string""" with Image.open(image_path) as img: # แปลง RGBA เป็น RGB (หากมี alpha channel) if img.mode == 'RGBA': img = img.convert('RGB') # Resize รูปภาพให้เล็กลงเพื่อลดขนาด (max 1024px) max_size = 1024 if max(img.size) > max_size: ratio = max_size / max(img.size) new_size = tuple(int(dim * ratio) for dim in img.size) img = img.resize(new_size, Image.Resampling.LANCZOS) buffer = BytesIO() img.save(buffer, format="JPEG", quality=85) return base64.b64encode(buffer.getvalue()).decode("utf-8") def moderate_content( image_path: str, text: str = None, user_id: str = None ) -> dict: """ ตรวจจับเนื้อหาที่ไม่เหมาะสมจากรูปภาพและข้อความร่วมกัน Args: image_path: พาธของรูปภาพ text: ข้อความ caption/description (optional) user_id: ID ของผู้ใช้ (สำหรับ logging) Returns: dict ที่มีผลการตรวจจับพร้อม confidence scores """ # เตรียมรูปภาพ base64_image = encode_image_to_base64(image_path) # สร้าง prompt สำหรับ content moderation moderation_prompt = """คุณเป็นระบบ Content Moderation ที่ตรวจจับเนื้อหาที่ไม่เหมาะสม วิเคราะห์รูปภาพและข้อความที่ให้มา แล้วตอบกลับเป็น JSON format ดังนี้: { "is_safe": true/false, "categories": { "violence": {"detected": true/false, "confidence": 0.0-1.0, "details": "..."}, "adult": {"detected": true/false, "confidence": 0.0-1.0, "details": "..."}, "hate_speech": {"detected": true/false, "confidence": 0.0-1.0, "details": "..."}, "harassment": {"detected": true/false, "confidence": 0.0-1.0, "details": "..."}, "dangerous_content": {"detected": true/false, "confidence": 0.0-1.0, "details": "..."}, "spam": {"detected": true/false, "confidence": 0.0-1.0, "details": "..."} }, "overall_confidence": 0.0-1.0, "summary": "สรุปการตรวจจับภาษาไทย", "recommended_action": "allow/warn/block" } หากพบเนื้อหาที่ไม่เหมาะสม ให้ระบุ confidence สูงสุดเท่ากับ 1.0 หากไม่พบ ให้ confidence เป็น 0.0-0.3""" # เตรียม messages messages = [ { "role": "user", "content": [ { "type": "text", "text": moderation_prompt }, { "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{base64_image}" } } ] } ] # เพิ่มข้อความ text หากมี if text: messages[0]["content"].insert(1, { "type": "text", "text": f"ข้อความที่ต้องวิเคราะห์เพิ่มเติม: {text}" }) # เรียก API response = client.chat.completions.create( model="gemini-2.0-flash", messages=messages, response_format={"type": "json_object"}, temperature=0.1 # ความแม่นยำสูง ลดความสุ่ม ) import json result = json.loads(response.choices[0].message.content) # เพิ่ม metadata result["metadata"] = { "user_id": user_id, "image_path": image_path, "text_length": len(text) if text else 0, "tokens_used": response.usage.total_tokens, "latency_ms": response.response_ms if hasattr(response, 'response_ms') else None } return result

ตัวอย่างการใช้งาน

if __name__ == "__main__": # ทดสอบกับรูปภาพตัวอย่าง result = moderate_content( image_path="test_image.jpg", text="ช่วงเทศกาลลดราคา คลิกเลย!", user_id="user_12345" ) print(f"Safe: {result['is_safe']}") print(f"Action: {result['recommended_action']}") print(f"Confidence: {result['overall_confidence']}") print(f"Tokens: {result['metadata']['tokens_used']}")

เวอร์ชัน Async สำหรับ High-Traffic Application

import asyncio
import os
import base64
from io import BytesIO
from openai import AsyncOpenAI
from PIL import Image
from dotenv import load_dotenv
from typing import List, Optional

load_dotenv()

Async client สำหรับ HolySheep AI

async_client = AsyncOpenAI( api_key=os.getenv("HOLYSHEEP_API_KEY"), base_url="https://api.holysheep.ai/v1" ) class AsyncContentModerator: """Async Content Moderation รองรับ high-traffic""" def __init__(self, max_concurrent: int = 10): self.semaphore = asyncio.Semaphore(max_concurrent) self.categories = [ "violence", "adult", "hate_speech", "harassment", "dangerous_content", "spam" ] async def moderate_single( self, image_bytes: bytes, text: Optional[str] = None ) -> dict: """ตรวจจับเนื้อหาจากรูปภาพเดียว""" async with self.semaphore: # เตรียมรูปภาพ image_base64 = base64.b64encode(image_bytes).decode("utf-8") # สร้าง prompt prompt = """ตรวจจับเนื้อหาที่ไม่เหมาะสมจากรูปภาพและข้อความ ตอบเป็น JSON พร้อม categories ต่อไปนี้: violence, adult, hate_speech, harassment, dangerous_content, spam แต่ละ category มี detected (boolean), confidence (0-1), details (string) { "is_safe": boolean, "categories": { "violence": {"detected": boolean, "confidence": float, "details": string}, "adult": {"detected": boolean, "confidence": float, "details": string}, ... }, "overall_confidence": float, "summary": string, "recommended_action": "allow|warn|block" }""" # เตรียม content list content = [ {"type": "text", "text": prompt}, { "type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"} } ] if text: content.insert(1, {"type": "text", "text": text}) # เรียก API พร้อมวัดเวลา import time start_time = time.perf_counter() response = await async_client.chat.completions.create( model="gemini-2.0-flash", messages=[{"role": "user", "content": content}], response_format={"type": "json_object"}, temperature=0.1 ) latency_ms = (time.perf_counter() - start_time) * 1000 import json result = json.loads(response.choices[0].message.content) result["_meta"] = { "latency_ms": round(latency_ms, 2), "tokens": response.usage.total_tokens } return result async def moderate_batch( self, items: List[dict] ) -> List[dict]: """ ตรวจจับเนื้อหาหลายรายการพร้อมกัน Args: items: List of {"image_bytes": bytes, "text": str|None, "id": str} """ tasks = [ self.moderate_single(item["image_bytes"], item.get("text")) for item in items ] results = await asyncio.gather(*tasks, return_exceptions=True) # เพิ่ม ID กลับเข้าไปในผลลัพธ์ final_results = [] for i, result in enumerate(results): if isinstance(result, Exception): final_results.append({ "error": str(result), "id": items[i].get("id") }) else: result["id"] = items[i].get("id") final_results.append(result) return final_results

ตัวอย่างการใช้งาน async version

async def main(): moderator = AsyncContentModerator(max_concurrent=10) # ตัวอย่าง: ตรวจจับหลายรูปภาพพร้อมกัน with open("image