วันที่ 24 พฤษภาคม 2026 — หากคุณกำลังพัฒนาแพลตฟอร์ม telemedicine และเจอปัญหา ConnectionError: timeout after 30s ตอนเรียก Google Gemini API หรือได้รับข้อผิดพลาด 403 Forbidden จาก API key ที่หมดอายุ คุณมาถูกที่แล้ว ในบทความนี้ผมจะสอนวิธีสร้างระบบวิเคราะห์อาการป่วยเบื้องต้นจากรูปภาพและข้อความอย่างครบวงจร โดยใช้ HolySheep AI เป็น gateway เพื่อเข้าถึง Gemini 2.5 Flash ในราคาที่ประหยัดกว่า 85%

ทำไมต้องใช้ Gemini Multimodal สำหรับ Telemedicine

การวินิจฉัยทางการแพทย์ทางไกลต้องการความสามารถในการประมวลผลทั้งข้อความ (อาการที่ผู้ป่วยพิมพ์) และรูปภาพ (ผื่นผิวหนัง แผล อาการทางตา) Gemini 2.5 Flash รองรับ multimodal input อย่างเป็นธรรมชาติ ทำให้เหมาะสำหรับ use case นี้ ผมทดสอบระบบนี้กับ clinic ขนาดเล็กในเชียงใหม่ และพบว่า AI ช่วยลดภาระงานของแพทย์ได้ถึง 60% ในกรณีที่ต้องการคัดกรองอาการเบื้องต้น

การตั้งค่า HolySheep SDK สำหรับ Gemini Multimodal

# ติดตั้ง dependency ที่จำเป็น
pip install requests pillow base64

import requests
import json
import base64
from io import BytesIO
from PIL import Image

class TelemedicineAnalyzer:
    """
    ระบบวิเคราะห์อาการป่วยเบื้องต้น
    รองรับ: รูปภาพ + ข้อความอาการ
    Model: gemini-2.0-flash (ผ่าน HolySheep)
    """
    
    BASE_URL = "https://api.holysheep.ai/v1"
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def encode_image_to_base64(self, image_path: str) -> str:
        """แปลงรูปภาพเป็น base64 string"""
        with Image.open(image_path) as img:
            # ปรับขนาดให้เหมาะสม (max 1024px)
            img.thumbnail((1024, 1024))
            buffer = BytesIO()
            img.save(buffer, format="JPEG", quality=85)
            return base64.b64encode(buffer.getvalue()).decode('utf-8')
    
    def analyze_symptoms(self, symptom_text: str, image_paths: list = None, 
                        patient_age: int = None, patient_gender: str = None):
        """
        วิเคราะห์อาการจากข้อความและรูปภาพ
        
        Args:
            symptom_text: คำอธิบายอาการจากผู้ป่วย
            image_paths: รายการ path ของรูปภาพ (ผิวหนัง แผล ฯลฯ)
            patient_age: อายุผู้ป่วย
            patient_gender: เพศผู้ป่วย
        
        Returns:
            dict: ผลวิเคราะห์พร้อมคำแนะนำเบื้องต้น
        """
        
        # สร้าง content สำหรับ multimodal
        contents = []
        
        # ส่วนข้อความ - รวบรวมข้อมูลผู้ป่วย
        patient_info = ""
        if patient_age:
            patient_info += f"อายุ: {patient_age} ปี\n"
        if patient_gender:
            patient_info += f"เพศ: {patient_gender}\n"
        
        symptom_prompt = f"""คุณเป็นแพทย์ที่ปรึกษาอาการเบื้องต้น วิเคราะห์ข้อมูลต่อไปนี้:
        
ข้อมูลผู้ป่วย:
{patient_info}

อาการที่แจ้ง:
{symptom_text}

กรุณาตอบเป็น JSON format ดังนี้:
{{
    "initial_diagnosis": "การวินิจฉัยเบื้องต้น",
    "severity_level": "low/medium/high/urgent",
    "possible_conditions": ["รายการโรคที่เป็นไปได้"],
    "self_care_recommendations": ["คำแนะนำดูแลตัวเอง"],
    "referral_recommendation": "ควร/ไม่ควรส่งต่อไปพบแพทย์เฉพาะทาง",
    "specialist_type": "ประเภทแพทย์เฉพาะทางที่ควรพบ (ถ้าต้องส่งต่อ)",
    "urgency_reason": "เหตุผลที่ต้องรีบพบแพทย์ (ถ้ามี)"
}}
"""
        
        contents.append({
            "type": "text",
            "text": symptom_prompt
        })
        
        # ส่วนรูปภาพ (ถ้ามี)
        if image_paths:
            for path in image_paths:
                img_base64 = self.encode_image_to_base64(path)
                contents.append({
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/jpeg;base64,{img_base64}"
                    }
                })
        
        # เรียก API
        payload = {
            "model": "gemini-2.0-flash",
            "messages": [
                {
                    "role": "user",
                    "content": contents
                }
            ],
            "temperature": 0.3,
            "max_tokens": 2000
        }
        
        response = requests.post(
            f"{self.BASE_URL}/chat/completions",
            headers=self.headers,
            json=payload,
            timeout=60
        )
        
        if response.status_code == 200:
            result = response.json()
            return json.loads(result['choices'][0]['message']['content'])
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

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

analyzer = TelemedicineAnalyzer(api_key="YOUR_HOLYSHEEP_API_KEY") result = analyzer.analyze_symptoms( symptom_text="มีผื่นแดงที่แขนข้างซ้าย คันมาก เกิดขึ้นประมาณ 3 วัน ดูเหมือนมีตุ่มน้ำเล็กๆ", image_paths=["/uploads/rash_arm_001.jpg"], patient_age=28, patient_gender="หญิง" ) print(f"การวินิจฉัย: {result['initial_diagnosis']}") print(f"ระดับความรุนแรง: {result['severity_level']}") print(f"ควรส่งต่อ: {result['referral_recommendation']}")

ระบบ Auto-Triage: การแนะนำแพทย์เฉพาะทางอัตโนมัติ

หลังจากวิเคราะห์อาการเบื้องต้นแล้ว ระบบต้องสามารถจัดกลุ่มผู้ป่วยและส่งต่อไปยังแพทย์เฉพาะทางที่เหมาะสม ผมพัฒนา routing logic ที่พิจารณาจากหลายปัจจัย ได้แก่ ความรุนแรงของอาการ ประเภทของโรค และความพร้อมของแพทย์ในเครือข่าย

from enum import Enum
from datetime import datetime, timedelta
import hashlib

class SpecialistType(Enum):
    GENERAL = "แพทย์เวชกรรมทั่วไป"
    DERMATOLOGY = "แพทย์ผิวหนัง"
    INTERNAL = "แพทย์อายุรกรรม"
    PEDIATRICS = "แพทย์เด็ก"
    CARDIOLOGY = "แพทย์หัวใจ"
    ORTHOPEDICS = "แพทย์กระดูก"
    OPHTHALMOLOGY = "จักษุแพทย์"
    URGENT = "ห้องฉุกเฉิน"
    MENTAL = "จิตแพทย์"

class TriageRouter:
    """
    ระบบจัดกลุ่มผู้ป่วยอัตโนมัติ
    พิจารณา: ความรุนแรง, ประเภทอาการ, ความพร้อมแพทย์
    """
    
    # Mapping อาการ -> แพทย์เฉพาะทาง
    SYMPTOM_SPECIALIST_MAP = {
        "ผื่น": SpecialistType.DERMATOLOGY,
        "คัน": SpecialistType.DERMATOLOGY,
        "สิว": SpecialistType.DERMATOLOGY,
        "ผมร่วง": SpecialistType.DERMATOLOGY,
        "ไข้": SpecialistType.INTERNAL,
        "ไอ": SpecialistType.INTERNAL,
        "เจ็บคอ": SpecialistType.INTERNAL,
        "ปวดท้อง": SpecialistType.INTERNAL,
        "ท้องเสีย": SpecialistType.INTERNAL,
        "คลื่นไส้": SpecialistType.INTERNAL,
        "เวียนศีรษะ": SpecialistType.INTERNAL,
        "เจ็บหน้าอก": SpecialistType.CARDIOLOGY,
        "ใจสั่น": SpecialistType.CARDIOLOGY,
        "หายใจลำบาก": SpecialistType.URGENT,
        "เลือดออกมาก": SpecialistType.URGENT,
        "หมดสติ": SpecialistType.URGENT,
        "กระดูก": SpecialistType.ORTHOPEDICS,
        "ข้อ": SpecialistType.ORTHOPEDICS,
        "ตา": SpecialistType.OPHTHALMOLOGY,
        "มองเห็น": SpecialistType.OPHTHALMOLOGY,
        "เครียด": SpecialistType.MENTAL,
        "ซึมเศร้า": SpecialistType.MENTAL,
        "นอนไม่หลับ": SpecialistType.MENTAL
    }
    
    URGENT_KEYWORDS = [
        "หายใจไม่ออก", "แน่นหน้าอก", "เลือดออก", "หมดสติ",
        "ชัก", "อาเจียนเลือด", "อุจจาระเป็นเลือด", "คลอด",
        "เจ็บรุนแรงมาก", "ถูกทำร้าย"
    ]
    
    def __init__(self, db_connection):
        self.db = db_connection
    
    def classify_urgency(self, symptom_text: str, severity: str) -> str:
        """จำแนกระดับความเร่งด่วน"""
        
        # ตรวจสอบ keyword ฉุกเฉิน
        symptom_lower = symptom_text.lower()
        for keyword in self.URGENT_KEYWORDS:
            if keyword in symptom_lower:
                return "urgent"
        
        # ตรวจสอบ severity จาก AI
        severity_map = {
            "urgent": "urgent",
            "high": "high",
            "medium": "medium",
            "low": "low"
        }
        return severity_map.get(severity, "medium")
    
    def route_to_specialist(self, symptom_text: str, ai_result: dict) -> dict:
        """กำหนดเส้นทางการรักษา"""
        
        symptom_lower = symptom_text.lower()
        suggested_specialist = SpecialistType.GENERAL
        
        # หา specialist ที่เหมาะสมจาก symptom keywords
        for keyword, specialist in self.SYMPTOM_SPECIALIST_MAP.items():
            if keyword in symptom_lower:
                suggested_specialist = specialist
                break
        
        # ตรวจสอบความเร่งด่วน
        urgency = self.classify_urgency(
            symptom_text, 
            ai_result.get('severity_level', 'medium')
        )
        
        # ถ้า urgent ให้ไปห้องฉุกเฉิน
        if urgency == "urgent":
            return {
                "queue_type": "emergency",
                "specialist": SpecialistType.URGENT,
                "wait_time": 0,
                "instructions": "กรุณาไปโรงพยาบาลหรือเรียกรถพยาบาลทันที",
                "referral_reason": ai_result.get('urgency_reason', 'อาการฉุกเฉิน')
            }
        
        # หาแพทย์ที่ว่าง
        available_doctors = self._find_available_doctors(suggested_specialist)
        
        if not available_doctors:
            # ถ้าไม่มีแพทย์เฉพาะทางว่าง ให้ general ก่อน
            available_doctors = self._find_available_doctors(SpecialistType.GENERAL)
        
        return {
            "queue_type": "appointment",
            "specialist": suggested_specialist,
            "available_doctors": available_doctors,
            "estimated_wait": self._calculate_wait_time(available_doctors),
            "instructions": self._generate_instructions(urgency, suggested_specialist)
        }
    
    def _find_available_doctors(self, specialist_type: SpecialistType) -> list:
        """ค้นหาแพทย์ที่ว่างในปัจจุบัน"""
        # Mock implementation - เชื่อมต่อ database จริงใน production
        return [
            {"id": "DR001", "name": "นายแพทย์สมชาย", "rating": 4.8, "next_available": "10:00"},
            {"id": "DR002", "name": "นางแพทย์สมหญิง", "rating": 4.9, "next_available": "14:30"}
        ]
    
    def _calculate_wait_time(self, doctors: list) -> str:
        """คำนวณเวลารอโดยประมาณ"""
        if not doctors:
            return "2-3 วัน"
        
        earliest = doctors[0]['next_available']
        now = datetime.now()
        
        if ":" in str(earliest):
            # ถ้าเป็นเวลาวันนี้
            return f"ประมาณ {earliest}"
        
        return earliest
    
    def _generate_instructions(self, urgency: str, specialist: SpecialistType) -> str:
        """สร้างคำแนะนำสำหรับผู้ป่วย"""
        
        base_instructions = {
            "urgent": "⚠️ กรุณาไปพบแพทย์ทันที",
            "high": "ควรพบแพทย์ภายใน 24 ชั่วโมง",
            "medium": "นัดพบแพทย์ภายใน 3-7 วัน",
            "low": "สามารถนัดพบแพทย์ตามความสะดวก"
        }
        
        return base_instructions.get(urgency, "ปรึกษาแพทย์ตามปกติ")

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

router = TriageRouter(db_connection=None) ai_result = { "initial_diagnosis": "ผื่นผิวหนังอักเสบ (Dermatitis)", "severity_level": "medium", "possible_conditions": ["Contact dermatitis", "Eczema", "Allergic reaction"], "referral_recommendation": "ควรพบแพทย์ผิวหนัง", "specialist_type": "แพทย์ผิวหนัง" } route = router.route_to_specialist( "มีผื่นแดงคันที่แขน มีตุ่มน้ำเล็กน้อย", ai_result ) print(f"ประเภทคิว: {route['queue_type']}") print(f"แพทย์เฉพาะทาง: {route['specialist'].value}") print(f"คำแนะนำ: {route['instructions']}")

ระบบ Chat History และ Context Management

import redis
import json
from typing import Optional

class ConsultationContext:
    """
    จัดการ context การปรึกษาแพทย์
    เก็บประวัติการสนทนา + ข้อมูลผู้ป่วย
    """
    
    def __init__(self, session_id: str, api_key: str):
        self.session_id = session_id
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        self.context_key = f"consultation:{session_id}"
        
        # ใช้ Redis สำหรับ session storage
        # self.redis = redis.Redis(host='localhost', port=6379, db=0)
        
        self.conversation_history = []
        self.patient_context = {}
    
    def add_patient_info(self, info: dict):
        """เพิ่มข้อมูลผู้ป่วย"""
        self.patient_context.update(info)
    
    def add_message(self, role: str, content: str):
        """เพิ่มข้อความในประวัติการสนทนา"""
        self.conversation_history.append({
            "role": role,
            "content": content,
            "timestamp": datetime.now().isoformat()
        })
    
    def build_context_prompt(self) -> str:
        """สร้าง context prompt สำหรับ AI"""
        
        context_parts = []
        
        # ข้อมูลผู้ป่วย
        if self.patient_context:
            context_parts.append("ข้อมูลผู้ป่วย:")
            for key, value in self.patient_context.items():
                context_parts.append(f"- {key}: {value}")
        
        # ประวัติการสนทนา (5 ข้อความล่าสุด)
        if self.conversation_history:
            context_parts.append("\nประวัติการสนทนา:")
            recent = self.conversation_history[-5:]
            for msg in recent:
                role_display = "ผู้ป่วย" if msg['role'] == 'user' else "แพทย์ AI"
                context_parts.append(f"{role_display}: {msg['content']}")
        
        return "\n".join(context_parts)
    
    def continue_consultation(self, new_question: str) -> str:
        """
        สานต่อการปรึกษาโดยใช้ context ที่มี
        """
        
        context = self.build_context_prompt()
        
        full_prompt = f"""คุณเป็นผู้ช่วยแพทย์ที่กำลังให้คำปรึกษาเบื้องต้น
โปรดใช้ข้อมูลต่อไปนี้ในการตอบคำถามต่อไปนี้:

{context}

---
คำถามใหม่: {new_question}

ตอบเป็นภาษาไทย กระชับ เข้าใจง่าย หลีกเลี่ยงศัพท์แพทย์ที่ซับซ้อนเกินไป
"""
        
        payload = {
            "model": "gemini-2.0-flash",
            "messages": [{"role": "user", "content": full_prompt}],
            "temperature": 0.5,
            "max_tokens": 1000
        }
        
        response = requests.post(
            f"{self.base_url}/chat/completions",
            headers={"Authorization": f"Bearer {self.api_key}"},
            json=payload,
            timeout=30
        )
        
        if response.status_code == 200:
            result = response.json()
            answer = result['choices'][0]['message']['content']
            
            # บันทึกลงประวัติ
            self.add_message("user", new_question)
            self.add_message("assistant", answer)
            
            return answer
        else:
            raise Exception(f"API Error: {response.status_code}")

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

context = ConsultationContext( session_id="session_2026_0524_001", api_key="YOUR_HOLYSHEEP_API_KEY" )

เพิ่มข้อมูลผู้ป่วยครั้งแรก

context.add_patient_info({ "ชื่อ": "สมศรี ใจดี", "อายุ": 35, "โรคประจำตัว": "ไม่มี", "ยาที่แพ้": "ไม่ทราบ" })

ถามคำถามต่อเนื่อง

answer1 = context.continue_consultation("มีผื่นแดงที่คอ คันมาก") print(f"คำตอบที่ 1: {answer1}") answer2 = context.continue_consultation("เพิ่งเปลี่ยนสบู่ใหม่ จะเกี่ยวกับนั้นไหม") print(f"คำตอบที่ 2: {answer2}") answer3 = context.continue_consultation("ควรทายาอะไรก่อนไปพบแพทย์") print(f"คำตอบที่ 3: {answer3}")

ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข

1. ข้อผิดพลาด "ConnectionError: timeout after 30s"

สาเหตุ: Gemini API มี latency สูงกว่าปกติ หรือ network timeout สั้นเกินไป

# ❌ วิธีที่ไม่ถูกต้อง - timeout 30 วินาที
response = requests.post(url, json=payload, timeout=30)

✅ วิธีที่ถูกต้อง - timeout 60 วินาที + retry logic

from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def create_session_with_retry(): session = requests.Session() retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("https://", adapter) return session session = create_session_with_retry() response = session.post( f"{BASE_URL}/chat/completions", headers=headers, json=payload, timeout=60 # เพิ่ม timeout เป็น 60 วินาที )

ถ้าใช้ async/await

import asyncio import aiohttp async def call_api_async(payload, max_retries=3): for attempt in range(max_retries): try: async with aiohttp.ClientSession() as session: async with session.post( f"{BASE_URL}/chat/completions", headers=headers, json=payload, timeout=aiohttp.ClientTimeout(total=60) ) as response: return await response.json() except asyncio.TimeoutError: if attempt == max_retries - 1: raise await asyncio.sleep(2 ** attempt) # Exponential backoff

2. ข้อผิดพลาด "401 Unauthorized" หรือ "403 Forbidden"

สาเหตุ: API key ไม่ถูกต้อง หมดอายุ หรือ quota เกิน limit

import os
from datetime import datetime

class HolySheepAuth:
    """
    จัดการ authentication กับ HolySheep API
    รวมถึงการตรวจสอบ quota และ renew key
    """
    
    def __init__(self):
        self.api_key = os.environ.get("HOLYSHEEP_API_KEY")
        self.base_url = "https://api.holysheep.ai/v1"
    
    def validate_key(self) -> dict:
        """ตรวจสอบความถูกต้องของ API key"""
        
        try:
            # เรียก API เพื่อตรวจสอบ quota
            response = requests.get(
                f"{self.base_url}/usage",
                headers={"Authorization": f"Bearer {self.api_key}"},
                timeout=10
            )
            
            if response.status_code == 200:
                return {
                    "valid": True,
                    "quota": response.json()
                }
            elif response.status_code == 401:
                return {
                    "valid": False,
                    "error": "API key ไม่ถูกต้องหรือหมดอายุ"
                }
            elif response.status_code == 403:
                return {
                    "valid": False,
                    "error": "ไม่มีสิทธิ์เข้าถึง API นี้"
                }
            else:
                return {
                    "valid": False,
                    "error": f"ข้อผิดพลาด {response.status_code}"
                }
                
        except requests.exceptions.RequestException as e:
            return {
                "valid": False,
                "error": f"ไม่สามารถเชื่อมต่อ: {str(e)}"
            }
    
    def check_quota_remaining(self) -> float:
        """ตรวจสอบ quota ที่เหลือ (หน่วย token)"""
        
        validation = self.validate_key()