Day 2B: Model Context Protocol (MCP) — Full Tutorial & Hands-On Demo
สรุปย่อ
ประเด็นสำคัญจากวิดีโอ
- **ช่อง:** 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 ที่ต้องมีการควบคุมและอนุมัติจริงจังครับ
คำแปลเต็ม
แปลตามบทสนทนาต้นฉบับ ปรับเป็นภาษาไทยธรรมชาติ
ในวิดีโอนี้เราจะสำรวจแนวปฏิบัติที่ดีสำหรับ 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 กันครับ
หมายเหตุการแปล
ความโปร่งใสเกี่ยวกับความไม่แน่นอนในต้นฉบับ
- ## จุดที่ใช้สัญลักษณ์ฟังไม่ชัด
อภิธานศัพท์เทคนิค
คำศัพท์และชื่อผลิตภัณฑ์ที่คงรูปภาษาอังกฤษ
| ศัพท์ | คำแปล / คำอธิบาย |
|---|---|
| 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 server | MCP server ตัวอย่างสำหรับทดสอบการทำ MCP integration |
| tool filter | ตัวกรองเลือกเฉพาะ tool ที่ต้องการจาก MCP server |
| long-running operations | งานที่ใช้เวลานานหรือต้องหยุดรอ input ภายนอกก่อนจึงทำต่อได้ |
| human-in-the-loop | รูปแบบที่มีมนุษย์เข้ามาอนุมัติหรือตัดสินใจในวงจรการทำงานของ agent |
| resumable workflow / app | workflow หรือแอปที่หยุดค้างไว้แล้วกลับมาทำงานต่อได้ พร้อมคง 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 event | special event ที่ส่งสัญญาณให้ agent หยุดค้างรอการยืนยัน |
| invocation ID | ID เฉพาะของการเรียก 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 ของคอร์สนี้ |
ซับไตเติ้ลภาษาไทย
ดาวน์โหลดหรือดูซับทั้งหมด
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
เปิดดูซับไตเติ้ลทั้งหมด (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 กันครับ