DEV Community

Cover image for ทำไม AI Agent ล้มเหลวใน Production (พร้อมวิธีทดสอบแต่ละโหมดความผิดพลาด)
Thanawat Wongchai
Thanawat Wongchai

Posted on • Originally published at apidog.com

ทำไม AI Agent ล้มเหลวใน Production (พร้อมวิธีทดสอบแต่ละโหมดความผิดพลาด)

เอเจนต์ของคุณอาจทำงานได้ดีในการสาธิต: อ่านตั๋ว เรียกใช้ API สามตัว แล้วโพสต์สรุปที่ชัดเจน แต่เมื่อใช้งานจริง เอเจนต์อาจส่งอีเมลซ้ำ ใช้โทเค็นจนเกินงบจาก retry loop หรือส่งเพย์โหลดที่ส่วนหน้าประมวลผลไม่ได้ ปัญหามักไม่ได้อยู่ที่โมเดล แต่อยู่ที่ HTTP requests ที่เกิดขึ้นระหว่างการเรียกใช้เครื่องมือ (tool calls)

ลองใช้ Apidog วันนี้

ช่องว่างระหว่างต้นแบบกับเอเจนต์ที่เชื่อถือได้คือการทดสอบ API dependencies ของเอเจนต์ ทุก tool call อาจถูก throttle, timeout, ส่งคืนข้อมูลผิดรูปแบบ หรือทำงานล้มเหลว หากไม่ทดสอบเส้นทางเหล่านี้ล่วงหน้า ความผิดพลาดจาก response เพียงครั้งเดียวอาจกลายเป็น incident ได้

แนวทางคือทดสอบความล้มเหลวอย่างตั้งใจ: กำหนด contract, mock dependency, รัน agent ผ่านสถานการณ์ผิดพลาด และตรวจสอบผลลัพธ์ Apidog ช่วยจัดการเลเยอร์ API นี้ได้

เอเจนต์ล้มเหลวที่ขอบเขตของ API ไม่ใช่ที่พร้อมต์

เมื่อเอเจนต์ผิดพลาดใน production ทีมมักเริ่มจากการแก้ prompt ซึ่งอาจช่วยได้บ้าง แต่ต้นเหตุที่พบบ่อยมักเป็น API response ที่ช้า ผิดรูปแบบ ถูก rate limit หรือมี schema ไม่ตรงกับที่เอเจนต์คาดไว้

หนึ่งรอบของ agent loop มักมีขั้นตอนดังนี้:

  1. โมเดลเลือกเครื่องมือ
  2. โค้ดแปลง tool call เป็น HTTP request
  3. บริการภายนอกตอบกลับ
  4. โค้ดส่งผลลัพธ์กลับให้โมเดลตัดสินใจต่อ

ในสี่ขั้นตอนนี้ มีสามขั้นตอนที่เป็นงาน integration API โดยตรง จึงควรใช้แนวทางทดสอบ API ปกติกับ agent ด้วย:

  • validate request ด้วย schema
  • mock endpoint ที่ช้าและล้มเหลว
  • ตรวจสอบ JSON response
  • วัดจำนวน retries และ tool calls
  • ป้องกัน side effects ที่ไม่ควรเกิดขึ้น

คำถามที่ควรถามจึงไม่ใช่ “โมเดลฉลาดพอหรือไม่” แต่คือ:

“ฉันได้ทดสอบทุกวิถีทางที่ API call ของเอเจนต์อาจล้มเหลวแล้วหรือยัง?”

ต่อไปนี้คือห้า failure modes ที่ควรทดสอบก่อน deploy

โหมดความล้มเหลวที่ 1: Tool call ไม่ตรงตามสัญญา API

โมเดลอาจสร้างพารามิเตอร์ที่ไม่มีอยู่ ลืมฟิลด์บังคับ ส่ง type ผิด หรือเรียก endpoint ถูกต้องแต่ส่ง argument ที่ไม่มีความหมาย

ตัวอย่าง: เอเจนต์เรียก POST /reservations โดยส่ง "guests": "two" แทนที่จะเป็น "guests": 2

{
  "date": "2025-03-20",
  "guests": "two"
}
Enter fullscreen mode Exit fullscreen mode

หาก API ตอบ 400 เอเจนต์ต้องรับรู้ว่าคำสั่งล้มเหลว ไม่ใช่ตีความว่า reservation สำเร็จแล้ว หรือแย่กว่านั้นคือ API ตอบ 200 แต่ซ่อน error ไว้ใน response body

วิธีทดสอบ

  1. กำหนด schema ให้ทุกเครื่องมือที่เอเจนต์เรียกได้
  2. Validate request ก่อนส่งออก
  3. ทำให้ validation failure เป็น test failure ที่ชัดเจน
  4. บันทึก request ที่ไม่ผ่าน schema เพื่อใช้ปรับ tool definition หรือ prompt

ตัวอย่าง JSON Schema:

{
  "type": "object",
  "required": ["date", "guests"],
  "properties": {
    "date": {
      "type": "string",
      "format": "date"
    },
    "guests": {
      "type": "integer",
      "minimum": 1
    }
  },
  "additionalProperties": false
}
Enter fullscreen mode Exit fullscreen mode

ตัวอย่าง validation ใน TypeScript:

if (!isValidReservationPayload(toolArgs)) {
  throw new Error("Invalid reservation tool arguments");
}
Enter fullscreen mode Exit fullscreen mode

อ่านเพิ่มเติมเกี่ยวกับการ ทดสอบการเรียกใช้เครื่องมือของ AI agent และการ ทดสอบเอเจนต์ที่เรียกใช้ API ของคุณ

สิ่งที่ควรทำทันที: รวบรวม schema ของ tools ทั้งหมด แล้วโหลดเข้า Apidog เพื่อ validate requests ที่เอเจนต์สร้างจริง

โหมดความล้มเหลวที่ 2: Upstream error, timeout และ rate limit

ทุก external call อาจตอบกลับด้วย:

  • 429 Too Many Requests
  • 500 Internal Server Error
  • timeout
  • connection reset
  • malformed response

เอเจนต์ที่แข็งแรงต้อง retry อย่างมีขอบเขต ใช้ backoff และหยุดเมื่อไม่ควรลองต่อ เอเจนต์ที่เปราะบางอาจ retry แบบไม่จำกัดจนใช้งบประมาณหมด หรือยอมแพ้ทันทีหลังเจอ error ครั้งแรก

หัวข้อนี้เป็นปัญหาที่พบบ่อยใน การสนทนาเกี่ยวกับรูปแบบการกู้คืนข้อผิดพลาดของเอเจนต์

สร้าง failure sequence ด้วย mock

อย่าทดสอบเฉพาะ happy path ให้ mock dependency เพื่อบังคับลำดับ response เช่น:

  1. ตอบ 429 พร้อม Retry-After
  2. ตอบ 500
  3. ตอบ 200 สำเร็จ

จากนั้นตรวจสอบว่า agent:

  • เคารพค่า Retry-After หรือไม่
  • ใช้ exponential backoff พร้อม jitter หรือไม่
  • จำกัดจำนวน retries หรือไม่
  • เปิด circuit breaker เมื่อ upstream ล้มเหลวต่อเนื่องหรือไม่
  • แจ้ง failure กลับอย่างเหมาะสมหรือไม่

ตัวอย่าง retry wrapper แบบมีขอบเขต:

const maxAttempts = 3;

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
  try {
    return await callExternalApi();
  } catch (error) {
    if (attempt === maxAttempts) {
      throw error;
    }

    const delayMs = 500 * 2 ** (attempt - 1);
    await sleep(delayMs);
  }
}
Enter fullscreen mode Exit fullscreen mode

หาก request มี side effect เช่น สร้างคำสั่งซื้อหรือส่งอีเมล ต้องรองรับ idempotency ด้วย ไม่เช่นนั้น retry อาจทำให้เกิดรายการซ้ำหรือเรียกเก็บเงินซ้ำ ใช้ คีย์ Idempotency เพื่อให้การ retry ปลอดภัย

สำหรับการจัดการ 429 ดูคู่มือ ความหมายของการตอบสนองที่เกินขีดจำกัดจำนวนครั้ง และแนวทาง การกู้คืนข้อผิดพลาดของ AI agent

โหมดความล้มเหลวที่ 3: ผลลัพธ์ไม่แน่นอน

แม้ตั้งค่า temperature เป็นศูนย์ ผลลัพธ์ของโมเดลอาจไม่เหมือนกันทุก byte ในแต่ละรัน ความแตกต่างของ hardware, batching และฝั่งผู้ให้บริการล้วนสร้างความแปรปรวนได้ ดังที่เห็นจาก เธรด vLLM เกี่ยวกับ seed และ temperature

ดังนั้น อย่าเขียน test ที่ยืนยันข้อความแบบตรงตัว:

expect(answer).toBe("ยอดรวมคือ 1,250 บาท");
Enter fullscreen mode Exit fullscreen mode

การทดสอบลักษณะนี้ flaky ได้ง่าย และ flaky test มักถูกปิดทิ้งในที่สุด

ทดสอบโครงสร้างและความหมายแทนข้อความเป๊ะ

ให้ตรวจสอบสิ่งที่คงที่จริง:

  • response ผ่าน JSON Schema
  • tool name ถูกต้อง
  • tool arguments มี type และรูปแบบถูกต้อง
  • มี required keys
  • ไม่มี forbidden fields
  • ค่าตัวเลขอยู่ในช่วงที่ยอมรับได้
  • จำนวน tool calls ไม่เกินเพดาน

ตัวอย่าง:

expect(result.total).toBeGreaterThanOrEqual(0);
expect(result.total).toBeLessThanOrEqual(cart.total);
expect(result.currency).toBe("THB");
expect(result.items).toHaveLength(cart.items.length);
Enter fullscreen mode Exit fullscreen mode

แทนที่จะคาดหวังคำตอบเป๊ะ ให้คาดหวัง invariant ที่ระบบต้องรักษาเสมอ

อ่านเพิ่มได้ที่ การทดสอบ AI agent ที่ไม่แน่นอน และ วิธีการทำงานของหน่วยความจำเอเจนต์ ซึ่งอธิบายว่าทำไม state จึงเพิ่มความซับซ้อนในการทดสอบ

โหมดความล้มเหลวที่ 4: ค่าใช้จ่ายควบคุมไม่ได้

เอเจนต์ทำงานแบบวนซ้ำ และทุก iteration มีค่าใช้จ่าย ทั้ง token, API request, latency และ compute

เอเจนต์เพียงตัวเดียวที่ติดอยู่ใน retry loop หรือเรียก tool ซ้ำโดยไม่จำเป็น อาจเพิ่มค่าใช้จ่ายจากเล็กน้อยเป็นจำนวนมากภายในคืนเดียว มีรายงานภาคสนามใน SDK discussion ที่ลดค่าใช้จ่าย agent จาก 500 ดอลลาร์ต่อเดือนเหลือ 80 ดอลลาร์โดยไม่เสียคุณภาพ ซึ่งสะท้อนว่าค่าใช้จ่ายที่ไม่จำเป็นมักซ่อนอยู่ใน flow ของ agent

ใส่ budget guardrails

ติดตามอย่างน้อย:

  • tokens ต่อ run
  • จำนวน tool calls ต่อ task
  • จำนวน retries ต่อ dependency
  • เวลารวมต่อ task
  • จำนวน context tokens ที่เพิ่มขึ้นในแต่ละ loop

ตัวอย่าง budget check:

const MAX_TOOL_CALLS = 10;
const MAX_TOKENS_PER_RUN = 20_000;

if (toolCallCount > MAX_TOOL_CALLS) {
  throw new Error("Tool call budget exceeded");
}

if (tokenUsage > MAX_TOKENS_PER_RUN) {
  throw new Error("Token budget exceeded");
}
Enter fullscreen mode Exit fullscreen mode

เมื่อทดสอบ recovery path ด้วย mock อย่าตรวจสอบเพียงว่า agent “สำเร็จ” หรือไม่ ตรวจสอบด้วยว่าต้องใช้กี่ requests จึงสำเร็จ Agent ที่สำเร็จหลังเรียก API 40 ครั้งอาจเป็น incident ด้านต้นทุนที่กำลังรอเกิดขึ้น

ดูแนวทางเพิ่มเติมในคู่มือ การลดค่าใช้จ่ายโทเค็นของเอเจนต์

โหมดความล้มเหลวที่ 5: ไม่มี guardrails สำหรับการกระทำจริง

ความล้มเหลวที่แพงที่สุดคือเอเจนต์ทำตามคำสั่งได้ถูกต้อง แต่การกระทำนั้นไม่ควรเกิดขึ้นตั้งแต่แรก เช่น:

  • ส่งอีเมลถึงลูกค้าหรือผู้บริหาร
  • ลบข้อมูล
  • สร้าง order
  • เปลี่ยน permission
  • เรียก API ที่มีผลกระทบต่อระบบจริง

ปัญหาไม่ใช่เพียงว่าโมเดล “ตัดสินใจผิด” แต่คือไม่มีชั้นควบคุมระหว่างการตัดสินใจและ side effect

เพิ่มชั้นป้องกันก่อน execute

ใช้ guardrails หลายระดับ:

  1. Allowlist — อนุญาตให้เอเจนต์เรียกได้เฉพาะ tools หรือ endpoints ที่กำหนด
  2. Human approval — การลบข้อมูล ส่งอีเมล หรือทำธุรกรรมต้องผ่านการอนุมัติ
  3. Dry-run mode — ให้ agent อธิบายสิ่งที่จะทำโดยไม่ execute จริง
  4. Scope limit — จำกัดจำนวนผู้รับ จำนวนรายการ หรือขอบเขตข้อมูลที่แก้ไขได้
  5. Audit log — เก็บ tool calls, arguments, approval และผลลัพธ์ทุกครั้ง

ตัวอย่าง policy:

const destructiveActions = [
  "delete_user",
  "send_email",
  "create_purchase_order"
];

if (destructiveActions.includes(toolName)) {
  return {
    status: "approval_required",
    toolName,
    args: toolArgs
  };
}
Enter fullscreen mode Exit fullscreen mode

ทดสอบ guardrail ด้วย mock endpoint ที่มี side effect แล้วตรวจสอบว่า agent เข้าสู่ approval flow แทนที่จะ execute request จริง

แนวทาง OWASP Top 10 สำหรับแอปพลิเคชัน LLM เป็น checklist ที่ดีสำหรับความเสี่ยงด้าน LLM และคู่มือ ราวกั้นความปลอดภัยของ AI agent อธิบาย approval gates และ blast-radius control เพิ่มเติม

วิธีจัดโครงสร้างการทดสอบเอเจนต์

ใช้รูปแบบทดสอบเดียวกันกับทุก tool:

  1. รวบรวม schema ของ tools ที่เอเจนต์เรียกได้
  2. สร้าง mock สำหรับ dependency แต่ละตัว
  3. กำหนด response scenarios ทั้ง happy path และ failure path
  4. รัน agent ผ่านแต่ละ scenario
  5. ตรวจสอบ request, response, retry behavior, tool-call count และ guardrail behavior
  6. เพิ่ม test เข้าสู่ CI เพื่อป้องกัน regression

โครงสร้าง test case ตัวอย่าง:

describe("reservation agent", () => {
  it("backs off after 429 and completes once upstream recovers", async () => {
    mockApi
      .onPost("/reservations")
      .reply(429, {}, { "Retry-After": "1" })
      .onPost("/reservations")
      .reply(200, { reservationId: "res_123" });

    const result = await runReservationAgent({
      date: "2025-03-20",
      guests: 2
    });

    expect(result.status).toBe("confirmed");
    expect(mockApi.history.post).toHaveLength(2);
  });

  it("requires approval before deleting data", async () => {
    const result = await runAgent({
      task: "Delete inactive user user_123"
    });

    expect(result.status).toBe("approval_required");
  });
});
Enter fullscreen mode Exit fullscreen mode

เริ่มจาก tool เดียวก่อน แล้วเพิ่ม coverage ทีละตัว การลงทุนนี้คุ้มค่าทันทีเมื่อคุณจับ tool call ที่เสียได้ก่อนผู้ใช้พบ

รายการตรวจสอบความน่าเชื่อถือของเอเจนต์

ก่อน deploy agent ให้ตรวจสอบว่า:

  • [ ] Tool call ทุกครั้งผ่านการตรวจสอบ schema
  • [ ] Contract violation ทำให้ test ล้มเหลวอย่างชัดเจน
  • [ ] มี mock สำหรับ 429, 500, timeout และ malformed response
  • [ ] Agent ใช้ retry, backoff และ retry limit อย่างถูกต้อง
  • [ ] Action ที่ retry ได้รองรับ idempotency
  • [ ] Tests ตรวจสอบ structure และ semantics แทนข้อความตรงตัว
  • [ ] มีการวัด token usage และ tool calls ต่อ run
  • [ ] มี budget สูงสุดเพื่อหยุด loop ที่ควบคุมไม่ได้
  • [ ] Destructive actions อยู่หลัง allowlist หรือ human approval
  • [ ] Guardrail paths ถูกทดสอบด้วย mock endpoint
  • [ ] มี audit log สำหรับ tool calls และ side effects

หากทำได้ครบ คุณไม่ได้แค่ทดสอบว่าเอเจนต์ “ตอบได้” แต่กำลังทดสอบว่าเอเจนต์รับมือกับ production failures ได้จริง

Apidog เหมาะสมกับอะไร และไม่เหมาะสมกับอะไร

Apidog ไม่ใช่ agent framework, model host หรือ evaluation suite สำหรับโมเดล มันไม่ได้สร้างหรือรันเอเจนต์แทนคุณ

สิ่งที่เหมาะสมคือการจัดการ API layer ที่เอเจนต์พึ่งพา:

  • ออกแบบและเก็บ API contracts สำหรับ tools
  • ตรวจสอบ request ที่ agent สร้างเทียบกับ schema
  • Mock dependencies และกำหนด failure responses
  • ทดสอบ 429, 500, timeout และ malformed payload
  • ตรวจสอบ response schema, data shape, ranges และ required keys

กล่าวคือ Apidog ช่วยทดสอบ API ที่เอเจนต์เรียก จำลอง failure ที่คุณต้องรับมือ และตรวจสอบ response ที่ agent ใช้ตัดสินใจต่อ ดูภาพรวมได้ในบทความ การทดสอบ AI agent

คำถามที่พบบ่อย

ความน่าเชื่อถือของเอเจนต์เป็นปัญหาของโมเดลหรือปัญหาทางวิศวกรรม?

ส่วนใหญ่เป็นปัญหาทางวิศวกรรม การเลือกโมเดลสำคัญ แต่ปัญหาอย่าง tool call ผิดรูปแบบ rate limit ที่ไม่ถูกจัดการ หรือ guardrail ที่ขาดหาย เป็นปัญหา integration และ testing ที่แก้ได้โดยไม่ต้องเปลี่ยนโมเดล

ฉันสามารถทดสอบเอเจนต์โดยไม่เรียก API จริงได้หรือไม่?

ได้ และควรทำ ใช้ mock dependency เพื่อบังคับ error response ควบคุม latency และหลีกเลี่ยง side effects จริง วิธีนี้จำเป็นสำหรับการทดสอบ recovery path และ guardrail

ควรเขียน test อย่างไรเมื่อผลลัพธ์เปลี่ยนทุกครั้งที่รัน?

ตรวจสอบ schema, tool-call pattern, required fields และช่วงค่าที่รับได้ แทนการเปรียบเทียบข้อความแบบตรงตัว ดูรายละเอียดใน การทดสอบ AI agent ที่ไม่แน่นอน

ควรเริ่มทดสอบจากอะไร?

เริ่มจาก guardrails สำหรับ destructive actions และ error recovery เพราะเป็นสองส่วนที่ป้องกันความเสียหายสูงสุด: การทำ action ที่ไม่ควรเกิด และ retry loop ที่ใช้งบประมาณจนหมด

เริ่มจาก failure mode เดียว

ไม่จำเป็นต้องครอบคลุมทั้งห้า failure modes ในครั้งเดียว เลือกความเสี่ยงสูงสุดของระบบคุณก่อน ซึ่งมักเป็น guardrails หรือ error recovery

ภายในสัปดาห์นี้ ให้ทำหนึ่ง scenario:

  1. เลือก tool ที่มีผลกระทบสูง
  2. สร้าง mock endpoint
  3. กำหนด failure เช่น 429 หรือ timeout
  4. รัน agent
  5. ตรวจสอบ retries, budget, output และ guardrail behavior
  6. เพิ่ม scenario นั้นเข้า CI

เมื่อคุณเห็น agent รับมือกับ 429 ด้วย backoff ที่ถูกต้อง แทนที่จะวนซ้ำจนงบหมด คุณจะเชื่อมั่นในระบบมากกว่าการดู demo ที่สมบูรณ์แบบเพียงครั้งเดียว

ดาวน์โหลด Apidog เพื่อออกแบบ contract, mock failures และตรวจสอบ responses ที่เอเจนต์ของคุณพึ่งพา

Top comments (0)