APIs + Integration
สัปดาห์นี้คือจุดที่ Python "คุยกับโลก" — ใช้ API ของคนอื่น (Google, LINE, Anthropic Claude, OpenAI, weather, …) · สร้าง API ของตัวเอง ด้วย Flask · เรียก LLM API ใส่ AI ใน app · รู้จัก GCP catalog ของ engineer · ครอบคลุมที่สุดของหลักสูตร
เป้าหมายสัปดาห์นี้
- เข้าใจ "API คืออะไร" และมีกี่ประเภท
- เรียก external REST API ด้วย
requests(GET, POST, headers, JSON) - เข้าใจ auth patterns 4 แบบ (API key / Bearer / OAuth / Service Account)
- เรียก LLM API (Claude / OpenAI / Gemini) จาก Python
- รู้จัก Google Cloud APIs ที่ engineer ใช้บ่อย
- สร้าง Flask API เล็ก ๆ ของตัวเอง
- เก็บ API key ใน
.envอย่างปลอดภัย + handle error + rate limit
🌐 API คืออะไร — เริ่มจาก mental model
API (Application Programming Interface) = "ประตูที่ระบบใช้คุยกัน" · ลองคิดเปรียบเทียบ:
| โลกจริง | API คือ |
|---|---|
| ร้านอาหาร — คนกินไม่ต้องเข้าครัว | เมนู + พนักงาน = API · ครัว = internal logic |
| ATM — ไม่ต้องเข้าธนาคาร | ปุ่ม + จอ = API · ระบบบัญชี = backend |
| เครื่องชงกาแฟ — กดปุ่ม | ปุ่มกาแฟ-นม-ฟอง = API · มอเตอร์ภายใน = implementation |
หลักการ: ระบบ A อยาก "ใช้" ระบบ B โดย "ไม่ต้องรู้ว่า B ทำงานยังไงข้างใน" · B เปิด API เป็น contract · A เรียก API → ได้ผลลัพธ์
🗺 API มีกี่ประเภท?
(ประตูคุยกับระบบ)"] api --> web["💻 Web API
(ผ่าน internet)"] api --> lib["📦 Library API
(ใน Python)"] api --> os["🖥 OS API
(เรียก system)"] api --> hw["🔌 Hardware API
(สื่อสารกับอุปกรณ์)"] web --> rest["REST
HTTP + JSON · ปกติที่สุด"] web --> graphql["GraphQL
1 endpoint · query ที่อยาก"] web --> ws["WebSocket
real-time bi-directional"] web --> rpc["gRPC
เร็ว · ใน datacenter"] lib --> pylib["pandas · numpy · requests
import แล้วใช้"] os --> file["file system
open() / read() / write()"] os --> proc["subprocess
เรียกโปรแกรมอื่น"] hw --> serial["Serial / UART
Arduino · sensor"] hw --> modbus["Modbus / OPC-UA
industrial"] hw --> mqtt["MQTT
IoT pub/sub"] classDef cat fill:#1e3a5f,stroke:#3776ab,color:#fff classDef impl fill:#3d2c1a,stroke:#ffd43b,color:#fff class api,web,lib,os,hw cat class rest,graphql,ws,rpc,pylib,file,proc,serial,modbus,mqtt impl
📊 เปรียบเทียบประเภท API
| ประเภท | ตัวอย่าง | ใช้เมื่อ | เรียกจาก Python ด้วย |
|---|---|---|---|
| REST API | Twitter, LINE, GitHub, Claude, IQAir | ปกติทั่วไป · public services | requests |
| GraphQL | GitHub v4, Shopify | ดึง data ที่อยากเฉพาะ field | requests + query string |
| WebSocket | chat, live dashboard, game | real-time 2 ทาง | websockets |
| gRPC | Google internal services | service-to-service ภายใน | grpcio |
| Library API | pandas, numpy | ทำงานใน Python ปกติ | import |
| OS API | file system, process | คุยกับเครื่อง | os, subprocess |
| Hardware | Modbus PLC, MQTT sensor, Serial Arduino | คุยกับอุปกรณ์ | pyserial, pymodbus, paho-mqtt |
🔌 REST API — พื้นฐานที่ต้องรู้
API = "ประตู" ระหว่างระบบ · REST = แบบที่ใช้บ่อยที่สุด · เป็น HTTP requests ปกติ
| HTTP Method | หมายความ | ตัวอย่าง |
|---|---|---|
GET | ดึงข้อมูล | GET /students — ขอรายชื่อ |
POST | เพิ่ม | POST /students — เพิ่มคน |
PUT/PATCH | แก้ | PATCH /students/5 — แก้คน id 5 |
DELETE | ลบ | DELETE /students/5 |
Status code ที่เจอบ่อย
200 OK | สำเร็จ |
201 Created | สร้างแล้ว |
400 Bad Request | ส่งข้อมูลผิด |
401 Unauthorized | ไม่ได้ login / API key ผิด |
404 Not Found | ไม่พบ |
429 Too Many Requests | เกิน rate limit |
500 Server Error | server พัง — ไม่ใช่ความผิดเรา |
📡 เรียก API ด้วย requests
ติดตั้ง: pip install requests
GET — ดึงข้อมูล
import requests
# ตัวอย่าง 1: Public API ไม่ต้อง auth
r = requests.get("https://api.coindesk.com/v1/bpi/currentprice.json")
print(r.status_code) # 200
print(r.json()) # เป็น dict
# ตัวอย่าง 2: GET with parameter
r = requests.get(
"https://api.airvisual.com/v2/city",
params={
"city": "Ubon Ratchathani",
"state": "Ubon Ratchathani",
"country": "Thailand",
"key": "YOUR_API_KEY",
}
)
data = r.json()
print(data["data"]["current"]["pollution"]["aqius"])
POST — ส่งข้อมูล (เช่นส่ง LINE)
import requests
LINE_TOKEN = "your_channel_access_token"
USER_ID = "Uxxxxxxxxxxxxxxxx"
r = requests.post(
"https://api.line.me/v2/bot/message/push",
headers={
"Authorization": f"Bearer {LINE_TOKEN}",
"Content-Type": "application/json",
},
json={
"to": USER_ID,
"messages": [{"type": "text", "text": "สวัสดีจาก Python 🎉"}],
}
)
print(r.status_code) # 200 ถ้าสำเร็จ
🔑 เก็บ API Key ปลอดภัย — .env
ติดตั้ง: pip install python-dotenv
โครงสร้างที่ถูก
my_project/
├── .env ← เก็บ secrets (อย่าลืม gitignore)
├── .env.example ← ตัวอย่างที่ commit ได้ (ไม่มีค่าจริง)
├── .gitignore ← มี .env
└── main.py
.env:
IQAIR_KEY=abc123_real_key_here
LINE_TOKEN=Bearer_real_token_here
.env.example:
IQAIR_KEY=your_iqair_key_here
LINE_TOKEN=your_line_token_here
main.py:
import os
from dotenv import load_dotenv
load_dotenv() # โหลด .env
key = os.getenv("IQAIR_KEY")
token = os.getenv("LINE_TOKEN")
# ใช้ตามปกติ
r = requests.get(url, params={"key": key, ...})
🔐 Auth Patterns — 4 แบบที่จะเจอ
"API key" เป็นแค่ 1 แบบ ของ authentication · ในงานจริงเจอหลายแบบ · AI ใช้แต่ละแบบไม่เหมือนกัน — ต้องดู doc ของ API ที่ใช้
1️⃣ API Key (ในย่อ params หรือ header)
ง่ายที่สุด · key เป็น string คงที่ · เหมาะกับ public API ทั่วไป
# แบบ A: ใส่ใน query params
r = requests.get(url, params={"api_key": KEY})
# แบบ B: ใส่ใน custom header
r = requests.get(url, headers={"X-API-Key": KEY})
ตัวอย่าง: IQAir, OpenWeather, NewsAPI, public APIs ส่วนใหญ่
2️⃣ Bearer Token (มาตรฐาน OAuth-style)
ใช้ Authorization: Bearer ... header · มาตรฐานของ REST API สมัยใหม่
r = requests.post(
"https://api.line.me/v2/bot/message/push",
headers={
"Authorization": f"Bearer {LINE_TOKEN}",
"Content-Type": "application/json",
},
json=payload,
)
ตัวอย่าง: LINE, Anthropic Claude, OpenAI, GitHub
3️⃣ OAuth 2.0 (login กับ provider · ขอ permission)
ซับซ้อนกว่า · ใช้เมื่อต้องการสิทธิ์ของ user (ไม่ใช่ของเรา) · เช่น "login ด้วย Google"
# Flow: user → ไป google.com/auth → approve → กลับมาที่ app เรา
# ได้ access_token + refresh_token
# ใช้ library: authlib, requests-oauthlib
from authlib.integrations.requests_client import OAuth2Session
oauth = OAuth2Session(CLIENT_ID, CLIENT_SECRET, scope="openid email")
auth_url, state = oauth.create_authorization_url("https://accounts.google.com/o/oauth2/auth")
# → redirect user → callback → exchange code for token
ตัวอย่าง: Google Sign-In, GitHub OAuth, "Connect with Facebook"
4️⃣ Service Account (server-to-server)
App ของเราติดต่อ Google ในนาม "ตัวเอง" — ไม่มี user · ใช้ JSON credentials file
import gspread
# credentials.json = key file ที่ download จาก GCP Console
gc = gspread.service_account(filename="credentials.json")
sheet = gc.open("MySheet").sheet1
ตัวอย่าง: Google Sheets API, GCS, BigQuery — เมื่อ app ทำงานเอง (cron job)
📊 เลือก auth แบบไหน
| สถานการณ์ | Auth ที่ใช้ |
|---|---|
| Public API ของบุคคล (weather, news) | API Key |
| API ของบริการที่คุณ register | Bearer Token |
| App ต้องใช้ข้อมูลของ user (email, photos) | OAuth 2.0 |
| Server background job (cron) | Service Account |
🤖 LLM APIs — ใส่ AI ใน App ของคุณ
หลัง W14 เราจะ deploy app · ก่อนหน้านั้น app บางอันต้อง เรียก LLM เพื่อ: สรุปข้อความ · แปลภาษา · ตอบคำถาม · จัดหมวด · สร้าง content · OCR
📊 เปรียบเทียบ Cloud LLM (ปี 2026)
| Provider | Model หลัก | Strength | ราคา input/output (per 1M tokens) |
|---|---|---|---|
| Anthropic Claude | claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5 | เก่งสุดที่ reasoning + code · ใส่ภาษาไทยได้ดี | $3-15 / $15-75 (opus) · $1 / $5 (sonnet) · $0.25 / $1.25 (haiku) |
| OpenAI GPT | gpt-4o, gpt-4o-mini, o-series | ecosystem ใหญ่ · function calling เก่ง | $2.50 / $10 (4o) · $0.15 / $0.60 (mini) |
| Google Gemini | gemini-2-flash, gemini-2-pro | context window ใหญ่ · เชื่อม Google services | $0.10 / $0.40 (flash) · $1.25 / $5 (pro) |
| Local (Ollama) | llama-3, qwen-2.5, phi-3 | ฟรี · ข้อมูลไม่ออกเครื่อง · ช้ากว่า · ต้อง GPU | $0 (ค่าไฟ + เครื่อง) |
- เรียน + ทำ demo: Claude Haiku หรือ GPT-4o-mini หรือ Gemini Flash — ถูก + เร็ว
- งานยากต้อง reasoning / code: Claude Opus หรือ Sonnet
- Final Project: เริ่มที่ Haiku/mini · upgrade เมื่อจำเป็น
- ข้อมูล sensitive: Local LLM (Ollama)
🐾 Pattern 1: Basic Completion (Claude SDK)
# pip install anthropic
import os
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv()
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=300,
messages=[
{"role": "user", "content": "สรุปย่อหน้านี้เป็น 1 ประโยค: ..."}
],
)
print(msg.content[0].text)
# Anthropic Python SDK: เอกสาร https://docs.claude.com
🐾 Pattern 2: System Prompt + User Prompt
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=500,
system="คุณคือ TA วิชา Computer Programming · ตอบเป็นภาษาไทย · กระชับ · ไม่ verbose",
messages=[
{"role": "user", "content": "อธิบาย concept 'self' ใน Python ในแบบนักศึกษาปี 1"}
],
)
🐾 Pattern 3: Multi-turn Conversation
history = []
def chat(user_msg):
history.append({"role": "user", "content": user_msg})
resp = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=500,
messages=history,
)
answer = resp.content[0].text
history.append({"role": "assistant", "content": answer})
return answer
print(chat("เครื่อง CNC คืออะไร?"))
print(chat("แล้ว 3-axis vs 5-axis ต่างกันยังไง?")) # AI จำ context
🐾 Pattern 4: Streaming (ตอบทีละคำ · ใช้กับ chat UI)
with client.messages.stream(
model="claude-haiku-4-5-20251001",
max_tokens=500,
messages=[{"role": "user", "content": "เล่าเรื่องสั้น ๆ"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print() # newline
🐾 Pattern 5: Structured Output (JSON ที่ parse ได้)
import json
prompt = """
สกัด field 3 อย่างจากประโยคนี้: 'อยากจอง CNC วันศุกร์ 10 โมง'
ตอบเป็น JSON มี keys: machine, day, time
ห้ามใส่ markdown · ห้ามมีข้อความอื่น
"""
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=200,
messages=[{"role": "user", "content": prompt}],
)
data = json.loads(msg.content[0].text)
print(data)
# → {"machine": "CNC", "day": "ศุกร์", "time": "10:00"}
🐾 Pattern 6: Function / Tool Calling
# ให้ LLM เรียก function ของเราเองได้ — สำหรับ agent
tools = [{
"name": "get_weather",
"description": "ดู PM2.5 ของเมือง",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}]
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=500,
tools=tools,
messages=[{"role": "user", "content": "PM2.5 อุบลฯ เท่าไหร่?"}],
)
# LLM จะตอบ tool_use block — เราต้อง execute function เอง · ส่งผลกลับ
# (ดู docs.claude.com/agents-and-tools/tool-use)
🐾 Pattern 7: Prompt Caching (ลด cost 90% เมื่อ context ซ้ำ)
# ใส่ cache_control ใน large system prompt (≥ 1024 tokens)
# ครั้งแรก: เสีย token + cost · ครั้งที่ 2-4 ภายใน 5 นาที: cache hit → ลด 90%
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=500,
system=[
{
"type": "text",
"text": LONG_TEXTBOOK_CONTENT, # ≥ 1024 tokens
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "ในหนังสือ section 3 พูดถึงอะไร?"}],
)
- Output token แพงกว่า input 3-5 เท่า — limit
max_tokensให้ตรง - Streaming = cost เหมือนกัน — แค่ UX ดีขึ้น
- Function calling = หลาย round trips — คูณ cost
- ตั้ง budget alert ใน dashboard · ครั้งหนึ่งเขียน loop ผิด = bill 1000 บาทใน 10 นาที
🔄 OpenAI / Gemini — เหมือนกันมาก
# OpenAI
# pip install openai
from openai import OpenAI
client = OpenAI(api_key=OPENAI_KEY)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
# Gemini
# pip install google-generativeai
import google.generativeai as genai
genai.configure(api_key=GOOGLE_KEY)
model = genai.GenerativeModel("gemini-2-flash")
resp = model.generate_content("Hello")
print(resp.text)
🏠 Local LLM (Ollama)
# 1. install Ollama จาก ollama.com
# 2. terminal: ollama pull llama3.2:3b
# 3. python:
import ollama
resp = ollama.chat(
model="llama3.2:3b",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp["message"]["content"])
# ไม่ต้องมี internet · ไม่ต้องมี key · ฟรี · ช้ากว่า cloud
🤖 AI Agents + RAG — ยุค 2026 ของ LLM
LLM ธรรมดา = "ถามตอบครั้งเดียว" · Agent = LLM ที่ ตัดสินใจเรียก tools ของเรา ทำงานหลาย step · RAG = LLM ที่ "ค้นข้อมูลของเราก่อนตอบ" · 2 pattern นี้คือ "ยุคใหม่" ของ LLM
🔄 Pattern A: Agent Loop — LLM เรียก Tool · วน
จาก Pattern 6 (tool calling) ขั้นต่อไป — "loop" · LLM ขอเรียก tool · เรารัน · ผลกลับให้ LLM · LLM อาจขอเรียก tool อีก · จนได้คำตอบ
import os, json
from anthropic import Anthropic
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
# ─── 1. Tools ที่ AI ใช้ได้ ───
def get_weather(city: str) -> dict:
"""Mock — ของจริงเรียก IQAir/OpenWeather"""
return {"city": city, "pm25": 75, "temp": 32}
def book_lab(machine: str, time: str) -> dict:
"""Mock — ของจริงเขียน DB"""
return {"status": "ok", "booking_id": "B001", "machine": machine, "time": time}
TOOLS_REGISTRY = {"get_weather": get_weather, "book_lab": book_lab}
# ─── 2. Tool schema ที่บอก AI ───
TOOLS = [
{
"name": "get_weather",
"description": "ดูข้อมูลอากาศของเมือง",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
},
{
"name": "book_lab",
"description": "จองเครื่องใน lab",
"input_schema": {
"type": "object",
"properties": {"machine": {"type": "string"}, "time": {"type": "string"}},
"required": ["machine", "time"],
},
},
]
# ─── 3. Agent loop ───
def run_agent(user_query: str, max_iterations: int = 5) -> str:
messages = [{"role": "user", "content": user_query}]
for i in range(max_iterations):
resp = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=1024,
tools=TOOLS,
messages=messages,
)
# ถ้า AI หยุดเอง (ตอบเสร็จ) → break
if resp.stop_reason == "end_turn":
return resp.content[0].text
# ถ้า AI ขอเรียก tool → รัน · ส่งผลกลับ
if resp.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": resp.content})
tool_results = []
for block in resp.content:
if block.type == "tool_use":
tool = TOOLS_REGISTRY[block.name]
result = tool(**block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result),
})
messages.append({"role": "user", "content": tool_results})
return "❌ เกิน max iterations"
# ─── 4. ใช้ ───
answer = run_agent("PM2.5 ที่อุบลฯ ตอนนี้ · ถ้าอากาศดีให้จอง CNC-1 บ่าย 2")
print(answer)
# Agent จะ:
# 1. เรียก get_weather("Ubon")
# 2. ดู PM2.5 → ตัดสินใจว่า "ดี" หรือไม่
# 3. ถ้าดี → เรียก book_lab("CNC-1", "14:00")
# 4. สรุปให้ user เป็นภาษาคน
- Infinite loop — ใส่
max_iterationsเสมอ · ป้องกัน bill ช็อก - Cost ขึ้นเร็ว — ทุก iteration = 1 LLM call · 5 iterations × Sonnet = $$$
- Tool security — AI เลือกเรียก tool อะไรก็ได้ · ห้ามให้ tool ลบ data/ส่งเงิน โดยไม่มี human approval
📚 Pattern B: RAG — Retrieval-Augmented Generation
LLM ไม่รู้เรื่อง ในบ้านของคุณ (เอกสารภาควิชา · spec product · chat history) · RAG = "ค้นข้อมูลของเราก่อน · แล้วให้ LLM ตอบโดยใช้ข้อมูลนั้น"
(PDF, MD, web)")] --> embed1[Embed
(text → vector)] embed1 --> vdb[(🌸 Vector DB
Chroma/Qdrant)] user(["👤 user question"]) --> embed2[Embed] embed2 --> search{Search
nearest vectors} vdb --> search search --> chunks[Top-K chunks] chunks --> llm[LLM
+ context] user --> llm llm --> answer([Answer
with citation]) classDef store fill:#3b2962,stroke:#8b5cf6,color:#fff classDef embed fill:#1e3a5f,stroke:#3776ab,color:#fff classDef ai fill:#5c2618,stroke:#ff7a18,color:#fff class docs,vdb store class embed1,embed2,search,chunks embed class llm,answer ai
🐾 RAG ใน Python (ขั้นต่ำ)
# pip install chromadb anthropic sentence-transformers
import chromadb
from anthropic import Anthropic
client = Anthropic()
chroma = chromadb.Client()
collection = chroma.create_collection("lab_docs")
# ─── 1. Index (ทำครั้งเดียว) ───
docs = [
"เครื่อง CNC-1 มี travel 800x500x400 mm · spindle 12000 rpm",
"เครื่อง CNC-2 มี travel 600x400x300 mm · spindle 8000 rpm",
"PLC FX5U รุ่นใหม่ใช้ GX Works3 · ไม่ใช่ Works2",
"Lab เปิด 9:00-17:00 จันทร์-ศุกร์ · ห้ามใช้ตอนกลางคืน",
]
collection.add(
documents=docs,
ids=[f"doc-{i}" for i in range(len(docs))],
) # Chroma จะ embed ให้อัตโนมัติ
# ─── 2. Query (ทุกครั้งที่ user ถาม) ───
def rag_answer(question: str) -> str:
# 2a. หา chunks ที่เกี่ยวข้องที่สุด
results = collection.query(query_texts=[question], n_results=2)
context = "\n".join(results["documents"][0])
# 2b. ใส่ context ใน prompt
prompt = f"""ข้อมูลจาก lab:
{context}
คำถาม: {question}
ตอบเฉพาะจากข้อมูลข้างบน · ถ้าไม่มีคำตอบให้บอกว่า "ไม่พบในเอกสาร" """
resp = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=500,
messages=[{"role": "user", "content": prompt}],
)
return resp.content[0].text
# ─── 3. ใช้ ───
print(rag_answer("CNC-1 spindle หมุนเร็วแค่ไหน?"))
# → "12,000 rpm" — มาจาก doc 0
print(rag_answer("Lab เปิดเสาร์อาทิตย์มั้ย?"))
# → "ไม่เปิด · จันทร์-ศุกร์เท่านั้น" — มาจาก doc 3
- Cost — ส่งเฉพาะ chunks ที่เกี่ยว · ไม่ใช่หนังสือทั้งเล่ม
- Accuracy — context ตรงประเด็น · hallucination ลดลง
- Update — เพิ่ม doc ใหม่ → reindex · ไม่ต้อง retrain LLM
- Citation — บอกได้ว่าคำตอบมาจาก doc ไหน
🌸 Vector DB ตัวเลือก
| Tool | ดี | เหมาะ |
|---|---|---|
| Chroma | ฟรี · ใน Python · เริ่มง่าย | prototype · < 100K docs |
| Qdrant | ฟรี · Rust · เร็ว · self-host | production · open-source |
| Pinecone | managed · scale ใหญ่ | production · มี budget |
| pgvector | extension ของ Postgres | ใช้ Postgres อยู่แล้ว |
| Weaviate | built-in modules | เน้น semantic search |
🚀 Agent + RAG = AI App สมัยใหม่
App แบบ Cursor, ChatGPT (ที่ search web ได้), Notion AI = Agent + RAG รวมกัน
- RAG ดึงข้อมูลในระบบของคุณ
- Agent เรียก tools ทำงาน · ค้น web · เขียน file · ส่ง email
- รวมกัน = "copilot ที่รู้เรื่องของคุณและทำงานให้คุณได้"
☁️ Google Cloud APIs — Catalog สำหรับ Engineer
GCP มี API หลายร้อยตัว — ที่นี่คือตัว ที่นักศึกษาวิศวะใช้บ่อยที่สุด · ทุกตัวมี free tier · ใช้ Service Account auth · เปิดใน console.cloud.google.com
| API | ทำอะไร | Engineer ใช้ทำอะไร |
|---|---|---|
| Vision API | OCR · object detection · face detection | อ่าน label / nameplate จากรูปอุปกรณ์ · QA visual inspection · อ่านลายมือ lab note |
| Translation API | แปล 100+ ภาษา | แปล manual จาก JP/CN เป็นไทย · แปล error message · แปล spec ของ vendor |
| Speech-to-Text | เสียง → ข้อความ (Thai support ดี) | ถอด lecture · transcript meeting · voice command บนเครื่องมือ |
| Text-to-Speech | ข้อความ → เสียง | warning audio บนเครื่อง · accessibility |
| Maps Platform | map · directions · places · geocoding | routing สำหรับ delivery · location-based booking · indoor maps |
| Sheets API | read/write Google Sheets | ใช้ Sheets เป็น database ฟรี (W08) |
| Drive API | upload/download/share files | เก็บ lab report · เก็บ CSV จาก sensor |
| Gmail API | ส่ง email | auto-notify · weekly report |
| Calendar API | read/write events | auto-book lab · check availability |
| Cloud Run | host Python container | deploy Flask API (W14) |
| Cloud Storage (GCS) | เก็บไฟล์ขนาดใหญ่ | image archive · sensor data backup |
| BigQuery | SQL บน data ใหญ่ (TB+) | analyze sensor data หลายปี · cross-machine analytics |
| Pub/Sub | message queue | IoT events · async job |
| Vertex AI | ML platform · host Gemini | ใช้ Gemini API · host custom ML model |
ตัวอย่าง: ใช้ Vision API อ่าน serial number จากรูปเครื่อง
# pip install google-cloud-vision
from google.cloud import vision
client = vision.ImageAnnotatorClient() # ใช้ Service Account จาก GOOGLE_APPLICATION_CREDENTIALS
with open("machine_photo.jpg", "rb") as f:
image = vision.Image(content=f.read())
# OCR
response = client.text_detection(image=image)
for text in response.text_annotations[:5]:
print(text.description)
# → "Mitsubishi FX5U" "Serial: 12345678" ...
📊 Integration จริง — 3 ตัวอย่าง
1) Google Sheets เป็น "Database" ฟรี
ใช้ gspread + Google Service Account · เซฟ/อ่านลง Sheet ได้เหมือน database
import gspread
gc = gspread.service_account(filename="credentials.json")
sh = gc.open("StudentBookings").sheet1
# อ่าน
rows = sh.get_all_records() # list of dicts
# เพิ่ม row
sh.append_row(["2026-05-15", "Ploy", "CNC-1", "10:00"])
2) LINE Messaging API
ส่งแจ้งเตือนเข้า LINE ได้ฟรี · เหมาะกับโปรเจกต์นักศึกษาที่อยาก "demo"
def line_push(text):
requests.post(
"https://api.line.me/v2/bot/message/push",
headers={"Authorization": f"Bearer {LINE_TOKEN}"},
json={"to": USER_ID, "messages": [{"type": "text", "text": text}]}
)
# ตัวอย่างใช้งาน
if pm25 > 100:
line_push(f"⚠️ PM2.5 = {pm25} — สูงผิดปกติ")
3) Weather / Air Quality (IQAir, OpenWeatherMap)
def get_air_quality(city, state, country):
r = requests.get(
"https://api.airvisual.com/v2/city",
params={"city": city, "state": state, "country": country, "key": IQAIR_KEY},
timeout=10,
)
r.raise_for_status() # error ถ้า status >= 400
return r.json()["data"]["current"]["pollution"]["aqius"]
🔒 Web Security — ก่อน Deploy ขึ้น Internet
เมื่อ app ของเรา "เปิดให้คนอื่นเรียก" = ตกเป้าของ attacker ทั่วโลก · bot scraper · script kiddie · script ทดลอง · พวกนี้ทดสอบ "จุดอ่อนพื้นฐาน" ก่อนเสมอ · ทุก app ต้องป้องกัน 4 อย่างขั้นต่ำ
🛡 1. SQL Injection — ทำไมเราใช้ ? placeholder
ถ้าคุณ concat user input ใส่ SQL ตรง ๆ → attacker ใส่ SQL แทนข้อมูล → ลบ table หมด
? placeholder เสมอ
cur.execute(sql, params) ✅ · ไม่ใช่ cur.execute(f"... {x} ...") ❌ ·
ORM (SQLAlchemy/Django) ก็ทำให้อัตโนมัติ
🛡 2. XSS (Cross-Site Scripting) — Escape User Output
ถ้าคุณแสดง user input ใน HTML ตรง ๆ → attacker ใส่ <script> → รัน JS ในเครื่อง user คนอื่น
# ❌ Unsafe — echo input ดิบ
@app.route("/search")
def search():
q = request.args.get("q")
return f"<h1>ผลค้น: {q}</h1>"
# Attacker เปิด:
# /search?q=<script>steal_cookie()</script>
# → JS รันในทุก browser ที่เปิด link นี้!
# ✅ Safe — ใช้ template engine
from flask import render_template_string
@app.route("/search")
def search():
q = request.args.get("q")
# Jinja2 auto-escape < > & ' "
return render_template_string("<h1>ผลค้น: {{ q }}</h1>", q=q)
# ✅ หรือ manual escape
from markupsafe import escape
return f"<h1>ผลค้น: {escape(q)}</h1>"
🛡 3. Password Hashing — ห้ามเก็บ Plaintext
ถ้า DB หลุด · ผู้โจมตีเห็น password ของทุก user → ใช้กับ Facebook/email/bank ของ user ทันที · ใช้ bcrypt เสมอ
bcrypt (slow by design) ·
Argon2 ยิ่งดี · libraries: bcrypt, passlib, argon2-cffi
🛡 4. CSRF (Cross-Site Request Forgery)
Attacker หลอก user login อยู่ → click link ที่ส่ง form ไปยัง app เรา → ทำ action ในนาม user (โอนเงิน, ลบ data)
# pip install flask-wtf
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY") # random ใน .env
csrf = CSRFProtect(app)
# ตอนนี้ทุก POST/PUT/DELETE form ต้องมี CSRF token
# <form method="post">
# {{ csrf_token() }} ← inject token
# ...
# </form>
# → Flask-WTF ตรวจ token ทุกครั้ง · ปลอม token ไม่ได้
🛡 5. Bonus — Headers ที่ต้องเปิด
from flask_talisman import Talisman
app = Flask(__name__)
Talisman(app) # auto-set security headers
# Headers ที่ Talisman ตั้งให้:
# - Strict-Transport-Security → บังคับ HTTPS
# - X-Content-Type-Options → no MIME sniffing
# - X-Frame-Options → ป้องกัน clickjacking
# - Content-Security-Policy → จำกัด script source
📋 Security Checklist ก่อน Deploy
- ☐ ใช้
?placeholder ใน SQL ทุกที่ · ไม่มี f-string ใน SQL - ☐ Template engine auto-escape ใน HTML output
- ☐ Password ทุกตัว hash ด้วย bcrypt
- ☐
.envใน.gitignore· API key/secret ไม่ commit - ☐ HTTPS เปิด (Cloud Run/Render auto · Streamlit auto)
- ☐ CSRF protection ใน Flask form (Flask-WTF)
- ☐ Rate limit ทุก endpoint public (
flask-limiter) - ☐ Validate input ที่ boundary ทุกครั้ง (Pydantic / WTForms)
- ☐ Dependency audit —
pip-auditก่อน deploy - ☐ Sentry/log ทุก error · debug=False ใน production
🌐 สร้าง API ของตัวเอง — Flask
ติดตั้ง: pip install flask
# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
# in-memory store (เปลี่ยนเป็น SQLite/Postgres ใน production)
students = []
next_id = 1
@app.route("/students", methods=["GET"])
def list_students():
return jsonify(students)
@app.route("/students", methods=["POST"])
def add_student():
global next_id
data = request.json
if "name" not in data:
return jsonify({"error": "missing name"}), 400
new_student = {
"id": next_id,
"name": data["name"],
"year": data.get("year", 1),
}
students.append(new_student)
next_id += 1
return jsonify(new_student), 201
@app.route("/students/<int:sid>", methods=["DELETE"])
def delete_student(sid):
global students
before = len(students)
students = [s for s in students if s["id"] != sid]
if len(students) == before:
return jsonify({"error": "not found"}), 404
return jsonify({"deleted": sid})
if __name__ == "__main__":
app.run(debug=True, port=5000)
รัน: python app.py · เปิด browser ไป http://localhost:5000/students หรือใช้ curl:
curl -X POST http://localhost:5000/students \
-H "Content-Type: application/json" \
-d '{"name": "Ploy", "year": 1}'
curl http://localhost:5000/students
🐘 PostgreSQL — เมื่อ SQLite ไม่พอ
เมื่อไหร่ต้องเปลี่ยนจาก SQLite
- มี user หลายคนเขียนพร้อมกัน — SQLite ล็อกทั้ง file ตอนเขียน
- มี deployment ที่ scale — Cloud Run multiple instances ใช้ SQLite ไม่ได้
- ข้อมูลเกิน 1-10 GB
- ต้องการ network access (app บนเครื่องหนึ่ง · db บนอีกเครื่อง)
ติดตั้ง PostgreSQL ใน Docker (ง่ายที่สุด)
docker run --name pg -e POSTGRES_PASSWORD=mysecret -p 5432:5432 -d postgres:16
เชื่อมจาก Python — psycopg2 หรือ SQLAlchemy
import psycopg2
conn = psycopg2.connect(
host="localhost",
port=5432,
database="mydb",
user="postgres",
password=os.getenv("PG_PASSWORD"),
)
cur = conn.cursor()
cur.execute("SELECT * FROM students WHERE year = %s", (1,))
for row in cur.fetchall():
print(row)
conn.close()
หรือใช้ SQLAlchemy (ORM) — เขียน Python class แทน SQL · จะลึกใน W14
SQLite → PostgreSQL ใช้ pandas ก็ได้
import pandas as pd
from sqlalchemy import create_engine
# อ่านจาก SQLite
src = create_engine("sqlite:///students.db")
df = pd.read_sql("SELECT * FROM students", src)
# เขียนไป Postgres
dst = create_engine("postgresql://postgres:mysecret@localhost:5432/mydb")
df.to_sql("students", dst, if_exists="replace", index=False)
🔁 Webhook vs Polling
2 แบบของการ "ฟังเหตุการณ์จากภายนอก":
| Polling | Webhook | |
|---|---|---|
| วิธี | เคาะถามทุก X วินาที | external ส่งมาเมื่อมี event |
| เร็ว | ช้า (delay = interval) | real-time |
| เปลือง | เปลือง resource (เคาะแม้ไม่มีอะไร) | ประหยัด |
| ทำง่าย | ง่าย (loop) | ต้องมี public URL |
| ตัวอย่าง | ดึง weather ทุก 5 นาที | LINE Webhook (user ส่งมา → server ได้ทันที) |
🧪 Workshop — สร้าง "Smart Lab Monitor + AI Summarizer"
Tool ที่ใช้ 3 ประเภท API + LLM:
- 📡 GET external API: ดึงค่า PM2.5 + อุณหภูมิจาก IQAir ทุก 30 นาที
- 💾 เซฟลง SQLite
- 📨 POST external API: ถ้า PM2.5 > 100 → ส่ง LINE แจ้งเตือน
- 🤖 LLM API: ทุกคืน 22:00 — ขอ Claude สรุปข้อมูล 24 ชม. + แนะนำ
- 🌐 Make your own API: Flask endpoint
/status+/summary
cron loop] poller --> db[(SQLite
history)] poller -->|ถ้า PM > 100| line[/LINE API/] db -->|ดึงล่าสุด| flask[Flask app
/status, /summary] db -->|รวม 24 ชม.| claude[/Claude API
Anthropic/] claude -->|สรุป + แนะนำ| flask user(["👤 user"]) -->|browser| flask classDef external fill:#3b2962,stroke:#8b5cf6,color:#fff classDef internal fill:#1e3a5f,stroke:#3776ab,color:#fff classDef db fill:#3d2c1a,stroke:#ffd43b,color:#fff class iqair,line,claude external class poller,flask internal class db db
-
สมัคร 3 บริการ + เก็บ key ใน
.env— IQAir · LINE Messaging · Anthropic Claude · gitignore .env เสมอ -
เขียน
fetch_aqi(city)— GET IQAir · timeout=10 · raise_for_status · คืน dict -
SQLite schema +
save_reading(data)— table reading(ts, aqi, temp, humidity) -
line_alert(text)— POST LINE Messaging API -
เขียน
poller.py— loop: fetch → save → if > 100 alert · sleep 1800 -
🤖 เขียน
summarize_today()— query 24 ชม. ล่าสุด → ส่งให้ Claude พร้อม prompt: "สรุป PM2.5 24 ชม. + แนะนำ ภาษาไทย กระชับ" · cache 1 ชั่วโมง -
🌐 Flask app
—
/status= JSON ล่าสุด ·/summary= AI summary ของวันนี้ -
requirements.txt+.env.example+ README — sequence diagram (W03) อธิบาย flow -
commit + push GitHub
— ห้าม commit
.env
claude-haiku-4-5-20251001 ($0.25/M input) · เรียก /summary แค่ 1-2 ครั้ง/วัน
· cache ผลลัพธ์ใน SQLite · cost < $0.01/เดือน
💡 ข้อควรระวังกับ API
timeout= = แฮงค์ตลอดไป · ใส่ timeout=10 ทุกครั้ง
git-secrets ป้องกัน
ส่งงานสัปดาห์นี้
- 📁 GitHub repo "Smart Lab Monitor" ครบทั้ง poller + Flask + Claude
- 📷 screenshot LINE แจ้งเตือน + Flask
/statusJSON +/summaryภาษาไทย - 📊 Sequence diagram ใน README (ดูตัวอย่างด้านบน)
- 📝
.env.example+requirements.txt+ README วิธีติดตั้ง - 💰 Screenshot Anthropic dashboard แสดง cost < $1 (พิสูจน์ใช้ Haiku + cache ถูก)
Reference จาก slide เดิม
เนื้อหานี้ ไม่อยู่ใน slide เดิม — เป็น Mainidea Foundation 17 (Industrial Communication) + 13 (System Composition) ที่ขยายเข้าสู่งานจริง