สร้าง AI Agent คัดกรองและสรุปอีเมลอัจฉริยะ: ดึง Action Item และส่งตรงเข้า Obsidian ด้วย Python + Ollama

จัดการกับ Inbox ที่ท่วมท้นด้วย AI Agent ส่วนตัวที่ช่วยดึงอีเมลล่าสุด คัดกรองความสำคัญ สรุปเนื้อหา พร้อมแยก Action Item ส่งตรงเข้า Obsidian Vault แบบส่วนตัวและปลอดภัย 100%

Share
สร้าง AI Agent คัดกรองและสรุปอีเมลอัจฉริยะ: ดึง Action Item และส่งตรงเข้า Obsidian ด้วย Python + Ollama

ในแต่ละวัน หนึ่งในแหล่งข้อมูลที่สร้างความเหนื่อยล้าทางความคิดให้กับเรามากที่สุดก็คือ อีเมล (Inbox) ไม่ว่าจะเป็นอีเมลติดต่องาน ข่าวสารประชาสัมพันธ์ บิลค่าบริการ หรือโฆษณาต่างๆ ที่คอยขัดจังหวะการทำงานอยู่เสมอ การประหยัดพลังงานสมองในการคัดกรองอีเมลเหล่านี้ หรือที่เรียกกันว่าการทำ Cognitive Automation จึงกลายเป็นทักษะที่จำเป็นสำหรับผู้ที่ต้องการเพิ่มประสิทธิภาพการทำงาน (Productivity)

การใช้บริการคลาวด์เพื่ออ่านและสรุปอีเมลส่วนตัวอาจทำให้เกิดความเสี่ยงเรื่อง ความเป็นส่วนตัวของข้อมูล (Data Privacy) เนื่องจากอีเมลเปรียบเสมือนไดอารี่การทำงานที่มีทั้งข้อมูลลูกค้า ธุรกรรม และความลับขององค์กร ดังนั้น การสร้าง AI Agent ส่วนตัวเพื่อทำหน้าที่ดึงข้อมูล วิเคราะห์ และจัดเก็บสรุปลงใน Second Brain ของเราอย่าง Obsidian ภายในเครื่องแบบ Local 100% จึงเป็นทางเลือกที่ปลอดภัยและตอบโจทย์ที่สุดในยุคนี้


โครงสร้างการทำงานของ AI Email Triage Agent

ระบบที่เรากำลังจะสร้างขึ้นมานี้ใช้หลักการง่ายๆ ในการเชื่อมโยงเทคโนโลยีแบบ Local-first โดยมีกระบวนการไหลของข้อมูลดังนี้:

  1. ดึงอีเมล (Fetch): สคริปต์ Python จะทำการเชื่อมต่อกับกล่องจดหมายของคุณอย่างปลอดภัยผ่านโปรโตคอล IMAP เพื่อดึงอีเมลที่ยังไม่ได้อ่าน (Unread Emails)
  2. คัดกรองและสรุป (Triage & Summarize): ส่งเนื้อหาอีเมลไปยัง Ollama เพื่อให้โมเดลภาษาขนาดใหญ่ (LLM) รันในเครื่องช่วยจัดหมวดหมู่ ประเมินความสำคัญ และสรุปออกมาเป็นรายการสิ่งที่ต้องทำ (Action Items)
  3. จัดเก็บลงระบบคิด (Store): นำผลลัพธ์ที่ได้มาสร้างเป็นไฟล์ Markdown (.md) ลงในโฟลเดอร์ของ Obsidian Vault เพื่อให้เราเห็นงานที่ต้องทำอย่างเป็นระบบโดยไม่ต้องเปิดเข้าแอปอีเมลบ่อยๆ

สิ่งที่ต้องเตรียมก่อนเริ่มสร้าง

โปรดตรวจสอบให้แน่ใจว่าเครื่องคอมพิวเตอร์ของคุณได้รับการติดตั้งสิ่งเหล่านี้เรียบร้อยแล้ว:

  • Python 3: ภาษาหลักที่ใช้ขับเคลื่อนระบบและไลบรารีมาตรฐาน
  • Ollama: ตัวรัน LLM ออฟไลน์ภายในเครื่อง (ดาวน์โหลดได้จาก ollama.com)
  • Obsidian: แอปพลิเคชันจดโน้ตแนว Second Brain สำหรับจัดเก็บผลสรุป

สำหรับบทความนี้ เราจะเลือกใช้โมเดลภาษาที่มีขนาดกะทัดรัดและเข้าใจภาษาไทยได้เป็นอย่างดี เช่น gemma2 หรือ llama3 ให้ดาวน์โหลดโมเดลเตรียมไว้ใน Terminal:

ollama pull gemma2
[!NOTE] ในการเชื่อมต่ออีเมลผ่านโปรโตคอล IMAP หากคุณใช้งานผู้ให้บริการยอดนิยมอย่าง Gmail หรือ Outlook คุณจำเป็นต้องเปิดการใช้งาน App Password (รหัสผ่านสำหรับแอปพลิเคชันภายนอก) ในการตั้งค่าบัญชีก่อน จึงจะสามารถใช้สคริปต์ล็อกอินได้

เขียนสคริปต์ Python ควบคุม AI Email Agent

เราจะสร้างไฟล์สคริปต์ Python โดยอาศัยเพียงไลบรารีมาตรฐานที่ติดมากับตัว Python อยู่แล้ว เพื่อให้ติดตั้งและเปิดใช้งานได้ทันทีโดยไม่ต้องรันคำสั่งติดตั้งแพ็กเกจเพิ่ม (Zero-dependency)

ให้สร้างไฟล์ชื่อ email_triage_agent.py ขึ้นมา แล้วคัดลอกโค้ดด้านล่างนี้ลงไป:

import imaplib
import email
from email.header import decode_header
import urllib.request
import json
import os
import re
from datetime import datetime

# ================= ตั้งค่าระบบ (Configuration) =================
IMAP_SERVER = "imap.gmail.com"           # เซิร์ฟเวอร์ IMAP ของอีเมลคุณ
EMAIL_USER = "[email protected]"       # ที่อยู่อีเมลของคุณ
EMAIL_PASS = "your_app_password"          # App Password ที่เจนมาจากบัญชี
OBSIDIAN_VAULT_PATH = "/home/linn/notes"  # Path สำหรับจัดเก็บโฟลเดอร์ของ Obsidian
LLM_MODEL = "gemma2"                     # โมเดล Ollama ที่ดึงไว้ในเครื่อง
# =============================================================

def clean_html(html_content):
    # ฟังก์ชันลบแท็ก HTML เพื่อลดขนาด Token ในการส่งให้ LLM ประมวลผล
    text = re.sub('<[^<]+?>', '', html_content)
    text = re.sub(r'\s+', ' ', text)
    return text.strip()

def get_email_body(msg):
    # ดึงข้อความของอีเมลไม่ว่าจะเป็นข้อความธรรมดาหรือ HTML
    if msg.is_multipart():
        for part in msg.walk():
            content_type = part.get_content_type()
            content_disposition = str(part.get("Content-Disposition"))
            if content_type == "text/plain" and "attachment" not in content_disposition:
                return part.get_payload(decode=True).decode(part.get_content_charset() or 'utf-8', errors='ignore')
            elif content_type == "text/html" and "attachment" not in content_disposition:
                html = part.get_payload(decode=True).decode(part.get_content_charset() or 'utf-8', errors='ignore')
                return clean_html(html)
    else:
        content_type = msg.get_content_type()
        if content_type == "text/plain":
            return msg.get_payload(decode=True).decode(msg.get_content_charset() or 'utf-8', errors='ignore')
        elif content_type == "text/html":
            html = msg.get_payload(decode=True).decode(msg.get_content_charset() or 'utf-8', errors='ignore')
            return clean_html(html)
    return ""

def process_email_with_ollama(subject, sender, body):
    # prompt ออกแบบพิเศษเพื่อให้ AI สรุปข้อมูลการทำงานออกมาเป็นระเบียบ
    prompt = f"""
คุณคือ AI Agent ผู้เชี่ยวชาญด้านการจัดสรรการทำงานและอีเมลอัจฉริยะ 
จงวิเคราะห์ข้อมูลอีเมลนี้แล้วตอบเป็นภาษาไทยอย่างชัดเจนตามรูปแบบโครงสร้างที่กำหนด:

[ข้อมูลอีเมล]
ผู้ส่ง: {sender}
หัวข้อ: {subject}
เนื้อหาดิบ: {body[:3000]} # จำกัดข้อความเพื่อไม่ให้โทเค็นล้น

[ขั้นตอนการตอบกลับ]
1. **หมวดหมู่**: เลือกข้อใดข้อหนึ่งจาก [งานสำคัญ / ประชาสัมพันธ์ / ธุรกรรม-การเงิน / สแปม-โฆษณา]
2. **ความสำคัญ**: ระบุระดับความสำคัญ [สูง / ปานกลาง / ต่ำ] พร้อมเหตุผลสั้นๆ
3. **สรุปเนื้อหา**: สรุปเนื้อหาสำคัญ 2-3 ประโยค
4. **Action Items**: เขียนสิ่งที่ผู้ใช้จำเป็นต้องทำเป็นข้อๆ หรือระบุ "ไม่มีสิ่งที่ต้องจัดการต่อ" หากเป็นเพียงแค่อีเมลแจ้งเพื่อทราบ
"""

    url = "http://localhost:11434/api/generate"
    data = {
        "model": LLM_MODEL,
        "prompt": prompt,
        "stream": False
    }

    req = urllib.request.Request(
        url, 
        data=json.dumps(data).encode('utf-8'), 
        headers={'Content-Type': 'application/json'}
    )

    try:
        with urllib.request.urlopen(req) as response:
            result = json.loads(response.read().decode('utf-8'))
            return result.get("response", "")
    except Exception as e:
        return f"เกิดข้อผิดพลาดในการประมวลผล Ollama: {str(e)}"

def save_to_obsidian(subject, sender, summary_content):
    # ตั้งชื่อไฟล์โดยลบอักขระพิเศษที่ระบบไฟล์ไม่รองรับออก
    safe_subject = re.sub(r'[\\/*?:"<>|]', "", subject)[:40]
    date_str = datetime.now().strftime("%Y-%m-%d")
    folder_path = os.path.join(OBSIDIAN_VAULT_PATH, "Emails")
    os.makedirs(folder_path, exist_ok=True)

    file_name = f"{date_str} - {safe_subject}.md"
    file_path = os.path.join(folder_path, file_name)

    markdown_template = f"""---
type: email-triage
sender: {sender}
subject: {subject}
date_analyzed: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
---

# สรุปวิเคราะห์อีเมลอัจฉริยะ
**หัวข้อ:** {subject}
**ผู้ส่ง:** {sender}

---

{summary_content}
"""

    with open(file_path, "w", encoding="utf-8") as f:
        f.write(markdown_template)
    print(f"บันทึกไฟล์สรุปอีเมลแล้วที่: {file_path}")

def run_agent():
    print("กำลังเชื่อมต่อกับกล่องจดหมายผ่าน IMAP...")
    try:
        mail = imaplib.IMAP4_SSL(IMAP_SERVER)
        mail.login(EMAIL_USER, EMAIL_PASS)
        mail.select("inbox")

        # ค้นหาอีเมลที่ยังไม่ได้อ่าน
        status, response_data = mail.search(None, 'UNSEEN')
        email_ids = response_data[0].split()

        if not email_ids:
            print("ไม่มีอีเมลใหม่ที่ยังไม่ได้อ่านในขณะนี้")
            return

        print(f"พบอีเมลใหม่ทั้งหมด {len(email_ids)} ฉบับ กำลังประมวลผล...")

        # ดึงมาประมวลผล 5 ฉบับล่าสุดเพื่อรักษาระยะเวลาทำงาน
        for e_id in email_ids[:5]:
            status, msg_data = mail.fetch(e_id, '(RFC822)')
            for response_part in msg_data:
                if isinstance(response_part, tuple):
                    msg = email.message_from_bytes(response_part[1])

                    # ถอดรหัสหัวข้ออีเมล
                    subject, encoding = decode_header(msg["Subject"])[0]
                    if isinstance(subject, bytes):
                        subject = subject.decode(encoding or "utf-8", errors="ignore")

                    # ถอดรหัสผู้ส่ง
                    sender, encoding = decode_header(msg["From"])[0]
                    if isinstance(sender, bytes):
                        sender = sender.decode(encoding or "utf-8", errors="ignore")

                    body = get_email_body(msg)

                    print(f"ประมวลผลอีเมล: {subject} จาก {sender}")
                    analysis_result = process_email_with_ollama(subject, sender, body)
                    save_to_obsidian(subject, sender, analysis_result)

        mail.logout()
        print("การคัดกรองและประมวลผลอีเมลเสร็จสมบูรณ์")
    except Exception as e:
        print(f"เกิดข้อผิดพลาดในการรันระบบ: {str(e)}")

if __name__ == "__main__":
    run_agent()

การตั้งเวลาทำงานอัตโนมัติ (Automation)

เมื่อสคริปต์สามารถรันได้เรียบร้อยแล้ว คุณสามารถตัังเวลาให้คอมพิวเตอร์ของคุณรันสคริปต์นี้โดยอัตโนมัติทุกๆ 30 นาที หรือทุกเช้าก่อนเข้างาน เพื่อดึงงานเด่นๆ เข้ามาคอยท่าใน Obsidian

หากใช้งานระบบปฏิบัติการ Linux หรือ macOS สามารถใช้ระบบตั้งเวลาสเก็ตจูลาร์อย่าง cron ได้ง่ายๆ โดยพิมพ์คำสั่งเปิดไฟล์กำหนดค่า:

crontab -e

จากนั้นเพิ่มบรรทัดตั้งค่าให้รันสคริปต์ทุกๆ 30 นาที (สลับเส้นทางไฟล์ Python และสคริปต์จริงตามโฟลเดอร์ของคุณ):

*/30 * * * * /usr/bin/python3 /home/linn/email_triage_agent.py >> /home/linn/email_agent.log 2>&1

และหากคุณใช้งาน Windows คุณก็สามารถสร้าง Task ใน Task Scheduler เพื่อสั่งรันไฟล์สคริปต์ตามรอบเวลาที่ต้องการได้อย่างง่ายดายเช่นกัน


สรุปแนวคิดการจัดการ Inbox แบบ Local-first

การปรับเปลี่ยนระบบอีเมลให้ทำงานด้วยสถาปัตยกรรมแบบ Local-first AI นี้ช่วยให้ข้อมูลการติดต่อสื่อสารและกิจกรรมทั้งหมดของเราถูกบันทึกและประเมินอย่างเป็นอิสระอยู่บนฮาร์ดแวร์ที่เราควบคุมได้ 100% การประมวลผลข้อมูลที่มีความลับสูงในเครื่องตัดปัญหาความจำเป็นในการแชร์ข้อมูลกับผู้ให้บริการรายใหญ่ภายนอกได้อย่างสมบูรณ์

และเมื่อบันทึกข้อมูลอีเมลคัดกรองเหล่านี้เข้าสู่ Obsidian ในรูปแบบของ Markdown ทำให้เราสามารถสร้างระบบจัดการงาน Second Brain ที่ทรงประสิทธิภาพ คุณสามารถรันปลั๊กอินอย่าง Dataview เพื่อดึง Action Items จากอีเมลที่มีความสำคัญระดับสูงขึ้นมาแสดงบนบอร์ดควบคุมหลัก (Dashboard) ได้อย่างรวดเร็ว เป็นอันเสร็จสิ้นขั้นตอนการทรานส์ฟอร์มกล่องข้อความรกๆ ของคุณให้กลายมาเป็นส่วนหนึ่งของขุมพลังคิดวิเคราะห์ส่วนตัวแบบไร้รอยต่อ

Read more

สร้าง AI Agent สกัด Knowledge Graph จากโน้ตส่วนตัว: เปลี่ยนข้อความกระจัดกระจายให้เป็นเครือข่ายความรู้ออฟไลน์ 100% ด้วย Python

สร้าง AI Agent สกัด Knowledge Graph จากโน้ตส่วนตัว: เปลี่ยนข้อความกระจัดกระจายให้เป็นเครือข่ายความรู้ออฟไลน์ 100% ด้วย Python + Ollama

เปลี่ยนเอกสารและโน้ตข้อความร้อยแก้วให้เป็น Knowledge Graph แบบโครงสร้าง (Subject-Predicate-Object) โดยอัตโนมัติ ด้วยสคริปต์ Python ร่วมกับ Local LLM ในเครื่อง ช่วยสกัดความสัมพันธ์ระหว่างความคิด เทคโนโลยี และบุคคลอย่างเป็นระบบออฟไลน์ 100%

By Linn
บอกลา Audible: คู่มือติดตั้ง Audiobookshelf เซิร์ฟเวอร์หนังสือเสียงและ eBook ส่วนตัวบน Home Server ด้วย Docker

บอกลา Audible: คู่มือติดตั้ง Audiobookshelf เซิร์ฟเวอร์หนังสือเสียงและ eBook ส่วนตัวบน Home Server ด้วย Docker

เปลี่ยนคลังหนังสือเสียงและดิจิทัลไฟล์ของคุณให้กลายเป็นเซิร์ฟเวอร์สตรีมมิ่งส่วนตัว ด้วย Audiobookshelf บน Home Server ผ่าน Docker พร้อมวิธีซิงค์ความคืบหน้าการฟังข้ามอุปกรณ์แบบเรียลไทม์

By Linn
สร้าง AI Agent สกัดและสังเคราะห์ Book Highlights: เปลี่ยนโน้ตอ่านหนังสือเป็น Book Summary อัจฉริยะใน Obsidian ด้วย Python + O

สร้าง AI Agent สกัดและสังเคราะห์ Book Highlights: เปลี่ยนโน้ตอ่านหนังสือเป็น Book Summary อัจฉริยะใน Obsidian ด้วย Python + Ollama

เปลี่ยนไฮไลต์และโน้ตย่อยจากการอ่านหนังสือให้กลายเป็น Book Summary ที่ทรงคุณค่าด้วย AI Agent ออฟไลน์ส่วนตัวที่จะสกัด Core Key Takeaways, Actionable Insights และเชื่อมโยงเข้ากับระบบ Second Brain แบบ Local 100%

By Linn
สร้าง Private AI Router ในเครื่องตัวเอง: คู่มือตั้งค่า LiteLLM สลับโมเดลอัจฉริยะตามประเภทงานแบบออฟไลน์ 100%

สร้าง Private AI Router ในเครื่องตัวเอง: คู่มือตั้งค่า LiteLLM สลับโมเดลอัจฉริยะตามประเภทงานแบบออฟไลน์ 100%

เรียนรู้วิธีตั้งค่า LiteLLM เป็น AI Gateway และ Router ในเครื่องตัวเอง เพื่อสลับรันโมเดล Ollama อัตโนมัติ ทั้งโมเดลเน้นความเร็ว โมเดลคิดวิเคราะห์เชิงลึก และโมเดลวิเคราะห์ภาพ ปลอดภัย 100% ไม่ต้องพึ่งคลาวด์

By Linn