Excessive agency & least privilege: หด blast radius เมื่อ detection พลาด
บท4 (input guard) กับบท5 (output guard) เป็นชั้น detection — มันพยายาม จับ injection ก่อน loop จะ dispatch tool และ กรอง output ก่อนมันไหลออก แต่ honesty spine ของคอร์สนี้พูดตรงๆ มาตั้งแต่บท1: detection ไม่มีทางสมบูรณ์ — Anthropic วัดการป้องกันที่ดีที่สุดของตัวเองแล้วยังเหลือ ~1% ที่ลอดได้ และ 1% นั้นคือ “meaningful risk” คำถามของบทนี้จึงไม่ใช่ “จะจับให้ได้ 100% ยังไง” (จับไม่ได้) แต่เป็น — เมื่อ injection ลอดทุกชั้น detection แล้วสั่งให้ agent issueRefund ให้ผู้โจมตี ความเสียหายจะใหญ่แค่ไหน? บทนี้คือชั้น containment: ลดสิ่งที่ agent ทำได้ ให้เหลือน้อยที่สุด แล้วคร่อม action ที่อันตรายที่สุดด้วยด่านมนุษย์
บทนี้ต่อยอด agent Order ตัวเดิม (tool getOrder / getDeliveryStatus / issueRefund จาก #15, resilient runtime จาก #17) — repo kaen-food-ordering (กำลังจัดทำ) เราไม่เพิ่ม tool ใหม่ แต่ หด สิทธิ์ของ tool ที่มี แล้วห่อ issueRefund ด้วย approval gate ทุกกติกา API ฝั่งแชตจาก #15 ยังยึดเดิมทุกข้อ — model id เป็น alias เปล่า claude-opus-4-8 (ลงวันที่ 2026-07 กำกับ ไม่ต่อ suffix) และ native approval surface (ApprovalRequiredAIFunction ฯลฯ) ยืนยันไว้กับ Microsoft.Extensions.AI version 10.7.0 — re-pin กับ version ที่อยู่ใน .csproj จริงตอน build (คอร์สนี้เดินบน 10.8.0 GA — เช็กว่าชื่อ type/member ยังตรง)
Excessive agency: LLM06 — agent ทำได้มากเกินงาน
หัวข้อที่มีชื่อว่า “Excessive agency: LLM06 — agent ทำได้มากเกินงาน”Excessive AgencyExcessive AgencyOWASP LLM06 — agent มี functionality, permission หรือ autonomy 'มากเกินงานที่ต้องทำ' เมื่อศัตรูบิด output ได้สำเร็จ agent จึงลงมือทำสิ่งอันตรายจริงได้ สามรากเหตุ: excessive functionality (tool ที่ไม่จำเป็น เช่น general runSql/httpGet), excessive permissions (credential กว้างเกิน), excessive autonomy (issueRefund ยิงเองโดยไม่มีคนยืนยัน) — รักษาด้วย least privilege + approval gate + blast-radius capProcess คือช่องโหว่ที่ agent มี functionality, permissions หรือ autonomy มากเกินกว่าที่งานต้องการ — พอผู้โจมตีบิด output ของ model ได้ (ผ่าน injection ที่ลอดบท4/5 มา) agent ก็ลงมือทำสิ่งที่เป็นอันตรายจริง OWASP LLM06 ตั้งต้นจากข้อเท็จจริงที่ว่า agent มีสิทธิ์ลงมือ อยู่แล้ว (S6, verbatim):
“An LLM-based system is often granted a degree of agency by its developer – the ability to call functions or interface with other systems via extensions.”
OWASP แยก root cause ออกเป็นสามอย่าง map แต่ละอันลงบน agent Order แล้วจะเห็นว่าแต่ละอันมีทางรักษาต่างกัน:
- excessive functionality (ความสามารถเกิน) — agent มี tool ที่งานไม่ต้องใช้ ทางรักษา: อย่าให้ tool ที่ไม่จำเป็น
getOrder/getDeliveryStatus/issueRefundเป็นชุด minimal อยู่แล้ว — บทนี้สอนว่าทำไมคุณ ไม่เคย เพิ่ม tool กว้างๆ อย่างrunSqlหรือhttpGet - excessive permissions (สิทธิ์เกิน) — tool ที่มี รันด้วย credential ที่กว้างเกิน ทางรักษา:
issueRefundได้ credential เฉพาะของตัวเอง มี cap ยอดเงินฝั่ง server ไม่ใช่ ambient authority ของทั้ง agent - excessive autonomy (อิสระเกิน) —
issueRefundยิงได้เองโดยไม่มีมนุษย์ยืนยัน ทางรักษา: ด่านอนุมัติโดยมนุษย์ — control เอกของบทนี้
Least privilege: ตัดความสามารถให้เหลือเท่างาน
หัวข้อที่มีชื่อว่า “Least privilege: ตัดความสามารถให้เหลือเท่างาน”Least PrivilegeLeast Privilegeหลักให้ agent และ tool ถือสิทธิ์ 'น้อยที่สุดเท่าที่งานต้องใช้' — ไม่เพิ่ม general tool, ให้ issueRefund ใช้ scoped credential + server-side amount cap แทน ambient authority, เปิด tool แบบ contextual/dynamic (เสนอ refund tooling เฉพาะตอนบทสนทนาเป็นเรื่อง refund จริง — SAIF, S18) Anthropic: 'so that a successful injection can do minimal damage' — เป็น upper bound ของ blast radius เมื่อ detection พังArchitecture คือหลักว่า agent ควรถือสิทธิ์และความสามารถ น้อยที่สุดเท่าที่งานต้องการ — ไม่ใช่มากที่สุดเท่าที่สะดวก Anthropic วางหลักนี้เป็นตัวจำกัดความเสียหายของ injection โดยตรง (S13, verbatim):
“Apply the principle of least privilege so that a successful injection can do minimal damage: don’t give Claude access to secrets it doesn’t need, run tools in sandboxed environments, and scope permissions as narrowly as possible.”
สังเกตวลี “so that a successful injection can do minimal damage” — นี่คือ mindset ของทั้งบท least privilege ไม่ได้พยายาม กัน injection (บท4/5 ทำหน้าที่นั้นและทำไม่สมบูรณ์) แต่มัน สมมติว่า injection สำเร็จแล้ว แล้วถามว่าความเสียหายจะเล็กแค่ไหน
จุดที่ทำพลาดกันบ่อยคือ static tool exposure — ยัด tool ทุกตัวเข้า ChatOptions.Tools ทุกเทิร์นไม่ว่าบทสนทนาจะเป็นเรื่องอะไร ทั้งที่ 90% ของ session เป็นแค่ “ออเดอร์ฉันอยู่ไหน” ที่ไม่ต้องแตะ issueRefund เลย ทางที่ดีกว่าคือ contextual / dynamic exposure — เปิด tool คืนเงินให้ model เห็น ต่อเมื่อ บทสนทนากลายเป็นเรื่องขอคืนเงินจริงๆ Google SAIF วางหลักนี้ว่าให้ใช้ least privilege เป็น ขอบบน ของสิทธิ์ agent และสิทธิ์ควรเป็นแบบ “contextual and dynamic” (S18) — tool ที่ model ไม่เห็น คือ tool ที่ injection สั่งเรียกไม่ได้
Approval gate: ด่านมนุษย์บน action ที่ย้อนคืนไม่ได้ — native เป็นทางหลัก
หัวข้อที่มีชื่อว่า “Approval gate: ด่านมนุษย์บน action ที่ย้อนคืนไม่ได้ — native เป็นทางหลัก”Approval GateApproval Gatehuman-in-the-loop ที่กั้น action สำคัญ/ย้อนไม่ได้ (issueRefund) ให้ 'คน' อนุมัติก่อน — path หลักคือ native: ApprovalRequiredAIFunction → ToolApprovalRequestContent → ToolApprovalResponseContent (FunctionInvokingChatClient เคารพ marker) ⚠️ ApprovalRequiredAIFunction แค่ MARK ไม่ enforce ('it is the responsibility of the invoker to obtain that approval') — invoker ต้อง block รอคนจริง ๆ เอง และเพราะ approval stickiness ให้ตั้ง AllowMultipleToolCalls=false; gate เฉพาะ action สำคัญ ไม่ใช่ทุกอย่าง (approval fatigue ~93% อนุมัติผ่าน, S17)Architecture คือด่านที่ หยุด loop ไว้ ก่อน action ที่มีผลข้างเคียงจะรัน แล้วส่งการตัดสินใจให้มนุษย์ยืนยัน OWASP LLM06 เรียกมันตรงๆ (S6, verbatim):
“Utilise human-in-the-loop control to require a human to approve high-impact actions.”
Microsoft.Extensions.AI มี native surface สำหรับด่านนี้ตั้งแต่ 10.7.0 — และมันคือ ทางหลัก (นี่เป็นการแก้โน้ตเก่าของ #17 ที่บอกว่า “hand-roll ไว้ก่อน”) วิธีคือ mark tool ที่ต้องขออนุมัติด้วย ApprovalRequiredAIFunction แล้ว FunctionInvokingChatClient (ตัว loop จาก #15) จะ ไม่ รัน tool นั้นเอง แต่จะ คาย คำขออนุมัติกลับออกมาให้ app แทน:
using Microsoft.Extensions.AI;
AIFunction issueRefundFn = AIFunctionFactory.Create(IssueRefund);
var options = new ChatOptions{ Tools = [ getOrder, // read-only — เปิดได้อิสระ ไม่ผ่าน gate getDeliveryStatus, // read-only — sensor รันซ้ำได้ไม่เจ็บ new ApprovalRequiredAIFunction(issueRefundFn) // write — mark ว่าต้องมนุษย์อนุมัติ ], AllowMultipleToolCalls = false // จำเป็น — กัน caveat "stickiness" ด้านล่าง};เมื่อ loop ไปเจอ issueRefund มันจะ ไม่ ยิงคืนเงิน แต่คืน message ที่มี ToolApprovalRequestContent (พก tool call + correlation id) กลับออกมา loop หยุดค้างไว้ — เงินยัง ไม่ ถูกคืน app ของคุณเอาคำขอนั้นไปถามมนุษย์ ได้ yes/no แล้วส่งกลับเข้า history เป็น ToolApprovalResponseContent:
// ctor: (string requestId, bool approved, ToolCallContent toolCall)var decision = new ToolApprovalResponseContent( request.RequestId, // จับคู่กับคำขอเดิม approved: humanSaidYes, // ผลจากมนุษย์จริง ไม่ใช่จาก model request.ToolCall); // tool call เดิมที่รอ
messages.Add(new ChatMessage(ChatRole.User, [decision])); // ToolApprovalResponseContent : InputResponseContent → เป็นคำตอบของ "ผู้ใช้" ต่อคำขออนุมัติ จึงพกด้วย role User (ยืนยัน role กับ version Microsoft.Extensions.AI ที่ pin ไว้)// ส่ง history กลับเข้า loop → FunctionInvokingChatClient รันต่อ:// approved:true → เรียก IssueRefund จริง// approved:false → ข้าม ไม่เรียก(1) ApprovalRequiredAIFunction แค่ mark — มัน ไม่ บังคับ เอกสาร Microsoft พูดตรงๆ (S31, verbatim): “It does not enforce the requirement for user approval; it is the responsibility of the invoker to obtain that approval.” ด่านเป็นของจริง ก็ต่อเมื่อ สองอย่างครบ: FunctionInvokingChatClient เคารพ marker (คายคำขอแทนที่จะรัน) และ app ของคุณ block รอมนุษย์จริงๆ ถ้า app เห็น ToolApprovalRequestContent แล้ว auto-approve เอง คุณก็ได้ gate ที่ไม่กันอะไรเลย
(2) approval “stickiness” — เอกสาร FunctionInvokingChatClient เตือนไว้ (S30, verbatim): “if any FunctionCallContent is received for a function that requires approval, all received FunctionCallContent in that same response will also require approval, even if they were not ApprovalRequiredAIFunction instances.” แปลว่าถ้า model ขอเรียกหลาย tool ในเทิร์นเดียวและมีตัวหนึ่งต้องอนุมัติ ทุกตัว ในเทิร์นนั้นจะติด flag อนุมัติไปด้วย ทางกัน: ตั้ง ChatOptions.AllowMultipleToolCalls = false เพื่อบังคับให้หนึ่งเทิร์นขอได้ทีละ tool
fallback: hand-rolled gate — “ข้างใต้มันทำงานยังไง”
หัวข้อที่มีชื่อว่า “fallback: hand-rolled gate — “ข้างใต้มันทำงานยังไง””native surface สะดวก แต่ทุกวิศวกรควรรู้ว่ามันทำอะไรข้างใต้ — และเมื่อคุณไม่ได้อยู่บน FunctionInvokingChatClient (เช่น hand-rolled loop ของ #15 บท6) คุณต้องทำเอง: ลงทะเบียน issueRefund เป็น declaration อย่างเดียว (ไม่ invocable) เพื่อให้ loop ส่ง FunctionCallContent กลับมาแทนที่จะรัน แล้ว คุณ ตรวจ call นั้น ขอมนุษย์ยืนยัน out-of-band รัน side effect จริงเอง แล้วป้อน FunctionResultContent กลับ — คุมได้มากกว่า แต่ boilerplate เยอะกว่า รูปนี้แบก shape เดิมของ hand-rolled loop จาก #15/#17 มาตรงๆ
Confused deputy: การ ตัดสินใจเรียก ไม่ใช่ authorization
หัวข้อที่มีชื่อว่า “Confused deputy: การ ตัดสินใจเรียก ไม่ใช่ authorization”ต่อให้มีด่านอนุมัติ ก็ยังมีกับดักหนึ่งที่ทำให้ gate เปล่าประโยชน์ — Confused DeputyConfused Deputyagent ถูกหลอกให้ใช้ authority ของมันทำสิ่งที่ผู้โจมตีต้องการ — 'การตัดสินใจเรียก' issueRefund ของ model 'ไม่เคย' เป็นขอบเขต authorization; authz ต้องอยู่ใน tool handler (complete mediation): 'caller คนนี้ได้รับอนุญาตให้ refund order นี้ ในวงเงินนี้ ให้ลูกค้าคนนี้ไหม' เช็คซ้ำใน code #14 สอน confused deputy ที่ tool-server boundary — นี่คือ corollary ระดับ agentProcess คือสภาพที่ agent (ผู้มีสิทธิ์) ถูกหลอกให้ใช้สิทธิ์นั้นแทนผู้โจมตี การที่ model ตัดสินใจเรียก issueRefund ไม่เคยเป็นเส้นแบ่ง authorization — ถ้าคุณเชื่อการตัดสินใจของ model ว่าเป็นการอนุญาต injection ที่ลอดเข้ามาก็สั่งคืนเงินได้ทันที OWASP LLM06 วางกฎนี้ว่า complete mediation (S6, verbatim):
“Implement authorization in downstream systems rather than relying on an LLM to decide if an action is allowed.”
authorization ต้องอยู่ใน handler ของ tool ไม่ใช่ในหัว model — ภายใน IssueRefund ตัวจริง ตรวจซ้ำใน code ในบริบทของ ผู้ใช้คนนี้: caller คนนี้มีสิทธิ์คืนเงินออเดอร์นี้ไหม? ยอด ≤ cap ไหม? ออเดอร์นี้เป็นของ customer คนนี้จริงไหม? — ตรวจใหม่ทั้งหมด ไม่ว่า model จะตัดสินใจอะไร #14 สอน confused deputy ที่ขอบ tool-server; บทนี้คือ corollary ระดับ agent: marker ของ gate กับการตรวจ authz ใน handler เป็น สองชั้น ไม่มีชั้นไหนชั้นเดียวพอ
Blast radius: reuse idempotency ของ #17 เป็น adversarial containment
หัวข้อที่มีชื่อว่า “Blast radius: reuse idempotency ของ #17 เป็น adversarial containment”Blast RadiusBlast Radiusขอบเขตความเสียหายเมื่อการโจมตี 'ลอด' detection สำเร็จ — least privilege + approval gate + refund cap + rate limit + idempotency key (reuse #17 เป็น adversarial containment) ทำให้ loop ที่ถูก hijack ยิง refund ซ้ำหรือเกินวงเงินไม่ได้ OWASP พูดตรง ๆ ว่ามาตรการเหล่านี้ 'will not prevent Excessive Agency, but can limit the level of damage caused' — การหด blast radius เมื่อ detection พังคือหัวใจของ containmentArchitecture คือ ขอบเขตความเสียหาย เมื่อ action หลุดออกไป — และ containment คือศิลปะของการทำให้มันเล็ก OWASP LLM06 ซื่อสัตย์ตรงจุดนี้มาก มันบอกว่า logging กับ rate-limiting (S6, verbatim):
“will not prevent Excessive Agency, but can limit the level of damage caused.”
“will not prevent … but can limit” — นี่คือ honesty spine ของทั้งคอร์สในประโยคเดียว containment ไม่ หยุด injection แต่มัน หด ความเสียหาย 3 primitive ที่ประกอบเข้าด้วยกัน:
- refund cap ฝั่ง server — ยอดคืนต่อครั้ง/ต่อ session มีเพดานตายตัวใน code handler injection สั่งคืน 999,999 ไม่ได้ถ้า cap คือยอดออเดอร์
- rate limit — OWASP บอกตรงๆ (S6, verbatim): “Implement rate-limiting to reduce the number of undesirable actions that can take place within a given time period.” loop ที่ถูก hijack ยิงคืนเงินรัวๆ ไม่ได้ถ้าจำกัด N ครั้งต่อหน้าต่างเวลา
- idempotency key ของ #17 — reuse ตรงๆ แต่เปลี่ยน threat model: #17 ใช้มันกัน ดับเบิลคลิกโดยบังเอิญ; บทนี้ใช้มันกัน replay โดยเจตนา — ผู้โจมตีที่ยิงคำขอเดิมซ้ำๆ ได้ผลแค่ครั้งเดียวถ้า handler เป็น idempotent primitive ความทนทานจาก #17 กลายเป็น adversarial containment โดยไม่ต้องเขียนใหม่
Anthropic วางเรื่องเดียวกันในภาษาของ agent loop — pause เพื่อให้มนุษย์เข้ามาที่ checkpoint และตั้งเงื่อนไขหยุดเพื่อคุม loop (S16, verbatim): “Agents can then pause for human feedback at checkpoints or when encountering blockers.” และ “It’s also common to include stopping conditions (such as a maximum number of iterations) to maintain control.”
approval fatigue: “gate ทุกอย่าง” คือ gate ปลอม
หัวข้อที่มีชื่อว่า “approval fatigue: “gate ทุกอย่าง” คือ gate ปลอม”มีความจริงเชิงมนุษย์ที่ต้องสอน ไม่งั้นคุณจะออกแบบ gate ที่พัง— ยิ่งขึ้น prompt อนุมัติบ่อย คนยิ่งกดผ่านโดยไม่อ่าน Anthropic เปิด telemetry ของ Claude Code เองว่าผู้ใช้กดอนุมัติ prompt ราว ~93% (S17) ถ้า agent เด้ง prompt ทุกครั้งที่จะอ่านออเดอร์ มนุษย์จะเรียนรู้ที่จะกด “yes” อัตโนมัติ แล้วพอ prompt ที่สำคัญจริง (คืนเงินก้อนใหญ่) โผล่มา มันก็ถูก rubber-stamp ไปด้วย
บทเรียนออกแบบ: gate เฉพาะ action ที่ impact สูง/ย้อนคืนไม่ได้ (issueRefund) — ไม่ใช่ ทุก read (getOrder / getDeliveryStatus ไม่ต้องผ่าน gate เพราะเป็น read-only รันซ้ำได้ไม่เจ็บ) gate ที่ดักทุกอย่างคือ gate ที่ไม่มีใครอ่าน — คือ gate ปลอม นี่คือเหตุผลที่ EU AI Act Art. 14 พูดถึง human oversight ที่ มีประสิทธิผลจริง และเตือนเรื่อง automation bias (S22) — ด่านมนุษย์ที่คนกดผ่านอัตโนมัติ ไม่ใช่ oversight
- vs #17 (robustness) — idempotency key ตัวเดียวกัน แต่ threat model ตรงข้าม: #17 กัน ดับเบิลคลิกโดยบังเอิญ; บทนี้กัน replay โดยเจตนา reuse code ไม่ reuse สมมติฐาน
- vs #14 (designing MCP servers) — #14 บังคับ authz ที่ขอบ protocol MCP / tool-server; บทนี้บังคับ authz ใน handler ของ agent เอง (complete mediation) ที่ต้องยืนได้แม้ tool/server เชื่อถือได้เต็มร้อย
- vs #13 (evals) — บทนี้ หด ความเสียหาย ไม่ วัด พฤติกรรม การนับว่า gate โดน bypass กี่ครั้งคือ eval ของ #13 ส่งเคสไปให้ harness นั้นวัด อย่าสอน scoring ซ้ำ
แผนภาพ: least privilege + ด่านอนุมัติบน tool ที่มีผลข้างเคียง
หัวข้อที่มีชื่อว่า “แผนภาพ: least privilege + ด่านอนุมัติบน tool ที่มีผลข้างเคียง”flowchart TD
ADV["injection ที่ลอดบท4/5<br/>สั่ง agent ให้คืนเงิน"] --> LOOP["loop agent<br/>FunctionInvocation"]
subgraph TOOLS["least privilege — tool ที่ model เห็น"]
R1["getOrder<br/>read-only · ไม่ผ่าน gate"]
R2["getDeliveryStatus<br/>read-only · ไม่ผ่าน gate"]
W["issueRefund<br/>write · mark ApprovalRequired"]
end
LOOP --> R1
LOOP --> R2
LOOP --> W
W --> REQ["loop หยุด · คายคำขอ<br/>ToolApprovalRequestContent"]
REQ --> HUMAN{"มนุษย์อนุมัติ?"}
HUMAN -->|"no"| SKIP["ข้าม · ไม่คืนเงิน"]
HUMAN -->|"yes"| AUTHZ["handler ตรวจ authz ซ้ำ<br/>caller · cap · idempotency"]
AUTHZ -->|"ผ่านทุกด่าน"| DO["คืนเงินจริง<br/>ภายใน blast radius"]
AUTHZ -->|"เกิน cap หรือไม่มีสิทธิ์"| DENY["ปฏิเสธใน code"]
classDef danger fill:#b91c1c,stroke:#7f1d1d,color:#f8fafc;
classDef guard fill:#475569,stroke:#1e293b,color:#f8fafc;
class ADV,W danger;
class REQ,HUMAN,AUTHZ guard;
คำบรรยายภาพ: injection ที่ลอด detection ของบท4/5 เข้ามาสั่งคืนเงิน — แต่ containment สามชั้นหด blast radius: (1) least privilege — model เห็นแค่ tool ที่งานต้องใช้ และ issueRefund ถูก mark ว่าต้องอนุมัติ (2) ด่านมนุษย์ — loop หยุดคายคำขอ ไม่รันคืนเงินเอง (3) complete mediation — handler ตรวจ authz + cap + idempotency ซ้ำใน code ไม่ว่า model หรือมนุษย์จะตัดสินใจอะไร ไม่มีชั้นไหนชั้นเดียว “แก้” injection ได้ แต่ซ้อนกันแล้วทำให้ความเสียหายที่เหลืออยู่ในกรอบ
สรุปก่อนไปต่อ
หัวข้อที่มีชื่อว่า “สรุปก่อนไปต่อ”บท4/5 พยายาม จับ injection และจับได้ไม่หมด บทนี้จึงถามคำถามที่ตามมา: เมื่อมันลอด ความเสียหายจะเล็กแค่ไหน? คำตอบคือ excessive agency (LLM06) — ตัด functionality/permissions/autonomy ให้เหลือเท่างานด้วย least privilege, คร่อม issueRefund ด้วย native approval gate (ApprovalRequiredAIFunction → ToolApprovalRequestContent → ToolApprovalResponseContent) ที่เป็นของจริงก็ต่อเมื่อ invoker block รอมนุษย์จริง, วาง authz ใน handler ไม่ใช่ในหัว model (กัน confused deputy), และ reuse idempotency + refund cap + rate limit ของ #17 เป็น adversarial containment — พร้อมความจริงว่า gate ที่ดักทุกอย่างจะถูก rubber-stamp (~93%) จนกลายเป็น gate ปลอม บทหน้าเราไปที่ภัยของการ ดูดข้อมูลออก — data exfiltration และ system prompt leakage
ความปลอดภัยคือกระบวนการ ไม่ใช่ checkbox — least privilege + approval + cap ไม่ได้หยุด injection มันหด blast radius เมื่อ detection พลาด OWASP พูดไว้ตรงๆ ว่า control เหล่านี้ “will not prevent Excessive Agency, but can limit the level of damage caused” — containment นั่นคือคือประเด็น
บทนี้อิงต้นทางที่ลงวันที่กำกับ อ่านต่อได้โดยตรง:
- OWASP, “LLM06:2025 Excessive Agency” (2025) — นิยาม agency, 3 root cause (functionality/permissions/autonomy), human-in-the-loop “require a human to approve high-impact actions”, complete mediation “Implement authorization in downstream systems”, และ honesty “will not prevent Excessive Agency, but can limit the level of damage caused”
- Anthropic, “Mitigate jailbreaks and prompt injections” (accessed 2026-07-19) — least privilege “so that a successful injection can do minimal damage… scope permissions as narrowly as possible”
- Anthropic, “Building Effective Agents” (2024-12-19) — checkpoints “pause for human feedback at checkpoints” และ stopping conditions “to maintain control”
- Anthropic, “How we contain Claude” (2025) — approval fatigue field data: ผู้ใช้กดอนุมัติ prompt ราว ~93%
- Google, “Secure AI Framework (SAIF) 2.0 — Security Controls” (2025) — least privilege เป็นขอบบนของสิทธิ์ agent; permissions ควร “contextual and dynamic”
- EU AI Act, Art. 14 — Human oversight (in force 2024-08-01) — human oversight ที่มีประสิทธิผลจริงและ automation bias (อ้าง Article ไม่อ้างวันที่ deadline)
- Microsoft Learn, “FunctionInvokingChatClient” (pkg v10.7.0, upd. 2026-06-12) — เคารพ approval marker และ caveat stickiness “all received FunctionCallContent in that same response will also require approval”
- Microsoft Learn, “ApprovalRequiredAIFunction” (pkg v10.7.0) — “It does not enforce the requirement for user approval; it is the responsibility of the invoker to obtain that approval.”
- Microsoft Learn, “ToolApprovalResponseContent” (pkg v10.7.0) — ctor
(string requestId, bool approved, ToolCallContent toolCall); propsApproved/ToolCall/RequestId/Reason
เช็กความเข้าใจ — บทที่ 6
ข้อ 1 / 3ทำไม `ApprovalRequiredAIFunction` เพียงตัวเดียวถึงยัง *ไม่* ทำให้ approval gate เป็นของจริง?