สร้าง AI Agent คัดกรองและสรุปอีเมลอัจฉริยะ: ดึง Action Item และส่งตรงเข้า Obsidian ด้วย Python + Ollama
จัดการกับ Inbox ที่ท่วมท้นด้วย AI Agent ส่วนตัวที่ช่วยดึงอีเมลล่าสุด คัดกรองความสำคัญ สรุปเนื้อหา พร้อมแยก Action Item ส่งตรงเข้า Obsidian Vault แบบส่วนตัวและปลอดภัย 100%
ในแต่ละวัน หนึ่งในแหล่งข้อมูลที่สร้างความเหนื่อยล้าทางความคิดให้กับเรามากที่สุดก็คือ อีเมล (Inbox) ไม่ว่าจะเป็นอีเมลติดต่องาน ข่าวสารประชาสัมพันธ์ บิลค่าบริการ หรือโฆษณาต่างๆ ที่คอยขัดจังหวะการทำงานอยู่เสมอ การประหยัดพลังงานสมองในการคัดกรองอีเมลเหล่านี้ หรือที่เรียกกันว่าการทำ Cognitive Automation จึงกลายเป็นทักษะที่จำเป็นสำหรับผู้ที่ต้องการเพิ่มประสิทธิภาพการทำงาน (Productivity)
การใช้บริการคลาวด์เพื่ออ่านและสรุปอีเมลส่วนตัวอาจทำให้เกิดความเสี่ยงเรื่อง ความเป็นส่วนตัวของข้อมูล (Data Privacy) เนื่องจากอีเมลเปรียบเสมือนไดอารี่การทำงานที่มีทั้งข้อมูลลูกค้า ธุรกรรม และความลับขององค์กร ดังนั้น การสร้าง AI Agent ส่วนตัวเพื่อทำหน้าที่ดึงข้อมูล วิเคราะห์ และจัดเก็บสรุปลงใน Second Brain ของเราอย่าง Obsidian ภายในเครื่องแบบ Local 100% จึงเป็นทางเลือกที่ปลอดภัยและตอบโจทย์ที่สุดในยุคนี้
โครงสร้างการทำงานของ AI Email Triage Agent
ระบบที่เรากำลังจะสร้างขึ้นมานี้ใช้หลักการง่ายๆ ในการเชื่อมโยงเทคโนโลยีแบบ Local-first โดยมีกระบวนการไหลของข้อมูลดังนี้:
- ดึงอีเมล (Fetch): สคริปต์ Python จะทำการเชื่อมต่อกับกล่องจดหมายของคุณอย่างปลอดภัยผ่านโปรโตคอล IMAP เพื่อดึงอีเมลที่ยังไม่ได้อ่าน (Unread Emails)
- คัดกรองและสรุป (Triage & Summarize): ส่งเนื้อหาอีเมลไปยัง Ollama เพื่อให้โมเดลภาษาขนาดใหญ่ (LLM) รันในเครื่องช่วยจัดหมวดหมู่ ประเมินความสำคัญ และสรุปออกมาเป็นรายการสิ่งที่ต้องทำ (Action Items)
- จัดเก็บลงระบบคิด (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) ได้อย่างรวดเร็ว เป็นอันเสร็จสิ้นขั้นตอนการทรานส์ฟอร์มกล่องข้อความรกๆ ของคุณให้กลายมาเป็นส่วนหนึ่งของขุมพลังคิดวิเคราะห์ส่วนตัวแบบไร้รอยต่อ