HYBRID MIND · Agent Engineering · บทเรียน Reliability
เครื่องมืออ่านข้อมูลล้มเหลวครั้งหนึ่ง Agent บอกว่า “จะลองใหม่” อีกสองวินาทีต่อมาก็ลองอีกครั้ง แต่ผู้ใช้รอจนหมดเวลาแล้ว ปัญหาอาจไม่ใช่จำนวน retry ที่น้อยเกินไป ปัญหาคือระบบไม่เคยถามว่ารอบต่อไปยังมีเวลาพอให้เกิดผลที่ตรวจรับได้หรือไม่ บทเรียนนี้ออกแบบคำตอบให้คำถามนั้นด้วยงบเวลาเดียวตลอดงาน

คำตอบทันที: ลองซ้ำได้ ต้องผ่านทั้งสิทธิ์ เหตุผล และเวลา
Retry budget ในตอนนี้หมายถึงเกณฑ์ให้เริ่มการเรียกเครื่องมือซ้ำโดยดูเวลาที่เหลือ จำนวนครั้งสูงสุด และประเภทความผิดพลาดร่วมกัน ไม่ใช่ตั้ง timeout สี่วินาทีแล้วให้ทุกครั้งเริ่มนับใหม่ เมื่อเวลาที่เหลือไม่พอสำหรับการพักก่อนลองใหม่ รอบที่ยังพอมีประโยชน์ และพื้นที่เผื่อตรวจผล ระบบควรหยุดพร้อมเหตุผลที่อ่านได้
ตัวอย่างจะอ่าน metadata ของ repository สาธารณะบน GitHub ด้วย Python Standard Library ใช้ clock แบบ monotonic นับเวลาที่ผ่าน ตรวจงบก่อนและหลัง I/O และปฏิเสธผลที่กลับมาหลัง deadline ซึ่งเป็นเวลาสิ้นสุดที่กำหนดไว้ แต่ไม่ได้ hard-cancel งานที่กำลังค้าง และไม่รับรองว่าตัวโปรแกรมจะคืนการควบคุมภายในสิบสองวินาทีจริงทุกกรณี
ผลลัพธ์การเรียนรู้: หลังอ่าน คุณควรคำนวณได้ว่าอนุญาตให้เริ่ม retry รอบถัดไปหรือควรหยุด พร้อมระบุเหตุผลที่ไม่ลองซ้ำแม้ยังเหลือเวลา เช่น คำสั่งรอจาก server สิทธิ์ไม่พอ หรือผลลัพธ์ไม่ตรง repository ที่ขอ ทุกตัวเลขเวลาในตัวอย่างและ Lab เป็นสมมติฐานสาธิต โค้ดและการทดสอบที่แนบมีสถานะ NOT RUN
จาก LLM chat สู่ Agent: ใครเป็นผู้ตัดสินใจใช้เวลาอีกหนึ่งรอบ
ในแชตทั่วไป คุณอาจพิมพ์ว่า “ลองอีกครั้ง” หลังเห็นคำตอบไม่ครบ แต่ Agent ที่มีเครื่องมือสามารถสร้างคำขอใหม่โดยไม่มีคนพิมพ์ซ้ำ การตัดสินใจเล็กนี้ใช้ทั้งเวลาผู้ใช้และโควตาของบริการที่เรียก เครื่องมืออ่านอย่างเดียวไม่เปลี่ยน repository แต่ยังสร้าง request และภาระให้ระบบปลายทางได้
นาฬิกา Timeout และ Deadline เป็นคนละหน้าที่
เอกสาร Python อธิบายว่า time.monotonic() ไม่ถอยหลังและไม่ตามการปรับนาฬิกาของระบบ ใช้ผลต่างระหว่างค่าที่อ่านเพื่อวัดเวลาที่ผ่าน ส่วน sleep() อาจพักนานกว่าค่าที่ขอ จึงต้องตรวจเวลาอีกครั้งหลังพัก อ่านต้นทาง Python time
ค่า timeout ของ urllib เป็นวินาทีสำหรับ blocking operations เช่น การเชื่อมต่อ เอกสารไม่ได้ให้คำรับรองว่าทั้ง request รวมทุกขั้นจะเสร็จภายในค่านั้น อ่านความหมาย urllib timeout
ในระบบที่ออกแบบนี้ I/O timeout คือพารามิเตอร์ที่ส่งให้ชั้นเครือข่าย ส่วน cooperative deadline คือเวลาสิ้นสุดที่โค้ดตรวจเมื่อกลับมาควบคุมงานได้ ใช้คำว่า cooperative เพราะแต่ละขั้นต้องยอมตรวจเวลาแล้วหยุดด้วยตัวเอง ไม่มีตัวสั่งยุติทุกกิจกรรมทันทีที่นาฬิกาถึงเส้นกำหนด
กฎเดียวที่ต้องคำนวณ: เวลาที่เหลือพอเริ่มรอบใหม่หรือไม่
ให้ R เป็นเวลาที่เหลือจริงจาก deadline ลบ clock ปัจจุบัน D เป็นเวลาพักที่วางแผนก่อนรอบนั้น S เป็นค่าที่เผื่อไว้สำหรับตรวจและคืนผล และ M เป็นขั้นต่ำที่ยอมให้รอบใหม่ลองทำงาน กฎของตัวอย่างคือเริ่มได้เมื่อ R > D + S + M เท่านั้น
Admission คือเกณฑ์อนุญาตให้เริ่ม ไม่ใช่สูตรทำนายเวลาตอบ ตัวอย่างเลือกหยุดเมื่อเท่ากับเส้นแบ่งเพราะไม่มีส่วนเผื่อเพิ่ม กฎนี้เป็นการออกแบบบทเรียน ไม่ใช่มาตรฐานของทุก Agent
| ค่า | กำหนดในตัวอย่าง | หน้าที่ |
|---|---|---|
| Task budget | 12 วินาที | งบเดียวเริ่มจากต้นงาน |
| I/O cap | 4 วินาที | เพดานพารามิเตอร์ timeout ที่ส่งให้ opener |
| Maximum attempts | 3 ครั้งรวมครั้งแรก | หยุดจำนวน request ไม่ใช่สาม retry เพิ่ม |
| Backoff | 0.5 แล้ว 1.0 วินาที | พักก่อนครั้งที่สองและสาม |
| Reserve | 1 วินาที | ส่วนเผื่อที่ใช้ในการอนุญาตรอบใหม่ |
| Minimum attempt | 1 วินาที | ขั้นต่ำที่ถือว่ารอบยังพอมีประโยชน์ |
เมื่อกฎผ่านแล้ว ตั้งพารามิเตอร์ I/O เป็น min(cap, remaining − reserve) ก่อนเปิด request และตรวจเวลาอีกครั้งหลังพัก ค่า reserve นี้ไม่ได้จอง CPU หรือบังคับว่าการตรวจผลจะกินหนึ่งวินาที จึงไม่ควรนำไปเขียนข้อรับประกันบริการ หากงานต้องมีขอบเขตเวลาที่บังคับได้จริง ต้องเลือก transport และวิธียุติงานที่รองรับแล้วทดสอบตามระบบนั้น

ตัวอย่างทำงาน: 502 ที่ยังพอลอง กับ 429 ที่ต้องหยุด
สมมติเริ่มด้วยงบสิบสองวินาที ครั้งแรกเป็น GET ที่อยู่ใน allowlist และได้รับ HTTP 502 หลังผ่านไป 8.5 วินาที ไม่มีคำสั่งรอจาก server และไม่มีหลักฐานว่าโควตาหมด ตอนนี้ R = 12 − 8.5 = 3.5 วินาที รอบที่สองใช้ D = 0.5 และ S + M = 2 จึงต้องมีมากกว่า 2.5 วินาที เหลือ 3.5 จึงผ่านเกณฑ์เวลา
พักครึ่งวินาทีแล้วตรวจ clock ใหม่ ถ้าเวลาที่ผ่านจริงตรงกับสมมติฐานจะเหลือสามวินาที พารามิเตอร์ I/O รอบนี้เป็น min(4, 3 − 1) = 2 วินาที อย่างไรก็ตามค่านี้ไม่ทำให้เรารับประกันว่า request ครบทุกขั้นจะใช้สองวินาที นี่คือสิ่งที่โค้ดตั้งให้ transport ไม่ใช่ผลวัด
สมมติรอบที่สองตอบ HTTP 429 หลังอีก 0.4 วินาที แม้เหลือ 2.6 วินาที ระบบไม่ควรอ้างว่า “ยังพอลองครั้งที่สาม” เพราะรอบใหม่ต้องผ่านประเภทความผิดพลาดด้วย ตัวอย่างหยุดเป็น RATE_OR_PERMISSION_REVIEW พร้อม attempts เท่ากับสอง ไม่แปลง error เป็นคำตอบว่าได้ default branch แล้ว
GitHub ระบุให้หยุดคำขอชั่วคราวตาม Retry-After หรือเวลาคืนโควตาเมื่อ x-ratelimit-remaining เป็นศูนย์ การฝืนเรียกระหว่างติด rate limit อาจทำให้ integration ถูกแบน ตัวอย่างนี้เลือกหยุดส่งให้ผู้ดูแลตรวจแทนการรอภายในงานสั้น อ่าน GitHub rate-limit guidance

สัญญาของเครื่องมือ: คำตอบที่ตรวจรับได้ต้องมีหน้าตาแน่นอน
Functional contract หรือสัญญาหน้าที่ของเครื่องมือในตอนนี้ระบุว่า input เป็น repository หนึ่งชื่อในรายการที่อนุญาต output สำเร็จต้องมีชื่อ repository ตรงที่ขอ ชื่อ default branch ที่เป็นข้อความไม่ว่าง URL ต้นทาง และเวลาที่อ่านข้อมูล ถ้าไม่ครบต้องคืนสถานะหยุดพร้อมเหตุผล ห้ามสร้างค่าทดแทนจากการคาดเดาหรือความจำของโมเดล
Reliability contract จำกัดหนึ่งงบเวลา สาม attempts รวมครั้งแรก และประเภทที่พอลองซ้ำได้ Security contract จำกัด hostname กับ path จาก allowlist และ GET เท่านั้น Observability contract หรือสัญญาการเห็นเหตุการณ์ให้มี execution ID เดียวกับหมายเลข attempt และเหตุผลหยุด Evaluation contract ใช้ fixture ซึ่งเป็นข้อมูลจัดเตรียมสำหรับทดสอบ ตรวจจำนวน request และผลที่ควรเกิด
Brief หกส่วนที่คัดลอกได้
เป้าหมาย: อ่านชื่อ default branch ของ repository สาธารณะที่อนุญาตหนึ่งแห่ง และตัดสินใจ retry ตามงบเวลาคงเหลือ ข้อมูลและเครื่องมือ: repository = psf/requests; fixed HTTPS GET GitHub repository metadata; ไม่ใช้โมเดลเรียกจริง ขอบเขตสิทธิ์: อ่านอย่างเดียว ไม่มี token ไม่มี private repository ไม่มี write ไม่มี redirect ไปปลายทางใหม่โดยอัตโนมัติ สิ่งส่งมอบ: structured status/reason/execution_id/attempts; ถ้าสำเร็จเพิ่ม repository/default_branch/source_url/retrieved_at เกณฑ์ตรวจรับ: validate identity/schema; ไม่เกิน3attempts; ตรวจเวลาใหม่ก่อนและหลัง I/O; ไม่รายงานข้อมูลที่ล่าช้าเป็นok จุดหยุดหรือส่งให้คน: budgetไม่พอ; deadlineผ่านแล้ว; 403/429; Retry-After; quotaเหลือ0; JSON/schemaผิด; destinationเปลี่ยน; write stateไม่แน่ชัด สมมติฐานเวลา: budget12s, I/O cap4s, reserve1s, minimum attempt1s, delay0.5/1.0s — ไม่ใช่ผลวัดและไม่รับรองhard cancellation
เครื่องมือจริงในตัวอย่าง: API สาธารณะ แต่ยังไม่ได้รัน Live
เอกสาร GitHub อนุญาตให้ใช้ Get a repository กับทรัพยากรสาธารณะโดยไม่ใช้ credential ตัวอย่าง request ปัจจุบันแสดง API version 2026-03-10 บทเรียนใช้ endpoint นี้เป็นเป้าหมายของ GET ที่ผู้อ่านเลือกทดสอบภายใต้สิทธิ์ของตน อ่าน repository endpoint
แยกสามสถานะให้ถูก: Mock คือ FakeOpener ที่จัดฉาก response ใน unittest; Sandbox คือสภาพทดสอบควบคุมเองที่ยังไม่มีในงานนี้; Live คือ request ไป GitHub จริงที่ผู้อ่าน opt-in ด้วย flag โค้ดส่งมอบครบพอให้พิจารณาวิธีลอง แต่ผู้เขียนไม่ได้รันทั้งสามอย่าง จึงไม่มีผลที่พิสูจน์ว่า integration ทำงานผ่านแล้ว
Allowlist มี psf/requests และ python/cpython โปรแกรมประกอบ URL จากคงที่ ไม่รับ URL หรือ HTTP method อิสระจากคำสั่ง Agent และไม่มี access token
Repository อาจย้ายชื่อหรือส่ง redirect ตัวอย่างเลือกไม่ตามเพื่อให้ตรวจขอบเขตปลายทางใหม่ก่อน นี่เป็นนโยบายจำกัดสิทธิ์ในบทเรียน ไม่ใช่ข้ออ้างว่าการ redirect ของ GitHub เป็น error เสมอ ถ้าใช้งานจริงต้องตัดสินว่าจะตรวจและอนุญาต redirect แบบใด รวมถึงตรวจ allowlist หลังปลายทางเปลี่ยน
อ่านโค้ด Python: จุดตัดสินใจก่อนเริ่ม I/O อยู่ตรงไหน
ไฟล์ retry_budget.py ใช้ Python 3.12 ขึ้นไปและ Standard Library ไม่มี dependency เพิ่ม โครงสร้างเริ่มจาก Policy ที่ตรวจตัวเลขเป็น finite positive หมายถึงไม่รับค่าติดลบ ค่าไม่ใช่ตัวเลข หรือค่าที่ไม่สิ้นสุด จากนั้นตรวจ allowlist ก่อนสร้าง request หากไม่มีงบที่ใช้ได้ต้องหยุด ไม่ใช้ค่า default แบบเดาเมื่อ policy ไม่ผ่าน
attempt_fits() เป็นฟังก์ชันตัดสินใจเล็กที่รับ remaining กับ delay แล้วใช้กฎเดียวกับบทความ เหมาะกับการทำ synthetic test เพราะใส่เวลาเองได้ ส่วน fetch_repository() เป็นผู้ควบคุม attempt ที่ตั้ง deadline ครั้งเดียว ไม่รีเซ็ตหลัง request ล้มเหลว รอบถัดไปจึงรับภาระเวลาที่ใช้ไปแล้วทั้งหมด
ก่อนเปิด request โค้ดตรวจเวลาหลังการพักอีกครั้ง ส่ง timeout ที่ไม่เกิน cap แล้วอ่าน body ภายใต้เพดานขนาด ตรวจ Content-Type แบบตรงชนิดข้อมูล แปลง JSON และตรวจ identity ขั้นเหล่านี้ใช้เวลาเช่นกัน จึงมี clock check หลังกลับจาก I/O และหลัง validate ถ้ากลับมาช้าให้คืน DEADLINE_EXPIRED ไม่คืน ok พร้อมข้อมูลใหม่
Implementation นี้เป็น read-only tool adapter ไม่ใช่ Agent ทั้งระบบ ไม่มี model selection, MCP server, queue ถาวร หรือ hard cancellation โค้ดไม่ได้ compile/import/run ผลที่แสดงเป็นสัญญาที่ต้องทดสอบ ไม่ใช่ actual run
ไฟล์ตัวอย่างและวิธีใช้
ไฟล์เต็มสามรายการอยู่ด้านล่าง: โปรแกรม, synthetic unittest และ README ซึ่งมี runtime/dependencies/env/install/run/input/expected output/test instructions/permissions/costs ไม่มี API key ฝังอยู่ใน source ถ้าไฟล์มีการแก้ไขในระบบของคุณ ต้องตรวจเนื้อหาก่อนอนุญาต live read ไม่ถือว่ารหัสเดิมทำให้เวอร์ชันแก้ผ่านโดยอัตโนมัติ
python retry_budget.py psf/requests
# ไม่มี --live-read: output ที่ออกแบบไว้คือ NOT RUN และไม่เปิด network
python -m unittest -v test_retry_budget.py
# วิธีรัน synthetic tests โดยผู้อ่าน; ผู้เขียนยังไม่ได้รัน
python retry_budget.py psf/requests --live-read
# ผู้อ่านใช้เมื่อได้รับอนุญาตให้ public GET แล้วเท่านั้น
Source Code: retry_budget.py — NOT RUN
"""Original teaching example: cooperative retry budget, NOT RUN by author.
Fixed public GitHub GET only. I/O timeout is NOT hard end-to-end cancellation.
"""
from __future__ import annotations
import argparse
import json
import math
import ssl
import sys
import time
from dataclasses import dataclass
from datetime import datetime, timezone
from http.client import IncompleteRead
from typing import Callable
from urllib.error import HTTPError, URLError
from urllib.request import HTTPRedirectHandler, ProxyHandler, Request, build_opener
from uuid import uuid4
ALLOWED_REPOSITORIES = frozenset({"psf/requests", "python/cpython"})
MAX_BODY_BYTES = 262_144
RETRYABLE_HTTP = frozenset({500, 502, 503, 504})
@dataclass(frozen=True)
class Policy:
total_seconds: float = 12.0
io_cap_seconds: float = 4.0
reserve_seconds: float = 1.0
min_attempt_seconds: float = 1.0
max_attempts: int = 3
def validate(self) -> None:
numbers = (self.total_seconds, self.io_cap_seconds,
self.reserve_seconds, self.min_attempt_seconds)
if any(type(x) not in (int, float) or not math.isfinite(x) or x <= 0
for x in numbers):
raise ValueError("INVALID_POLICY")
if self.total_seconds > 60 or self.io_cap_seconds > 10:
raise ValueError("POLICY_LIMIT")
if type(self.max_attempts) is not int or not 1 <= self.max_attempts <= 3:
raise ValueError("ATTEMPT_LIMIT")
def attempt_fits(remaining: float, delay: float, policy: Policy) -> bool:
"""Admission rule only: no guarantee about actual transport duration."""
return remaining > delay + policy.reserve_seconds + policy.min_attempt_seconds
def retry_reason(code: int, headers, read_only: bool = True) -> str:
if not read_only:
return "WRITE_STATE_REVIEW"
if code in (403, 429):
return "RATE_OR_PERMISSION_REVIEW"
if headers.get("Retry-After") is not None:
return "SERVER_WAIT_REVIEW"
if str(headers.get("X-RateLimit-Remaining", "")).strip() == "0":
return "QUOTA_REVIEW"
return "TRANSIENT_HTTP" if code in RETRYABLE_HTTP else "HTTP_REVIEW"
class RejectRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None # Destination change needs a new allowlist review.
def fetch_repository(repository: str, policy: Policy = Policy(), *,
clock: Callable[[], float] = time.monotonic,
sleeper: Callable[[float], None] = time.sleep,
opener=None) -> dict:
policy.validate()
if repository not in ALLOWED_REPOSITORIES:
raise ValueError("REPOSITORY_DENIED")
transport = opener or build_opener(ProxyHandler({}), RejectRedirect())
endpoint = "https://api.github.com/repos/" + repository
execution_id = str(uuid4())
deadline = clock() + policy.total_seconds
attempts = 0
events = []
def remaining() -> float:
return deadline - clock()
def finish(status: str, reason: str, **fields) -> dict:
return {"status": status, "reason": reason, "execution_id": execution_id,
"attempts": attempts, "events": events, **fields}
while attempts < policy.max_attempts:
# This small single-worker lesson uses deterministic capped backoff.
delay = 0.0 if attempts == 0 else min(0.5 * 2 ** (attempts - 1), 2.0)
if not attempt_fits(remaining(), delay, policy):
return finish("stopped", "BUDGET_TOO_SMALL")
if delay:
sleeper(delay)
if not attempt_fits(remaining(), 0.0, policy):
return finish("stopped", "BUDGET_AFTER_WAIT")
request = Request(endpoint, method="GET", headers={
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2026-03-10",
"User-Agent": "HybridMind-Retry-Budget-Education",
})
# Check again immediately before the blocking operation.
current_remaining = remaining()
if not attempt_fits(current_remaining, 0.0, policy):
return finish("stopped", "BUDGET_BEFORE_IO")
io_timeout = min(policy.io_cap_seconds, current_remaining - policy.reserve_seconds)
attempts += 1
events.append({"attempt": attempts, "event": "started",
"io_timeout_seconds": round(io_timeout, 3)})
try:
with transport.open(request, timeout=io_timeout) as response:
if remaining() <= 0:
return finish("stopped", "DEADLINE_EXPIRED")
if response.status != 200:
return finish("stopped", "UNEXPECTED_STATUS")
media_type = response.headers.get("Content-Type", "").split(";", 1)[0].strip().lower()
if media_type not in ("application/json", "application/vnd.github+json"):
return finish("stopped", "CONTENT_TYPE_REVIEW")
raw = response.read(MAX_BODY_BYTES + 1)
if remaining() <= 0:
return finish("stopped", "DEADLINE_EXPIRED")
if len(raw) > MAX_BODY_BYTES:
return finish("stopped", "BODY_LIMIT")
payload = json.loads(raw)
if type(payload) is not dict or payload.get("full_name") != repository:
return finish("stopped", "IDENTITY_REVIEW")
branch = payload.get("default_branch")
if type(branch) is not str or not 1 <= len(branch) <= 200:
return finish("stopped", "SCHEMA_REVIEW")
if remaining() <= 0:
return finish("stopped", "DEADLINE_EXPIRED")
events.append({"attempt": attempts, "event": "validated"})
return finish("ok", "VALIDATED", repository=repository,
default_branch=branch, source_url=endpoint,
retrieved_at=datetime.now(timezone.utc).isoformat())
except HTTPError as error:
reason = retry_reason(error.code, error.headers)
error.close()
events.append({"attempt": attempts, "event": reason})
if remaining() <= 0:
return finish("stopped", "DEADLINE_EXPIRED")
if reason != "TRANSIENT_HTTP":
return finish("stopped", reason)
except ssl.SSLError:
return finish("stopped", "TLS_REVIEW")
except URLError as error:
if remaining() <= 0:
return finish("stopped", "DEADLINE_EXPIRED")
if not isinstance(error.reason, (TimeoutError, ConnectionError)):
return finish("stopped", "NETWORK_REVIEW")
events.append({"attempt": attempts, "event": "TRANSIENT_NETWORK"})
except (TimeoutError, ConnectionError, IncompleteRead):
if remaining() <= 0:
return finish("stopped", "DEADLINE_EXPIRED")
events.append({"attempt": attempts, "event": "TRANSIENT_NETWORK"})
except (ValueError, UnicodeError):
return finish("stopped", "JSON_REVIEW")
return finish("stopped", "MAX_ATTEMPTS")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("repository", choices=sorted(ALLOWED_REPOSITORIES))
parser.add_argument("--live-read", action="store_true",
help="Explicit reader opt-in for an unauthenticated public GET")
args = parser.parse_args()
if not args.live_read:
print(json.dumps({"status": "NOT RUN", "reason": "LIVE_READ_NOT_OPTED_IN"}))
return 2
result = fetch_repository(args.repository)
for event in result["events"]:
print(json.dumps({"execution_id": result["execution_id"], **event}), file=sys.stderr)
print(json.dumps({k: v for k, v in result.items() if k != "events"}, ensure_ascii=False))
return 0 if result["status"] == "ok" else 1
if __name__ == "__main__":
raise SystemExit(main())
Source Code: test_retry_budget.py — NOT RUN
"""Synthetic test specifications. AUTHORED ONLY; NOT RUN by the author."""
import json
import unittest
from email.message import Message
from urllib.error import HTTPError
from retry_budget import Policy, attempt_fits, fetch_repository, retry_reason
class FakeClock:
def __init__(self):
self.now = 0.0
self.sleeps = []
self.extra_sleep = 0.0
def __call__(self):
return self.now
def sleep(self, seconds):
self.sleeps.append(seconds)
self.now += seconds + self.extra_sleep
def make_headers(values):
headers = Message()
for key, value in values.items():
headers[key] = str(value)
return headers
class FakeResponse:
status = 200
def __init__(self, clock, step):
self.clock, self.step = clock, step
self.headers = make_headers({"Content-Type": "application/json"})
def __enter__(self):
return self
def __exit__(self, exc_type, exc, traceback):
return False
def read(self, maximum):
self.clock.now += self.step.get("read_elapsed", 0.0)
body = self.step.get("body", {"full_name": "psf/requests", "default_branch": "main"})
raw = body if isinstance(body, bytes) else json.dumps(body).encode()
return raw[:maximum]
class FakeOpener:
def __init__(self, clock, steps):
self.clock, self.steps = clock, list(steps)
self.calls = []
def open(self, request, timeout):
self.calls.append({"method": request.get_method(),
"url": request.full_url, "timeout": timeout})
step = self.steps.pop(0)
self.clock.now += step.get("elapsed", 0.0)
if "http" in step:
raise HTTPError(request.full_url, step["http"], "synthetic",
make_headers(step.get("headers", {})), None)
if step.get("timeout"):
raise TimeoutError("synthetic timeout")
return FakeResponse(self.clock, step)
class RetryBudgetSpecifications(unittest.TestCase):
def run_fixture(self, steps, policy=Policy(), oversleep=0.0):
clock = FakeClock()
clock.extra_sleep = oversleep
opener = FakeOpener(clock, steps)
result = fetch_repository("psf/requests", policy, clock=clock,
sleeper=clock.sleep, opener=opener)
return result, opener, clock
def test_budget_boundary_is_strict(self):
self.assertFalse(attempt_fits(2.5, 0.5, Policy()))
self.assertTrue(attempt_fits(2.6, 0.5, Policy()))
def test_validated_success_one_get(self):
result, opener, clock = self.run_fixture([{"elapsed": 1.0}])
self.assertEqual(result["status"], "ok")
self.assertEqual(result["attempts"], 1)
self.assertEqual(opener.calls[0]["method"], "GET")
self.assertEqual(opener.calls[0]["timeout"], 4.0)
self.assertEqual(clock.sleeps, [])
def test_small_initial_budget_performs_no_io(self):
result, opener, _ = self.run_fixture([], Policy(total_seconds=2.0))
self.assertEqual(result["reason"], "BUDGET_TOO_SMALL")
self.assertEqual(opener.calls, [])
def test_unknown_repository_never_opens_transport(self):
clock = FakeClock()
opener = FakeOpener(clock, [])
with self.assertRaisesRegex(ValueError, "REPOSITORY_DENIED"):
fetch_repository("other/unapproved", opener=opener)
self.assertEqual(opener.calls, [])
def test_missing_or_nonfinite_policy_value_rejected(self):
for invalid in (None, float("nan"), float("inf"), -1.0):
with self.subTest(invalid=invalid):
with self.assertRaisesRegex(ValueError, "INVALID_POLICY"):
Policy(total_seconds=invalid).validate()
def test_transient_http_then_success(self):
result, opener, clock = self.run_fixture([{"http": 503, "elapsed": 1.0}, {}])
self.assertEqual(result["status"], "ok")
self.assertEqual(len(opener.calls), 2)
self.assertEqual(clock.sleeps, [0.5])
def test_retry_does_not_fit_after_elapsed_first_attempt(self):
result, opener, clock = self.run_fixture([{"http": 503, "elapsed": 10.0}])
self.assertEqual(result["reason"], "BUDGET_TOO_SMALL")
self.assertEqual(len(opener.calls), 1)
self.assertEqual(clock.sleeps, [])
def test_wait_overshoot_blocks_second_request(self):
result, opener, _ = self.run_fixture([{"http": 503}], oversleep=13.0)
self.assertEqual(result["reason"], "BUDGET_AFTER_WAIT")
self.assertEqual(len(opener.calls), 1)
def test_403_and_429_stop_without_retry(self):
for code in (403, 429):
with self.subTest(code=code):
result, opener, clock = self.run_fixture([{"http": code}])
self.assertEqual(result["reason"], "RATE_OR_PERMISSION_REVIEW")
self.assertEqual(len(opener.calls), 1)
self.assertEqual(clock.sleeps, [])
def test_retry_after_on_503_stops(self):
result, opener, _ = self.run_fixture([
{"http": 503, "headers": {"Retry-After": "60"}}])
self.assertEqual(result["reason"], "SERVER_WAIT_REVIEW")
self.assertEqual(len(opener.calls), 1)
def test_zero_quota_on_server_error_stops(self):
result, opener, _ = self.run_fixture([
{"http": 500, "headers": {"X-RateLimit-Remaining": "0"}}])
self.assertEqual(result["reason"], "QUOTA_REVIEW")
self.assertEqual(len(opener.calls), 1)
def test_late_success_is_not_accepted(self):
result, opener, _ = self.run_fixture([{"elapsed": 13.0}])
self.assertEqual(result["reason"], "DEADLINE_EXPIRED")
self.assertNotEqual(result["status"], "ok")
self.assertEqual(len(opener.calls), 1)
def test_body_read_crosses_deadline(self):
result, _, _ = self.run_fixture([{"elapsed": 1.0, "read_elapsed": 12.0}])
self.assertEqual(result["reason"], "DEADLINE_EXPIRED")
def test_invalid_json_and_identity_are_not_retried(self):
for body, expected in ((b"not json", "JSON_REVIEW"),
({"full_name": "other/repo"}, "IDENTITY_REVIEW")):
with self.subTest(expected=expected):
result, opener, _ = self.run_fixture([{"body": body}])
self.assertEqual(result["reason"], expected)
self.assertEqual(len(opener.calls), 1)
def test_repeated_transient_failure_stops_at_cap(self):
result, opener, clock = self.run_fixture([{"timeout": True}] * 3)
self.assertEqual(result["reason"], "MAX_ATTEMPTS")
self.assertEqual(len(opener.calls), 3)
self.assertEqual(clock.sleeps, [0.5, 1.0])
def test_write_uncertainty_never_enters_retry_policy(self):
self.assertEqual(retry_reason(503, make_headers({}), read_only=False),
"WRITE_STATE_REVIEW")
if __name__ == "__main__":
unittest.main()
Source Code: README.md — NOT RUN
# Cooperative Retry Budget — ตัวอย่างต้นฉบับ Hybrid Mind
**Status: AUTHORED / NOT RUN** ผู้เขียนไม่ได้ execute/import/compile โค้ด ไม่ได้รัน unittest และไม่ได้เรียก GitHub ผ่านตัวอย่างนี้ วิธีรันด้านล่างเป็นคำแนะนำให้ผู้อ่านทดสอบในขอบเขตที่ตนได้รับอนุญาต ไม่ใช่หลักฐานว่าผ่านแล้ว
## Runtime / Dependencies / Environment
- Python 3.12+; เลือกจากเอกสาร Python 3.12.15 ที่เปิดอ่าน ไม่ได้วัด runtime ที่ติดตั้ง
- Standard Library เท่านั้น: urllib, time, dataclasses, json, unittest และโมดูลประกอบ ไม่มี pip install หรือ SDK/API model
- ไม่ต้องตั้ง environment variables ไม่มี API key/token ไม่อ่าน credential จากระบบ ปิด automatic proxy handling ใน opener ตัวอย่างเพื่อให้ destination คงตามที่ออกแบบ
- ใช้ HTTPS certificate verification ตาม default ของ Python; ไม่มี custom CA หรือ insecure bypass
## Installation
ติดตั้ง Python 3.12+ ด้วยช่องทางที่องค์กรอนุญาต บันทึกไฟล์ `retry_budget.py` และ `test_retry_budget.py` ในโฟลเดอร์เดียวกัน ตัวอย่างนี้ไม่มี dependency เพิ่มเติม เรียกจากโฟลเดอร์นั้น
## Input / Allowed operation
Input คือชื่อ repository ใน allowlist: `psf/requests` หรือ `python/cpython` ไม่มีช่องให้ผู้ใช้เปลี่ยน HTTP method, hostname, credential, query URL หรือเพิ่ม write operation ใช้ GET metadata ของ repository สาธารณะเท่านั้น ไม่อ่าน issues/private data ไม่เปลี่ยนข้อมูล GitHub
ค่าออกแบบ default: total budget 12 วินาที, I/O timeout cap 4 วินาที, attempts ไม่เกิน 3, delay 0.5 แล้ว 1.0 วินาที, reserve 1 วินาที, minimum attempt 1 วินาที เป็นสมมติฐานสาธิตที่ต้องปรับจากงานจริง ไม่ใช่ค่าที่ benchmark มา
## Run commands
ยังไม่เรียก network:
```text
python retry_budget.py psf/requests
```
Expected output ที่ออกแบบไว้ (ไม่ใช่ actual run):
```json
{"status": "NOT RUN", "reason": "LIVE_READ_NOT_OPTED_IN"}
```
เมื่อผู้อ่านได้รับอนุญาตให้ทำ public GET แล้วจึงเลือก opt-in:
```text
python retry_budget.py psf/requests --live-read
```
Expected successful shape ที่ออกแบบไว้; ค่า ID/time/branch เป็น placeholder ไม่ใช่ผล API ที่เก็บมา:
```json
{
"status": "ok",
"reason": "VALIDATED",
"execution_id": "<generated-uuid>",
"attempts": 1,
"repository": "psf/requests",
"default_branch": "<validated-branch>",
"source_url": "https://api.github.com/repos/psf/requests",
"retrieved_at": "<utc-iso-time>"
}
```
CLI คืน exit 0 เฉพาะ validated success; exit 1 เมื่อ stop; exit 2 เมื่อไม่มี live opt-in มี structured event เฉพาะ attempt/reason/timeout/execution ID ทาง stderr ไม่ log response body/header dump/secret หรือ traceback ของ remote payload
## Mechanism / Scope
1. ตรวจ policy finite positive และ allowlist ก่อนมี request
2. ตั้ง cooperative deadline จาก monotonic
3. admission rule ต้องเหลือมากกว่า delay + reserve + minimum attempt
4. recheck หลัง sleep และก่อน I/O; ส่ง min(I/O cap, remaining − reserve) เป็น timeout ให้ opener
5. หลังเปิด connection/read/validate ตรวจ clock อีกครั้ง; ข้อมูลที่เสร็จหลัง deadline ไม่ถูกอ้างว่าสำเร็จตามงบ
6. เฉพาะ selected 5xx หรือ timeout/connection interruptions ที่อ่านอย่างเดียวอาจลองใหม่ใน cap; permission/rate/quota/server-wait/schema/JSON/identity/redirect/TLS outcomes ไม่ฝืน retry
HTTP 403/429 หยุดตรวจ ไม่สรุปทุก403ว่า rate limit; HTTP error ใดที่มี Retry-After หรือ quota remaining=0 หยุดตาม policy นี้ ไม่รอจนครบ header ภายในงานสั้น ไม่ตีความว่า GitHubห้ามretryถาวร ตำแหน่ง redirect ต้องตรวจขอบเขตใหม่ก่อนใช้ เป็น local fail-closed design แม้ GitHub เอกสารแนะนำตาม redirect เมื่อจัดการอย่างถูกต้อง
**ขอบเขตสำคัญ:** timeout ของ urllib ควบคุม blocking I/O ไม่ได้ hard-abort งานครบทุกขั้น การอ่าน body, DNS/TLS, scheduling, sleep, parse หรือ cleanup อาจเกินเวลาที่ตั้ง โค้ดตรวจ cooperative deadline หลังงานคืนการควบคุมเท่านั้น Reserve ไม่ใช่การกันCPUเวลาจริง ไม่มี hard latency/SLA guarantee ไม่มี cancellation ของงานที่ค้างอยู่
Read-only retries ยังสร้าง request/quota/load ได้ ไม่ใช่ฟรี และผลอ่านแต่ละรอบอาจเปลี่ยนตามเวลา ไม่ขยาย helper ไปใช้กับ POST/PATCH/DELETE หากสถานะ write ไม่แน่ชัดต้อง reconcile/ใช้ idempotency contract ที่บริการรองรับก่อน ไม่มี write-recovery implementation ในตอนนี้
## Test instructions / Acceptance
Synthetic tests inject FakeClock/FakeOpener ไม่มี live API:
```text
python -m unittest -v test_retry_budget.py
```
**Test status: NOT RUN** เป็น specification ที่เขียนไว้ 16 test methods ไม่มี actual PASS count
Acceptance ที่ต้องพิสูจน์: no I/O เมื่อ budget ไม่พอ; no retry หลัง permission/rate/quota/wait directives; max3 requests; elapsed firstcall และ oversleepลดโอกาสเริ่มรอบใหม่; late200ไม่เป็นok; schema/identityต้องตรง; uncertain writeเข้าstop ไม่retry
Live integration plan (NOT RUN): สั่ง allowlisted GET 1ครั้งใน quota ที่อนุญาต อ่าน source_url/full_name/default_branch/UTCtime และจำนวน calls จริง ทดสอบ timeout/redirect/rate behaviors ใน sandbox/controlled fake service ที่ตนมีอำนาจเท่านั้น ไม่จงใจทำให้บริการสาธารณะ rate-limit หรือเพิ่มloadเพื่อทดสอบ
## Permissions / Costs / Production limits
- ตัวอย่าง public read ไม่ใช้ token ไม่ใช้ LLM/model/paid API จึงไม่มีการเรียกเก็บ model cost จากโค้ดนี้ การเชื่อมอินเทอร์เน็ต/โฮสต์และGitHubquotaขึ้นกับระบบผู้อ่าน ไม่อ้างราคา/availabilityปัจจุบัน
- ไม่มี actual agent execution, tool-selection model, persistent queue/checkpoint, distributed retry owner, circuit breaker, traces SDK, credential management หรือ deployment
- Single-worker teaching delay ไม่ใส่ jitter เพื่อให้อ่านกฎได้ตรง Production หลายworkerต้องออกแบบ boundedjitter, concurrency limits และเลือกretryownerหนึ่งชั้นตามระบบจริง ไม่คัดลอกconstantsแล้วเรียกproductionready
- Logs อยู่ stdout/stderr ไม่ persist ให้อัตโนมัติ Production retention/redaction/access control ต้องออกแบบเพิ่ม
## References
- [Python monotonic and sleep](https://docs.python.org/3.12/library/time.html)
- [Python urllib blocking-operation timeout](https://docs.python.org/3.12/library/urllib.request.html)
- [GitHub rate-limit handling](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api#handle-rate-limit-errors-appropriately)
- [GitHub public repository endpoint](https://docs.github.com/en/rest/repos/repos#get-a-repository)
- [AWS timeout/retry guidance](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/)
Algorithm/code/fake fixtures เป็นงานเขียนต้นฉบับ ชื่อAPI/status/headerเป็นเงื่อนไข integration ไม่มี copiedvendorcode
Failure modes: หยุดให้ถูกเหตุผลดีกว่าลองต่อทุกกรณี
| เหตุการณ์ | ตัดสินใจ | สิ่งที่ผู้ดูแลควรตรวจ |
|---|---|---|
| 502/503 และไม่มีคำสั่งรอ | พิจารณางบกับattempt capก่อน | เหตุการณ์ชั่วคราวหรือปัญหาถาวร |
| Timeout ของการอ่าน | ลองได้เฉพาะreadและงบยังผ่าน | จุดที่ค้างจริงและความหมายtimeoutของtransport |
| 403/429 | หยุดตรวจ ไม่เพิ่มrequest | สิทธิ์, rate limit, quota,คำแนะนำบริการ |
| Retry-After หรือremaining=0 | หยุดงานสั้นตามpolicy | ออกแบบคิวรอรอบอื่นถ้างานยังมีคุณค่า |
| JSON/schema/identityผิด | หยุด ไม่สร้างผลจากการเดา | APIเปลี่ยน, responseผิดปลายทาง, parser/สัญญาข้อมูล |
| ผลสำเร็จหลังdeadline | ไม่รายงานokตามงบ | ยอมรับlate resultหรือยุติงานอย่างไรในระบบจริง |
| Write ไม่ทราบว่าสำเร็จหรือยัง | ไม่ใช้read-retry policy | สถานะจริง, idempotency, reconciliation |
AWS อธิบายว่า retry เพิ่มภาระให้ระบบที่อาจกำลังล้ม และ timeout ไม่พิสูจน์ว่า side effect ยังไม่เกิด จึงต้องพิจารณา idempotency และความหมาย timeout ของ client จริง อ่าน AWS timeout/retry guidance
กรณีเขียนข้อมูลต่างจากอ่านอย่างเดียว สมมติการส่งโพสต์หมดเวลารอ แต่ปลายทางอาจบันทึกโพสต์แล้ว การส่งซ้ำด้วยกฎงบเวลาอย่างเดียวทำให้เกิดข้อมูลซ้ำได้ เวลายังเหลือไม่ได้ตอบคำถามว่าสิทธิ์หรือสถานะการเขียนปลอดภัยหรือไม่ บทเรียนนี้จึงไม่แถมการ retry POST เข้าในโค้ด GET และไม่สร้างภาพว่าแก้ uncertain write ด้วยการเพิ่ม backoff แล้วจบ

หลักฐานขั้นต่ำ: เห็นการตัดสินใจแทนการเชื่อคำบอกเล่า
บันทึก execution ID หนึ่งค่าเพื่อผูกเหตุการณ์ของงาน หมายเลข attempt เหตุผลว่ารอ เริ่ม validate หรือหยุด และค่าพารามิเตอร์ timeout ที่ตั้ง Event ต้องไม่เท่ากับการพิสูจน์เวลาจริง เช่น io_timeout_seconds: 2 แสดงว่าตั้งสองวินาที ไม่ได้ยืนยันว่า I/O ใช้สองวินาที
CLI ส่งเหตุการณ์ JSON ทาง stderr และผลสรุปทาง stdout ไม่มี log ถาวร Production ต้องเพิ่มเวลาเริ่ม/จบที่วัดจริง นโยบายเก็บข้อมูลและสิทธิ์อ่าน โดยไม่ dump response body หรือ credential
วิธีทดสอบ: สถานะ NOT RUN กับแผนที่ตรวจได้
Synthetic test จัดเวลาและ response เอง เช่น ให้ fake clock ผ่านสิบวินาทีหลัง 503 แล้วตรวจว่าไม่มี request ใหม่ ไม่ใช่การพิสูจน์เครือข่ายจริง ส่วน integration test ต้องมีหลักฐานการเชื่อมบริการในขอบเขตที่อนุญาต
ไฟล์ unittest ที่แนบเขียนกรณีงบแรกไม่พอ, repository ไม่ได้รับอนุญาต, 503แล้วสำเร็จ, เวลาผ่านจนretryไม่พอ, sleepนานกว่าที่ตั้ง, 403/429, Retry-After, quotaศูนย์, successช้า, bodyอ่านช้า, JSON/identityผิด, capครบ และwriteต้องส่งตรวจ ผู้เขียนไม่ได้รัน จึงรายงาน NOT RUN ทั้งunit/integration/failure/security tests ไม่มีจำนวน PASS หรือ success rate
เกณฑ์ก่อนเพิ่มเข้าระบบจริงควรพิสูจน์ว่าการปฏิเสธมี zero new calls หลังจุดหยุด ไม่ใช่เพียงมี error message เกณฑ์ late response ต้องพิสูจน์ว่าระบบรับต่อไม่ใช้ข้อมูลนั้นอย่างเงียบ หาก transport ค้างนานเกินdeadline ต้องมีแผนยุติ/แยก worker ที่ทดสอบแล้วตาม platform ไม่ถือว่าการตรวจclockหลังreturnแก้การค้างได้ทั้งหมด
ทดสอบ rate/error ด้วย fixture หรือบริการที่คุณมีอำนาจควบคุม ไม่จงใจยิง API สาธารณะให้ติด rate limit และไม่อ้าง production ready จาก happy path ครั้งเดียว
สิทธิ์และความปลอดภัยต้องเป็นกฎนอกคำตอบโมเดล
Allowlist คือรายชื่อที่ระบบยอมให้เรียก แยกจากชื่อที่ผู้ใช้หรือโมเดลขอมา ถ้า repository อยู่นอกรายการให้ปฏิเสธก่อน I/O ไม่ให้การพยายามแก้ timeout กลายเป็นการขยายขอบเขตข้อมูล ตัวอย่างไม่รับ hostname จากข้อความจึงลดพื้นที่ให้ข้อความชักนำไปเรียกบริการอื่น แต่ยังไม่ใช่หลักฐานว่าผ่าน security test จริง
ตรวจ identity และเก็บเฉพาะฟิลด์ที่ต้องใช้ อย่าส่ง remote body ทั้งก้อนเข้าคำสั่งโมเดลพร้อมสิทธิ์ทั้งหมด ตัวอย่างนี้ไม่ได้ implement prompt-injection defense ครบระบบและไม่ผ่าน security runtime test หากเพิ่มโมเดลต้องตรวจขอบเขตใหม่
ต้นทุนและ Production: ไม่ใช่ทุก retry จะช่วยผู้ใช้
ต้นทุนของตัวอย่างไม่ได้มีราคาต่อ token เพราะไม่มี model call แต่ยังมีจำนวน HTTP requests โควตา network และเวลาของ worker หากโครงการคุณมี LLM อยู่ก่อนและหลังเครื่องมือ ให้แยกนับต้นทุนแต่ละรอบ อย่าเอาจำนวน tool calls ไปแทนจำนวน model callsโดยตรง และอย่านำตัวเลขสมมติในLabไปอ้างราคาจริง
เลือกเวลาใช้จริงจากสัญญางานและหลักฐานบริการ ตัวอย่างมี delay แน่นอนเพื่อสอนหนึ่ง worker ไม่มี jitter ซึ่งคือการสุ่มช่วงรอเพื่อกระจาย load หากมีหลาย worker ต้องออกแบบ concurrency และ retry owner เพิ่ม ไม่ก๊อบปี้ค่าบทเรียนแล้วรับรอง SLA
แพลตฟอร์มสำเร็จรูปต้องตรวจความหมาย timeout และ retry ที่ framework ทำเอง รวมถึงอ่าน request count/stop reason ได้หรือไม่ หากสร้างเองต้องรับผิดชอบสัญญาเหล่านี้ เลือกจากขอบเขตที่งานต้องบังคับได้
ทดลองแผนเวลาและประตู Retry โดยไม่เรียก API จริง
Lab แรกแสดงงบสิบสองวินาทีในฉากสามมิติ ตั้ง elapsed/delay/reserve/minimum แล้วดูว่ารอบใหม่ผ่านกฎหรือไม่ ส่วน visual interactive แยกอีกชุดเลือกชนิด response และสิทธิ์ operation แสดงว่าผ่านเวลาอย่างเดียวไม่พอ ผลเป็นคำอธิบายกฎ ไม่ใช่ความเร็ว GitHub หรือผลโมเดล
เหลือสองวินาทีแต่ต้องพักครึ่งวินาทีและสำรอง/ขั้นต่ำรวมสองวินาที กฎควรหยุด เพิ่มเวลาจนผ่านแล้วเลือก 429 กฎ rate ควรยังหยุด ไม่มีปุ่มใดเรียก Agent/API จริงหรือเขียนข้อมูลภายนอก หน่วยเวลามาจากค่าที่ตั้ง ไม่ได้วัด network
ออกแบบ labels, ผลที่อ่านข้อความได้, keyboard/reduced motion และ fallback ในบทความแล้ว แต่ผลทดสอบ UI จริงทุกปุ่ม/หน้าจอยังเป็น UNKNOWN เจ้าของตรวจเอง ไม่ถือ simulation ว่า Lab ผ่าน browser QA
3D Budget Timeline: งบที่ใช้คือเวลา หน่วยเป็นวินาที
SIMULATION: ค่าด้านล่างกำหนดเพื่อฝึกตัดสินใจ ไม่ใช่ latency ที่วัดจาก GitHub หรือโมเดล เส้น deadline เป็นจุดตรวจของตัวควบคุม ไม่ได้หยุด I/O ที่กำลังทำงานด้วยตัวเอง ปรับเวลาแล้วดูพื้นที่ของ attempt ที่เหลือ
สูตรและ fallback แบบอ่านข้อความ
remaining = total − spent; available after wait = remaining − delay − reserve; reserve = 1 วินาที; minimum attempt = 1 วินาที กฎตัวอย่างอนุญาต retry เมื่อ remaining มากกว่า delay + reserve + minimum เท่านั้น I/O parameter = min(cap, available) ถ้าเงื่อนไขผ่าน
ตัวอย่างงบ12 ใช้ไป11 รอ0.5 เหลือหลังรอและสำรอง −0.5 วินาที ต้องหยุด ไม่ปัดผลติดลบเป็นการประหยัด หากภาพสามมิติหรือ JavaScript ไม่พร้อมให้อ่านสูตรและตัวอย่างนี้แทน
Retry Gate Inspector: เวลาเหลือไม่ได้แปลว่าอนุญาตให้ลอง
Synthetic decision cases: เปลี่ยนเหตุผิดพลาด จำนวน attempt และเวลาเหลือ ผลเป็นข้อเสนอจากกฎจำลอง ไม่ยิง API และไม่เปลี่ยนข้อมูลภายนอก
| กรณีอ่านโดยไม่รัน | ผลคาดหมายตามกฎ |
|---|---|
| 503, attempt1, remaining8 | เสนอ retry หลัง0.5s; ตรวจงบใหม่หลังรอ |
| 503, attempt2, remaining3 | หยุด: เท่ากับ delay1 + reserve1 + minimum1 ไม่เหลือมากกว่าเกณฑ์ |
| 503, attempt3 | หยุด: ครบจำนวนครั้ง |
| 429 หรือ Retry-After | หยุดให้คน/ตัวจัดคิวตรวจ ไม่เพิ่มสิทธิ์ |
| เขียนแล้วผลคลุมเครือ | ตรวจสถานะงานเดิมก่อน ห้ามใช้กฎ GET นี้ retry การเขียน |
เชื่อม Video Agent Lab กับทักษะตั้งงบการลองซ้ำ
Native Video Agent Lab ของ Hybrid Mind เป็น simulation วงจรงานวิดีโอ ไม่ใช่ตัวรัน Python ใช้ฝึกวาง brief ว่าถ้าขั้นอ่านข้อมูลอ้างอิงลองซ้ำจะนับงบต่ออย่างไร แล้วบันทึกคำตัดสินข้าง Lab ไม่อ้างว่าปลั๊กอินบังคับ deadline 12 วินาทีหรือเติม brief หกส่วนให้อัตโนมัติ
อ่าน brief เลือกขั้นที่พึ่งข้อมูลภายนอกและตั้ง elapsed/backoff เอง ถ้ากฎหยุดบันทึกว่าต้องส่งต่อคนและข้อมูลยังไม่ยืนยัน เวลา/เงินใน simulation ไม่ใช่ผลวัดการผลิตวิดีโอจริง
เป้าหมาย: วางแผนตรวจข้อมูลประกอบวิดีโอหนึ่งชิ้น โดยกำหนดจุดหยุดเมื่อการลองอ่านซ้ำไม่เหมาะสม ข้อมูลและเครื่องมือ: ใช้simulationวงจรVideoLabและโจทย์อ่านmetadataสมมติ; ไม่สั่งAgentหรือAPIจริง ขอบเขตสิทธิ์: แผนอ่านอย่างเดียว ไม่มีการเผยแพร่วิดีโอ ส่งข้อความ แก้ไฟล์ภายนอก หรืออนุมัติwriteผ่านปุ่ม สิ่งส่งมอบ: บันทึกattempt/เวลาที่ผ่าน/remaining/เหตุผลอนุญาตหรือหยุดในข้อความที่อ่านได้ เกณฑ์ตรวจรับ: ใช้งบ12sเป็นสมมติฐานเดียวทั้งงาน; รวมbackoff; แยกrate/permissionจากงบ; ไม่อ้างLabบังคับกฎPythonเอง จุดหยุดหรือส่งให้คน: งบไม่พอ, 403/429, serverขอรอ, quotaหมด, ข้อมูลยังยืนยันไม่ได้ สถานะ: SIMULATION; nativeLabไม่ใช่hardcancellation; เวลาและเงินเป็นค่าตั้ง; UIจริงUNKNOWN
จากคลิปดิบ
สู่จักรวาลคอนเทนต์
ChatGPT ตัดต่อวิดีโอแทนคนได้จริงแค่ไหน? สำรวจระบบ AI Video Editing Agent ผ่านฉากสามมิติที่โต้ตอบได้ พร้อมแยกข้อเท็จจริง สร้างพรอมป์ต์ และจำลองต้นทุนก่อนลงมือทำจริง
ออกแบบระบบตัดต่อในมุมมอง 3D
ลากบนฉากเพื่อหมุนมุมมอง แตะโหนดเพื่อดูหน้าที่ของ AI ในแต่ละขั้นตอน
จัดระเบียบไฟล์ ตรวจคุณภาพภาพและเสียง และกำหนดสิทธิ์เข้าถึงให้ AI Agent
แยกความสามารถจริงจากคำโฆษณา
กดแต่ละประเด็นเพื่ออ่านรายละเอียดและแหล่งข้อมูล
ChatGPT Work เข้าถึงไฟล์ในเครื่องได้ตรวจสอบแล้ว / มีเงื่อนไข+
Astra ใช้โปรแกรมคอมพิวเตอร์ทำงานได้ตรวจสอบแล้ว / ไม่ใช่โปรแกรมตัดต่อในตัว+
ตัดเสร็จใน 5–10 นาทีทุกกรณียังพิสูจน์เป็นเกณฑ์ทั่วไปไม่ได้+
ถูกกว่าจ้างคน 10 เท่าต้องกำหนดต้นทุนให้เทียบกันได้+
หนึ่งไอเดีย สามรูปแบบคอนเทนต์
ปรับรูปแบบวิดีโอ แล้วดูกรอบภาพตัวอย่างและ Prompt เปลี่ยนตามแบบเรียลไทม์
ช่วยตัดต่อ?
พร้อมนำไปใช้กับ AI Agent
ตัวสร้าง Prompt นี้ไม่อัปโหลดคลิป ไม่เรียก API เสียเงิน และไม่ตัดต่อวิดีโอจริง ต้องใช้กับเครื่องมือที่ผู้ใช้อนุญาตภายหลัง
จำลองเวลาและต้นทุนของคุณเอง
เลื่อนตัวเลขเพื่อเปรียบเทียบสถานการณ์สมมติ ไม่ใช่ผล benchmark ที่ยืนยันแล้ว
ตัวเลขเป็นตัวอย่างเพื่อช่วยตัดสินใจ ไม่ใช่ผลประหยัดที่รับประกัน
AI ที่ตัดคลิปเป็น ไม่ได้ทำงานลำพัง
สิ่งที่น่าสนใจของ GPT-6 Astra และ GPT-6.1 Sol ไม่ได้อยู่ที่ว่าตัวโมเดลมีปุ่ม Render MP4 ซ่อนอยู่ แต่คือความสามารถในการจัดการงานหลายขั้นตอนร่วมกับเครื่องมืออื่น: อ่านไฟล์ วางลำดับเรื่อง ใช้แอป ตรวจผลลัพธ์ และแก้งานตามข้อเสนอแนะของมนุษย์
สำหรับคนทำคอนเทนต์ ความต่างสำคัญคือการเปลี่ยนจาก “ส่งคลิปให้ AI แล้วหวังว่าจะได้ผลงาน” ไปเป็น Workflow ที่ตรวจรับได้ทุกขั้น ตั้งแต่การเลือกช่วงเด็ด การสร้างซับ การตรวจคำผิด จนถึงการเรนเดอร์ไฟล์ตามสัดส่วนของแต่ละแพลตฟอร์ม
ข้อควรระวังคือระบบ Work ที่รันบนเว็บหรือมือถือไม่ได้หมายความว่า AI เข้าถึงคลิปบนคอมพิวเตอร์บ้านได้ทันที ต้องทำงานในสภาพแวดล้อมที่เปิดให้เข้าถึงไฟล์จริง มีเครื่องมือตัดต่อ และมีสิทธิ์ใช้งานอย่างชัดเจน
รวบรวมไฟล์ในโฟลเดอร์ที่เข้าถึงได้ ระบุสิทธิ์และข้อจำกัด
ให้ Agent เสนอ Hook, ช่วงตัด และโครงเรื่องก่อนตัดต่อ
ตรวจชื่อเฉพาะ ตัวเลข ภาษาไทย และความถูกต้องของคำกล่าวอ้าง
ตรวจความละเอียด เสียง ระยะเวลา safe zone และคำบรรยาย
Checklist ก่อนนำกฎไปต่อกับ Agent ของคุณ
- ระบุจุดเริ่ม/จบและ deadline เดียว ไม่รีเซ็ตจาก retry
- รู้ความหมาย timeout ของ transport ไม่อ้าง hard cancellation จาก post-check
- คำนวณ delay/reserve/minimum จาก remaining ที่อ่านใหม่หลังพัก
- ตั้ง attempt cap รวมครั้งแรกและ retry owner หนึ่งจุด
- ตรวจ permission/rate/server-wait/schema ก่อน request ใหม่
- แยก read จาก write และ uncertain state ไม่ย้าย helper โดยไม่มีสัญญาเพิ่ม
- ส่ง stop reason/execution ID/attempts ระบุข้อมูลที่ยังไม่ยืนยัน
- ทดสอบตามสิทธิ์พร้อม actual evidence ก่อนอ้างผลผ่าน
อ่านต่อ สถาปัตยกรรม Agent และการเลือก Workflow/Single-Agent/Multi-Agent เพื่อวาง retry adapter ในระบบ การเพิ่มจำนวน Agent ไม่ได้เพิ่มเวลาหรือสิทธิ์ API โดยอัตโนมัติ
แบบฝึกหนึ่งข้อ: เขียนคำตัดสินของรอบถัดไป
ใช้ budget 12s/reserve 1s/minimum 1s/attempt cap 3 หลัง GET แรกผ่านไป 9.2s และตอบ 503 ไม่มีคำสั่งรอ รอบสอง delay 0.5s ให้เขียน remaining เส้นแบ่ง คำตัดสินและ timeout ถ้าอนุญาต จากนั้นเปลี่ยน response เป็น 429 แล้วเขียนเหตุผลใหม่ เป็น synthetic exercise ไม่ต้องเรียก API
เกณฑ์ตรวจรับและเฉลย
Remaining เท่ากับ 2.8s มากกว่า 0.5+1+1=2.5s จึงผ่านประตูเวลา หลังพักตรง 0.5s เหลือ 2.3s ค่า I/O เป็น min(4,2.3−1)=1.3s ถ้าพักนานกว่านี้ต้องอ่าน clock ใหม่ เมื่อ response เป็น 429 ให้หยุดตรวจ rate/permission แม้เวลายังผ่าน
ตรวจห้าข้อ: คำนวณ remaining ถูก นับ backoff รวม reserve/minimum แยก 429 จาก 5xx ทั่วไป และระบุว่า 1.3s เป็น I/O parameter ไม่ใช่ actual duration แก้ reasoning ที่คลาด ไม่แปลงคะแนนแบบฝึกเป็นคะแนนประสิทธิภาพ Agent
FAQ
ตั้ง timeout ต่อครั้งแล้ว ยังต้องมีงบรวมอีกหรือ?
ต้องพิจารณาถ้าสัญญางานมีเวลารอรวม เพราะเวลารอบก่อนและbackoffยังถูกใช้ไปแล้ว ค่าต่อoperationไม่แทนdeadlineทั้งงานและไม่พิสูจน์ว่าทุกขั้นหยุดตามเวลา
อ่านอย่างเดียวลองซ้ำได้เสมอหรือ?
ไม่ได้ แม้ไม่มี write ยังมี quota/load และสิทธิ์ ต้องผ่านชนิด error คำสั่งรอ attempt cap และงบ ข้อมูลแต่ละรอบอาจเป็นคนละเวลา ต้องบันทึกแหล่งและเวลาที่ตรวจได้
Reserveหนึ่งวินาทีเป็นค่าที่ทุกระบบควรใช้หรือ?
เป็นสมมติฐานของบทเรียน ใช้แสดงเกณฑ์เริ่มงานเพิ่ม ไม่ใช่เวลาที่วัดหรือการจอง CPU เลือกค่าจาก contract และหลักฐานบริการที่จะใช้
ตรวจ clock หลังrequestเท่ากับยกเลิกทันdeadlineหรือ?
ไม่เท่ากัน โค้ดตรวจได้หลัง operation คืนการควบคุม หาก transport ค้างก็ยังรอ ต้องออกแบบและทดสอบการยุติงานด้วยกลไกที่ platform รองรับจึงจะอ้างขอบเขตเวลาแบบบังคับได้
มีโค้ดและtestsครบแล้วนำขึ้นProductionได้เลยหรือ?
ยังต้องทดสอบและออกแบบส่วนที่ขาด ตัวอย่าง NOT RUN ไม่มี persistent queue/distributed retry owner/hard cancellation หรือ runtime validation ไม่มีผล PASS จาก synthetic หรือ GitHub จริงให้รับรอง production
แหล่งอ้างอิงและขอบเขตหลักฐาน
เปิดต้นทางเมื่อ 14 ตุลาคม 2026 เวลา 02:20 UTC ตรวจรุ่นและสัญญาล่าสุดก่อนนำไปใช้ ไม่มีผล benchmark ของผู้เขียนหรือบริการ Deep Research เฉพาะ AWS เป็นแนวทางจากผู้พัฒนา ไม่ใช่ผลทดสอบอิสระของบทเรียนนี้
- Python 3.12 time: monotonic และ sleep — หน้าที่นาฬิกาและการพัก
- Python 3.12 urllib.request — ความหมาย timeout
- GitHub REST best practices — rate และคำสั่งรอ
- GitHub Get a repository — public metadata GET และ API version
- AWS: Timeouts, retries, and backoff with jitter — retry load และ side effect; URL ต้นฉบับ redirect ไป Builder Center หน้าที่อ่านแสดง published 12 มิถุนายน 2026/modified 15 มิถุนายน 2026 ไม่ใช้ metadata นี้สรุปวันที่หลักการเริ่ม
สถานะ: ต้นทางอ่านแล้ว โค้ด/แผนทดสอบ NOT RUN, Lab เป็น simulation, browser UI UNKNOWN ไม่มี actual latency/cost/success rate หรือ production ready claim

ใส่ความเห็น