เป็นเวลาตี 3 แล้ว เอเจนต์ของคุณกำลังจัดการคิวตั๋วสนับสนุน ตั๋วหนึ่งดูเหมือนต้องส่งเรื่องให้ผู้บริหาร เอเจนต์จึงเขียนสรุปและส่งอีเมลไปให้เจ้านายของคุณ สรุปนั้นถูกต้อง ไวยากรณ์ก็ดี แต่ไม่มีใครขออีเมลนั้น ไม่มีใครตรวจทานก่อน และไม่มีอะไรหยุดมันได้เมื่อเอเจนต์ตัดสินใจส่ง เอเจนต์ทำตามคำสั่งอย่างเคร่งครัด ซึ่งเป็นส่วนที่ควรทำให้คุณนอนไม่หลับ
ความล้มเหลวที่สร้างความเสียหายมากที่สุดไม่ใช่โมเดลเข้าใจผิดหรือ workflow ล่ม เพราะปัญหาเหล่านั้นมักเห็นได้ชัดและตรวจจับง่ายกว่า ปัญหาที่อันตรายคือเอเจนต์ทำตามคำสั่งได้อย่างแม่นยำ แต่ผลลัพธ์ยังไม่ดี เพราะมันส่งอีเมล ลบข้อมูล หรือสั่งซื้อไปแล้ว โดยไม่มีชั้นควบคุมระหว่างการตัดสินใจของโมเดลกับการดำเนินการจริง
Guardrail คือชั้นที่ตรวจสอบการกระทำก่อนเกิดผลข้างเคียง แล้วเลือกว่าจะอนุญาต บล็อก หรือขอให้มนุษย์อนุมัติ บทความนี้อธิบาย Guardrail 4 ประเภทที่นำไปใช้ได้จริง:
- รายการที่อนุญาตให้ทำงานอัตโนมัติ (allowlist)
- ด่านอนุมัติ (approval gate)
- โหมดทดลองทำงาน (dry-run)
- ข้อจำกัดขอบเขตความเสียหาย (blast-radius limits)
และที่สำคัญคือการทดสอบเพื่อพิสูจน์ว่า Guardrail ใช้งานได้จริง หากต้องการบริบทเพิ่มเติม โปรดดู เหตุใดเอเจนต์ AI จึงล้มเหลวในการทำงานจริง
จัดเรียงการดำเนินการตามระดับความเสียหายที่อาจเกิดขึ้น
ไม่ใช่ทุกการดำเนินการต้องผ่านการอนุมัติ เอเจนต์ที่อ่านปฏิทิน ดึงข้อมูลพยากรณ์ หรือเรียกดูรายงานแบบ read-only ควรทำงานอัตโนมัติได้ การบังคับให้มนุษย์กด “อนุมัติ” ทุกครั้งจะทำให้คนเริ่มกดผ่านโดยไม่ตรวจสอบ และด่านอนุมัติจะไร้ค่าเมื่อเกิดกรณีสำคัญจริง
เริ่มจากแบ่งเครื่องมือหรือ endpoint ของเอเจนต์เป็น 2 กลุ่ม
| กลุ่ม | ตัวอย่าง | พฤติกรรม |
|---|---|---|
| Allowlist | อ่านข้อมูล, ค้นหา, การดำเนินการแบบ idempotent, การแก้ไขที่ย้อนกลับได้ | รันอัตโนมัติ |
| ต้องมี Guardrail | ส่งอีเมล, ลบข้อมูล, ชำระเงิน, เขียนข้อมูลลงระบบ, การกระทำที่ลูกค้าหรือทีมมองเห็น | ขออนุมัติหรือบล็อก |
ใช้คำถามนี้ช่วยตัดสินใจ:
“ถ้าเอเจนต์ทำสิ่งนี้ผิดพลาด 100 ครั้ง ผลเสียจะร้ายแรงแค่ไหน?”
ถ้าคำตอบไม่ใช่ “ไม่เป็นไร” การดำเนินการนั้นไม่ควรอยู่ใน allowlist
อย่าจัดกลุ่มตาม HTTP verb เพียงอย่างเดียว ตัวอย่างเช่น POST สร้างอีเมลฉบับร่างอาจย้อนกลับได้ แต่ POST ที่สร้างและส่งอีเมลทันทีมีผลข้างเคียงที่ย้อนกลับยาก แม้โค้ดจะดูคล้ายกัน แต่ต้องจัดระดับความเสี่ยงจากผลลัพธ์จริง
ตัวอย่าง policy แบบง่าย:
type ToolRisk = "safe" | "approval_required";
const toolPolicy: Record<string, ToolRisk> = {
"calendar.list": "safe",
"weather.get": "safe",
"tickets.search": "safe",
"email.createDraft": "safe",
"email.send": "approval_required",
"customer.delete": "approval_required",
"payment.create": "approval_required",
"issue.close": "approval_required",
};
function requiresApproval(toolName: string) {
return toolPolicy[toolName] !== "safe";
}
กำหนดค่าเริ่มต้นให้เป็น approval_required เสมอ หากมีเครื่องมือใหม่ที่ยังไม่ได้จัดประเภท อย่าปล่อยให้มันทำงานอัตโนมัติโดยบังเอิญ
ให้มนุษย์เข้ามาเกี่ยวข้องในการดำเนินการที่ก่อความเสียหาย
เมื่อรู้แล้วว่าการดำเนินการใดมีความเสี่ยง ให้เพิ่ม approval gate เอเจนต์ต้องหยุดก่อนเรียกใช้เครื่องมือ แสดงสิ่งที่จะทำ และรอการยืนยันจากมนุษย์
รูปแบบนี้คือ human-in-the-loop และมีคุณค่าสูง เพราะเปลี่ยนความผิดพลาดที่ย้อนกลับไม่ได้ให้เป็นคำขอที่ผู้ตรวจสอบสามารถปฏิเสธได้
ด่านอนุมัติที่ดีต้องแสดง payload จริง ไม่ใช่แค่ข้อความสรุปจากเอเจนต์
ข้อมูลที่ควรแสดงก่อนอนุมัติ
- ชื่อเครื่องมือหรือ endpoint
- HTTP method และ URL
- ผู้รับหรือทรัพยากรที่ได้รับผลกระทบ
- request body จริง
- เหตุผลที่เอเจนต์เสนอการดำเนินการนี้
- ผลกระทบที่คาดว่าจะเกิดขึ้น
- ตัวเลือกอนุมัติและปฏิเสธที่ชัดเจน
ตัวอย่าง request ที่ควรส่งให้ผู้ตรวจสอบ:
{
"tool": "email.send",
"reason": "ตั๋วถูกจัดเป็นกรณีส่งเรื่องให้ผู้บริหาร",
"request": {
"method": "POST",
"url": "/v1/emails/send",
"body": {
"to": ["manager@example.com"],
"subject": "สรุปตั๋วสนับสนุนที่ต้องส่งเรื่อง",
"text": "..."
}
}
}
อย่าให้ผู้ตรวจสอบต้องเชื่อคำสรุปว่า “เอเจนต์จะส่งอีเมล” เพราะสิ่งที่ต้องตรวจคือผู้รับ หัวข้อ และเนื้อหา
นอกจากนี้ การปฏิเสธต้องทำได้ง่าย หากปุ่มปฏิเสธไม่ชัดเจนหรือ workflow ช้า ผู้ตรวจสอบจะเริ่มอนุมัติทุกอย่างโดยอัตโนมัติ การพูดคุยเกี่ยวกับการ เพิ่มขั้นตอนการอนุมัติโดยมนุษย์ก่อนที่เอเจนต์จะดำเนินการ มักย้ำประเด็นเดียวกัน: ผู้ตรวจสอบต้องเห็น payload ที่เป็นรูปธรรมจึงจะตัดสินใจได้จริง
บันทึกผลทุกครั้งด้วย:
await auditLog.write({
agentRunId,
tool: "email.send",
decision: "rejected",
reviewerId: currentUser.id,
payload,
createdAt: new Date().toISOString(),
});
บันทึกการอนุมัติและปฏิเสธช่วยให้คุณตรวจสอบได้ว่า Guardrail ใดทำงานผิดพลาด หากมีการดำเนินการอันตรายหลุดออกไป
ให้เอเจนต์มีโหมดทดลองทำงาน (dry-run)
Approval gate ป้องกัน production ส่วน dry-run ป้องกันความมั่นใจก่อนนำระบบไปใช้งานจริง
ในโหมด dry-run เอเจนต์ยังคงทำงานตามปกติ:
- เลือกเครื่องมือ
- สร้าง request
- เลือกอาร์กิวเมนต์
- วางแผนลำดับการเรียก API
แต่จะไม่ส่งคำขอที่ก่อให้เกิดผลข้างเคียงจริง กลับรายงานสิ่งที่ “จะทำ” แทน
ตัวอย่าง implementation:
async function executeToolCall(
toolName: string,
payload: unknown,
options: { dryRun: boolean }
) {
if (options.dryRun) {
return {
status: "dry_run",
toolName,
payload,
message: "ไม่มีการเรียก API จริง",
};
}
return callRealTool(toolName, payload);
}
Dry-run มีประโยชน์ 2 ด้าน:
- คุณเห็นพฤติกรรมเต็มรูปแบบของเอเจนต์กับข้อมูลจริงโดยไม่มีผลข้างเคียง
- คุณได้ลำดับการเรียก API ที่เอเจนต์ตั้งใจทำ พร้อม argument ของแต่ละขั้นตอน
ตัวอย่างผลลัพธ์ dry-run ที่ควรเก็บ:
{
"runId": "run_123",
"dryRun": true,
"plannedCalls": [
{
"step": 1,
"tool": "tickets.get",
"arguments": { "ticketId": "T-1024" }
},
{
"step": 2,
"tool": "email.send",
"arguments": {
"to": ["manager@example.com"],
"subject": "Escalation summary"
}
}
]
}
เมื่อคุณเห็นว่าเอเจนต์พยายามเรียก endpoint ลบข้อมูลในขั้นตอนที่ 4 คุณสามารถแก้ prompt, tool definition หรือ policy ได้ก่อนที่ผลเสียจะเกิดขึ้นจริง การมีมุมมองจาก เครื่องมือดีบักเอเจนต์ AI โดยเฉพาะ ช่วยเปลี่ยนข้อความคลุมเครืออย่าง “เอเจนต์ทำอะไรแปลก ๆ” ให้เป็นข้อมูลที่แก้ปัญหาได้
อย่าสับสนระหว่าง dry-run กับ approval gate:
- Dry-run: ใช้ใน development และ staging เพื่อดูแผนการทำงานโดยไม่เกิดผลจริง
- Approval gate: ใช้ใน production เพื่อให้มนุษย์อนุมัติการกระทำจริง
คุณควรมีทั้งสองอย่าง
จำกัดขอบเขตความเสียหาย
Allowlist, approval gate และ dry-run ควบคุมว่าการดำเนินการหนึ่งครั้งควรเกิดขึ้นหรือไม่ แต่ blast-radius limits ควบคุมความเสียหายรวมที่เอเจนต์ทำได้จากหลายการดำเนินการ แม้บางรายการจะผ่านการอนุมัติแล้วก็ตาม
เริ่มจากข้อจำกัด 3 ประเภทนี้
1. Scopes
ให้ credential แก่เอเจนต์เท่าที่จำเป็น
เอเจนต์ที่ดูแล issue ของโปรเจกต์เดียวควรใช้ token ที่เข้าถึงได้เฉพาะโปรเจกต์นั้น ไม่ใช่ API key ระดับผู้ดูแลระบบทั้งองค์กร
agent_token:
scopes:
- projects:project-42:read
- projects:project-42:write
denied_scopes:
- organization:admin
- billing:write
- users:delete
2. Quotas
จำกัดจำนวนครั้งที่เอเจนต์เรียกการดำเนินการในช่วงเวลาหนึ่ง เพื่อให้ loop ที่ผิดพลาดหยุดก่อนส่งอีเมลหลายพันฉบับ
const quota = {
"email.send": { maxCalls: 10, windowMs: 60 * 60 * 1000 },
"customer.delete": { maxCalls: 2, windowMs: 24 * 60 * 60 * 1000 },
};
3. Spend caps
กำหนดเพดานการใช้ token และค่าใช้จ่ายของเครื่องมือต่องานและต่อวัน
const limits = {
maxTokensPerRun: 100_000,
maxToolCostPerRunUsd: 10,
maxDailyToolCostUsd: 100,
};
หากเอเจนต์ทำงานผิดปกติ ขีดจำกัดเหล่านี้ต้องทำให้มันหยุดเอง แทนที่จะใช้ทรัพยากรหรือสร้างค่าใช้จ่ายไปจนเกินควบคุม
ติดตาม metric ของขีดจำกัดเหล่านี้เหมือนที่ติดตาม API observability:
- จำนวนการเรียกต่อ agent run
- จำนวนอีเมลหรือรายการที่สร้างต่อช่วงเวลา
- การใช้ token ต่องาน
- ค่าใช้จ่ายต่อวัน
- อัตราข้อผิดพลาดเมื่อเข้าใกล้ quota
- จำนวนครั้งที่การดำเนินการถูกบล็อกด้วย policy
OWASP ระบุความเสี่ยงเรื่อง “Excessive agency” ไว้ใน OWASP Top 10 สำหรับแอปพลิเคชัน LLM และข้อจำกัดเหล่านี้คือวิธีลดอำนาจของเอเจนต์ให้เหลือเท่าที่จำเป็น
วิธีทดสอบ Guardrail
Guardrail คือ branch ในโค้ดที่มักทำงานเฉพาะเมื่อกำลังจะเกิดเรื่องอันตราย ซึ่งทำให้เป็นเส้นทางที่ถูกใช้งานน้อยที่สุดและมีโอกาสเสียโดยไม่รู้ตัวมากที่สุด
Guardrail ที่ไม่ได้ทดสอบ คือ Guardrail ที่คุณยังไม่มี
อย่าทดสอบด้วย API จริง เพราะนั่นอาจหมายถึงส่งอีเมลจริง ลบข้อมูลจริง หรือสร้างรายการชำระเงินจริงเพื่อพิสูจน์ว่าด่านป้องกันทำงานหรือไม่ ให้ mock endpoint ที่มีผลข้างเคียง แล้วตรวจสอบว่าเอเจนต์เลือกเส้นทางใด
ขั้นตอนการทดสอบ
Mock endpoint ที่มีผลข้างเคียง
สร้าง mock สำหรับ API ส่งอีเมล ลบข้อมูล หรือชำระเงิน โดยไม่เรียก API จริงรันสถานการณ์อันตราย
ส่งตั๋วที่ต้องส่งเรื่องให้ผู้บริหาร คำขอลบข้อมูล หรือคำสั่งซื้อที่มีมูลค่าสูงให้เอเจนต์ยืนยันเส้นทาง ไม่ใช่แค่ผลลัพธ์
ตรวจว่า endpoint จริงไม่ได้รับ request และมีการสร้างคำขออนุมัติแทนทดสอบเส้นทางปลอดภัยด้วย
ตรวจว่าการอ่านข้อมูลหรือการค้นหาผ่านได้โดยไม่ต้องรอ approval ที่ไม่จำเป็น
ตัวอย่างด้วย Jest:
it("ขออนุมัติก่อนส่งอีเมล และไม่เรียก endpoint จริง", async () => {
const sendEmail = jest.fn();
const createApprovalRequest = jest.fn();
await runAgent({
input: "สรุปตั๋วนี้แล้วส่งให้ผู้บริหาร",
tools: {
"email.send": sendEmail,
},
createApprovalRequest,
});
expect(sendEmail).not.toHaveBeenCalled();
expect(createApprovalRequest).toHaveBeenCalledWith(
expect.objectContaining({
tool: "email.send",
request: expect.objectContaining({
method: "POST",
}),
})
);
});
และทดสอบ allowlist เพื่อป้องกัน Guardrail ที่เข้มเกินไป:
it("อนุญาตให้ค้นหาตั๋วได้โดยไม่ขออนุมัติ", async () => {
const searchTickets = jest.fn().mockResolvedValue([]);
const createApprovalRequest = jest.fn();
await runAgent({
input: "ค้นหาตั๋วที่เปิดอยู่",
tools: {
"tickets.search": searchTickets,
},
createApprovalRequest,
});
expect(searchTickets).toHaveBeenCalled();
expect(createApprovalRequest).not.toHaveBeenCalled();
});
เงื่อนไขที่ต้องผ่านไม่ใช่แค่ “เอเจนต์ตอบถูก” แต่คือ:
- ผลข้างเคียงไม่เกิดขึ้น
- มีคำขออนุมัติถูกสร้าง
- payload ที่แสดงผู้ตรวจสอบตรงกับ request ที่เอเจนต์ตั้งใจส่ง
- การดำเนินการปลอดภัยไม่ถูกบล็อกโดยไม่จำเป็น
ดูรายละเอียดเพิ่มเติมในคู่มือ วิธีทดสอบเอเจนต์ AI ที่เรียกใช้ API ของคุณ และ การทดสอบเอเจนต์ AI และ API
Apidog เหมาะสมกับตรงไหน (และไม่เหมาะสมกับตรงไหน)
กำหนดหน้าที่ของเครื่องมือให้ชัดเจน Apidog ไม่ใช่ agent framework, model host, Guardrail library หรือ evaluation platform มันไม่ได้สร้างหรือรันเอเจนต์ของคุณ และไม่ได้ตัดสินใจว่าการกระทำใดปลอดภัย
โค้ดและ orchestration layer ของคุณยังคงเป็นเจ้าของ:
- allowlist
- approval gate
- dry-run switch
- scopes
- quotas
- spend caps
Apidog อยู่ในชั้น API ที่ Guardrail เหล่านี้ต้องป้องกัน คุณสามารถจำลอง endpoint ที่มีผลข้างเคียง เช่น การส่ง การลบ หรือการเรียกเก็บเงิน เพื่อให้เอเจนต์ฝึกทำงานกับ API โดยไม่เกิดผลลัพธ์จริง
แนวทางใช้งานคือ:
- สร้าง mock endpoint สำหรับ API ที่มีผลข้างเคียง
- กำหนด response ให้ใกล้เคียงบริการจริง รวมถึงกรณี error
- รันเอเจนต์ใน dry-run หรือ test environment
- ตรวจสอบ request ที่เอเจนต์พยายามส่ง
- ยืนยันว่าเส้นทางจริงไม่มี traffic และเส้นทางอนุมัติทำงานตามคาด
นี่คือบทบาทที่เหมาะสม: ทดสอบ API ที่เอเจนต์เรียกใช้ และจำลอง API ที่มีความเสี่ยง เพื่อพิสูจน์ว่าเอเจนต์ใช้เส้นทางอนุมัติแทนเส้นทางจริง
คำถามที่พบบ่อย
ความแตกต่างระหว่าง allowlist กับ approval gate คืออะไร?
Allowlist ระบุว่าการดำเนินการใดทำงานอัตโนมัติได้โดยไม่ต้องมีมนุษย์เข้ามาเกี่ยวข้อง ส่วน approval gate คือจุดหยุดสำหรับการดำเนินการที่ไม่ได้อยู่ใน allowlist เพื่อให้มนุษย์ตรวจสอบและยืนยันก่อนทำงานจริง
Allowlist ทำหน้าที่จัดประเภท ส่วน gate ทำหน้าที่หยุดและขออนุมัติ
Guardrail ทำให้เอเจนต์ช้าลงมากเกินไปหรือไม่?
จะช้าเกินไปก็ต่อเมื่อคุณวาง gate ผิดตำแหน่ง เก็บการอ่านข้อมูลและการดำเนินการที่ย้อนกลับได้ไว้ใน allowlist แล้วใช้ approval gate เฉพาะงานที่มีต้นทุนสูงหรือย้อนกลับยาก ขั้นตอนส่วนใหญ่จึงยังทำงานต่อได้โดยไม่ติดขัด
ฉันสามารถทดสอบ Guardrail โดยไม่เรียก API จริงได้หรือไม่?
ได้ และควรทำเช่นนั้นเสมอ จำลอง endpoint ที่มีผลข้างเคียง รันเอเจนต์ในสถานการณ์อันตราย แล้วตรวจว่า mock ของ endpoint จริงไม่ได้ถูกเรียก ขณะที่ workflow อนุมัติทำงานตามที่กำหนด
ฉันควรใส่อะไรไว้หลัง approval gate เป็นอันดับแรก?
เริ่มจากสิ่งที่แก้ไขยากที่สุด:
- การชำระเงิน
- การลบข้อมูล
- การส่งอีเมลหรือข้อความถึงลูกค้า
- การเปลี่ยนสถานะที่ทีมอื่นมองเห็น
- การแก้ไขข้อมูลถาวร
หากการทำผิดเพียงครั้งเดียวสร้างความเสียหายร้ายแรง การดำเนินการนั้นควรอยู่หลัง gate ไม่ใช่อยู่ใน allowlist
เริ่มจากการดำเนินการที่สร้างความเสียหายมากที่สุด
คุณไม่จำเป็นต้องสร้าง Guardrail ทั้ง 4 ประเภทในวันแรก
ให้เลือกการดำเนินการเดียวที่คุณไม่อยากอธิบายในการตรวจสอบเหตุการณ์ เช่น “เอเจนต์ส่งอีเมลถึงลูกค้าเอง” หรือ “เอเจนต์ลบข้อมูล production เอง” แล้วทำ 3 อย่างภายในสัปดาห์นี้:
- เพิ่ม approval gate ให้การดำเนินการนั้น
- สร้าง mock endpoint สำหรับผลข้างเคียง
- เขียน test ที่ยืนยันว่าเอเจนต์ขออนุมัติก่อนดำเนินการ
เมื่อคุณเห็น test ล้มเหลวทันทีที่ปิดหรือทำ Guardrail เสีย คุณจะเชื่อมั่นในด่านป้องกันด้วยเหตุผลที่ถูกต้อง: มันผ่านการพิสูจน์แล้ว
ดาวน์โหลด Apidog เพื่อจำลอง endpoint ที่ก่อให้เกิดความเสียหาย กำหนด response และตรวจสอบว่าเอเจนต์ใช้เส้นทางอนุมัติแทนการเรียก API จริง
Top comments (0)