อะไรอาจผิดพลาดได้เมื่อคุณเปิดใช้นโยบายที่รับรู้ผู้กระทำ?
นโยบายที่รับรู้ผู้กระทำจะเปลี่ยนวิธีที่แอปพลิเคชันของคุณตอบสนองตามว่าใครหรืออะไรเป็นผู้กระทำ ถ้าทำได้ดี ก็จะให้ประสบการณ์ที่ราบรื่นแก่ผู้ใช้ เปิดทางที่มีโครงสร้างให้กับ agent ที่ชอบธรรม และเพิ่มแรงเสียดทานเฉพาะตรงที่ควรเท่านั้น ถ้าทำได้ไม่ดี ก็จะปิดกั้นผู้ใช้จริง ทำให้ integration ที่ลูกค้าพึ่งพาใช้งานไม่ได้ และสร้างทิกเก็ตซัพพอร์ตที่ไม่มีใครอธิบายได้
ความแตกต่างนี้แทบไม่ใช่เรื่องของตรรกะนโยบาย แต่เป็นเรื่องของ rollout มากกว่า (สำหรับตัวโมเดลนโยบายเอง ดู การออกแบบนโยบายระดับแอปสำหรับมนุษย์และ agent AI) คู่มือนี้อธิบายกระบวนการแบบเป็นขั้นตอนที่ทีมสามารถใช้ได้กับทุก route: สังเกต ดูตัวอย่าง บังคับใช้กับบางส่วน แล้วค่อยขยาย แต่ละขั้นมีเกณฑ์ออกที่ชัดเจนและมีทางย้อนกลับที่รวดเร็ว
สามโหมด
ให้คิดว่านโยบายแต่ละตัวทำงานในหนึ่งในสามโหมดต่อ route:
- สังเกต มีการบันทึกการจัดประเภท แอปพลิเคชันตอบสนองเหมือนเดิมทุกอย่างราวกับไม่มีสิ่งเหล่านี้
- ดูตัวอย่าง มีการประเมินนโยบายและบันทึกการตัดสิน แต่ยังไม่ถูกนำไปใช้ คุณจึงเห็นได้ว่าจะเกิดอะไรขึ้น
- บังคับใช้ นำการตัดสินไปใช้กับคำขอจริง
โหมดเป็นของคู่ route กับนโยบาย ไม่ใช่ของทั้งแอปพลิเคชัน หน้าเอกสารของคุณอาจกำลังบังคับใช้อยู่ ในขณะที่ checkout ยังอยู่ในโหมดสังเกต
ขั้นที่ 1: สังเกตจนรู้ baseline ของคุณ
เริ่มโดยให้ทุก route อยู่ในโหมดสังเกต เก็บข้อมูลอย่างน้อยดังนี้:
- route และ method,
- actor class (Human, Human + AI, Agent หรือ Unknown) และ basis (Verified, Declared, Inferred หรือ Unknown) ตามที่กำหนดไว้ใน อนุกรมวิธานของเราสำหรับทราฟฟิกของแอปพลิเคชัน,
- สถานะ response และตัวบ่งชี้ผลลัพธ์แบบง่ายสำหรับ route นั้น เช่น "account created" หรือ "search returned results".
เก็บข้อมูลให้นานพอที่จะครอบคลุมรอบรายสัปดาห์ปกติและทุก release ที่คุณส่งในช่วงเวลานั้น จากนั้นจด baseline ของแต่ละ route ที่คุณสนใจไว้: สัดส่วน class, อัตรา Unknown และอัตราผลลัพธ์ต่อ class คุณจะใช้ตัวเลขเหล่านี้เป็นจุดอ้างอิงสำหรับทุกอย่างที่ตามมา
เกณฑ์ออก: คุณอธิบายได้ว่า สำหรับแต่ละ candidate route ใครใช้งาน และผลลัพธ์แตกต่างกันอย่างไรตาม class
ขั้นที่ 2: เขียนนโยบายเป็นข้อมูล
นโยบายจะตรวจสอบ ทดสอบ และย้อนกลับได้ง่ายกว่าเมื่ออยู่ในรูปข้อมูล มากกว่าจะกระจายเป็น if statements หลายจุด
type ActorClass = "human" | "human_ai" | "agent" | "unknown";
type Basis = "verified" | "declared" | "inferred" | "unknown";
type Action = "allow" | "adapt" | "challenge" | "review" | "limit";
type Mode = "observe" | "preview" | "enforce";
interface Rule {
actor: ActorClass | "*";
basis?: Basis[];
action: Action;
}
interface RoutePolicy {
route: string;
mode: Mode;
enforcePercent: number;
rules: Rule[];
fallback: Action;
}
export const policies: RoutePolicy[] = [
{
route: "GET /api/catalog",
mode: "preview",
enforcePercent: 0,
rules: [
{ actor: "agent", basis: ["verified"], action: "allow" },
{ actor: "agent", basis: ["declared", "inferred"], action: "limit" },
],
fallback: "allow",
},
];นโยบายทุกตัวต้องมี fallback ที่ชัดเจนเสมอ นี่คือการกระทำที่ใช้กับผลลัพธ์ Unknown, กับ timeout และกับกรณีใด ๆ ที่ไม่มีกฎครอบคลุม การเลือกอย่างตั้งใจเป็นการตัดสินใจด้านความปลอดภัยที่สำคัญที่สุดในกระบวนการทั้งหมด
ขั้นที่ 3: ประเมินในที่เดียว
ฟังก์ชันประเมินเพียงตัวเดียวจะทำให้พฤติกรรมสอดคล้องกันระหว่างโหมดต่าง ๆ
export function decide(policy: RoutePolicy, actor: ActorClass, basis: Basis): Action {
for (const rule of policy.rules) {
const actorMatch = rule.actor === "*" || rule.actor === actor;
const basisMatch = !rule.basis || rule.basis.includes(basis);
if (actorMatch && basisMatch) return rule.action;
}
return policy.fallback;
}จากนั้นจึงนำโหมดไปใช้:
export function applyPolicy(policy: RoutePolicy, actor: ActorClass, basis: Basis, requestKey: string) {
const decision = decide(policy, actor, basis);
const inSlice = bucket(requestKey) < policy.enforcePercent;
const enforced = policy.mode === "enforce" && inSlice;
record({ route: policy.route, mode: policy.mode, actor, basis, decision, enforced });
return enforced ? decision : "allow";
}
function bucket(key: string): number {
let h = 0;
for (const c of key) h = (h * 31 + c.charCodeAt(0)) >>> 0;
return h % 100;
}stable bucket หมายความว่า session หรือ client เดิมจะอยู่ในหรืออยู่นอกส่วนที่บังคับใช้เหมือนเดิม ผู้ใช้จึงไม่เห็นพฤติกรรมสลับไปมาระหว่าง request
ขั้นที่ 4: คุณจะวัด false positive ก่อนบังคับใช้ได้อย่างไร?
ย้าย route หนึ่งไปที่ preview ตอนนี้ทุก request จะบันทึกการตัดสินที่มันน่าจะได้รับ นี่คือจุดที่คุณจะรู้ว่านโยบายถูกต้องหรือไม่
false positive คือ request ที่นโยบายจะจำกัดแต่จริง ๆ ควรถูกอนุญาต คุณวัดจาก classification อย่างเดียวไม่ได้ เพราะ classification นั่นแหละคือสิ่งที่คุณกำลังทดสอบอยู่ ให้วัดเทียบกับสัญญาณอิสระของการใช้งานที่ชอบธรรม:
- request ดำเนินการเสร็จใน flow ปกติที่สำเร็จ เช่น ผู้ใช้ที่ลงชื่อเข้าใช้แล้วบันทึกงาน,
- client เป็น integration ของพาร์ตเนอร์ที่รู้จักหรือเป็น operator ที่ยืนยันแล้วและคุณตกลงให้บริการ,
- ทิกเก็ตซัพพอร์ตหรือรายงานจากผู้ใช้กล่าวถึง route ที่ได้รับผลกระทบ,
- การตรวจทานด้วยคนของตัวอย่าง request ที่ถ้าเป็นจริงจะถูกจำกัด
ทบทวนตัวอย่างด้วยมือ การอ่าน request จริง 20 รายการที่นโยบายจะท้าทาย เป็นวิธีที่เร็วที่สุดในการหากฎที่กว้างเกินไป
เกณฑ์ออก: ชุดที่น่าจะถูกจำกัดมีทราฟฟิกที่คุณตั้งใจจะจำกัดจริง ๆ เป็นสัดส่วนหลัก และคุณมีคำอธิบายสำหรับทุกประเภทของ false positive ที่พบ
ขั้นที่ 5: บังคับใช้กับบางส่วน
สลับ route ไปที่ enforce ด้วย enforcePercent ขนาดเล็ก ให้ slice เล็กพอที่ถ้าพลาดจะกระทบ request เพียงไม่กี่รายการ และใหญ่พอที่จะสร้างสัญญาณที่วัดได้ภายในหนึ่งหรือสองวัน
ติดตามเมตริกเดิมที่คุณใช้เป็น baseline:
- อัตราผลลัพธ์ต่อ class บน route,
- อัตรา error และ challenge completion,
- ปริมาณซัพพอร์ตที่กล่าวถึง route นั้น,
- latency เพราะการประเมินอยู่บนเส้นทางของ request
หากผลลัพธ์ของทราฟฟิก Human ลดลงใน slice ที่บังคับใช้เมื่อเทียบกับส่วนอื่น ให้หยุดและตรวจสอบก่อนขยายต่อ
ขั้นที่ 6: ขยายอย่างค่อยเป็นค่อยไป
เพิ่มสัดส่วนทีละขั้น เช่น จากสัดส่วนเล็กไปหนึ่งในสี่ แล้วครึ่งหนึ่ง แล้วทราฟฟิกทั้งหมด โดยหยุดที่แต่ละขั้นให้นานพอที่จะเห็นรอบปกติ การขยายจะน่าเบื่อเมื่อขั้นก่อนหน้าทำได้ดี นั่นคือเป้าหมาย
การเลือก fallback และพฤติกรรมเมื่อเกิดความล้มเหลว
ตัดสินใจล่วงหน้าว่าเกิดอะไรขึ้นเมื่อ evidence หายไปหรือประเมินไม่ได้
- fail open หมายถึง request จะดำเนินต่อไปเหมือนเดิมโดยไม่มีบริบทของ actor ใช้กับ route ที่มีความเสี่ยงต่ำ เช่น หน้า public เอกสาร และ endpoint แบบอ่านอย่างเดียวในแคตตาล็อก
- fail closed หมายถึงมีการใช้การตรวจที่เข้มงวดกว่าที่มีอยู่แล้ว เช่น การยืนยันตัวตนซ้ำหรือคิวตรวจทาน ใช้กับการกระทำที่มีผลกระทบสูงซึ่งคุณมีการตรวจแบบนี้อยู่แล้ว
อย่าให้ timeout หรือ error สร้างผลลัพธ์เป็น Human ได้ evidence ที่หายไปคือ Unknown และ Unknown จะใช้ fallback ของ route นั้น บทความของเราเรื่อง ทำไม Unknown จึงเป็นผลลัพธ์ที่ถูกต้องเมื่อจัดประเภททราฟฟิก AI อธิบายว่าทำไมกฎนี้จึงสำคัญ
async function actorWithTimeout(evaluate: () => Promise<{ actor: ActorClass; basis: Basis }>) {
const timeout = new Promise<{ actor: ActorClass; basis: Basis }>((resolve) =>
setTimeout(() => resolve({ actor: "unknown", basis: "unknown" }), 150),
);
return Promise.race([evaluate(), timeout]);
}คุณจะย้อนกลับนโยบายได้อย่างรวดเร็วอย่างไร?
Rollback ควรเป็นการเปลี่ยนแปลง configuration ไม่ใช่การ deploy เก็บนโยบายไว้ในที่จัดเก็บที่ทีมของคุณแก้ไขได้อย่างรวดเร็ว และทำให้การกระทำเหล่านี้เกิดขึ้นทันที:
- ตั้ง route กลับไปที่ preview ซึ่งจะหยุดการบังคับใช้แต่ยังคงการวัดผลไว้
- ตั้ง
enforcePercentเป็นศูนย์ - ปิดใช้กฎเพียงข้อเดียวโดยไม่แตะกฎอื่น
บันทึกทุกการเปลี่ยนแปลงนโยบายพร้อมว่าใครเป็นผู้ทำและเมื่อไร เมื่อผลลัพธ์เปลี่ยน คำถามแรกคืออะไรที่เปลี่ยนไปเสมอ
หลีกเลี่ยงการตัดสินใจที่ส่งผลกระทบต่อคน
การจัดประเภท actor อธิบาย evidence เกี่ยวกับการโต้ตอบ ไม่ใช่การตัดสินคน อย่าใช้ class แบบ inferred เป็นฐานเดียวของการตัดสินใจที่กระทบการจ้างงาน เครดิต การศึกษา ที่อยู่อาศัย หรือการเข้าถึงบริการจำเป็น สำหรับ flow เหล่านั้น ให้ใช้บริบทของ actor เพื่อเลือกกระบวนการ เช่น ส่งไป human review แทนที่จะใช้เพื่อตัดสินผลลัพธ์
สื่อสารก่อนบังคับใช้
นโยบายที่กระทบ client อัตโนมัติยังกระทบคนและบริษัทที่ดูแลมันด้วย การสื่อสารเพียงเล็กน้อยช่วยลดงานซัพพอร์ตได้มาก:
- ประกาศกฎสำหรับการเข้าถึงแบบอัตโนมัติของคุณ ระบุว่า route ใดเปิดอยู่ มีข้อจำกัดอะไรบ้าง และ client จะระบุตัวเองด้วย signed requests อย่างไร โดยใช้ HTTP Message Signatures (RFC 9421) และ IETF ร่างโปรโตคอล Web Bot Auth.
- แจ้งพาร์ตเนอร์และ integrator ล่วงหน้า ถ้า integration ที่รู้จักตกอยู่ใน slice ที่ถูกจำกัดระหว่าง preview ให้ติดต่อเจ้าของก่อนที่คุณจะบังคับใช้
- ส่ง response ที่มีประโยชน์ เมื่อ policy จำกัดหรือท้าทาย request ให้ตอบด้วย status code ที่ชัดเจน เช่น
429 Too Many Requestsพร้อมRetry-Afterและลิงก์ไปยังเอกสารของคุณ body ที่อ่านได้ด้วยเครื่องในรูปแบบ Problem Details จะให้เหตุผลแบบมีโครงสร้างแก่ client อัตโนมัติ Agent ที่สร้างมาอย่างดีจะปรับตัวได้เมื่อเข้าใจว่าเพราะอะไร - ให้ evidence แก่ทีมซัพพอร์ตของคุณ เมื่อมีทิกเก็ตเกี่ยวกับ request ที่ถูกบล็อก ซัพพอร์ตควรดู route, class, basis และการตัดสินของนโยบายได้โดยไม่ต้องถามวิศวกรรม
ทีมที่ประกาศการเปลี่ยนแปลงและอธิบายมันจะเจอเรื่องเซอร์ไพรส์น้อยกว่าทีมที่สลับกฎแบบเงียบ ๆ และ client ที่ปรับตัวได้จะยิ่งระบุได้ง่ายขึ้นเมื่อเวลาผ่านไป
เช็กลิสต์สำหรับแต่ละ route
- บันทึก baseline สำหรับสัดส่วน class, อัตรา Unknown และผลลัพธ์แล้ว
- เขียนนโยบายเป็นข้อมูลพร้อม fallback ที่ชัดเจน
- รัน preview ตรวจตัวอย่าง และอธิบายหมวดหมู่ของ false positive แล้ว
- เริ่มบังคับใช้กับ slice เล็ก ๆ พร้อมติดตามเมตริก
- เลือกพฤติกรรมเมื่อเกิดความล้มเหลวแล้ว: fail open หรือ fail closed
- ทดสอบ rollback ก่อนขยาย
Proof of Actor เข้ามาอยู่ตรงไหน
Proof of Actor ออกแบบมารอบกระบวนการนี้ การสังเกตเป็นค่าเริ่มต้น ทุกผลลัพธ์พก basis ของมันมาด้วย และนโยบาย Route เป็นของคุณที่จะกำหนดและบังคับใช้ในโค้ดของคุณเอง ทีละ route Detect, Understand, Route ตามลำดับนี้
หากต้องการทำ rollout บนแอปพลิเคชันของคุณร่วมกับทีมของเรา สมัครขอสิทธิ์เข้าถึงก่อน สำหรับ flow การตัดสินแบบ end to end ให้อ่าน Proof of Actor ทำงานอย่างไร
แหล่งอ้างอิง
- RFC 6585: Additional HTTP Status Codes, Section 4: รหัสสถานะ
429 Too Many Requests - RFC 9110: HTTP Semantics, Retry-After: บอก client ว่าควรลองใหม่เมื่อไร
- RFC 9457: Problem Details for HTTP APIs: body ข้อผิดพลาดที่อ่านได้ด้วยเครื่อง
- RFC 9421: HTTP Message Signatures: วิธีที่ client อัตโนมัติลงนามใน request
- draft-ietf-webbotauth-httpsig-protocol: ร่าง IETF Web Bot Auth สำหรับทราฟฟิกอัตโนมัติที่ลงนามแล้ว (อยู่ระหว่างดำเนินการ ณ เดือนพฤศจิกายน 2026)