▶SUBTHAIแปลไทย · ซับ · พากย์⚙
บทความแปลภาษาไทย · YouTube

Day 2B: Model Context Protocol (MCP) — Full Tutorial & Hands-On Demo

0:000:00
เล่นเสียงต้นฉบับของวิดีโอ พร้อมแสดงซับไทยซิงก์ตามเวลา
กำลังโหลดวิดีโอ…
01

สรุปย่อ

ประเด็นสำคัญจากวิดีโอ

- **ช่อง:** Adspolitan Knowledge Studio · **ความยาว:** ~28 นาที · **ลิงก์:** https://www.youtube.com/watch?v=4jbDR1Agutc

# สรุป: Day 2B: Model Context Protocol (MCP) — Full Tutorial & Hands-On Demo
- **ช่อง:** Adspolitan Knowledge Studio · **ความยาว:** ~28 นาที · **ลิงก์:** https://www.youtube.com/watch?v=4jbDR1Agutc

## ประเด็นหลัก
- แบบฝึกหัด Day 2 Part B ของคอร์ส Google × Kaggle 5-Day AI Agents Intensive โฟกัสสอง pattern หลัก คือ MCP integration และ long-running operations
- MCP (Model Context Protocol) คือมาตรฐานเปิดที่ให้ agent ใช้ integration ที่ชุมชนสร้างไว้แล้ว เข้าถึงข้อมูลภายนอกได้โดยไม่ต้องเขียนโค้ดเชื่อมต่อเอง
- ขั้นตอนใช้ MCP มีแค่สี่อย่าง: เลือก MCP server และ tool, สร้าง toolset, เพิ่ม agent เข้ากับการเชื่อมต่อ แล้วรันทดสอบ
- เบื้องหลัง MCP มีการ launch server, handshake สร้างช่องทางสื่อสาร, tool discovery แล้วผลลัพธ์จาก server วนกลับมาที่ agent อย่างราบรื่น
- Demo ใช้ Everything MCP server กับ tool get_tiny_image (ภาพทดสอบ 16x16 pixel แบบ base64) โดยรันผ่าน npx และกรองด้วย tool filter
- Long-running operations / human-in-the-loop คือการให้ tool หยุดค้างรอการอนุมัติจากมนุษย์แล้วจึงทำงานต่อ เช่น ธุรกรรมการเงิน, bulk operations, compliance checkpoint, ค่าใช้จ่ายสูง หรืองานที่ย้อนกลับไม่ได้
- Tool context ให้ความสามารถ request approval และ check approval status เป็นหัวใจของการหยุดรออนุมัติ
- ตัวอย่าง shipping coordinator agent: ออเดอร์ไม่เกิน 5 คอนเทนเนอร์อนุมัติอัตโนมัติ เกิน 5 ให้หยุดรอมนุษย์ตัดสินใจ แล้วคืนสถานะ approved หรือ rejected
- แนวคิดเทคนิคสำคัญ: event (ทุกการเรียก tool/ผลลัพธ์กลายเป็น event), ADK request confirmation event (สัญญาณหยุด) และ invocation ID (key บอก ADK ว่าจะ resume execution ไหน ถ้าไม่มีจะเริ่มใหม่แทน)
- ResumabilityConfig ใช้ห่อ agent เป็น resumable app ที่หยุดแล้วทำต่อได้ ขณะนี้ยังเป็น experimental และอาจเปลี่ยนแปลงในอนาคต
- ทดสอบครบสามสถานการณ์: 3 คอนเทนเนอร์ผ่านอัตโนมัติ, 10 ไป Rotterdam หยุดรอแล้วอนุมัติ, 8 ไป Los Angeles หยุดรอแล้วถูกปฏิเสธ
- แบบฝึกหัด optional: สร้าง agent สร้างภาพผ่าน MCP server ที่ขออนุมัติเมื่อ generate หลายภาพพร้อมกัน

## ความเห็นสรุป
วิดีโอนี้อธิบาย MCP และ human-in-the-loop ได้เป็นระบบดีมาก โดยเดินจากแนวคิดไปสู่โค้ดจริงทีละส่วน จุดเด่นคือการใช้ flowchart ประกอบโค้ด shipping order ทำให้เข้าใจการหยุด-รอ-ทำต่อได้ง่าย และการเน้นว่า invocation ID คือ key ของการ resume ช่วยขจัดความสับสนเรื่อง event handling ซึ่งเป็นส่วนที่ยากที่สุดของแบบฝึกหัดนี้ เหมาะสำหรับคนที่อยากทำ agent ระดับ production ที่ต้องมีการควบคุมและอนุมัติจริงจังครับ
02

คำแปลเต็ม

แปลตามบทสนทนาต้นฉบับ ปรับเป็นภาษาไทยธรรมชาติ

ในวิดีโอนี้เราจะสำรวจแนวปฏิบัติที่ดีสำหรับ tool ซึ่งรวมถึงการใช้ MCP และ long-running operations ครับ นี่คือส่วน Part B ของแบบฝึกหัด Day 2 เราจะกด Explore เพื่อเข้าไปยัง notebook แล้วกด Copy and Edit เพื่อให้ได้ notebook ที่แก้ไขได้ของเราครับ ขั้นตอนเริ่มต้นซึ่งเป็นส่วนของการเตรียมสภาพแวดล้อมจะคล้ายกับที่เราเคยทำมาแล้ว ผมจึงไม่ขอเสียเวลาอธิบายซ้ำมากนักครับ

สิ่งที่แบบฝึกหัดนี้จะให้เราทำมีอะไรบ้าง? เราจะเชื่อมต่อกับ MCP server ภายนอก เราจะ implement long-running operations ที่สามารถหยุดการทำงานของ agent ค้างไว้เพื่อรอ input จากภายนอก เราจะสร้าง resumable workflow (workflow ที่หยุดแล้วกลับมาทำต่อได้) ซึ่งคงสถานะ (state) ข้ามช่วงที่บทสนทนาถูก interrupt และเราจะเข้าใจว่าควรใช้ pattern เหล่านี้เมื่อไหร่และอย่างไรครับ

Section 1 ซึ่งเป็นส่วนเตรียมสภาพแวดล้อมก็เหมือนกับที่เราทำในทุก notebook มาตลอด ผมจะไม่ใช้เวลามากตรงนี้แล้วไปกันต่อที่ section ถัดไปเลยนะครับ ขอรันโค้ดนี้เร็ว ๆ ไปที่ Add-ons กด Secret เช็ค Google API key แล้วรันเลย เอาล่อ การตั้งค่าและการยืนยันตัวตนเสร็จสมบูรณ์แล้ว ผมจะพับส่วนนี้ทิ้งไปครับ

ต่อไปเราจะ import คอมโพเนนต์ของ ADK ครับ คุณจะเห็นว่ามีคอมโพเนนต์เพิ่มขึ้นเรื่อย ๆ เมื่อเราไปจากแบบฝึกหัดหนึ่งสู่อีกแบบฝึกหัดหนึ่ง ขอรันอันนี้ก่อน แล้วระหว่างรอเรามาดูกันว่าแบบฝึกหัดนี้ใช้คอมโพเนนต์อะไรบ้าง เรามี agent, Gemini, runner และ session ซึ่งเป็นของคู่กันเหมือนเดิม ตรงนี้เราเพิ่ม MCP toolset (ชุดเครื่องมือจาก MCP) เข้ามา เรา import tool context ซึ่งทำไปแล้วใน Part A ของ Day 2 ตัวใหม่คือ StdioConnectionParams (พารามิเตอร์การเชื่อมต่อแบบ stdio) เพราะเราจะเชื่อมกับระบบภายนอก และ ServerParameters ด้วยเหตุผลเดียวกัน จากนั้นคือ ResumabilityConfig (การตั้งค่าความสามารถหยุดแล้วทำต่อ) เพราะในแบบฝึกหัดนี้เราจะสร้างระบบหรือ agent ที่ resumable แล้วก็ function tool ต่าง ๆ ครับ ตอนนี้คอมโพเนนต์ของ ADK import สำเร็จเรียบร้อยแล้ว ไปกันต่อที่ส่วนตั้งค่า retry option ครับ เราอาจใส่คำสั่ง print ได้เหมือนที่ผมชอบทำ แต่ครั้งนี้คงไม่จำเป็นแล้ว ถือว่าเรียบร้อยครับ

ตอนนี้เข้าสู่ Section 2 ซึ่งเป็นเรื่อง Model Context Protocol ครับ ก่อนจะเริ่มเขียนโค้ด มาทำความเข้าใจกันก่อนว่า MCP คืออะไร MCP ย่อมาจาก Model Context Protocol ครับ มันคือมาตรฐานเปิด (open standard) ที่ให้ agent ใช้งาน integration ที่ชุมชนสร้างไว้แล้ว แทนที่คุณจะต้องเขียน integration และ API client ของคุณเอง คุณแค่เชื่อมต่อไปยัง MCP server ที่มีอยู่แล้ว มันจะช่วยให้ agent เข้าถึงข้อมูลภายนอกแบบสด ๆ จากฐานข้อมูล API และ server ต่าง ๆ ได้โดยไม่ต้องเขียนโค้ด integrate เอง อีกทั้งยังช่วยให้คุณใช้ประโยชน์จาก tool ที่ชุมชนสร้างไว้ผ่าน interface แบบมาตรฐาน และที่สำคัญคุณขยายขีดความสามารถได้โดยเชื่อมต่อกับ server เฉพาะทางหลายตัวพร้อมกันครับ

แล้ว MCP ทำงานอย่างไร? MCP เชื่อม MCP client หรือ agent ของคุณ เข้ากับ MCP server ภายนอก เช่น Slack MCP, Google Maps MCP, GitHub server ฯลฯ ผ่านโปรโตคอล MCP มาตรฐานครับ ในแผนภาพนี้ MCP server หมายถึงตัวที่ให้ tool เฉพาะทาง เช่น การสร้างภาพ การเข้าถึงฐานข้อมูล ฯลฯ ส่วน MCP client ซึ่งก็คือ client ของคุณ เป็นผู้ใช้ tool เหล่านั้น และ server ทั้งหมดนี้ทำงานด้วยวิธีเดียวกันผ่าน interface มาตรฐานเดียวกัน คือโปรโตคอลนี้ครับ ประเด็นถัดไปคือเราใช้ MCP กับ agent ได้อย่างไร? workflow นั้นเรียบง่ายมาก คุณเลือก MCP server และ tool หนึ่งตัว จากนั้นสร้าง toolset ซึ่งก็คือการตั้งค่าการเชื่อมต่อนั่นเอง แล้วคุณเพิ่ม agent ของคุณเข้าไปในการเชื่อมต่อนั้น แล้วรันและทดสอบ agent ครับ ฟังดูง่ายใช่ไหมครับ ลงมือกันเลย

ก่อนเริ่ม ขอบอกไว้ก่อนว่าสำหรับ demo นี้เราจะใช้ Everything MCP server ซึ่งออกแบบมาเพื่อทดสอบ MCP integration ครับ และมันมี tool ชื่อ get_tiny_image ที่คืนภาพทดสอบง่าย ๆ ขนาด 16x16 pixel ในรูปแบบ base64 encoding (การเข้ารหัสข้อมูลภาพเป็นข้อความ) ถ้าอยากได้ข้อมูลเพิ่มหรือหา server อื่น ๆ ให้ไปที่ modelcontextprotocol.io ในส่วนตัวอย่างครับ อันนี้เป็น server สำหรับ demo เพื่อเรียนรู้ MCP เท่านั้น แต่ใน production คุณใช้อันนี้ไม่ได้นะครับ คุณจะใช้ server ของ Google Maps, Slack, Discord ฯลฯ แทน

ถึงขั้นตอนที่สองตามที่กล่าวไว้ เราจะสร้าง MCP toolset ก่อน ในขั้นที่หนึ่งเราตัดสินใจแล้วว่าจะใช้ server ไหนและใช้ tool อะไร ทีนี้ขั้นที่สองคือสร้าง MCP toolset ซึ่งใช้สำหรับเชื่อม ADK agent เข้ากับ MCP server ครับ อย่างที่บอกไว้ก่อนหน้า เราจะใช้ node package runner รัน MCP server ไม่ใช่ runner ปกติ เราจะเชื่อมต่อไปยัง MCP server นี้ และกรองให้ใช้เฉพาะ tool ชื่อ tiny image เท่านั้น ถ้าเข้าไปดูใน server จริง ๆ มันมี tool อื่นอีก แต่ตอนนี้เราต้องใช้แค่ตัวนี้ ถ้าอยากลอง ก็เข้าไปดูรายละเอียดของ server แล้วเลือกดูว่ามี tool อื่นที่อยากใช้อีกไหม ลองรันโค้ดนี้ครับ และเราก็สร้าง MCP tool สำเร็จเรียบร้อยแล้ว

โค้ดนี้ทำอะไรบ้าง? ตรงนี้เรากำลังนิยาม mcp_image_server เขียน connection params ของมัน เรา import connection param กับ server parameter ไว้แล้ว ตอนนี้จึงกำหนดค่าตรงนี้ เราใส่คำสั่ง node package runner เพื่อให้รัน MCP server ผ่าน npx แล้วเพิ่ม argument เพื่อยืนยันการติดตั้งอัตโนมัติ เราจึงไม่ต้องติดตั้งเอง และเราระบุชื่อของ server ตรงนี้ ส่วน tool filter ตามที่บอกไปแล้วคือ get_tiny_image และเรากำหนด timeout ไว้ที่ 30 ครับ

แล้วเบื้องหลังเกิดอะไรขึ้นบ้าง? แม้ผมจะอธิบายไปแล้วว่ามันทำอะไร ตรงนี้ก็อธิบายสิ่งเดียวกันแต่ในรูปแบบเทคนิคหน่อย ผมขออ่านสรุปให้ฟังครับ ลองโยงกับที่เราเพิ่งคุยกันดูนะครับ เบื้องหลังมีการ launch server คือเรากำลังเปิด server ขึ้นมา จากนั้นเราทำ handshake (การจับมือเริ่มต้นสื่อสาร) คือเรากำลังสื่อสารกัน สร้างช่องทางการสื่อสารขึ้นมาผ่าน connection params และ server parameters ต่อมาเราทำ tool discovery (การค้นพบ tool) ซึ่งก็คือ tool filter ที่เลือก get_tiny_image ตรงนี้ server จะบอก ADK ว่าโอเค ฉันจะให้ฟังก์ชันการทำงานนี้กับเธอ เมื่อเรามี function หรือ tool นี้แล้ว แปลว่าเรา integrate tool เข้ากับ agent สำเร็จโดยอัตโนมัติเรียบร้อย จากนั้นเราจะเรียกใช้ function tiny image คือเมื่อใดก็ตามที่ agent เรียก function นี้ ADK จะส่งต่อข้อมูลของ function นั้นไปยัง MCP server และตอบแทน ผลลัพธ์จาก server ถูกส่งกลับมาที่ agent อย่างราบรื่นครับ เหตุผลที่ทั้งหมดนี้สำคัญคือ คุณได้เข้าถึง tool ทันทีโดยไม่ต้องเขียนโค้ด integrate เอง ผมว่าเราแทบไม่ได้เขียนอะไรมากมายเลยใช่ไหมครับ มีแค่ไม่กี่ขั้นตอน แค่ต้องจำรูปแบบที่ถูกต้องไว้ครับ

เมื่อ MCP tool ถูกสร้างแล้ว ขั้นถัดไปคือเพิ่ม MCP tool เข้าไปให้ agent ครับ มาเพิ่ม MCP server นี้เข้าไปใน array ของ tools ของ image agent กันเถอะ คุณกำหนด retry option ของ agent และอื่น ๆ ไว้เรียบร้อยแล้ว instructions คือให้ใช้ MCP tool สร้างภาพตามคำขอของผู้ใช้ และ tool ก็คือ MCP image server ที่เราเพิ่งสร้างไปครับ ระบบบอกว่าสำเร็จ ตอนนี้เราต้องสร้าง runner ซึ่งก็เป็น InMemoryRunner เหมือนเดิม และ agent คือ image agent เสร็จแล้วครับ ทีนี้เราจะส่ง prompt ให้ agent สร้างภาพ ลองดูว่ามันทำได้อย่างไร ค่า prompt เริ่มต้นคือ provide a sample tiny image ผมรันอันนี้ครับ ใน session ใหม่ มันบอกว่าผู้ใช้ขอภาพ tiny image ตัวอย่าง แล้วตอนนี้มันให้ข้อมูลของภาพมาพร้อมบอกว่านี่คือ tiny image ภาพด้านบนนี้คือ MCP tiny image ครับ

ต่อไปเราจะให้มันแสดงภาพออกมา เพราะที่ได้มาเป็นข้อมูลภาพแบบ base64 encoded เราจึงยังไม่รู้ว่าภาพหน้าตาเป็นอย่างไร ตอนนี้เราจะให้มันแสดงภาพโดยใช้ function นี้ครับ ตรงนี้เรา import display จาก IPython display library และ Image ในชื่อ Img พร้อม import base64 เพื่อให้ข้อมูล base64 ถูกแปลงเป็นภาพที่เหมาะสม แล้วรันโค้ดนี้ ก็ได้ภาพออกมาอย่างที่เห็นครับ สวยงาม

ขั้นถัดไปคือเราต้องขยายไปยัง MCP server ตัวอื่นด้วย นั่นคือ MCP server สำหรับ dataset และการทำงานกับ notebook ซึ่งโดยพื้นฐานแล้วช่วยให้ agent ของคุณโต้ตอบกับ dataset notebook และการคำนวณต่าง ๆ ของ Kaggle ครับ ตัวอย่างการเชื่อมต่อระบุไว้ตรงนี้ เราทำแบบนี้มาแล้วกับ Everything MCP server และตรงนี้เราใช้ MCP remote กับ kaggle.com มีแบบเดียวกันนี้สำหรับ GitHub ด้วย นี่เป็นข้อมูลที่ดี ถ้าอยากลองก็สามารถรันดูได้เลย เป็นเพียงข้อมูลที่ควรรู้ไว้ครับ

ต่อไปคือ long-running operation ซึ่งก็คือ human-in-the-loop ครับ เกิดอะไรขึ้นตรงนี้? จนถึงตอนนี้ tool ทุกตัวทำงานเสร็จแล้วคืนข้อมูลกลับมาทันทีใช่ไหมครับ ผู้ใช้ส่ง prompt แล้ว agent เรียก tool ใดตัวหนึ่ง tool คืนผลลัพธ์ แล้ว agent จึงตอบผู้ใช้ด้วยผลลัพธ์นั้น บางครั้งปรับแต่งเนื้อหาก่อน บางครั้งส่งตรง ๆ ตาม workflow ที่เลือกใช้ แต่คำถามคือ ถ้า tool ของคุณเป็นงานที่ใช้เวลานาน หรือคุณต้องการการอนุมัติจากมนุษย์ก่อนทำ action เสร็จสิ้น เช่น คุณต้อง merge อะไรบางอย่างแล้วต้องได้รับอนุมัติจาก release manager หรือผู้จัดการระดับสูงทำนองนี้ นี่เป็นแค่ตัวอย่างคร่าว ๆ นะครับ ในสถานการณ์แบบนี้ tool จำเป็นต้องหยุดค้างรออยู่ รอ input จากภายนอกซึ่งก็คือการอนุมัติของมนุษย์ แล้วจึงกลับมาทำงานต่อ workflow แบบนี้เรียกว่า long-running operation หรือ human-in-the-loop (มีมนุษย์เข้ามามีส่วนร่วมในวงจร) เพราะมีความต้องการ input จากภายนอกแล้วจึงเริ่มกระบวนการต่อครับ

แล้วสถานการณ์ใดบ้างที่คุณควรใช้ long-running operation? อย่างแรกคือธุรกรรมการเงิน ที่คุณอาจต้องใส่ OTP หรือมีการยืนยันสิทธิ์ใด ๆ อย่างที่สองคือการทำงานเป็นชุดใหญ่ (bulk operations) เช่น ต้องลบข้อมูลพันรายการ คุณต้องยืนยันก่อนว่าแน่ใจหรือไม่ที่จะลบข้อมูลมากขนาดนี้ อย่างถัดไปคือจุดตรวจสอบการปฏิบัติตามกฎระเบียบ (compliance checkpoint) ซึ่งเห็นได้ชัดมาก คือคุณต้องได้รับอนุมัติตามข้อกำหนด จากนั้นอีกตัวอย่างคือ action ที่มีค่าใช้จ่ายสูง เช่น การสั่ง spin up เซิร์ฟเวอร์ 50 เครื่อง จริง ๆ เหรอ แน่ใจนะ? คำถามก็จะเป็นแบบนี้ครับ คุณจึงต้องให้มนุษย์ยืนยัน อีกตัวอย่างคือการทำงานที่ย้อนกลับไม่ได้ (irreversible operations) เช่น การลบถาวรโดยสมบูรณ์ ถ้าวันนี้ผมต้องลบ account แบบถาวร ผมต้องพิมพ์ delete แล้วยืนยันอีกครั้ง ใช่ไหมครับ นั่นคือการยืนยันที่ต้องมี ซึ่งก็คือ human-in-the-loop การอนุมัติโดยมนุษย์นั่นเองครับ

แล้วตอนนี้เราจะทำอะไร? เราจะสร้าง shipping coordinator agent ที่มี tool หนึ่งตัวซึ่งจะอนุมัติออเดอร์เล็ก ๆ ที่ต่ำกว่าห้าคอนเทนเนอร์โดยอัตโนมัติ และจะหยุดค้างเพื่อขออนุมัติสำหรับออเดอร์ใหญ่ที่เกินห้าคอนเทนเนอร์ แล้วจึงเสร็จสิ้นหรือยกเลิกตามการตัดสินใจอนุมัตินั้นครับ นี่สาธิตให้เห็นว่า pattern หลักของ long-running operation ถูกทำให้เป็นอัตโนมัติด้วย คือมันจะหยุด จะรอ input ของมนุษย์ แล้วจะทำงานต่อครับ เริ่มกันเลย

สำหรับ demo นี้ เราต้องเขียน function ให้สมบูรณ์ก่อน โดยมี tool context เป็นพารามิเตอร์ครับ tool context parameter อยู่ในรูปแบบฟังก์ชัน (function signature) ซึ่งหมายความว่า ADK จะจัดหา object นี้ให้อัตโนมัติเมื่อ tool ของคุณทำงาน มันให้ความสามารถหลักสองอย่าง คือ request approval (ขออนุมัติ) ก็คือเรียกใช้ confirmation แล้วก็ check approval status (ตรวจสอบสถานะการอนุมัติ) โดยจะอ่านจาก confirmation นั้น ส่วน large_order_threshold เท่ากับห้าคือค่าเกณฑ์ (threshold) ที่เราตั้งไว้ครับ

เรากำลังนิยาม function ที่ชื่อ place_shipping_order ครับ เรากำหนดอาร์กิวเมนต์ไว้ตรงนี้ docstring บอกว่า function วางออเดอร์ขนส่งนี้ต้องได้รับอนุมัติสำหรับออเดอร์ที่มากกว่าห้าคอนเทนเนอร์ ซึ่งเป็นเกณฑ์ของออเดอร์ใหญ่ อาร์กิวเมนต์ number_of_containers คือจำนวนคอนเทนเนอร์ที่จะขนส่ง ส่วน destination คือปลายทางการขนส่ง ค่าที่คืนกลับเป็น dictionary พร้อม order_status ครับ สถานการณ์แรก ออเดอร์เล็กที่น้อยกว่าหรือเท่ากับห้าคอนเทนเนอร์ควรได้รับการอนุมัติอัตโนมัติ ทางนี้มีเงื่อนไข if ไว้สำหรับกรณีนี้แล้ว อย่างไรก็ตาม สถานการณ์ที่สองคือ ครั้งแรกที่ tool นี้ถูกเรียก ออเดอร์ใหญ่ต้องได้รับอนุมัติจากมนุษย์ คุณต้องหยุดรอตรงนี้ใช่ไหมครับ ในโค้ดนี้ เราบอกว่าถ้านี่ไม่ใช่ออเดอร์ใหญ่ตามเกณฑ์ที่นิยามไว้เป็นห้าตรงบนสุด ถ้าเงื่อนไขนี้ไม่เป็นจริง คุณจะมาจากตรงนี้ไปยังส่วนนี้ แล้วมองหา confirmation โดย hint คือ large order จำนวนคอนเทนเนอร์ไปยังปลายทางนี้ คุณต้องการอนุมัติหรือไม่ นี่คือข้อความที่ถูกส่งออกไป แล้วสถานะที่คืนกลับจะเป็น pending โดยข้อความจะบอกว่าออเดอร์สำหรับกี่คอนเทนเนอร์ต้องได้รับอนุมัติครับ

สถานการณ์ถัดไปคือ tool นี้ถูกเรียกอีกครั้งและกำลัง resume ทำต่อ เมื่อคุณอนุมัติหรือไม่อนุมัติ มันจะ resume และทำขั้นตอนที่เกี่ยวข้องต่อไป คือ handle approval response ที่คุณ resume ตรงนี้ ถ้า confirmation นี้ถูกยืนยันแล้ว คุณคืนค่าสถานะ approved พร้อมรายละเอียดออเดอร์ จำนวนคอนเทนเนอร์ และอย่างอื่นทั้งหมด ถ้าไม่ได้อนุมัติ มันจะบอกว่า rejected และบอกว่าออเดอร์ถูกปฏิเสธสำหรับกี่คอนเทนเนอร์ไปยังปลายทางใดครับ ลองรันและสร้าง function นี้กัน เราสร้าง long-running function สำเร็จเรียบร้อยแล้วครับ ผมอธิบายค่อนข้างคร่าว ๆ มา เพราะอยากใช้ flowchart ตัวนี้อธิบายโค้ดให้ฟังมากกว่า เพราะมันเข้าใจง่ายกว่าครับ ถ้าจำนวนคอนเทนเนอร์มากกว่าหรือเท่ากับห้า มันจะอนุมัติโดยอัตโนมัติ แต่ถ้าไม่ใช่ ก็จะเข้าสู่ function นี้ ถ้าตอบว่าใช่ คืออนุมัติ มันจะคืนค่า approve ถ้าตอบว่าไม่ มันจะ reject ถ้า function นั้นยังไม่มีอยู่ มันจะไปหาผู้ใช้เพื่อขอการยืนยัน และระหว่างนั้นสถานะจะค้างเป็น pending ครับ ถ้าคุณย้อนกลับไปอ่านโค้ดนี้อีกครั้ง คุณจะเข้าใจได้ง่ายขึ้นมากครับ

ต่อไปขอเล่าให้ฟังว่าทั้งสามสถานการณ์ทำงานอย่างไรในบริบทนี้ อย่างแรกคือออเดอร์เล็ก มันจะอนุมัติสถานะทันทีโดยอัตโนมัติ จะไม่เข้าไปที่ function นี้ตามที่ระบุไว้ ใช่ไหมครับ มันแค่อนุมัติเลย อย่างไรก็ตาม ถ้าเงื่อนไขนั้นไม่เป็นจริง มันจะเข้าสู่การเรียกครั้งแรกซึ่งจะตรวจสอบก่อนว่ามี confirmation หรือยัง ถ้ายังไม่มี confirmation มันจะขอ confirmation ขอการอนุมัติจากมนุษย์ และสถานะจะถูกตั้งเป็น pending ทันที และอย่างที่สาม ถ้าออเดอร์ใหญ่ได้รับการตอบรับ มันจะตรวจจับได้ แล้วอิงจากใช่หรือไม่ มันจะคืนสถานะ approved หรือ rejected ครับ

มาถึง section ถัดไปซึ่งคือสร้าง agent, app และ runner ครับ ขั้นแรกเราจะสร้าง agent แล้วเราจะห่อมันไว้ใน resumable app แล้วจึงรัน agent ทั้งหมด ขั้นแรกคือสร้าง agent เราเพิ่ม tool เข้าไปให้ agent นั่นคือสิ่งแรกที่เราทำ ตรงนี้คือ shipping agent พร้อม name, model, retry option แล้ว instruction คือ คุณคือผู้ช่วยโคออร์ดิเนเตอร์ฝ่ายขนส่ง เมื่อผู้ใช้ขอขนส่งคอนเทนเนอร์ ให้ใช้ tool ชื่อ place_shipping_order พร้อมจำนวนคอนเทนเนอร์และปลายทาง ถ้าสถานะของออเดอร์เป็น pending ให้แจ้งผู้ใช้ว่าต้องได้รับอนุมัติ เป็นต้น ระบุว่า tool คือ function tool ชื่อ place_shipping_order แล้วรัน ก็สร้างเสร็จแล้วครับ ไปที่อันที่สองซึ่งเป็นการห่อ resumable app คุณห่อมันตรงนี้ในลักษณะที่ใช้ ResumabilityConfig ที่เรา import ไว้ตั้งแต่ต้นแบบฝึกหัดนี้ ลองรันดู มี warning ขึ้นมาบอกว่า config นี้เป็นแบบทดลอง (experimental) และอาจเปลี่ยนแปลงหรือถูกลบออกในเวอร์ชันอนาคตโดยไม่แจ้งล่วงหน้า ซึ่งไม่เป็นไรครับ เราไม่ต้องกังวลตอนนี้ ค่าเป็น true ซึ่งเป็นสิ่งที่เราต้องการ ถือว่าเสร็จเรียบร้อยครับ

ขั้นถัดไปคือสร้าง session และ runner ขึ้นกับ app แทนที่จะรัน agent ตรง ๆ เราจะใช้ app แล้วส่งมันผ่าน runner ครับ session service ก็เป็น InMemorySessionService เหมือนเดิม เรากำลังสร้าง runner ด้วย resumable app ดังนั้น shipping runner ตรงนี้จะมี app เท่ากับตัวนี้ คือเราจะส่ง app เข้าไปแทน agent นั่นเองครับ รันไปแล้ว runner ถูกสร้างเรียบร้อย ก่อนไปขั้นถัดไปในการสร้าง workflow ขอสรุปสั้น ๆ ตามที่ระบุไว้ตรงนี้ครับ เรามี tool ที่สามารถหยุดค้างเพื่อรอการอนุมัติ เราสร้างสิ่งนั้นแล้ว เราสร้าง agent ที่ใช้ tool นี้ จากนั้นเราสร้าง resumable app ซึ่งก็คือ shipping app แล้วเราจึงสร้าง runner ที่จัดการการหยุดและทำต่อของ app นี้ได้ครับ

ตอนนี้มาถึง Section 4 ครับ ใน Section 4 โค้ด workflow ใช้แนวคิดของ ADK อย่าง session, runner และ event ส่วนนี้จะครอบคลุมเฉพาะสิ่งที่คุณต้องรู้สำหรับ long-running operations ใน notebook นี้ ถ้าอยากรู้เพิ่มเติม แบบฝึกหัดถัดไปใน Day 3 จะพูดถึงเรื่องนั้น คุณสามารถดูเอกสาร ADK และวิดีโอที่เกี่ยวข้องได้ ซึ่งผมจะใส่ลิงก์ไว้ในคำอธิบายวิดีโอครับ ตรงนี้คือส่วนที่ critical ที่สุด เพราะทุกอย่างมาตลอดแบบสบาย ๆ จนถึงตอนนี้ ส่วนที่ผมว่า critical ที่สุดคือการจัดการ event ใน workflow ครับ หมายความว่าอย่างไร? agent จะจัดการการหยุดและทำต่อโดยอัตโนมัติ อย่างที่ผมพูดมาตั้งแต่ต้นของแบบฝึกหัดนี้ ทุก workflow แบบ long-running operation จำเป็นต้องตรวจจับการหยุด รับคำตัดสินใจจากมนุษย์ แล้วจึงให้ agent ทำงานต่อครับ เพื่อเข้าใจให้ดีขึ้น เราต้องเข้าใจแนวคิดทางเทคนิคหลัก ๆ ก่อน

ตัวแรกคือ event (เหตุการณ์ในระบบ) ครับ ADK สร้าง event ขึ้นเมื่อ agent ทำงาน การเรียก tool การตอบกลับของโมเดล ผลลัพธ์ของ function ล้วนกลายเป็น event ทั้งหมด ตอนนี้ตัว ADK request confirmation นี้ก็เป็น event หนึ่งในโค้ดของเรา มันเป็น special event ที่ส่งสัญญาณว่าให้หยุดค้างตรงนี้ครับ ตัวถัดไปคือ invocation ID คือทุกครั้งที่มีการเรียก run sync จะได้ ID ที่ไม่ซ้ำกันมาหนึ่งตัว นั่นคือ invocation ID หน้าตาประมาณ ABC 1-2-3 หรืออะไรก็ตามที่ปกติจะไม่ซ้ำกัน เมื่อ tool หยุดค้าง คุณบันทึก ID นี้ไว้ เวลา resume คุณส่ง ID เดิมกลับเข้าไป เพื่อให้ ADK รู้ว่าควรทำ execution ไหนต่อ ถ้าไม่มีมัน ADK จะเริ่ม execution ใหม่แทนที่จะ resume การรันที่ค้างอยู่ ดังนั้นมันจึงทำหน้าที่เป็น key ครับ เหมือนที่เราเคยนิยาม helper function ไว้ก่อนหน้านี้ ตรงนี้เราจะรัน helper function เพื่อประมวลผล event ทั้งหมดนี้ ซึ่งจะตรวจหาการอนุมัติครับ

การ check for approval นี้จะตรวจจับว่า agent หยุดค้างอยู่หรือไม่ โดยวนลูปดูทุก event เพื่อหา special event ที่ชื่อ ADK request confirmation event จากนั้นคืนค่า approval ID กับ invocation ID ถ้าไม่พบการหยุดค้าง มันจะคืนค่า none ครับ ผมกำลังรันอันนี้ ขั้นถัดไปคือ print agent response ซึ่งหมายถึงแสดงข้อความของ agent ตรงนี้ครับ ตอนนี้คุณสร้าง approved response ซึ่งหมายถึงคุณต้องจัดรูปแบบคำตัดสินใจของมนุษย์ สิ่งนี้รับ approval info และค่า boolean จากมนุษย์ เช่น true กับ false แล้วสร้าง function response ที่ ADK เข้าใจ แล้วห่อมันไว้ใน content object เพื่อส่งกลับไปให้ agent ครับ ดังนั้นเราจึงนิยาม create approval function response นี้ขึ้นมาด้วย ตรงนี้เราใส่ approval info กับค่าอนุมัติหรือไม่ ซึ่งก็เหมือนศูนย์กับหนึ่ง แล้วยืนยัน response จาก function response เราก็สร้าง helper function ตัวนี้เรียบร้อยแล้วครับ

ขั้นถัดไปคือเข้าใจว่าทุกอย่างผูกกันอย่างไร คุณมี user query ขั้นที่หนึ่งมันจะเรียก agent นั่นคือ run sync ซึ่งสร้าง invocation ID ขึ้นมา ใช่ไหมครับ ถ้าทุกอย่างเรียบร้อยดี มันจะวางออเดอร์ขนส่ง ถ้ามี event ขึ้น มันจะตรวจสอบการอนุมัติ ตรงนี้จะวนผ่าน event loop ถ้าไม่พบ event มันจะบอกว่าไม่ต้องการอนุมัติ แล้ววางออเดอร์เลย อย่างไรก็ตาม ถ้าคุณพบ confirmation มันจะเข้าสู่กระบวนการอนุมัติ คือ agent จะหยุดค้างตรงนี้ รับคำตัดสินใจจากมนุษย์ แล้วเรียก agent อีกครั้งซึ่งก็คือ run sync แล้วมันก็ resume ต่อ โดยจะใช้ invocation ID เดิมที่สร้างไว้ตรงนี้ครับ

หลังจากทำสิ่งนี้แล้ว สิ่งที่ต้องทำต่อคือสร้าง run shipping workflow ซึ่งเป็นตัวควบคุมจังหวะทั้ง workflow ครับ เรากำลังนิยาม sync function นี้ ผมรันไปแล้ว สิ่งที่เกิดขึ้นตรงนี้คือมันจะดูว่า auto approval เป็น true หรือ false แล้วมันรัน shipping workflow ที่จัดการการอนุมัติ query คือคำขอขนส่งของผู้ใช้ ว่าจะอนุมัติอัตโนมัติหรือเป็นออเดอร์ใหญ่ และจำลองคำตัดสินใจของมนุษย์ โดยพื้นฐานแล้วมันคือรูปโค้ดของ flowchart ที่เราพูดถึงตรงนี้ครับ ในขั้นที่หนึ่งมันจะรับ test user ID กับ session ID รับข้อความใหม่ แล้วเข้าสู่ลูป ในขั้นที่สองจะเช็คว่ามีการอนุมัติหรือไม่ ในขั้นที่สามตามที่แสดงใน flowchart มันจะหยุดค้างเพื่อรอการอนุมัติ แล้วเรา print คำตัดสินใจของมนุษย์ว่า approved หรือถ้าไม่อนุมัติก็บอกว่า rejected ครับ ต่อไปมี path A กับ path B มันจะ resume agent โดยเรียก run sync อีกครั้งพร้อมคำตัดสินใจอนุมัติ มันเรียกแบบนี้ครับ ส่งคำตัดสินใจของมนุษย์ตรงนี้ โดย invocation ID เท่ากับ approval info ซึ่งต้องมี invocation ID เดียวกัน หมายความว่ามันบอก ADK ให้ resume ถ้าสองอย่างนี้ไม่เท่ากัน คุณจะไปทาง path B แล้วโยน error นั้นออกมา แล้วบอกว่า workflow function พร้อมแล้ว จากนั้นบอกว่าไม่พบ หรือไม่ได้รับอนุมัติ หรือไม่ต้องการอนุมัติครับ เราสร้าง workflow สำเร็จเรียบร้อยแล้วครับ

ถ้าอยากเข้าใจว่าโค้ดทำงานอย่างไร คุณสามารถย้อนกลับไปดูอีกครั้งได้ ผมพาดู flowchart และโค้ดนี้ไปแล้ว แต่ถ้ายังอยากเข้าใจให้ดีขึ้นด้วยตัวเอง ก็เดินดูตามส่วนแยกโค้ด (code breakdown) นี้ได้เลยครับ ต่อไปคือการทดสอบ workflow เราสร้าง workflow สำเร็จแล้ว ตอนนี้จะรันมันครับ มี warning ขึ้นว่ามีส่วนที่ไม่ใช่ข้อความใน response ของ function call เราเคยเจอแบบนี้มาแล้วในตัวอย่างก่อน ๆ และผมเคยบอกไปแล้วว่าไม่ต้องกังวล มันบอกว่านี่เป็นเรื่องปกติและไม่สนใจได้ แค่หมายความว่า agent กำลังเรียก tool เพิ่มเติมนอกเหนือจากการสร้างข้อความเท่านั้นเองครับ ตอนนี้ผมจะรัน แล้วก็ได้ response แบบเดียวกันครับ คำถามที่ใช้คือขนส่งสามคอนเทนเนอร์ไป Singapore บอกว่า ship three containers to Singapore แล้วบอกว่าขนส่งสิบไป Rotterdam และขนส่งแปดไป Los Angeles โดยมี approval เป็น true กับ false ครับ ออเดอร์แรกผ่านไปเลยโดยไม่ต้องขออนุมัติ มันบอกอัตโนมัติว่าออเดอร์ได้รับอนุมัติแล้วและให้ order ID มา ส่วนอันที่สอง เพราะค่าเป็น true แปลว่าอนุมัติ สิ่งที่เกิดขึ้นคือมันจะหยุดค้างรอการอนุมัติ รับคำตัดสินใจของมนุษย์ว่าอนุมัติ แล้วจึงบอกว่าโอเค ออเดอร์ขนส่งได้รับอนุมัติ นี่คือ order ID และอันสุดท้ายมันรอการอนุมัติ แล้วคำตัดสินใจของมนุษย์คือปฏิเสธ ออเดอร์แปดคอนเทนเนอร์ไป Los Angeles จึงถูกปฏิเสธครับ เราได้เห็นครบทั้งสามสถานการณ์และการตอบสนองของมันในแต่ละสถานการณ์แล้ว ตรงนี้ยังมีเส้นทางการทำงานฉบับเต็มแบบ optional ที่คุณอยากดูจริง ๆ ก็เข้าไปได้ครับ

มาถึง Section 5 ซึ่งอธิบาย pattern ของ MCP integration และ long-running operations ที่เราใส่เข้าไปในโค้ดวันนี้ และควรใช้เมื่อไหร่ครับ โดยพื้นฐานแล้ว เมื่อคุณต้องเชื่อมต่อกับบริการภายนอก คุณใช้ MCP integration และเมื่อคุณต้องการหยุด workflow เพื่อ human-in-the-loop โดยเฉพาะงานเบื้องหลังที่ใช้เวลานานหรือจุดตรวจสอบด้าน compliance หรือ security คุณใช้ long-running operations เหล่านี้ครับ นี่เป็นแนวคิดระดับ production ready เราได้เข้าใจแล้วว่าจะสร้าง agent ที่ขยายขนาดได้ จัดการเวลา รับประกันความถูกต้องตามกฎ และคงสถานะไว้ได้อย่างไร ถ้าอยากลอง มีแบบฝึกหัด optional ให้ทำครับ สถานการณ์คือคุณต้องสร้าง agent ที่สร้างภาพด้วย MCP server แต่ต้องได้รับอนุมัติสำหรับการสร้างภาพเป็นชุด คำขอภาพเดี่ยวให้อนุมัติอัตโนมัติและสร้างทันที ส่วนคำขอเป็นชุดซึ่งมีมากกว่าหนึ่งภาพ ให้หยุดและขออนุมัติก่อนสร้างหลายภาพ และให้สำรวจ image generation MCP server สาธารณะต่าง ๆ ด้วย สำหรับส่วนนี้คุณสามารถตามลิงก์ที่ผมใส่ไว้ในคำอธิบาย เพื่อดูว่า server ไหนเหมาะกว่าครับ

ไปกันต่อที่วิดีโอถัดไปเพื่อเรียนเรื่องการจัดการ state และ memory กันครับ และโปรดทราบว่าคุณไม่จำเป็นต้องส่งโปรเจกต์นี้หรือสิ่งที่เรากำลังทำอยู่นี้ไปที่ไหนเลย ทั้งหมดเพื่อให้เราเข้าใจและเรียนรู้ให้ดีขึ้นเท่านั้นครับ ด้วยเท่านี้เราก็สำเร็จแบบฝึกหัดของ Day 2 ครบถ้วนแล้ว ไปกันต่อที่วิดีโอ Day 3 Part A เพื่อเรียนเรื่องการจัดการ state และ memory กันครับ

03

หมายเหตุการแปล

ความโปร่งใสเกี่ยวกับความไม่แน่นอนในต้นฉบับ

  • ## จุดที่ใช้สัญลักษณ์ฟังไม่ชัด
04

อภิธานศัพท์เทคนิค

คำศัพท์และชื่อผลิตภัณฑ์ที่คงรูปภาษาอังกฤษ

ศัพท์คำแปล / คำอธิบาย
MCP (Model Context Protocol)โปรโตคอลมาตรฐานเปิดสำหรับเชื่อม tool/บริการภายนอกเข้ากับ agent โดยไม่ต้องเขียน integration เอง
MCP serverเซิร์ฟเวอร์ที่ให้บริการ tool เฉพาะทางผ่านโปรโตคอล MCP เช่น Google Maps, Slack, GitHub
MCP clientฝั่งผู้ใช้ tool ที่ MCP จัดเตรียมไว้ โดยปกติคือ agent ของเรา
MCP toolsetชุดเครื่องมือที่สร้างขึ้นจากการเชื่อมต่อกับ MCP server เพื่อให้ agent เรียกใช้
Everything MCP serverMCP server ตัวอย่างสำหรับทดสอบการทำ MCP integration
tool filterตัวกรองเลือกเฉพาะ tool ที่ต้องการจาก MCP server
long-running operationsงานที่ใช้เวลานานหรือต้องหยุดรอ input ภายนอกก่อนจึงทำต่อได้
human-in-the-loopรูปแบบที่มีมนุษย์เข้ามาอนุมัติหรือตัดสินใจในวงจรการทำงานของ agent
resumable workflow / appworkflow หรือแอปที่หยุดค้างไว้แล้วกลับมาทำงานต่อได้ พร้อมคง state เดิม
stateสถานะของการทำงานที่คงไว้ข้ามช่วงที่บทสนทนาถูกขัดจังหวะ
ResumabilityConfigการตั้งค่าของ ADK สำหรับทำ agent เป็น resumable app (ยังเป็น experimental)
tool contextบริบทที่ ADK ส่งให้ tool อัตโนมัติเมื่อทำงาน ให้ความสามารถขออนุมัติและเช็คสถานะอนุมัติ
request approval / check approval statusการขออนุมัติ และการตรวจสอบสถานะการอนุมัติ สองความสามารถหลักของ tool context
handshakeการจับมือเริ่มต้นการสื่อสารระหว่าง client กับ server ตอนเชื่อมต่อ
tool discoveryขั้นตอนที่ server บอก ADK ว่ามี tool อะไรให้ใช้บ้าง
base64 encodingการเข้ารหัสข้อมูล (เช่น ภาพ) เป็นข้อความเพื่อส่งผ่านระบบได้
eventเหตุการณ์ในระบบ ADK — การเรียก tool การตอบกลับของโมเดล ผลลัพธ์ function ล้วนกลายเป็น event
ADK request confirmation eventspecial event ที่ส่งสัญญาณให้ agent หยุดค้างรอการยืนยัน
invocation IDID เฉพาะของการเรียก run_sync แต่ละครั้ง ใช้เป็น key บอก ADK ว่าจะ resume execution ไหน
run_sync / run_asyncคำสั่งเรียกใช้งาน agent แบบซิงโครนัส/อะซิงโครนัสใน ADK
event loopวงจรวนตรวจเช็ค event ที่เกิดขึ้นระหว่าง agent ทำงาน
compliance checkpointจุดตรวจสอบการปฏิบัติตามข้อกำหนด กฎระเบียบ ก่อนดำเนินการต่อ
bulk operationsการทำงานเป็นชุดใหญ่ เช่น ลบข้อมูลนับพันรายการ ที่ควรมีมนุษย์ยืนยันก่อน
irreversible operationsงานที่ทำแล้วย้อนกลับไม่ได้ เช่น การลบถาวร
OTPรหัสผ่านใช้ครั้งเดียว ตัวอย่างของการยืนยันตัวตนในธุรกรรมการเงิน
npx / node package runnerตัวรันแพ็กเกจ Node.js ที่ใช้เปิด MCP server โดยไม่ต้องติดตั้งถาวร
StdioConnectionParams / ServerParametersพารามิเตอร์การเชื่อมต่อและตั้งค่า server ที่ import จาก ADK
Google ADK (Agent Development Kit)ชุดเครื่องมือของ Google สำหรับพัฒนา agent
Geminiโมเดลภาษาขนาดใหญ่ของ Google ที่ขับเคลื่อน agent
Kaggleแพลตฟอร์ม notebook/ชุดข้อมูลของ Google ที่ใช้ทำ code lab ของคอร์สนี้
05

ซับไตเติ้ลภาษาไทย

ดาวน์โหลดหรือดูซับทั้งหมด

1
00:00:00,000 --> 00:00:03,540
ในวิดีโอนี้เราจะสำรวจแนวปฏิบัติที่ดีสำหรับ

2
00:00:03,540 --> 00:00:07,081
tool ซึ่งรวมถึงการใช้ MCP และ long-running

3
00:00:07,081 --> 00:00:10,958
operations ครับ นี่คือส่วน Part B ของแบบฝึกหัด

4
00:00:10,958 --> 00:00:14,751
Day 2 เราจะกด Explore เพื่อเข้าไปยัง notebook
thai-subtitles.srt
SubRip — ใช้กับเครื่องเล่นวิดีโอส่วนใหญ่
↓ ดาวน์โหลด
thai-subtitles.vtt
WebVTT — ใช้กับเว็บ / YouTube
↓ ดาวน์โหลด
เปิดดูซับไตเติ้ลทั้งหมด (526 segments)
1
00:00:00,000 --> 00:00:03,540
ในวิดีโอนี้เราจะสำรวจแนวปฏิบัติที่ดีสำหรับ

2
00:00:03,540 --> 00:00:07,081
tool ซึ่งรวมถึงการใช้ MCP และ long-running

3
00:00:07,081 --> 00:00:10,958
operations ครับ นี่คือส่วน Part B ของแบบฝึกหัด

4
00:00:10,958 --> 00:00:14,751
Day 2 เราจะกด Explore เพื่อเข้าไปยัง notebook

5
00:00:14,751 --> 00:00:18,207
แล้วกด Copy and Edit เพื่อให้ได้ notebook

6
00:00:18,207 --> 00:00:19,978
ที่แก้ไขได้ของเราครับ

7
00:00:19,978 --> 00:00:26,468
ขั้นตอนเริ่มต้นซึ่งเป็นส่วนของการเตรียมสภาพแวดล้อมจะคล้ายกับที่เราเคยทำมาแล้ว

8
00:00:26,468 --> 00:00:29,587
ผมจึงไม่ขอเสียเวลาอธิบายซ้ำมากนักครับ

9
00:00:29,587 --> 00:00:32,959
สิ่งที่แบบฝึกหัดนี้จะให้เราทำมีอะไรบ้าง?

10
00:00:32,959 --> 00:00:36,415
เราจะเชื่อมต่อกับ MCP server ภายนอก เราจะ

11
00:00:36,415 --> 00:00:39,196
implement long-running operations

12
00:00:39,196 --> 00:00:42,990
ที่สามารถหยุดการทำงานของ agent ค้างไว้เพื่อรอ

13
00:00:42,990 --> 00:00:46,783
input จากภายนอก เราจะสร้าง resumable workflow

14
00:00:46,783 --> 00:00:49,817
(workflow ที่หยุดแล้วกลับมาทำต่อได้)

15
00:00:49,817 --> 00:00:53,273
ซึ่งคงสถานะ (state) ข้ามช่วงที่บทสนทนาถูก

16
00:00:53,273 --> 00:00:56,729
interrupt และเราจะเข้าใจว่าควรใช้ pattern

17
00:00:56,729 --> 00:01:00,185
เหล่านี้เมื่อไหร่และอย่างไรครับ Section 1

18
00:01:00,185 --> 00:01:04,653
ซึ่งเป็นส่วนเตรียมสภาพแวดล้อมก็เหมือนกับที่เราทำในทุก

19
00:01:04,653 --> 00:01:05,917
notebook มาตลอด

20
00:01:05,917 --> 00:01:09,795
ผมจะไม่ใช้เวลามากตรงนี้แล้วไปกันต่อที่ section

21
00:01:09,795 --> 00:01:13,082
ถัดไปเลยนะครับ ขอรันโค้ดนี้เร็ว ๆ ไปที่

22
00:01:13,082 --> 00:01:16,201
Add-ons กด Secret เช็ค Google API key

23
00:01:16,201 --> 00:01:17,634
แล้วรันเลย เอาล่อ

24
00:01:17,634 --> 00:01:21,259
การตั้งค่าและการยืนยันตัวตนเสร็จสมบูรณ์แล้ว

25
00:01:21,259 --> 00:01:24,799
ผมจะพับส่วนนี้ทิ้งไปครับ ต่อไปเราจะ import

26
00:01:24,799 --> 00:01:26,654
คอมโพเนนต์ของ ADK ครับ

27
00:01:26,654 --> 00:01:30,110
คุณจะเห็นว่ามีคอมโพเนนต์เพิ่มขึ้นเรื่อย ๆ

28
00:01:30,110 --> 00:01:34,071
เมื่อเราไปจากแบบฝึกหัดหนึ่งสู่อีกแบบฝึกหัดหนึ่ง

29
00:01:34,071 --> 00:01:35,336
ขอรันอันนี้ก่อน

30
00:01:35,336 --> 00:01:40,309
แล้วระหว่างรอเรามาดูกันว่าแบบฝึกหัดนี้ใช้คอมโพเนนต์อะไรบ้าง

31
00:01:40,309 --> 00:01:43,597
เรามี agent, Gemini, runner และ session

32
00:01:43,597 --> 00:01:47,474
ซึ่งเป็นของคู่กันเหมือนเดิม ตรงนี้เราเพิ่ม MCP

33
00:01:47,474 --> 00:01:50,930
toolset (ชุดเครื่องมือจาก MCP) เข้ามา เรา

34
00:01:50,930 --> 00:01:54,723
import tool context ซึ่งทำไปแล้วใน Part A ของ

35
00:01:54,723 --> 00:01:57,926
Day 2 ตัวใหม่คือ StdioConnectionParams

36
00:01:57,926 --> 00:02:00,792
(พารามิเตอร์การเชื่อมต่อแบบ stdio)

37
00:02:00,792 --> 00:02:03,574
เพราะเราจะเชื่อมกับระบบภายนอก และ

38
00:02:03,574 --> 00:02:07,452
ServerParameters ด้วยเหตุผลเดียวกัน จากนั้นคือ

39
00:02:07,452 --> 00:02:08,969
ResumabilityConfig

40
00:02:08,969 --> 00:02:11,919
(การตั้งค่าความสามารถหยุดแล้วทำต่อ)

41
00:02:11,919 --> 00:02:15,544
เพราะในแบบฝึกหัดนี้เราจะสร้างระบบหรือ agent

42
00:02:15,544 --> 00:02:19,421
ที่ resumable แล้วก็ function tool ต่าง ๆ ครับ

43
00:02:19,421 --> 00:02:21,950
ตอนนี้คอมโพเนนต์ของ ADK import

44
00:02:21,950 --> 00:02:25,490
สำเร็จเรียบร้อยแล้ว ไปกันต่อที่ส่วนตั้งค่า

45
00:02:25,490 --> 00:02:28,778
retry option ครับ เราอาจใส่คำสั่ง print

46
00:02:28,778 --> 00:02:32,655
ได้เหมือนที่ผมชอบทำ แต่ครั้งนี้คงไม่จำเป็นแล้ว

47
00:02:32,655 --> 00:02:36,280
ถือว่าเรียบร้อยครับ ตอนนี้เข้าสู่ Section 2

48
00:02:36,280 --> 00:02:39,820
ซึ่งเป็นเรื่อง Model Context Protocol ครับ

49
00:02:39,820 --> 00:02:43,613
ก่อนจะเริ่มเขียนโค้ด มาทำความเข้าใจกันก่อนว่า

50
00:02:43,613 --> 00:02:46,817
MCP คืออะไร MCP ย่อมาจาก Model Context

51
00:02:46,817 --> 00:02:49,935
Protocol ครับ มันคือมาตรฐานเปิด (open

52
00:02:49,935 --> 00:02:53,391
standard) ที่ให้ agent ใช้งาน integration

53
00:02:53,391 --> 00:02:56,847
ที่ชุมชนสร้างไว้แล้ว แทนที่คุณจะต้องเขียน

54
00:02:56,847 --> 00:02:59,882
integration และ API client ของคุณเอง

55
00:02:59,882 --> 00:03:03,675
คุณแค่เชื่อมต่อไปยัง MCP server ที่มีอยู่แล้ว

56
00:03:03,675 --> 00:03:07,468
มันจะช่วยให้ agent เข้าถึงข้อมูลภายนอกแบบสด ๆ

57
00:03:07,468 --> 00:03:10,334
จากฐานข้อมูล API และ server ต่าง ๆ

58
00:03:10,334 --> 00:03:13,369
ได้โดยไม่ต้องเขียนโค้ด integrate เอง

59
00:03:13,369 --> 00:03:16,656
อีกทั้งยังช่วยให้คุณใช้ประโยชน์จาก tool

60
00:03:16,656 --> 00:03:20,112
ที่ชุมชนสร้างไว้ผ่าน interface แบบมาตรฐาน

61
00:03:20,112 --> 00:03:24,243
และที่สำคัญคุณขยายขีดความสามารถได้โดยเชื่อมต่อกับ

62
00:03:24,243 --> 00:03:27,867
server เฉพาะทางหลายตัวพร้อมกันครับ แล้ว MCP

63
00:03:27,867 --> 00:03:28,967
ทำงานอย่างไร?

64
00:03:28,967 --> 00:03:35,794
MCP เชื่อม MCP client หรือ agent ของคุณ เข้ากับ MCP server ภายนอก เช่น Slack MCP,

65
00:03:35,794 --> 00:03:38,660
Google Maps MCP, GitHub server ฯลฯ

66
00:03:38,660 --> 00:03:42,369
ผ่านโปรโตคอล MCP มาตรฐานครับ ในแผนภาพนี้ MCP

67
00:03:42,369 --> 00:03:45,909
server หมายถึงตัวที่ให้ tool เฉพาะทาง เช่น

68
00:03:45,909 --> 00:03:49,618
การสร้างภาพ การเข้าถึงฐานข้อมูล ฯลฯ ส่วน MCP

69
00:03:49,618 --> 00:03:53,496
client ซึ่งก็คือ client ของคุณ เป็นผู้ใช้ tool

70
00:03:53,496 --> 00:03:55,182
เหล่านั้น และ server

71
00:03:55,182 --> 00:03:58,975
ทั้งหมดนี้ทำงานด้วยวิธีเดียวกันผ่าน interface

72
00:03:58,975 --> 00:04:01,841
มาตรฐานเดียวกัน คือโปรโตคอลนี้ครับ

73
00:04:01,841 --> 00:04:04,791
ประเด็นถัดไปคือเราใช้ MCP กับ agent

74
00:04:04,791 --> 00:04:08,669
ได้อย่างไร? workflow นั้นเรียบง่ายมาก คุณเลือก

75
00:04:08,669 --> 00:04:12,125
MCP server และ tool หนึ่งตัว จากนั้นสร้าง

76
00:04:12,125 --> 00:04:16,002
toolset ซึ่งก็คือการตั้งค่าการเชื่อมต่อนั่นเอง

77
00:04:16,002 --> 00:04:17,519
แล้วคุณเพิ่ม agent

78
00:04:17,519 --> 00:04:21,397
ของคุณเข้าไปในการเชื่อมต่อนั้น แล้วรันและทดสอบ

79
00:04:21,397 --> 00:04:24,937
agent ครับ ฟังดูง่ายใช่ไหมครับ ลงมือกันเลย

80
00:04:24,937 --> 00:04:27,972
ก่อนเริ่ม ขอบอกไว้ก่อนว่าสำหรับ demo

81
00:04:27,972 --> 00:04:30,754
นี้เราจะใช้ Everything MCP server

82
00:04:30,754 --> 00:04:34,378
ซึ่งออกแบบมาเพื่อทดสอบ MCP integration ครับ

83
00:04:34,378 --> 00:04:37,160
และมันมี tool ชื่อ get_tiny_image

84
00:04:37,160 --> 00:04:41,037
ที่คืนภาพทดสอบง่าย ๆ ขนาด 16x16 pixel ในรูปแบบ

85
00:04:41,037 --> 00:04:42,302
base64 encoding

86
00:04:42,302 --> 00:04:45,083
(การเข้ารหัสข้อมูลภาพเป็นข้อความ)

87
00:04:45,083 --> 00:04:48,539
ถ้าอยากได้ข้อมูลเพิ่มหรือหา server อื่น ๆ

88
00:04:48,539 --> 00:04:51,237
ให้ไปที่ modelcontextprotocol.io

89
00:04:51,237 --> 00:04:54,861
ในส่วนตัวอย่างครับ อันนี้เป็น server สำหรับ

90
00:04:54,861 --> 00:04:57,980
demo เพื่อเรียนรู้ MCP เท่านั้น แต่ใน

91
00:04:57,980 --> 00:05:01,689
production คุณใช้อันนี้ไม่ได้นะครับ คุณจะใช้

92
00:05:01,689 --> 00:05:05,567
server ของ Google Maps, Slack, Discord ฯลฯ แทน

93
00:05:05,567 --> 00:05:09,360
ถึงขั้นตอนที่สองตามที่กล่าวไว้ เราจะสร้าง MCP

94
00:05:09,360 --> 00:05:10,459
toolset ก่อน

95
00:05:10,459 --> 00:05:14,168
ในขั้นที่หนึ่งเราตัดสินใจแล้วว่าจะใช้ server

96
00:05:14,168 --> 00:05:17,793
ไหนและใช้ tool อะไร ทีนี้ขั้นที่สองคือสร้าง

97
00:05:17,793 --> 00:05:21,249
MCP toolset ซึ่งใช้สำหรับเชื่อม ADK agent

98
00:05:21,249 --> 00:05:25,126
เข้ากับ MCP server ครับ อย่างที่บอกไว้ก่อนหน้า

99
00:05:25,126 --> 00:05:28,751
เราจะใช้ node package runner รัน MCP server

100
00:05:28,751 --> 00:05:32,291
ไม่ใช่ runner ปกติ เราจะเชื่อมต่อไปยัง MCP

101
00:05:32,291 --> 00:05:36,000
server นี้ และกรองให้ใช้เฉพาะ tool ชื่อ tiny

102
00:05:36,000 --> 00:05:39,540
image เท่านั้น ถ้าเข้าไปดูใน server จริง ๆ

103
00:05:39,540 --> 00:05:41,057
มันมี tool อื่นอีก

104
00:05:41,057 --> 00:05:44,345
แต่ตอนนี้เราต้องใช้แค่ตัวนี้ ถ้าอยากลอง

105
00:05:44,345 --> 00:05:46,874
ก็เข้าไปดูรายละเอียดของ server

106
00:05:46,874 --> 00:05:50,414
แล้วเลือกดูว่ามี tool อื่นที่อยากใช้อีกไหม

107
00:05:50,414 --> 00:05:53,786
ลองรันโค้ดนี้ครับ และเราก็สร้าง MCP tool

108
00:05:53,786 --> 00:05:56,989
สำเร็จเรียบร้อยแล้ว โค้ดนี้ทำอะไรบ้าง?

109
00:05:56,989 --> 00:06:00,529
ตรงนี้เรากำลังนิยาม mcp_image_server เขียน

110
00:06:00,529 --> 00:06:04,407
connection params ของมัน เรา import connection

111
00:06:04,407 --> 00:06:07,273
param กับ server parameter ไว้แล้ว

112
00:06:07,273 --> 00:06:10,729
ตอนนี้จึงกำหนดค่าตรงนี้ เราใส่คำสั่ง node

113
00:06:10,729 --> 00:06:14,606
package runner เพื่อให้รัน MCP server ผ่าน npx

114
00:06:14,606 --> 00:06:16,124
แล้วเพิ่ม argument

115
00:06:16,124 --> 00:06:18,652
เพื่อยืนยันการติดตั้งอัตโนมัติ

116
00:06:18,652 --> 00:06:22,108
เราจึงไม่ต้องติดตั้งเอง และเราระบุชื่อของ

117
00:06:22,108 --> 00:06:24,637
server ตรงนี้ ส่วน tool filter

118
00:06:24,637 --> 00:06:28,430
ตามที่บอกไปแล้วคือ get_tiny_image และเรากำหนด

119
00:06:28,430 --> 00:06:30,285
timeout ไว้ที่ 30 ครับ

120
00:06:30,285 --> 00:06:32,898
แล้วเบื้องหลังเกิดอะไรขึ้นบ้าง?

121
00:06:32,898 --> 00:06:35,511
แม้ผมจะอธิบายไปแล้วว่ามันทำอะไร

122
00:06:35,511 --> 00:06:39,557
ตรงนี้ก็อธิบายสิ่งเดียวกันแต่ในรูปแบบเทคนิคหน่อย

123
00:06:39,557 --> 00:06:41,412
ผมขออ่านสรุปให้ฟังครับ

124
00:06:41,412 --> 00:06:44,278
ลองโยงกับที่เราเพิ่งคุยกันดูนะครับ

125
00:06:44,278 --> 00:06:48,071
เบื้องหลังมีการ launch server คือเรากำลังเปิด

126
00:06:48,071 --> 00:06:51,105
server ขึ้นมา จากนั้นเราทำ handshake

127
00:06:51,105 --> 00:06:53,297
(การจับมือเริ่มต้นสื่อสาร)

128
00:06:53,297 --> 00:06:55,067
คือเรากำลังสื่อสารกัน

129
00:06:55,067 --> 00:06:58,692
สร้างช่องทางการสื่อสารขึ้นมาผ่าน connection

130
00:06:58,692 --> 00:07:02,401
params และ server parameters ต่อมาเราทำ tool

131
00:07:02,401 --> 00:07:05,772
discovery (การค้นพบ tool) ซึ่งก็คือ tool

132
00:07:05,772 --> 00:07:09,481
filter ที่เลือก get_tiny_image ตรงนี้ server

133
00:07:09,481 --> 00:07:10,914
จะบอก ADK ว่าโอเค

134
00:07:10,914 --> 00:07:14,623
ฉันจะให้ฟังก์ชันการทำงานนี้กับเธอ เมื่อเรามี

135
00:07:14,623 --> 00:07:18,501
function หรือ tool นี้แล้ว แปลว่าเรา integrate

136
00:07:18,501 --> 00:07:22,378
tool เข้ากับ agent สำเร็จโดยอัตโนมัติเรียบร้อย

137
00:07:22,378 --> 00:07:25,750
จากนั้นเราจะเรียกใช้ function tiny image

138
00:07:25,750 --> 00:07:29,374
คือเมื่อใดก็ตามที่ agent เรียก function นี้

139
00:07:29,374 --> 00:07:33,083
ADK จะส่งต่อข้อมูลของ function นั้นไปยัง MCP

140
00:07:33,083 --> 00:07:35,949
server และตอบแทน ผลลัพธ์จาก server

141
00:07:35,949 --> 00:07:39,152
ถูกส่งกลับมาที่ agent อย่างราบรื่นครับ

142
00:07:39,152 --> 00:07:43,030
เหตุผลที่ทั้งหมดนี้สำคัญคือ คุณได้เข้าถึง tool

143
00:07:43,030 --> 00:07:46,233
ทันทีโดยไม่ต้องเขียนโค้ด integrate เอง

144
00:07:46,233 --> 00:07:50,026
ผมว่าเราแทบไม่ได้เขียนอะไรมากมายเลยใช่ไหมครับ

145
00:07:50,026 --> 00:07:51,544
มีแค่ไม่กี่ขั้นตอน

146
00:07:51,544 --> 00:07:55,084
แค่ต้องจำรูปแบบที่ถูกต้องไว้ครับ เมื่อ MCP

147
00:07:55,084 --> 00:07:58,793
tool ถูกสร้างแล้ว ขั้นถัดไปคือเพิ่ม MCP tool

148
00:07:58,793 --> 00:08:02,080
เข้าไปให้ agent ครับ มาเพิ่ม MCP server

149
00:08:02,080 --> 00:08:05,705
นี้เข้าไปใน array ของ tools ของ image agent

150
00:08:05,705 --> 00:08:08,992
กันเถอะ คุณกำหนด retry option ของ agent

151
00:08:08,992 --> 00:08:12,280
และอื่น ๆ ไว้เรียบร้อยแล้ว instructions

152
00:08:12,280 --> 00:08:15,904
คือให้ใช้ MCP tool สร้างภาพตามคำขอของผู้ใช้

153
00:08:15,904 --> 00:08:18,517
และ tool ก็คือ MCP image server

154
00:08:18,517 --> 00:08:21,805
ที่เราเพิ่งสร้างไปครับ ระบบบอกว่าสำเร็จ

155
00:08:21,805 --> 00:08:24,839
ตอนนี้เราต้องสร้าง runner ซึ่งก็เป็น

156
00:08:24,839 --> 00:08:28,633
InMemoryRunner เหมือนเดิม และ agent คือ image

157
00:08:28,633 --> 00:08:32,342
agent เสร็จแล้วครับ ทีนี้เราจะส่ง prompt ให้

158
00:08:32,342 --> 00:08:35,882
agent สร้างภาพ ลองดูว่ามันทำได้อย่างไร ค่า

159
00:08:35,882 --> 00:08:39,759
prompt เริ่มต้นคือ provide a sample tiny image

160
00:08:39,759 --> 00:08:42,372
ผมรันอันนี้ครับ ใน session ใหม่

161
00:08:42,372 --> 00:08:45,744
มันบอกว่าผู้ใช้ขอภาพ tiny image ตัวอย่าง

162
00:08:45,744 --> 00:08:49,706
แล้วตอนนี้มันให้ข้อมูลของภาพมาพร้อมบอกว่านี่คือ

163
00:08:49,706 --> 00:08:53,584
tiny image ภาพด้านบนนี้คือ MCP tiny image ครับ

164
00:08:53,584 --> 00:08:55,944
ต่อไปเราจะให้มันแสดงภาพออกมา

165
00:08:55,944 --> 00:08:59,653
เพราะที่ได้มาเป็นข้อมูลภาพแบบ base64 encoded

166
00:08:59,653 --> 00:09:02,856
เราจึงยังไม่รู้ว่าภาพหน้าตาเป็นอย่างไร

167
00:09:02,856 --> 00:09:06,143
ตอนนี้เราจะให้มันแสดงภาพโดยใช้ function

168
00:09:06,143 --> 00:09:09,852
นี้ครับ ตรงนี้เรา import display จาก IPython

169
00:09:09,852 --> 00:09:13,392
display library และ Image ในชื่อ Img พร้อม

170
00:09:13,392 --> 00:09:16,343
import base64 เพื่อให้ข้อมูล base64

171
00:09:16,343 --> 00:09:19,630
ถูกแปลงเป็นภาพที่เหมาะสม แล้วรันโค้ดนี้

172
00:09:19,630 --> 00:09:22,665
ก็ได้ภาพออกมาอย่างที่เห็นครับ สวยงาม

173
00:09:22,665 --> 00:09:25,952
ขั้นถัดไปคือเราต้องขยายไปยัง MCP server

174
00:09:25,952 --> 00:09:29,745
ตัวอื่นด้วย นั่นคือ MCP server สำหรับ dataset

175
00:09:29,745 --> 00:09:31,684
และการทำงานกับ notebook

176
00:09:31,684 --> 00:09:34,297
ซึ่งโดยพื้นฐานแล้วช่วยให้ agent

177
00:09:34,297 --> 00:09:36,995
ของคุณโต้ตอบกับ dataset notebook

178
00:09:36,995 --> 00:09:39,776
และการคำนวณต่าง ๆ ของ Kaggle ครับ

179
00:09:39,776 --> 00:09:42,558
ตัวอย่างการเชื่อมต่อระบุไว้ตรงนี้

180
00:09:42,558 --> 00:09:46,098
เราทำแบบนี้มาแล้วกับ Everything MCP server

181
00:09:46,098 --> 00:09:49,554
และตรงนี้เราใช้ MCP remote กับ kaggle.com

182
00:09:49,554 --> 00:09:52,420
มีแบบเดียวกันนี้สำหรับ GitHub ด้วย

183
00:09:52,420 --> 00:09:53,938
นี่เป็นข้อมูลที่ดี

184
00:09:53,938 --> 00:09:56,382
ถ้าอยากลองก็สามารถรันดูได้เลย

185
00:09:56,382 --> 00:09:59,754
เป็นเพียงข้อมูลที่ควรรู้ไว้ครับ ต่อไปคือ

186
00:09:59,754 --> 00:10:02,451
long-running operation ซึ่งก็คือ

187
00:10:02,451 --> 00:10:05,991
human-in-the-loop ครับ เกิดอะไรขึ้นตรงนี้?

188
00:10:05,991 --> 00:10:07,340
จนถึงตอนนี้ tool

189
00:10:07,340 --> 00:10:11,555
ทุกตัวทำงานเสร็จแล้วคืนข้อมูลกลับมาทันทีใช่ไหมครับ

190
00:10:11,555 --> 00:10:14,758
ผู้ใช้ส่ง prompt แล้ว agent เรียก tool

191
00:10:14,758 --> 00:10:17,877
ใดตัวหนึ่ง tool คืนผลลัพธ์ แล้ว agent

192
00:10:17,877 --> 00:10:20,153
จึงตอบผู้ใช้ด้วยผลลัพธ์นั้น

193
00:10:20,153 --> 00:10:23,862
บางครั้งปรับแต่งเนื้อหาก่อน บางครั้งส่งตรง ๆ

194
00:10:23,862 --> 00:10:27,655
ตาม workflow ที่เลือกใช้ แต่คำถามคือ ถ้า tool

195
00:10:27,655 --> 00:10:29,847
ของคุณเป็นงานที่ใช้เวลานาน

196
00:10:29,847 --> 00:10:33,724
หรือคุณต้องการการอนุมัติจากมนุษย์ก่อนทำ action

197
00:10:33,724 --> 00:10:36,084
เสร็จสิ้น เช่น คุณต้อง merge

198
00:10:36,084 --> 00:10:39,793
อะไรบางอย่างแล้วต้องได้รับอนุมัติจาก release

199
00:10:39,793 --> 00:10:42,912
manager หรือผู้จัดการระดับสูงทำนองนี้

200
00:10:42,912 --> 00:10:45,609
นี่เป็นแค่ตัวอย่างคร่าว ๆ นะครับ

201
00:10:45,609 --> 00:10:47,464
ในสถานการณ์แบบนี้ tool

202
00:10:47,464 --> 00:10:50,246
จำเป็นต้องหยุดค้างรออยู่ รอ input

203
00:10:50,246 --> 00:10:53,364
จากภายนอกซึ่งก็คือการอนุมัติของมนุษย์

204
00:10:53,364 --> 00:10:57,158
แล้วจึงกลับมาทำงานต่อ workflow แบบนี้เรียกว่า

205
00:10:57,158 --> 00:11:00,951
long-running operation หรือ human-in-the-loop

206
00:11:00,951 --> 00:11:03,648
(มีมนุษย์เข้ามามีส่วนร่วมในวงจร)

207
00:11:03,648 --> 00:11:05,671
เพราะมีความต้องการ input

208
00:11:05,671 --> 00:11:08,790
จากภายนอกแล้วจึงเริ่มกระบวนการต่อครับ

209
00:11:08,790 --> 00:11:12,499
แล้วสถานการณ์ใดบ้างที่คุณควรใช้ long-running

210
00:11:12,499 --> 00:11:13,598
operation?

211
00:11:13,598 --> 00:11:19,667
อย่างแรกคือธุรกรรมการเงิน ที่คุณอาจต้องใส่ OTP หรือมีการยืนยันสิทธิ์ใด ๆ

212
00:11:19,667 --> 00:11:22,955
อย่างที่สองคือการทำงานเป็นชุดใหญ่ (bulk

213
00:11:22,955 --> 00:11:26,158
operations) เช่น ต้องลบข้อมูลพันรายการ

214
00:11:26,158 --> 00:11:30,794
คุณต้องยืนยันก่อนว่าแน่ใจหรือไม่ที่จะลบข้อมูลมากขนาดนี้

215
00:11:30,794 --> 00:11:34,587
อย่างถัดไปคือจุดตรวจสอบการปฏิบัติตามกฎระเบียบ

216
00:11:34,587 --> 00:11:38,043
(compliance checkpoint) ซึ่งเห็นได้ชัดมาก

217
00:11:38,043 --> 00:11:40,909
คือคุณต้องได้รับอนุมัติตามข้อกำหนด

218
00:11:40,909 --> 00:11:43,269
จากนั้นอีกตัวอย่างคือ action

219
00:11:43,269 --> 00:11:46,557
ที่มีค่าใช้จ่ายสูง เช่น การสั่ง spin up

220
00:11:46,557 --> 00:11:50,181
เซิร์ฟเวอร์ 50 เครื่อง จริง ๆ เหรอ แน่ใจนะ?

221
00:11:50,181 --> 00:11:52,120
คำถามก็จะเป็นแบบนี้ครับ

222
00:11:52,120 --> 00:11:54,227
คุณจึงต้องให้มนุษย์ยืนยัน

223
00:11:54,227 --> 00:11:57,515
อีกตัวอย่างคือการทำงานที่ย้อนกลับไม่ได้

224
00:11:57,515 --> 00:12:00,044
(irreversible operations) เช่น

225
00:12:00,044 --> 00:12:03,837
การลบถาวรโดยสมบูรณ์ ถ้าวันนี้ผมต้องลบ account

226
00:12:03,837 --> 00:12:07,630
แบบถาวร ผมต้องพิมพ์ delete แล้วยืนยันอีกครั้ง

227
00:12:07,630 --> 00:12:11,508
ใช่ไหมครับ นั่นคือการยืนยันที่ต้องมี ซึ่งก็คือ

228
00:12:11,508 --> 00:12:12,941
human-in-the-loop

229
00:12:12,941 --> 00:12:15,469
การอนุมัติโดยมนุษย์นั่นเองครับ

230
00:12:15,469 --> 00:12:17,324
แล้วตอนนี้เราจะทำอะไร?

231
00:12:17,324 --> 00:12:24,320
เราจะสร้าง shipping coordinator agent ที่มี tool หนึ่งตัวซึ่งจะอนุมัติออเดอร์เล็ก ๆ

232
00:12:24,320 --> 00:12:27,355
ที่ต่ำกว่าห้าคอนเทนเนอร์โดยอัตโนมัติ

233
00:12:27,355 --> 00:12:32,834
และจะหยุดค้างเพื่อขออนุมัติสำหรับออเดอร์ใหญ่ที่เกินห้าคอนเทนเนอร์

234
00:12:32,834 --> 00:12:37,470
แล้วจึงเสร็จสิ้นหรือยกเลิกตามการตัดสินใจอนุมัตินั้นครับ

235
00:12:37,470 --> 00:12:40,336
นี่สาธิตให้เห็นว่า pattern หลักของ

236
00:12:40,336 --> 00:12:42,190
long-running operation

237
00:12:42,190 --> 00:12:45,815
ถูกทำให้เป็นอัตโนมัติด้วย คือมันจะหยุด จะรอ

238
00:12:45,815 --> 00:12:49,692
input ของมนุษย์ แล้วจะทำงานต่อครับ เริ่มกันเลย

239
00:12:49,692 --> 00:12:52,811
สำหรับ demo นี้ เราต้องเขียน function

240
00:12:52,811 --> 00:12:55,593
ให้สมบูรณ์ก่อน โดยมี tool context

241
00:12:55,593 --> 00:12:59,133
เป็นพารามิเตอร์ครับ tool context parameter

242
00:12:59,133 --> 00:13:02,589
อยู่ในรูปแบบฟังก์ชัน (function signature)

243
00:13:02,589 --> 00:13:05,455
ซึ่งหมายความว่า ADK จะจัดหา object

244
00:13:05,455 --> 00:13:08,574
นี้ให้อัตโนมัติเมื่อ tool ของคุณทำงาน

245
00:13:08,574 --> 00:13:11,946
มันให้ความสามารถหลักสองอย่าง คือ request

246
00:13:11,946 --> 00:13:14,812
approval (ขออนุมัติ) ก็คือเรียกใช้

247
00:13:14,812 --> 00:13:18,268
confirmation แล้วก็ check approval status

248
00:13:18,268 --> 00:13:21,387
(ตรวจสอบสถานะการอนุมัติ) โดยจะอ่านจาก

249
00:13:21,387 --> 00:13:25,096
confirmation นั้น ส่วน large_order_threshold

250
00:13:25,096 --> 00:13:27,877
เท่ากับห้าคือค่าเกณฑ์ (threshold)

251
00:13:27,877 --> 00:13:31,249
ที่เราตั้งไว้ครับ เรากำลังนิยาม function

252
00:13:31,249 --> 00:13:34,031
ที่ชื่อ place_shipping_order ครับ

253
00:13:34,031 --> 00:13:37,908
เรากำหนดอาร์กิวเมนต์ไว้ตรงนี้ docstring บอกว่า

254
00:13:37,908 --> 00:13:39,007
function

255
00:13:39,007 --> 00:13:45,077
วางออเดอร์ขนส่งนี้ต้องได้รับอนุมัติสำหรับออเดอร์ที่มากกว่าห้าคอนเทนเนอร์

256
00:13:45,077 --> 00:13:48,448
ซึ่งเป็นเกณฑ์ของออเดอร์ใหญ่ อาร์กิวเมนต์

257
00:13:48,448 --> 00:13:50,134
number_of_containers

258
00:13:50,134 --> 00:13:54,012
คือจำนวนคอนเทนเนอร์ที่จะขนส่ง ส่วน destination

259
00:13:54,012 --> 00:13:57,046
คือปลายทางการขนส่ง ค่าที่คืนกลับเป็น

260
00:13:57,046 --> 00:13:59,912
dictionary พร้อม order_status ครับ

261
00:13:59,912 --> 00:14:01,011
สถานการณ์แรก

262
00:14:01,011 --> 00:14:07,333
ออเดอร์เล็กที่น้อยกว่าหรือเท่ากับห้าคอนเทนเนอร์ควรได้รับการอนุมัติอัตโนมัติ

263
00:14:07,333 --> 00:14:10,705
ทางนี้มีเงื่อนไข if ไว้สำหรับกรณีนี้แล้ว

264
00:14:10,705 --> 00:14:14,330
อย่างไรก็ตาม สถานการณ์ที่สองคือ ครั้งแรกที่

265
00:14:14,330 --> 00:14:15,678
tool นี้ถูกเรียก

266
00:14:15,678 --> 00:14:18,797
ออเดอร์ใหญ่ต้องได้รับอนุมัติจากมนุษย์

267
00:14:18,797 --> 00:14:22,085
คุณต้องหยุดรอตรงนี้ใช่ไหมครับ ในโค้ดนี้

268
00:14:22,085 --> 00:14:27,648
เราบอกว่าถ้านี่ไม่ใช่ออเดอร์ใหญ่ตามเกณฑ์ที่นิยามไว้เป็นห้าตรงบนสุด

269
00:14:27,648 --> 00:14:29,755
ถ้าเงื่อนไขนี้ไม่เป็นจริง

270
00:14:29,755 --> 00:14:32,958
คุณจะมาจากตรงนี้ไปยังส่วนนี้ แล้วมองหา

271
00:14:32,958 --> 00:14:36,077
confirmation โดย hint คือ large order

272
00:14:36,077 --> 00:14:38,690
จำนวนคอนเทนเนอร์ไปยังปลายทางนี้

273
00:14:38,690 --> 00:14:40,713
คุณต้องการอนุมัติหรือไม่

274
00:14:40,713 --> 00:14:42,989
นี่คือข้อความที่ถูกส่งออกไป

275
00:14:42,989 --> 00:14:45,771
แล้วสถานะที่คืนกลับจะเป็น pending

276
00:14:45,771 --> 00:14:51,334
โดยข้อความจะบอกว่าออเดอร์สำหรับกี่คอนเทนเนอร์ต้องได้รับอนุมัติครับ

277
00:14:51,334 --> 00:14:53,189
สถานการณ์ถัดไปคือ tool

278
00:14:53,189 --> 00:14:56,561
นี้ถูกเรียกอีกครั้งและกำลัง resume ทำต่อ

279
00:14:56,561 --> 00:15:00,101
เมื่อคุณอนุมัติหรือไม่อนุมัติ มันจะ resume

280
00:15:00,101 --> 00:15:03,557
และทำขั้นตอนที่เกี่ยวข้องต่อไป คือ handle

281
00:15:03,557 --> 00:15:07,097
approval response ที่คุณ resume ตรงนี้ ถ้า

282
00:15:07,097 --> 00:15:10,806
confirmation นี้ถูกยืนยันแล้ว คุณคืนค่าสถานะ

283
00:15:10,806 --> 00:15:13,419
approved พร้อมรายละเอียดออเดอร์

284
00:15:13,419 --> 00:15:16,454
จำนวนคอนเทนเนอร์ และอย่างอื่นทั้งหมด

285
00:15:16,454 --> 00:15:19,573
ถ้าไม่ได้อนุมัติ มันจะบอกว่า rejected

286
00:15:19,573 --> 00:15:24,883
และบอกว่าออเดอร์ถูกปฏิเสธสำหรับกี่คอนเทนเนอร์ไปยังปลายทางใดครับ

287
00:15:24,883 --> 00:15:28,171
ลองรันและสร้าง function นี้กัน เราสร้าง

288
00:15:28,171 --> 00:15:31,964
long-running function สำเร็จเรียบร้อยแล้วครับ

289
00:15:31,964 --> 00:15:35,251
ผมอธิบายค่อนข้างคร่าว ๆ มา เพราะอยากใช้

290
00:15:35,251 --> 00:15:38,539
flowchart ตัวนี้อธิบายโค้ดให้ฟังมากกว่า

291
00:15:38,539 --> 00:15:40,730
เพราะมันเข้าใจง่ายกว่าครับ

292
00:15:40,730 --> 00:15:44,102
ถ้าจำนวนคอนเทนเนอร์มากกว่าหรือเท่ากับห้า

293
00:15:44,102 --> 00:15:47,221
มันจะอนุมัติโดยอัตโนมัติ แต่ถ้าไม่ใช่

294
00:15:47,221 --> 00:15:50,340
ก็จะเข้าสู่ function นี้ ถ้าตอบว่าใช่

295
00:15:50,340 --> 00:15:53,964
คืออนุมัติ มันจะคืนค่า approve ถ้าตอบว่าไม่

296
00:15:53,964 --> 00:15:57,505
มันจะ reject ถ้า function นั้นยังไม่มีอยู่

297
00:15:57,505 --> 00:16:00,118
มันจะไปหาผู้ใช้เพื่อขอการยืนยัน

298
00:16:00,118 --> 00:16:03,658
และระหว่างนั้นสถานะจะค้างเป็น pending ครับ

299
00:16:03,658 --> 00:16:06,608
ถ้าคุณย้อนกลับไปอ่านโค้ดนี้อีกครั้ง

300
00:16:06,608 --> 00:16:09,053
คุณจะเข้าใจได้ง่ายขึ้นมากครับ

301
00:16:09,053 --> 00:16:13,942
ต่อไปขอเล่าให้ฟังว่าทั้งสามสถานการณ์ทำงานอย่างไรในบริบทนี้

302
00:16:13,942 --> 00:16:15,796
อย่างแรกคือออเดอร์เล็ก

303
00:16:15,796 --> 00:16:18,662
มันจะอนุมัติสถานะทันทีโดยอัตโนมัติ

304
00:16:18,662 --> 00:16:22,034
จะไม่เข้าไปที่ function นี้ตามที่ระบุไว้

305
00:16:22,034 --> 00:16:25,406
ใช่ไหมครับ มันแค่อนุมัติเลย อย่างไรก็ตาม

306
00:16:25,406 --> 00:16:27,597
ถ้าเงื่อนไขนั้นไม่เป็นจริง

307
00:16:27,597 --> 00:16:31,812
มันจะเข้าสู่การเรียกครั้งแรกซึ่งจะตรวจสอบก่อนว่ามี

308
00:16:31,812 --> 00:16:35,605
confirmation หรือยัง ถ้ายังไม่มี confirmation

309
00:16:35,605 --> 00:16:39,146
มันจะขอ confirmation ขอการอนุมัติจากมนุษย์

310
00:16:39,146 --> 00:16:42,096
และสถานะจะถูกตั้งเป็น pending ทันที

311
00:16:42,096 --> 00:16:45,805
และอย่างที่สาม ถ้าออเดอร์ใหญ่ได้รับการตอบรับ

312
00:16:45,805 --> 00:16:48,839
มันจะตรวจจับได้ แล้วอิงจากใช่หรือไม่

313
00:16:48,839 --> 00:16:52,295
มันจะคืนสถานะ approved หรือ rejected ครับ

314
00:16:52,295 --> 00:16:56,173
มาถึง section ถัดไปซึ่งคือสร้าง agent, app และ

315
00:16:56,173 --> 00:16:59,123
runner ครับ ขั้นแรกเราจะสร้าง agent

316
00:16:59,123 --> 00:17:02,916
แล้วเราจะห่อมันไว้ใน resumable app แล้วจึงรัน

317
00:17:02,916 --> 00:17:06,625
agent ทั้งหมด ขั้นแรกคือสร้าง agent เราเพิ่ม

318
00:17:06,625 --> 00:17:10,250
tool เข้าไปให้ agent นั่นคือสิ่งแรกที่เราทำ

319
00:17:10,250 --> 00:17:13,874
ตรงนี้คือ shipping agent พร้อม name, model,

320
00:17:13,874 --> 00:17:16,656
retry option แล้ว instruction คือ

321
00:17:16,656 --> 00:17:19,775
คุณคือผู้ช่วยโคออร์ดิเนเตอร์ฝ่ายขนส่ง

322
00:17:19,775 --> 00:17:23,652
เมื่อผู้ใช้ขอขนส่งคอนเทนเนอร์ ให้ใช้ tool ชื่อ

323
00:17:23,652 --> 00:17:25,338
place_shipping_order

324
00:17:25,338 --> 00:17:27,951
พร้อมจำนวนคอนเทนเนอร์และปลายทาง

325
00:17:27,951 --> 00:17:30,480
ถ้าสถานะของออเดอร์เป็น pending

326
00:17:30,480 --> 00:17:33,936
ให้แจ้งผู้ใช้ว่าต้องได้รับอนุมัติ เป็นต้น

327
00:17:33,936 --> 00:17:36,887
ระบุว่า tool คือ function tool ชื่อ

328
00:17:36,887 --> 00:17:39,247
place_shipping_order แล้วรัน

329
00:17:39,247 --> 00:17:40,933
ก็สร้างเสร็จแล้วครับ

330
00:17:40,933 --> 00:17:44,473
ไปที่อันที่สองซึ่งเป็นการห่อ resumable app

331
00:17:44,473 --> 00:17:46,917
คุณห่อมันตรงนี้ในลักษณะที่ใช้

332
00:17:46,917 --> 00:17:49,615
ResumabilityConfig ที่เรา import

333
00:17:49,615 --> 00:17:53,408
ไว้ตั้งแต่ต้นแบบฝึกหัดนี้ ลองรันดู มี warning

334
00:17:53,408 --> 00:17:56,358
ขึ้นมาบอกว่า config นี้เป็นแบบทดลอง

335
00:17:56,358 --> 00:17:57,538
(experimental)

336
00:17:57,538 --> 00:18:02,765
และอาจเปลี่ยนแปลงหรือถูกลบออกในเวอร์ชันอนาคตโดยไม่แจ้งล่วงหน้า

337
00:18:02,765 --> 00:18:06,052
ซึ่งไม่เป็นไรครับ เราไม่ต้องกังวลตอนนี้

338
00:18:06,052 --> 00:18:09,255
ค่าเป็น true ซึ่งเป็นสิ่งที่เราต้องการ

339
00:18:09,255 --> 00:18:12,795
ถือว่าเสร็จเรียบร้อยครับ ขั้นถัดไปคือสร้าง

340
00:18:12,795 --> 00:18:16,336
session และ runner ขึ้นกับ app แทนที่จะรัน

341
00:18:16,336 --> 00:18:20,213
agent ตรง ๆ เราจะใช้ app แล้วส่งมันผ่าน runner

342
00:18:20,213 --> 00:18:22,489
ครับ session service ก็เป็น

343
00:18:22,489 --> 00:18:25,271
InMemorySessionService เหมือนเดิม

344
00:18:25,271 --> 00:18:28,558
เรากำลังสร้าง runner ด้วย resumable app

345
00:18:28,558 --> 00:18:31,761
ดังนั้น shipping runner ตรงนี้จะมี app

346
00:18:31,761 --> 00:18:35,555
เท่ากับตัวนี้ คือเราจะส่ง app เข้าไปแทน agent

347
00:18:35,555 --> 00:18:39,432
นั่นเองครับ รันไปแล้ว runner ถูกสร้างเรียบร้อย

348
00:18:39,432 --> 00:18:43,225
ก่อนไปขั้นถัดไปในการสร้าง workflow ขอสรุปสั้น

349
00:18:43,225 --> 00:18:46,260
ๆ ตามที่ระบุไว้ตรงนี้ครับ เรามี tool

350
00:18:46,260 --> 00:18:49,126
ที่สามารถหยุดค้างเพื่อรอการอนุมัติ

351
00:18:49,126 --> 00:18:52,666
เราสร้างสิ่งนั้นแล้ว เราสร้าง agent ที่ใช้

352
00:18:52,666 --> 00:18:55,869
tool นี้ จากนั้นเราสร้าง resumable app

353
00:18:55,869 --> 00:18:59,663
ซึ่งก็คือ shipping app แล้วเราจึงสร้าง runner

354
00:18:59,663 --> 00:19:03,203
ที่จัดการการหยุดและทำต่อของ app นี้ได้ครับ

355
00:19:03,203 --> 00:19:06,912
ตอนนี้มาถึง Section 4 ครับ ใน Section 4 โค้ด

356
00:19:06,912 --> 00:19:10,284
workflow ใช้แนวคิดของ ADK อย่าง session,

357
00:19:10,284 --> 00:19:11,632
runner และ event

358
00:19:11,632 --> 00:19:15,425
ส่วนนี้จะครอบคลุมเฉพาะสิ่งที่คุณต้องรู้สำหรับ

359
00:19:15,425 --> 00:19:18,713
long-running operations ใน notebook นี้

360
00:19:18,713 --> 00:19:22,253
ถ้าอยากรู้เพิ่มเติม แบบฝึกหัดถัดไปใน Day 3

361
00:19:22,253 --> 00:19:25,625
จะพูดถึงเรื่องนั้น คุณสามารถดูเอกสาร ADK

362
00:19:25,625 --> 00:19:27,732
และวิดีโอที่เกี่ยวข้องได้

363
00:19:27,732 --> 00:19:31,020
ซึ่งผมจะใส่ลิงก์ไว้ในคำอธิบายวิดีโอครับ

364
00:19:31,020 --> 00:19:33,717
ตรงนี้คือส่วนที่ critical ที่สุด

365
00:19:33,717 --> 00:19:36,077
เพราะทุกอย่างมาตลอดแบบสบาย ๆ

366
00:19:36,077 --> 00:19:44,085
จนถึงตอนนี้ ส่วนที่ผมว่า critical ที่สุดคือการจัดการ event ใน workflow ครับ หมายความว่าอย่างไร?

367
00:19:44,085 --> 00:19:47,541
agent จะจัดการการหยุดและทำต่อโดยอัตโนมัติ

368
00:19:47,541 --> 00:19:51,250
อย่างที่ผมพูดมาตั้งแต่ต้นของแบบฝึกหัดนี้ ทุก

369
00:19:51,250 --> 00:19:54,200
workflow แบบ long-running operation

370
00:19:54,200 --> 00:19:56,223
จำเป็นต้องตรวจจับการหยุด

371
00:19:56,223 --> 00:19:59,511
รับคำตัดสินใจจากมนุษย์ แล้วจึงให้ agent

372
00:19:59,511 --> 00:20:02,293
ทำงานต่อครับ เพื่อเข้าใจให้ดีขึ้น

373
00:20:02,293 --> 00:20:05,580
เราต้องเข้าใจแนวคิดทางเทคนิคหลัก ๆ ก่อน

374
00:20:05,580 --> 00:20:09,120
ตัวแรกคือ event (เหตุการณ์ในระบบ) ครับ ADK

375
00:20:09,120 --> 00:20:12,661
สร้าง event ขึ้นเมื่อ agent ทำงาน การเรียก

376
00:20:12,661 --> 00:20:16,285
tool การตอบกลับของโมเดล ผลลัพธ์ของ function

377
00:20:16,285 --> 00:20:19,657
ล้วนกลายเป็น event ทั้งหมด ตอนนี้ตัว ADK

378
00:20:19,657 --> 00:20:22,692
request confirmation นี้ก็เป็น event

379
00:20:22,692 --> 00:20:25,979
หนึ่งในโค้ดของเรา มันเป็น special event

380
00:20:25,979 --> 00:20:29,014
ที่ส่งสัญญาณว่าให้หยุดค้างตรงนี้ครับ

381
00:20:29,014 --> 00:20:31,121
ตัวถัดไปคือ invocation ID

382
00:20:31,121 --> 00:20:34,661
คือทุกครั้งที่มีการเรียก run sync จะได้ ID

383
00:20:34,661 --> 00:20:38,370
ที่ไม่ซ้ำกันมาหนึ่งตัว นั่นคือ invocation ID

384
00:20:38,370 --> 00:20:40,225
หน้าตาประมาณ ABC 1-2-3

385
00:20:40,225 --> 00:20:43,765
หรืออะไรก็ตามที่ปกติจะไม่ซ้ำกัน เมื่อ tool

386
00:20:43,765 --> 00:20:47,137
หยุดค้าง คุณบันทึก ID นี้ไว้ เวลา resume

387
00:20:47,137 --> 00:20:50,255
คุณส่ง ID เดิมกลับเข้าไป เพื่อให้ ADK

388
00:20:50,255 --> 00:20:53,964
รู้ว่าควรทำ execution ไหนต่อ ถ้าไม่มีมัน ADK

389
00:20:53,964 --> 00:20:57,083
จะเริ่ม execution ใหม่แทนที่จะ resume

390
00:20:57,083 --> 00:21:00,792
การรันที่ค้างอยู่ ดังนั้นมันจึงทำหน้าที่เป็น

391
00:21:00,792 --> 00:21:04,585
key ครับ เหมือนที่เราเคยนิยาม helper function

392
00:21:04,585 --> 00:21:08,379
ไว้ก่อนหน้านี้ ตรงนี้เราจะรัน helper function

393
00:21:08,379 --> 00:21:10,907
เพื่อประมวลผล event ทั้งหมดนี้

394
00:21:10,907 --> 00:21:14,279
ซึ่งจะตรวจหาการอนุมัติครับ การ check for

395
00:21:14,279 --> 00:21:16,808
approval นี้จะตรวจจับว่า agent

396
00:21:16,808 --> 00:21:20,095
หยุดค้างอยู่หรือไม่ โดยวนลูปดูทุก event

397
00:21:20,095 --> 00:21:23,551
เพื่อหา special event ที่ชื่อ ADK request

398
00:21:23,551 --> 00:21:27,260
confirmation event จากนั้นคืนค่า approval ID

399
00:21:27,260 --> 00:21:30,379
กับ invocation ID ถ้าไม่พบการหยุดค้าง

400
00:21:30,379 --> 00:21:33,582
มันจะคืนค่า none ครับ ผมกำลังรันอันนี้

401
00:21:33,582 --> 00:21:36,364
ขั้นถัดไปคือ print agent response

402
00:21:36,364 --> 00:21:39,904
ซึ่งหมายถึงแสดงข้อความของ agent ตรงนี้ครับ

403
00:21:39,904 --> 00:21:42,602
ตอนนี้คุณสร้าง approved response

404
00:21:42,602 --> 00:21:46,479
ซึ่งหมายถึงคุณต้องจัดรูปแบบคำตัดสินใจของมนุษย์

405
00:21:46,479 --> 00:21:49,767
สิ่งนี้รับ approval info และค่า boolean

406
00:21:49,767 --> 00:21:53,054
จากมนุษย์ เช่น true กับ false แล้วสร้าง

407
00:21:53,054 --> 00:21:55,751
function response ที่ ADK เข้าใจ

408
00:21:55,751 --> 00:21:58,280
แล้วห่อมันไว้ใน content object

409
00:21:58,280 --> 00:22:00,640
เพื่อส่งกลับไปให้ agent ครับ

410
00:22:00,640 --> 00:22:04,265
ดังนั้นเราจึงนิยาม create approval function

411
00:22:04,265 --> 00:22:07,974
response นี้ขึ้นมาด้วย ตรงนี้เราใส่ approval

412
00:22:07,974 --> 00:22:10,081
info กับค่าอนุมัติหรือไม่

413
00:22:10,081 --> 00:22:13,874
ซึ่งก็เหมือนศูนย์กับหนึ่ง แล้วยืนยัน response

414
00:22:13,874 --> 00:22:17,162
จาก function response เราก็สร้าง helper

415
00:22:17,162 --> 00:22:19,859
function ตัวนี้เรียบร้อยแล้วครับ

416
00:22:19,859 --> 00:22:23,400
ขั้นถัดไปคือเข้าใจว่าทุกอย่างผูกกันอย่างไร

417
00:22:23,400 --> 00:22:27,193
คุณมี user query ขั้นที่หนึ่งมันจะเรียก agent

418
00:22:27,193 --> 00:22:30,565
นั่นคือ run sync ซึ่งสร้าง invocation ID

419
00:22:30,565 --> 00:22:33,936
ขึ้นมา ใช่ไหมครับ ถ้าทุกอย่างเรียบร้อยดี

420
00:22:33,936 --> 00:22:37,055
มันจะวางออเดอร์ขนส่ง ถ้ามี event ขึ้น

421
00:22:37,055 --> 00:22:40,680
มันจะตรวจสอบการอนุมัติ ตรงนี้จะวนผ่าน event

422
00:22:40,680 --> 00:22:42,281
loop ถ้าไม่พบ event

423
00:22:42,281 --> 00:22:46,159
มันจะบอกว่าไม่ต้องการอนุมัติ แล้ววางออเดอร์เลย

424
00:22:46,159 --> 00:22:49,025
อย่างไรก็ตาม ถ้าคุณพบ confirmation

425
00:22:49,025 --> 00:22:52,228
มันจะเข้าสู่กระบวนการอนุมัติ คือ agent

426
00:22:52,228 --> 00:22:55,515
จะหยุดค้างตรงนี้ รับคำตัดสินใจจากมนุษย์

427
00:22:55,515 --> 00:22:59,056
แล้วเรียก agent อีกครั้งซึ่งก็คือ run sync

428
00:22:59,056 --> 00:23:02,680
แล้วมันก็ resume ต่อ โดยจะใช้ invocation ID

429
00:23:02,680 --> 00:23:06,558
เดิมที่สร้างไว้ตรงนี้ครับ หลังจากทำสิ่งนี้แล้ว

430
00:23:06,558 --> 00:23:10,435
สิ่งที่ต้องทำต่อคือสร้าง run shipping workflow

431
00:23:10,435 --> 00:23:13,891
ซึ่งเป็นตัวควบคุมจังหวะทั้ง workflow ครับ

432
00:23:13,891 --> 00:23:17,516
เรากำลังนิยาม sync function นี้ ผมรันไปแล้ว

433
00:23:17,516 --> 00:23:20,803
สิ่งที่เกิดขึ้นตรงนี้คือมันจะดูว่า auto

434
00:23:20,803 --> 00:23:24,175
approval เป็น true หรือ false แล้วมันรัน

435
00:23:24,175 --> 00:23:27,800
shipping workflow ที่จัดการการอนุมัติ query

436
00:23:27,800 --> 00:23:29,570
คือคำขอขนส่งของผู้ใช้

437
00:23:29,570 --> 00:23:32,942
ว่าจะอนุมัติอัตโนมัติหรือเป็นออเดอร์ใหญ่

438
00:23:32,942 --> 00:23:35,218
และจำลองคำตัดสินใจของมนุษย์

439
00:23:35,218 --> 00:23:38,589
โดยพื้นฐานแล้วมันคือรูปโค้ดของ flowchart

440
00:23:38,589 --> 00:23:42,382
ที่เราพูดถึงตรงนี้ครับ ในขั้นที่หนึ่งมันจะรับ

441
00:23:42,382 --> 00:23:45,923
test user ID กับ session ID รับข้อความใหม่

442
00:23:45,923 --> 00:23:47,103
แล้วเข้าสู่ลูป

443
00:23:47,103 --> 00:23:50,475
ในขั้นที่สองจะเช็คว่ามีการอนุมัติหรือไม่

444
00:23:50,475 --> 00:23:53,341
ในขั้นที่สามตามที่แสดงใน flowchart

445
00:23:53,341 --> 00:23:57,050
มันจะหยุดค้างเพื่อรอการอนุมัติ แล้วเรา print

446
00:23:57,050 --> 00:23:59,663
คำตัดสินใจของมนุษย์ว่า approved

447
00:23:59,663 --> 00:24:02,950
หรือถ้าไม่อนุมัติก็บอกว่า rejected ครับ

448
00:24:02,950 --> 00:24:06,659
ต่อไปมี path A กับ path B มันจะ resume agent

449
00:24:06,659 --> 00:24:08,092
โดยเรียก run sync

450
00:24:08,092 --> 00:24:10,621
อีกครั้งพร้อมคำตัดสินใจอนุมัติ

451
00:24:10,621 --> 00:24:12,138
มันเรียกแบบนี้ครับ

452
00:24:12,138 --> 00:24:16,016
ส่งคำตัดสินใจของมนุษย์ตรงนี้ โดย invocation ID

453
00:24:16,016 --> 00:24:19,893
เท่ากับ approval info ซึ่งต้องมี invocation ID

454
00:24:19,893 --> 00:24:23,349
เดียวกัน หมายความว่ามันบอก ADK ให้ resume

455
00:24:23,349 --> 00:24:26,889
ถ้าสองอย่างนี้ไม่เท่ากัน คุณจะไปทาง path B

456
00:24:26,889 --> 00:24:30,514
แล้วโยน error นั้นออกมา แล้วบอกว่า workflow

457
00:24:30,514 --> 00:24:33,633
function พร้อมแล้ว จากนั้นบอกว่าไม่พบ

458
00:24:33,633 --> 00:24:37,510
หรือไม่ได้รับอนุมัติ หรือไม่ต้องการอนุมัติครับ

459
00:24:37,510 --> 00:24:40,966
เราสร้าง workflow สำเร็จเรียบร้อยแล้วครับ

460
00:24:40,966 --> 00:24:43,664
ถ้าอยากเข้าใจว่าโค้ดทำงานอย่างไร

461
00:24:43,664 --> 00:24:46,951
คุณสามารถย้อนกลับไปดูอีกครั้งได้ ผมพาดู

462
00:24:46,951 --> 00:24:49,143
flowchart และโค้ดนี้ไปแล้ว

463
00:24:49,143 --> 00:24:52,346
แต่ถ้ายังอยากเข้าใจให้ดีขึ้นด้วยตัวเอง

464
00:24:52,346 --> 00:24:55,633
ก็เดินดูตามส่วนแยกโค้ด (code breakdown)

465
00:24:55,633 --> 00:24:58,921
นี้ได้เลยครับ ต่อไปคือการทดสอบ workflow

466
00:24:58,921 --> 00:25:01,281
เราสร้าง workflow สำเร็จแล้ว

467
00:25:01,281 --> 00:25:03,726
ตอนนี้จะรันมันครับ มี warning

468
00:25:03,726 --> 00:25:07,434
ขึ้นว่ามีส่วนที่ไม่ใช่ข้อความใน response ของ

469
00:25:07,434 --> 00:25:08,534
function call

470
00:25:08,534 --> 00:25:11,652
เราเคยเจอแบบนี้มาแล้วในตัวอย่างก่อน ๆ

471
00:25:11,652 --> 00:25:14,350
และผมเคยบอกไปแล้วว่าไม่ต้องกังวล

472
00:25:14,350 --> 00:25:17,637
มันบอกว่านี่เป็นเรื่องปกติและไม่สนใจได้

473
00:25:17,637 --> 00:25:20,672
แค่หมายความว่า agent กำลังเรียก tool

474
00:25:20,672 --> 00:25:24,886
เพิ่มเติมนอกเหนือจากการสร้างข้อความเท่านั้นเองครับ

475
00:25:24,886 --> 00:25:27,584
ตอนนี้ผมจะรัน แล้วก็ได้ response

476
00:25:27,584 --> 00:25:28,848
แบบเดียวกันครับ

477
00:25:28,848 --> 00:25:32,641
คำถามที่ใช้คือขนส่งสามคอนเทนเนอร์ไป Singapore

478
00:25:32,641 --> 00:25:36,098
บอกว่า ship three containers to Singapore

479
00:25:36,098 --> 00:25:39,806
แล้วบอกว่าขนส่งสิบไป Rotterdam และขนส่งแปดไป

480
00:25:39,806 --> 00:25:43,684
Los Angeles โดยมี approval เป็น true กับ false

481
00:25:43,684 --> 00:25:47,309
ครับ ออเดอร์แรกผ่านไปเลยโดยไม่ต้องขออนุมัติ

482
00:25:47,309 --> 00:25:51,355
มันบอกอัตโนมัติว่าออเดอร์ได้รับอนุมัติแล้วและให้

483
00:25:51,355 --> 00:25:54,979
order ID มา ส่วนอันที่สอง เพราะค่าเป็น true

484
00:25:54,979 --> 00:25:56,078
แปลว่าอนุมัติ

485
00:25:56,078 --> 00:25:59,703
สิ่งที่เกิดขึ้นคือมันจะหยุดค้างรอการอนุมัติ

486
00:25:59,703 --> 00:26:02,400
รับคำตัดสินใจของมนุษย์ว่าอนุมัติ

487
00:26:02,400 --> 00:26:06,025
แล้วจึงบอกว่าโอเค ออเดอร์ขนส่งได้รับอนุมัติ

488
00:26:06,025 --> 00:26:09,734
นี่คือ order ID และอันสุดท้ายมันรอการอนุมัติ

489
00:26:09,734 --> 00:26:12,431
แล้วคำตัดสินใจของมนุษย์คือปฏิเสธ

490
00:26:12,431 --> 00:26:15,382
ออเดอร์แปดคอนเทนเนอร์ไป Los Angeles

491
00:26:15,382 --> 00:26:16,730
จึงถูกปฏิเสธครับ

492
00:26:16,730 --> 00:26:22,462
เราได้เห็นครบทั้งสามสถานการณ์และการตอบสนองของมันในแต่ละสถานการณ์แล้ว

493
00:26:22,462 --> 00:26:26,340
ตรงนี้ยังมีเส้นทางการทำงานฉบับเต็มแบบ optional

494
00:26:26,340 --> 00:26:29,711
ที่คุณอยากดูจริง ๆ ก็เข้าไปได้ครับ มาถึง

495
00:26:29,711 --> 00:26:32,746
Section 5 ซึ่งอธิบาย pattern ของ MCP

496
00:26:32,746 --> 00:26:36,033
integration และ long-running operations

497
00:26:36,033 --> 00:26:38,309
ที่เราใส่เข้าไปในโค้ดวันนี้

498
00:26:38,309 --> 00:26:41,428
และควรใช้เมื่อไหร่ครับ โดยพื้นฐานแล้ว

499
00:26:41,428 --> 00:26:45,053
เมื่อคุณต้องเชื่อมต่อกับบริการภายนอก คุณใช้

500
00:26:45,053 --> 00:26:48,256
MCP integration และเมื่อคุณต้องการหยุด

501
00:26:48,256 --> 00:26:50,953
workflow เพื่อ human-in-the-loop

502
00:26:50,953 --> 00:26:55,337
โดยเฉพาะงานเบื้องหลังที่ใช้เวลานานหรือจุดตรวจสอบด้าน

503
00:26:55,337 --> 00:26:59,045
compliance หรือ security คุณใช้ long-running

504
00:26:59,045 --> 00:27:02,586
operations เหล่านี้ครับ นี่เป็นแนวคิดระดับ

505
00:27:02,586 --> 00:27:06,210
production ready เราได้เข้าใจแล้วว่าจะสร้าง

506
00:27:06,210 --> 00:27:08,823
agent ที่ขยายขนาดได้ จัดการเวลา

507
00:27:08,823 --> 00:27:10,931
รับประกันความถูกต้องตามกฎ

508
00:27:10,931 --> 00:27:14,808
และคงสถานะไว้ได้อย่างไร ถ้าอยากลอง มีแบบฝึกหัด

509
00:27:14,808 --> 00:27:18,433
optional ให้ทำครับ สถานการณ์คือคุณต้องสร้าง

510
00:27:18,433 --> 00:27:21,130
agent ที่สร้างภาพด้วย MCP server

511
00:27:21,130 --> 00:27:24,839
แต่ต้องได้รับอนุมัติสำหรับการสร้างภาพเป็นชุด

512
00:27:24,839 --> 00:27:28,632
คำขอภาพเดี่ยวให้อนุมัติอัตโนมัติและสร้างทันที

513
00:27:28,632 --> 00:27:31,667
ส่วนคำขอเป็นชุดซึ่งมีมากกว่าหนึ่งภาพ

514
00:27:31,667 --> 00:27:34,617
ให้หยุดและขออนุมัติก่อนสร้างหลายภาพ

515
00:27:34,617 --> 00:27:37,905
และให้สำรวจ image generation MCP server

516
00:27:37,905 --> 00:27:39,422
สาธารณะต่าง ๆ ด้วย

517
00:27:39,422 --> 00:27:43,721
สำหรับส่วนนี้คุณสามารถตามลิงก์ที่ผมใส่ไว้ในคำอธิบาย

518
00:27:43,721 --> 00:27:46,587
เพื่อดูว่า server ไหนเหมาะกว่าครับ

519
00:27:46,587 --> 00:27:50,549
ไปกันต่อที่วิดีโอถัดไปเพื่อเรียนเรื่องการจัดการ

520
00:27:50,549 --> 00:27:52,572
state และ memory กันครับ

521
00:27:52,572 --> 00:27:59,568
และโปรดทราบว่าคุณไม่จำเป็นต้องส่งโปรเจกต์นี้หรือสิ่งที่เรากำลังทำอยู่นี้ไปที่ไหนเลย

522
00:27:59,568 --> 00:28:04,289
ทั้งหมดเพื่อให้เราเข้าใจและเรียนรู้ให้ดีขึ้นเท่านั้นครับ

523
00:28:04,289 --> 00:28:07,660
ด้วยเท่านี้เราก็สำเร็จแบบฝึกหัดของ Day 2

524
00:28:07,660 --> 00:28:11,201
ครบถ้วนแล้ว ไปกันต่อที่วิดีโอ Day 3 Part A

525
00:28:11,201 --> 00:28:14,741
เพื่อเรียนเรื่องการจัดการ state และ memory

526
00:28:14,741 --> 00:28:15,840
กันครับ