Laravel AI SDK 1.4 มีอะไรใหม่ agent ทำงานต่อได้แม้ tool จะโยน exception

Laravel AI SDK 1.4 ส่ง exception จาก tool กลับไปให้ model แทนการหยุดทั้งรอบ ให้ Agent::fake() ทดสอบ flow อนุมัติ tool ได้ครบ เปิด raw response ให้ทุกประเภท และแยกเครดิต OpenAI หมดออกจาก rate limit

เผยแพร่
เวลาอ่าน
4 นาที
สรุปจาก
Paul Redmond · laravel-news.com
แท็ก
#AI#Laravel
ในโพสต์นี้ 6 หัวข้อ
  1. Agent ทำงานต่อได้เมื่อ tool โยน exception
  2. ทดสอบ flow อนุมัติ tool ด้วย Agent::fake() ได้ครบ
  3. คำตอบทุกประเภทอ่าน raw response ได้แล้ว
  4. เครดิต OpenAI หมดแยกจาก rate limit
  5. อัปเดตอื่น ๆ
  6. เช็คห้าจุดนี้ก่อนอัปเกรด

ถ้ายังไม่เคยเขียน agent ด้วย AI SDK ลองอ่านตัวอย่าง agent ของ Spatieก่อน ส่วนเรื่อง tool ที่ต้องมีคนอนุมัติ เริ่มมาตั้งแต่ Laravel AI SDK 1.1

Agent ทำงานต่อได้เมื่อ tool โยน exception

tool คือฟังก์ชันในแอปของเราที่ model เรียกใช้ได้ เช่น ดูสถานะออเดอร์ หรือคืนเงิน เดิมถ้า tool ตัวไหนโยน exception งานทั้งรอบจะหยุดทันทีและ SDK ยิง event AgentFailed ผู้ใช้ก็ได้หน้า error ทั้งที่ model อาจแก้สถานการณ์เองได้

1.4 จับ exception ไว้แล้วส่งข้อความกลับไปให้ model เป็นผลของ tool ที่ล้มเหลว สมมติ tool RefundOrder โยน exception ว่าออเดอร์นี้คืนเงินไม่ได้แล้ว model จะได้รับข้อความนี้

The tool call failed: Order 1234 can no longer be refunded.

model ก็ตอบผู้ใช้ต่อได้ว่าออเดอร์นี้เลยระยะคืนเงินแล้ว หรือเสนอทางอื่นให้แทน

ส่วนที่ไม่เปลี่ยน

  • exception ยังส่งเข้า exception handler ผ่าน report() เหมือนเดิม ถ้าต่อ Sentry หรือ Watchtower ไว้ก็ยังเห็น
  • event ToolFailed ยังยิงอยู่
  • ถ้า stream อยู่ ความล้มเหลวจะมาเป็น event ToolResult ที่ successful เป็น false
  • SDK กู้เฉพาะ exception ที่มาจากตัว tool ถ้า event listener โยน exception งานยังหยุดทั้งรอบ

ถ้าอยากได้พฤติกรรมเดิม ให้โยนซ้ำจาก listener ของ ToolFailed เพราะ exception จาก listener ยังหยุดงานได้

use Illuminate\Support\Facades\Event;
use Laravel\Ai\Events\ToolFailed;

Event::listen(ToolFailed::class, fn (ToolFailed $event) => throw $event->exception);

ทดสอบ flow อนุมัติ tool ด้วย Agent::fake() ได้ครบ

tool ที่ต้องมีคนอนุมัติจะหยุดงานไว้กลางทาง รอให้คนกดอนุมัติหรือปฏิเสธแล้วค่อยทำต่อ เดิมพอใช้ Agent::fake() ใน test แล้วสั่งทำต่อหลังอนุมัติ fake จะข้าม tool ไปเลย tool ไม่ได้รัน ผลก็ไม่ได้เก็บ test จึงเช็คได้แค่ครึ่งแรกของ flow

1.4 ให้ fake ทำงานต่อแบบเดียวกับ provider จริง ถ้าอนุมัติ tool จะรันและเก็บผล ถ้าปฏิเสธ model จะได้ผลว่าคนไม่อนุมัติ แล้ว fake จึงคืนคำตอบถัดไปที่เราเตรียมไว้ ตอนนี้จึงเขียน test ได้ตั้งแต่ agent ขออนุมัติ คนกดอนุมัติ tool ทำงานจริง จนถึงคำตอบสุดท้าย

test เดิมที่ปฏิเสธโดยไม่ใส่เหตุผลแล้วรอคำตอบจาก model จะพัง เพราะตอนนี้การปฏิเสธที่ไม่มีเหตุผลจะจบรอบนั้นทันทีเหมือน provider จริง แก้ด้วยการใส่เหตุผล เช่น Decision::reject('Not now.')

คำตอบทุกประเภทอ่าน raw response ได้แล้ว

คำตอบแบบข้อความมี raw ให้อ่าน HTTP response ดิบมาตั้งแต่ 0.10.3 ตอนนี้ classification, embeddings, transcription, image และ reranking มีด้วย ใช้อ่านช่องที่ SDK ไม่ได้ map ไว้ให้ เช่น ค่าใช้จ่ายต่อ request ที่ OpenRouter ส่งมาใน usage.cost

$response = Classification::of($ticket)
    ->questions($questions)
    ->classify(provider: 'openrouter');

$cost = $response->raw->json('usage.cost');

raw จะเป็น null หลัง serialize เช่น ตอนส่งผ่าน queue และใน embeddings ถ้าทุก vector มาจาก cache ก็เป็น null เหมือนกัน ควรเช็คก่อนอ่าน

เครดิต OpenAI หมดแยกจาก rate limit

OpenAI ตอบ 429 ทั้งตอนยิงถี่เกินและตอนเครดิตหมด เดิม SDK แปลงทุก 429 เป็น RateLimitedException และอาจ retry ให้ถ้ามี header Retry-After สั้น ๆ ซึ่งถ้าเครดิตหมดจริง retry เท่าไหร่ก็ไม่ผ่าน

1.4 แยก 429 ที่มีรหัส insufficient_quota หรือ credit_balance_exhausted ออกมาเป็น InsufficientCreditsException และไม่ retry ส่วน 429 แบบอื่นยังเป็น RateLimitedException เหมือนเดิม failover ไป provider สำรองยังทำงานกับทั้งสองแบบ

ถ้ามีโค้ดที่ดัก RateLimitedException ไว้แจ้งเตือนว่าเครดิตหมด ให้เปลี่ยนไปดัก InsufficientCreditsException แทน แล้วแยกการแจ้งเตือนเป็นสองเรื่องได้เลย เครดิตหมดต้องมีคนไปเติมเงิน ส่วน rate limit รอแล้วหายเอง

อัปเดตอื่น ๆ

  • error ระหว่าง stream ที่ broadcast ออกไปใช้ชื่อ event error ชื่อเดียว ไม่เปลี่ยนตามรหัสของ provider อย่าง 403 หรือ PERMISSION_DENIED อีก รหัสเดิมยังอยู่ใน type ของ payload ส่วน job ที่พังยังเป็น stream_failed
  • ตั้งชื่อบทสนทนา ตอนนี้รันเป็น agent run ปกติ ใช้ provider เดียวกับรอบนั้นและ model ที่ถูกที่สุด listener ของ PromptingAgent, AgentPrompted, AgentFailed จึงเห็นรอบตั้งชื่อด้วย แยกจากรอบของผู้ใช้ด้วย parentInvocationId ถ้าตั้งชื่อไม่สำเร็จ SDK ใช้ข้อความ prompt เป็นชื่อแทนและไม่ทำให้รอบนั้นพัง
  • แก้ tool call ของ Anthropic ที่จบด้วย end_turn และของ provider ที่ใช้ API แบบ OpenAI ที่ส่ง tool call มาโดยไม่มี index ให้รันได้ถูก
  • embeddings แบบ batch ของ OpenAI คืนผลตามลำดับที่ส่งไป
  • model ของ Mistral อัปเดตเป็นรุ่นล่าสุด

เช็คห้าจุดนี้ก่อนอัปเกรด

ถ้าโค้ดเรา… สิ่งที่เปลี่ยนใน 1.4 ต้องทำ
รอ AgentFailed เมื่อ tool พัง event นี้ไม่ยิงแล้วเมื่อ tool โยน exception ฟัง ToolFailed แทน หรือโยนซ้ำจาก listener
มี test ที่ปฏิเสธ tool โดยไม่ใส่เหตุผล การปฏิเสธที่ไม่มีเหตุผลจบรอบทันที ใส่เหตุผลใน Decision::reject()
ดัก RateLimitedException ตอนเครดิตหมด เครดิตหมดโยน InsufficientCreditsException ดัก exception ตัวใหม่
หน้าเว็บฟัง error ตามรหัสของ provider ทุก error ใช้ชื่อ error ฟัง .error แล้วอ่าน type
นับรอบใน listener ของ agent event รอบตั้งชื่อบทสนทนาเข้ามาด้วย กรองด้วย parentInvocationId

รายการ PR ทั้งหมดอยู่ใน release notes v1.4.0 และตัวอย่างโค้ดของทุกข้ออยู่ในบทความต้นฉบับของ Laravel News

อ่านต่อ

ล่าสุด

ดูทั้งหมด →