ข้าม​ไป​ยัง​เนื้อหา

Defense-in-depth capstone: ประกอบ guard ทุก​ชั้น​เป็น pipeline เดียว

เจ็ด​บท​ที่​ผ่าน​มาสร้าง​ของ​ครบ​ชุด​แล้ว บท1 กาง threat model และ honesty spine, บท2–3 กาง​ภัย — prompt injection ทั้ง direct/indirect, jailbreak, และ​วินัย “ปฏิบัติ​ต่อ​ทุก content ที่ retrieve มา​เป็น untrusted”, บท4–7 สร้าง​ชั้น guard ที​ละ​ชั้น: input guard (บท4), output guard/redactor (บท5), least-privilege + approval gate (บท6), data-leak defense (บท7) บท​นี้​คือ capstone — เอา​ทั้ง​สี่​ชั้น​มา ประกอบ​เข้า​เป็น pipeline เดียว คร่อม agent Order แล้ว​พิสูจน์​ด้วย demo รัน​ได้​ว่า​มัน​ทำงาน​อย่าง​ที่ honesty spine บอก​ไว้​ตั้งแต่​บท1: guard ลด ความ​เสี่ยง​และ หด 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 แต่​ไม่ กำจัด ภัย

📦 code ตัวอย่าง

บท​นี้​ประกอบ agent Order ตัว​เดิม (ความ​สามารถ​จาก #15, context layer จาก #16, resilient runtime จาก #17) เข้า​กับ​ชั้น guard ทั้ง​สี่​ที่​สร้าง​ใน 🔁 บท4, 🔁 บท5, 🔁 บท6, และ 🔁 บท7 — repo kaen-food-ordering (กำลัง​จัด​ทำ) ไม่มี tool หรือ store ใหม่​มี​แต่​การ ประกอบ และ residual-attack demo ทุก​กติกา API ฝั่ง​แชต​จาก #15 ยัง​ยึด​เดิม​ทุก​ข้อ

ประกอบ​ทั้ง​สี่​ชั้น​เป็น pipeline เดียว — ลำดับ​คือ​การ​ตัดสิน​ใจ​ด้าน​ความ​ปลอดภัย

หัวข้อ​ที่​มีชื่อ​ว่า “ประกอบ​ทั้ง​สี่​ชั้น​เป็น pipeline เดียว — ลำดับ​คือ​การ​ตัดสิน​ใจ​ด้าน​ความ​ปลอดภัย”

Defense in DepthDefense in Depthการ​วาง​หลาย​ชั้น guard ที่​แต่ละ​ชั้น 'ไม่​สมบูรณ์' แต่​รวม​กัน​แล้วกันภัย​ส่วน​ใหญ่​ได้ — Anthropic: 'individually imperfect, but in unison combine to prevent most threats' และ 'by layering these strategies, you create a robust defense' ทั้ง​คอร์ส​คือ​รูป​นี้: input guard → output guard/redactor → least-privilege + approval → data-leak defense ประกอบ​เป็น guard ชั้น​เดียว​ใน capstone ไม่มี​ชั้น​ใด​ชั้น​เดียว 'แก้' injection — ปิด​ท้าย​ทุกบทว่า 'security is a process, not a checkbox'Architecture คือ​การ​วาง​หลาย​ชั้น​ที่​แต่ละ​ชั้น​ไม่​สมบูรณ์ แต่​ซ้อน​กัน​แล้วกันภัย​ส่วน​ใหญ่​และ​กัน​ความ​เสียหาย​ที่​เหลือ — และ​หัวใจ​ของ​บท​นี้​คือ: มัน​ไม่ใช่​แค่ “มี guard หลาย​ตัว” แต่​คือ เรียง guard ให้​ถูก​ลำดับ​ใน pipeline เดียว ทุก​ชั้น​ที่​สร้าง​มา​เป็น DelegatingChatClient หรือ policy ที่​บังคับ​ใน handler — บท​นี้​ร้อย​มัน​เข้า​ด้วย​กัน

using Anthropic; // AnthropicClient, AsIChatClient (beta 12.8.0 — pin version)
using Microsoft.Extensions.AI; // GA 10.x
AnthropicClient anthropic = new(); // อ่าน ANTHROPIC_API_KEY; x-api-key ใต้ฝากระโปรง
var options = new ChatOptions
{
Tools =
[
getOrder, // read-only · scope ต่อ caller ใน handler (บท7)
getDeliveryStatus, // read-only
new ApprovalRequiredAIFunction(AIFunctionFactory.Create(IssueRefund)), // gated (บท6)
],
AllowMultipleToolCalls = false, // กัน approval-stickiness (บท6)
};
IChatClient agent = anthropic.AsIChatClient("claude-opus-4-8") // bare id, ตัวอย่าง ณ 2026-07 — ไม่ต่อ suffix
.AsBuilder()
.UseGuardrails() // ชั้นนอกสุด: input screen (บท4) + output validate/redact (บท5)
.UseFunctionInvocation() // loop #15; honor approval marker → ปล่อย ToolApprovalRequestContent
.UseOpenTelemetry() // continuous monitoring / audit (NIST MANAGE)
.Build();

UseGuardrails() อยู่ นอก UseFunctionInvocation() เสมอ นี่​ไม่ใช่​รสนิยม — มัน​คือ​การ​ตัดสิน​ใจ​ด้าน​ความ​ปลอดภัย​ที่ load-bearing: guard ที่​อยู่​ชั้น​นอก​จะ​เห็น input ของ​ผู้​ใช้ ก่อน loop จะ dispatch tool ได้ และ​เห็น output หลัง loop คลี่คลาย​เสร็จ ถ้า​สลับ​ลำดับ​ให้ guard อยู่​ใน loop คุณ​จะ validate input หลัง​จาก​ที่ tool ถูก​เรียก​ไป​แล้ว — สาย​เกิน​ไป

ชั้น​ทั้งหมด​ที่​ประกอบ​กัน แต่ละ​ชั้น​เป็น​อิสระ​และ​ไม่​สมบูรณ์​ใน​ตัว (นั่น​คือ​รูป​ของ defense-in-depth):

  1. input screen — normalize + allowlist + injection screen ก่อน dispatch (บท4)
  2. tool-output screening — ส่ง data ผ่าน tool_result ที่ JSON-encode แล้ว classifier screen ก่อน model ลงมือ (บท5)
  3. output validation + redaction — structured-output validate ข้อ​เสนอ refund + redact PII/secret วน​ทั่ว Messages[].Contents[] ไม่ใช่​แค่ .Text (บท5)
  4. least privilege — แยก tool read-only จาก tool ที่​มี​สิทธิ์; IssueRefund ใช้ credential เฉพาะ + cap ฝั่ง server (บท6)
  5. approval gate — native ApprovalRequiredAIFunction ที่ block รอ​มนุษย์​จริง (บท6)
  6. handler-side authz + idempotency + cap — reuse idempotency key จาก #17 เป็น containment เชิง​ปฏิปักษ์ (บท6)
  7. prompt hygiene + data scoping — ไม่มี secret ใน system prompt, getOrder scope ต่อ caller (บท7)
  8. audit log — ทุก tripwire, ทุก​ครั้ง​ที่​ขอ refund, ทุก​การ​ตัดสิน approval (NIST MANAGE)

Anthropic เรียก​การ​วาง​ชั้น​แบบ​นี้ตรงๆ ว่า​เป็นการ​สร้าง defense ที่​แข็ง (S13, verbatim):

“By layering these strategies, you create a robust defense against jailbreaking and prompt injections.”

flowchart TD
  U["ผู้ใช้ / ผู้โจมตี"]
  subgraph PIPE["pipeline เดียว — ลำดับคือการตัดสินใจด้านความปลอดภัย"]
    direction TB
    G1["UseGuardrails ชั้นนอกสุด<br/>input screen (บท4)"]
    FI["UseFunctionInvocation loop<br/>getOrder · getDeliveryStatus · issueRefund gated"]
    G2["กลับออกชั้นนอก<br/>output validate + redact Contents[] (บท5)"]
    G1 --> FI --> G2
  end
  U --> G1
  G2 --> OUT["คำตอบถึงผู้ใช้"]

  subgraph LAYERS["ชั้น containment ที่บังคับนอก pipeline"]
    L1["scope getOrder ต่อ caller (บท7)"]
    L2["approval gate + cap + idempotency (บท6)"]
    L3["ไม่มี secret ใน prompt (บท7)"]
    L4["audit log ทุก tripwire/refund (NIST MANAGE)"]
  end
  FI -. บังคับที่ handler .-> L1
  FI -. containment .-> L2
  G1 -. ตั้งค่า .-> L3
  G2 -. observability .-> L4

  classDef guard fill:#475569,stroke:#1e293b,color:#f8fafc;
  classDef engine fill:#b91c1c,stroke:#7f1d1d,color:#f8fafc;
  class G1,G2,L1,L2,L3,L4 guard;
  class FI engine;

คำ​บรรยาย​ภาพ: pipeline เดียว​ที่ UseGuardrails() ห่อ นอก UseFunctionInvocation() — input ของ​ผู้​ใช้​ผ่าน input screen (บท4) ก่อน​เข้า loop (สี​แดง = engine ที่ dispatch tool) แล้ว output ไหล​กลับ​ออก​ชั้น​นอก​ให้ validate + redact (บท5) ก่อน​ถึง​ผู้​ใช้ สี่​กล่อง​สี​เทา​ด้าน​ล่าง​คือ​ชั้น containment ที่​บังคับ นอก pipeline — scope ข้อมูล, approval gate + cap + idempotency, prompt hygiene, และ audit log — ไม่มี​ชั้น​ใด​ชั้น​เดียว​สมบูรณ์ ซ้อน​กัน​จึง​กันภัย​ส่วน​ใหญ่​และ​กัน​ความ​เสียหาย​ที่​เหลือ

มาตรฐาน​คือ แผนที่ ไม่ใช่​ตัว​รักษา​ความ​ปลอดภัย​เอง — code guard ที่​ประกอบ​ไว้​ต่างหาก​ที่​ลด​ความ​เสี่ยง​จริง แต่​การ​วางแผนที่​ทับ pipeline ช่วย​ให้​เห็น​ว่า​ไม่มี​มุม​ไหน​ถูก​ลืม (อ้าง​โดย​เลข/Article แบบ​เบาๆ):

  • input screen (บท4) → OWASP LLM01 Prompt Injection
  • output validate + redact (บท5)LLM05 Improper Output Handling + LLM02 Sensitive Information Disclosure
  • least privilege + approval gate + cap (บท6)LLM06 Excessive Agency; EU AI Act Art. 14 human oversight; least-privilege controls ของ SAIF ที่​บท6 อ้าง​ไว้
  • prompt hygiene + data scoping (บท7)LLM07 System Prompt Leakage + LLM02
  • rate / loop capsLLM10 Unbounded Consumption
  • ทั้ง posture รวม​กัน → NIST GOVERN/MANAGE (S19) + EU AI Act Art. 15 ที่​ระบุ​ว่า adversarial robustness เป็น​ข้อ​กำหนด ทาง​กฎหมาย ของ​ระบบ high-risk ไม่ใช่​ทาง​เลือก (S21)
  • MITRE ATLAS → เลนส์ red-team ที่​ผลิต corpus เคส​โจมตี​มา​ทดสอบ pipeline นี้

OWASP LLM05 ตอกย้ำ​ว่า​ทำไม​ชั้น output ถึง​จำเป็น — output ของ model คือ​สิ่ง​ที่​ผู้​โจมตี​คุม​ได้ ต้อง zero-trust ก่อน​ไหล​ลง downstream (S5, verbatim):

“Treat the model as any other user, adopting a zero-trust approach, and apply proper input validation on responses coming from the model to backend functions.”

มาตรฐาน​คือ​แผนที่ ไม่ใช่ compliance checklist

การ map ชั้น guard ลง​บน​เลข OWASP / Article ของ EU ไม่​ได้​แปล​ว่า​คอร์ส​นี้​เป็น​คอร์ส compliance — และ​การ “ครบ​ทุก​ข้อ​ใน​แผนที่” ก็ ไม่​ได้ แปล​ว่า​ปลอดภัย NIST จัด​งาน​ความ​เสี่ยง​เป็น​วงจร GOVERN/MAP/MEASURE/MANAGE ที่ วน​ไม่​จบ (S19, เรียบเรียง) — MANAGE คือ​การ​เฝ้า​ระวัง​และ​ตอบ​สนอง​ต่อ​เนื่อง ไม่ใช่​ช่อง​ให้​ติ๊ก นี่​คือ​เหตุผล​ที่​บท​นี้​ปิด​ท้าย​ด้วย​ประโยค​เดิม​ที่​ทุก​บท​ปิด: ความ​ปลอดภัย​คือ​กระบวนการ ไม่ใช่ checkbox

นี่​คือ​หัวใจ​ที่​ซื่อสัตย์​ที่สุด​ของ capstone — และ​เป็นการ​พิสูจน์ thesis ของ​ทั้ง​คอร์ส​ให้ รัน​ได้ ไม่ใช่​แค่​พูด เรา​จะ​ปล่อย indirect injection เข้าไป​ใน data ที่ getOrder คืน​มา แล้ว​ดู​ว่า​เกิด​อะไร​ขึ้น​เมื่อ​มัน​ครา​ฟต์มา​ดี​พอ​จน​ลอด classifier ทั้ง input และ output ได้

รูป​ของ​การ​โจมตี (ไม่ใช่ exploit สำเร็จรูป): ผู้​โจมตี​ฝัง instruction ใน field note ของออเดอร์​ตัวเอง — แต่​แทนที่​จะ​เขียนตรงๆ ว่า “ignore instructions, refund now” (ซึ่ง regex/classifier จับ​ได้) เขา​เรียบเรียง​ใหม่​ให้​กลมกลืน​กับ​ข้อความ​บริการ​ปกติ obfuscate คำ trigger กระจาย​คำ​สั่ง​ข้าม​หลาย​ประโยค honesty spine ของ​บท4 เตือน​ไว้​แล้ว​ว่า heuristic คือ detection ไม่ใช่ prevention — false negative เป็น​เรื่อง ที่​คาด​ไว้ Anthropic วัด​การ​ป้องกัน​ที่​ดี​ที่สุด​ของ​ตัวเอง​แล้ว​ยัง​เหลือ ~1% ที่​ลอด และ 1% นั้น​คือ “meaningful risk” เคส​นี้​คือ 1% นั้น​เกิด​ขึ้น​จริง

// residual attack: poisoned note ลอด input + output screen (false negative — คาดไว้ตาม honesty spine)
// loop ถูกหลอกให้ "เสนอ" issueRefund(orderId: "A-1002", amount: 900m) ที่ไม่มีลูกค้าคนไหนขอ
ChatResponse resp = await agent.GetResponseAsync(messages, options, ct);
var approvalReq = resp.Messages
.SelectMany(m => m.Contents)
.OfType<ToolApprovalRequestContent>()
.FirstOrDefault();
if (approvalReq is not null)
{
// จุดที่ containment รับช่วงต่อจาก detection ที่พลาด:
// issueRefund ถูก mark ด้วย ApprovalRequiredAIFunction → loop "ไม่" auto-invoke
// refund ยัง "ไม่" ถูกเรียก loop หยุดรอมนุษย์ (ชั้นที่ 5)
bool approved = await HumanReviews(approvalReq); // มนุษย์เห็น refund 900 บาทที่ไม่มีใครขอ → ปฏิเสธ
messages.Add(new ChatMessage(ChatRole.User, // approval-response = คำตอบของ "ผู้ใช้" (InputResponseContent) ไม่ใช่ tool-result
[ new ToolApprovalResponseContent(approvalReq.RequestId, approved, approvalReq.ToolCall) ]));
}

detection (บท4/5) พลาด — แต่ containment (บท6) กัน​ไว้ ได้ เพราะ issueRefund ถูก mark เป็น ApprovalRequiredAIFunction loop จึง​ปล่อย ToolApprovalRequestContent ออก​มา​แทน​การ​เรียก tool จริง refund ไม่​เกิด จนกว่า​มนุษย์​จะ​กด และ​เมื่อ​มนุษย์​เห็น “refund 900 บาท” ที่​ไม่มี​บริบท​ว่า​ใคร​ขอ เขา​ก็​ปฏิเสธ

และ​แม้​สมมติ​เลว​ร้าย​สุด — มนุษย์​เผลอ rubber-stamp (บท6 เตือน​ว่า Anthropic วัด​ได้​ว่า​ผู้​ใช้​กด approve ~93% ของ prompt) — ชั้น containment ที่​เหลือ​ก็​ยัง หด blast radius: handler เช็ค authz ใน code, amount cap ฝั่ง server ตัด refund ที่​เกิน​เพดาน, และ idempotency key (reuse จาก #17) กัน​ไม่​ให้ loop ที่​ถูก hijack ยิง refund ซ้ำ N ครั้ง — loop ที่​ถูก​ยึด​จ่าย​ได้​อย่าง​มาก​หนึ่ง​ครั้ง ไม่​เกิน​เพดาน ไม่ใช่ N ครั้ง​ไม่​จำกัด OWASP พูด​เรื่อง​นี้ตรงๆ ว่า containment ไม่​ได้ กำจัด excessive agency แต่ จำกัด​ความ​เสียหาย (S6, verbatim):

“will not prevent Excessive Agency, but can limit the level of damage caused.”

Anthropic เสริม​ด้วย​ว่า​รูป checkpoint แบบ​นี้ — หยุด​รอ​มนุษย์​ที่​จุด​สำคัญ — คือ​กลไก​ควบคุม​มาตรฐาน​ของ agent (S16, verbatim):

“Agents can then pause for human feedback at checkpoints or when encountering blockers.”

flowchart TD
  ADV["ผู้โจมตี"] -->|"ฝัง payload หลบ classifier"| NOTE["poisoned note<br/>ใน getOrder data"]
  NOTE --> DET{"input + output screen<br/>บท4/5"}
  DET -->|"false negative — ลอด (คาดไว้)"| PROP["loop เสนอ issueRefund<br/>900 บาท ที่ไม่มีใครขอ"]
  PROP --> GATE{"approval gate บท6<br/>ApprovalRequiredAIFunction"}
  GATE -->|"loop หยุด · refund ยังไม่ถูกเรียก"| HUMAN["มนุษย์ตรวจ → ปฏิเสธ"]
  HUMAN --> SAFE["ถูกกัน · blast radius = 0"]
  GATE -.->|"ถึงมนุษย์เผลอกด"| CAP["cap + idempotency (บท6/#17)<br/>refund 1 ครั้ง ≤ เพดาน"]
  CAP --> SAFE

  classDef danger fill:#b91c1c,stroke:#7f1d1d,color:#f8fafc;
  classDef guard fill:#475569,stroke:#1e293b,color:#f8fafc;
  classDef ok fill:#166534,stroke:#14532d,color:#f8fafc;
  class ADV,NOTE,PROP danger;
  class DET,GATE,HUMAN,CAP guard;
  class SAFE ok;

คำ​บรรยาย​ภาพ: สาย​สี​แดง​คือ​การ​โจมตี​ที่ ลอด detection ได้ — poisoned note ผ่าน input/output screen แบบ false negative (สิ่ง​ที่ honesty spine คาด​ไว้) แล้ว​หลอก loop ให้​เสนอ refund ที่​ไม่มี​ใคร​ขอ แต่​ชั้น containment สี​เทา​กัน​ไว้: approval gate หยุด loop ก่อน refund จะ​ถูก​เรียก​จริง มนุษย์​ปฏิเสธ → blast radius = 0 (สี​เขียว) และ​แม้​มนุษย์​เผลอ​กด cap + idempotency ก็​ยัง​จำกัด​ให้​จ่าย​ได้​อย่าง​มาก​หนึ่ง​ครั้ง​ไม่​เกิน​เพดาน detection พลาด containment กัน — นี่​คือ thesis ของ​คอร์ส​ที่​รัน​ได้

capstone ไม่​ได้​ปิด​จ๊อบ — มัน​คือ​จุด​ตั้งต้น​ของ​งาน​ต่อ​เนื่อง (MANAGE ที่​วน​ไม่​จบ) ขีด​เส้น​ให้​ชัด​ว่า​เรื่อง​ไหน​ไป​ต่อ​ที่​คอร์ส​ไหน:

  • การ​ให้​คะแนน​เคส​โจมตี → #13 🔁 evals for AI agents — corpus เคส​โจมตี​ที่​คุณ red-team มา​ทั้ง​คอร์ส​นี้​คือ input ของ harness วัดผล​ใน #13 Anthropic เรียก​งาน​นี้​ว่า (S13, verbatim) “Red-team your own agent. Before deploying, test your workflow with documents, emails, and tool outputs that deliberately contain injection attempts.” — #18 ผลิต เคส​โจมตี, #13 แปลง​มัน​เป็น การ​วัดผล​เชิง​ระบบ คนละ​งาน ไม่​สอน scoring ซ้ำ
  • ความ​ปลอดภัย​ของ tool-server → #14 🔁 designing MCP servers — ถ้า tool ย้าย​ไป​เป็น MCP server ระยะ​ไกล การ​รักษา protocol และ​ตัว server (tool-poisoning ใน metadata) เป็น​งาน​ของ #14 ที่ #18 ไม่ ทับ — #18 ยืน​ได้​แม้ server เชื่อถือ​ได้​เต็ม​ร้อย
  • untrusted retrieval / poisoned memory → #16 🔁 context engineering — เส้นทาง memory/retrieval ที่​ถูก poison คือ indirect injection อีก​ช่อง #16 curate context เพื่อ​คุณภาพ #18 ปฏิบัติ​ต่อ​สิ่ง​ที่ retrieve มา​เป็น​ศัตรู
  • resilient runtime ที่ guard ขี่​อยู่ → #17 🔁 agent harness & loop — idempotency key + cap ที่ #17 สร้าง​เพื่อ​กัน อุบัติเหตุ ถูก reuse ในบท6/8 เป็น containment เชิง ปฏิปักษ์ รูป wrapper เดียวกัน threat model ตรง​ข้าม

capstone ประกอบ​ชั้น guard ทั้ง​สี่ (input บท4, output บท5, least-privilege/approval บท6, data-leak บท7) เข้า​เป็น pipeline เดียว​คร่อม agent Order โดย UseGuardrails() ห่อ นอก UseFunctionInvocation() เสมอ — ลำดับ​คือ​การ​ตัดสิน​ใจ​ด้าน​ความ​ปลอดภัย แผนที่ OWASP/NIST/EU ทับ​ให้​เห็น​ว่า​ชั้น​ไหน​ตอบ​ความ​เสี่ยง​ข้อ​ไหน และ residual-attack demo พิสูจน์​ให้​เห็น​กับ​ตา​ว่า เมื่อ detection พลาด (ซึ่ง honesty spine บอกว่า​มัน​จะ​พลาด) containment ยัง​กัน​ไว้​ได้ — detection ลด containment หด รวม​กัน​คือ defense-in-depth

ความ​ปลอดภัย​คือ​กระบวนการ ไม่ใช่ checkbox — ไม่มี​ชั้น​ไหน​ชั้น​เดียว “แก้” injection ได้ และ pipeline ที่​ประกอบ​เสร็จ​ก็​ไม่​ได้ กำจัด ภัย มัน​เอาชนะ​การ​โจมตี​ส่วน​ใหญ่​และ กัน ส่วน​ที่​เหลือ Anthropic สรุป​รูป​นี้​ไว้​ตรง​ที่สุด​ว่า​มัน​คือ​การ​วาง​หลาย​ชั้น​ที่ (S15, verbatim):

“several different overlapping safeguards that may be individually imperfect, but in unison combine to prevent most threats.”

indirect injection ที่​ตั้งใจ​จริง​ยัง​ลอด detection ได้ — นั่น​คือ​คือ​เหตุผล​ที่ containment (least privilege + human gate + cap + idempotency) ไม่ใช่​ทาง​เลือก guard ลด ความ​เสี่ยง​และ หด blast radius มัน​ไม่ กำจัด ภัย และ​คอร์ส​นี้​จะ​ไม่​แกล้ง​ทำ​เป็น​ว่า​มัน​ทำได้ — ตั้งแต่​บท​แรก​จน​บท​สุดท้าย


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

บท​นี้​อิง​ต้นทาง​ที่​ลง​วัน​ที่​กำกับ อ่าน​ต่อ​ได้​โดยตรง:

เช็กความเข้าใจ — บทที่ 8

ข้อ 1 / 3

ในการประกอบ pipeline ของ capstone ทำไม `UseGuardrails()` ต้องห่อ *นอก* `UseFunctionInvocation()` เสมอ?