Day 3A: Agent Memory Explained — Sessions, Long-Term Context & Recall
สรุปย่อ
ประเด็นสำคัญจากวิดีโอ
- **ช่อง:** Adspolitan Knowledge Studio · **ความยาว:** ~20 นาที · **ลิงก์:** https://www.youtube.com/watch?v=Cyr6l8Sd8rU
# สรุป: Day 3A: Agent Memory Explained — Sessions, Long-Term Context & Recall - **ช่อง:** Adspolitan Knowledge Studio · **ความยาว:** ~20 นาที · **ลิงก์:** https://www.youtube.com/watch?v=Cyr6l8Sd8rU ## ประเด็นหลัก - แบบฝึกหัด Day 3 Part A ของคอร์ส Google × Kaggle 5-Day AI Agents Intensive พาทำ stateful agent ด้วย session และ context engineering ใน ADK - LLM ทุกตัว stateless โดยธรรมชาติ — รู้เฉพาะข้อมูลในการเรียก API ครั้งเดียว จึงต้องใช้ session เป็นความจำระยะสั้น และ memory เป็นความจำระยะยาว - Session คือภาชนะบรรจุบทสนทนาของ user หนึ่งคนกับ agent หนึ่งตัว ประกอบด้วยสองส่วนคือ event (input, คำตอบ, tool call, ผลลัพธ์) และ state (scratchpad key-value ที่ tool ทุกตัวเข้าถึงได้) - เปรียบเทียบให้เห็นภาพ: session คือสมุดบันทึก, event คือรายการในหน้า, session service คือตู้แฟ้ม, runner คือผู้ช่วยบริหารการสนทนา - InMemorySessionService เก็บใน RAM จึงลืมทุกอย่างเมื่อรีสตาร์ท kernel — demo พิสูจน์ด้วยการรีสตาร์ทแล้ว agent ตอบว่าจำอดีตไม่ได้ - ทางเลือก session service สามแบบ: InMemory (dev/ทดสอบ), Database (self-managed, อยู่รอดการรีสตาร์ท), Agent Engine (production บน GCP แบบ fully managed) - อัปเกรดเป็น DatabaseSessionService ด้วย SQLite (ไฟล์ในเครื่อง ไม่ต้องมี DB server) แล้วทดสอบว่า session แยกส่วนจริง — session 02 ไม่รู้จักชื่อที่บอกไว้ใน session 01 - Context compaction ใช้ EventCompactionConfig (interval และ overlap size) ให้ runner สรุปย่อประวัติเก่าโดยอัตโนมัติ — ไม่ลบ event เก่าแต่แทนที่ด้วย event เดียวที่บรรจุสรุป ช่วยลด cost และเพิ่มความเร็ว - ตัวเลือกขั้นสูงเพิ่มเติม: custom sliding window compactor (ควบคุม summarization prompt หรือใช้ LLM เฉพาะทาง) และ gzip context (บีบอัด static instructions ให้เล็กลง) - การจัดการ session state ด้วย custom tool: สร้าง save_user_info / retrieve_user_info เก็บ username กับ country ผ่าน tool context — ข้อมูลแนะนำครั้งเดียวแต่อ้างอิงได้ตลอดบทสนทนา - Cross-session state sharing: สร้าง session ใหม่โดยยก state จาก session ก่อนหน้ามาใช้ได้ เช่น ชื่อ Sam และประเทศ Poland ## ความเห็นสรุป วิดีโอนี้อธิบายแนวคิด session/state/compaction ได้เชื่อมโยงกันดีมาก จุดเด่นคือการพิสูจน์ความขี้ลืมของ in-memory agent ด้วยการรีสตาร์ท kernel จริง ๆ แล้วค่อยแก้ด้วย SQLite ทีละขั้น ทำให้เห็นปัญหาและทางแก้อย่างจับต้องได้ ส่วน context compaction กับ session state tool เป็นพื้นฐานสำคัญของ context engineering ที่จำเป็นต่อการทำ agent ใช้งานจริงใน production ครับ
คำแปลเต็ม
แปลตามบทสนทนาต้นฉบับ ปรับเป็นภาษาไทยธรรมชาติ
สวัสดีอีกครั้งครับ ยินดีต้อนรับสู่ Day 3 ของซีรีส์นี้ ในแบบฝึกหัดนี้เราจะเรียนวิธีทำให้ agent มีสถานะ (stateful) โดยการจัดการประวัติบทสนทนาผ่าน context engineering ใน ADK ครับ หัวข้อนี้เกี่ยวกับ context engineering, session และ memory และแบบฝึกหัดนี้ก็มี code lab สองส่วนเหมือนเดิม พร้อมตอนสรุปแบบพอดแคสต์ และ white paper ประกอบเรื่อง context engineering, session และ memory ด้วยครับ มาเริ่มกันที่ Part A ของ Day 3 ซึ่งก็คือการสร้าง stateful agent และทำ context engineering กันเลย กด Edit เพื่อเอาสำเนาที่แก้ไขได้ครับ
ใน session นี้เราจะเรียนอะไรบ้าง? ใน Day 3 ส่วนที่หนึ่ง เราจะเรียนว่า session คืออะไรและใช้กับ agent อย่างไร วิธีสร้าง stateful agent ด้วย session และ event และวิธีทำให้ session คงอยู่ถาวร (persist) ในฐานข้อมูล รวมถึงแนวปฏิบัติด้านการจัดการบริบท เช่น context compaction และแนวปฏิบัติที่ดีในการแบ่งปันสถานะของ session ครับ อย่างที่รู้กัน เราไม่ต้องติดตั้งไลบรารี Google ADK สำหรับ Python เพิ่ม ไปกันเลยที่ส่วนตั้งค่า Gemini API key ของ Section 1 สำหรับขั้นนี้ให้กด Add-ons แล้วเลือก Secret เลือก key แล้วรันโค้ดได้เลย เสร็จเรียบร้อยครับ
ต่อไป import คอมโพเนนต์ของ ADK ครับ ตรงนี้เรามี event compaction config ซึ่งเป็นสิ่งใหม่ในแบบฝึกหัดนี้ รวมถึง DatabaseSessionService และ InMemorySessionService เราจะคุยกันเรื่อง event compaction config ตอนอ่านโค้ดกันแน่นอนครับ เยี่ยมเลย ตอนนี้เรา import คอมโพเนนต์ของ ADK สำเร็จแล้ว ส่วนถัดไปคือการนิยาม helper function ครับ helper function ตรงนี้จัดการ session บทสนทนาแบบครบวงจร ทั้งการสร้างและดึง session การประมวลผลคำถาม และการสตรีมคำตอบกลับมา รองรับทั้งคำถามเดี่ยวและหลายคำถามเรียงกัน เช่น ถามว่าเมืองหลวงของฝรั่งเศสคืออะไรใน session เรื่องภูมิศาสตร์ หรือทักว่าสวัสดีแล้วถามว่าชื่ออะไร แล้วค่อยแนะนำตัวใน session แนวนั้นครับ ขอรันก่อน เสร็จไวมากครับ แล้วก็รันส่วนตั้งค่าจนจบ นั่นแปลว่า agent ที่ไม่มีการจัดการบริบทที่เหมาะสมจะตอบสนองกับ prompt ปัจจุบันโดยไม่สนประวัติที่ผ่านมา ถือว่าเตรียมความพร้อมเสร็จสมบูรณ์แล้วครับ
ไปกันต่อที่ส่วนจัดการ session ครับ LLM ทุกตัวนั้น stateless (ไร้สถานะ) โดยธรรมชาติ หมายความว่าความรับรู้ของมันจำกัดอยู่แค่ข้อมูลที่คุณให้ในการเรียก API ครั้งเดียว นั่นหมายความว่า agent ที่ไม่มีการจัดการบริบทที่ดีจะตอบสนองกับ prompt ปัจจุบันโดยไม่คำนึงถึงประวัติที่ผ่านมาเลย ไม่ว่าคุณจะเคยคุยอะไรกับมันไว้ก่อนหน้าครับ ทำไมนี่ถึงเป็นปัญหา? เพราะในโลกจริง ลองนึกภาพคุณกำลังคุยกับใครสักคน พยายามมีบทสนทนาที่มีความหมายด้วยกัน แต่คน ๆ นั้นลืมทุกอย่าง ลืมทุกประโยคที่คุณเพิ่งพูดไปก่อนหน้านี้ตลอดเวลา นี่คือความท้าทายที่เราเผชิญกับ LLM ดิบ ๆ ครับ ด้วยเหตุนี้เราจึงใช้ session และ memory — session สำหรับจัดการความจำระยะสั้น และ memory สำหรับความจำระยะยาวครับ ใน notebook นี้เราจะโฟกัสเรื่อง session ก่อน แต่ก่อนหน้านั้น มาทำความเข้าใจกันว่า session คืออะไร
session คือภาชนะบรรจุบทสนทนาครับ มันห่อหุ้มประวัติบทสนทนาไว้อย่างเรียงตามลำดับเวลา และบันทึกการเรียกใช้ tool และการตอบกลับทั้งหมดของบทสนทนาต่อเนื่องหนึ่งรอบ session ผูกกับ user หนึ่งคนกับ agent หนึ่งตัว ไม่ถูกแบ่งปันกับ user คนอื่น ๆ เช่นกัน ประวัติ session ของ agent หนึ่งตัวก็ไม่ถูกแบ่งปันให้ agent ตัวอื่นด้วยครับ ใน ADK นั้น session ประกอบด้วยสองส่วน คือ event และ state ครับ มาดูกันว่า event กับ state คืออะไร ตัวอย่างเช่น คุณมี input ของผู้ใช้ มีคำตอบของ agent มีการเรียก tool และมีผลลัพธ์ของ tool ทั้งหมดนี้ล้วนเป็นตัวอย่างของ event ครับ เรียกแต่ละอย่างว่า event ได้เลย แล้ว state คืออะไร? session state คือกระดาษทดของ agent (scratchpad) ที่มันใช้เก็บและอัปเดตรายละเอียดแบบไดนามิกที่ต้องใช้ระหว่างบทสนทนา คุณอาจนึกภาพเป็นที่เก็บคู่ key-value แบบ global ที่ sub-agent และ tool ทุกตัวเข้าถึงได้ครับ มันทำงานแบบนี้ ใน session คุณมีสององค์ประกอบคือ event กับ state นี่คือ user นี่คือแอปพลิเคชัน agent ครับ
คำถามถัดไปคือเราจัดการ session เหล่านี้อย่างไร แอปพลิเคชันแบบ agent หนึ่งตัวมีได้หลาย user และ user แต่ละคนอาจมีหลาย session กับแอปพลิเคชันครับ เพื่อจัดการ session และ event เหล่านี้ ADK มี session service และ runner ให้ครับ session service ทำอะไร? มันคือชั้นการจัดเก็บ (storage layer) ครับ ดูแลการสร้าง การเก็บ และการดึงข้อมูล session และยังมี implementation หลายแบบสำหรับความต้องการที่ต่างกัน เช่น แบบเก็บในหน่วยความจำ แบบฐานข้อมูล แบบคลาวด์ ฯลฯ ส่วน runner อย่างที่ทุกคนรู้กันคือชั้นควบคุมจังหวะ (orchestration layer) ครับ มันดูแลการไหลของข้อมูลระหว่าง user กับ agent บำรุงรักษาประวัติบทสนทนาโดยอัตโนมัติ และจัดการ context engineering อยู่เบื้องหลังครับ พูดง่าย ๆ ก็คือ session เปรียบเหมือนสมุดบันทึก event เปรียบเหมือนรายการต่าง ๆ ในหนึ่งหน้า session service เปรียบเหมือนตู้เก็บแฟ้มที่เก็บสมุดเหล่านั้น และ runner เปรียบเหมือนผู้ช่วยที่บริหารการสนทนาครับ
มา implement stateful agent ตัวแรกของเรากันเถอะ ขอรันโค้ดก่อนแล้วค่อยไล่ดูนะครับ ตรงนี้เรามี app name, user ID และ session โมเดลที่ใช้คือ Gemini 2.5 Flash-Lite ครับ ขั้นแรกคือสร้าง LLM agent ซึ่งเราทำเป็นอยู่แล้ว โดย description ระบุว่าเป็น chatbot ที่เรากำลังสร้างครับ ขั้นที่สองคือการจัดการ session ซึ่งจะมี service หนึ่งตัว เรานิยาม session service เป็น InMemorySessionService ครับ แล้วเราสร้าง runner ซึ่งโดยพื้นฐานแล้วต้องมี agent, app name และ session service ครับ แล้วเราก็ print ทั้งหมดนี้ออกมา เสร็จเรียบร้อย ขั้นถัดไปคือทดสอบ stateful agent ของเรา โดยใน runner เราถามสองคำถาม คือ หนึ่ง สวัสดี ผมชื่อ Sam เมืองหลวงของสหรัฐอเมริกาคืออะไร และสอง สวัสดี ผมชื่ออะไรครับ พอเสร็จขั้นตอนนี้ agent ควรจะจำชื่อ Sam ได้ มาดูกันว่ามันเกิดขึ้นอย่างไรครับ
โอเค คำถามของ user คือ สวัสดี ผมชื่อ Sam เมืองหลวงของสหรัฐอเมริกาคืออะไร chatbot ตอบว่า สวัสดี Sam เมืองหลวงของสหรัฐอเมริกาคือ Washington DC ครับ จากนั้น user ถามต่อว่า สวัสดี ผมชื่ออะไรครับ chatbot ตอบกลับมาว่า ชื่อของคุณคือ Sam ครับ จากตรงนี้เราเข้าใจได้ว่า runner บำรุงรักษาประวัติบทสนทนาให้อัตโนมัติ แต่มีข้อควรระวังอยู่อย่างคือ in-memory session service ตัวนี้เป็นแบบชั่วคราว อย่างที่ระบุไว้ตรงนี้ครับ มันเก็บบทสนทนาไว้ใน RAM ซึ่งเป็นการเก็บแบบชั่วคราว ดังนั้นพอแอปพลิเคชันหยุดทำงาน ประวัติบทสนทนาทั้งหมดก็หายไปครับ
ทีนี้มาทดสอบความขี้ลืมของ agent ตัวนี้กัน เราจะพิสูจน์ว่า agent ลืมบทสนทนาจริง ๆ โดยรีสตาร์ท kernel ด้วยการรันอันนี้ครับ แล้วเราจะรันเซลล์ก่อนหน้าทั้งหมดใน notebook นี้ ยกเว้นเซลล์รัน session ข้อ 2.5 ครับ โอเค ตอนนี้เราทำเสร็จแล้ว มันตอบว่า ผมถามอะไรกับคุณไปก่อนหน้านี้? คุณถามผมเรื่องเมืองหลวงของสหรัฐอเมริกา และ ชื่อผมคืออะไร คุณชื่อ Sam ครับ ทีนี้ผมย้อนกลับไปรันเซลล์ก่อนหน้าทั้งหมดตามปกติ เสร็จแล้ว แล้วผมจะข้ามส่วนนี้ไปครับ แล้วมาตรงส่วนนี้เลย คุณเห็นว่าคำตอบเปลี่ยนไปแล้ว ผมถามอะไรกับคุณไปก่อนหน้านี้? ขอโทษด้วย ผมจำบทสนทนาที่ผ่านมาไม่ได้ ผมไม่มีความจำเกี่ยวกับการสนทนาก่อน ๆ ของเรา และ ชื่อผมคืออะไร ผมไม่มีสิทธิ์เข้าถึงข้อมูลส่วนตัวของคุณ รวมถึงชื่อด้วย ผมเป็นแค่ chatbot ข้อความ และไม่ได้เก็บข้อมูลของผู้ใช้ครับ เพราะเราข้ามส่วนนั้นไป เรารันแบบนี้แล้วกระโดดมาที่ runner session ถัดไปตรง ๆ มันจึงลืมสิ่งที่เราคุยกันไปครับ แปลว่าทุกครั้งที่คุณรีเซ็ตแอปพลิเคชัน รีสตาร์ท kernel ประวัติก็หายวับไปครับ นี่เป็นปัญหาใช่ไหมครับ เพราะถ้าข้อมูล session ไม่ถูกเก็บอย่างถาวร มันก็ใช้งานในโลกจริงไม่ได้ user ควรจะอ้างอิงเรื่องราวในอดีตและกลับมาต่อบทสนทนาเดิมได้ครับ ด้วยเหตุนี้เราจึงไปกันต่อที่ส่วนถัดไป ซึ่งก็คือ persistent session ด้วย DatabaseSessionService ครับ ถ้าจำได้ เรา import service ตัวนี้ไว้แล้วครับ
ขั้นแรกคือเลือก session service ที่ใช่ครับ ADK มี implementation ของ session service หลายแบบสำหรับความต้องการที่ต่างกัน โอเค ตัวแรกคือ InMemorySessionService สำหรับพัฒนาและทดสอบ ซึ่งข้อมูลหายเมื่อรีสตาร์ท และนี่คือโค้ดที่เราใช้ไปก่อนหน้าครับ ตัวถัดไปคือ database session สำหรับแอปที่ดูแลเอง มันอยู่รอดจากการรีสตาร์ท หมายความว่าจะไม่เสียประวัติไปครับ ตัวที่สามคือ Agent Engine session สำหรับ production บน GCP เป็นแบบ fully managed ระดับ enterprise ครับ สำหรับตอนนี้เพราะเราอยู่ใน sandbox อยู่ใน demo เราจะใช้ DatabaseSessionService กันครับ มาอัปเกรดกันด้วย SQLite กันเลย ซึ่งให้ความถาวรแบบ persistence โดยไม่ต้องมีเซิร์ฟเวอร์ฐานข้อมูลแยกต่างหากครับ สำหรับ demo นี้เราจะสร้าง chatbot ที่สามารถรับมือบทสนทนากับ user ได้ ประมาณ ChatGPT หรือ Gemini ครับ
ทีนี้เรานิยาม agent กัน มี name และมี description ระบุว่าเป็น chatbot ที่มีความจำถาวรครับ แล้วเราสลับมาใช้บริการฐานข้อมูล และจะสร้างฐานข้อมูล SQLite ขึ้นมาโดยอัตโนมัติ เรามี DB URL ซึ่งเป็นไฟล์ในเครื่อง และมี session service โดยเราแทน InMemorySessionService ด้วย DatabaseSessionService ครับ ทีนี้รัน runner ก็สำเร็จเรียบร้อย อัปเกรดเสร็จแล้วครับ ต่อไปเราจะพิสูจน์ความถาวร คือเราจะรันอันนี้ก่อน มันถามว่า สวัสดี ผมชื่อ Sam เมืองหลวงของสหรัฐอเมริกาคืออะไร เราถามคำถามเดิมและได้คำตอบเดิมครับ ตรงนี้เรามี session ID ซึ่งก็คือ 01 ครับ
ทีนี้มาดูกันว่า session นี้แยกส่วน (isolated) จริงหรือไม่ นี่คืออีก session หนึ่ง ถามว่า ผมชื่ออะไรครับ มันตอบว่า ผมไม่มีสิทธิ์เข้าถึงข้อมูลส่วนตัว รวมถึงชื่อของคุณด้วย ดังนั้นผมบอกชื่อคุณไม่ได้ครับ นี่แปลว่า session เป็นเรื่องส่วนตัวระหว่าง agent กับ user ครับ ในสอง session ข้อมูลไม่ถูกแบ่งปันกัน คุณรัน session นี้ด้วย session ID ที่ต่างกัน อย่างที่เห็นคือ 02 ส่วนอันก่อนเป็น 01 มันก็จะบอกว่าไม่รู้ชื่อคุณครับ
ส่วนถัดไปคือ context compaction ครับ ในส่วนนี้เราเห็นว่า event ทั้งหมดถูกเก็บแบบเต็ม ๆ ในฐานข้อมูล session และเพิ่มปริมาณเร็วมากครับ สำหรับงานยาว ๆ ซับซ้อน ลิสต์นี้อาจใหญ่โตมาก นำไปสู่ประสิทธิภาพที่ช้าลงและค่าใช้จ่ายที่สูงขึ้น แต่ถ้าเราสรุปอดีตที่ผ่านมาแบบอัตโนมัติได้ล่ะ? มาใช้ฟีเจอร์ context compaction กันดู ว่ามันลดบริบทที่เก็บใน session โดยอัตโนมัติอย่างไรครับ สำหรับส่วนนี้ เราจะสร้าง app สำหรับ agent โดยใช้ chatbot ตัวเดิมที่ใช้ใน session ก่อนหน้าครับ ขั้นแรกคือสร้าง object ชื่อ app แล้วสร้าง config ใหม่สำหรับทำ context compaction ครับ config นี้ก็คือ event compaction config ซึ่งมีตัวแปรหลักสองตัว คือ interval กับ overlap size ครับ interval นั้นบอก runner ให้ compact ประวัติหลังจากผ่านไปกี่บทสนทนา เพื่อให้มันเล็กลงครับ ส่วน overlap size กำหนดจำนวนบทสนทนาก่อนหน้าที่จะเก็บไว้ให้ซ้อนทับกันครับ
ทีนี้เราจะมอบ app นี้ให้ runner เรานิยาม app ตรงนี้ก่อน นี่คือ app ของ agent โดยมีส่วนใหม่ตรงนี้คือ event compaction ซึ่งเป็น config ประกอบด้วย interval กับ overlap ตามที่กล่าวไปครับ เรากำหนด interval เป็นสาม หมายความว่าให้ trigger การ compact ทุก ๆ สามการเรียกใช้ และ overlap size เป็นหนึ่ง หมายความว่าเก็บบทสนทนาเทิร์นก่อนหน้าไว้หนึ่งเทิร์นเพื่อเป็นบริบทครับ เรากำหนด DB URL กับ session service แล้วสร้าง runner ใหม่สำหรับ app ที่อัปเกรดแล้ว ตั้งชื่อว่า compacting runner และ app คือ compacting app โดย session service เท่ากับตัว session service เดิมครับ รันโค้ดนี้ คุณเห็นว่า app ได้รับการอัปเกรดด้วย event compaction แล้ว ได้คำตอบออกมา ได้ print statement ตามต้องการครับ
ต่อไปรัน demo แล้วดูว่ามันทำงานอย่างไร มาคุยกันยาว ๆ โดยมีเทิร์นที่หนึ่ง สอง สาม และสี่ หมายความว่ายาวพอจะ trigger การ compaction ได้ครับ และเมื่อเรารันชุดโค้ดนี้ ผลลัพธ์ (output) ควรดูเหมือนบทสนทนาปกติ เพราะเราตั้งค่า app ไว้แล้ว กระบวนการ compaction จะรันแบบเงียบ ๆ ในเบื้องหลังหลังการเรียกใช้ครั้งที่สามครับ ขอรันเลย เสร็จแล้ว และคุณเห็น demo ของการ compaction ครับ มีบทสนทนาทั้งหมดอยู่ตรงนี้ คำถามแรกคือ มีข่าวล่าสุดอะไรเกี่ยวกับ AI ในวงการสุขภาพบ้าง มันก็ตอบมาครับ ถัดไปเป็นเรื่อง มีความก้าวหน้าใหม่ ๆ ในการค้นพบยาหรือไม่ มีพัฒนาการใหม่ในด้านนั้นไหม คุณเห็นว่ามันตอบมายาวและดีมากครับ แล้วก็มีอันถัดไป ถ้าดูต่อไปมันคุยกันเรื่อย ๆ และคุณมีบทสนทนาทั้งหมดอยู่ตรงนี้ครับ
ทีนี้เรามาตรวจสอบการ compaction และประวัติ session กันครับ โอเค นั่นหมายความว่าบทสนทนานี้ดูปกติ แต่ประวัติเปลี่ยนไปแล้วเบื้องหลัง มาดูกันว่าเกิดอะไรขึ้น เราจะรันอันนี้ครับ เราสามารถตรวจสอบ event ใน session ของเราได้ และกระบวนการ compaction ไม่ได้ลบ event เก่าทิ้ง แต่มันแทนที่ด้วย event ใหม่ตัวเดียวที่บรรจุสรุปย่อไว้ครับ มาดูกันตรงนี้ มันบอกว่าพบ compaction event ที่มี author กำกับ และมันบอกว่าเกิดอะไรขึ้นตรงนี้ คุณสามารถไล่อ่านดูได้ครับ มันจะ save กับอันที่สอง และมันพูดถึง AI ตรงนี้ใช่ไหมครับ และคุณต้องแค่ไล่อ่านต่อ คุณจะพบว่ามีการระบุ author กับข้อมูลที่ถูก compact พร้อมสรุปย่อที่ได้มาครับ
ทีนี้สิ่งที่คุณทำสำเร็จแล้วคืออะไร ขอสรุปกันหน่อยครับ คุณได้ทำการทำงานแบบเงียบ (silent operation) คือเรารันบทสนทนาแบบมาตรฐาน จากมุมภายนอกดูเหมือนไม่มีอะไรต่างไป เหมือนบทสนทนาปกติของเรา แต่เบื้องหลังนั้น เราตั้งค่า app ด้วย event compaction config แล้ว runner ของ ADK เฝ้าติดตามความยาวบทสนทนาโดยอัตโนมัติ พอครบเกณฑ์ มันก็ trigger กระบวนการสรุปย่อขึ้นมาในเบื้องหลังครับ หลังจากนั้นเราก็ตรวจสอบผลลัพธ์ เราไปดู event ของ session และเราพบว่าสรุปย่อที่ LLM สร้างขึ้นมา ตัวนี้แทนที่เทิร์นเก่า ๆ ที่ยืดยาวกว่าในบริบทที่ agent ใช้งานอยู่ครับ
ส่วนถัดไปคือตัวเลือก context engineering อื่น ๆ ใน ADK ครับ อย่างแรกคือ compaction อย่างถัดไปคือ gzip ครับ custom compaction พื้นฐานแล้วมีไว้สำหรับ use case ขั้นสูงกว่า คุณสามารถกำหนดเองได้โดยนิยาม custom sliding window compactor แล้วส่งเข้าไปใน config ครับ สิ่งที่ได้คือคุณควบคุม prompt ที่ใช้สรุปย่อได้ หรือจะใช้ LLM เฉพาะทางตัวอื่นสำหรับงานนี้ก็ได้ครับ อ่านเพิ่มเติมได้ในเอกสารทางการตรงนี้ครับ อย่างถัดไปคือ context gzip หมายความว่า ADK ยังมีความสามารถนี้เพื่อช่วยลดขนาด token ของคำสั่งกำกับแบบคงที่ (static instructions) ที่ป้อนเข้าไปให้ LLM โดยการบีบอัด (compress) ข้อมูลคำขอครับ อ่านเพิ่มเติมได้ที่ลิงก์ตรงนี้ ผมจะใส่ลิงก์ทั้งหมดนี้ไว้ในคำอธิบายวิดีโอครับ
ตรงนี้เราจะพูดถึง session state ครับ ว่าการจัดการสถานะของ session ถูกสร้างขึ้นอย่างไร ส่วนย่อยแรกคือการสร้าง custom tool สำหรับจัดการ session state ครับ เราจะจัดการ session state ด้วยมือผ่าน custom tool กัน ในตัวอย่างที่จะ demo เราจะจับคุณลักษณะที่ถ่ายโอนได้ เช่น username กับประเทศของ user แล้วสร้าง tool ไว้เก็บและดึงข้อมูลเหล่านั้นครับ ทำไมต้องใช้ตัวอย่างนี้? username เป็นตัวอย่างที่สมบูรณ์แบบของข้อมูลที่แนะนำเข้ามาครั้งเดียวแต่ถูกอ้างอิงหลายครั้งใช่ไหมครับ แล้วมันควรคงอยู่ตลอดทั้งบทสนทนา เพราะมันแทนคุณลักษณะเฉพาะของ user ที่เพิ่มความเป็นส่วนตัว (personalization) ให้ครับ สำหรับ demo นี้เราจะสร้างสอง tool ที่สามารถเก็บและดึง username กับประเทศจาก session state ได้ครับ ข้อสังเกตคือ tool ทุกตัวเข้าถึง tool context object ที่เรา import ไว้ตั้งแต่ต้นตอนสร้าง notebook นี้ครับ คุณไม่จำเป็นต้องสร้าง tool แยกสำหรับทุก ๆ ชิ้นข้อมูลที่ต้องการแบ่งปัน จัดการทุกอย่างไว้ตรงนี้ได้เลยครับ ดังนั้น scope ของ username คือระดับ temp, user และ app ซึ่งนี่คือแนวปฏิบัติที่ดี และนี่คือสิ่งที่เรากำลังทำตามครับ
จากนั้นคุณจะนิยาม user info โดยมี tool context เป็น string และ country ก็เป็น string ครับ ในอาร์กิวเมนต์เราระบุว่า username ถูกเก็บใน session state และ country คือชื่อประเทศของ user ครับ เราเขียนลง session state ตรงนี้ด้วย tool context กับ username และ country แล้วคืน status เป็น success ครับ ขั้นถัดไปคือเข้าใจวิธีอ่านข้อมูลจาก state ครับ เรากำลังดึงข้อมูลออกมาตรงนี้ เราเซฟข้อมูลไว้แล้ว ตอนนี้เราดึงข้อมูลกลับมาครับ ตรงนี้คือ tool สำหรับดึง username กับ country จาก session เราจะดึงจาก session state ตรงนี้ พร้อม country ครับ ถ้าไม่มีข้อมูล มันจะบอกว่า country ไม่พบ หรือ username ไม่พบ มิฉะนั้นมันจะคืนสถานะ success พร้อม username กับ country ครับ มาสร้าง tool กัน รัน snippet เสร็จเรียบร้อยครับ
ขั้นถัดไปคือสร้าง agent สำหรับ session tool ครับ เรามี app, user id, model โดย agent เป็น text chatbot อีกครั้ง description คือ tool สำหรับจัดการบริบทของ user เพื่อบันทึก username และ country เมื่อได้รับข้อมูล ให้ใช้ save_user_info เพื่อบันทึก และใช้ retrieve_user_info เพื่อดึงข้อมูลครับ ส่วน tool ก็เป็น save กับ retrieve ตามปกติ เราตั้งค่า session service กับ runner ครับ ตรงนี้เราจะรัน agent นี้พร้อม session state tool ที่ตั้งค่าไว้ครับ ทีนี้เรามาทดสอบ session state กัน prompt คือ สวัสดี วันนี้เป็นอย่างไรบ้าง ผมชื่ออะไรครับ แล้วมันบอกว่า ผมชื่อ Sam ผมมาจาก Poland แล้วถามซ้ำอีกว่า ผมชื่ออะไร ผมมาจากประเทศไหนครับ มันทักว่า สวัสดี วันนี้เป็นอย่างไรบ้าง ผมชื่ออะไร มันตอบว่า สวัสดีครับ ผมสบายดี ขอบคุณที่ถามนะครับ ผมจำชื่อคุณไม่ได้เพราะไม่มีสิทธิ์เข้าถึงบทสนทนาที่ผ่านมา ถ้าอยากให้ผมจำ กรุณาบอกชื่อกับประเทศของคุณครับ ทีนี้เราบอกว่า ผมชื่อ Sam และผมมาจาก Poland มันตอบว่า ผมบันทึกชื่อคุณเป็น Sam และประเทศเป็น Poland แล้วครับ มีอะไรให้ช่วยอีกไหมครับ แล้วเราจึงถามคำถามเดิมซ้ำ มันตอบว่า คุณชื่อ Sam และคุณมาจาก Poland ครับ
ทีนี้มาตรวจสอบ session state กันครับ ใน session state คุณมี username เป็น Sam และ country เป็น Poland นี่คือวิธีที่มันถูกป้อนเข้าไปครับ ต่อไปมาแยกส่วนกัน ตอนนี้เราแยกมันออก นี่คือ session ใหม่ที่แยกไว้ แล้วเราถามคำถามเดิมซ้ำ และมันบอกว่า ผมไม่แน่ใจว่าคุณชื่ออะไรนะครับ ช่วยบอกได้ไหม หมายความว่า agent จะไม่รู้จักชื่อนี้ เพราะนี่เป็นคนละ session กันครับ ทีนี้มาทำการแบ่งปัน state ข้าม session (cross-session state sharing) กัน ผมรัน snippet นี้ ซึ่งพื้นฐานแล้วบอกว่า คุณมี session ใหม่ และ session state คือ state ของ session ก่อนหน้า ดังนั้นใน state ใหม่จึงมีชื่อกับประเทศอยู่ครับ
สุดท้ายเราจะล้างฐานข้อมูลที่มีอยู่แล้วเริ่มใหม่ครับ ตอนนี้ฐานข้อมูลถูกล้างแล้ว ล้างไฟล์ข้อมูลเก่าเรียบร้อยครับ ดังนั้นโดยสรุป ตอนนี้เรารู้แล้วว่า context engineering, session และ event, การเก็บถาวร, session state, การจัดการ state ด้วยตนเอง และระดับ production คืออะไรครับ ไปกันต่อที่วิดีโอถัดไปซึ่งเป็น Part 2 ของ Day 3 ที่เราจะรัน code lab เรื่องระบบความจำระยะยาว (long-term memory) กันครับ ขอบคุณครับ
หมายเหตุการแปล
ความโปร่งใสเกี่ยวกับความไม่แน่นอนในต้นฉบับ
- ## จุดที่ใช้สัญลักษณ์ฟังไม่ชัด
อภิธานศัพท์เทคนิค
คำศัพท์และชื่อผลิตภัณฑ์ที่คงรูปภาษาอังกฤษ
| ศัพท์ | คำแปล / คำอธิบาย |
|---|---|
| session | เซสชัน — ภาชนะบรรจุบทสนทนาของ user หนึ่งคนกับ agent หนึ่งตัว เรียงตามลำดับเวลา |
| stateful agent | agent ที่มีสถานะ สามารถจำบทสนทนาก่อนหน้าและใช้ต่อได้ |
| stateless | ไร้สถานะ — คุณสมบัติของ LLM ที่รู้เฉพาะข้อมูลในการเรียก API ครั้งเดียว |
| context engineering | วิศวกรรมการจัดการบริบทที่ส่งให้โมเดล เช่น สรุปย่อ บีบอัด จัดระเบียบ |
| event | เหตุการณ์ใน session เช่น user input, คำตอบของ agent, tool call, ผลลัพธ์ tool |
| session state | กระดาษทด (scratchpad) ของ agent — ที่เก็บ key-value ที่ sub-agent และ tool ทุกตัวเข้าถึงได้ |
| session service | ชั้นการจัดเก็บ session ของ ADK ดูแลการสร้าง เก็บ และดึงข้อมูล session |
| InMemorySessionService | บริการ session แบบเก็บใน RAM ใช้พัฒนา/ทดสอบ ข้อมูลหายเมื่อรีสตาร์ท |
| DatabaseSessionService | บริการ session แบบฐานข้อมูล เก็บถาวร อยู่รอดการรีสตาร์ท |
| Agent Engine session | บริการ session ระดับ production บน GCP แบบ fully managed |
| SQLite | ฐานข้อมูลไฟล์ในเครื่อง ให้ความถาวรโดยไม่ต้องมี DB server แยก |
| persistence | ความถาวรของข้อมูล — อยู่รอดแม้ปิดแอปหรือรีสตาร์ท |
| runner | ชั้น orchestration ของ ADK บริหารการไหลของข้อมูลระหว่าง user กับ agent และบำรุงรักษาประวัติบทสนทนา |
| context compaction | การสรุปย่อบริบทเก่าเป็น event เดียวเพื่อลดขนาด ลด cost เพิ่มความเร็ว |
| EventCompactionConfig | config ของการ compaction มี interval (compact ทุกกี่การเรียก) และ overlap size (เก็บกี่เทิร์นก่อนหน้าไว้) |
| custom sliding window compactor | ตัว compact แบบกำหนดเอง ควบคุม summarization prompt หรือเลือก LLM เฉพาะทางได้ |
| gzip context | การบีบอัด (compress) static instructions เพื่อลดขนาด token ที่ป้อนให้ LLM |
| tool context | บริบทที่ส่งเข้า tool อัตโนมัติ ให้เข้าถึงและเขียน session state ได้ |
| save_user_info / retrieve_user_info | ตัวอย่าง custom tool สำหรับเก็บ/ดึง username และ country จาก session state |
| personalization | การทำให้บทสนทนาเป็นส่วนตัว เช่น จำชื่อและประเทศของ user ไว้ใช้ตลอด |
| cross-session state sharing | การยก state จาก session เก่ามาใช้ใน session ใหม่ |
| memory | ความจำระยะยาวของ agent (ส่วนนี้โฟกัส session ส่วน memory เป็นเนื้อหา Day 3 Part B) |
| scratchpad | กระดาษทด — อุปมาของ session state ที่ agent ใช้จดรายละเอียดไดนามิก |
| white paper | เอกสารสรุปแนวคิดหลักประกอบแบบฝึกหัด |
| Gemini 2.5 Flash-Lite | โมเดลของ Google ที่ใช้ใน code lab นี้ |
| ChatGPT / Gemini | ตัวอย่าง chatbot ที่รับมือบทสนทนาต่อเนื่องได้ซึ่ง demo อ้างถึง |
ซับไตเติ้ลภาษาไทย
ดาวน์โหลดหรือดูซับทั้งหมด
1 00:00:00,000 --> 00:00:02,967 สวัสดีอีกครั้งครับ ยินดีต้อนรับสู่ Day 3 2 00:00:02,967 --> 00:00:06,380 ของซีรีส์นี้ ในแบบฝึกหัดนี้เราจะเรียนวิธีทำให้ 3 00:00:06,380 --> 00:00:08,160 agent มีสถานะ (stateful) 4 00:00:08,160 --> 00:00:10,980 โดยการจัดการประวัติบทสนทนาผ่าน context
เปิดดูซับไตเติ้ลทั้งหมด (408 segments)
1 00:00:00,000 --> 00:00:02,967 สวัสดีอีกครั้งครับ ยินดีต้อนรับสู่ Day 3 2 00:00:02,967 --> 00:00:06,380 ของซีรีส์นี้ ในแบบฝึกหัดนี้เราจะเรียนวิธีทำให้ 3 00:00:06,380 --> 00:00:08,160 agent มีสถานะ (stateful) 4 00:00:08,160 --> 00:00:10,980 โดยการจัดการประวัติบทสนทนาผ่าน context 5 00:00:10,980 --> 00:00:14,095 engineering ใน ADK ครับ หัวข้อนี้เกี่ยวกับ 6 00:00:14,095 --> 00:00:16,989 context engineering, session และ memory 7 00:00:16,989 --> 00:00:20,401 และแบบฝึกหัดนี้ก็มี code lab สองส่วนเหมือนเดิม 8 00:00:20,401 --> 00:00:23,294 พร้อมตอนสรุปแบบพอดแคสต์ และ white paper 9 00:00:23,294 --> 00:00:26,633 ประกอบเรื่อง context engineering, session และ 10 00:00:26,633 --> 00:00:30,045 memory ด้วยครับ มาเริ่มกันที่ Part A ของ Day 3 11 00:00:30,045 --> 00:00:33,458 ซึ่งก็คือการสร้าง stateful agent และทำ context 12 00:00:33,458 --> 00:00:35,387 engineering กันเลย กด Edit 13 00:00:35,387 --> 00:00:38,280 เพื่อเอาสำเนาที่แก้ไขได้ครับ ใน session 14 00:00:38,280 --> 00:00:41,544 นี้เราจะเรียนอะไรบ้าง? ใน Day 3 ส่วนที่หนึ่ง 15 00:00:41,544 --> 00:00:44,808 เราจะเรียนว่า session คืออะไรและใช้กับ agent 16 00:00:44,808 --> 00:00:48,147 อย่างไร วิธีสร้าง stateful agent ด้วย session 17 00:00:48,147 --> 00:00:51,188 และ event และวิธีทำให้ session คงอยู่ถาวร 18 00:00:51,188 --> 00:00:52,746 (persist) ในฐานข้อมูล 19 00:00:52,746 --> 00:00:55,639 รวมถึงแนวปฏิบัติด้านการจัดการบริบท เช่น 20 00:00:55,639 --> 00:00:56,975 context compaction 21 00:00:56,975 --> 00:01:00,387 และแนวปฏิบัติที่ดีในการแบ่งปันสถานะของ session 22 00:01:00,387 --> 00:01:03,651 ครับ อย่างที่รู้กัน เราไม่ต้องติดตั้งไลบรารี 23 00:01:03,651 --> 00:01:05,877 Google ADK สำหรับ Python เพิ่ม 24 00:01:05,877 --> 00:01:08,919 ไปกันเลยที่ส่วนตั้งค่า Gemini API key ของ 25 00:01:08,919 --> 00:01:12,331 Section 1 สำหรับขั้นนี้ให้กด Add-ons แล้วเลือก 26 00:01:12,331 --> 00:01:14,854 Secret เลือก key แล้วรันโค้ดได้เลย 27 00:01:14,854 --> 00:01:18,192 เสร็จเรียบร้อยครับ ต่อไป import คอมโพเนนต์ของ 28 00:01:18,192 --> 00:01:21,456 ADK ครับ ตรงนี้เรามี event compaction config 29 00:01:21,456 --> 00:01:24,201 ซึ่งเป็นสิ่งใหม่ในแบบฝึกหัดนี้ รวมถึง 30 00:01:24,201 --> 00:01:26,130 DatabaseSessionService และ 31 00:01:26,130 --> 00:01:29,542 InMemorySessionService เราจะคุยกันเรื่อง event 32 00:01:29,542 --> 00:01:32,658 compaction config ตอนอ่านโค้ดกันแน่นอนครับ 33 00:01:32,658 --> 00:01:35,922 เยี่ยมเลย ตอนนี้เรา import คอมโพเนนต์ของ ADK 34 00:01:35,922 --> 00:01:38,741 สำเร็จแล้ว ส่วนถัดไปคือการนิยาม helper 35 00:01:38,741 --> 00:01:41,857 function ครับ helper function ตรงนี้จัดการ 36 00:01:41,857 --> 00:01:45,121 session บทสนทนาแบบครบวงจร ทั้งการสร้างและดึง 37 00:01:45,121 --> 00:01:46,902 session การประมวลผลคำถาม 38 00:01:46,902 --> 00:01:48,534 และการสตรีมคำตอบกลับมา 39 00:01:48,534 --> 00:01:51,947 รองรับทั้งคำถามเดี่ยวและหลายคำถามเรียงกัน เช่น 40 00:01:51,947 --> 00:01:55,137 ถามว่าเมืองหลวงของฝรั่งเศสคืออะไรใน session 41 00:01:55,137 --> 00:01:56,323 เรื่องภูมิศาสตร์ 42 00:01:56,323 --> 00:01:58,846 หรือทักว่าสวัสดีแล้วถามว่าชื่ออะไร 43 00:01:58,846 --> 00:02:01,665 แล้วค่อยแนะนำตัวใน session แนวนั้นครับ 44 00:02:01,665 --> 00:02:03,445 ขอรันก่อน เสร็จไวมากครับ 45 00:02:03,445 --> 00:02:06,487 แล้วก็รันส่วนตั้งค่าจนจบ นั่นแปลว่า agent 46 00:02:06,487 --> 00:02:09,751 ที่ไม่มีการจัดการบริบทที่เหมาะสมจะตอบสนองกับ 47 00:02:09,751 --> 00:02:12,644 prompt ปัจจุบันโดยไม่สนประวัติที่ผ่านมา 48 00:02:12,644 --> 00:02:15,686 ถือว่าเตรียมความพร้อมเสร็จสมบูรณ์แล้วครับ 49 00:02:15,686 --> 00:02:18,505 ไปกันต่อที่ส่วนจัดการ session ครับ LLM 50 00:02:18,505 --> 00:02:21,695 ทุกตัวนั้น stateless (ไร้สถานะ) โดยธรรมชาติ 51 00:02:21,695 --> 00:02:26,443 หมายความว่าความรับรู้ของมันจำกัดอยู่แค่ข้อมูลที่คุณให้ในการเรียก 52 00:02:26,443 --> 00:02:29,114 API ครั้งเดียว นั่นหมายความว่า agent 53 00:02:29,114 --> 00:02:32,526 ที่ไม่มีการจัดการบริบทที่ดีจะตอบสนองกับ prompt 54 00:02:32,526 --> 00:02:35,568 ปัจจุบันโดยไม่คำนึงถึงประวัติที่ผ่านมาเลย 55 00:02:35,568 --> 00:02:38,684 ไม่ว่าคุณจะเคยคุยอะไรกับมันไว้ก่อนหน้าครับ 56 00:02:38,684 --> 00:02:40,167 ทำไมนี่ถึงเป็นปัญหา? 57 00:02:40,167 --> 00:02:46,844 เพราะในโลกจริง ลองนึกภาพคุณกำลังคุยกับใครสักคน พยายามมีบทสนทนาที่มีความหมายด้วยกัน แต่คน ๆ 58 00:02:46,844 --> 00:02:47,957 นั้นลืมทุกอย่าง 59 00:02:47,957 --> 00:02:51,444 ลืมทุกประโยคที่คุณเพิ่งพูดไปก่อนหน้านี้ตลอดเวลา 60 00:02:51,444 --> 00:02:54,782 นี่คือความท้าทายที่เราเผชิญกับ LLM ดิบ ๆ ครับ 61 00:02:54,782 --> 00:02:57,824 ด้วยเหตุนี้เราจึงใช้ session และ memory — 62 00:02:57,824 --> 00:03:01,162 session สำหรับจัดการความจำระยะสั้น และ memory 63 00:03:01,162 --> 00:03:03,758 สำหรับความจำระยะยาวครับ ใน notebook 64 00:03:03,758 --> 00:03:06,132 นี้เราจะโฟกัสเรื่อง session ก่อน 65 00:03:06,132 --> 00:03:09,397 แต่ก่อนหน้านั้น มาทำความเข้าใจกันว่า session 66 00:03:09,397 --> 00:03:12,364 คืออะไร session คือภาชนะบรรจุบทสนทนาครับ 67 00:03:12,364 --> 00:03:15,999 มันห่อหุ้มประวัติบทสนทนาไว้อย่างเรียงตามลำดับเวลา 68 00:03:15,999 --> 00:03:17,854 และบันทึกการเรียกใช้ tool 69 00:03:17,854 --> 00:03:21,341 และการตอบกลับทั้งหมดของบทสนทนาต่อเนื่องหนึ่งรอบ 70 00:03:21,341 --> 00:03:24,679 session ผูกกับ user หนึ่งคนกับ agent หนึ่งตัว 71 00:03:24,679 --> 00:03:28,091 ไม่ถูกแบ่งปันกับ user คนอื่น ๆ เช่นกัน ประวัติ 72 00:03:28,091 --> 00:03:31,356 session ของ agent หนึ่งตัวก็ไม่ถูกแบ่งปันให้ 73 00:03:31,356 --> 00:03:34,397 agent ตัวอื่นด้วยครับ ใน ADK นั้น session 74 00:03:34,397 --> 00:03:37,513 ประกอบด้วยสองส่วน คือ event และ state ครับ 75 00:03:37,513 --> 00:03:40,035 มาดูกันว่า event กับ state คืออะไร 76 00:03:40,035 --> 00:03:43,374 ตัวอย่างเช่น คุณมี input ของผู้ใช้ มีคำตอบของ 77 00:03:43,374 --> 00:03:46,490 agent มีการเรียก tool และมีผลลัพธ์ของ tool 78 00:03:46,490 --> 00:03:49,457 ทั้งหมดนี้ล้วนเป็นตัวอย่างของ event ครับ 79 00:03:49,457 --> 00:03:52,573 เรียกแต่ละอย่างว่า event ได้เลย แล้ว state 80 00:03:52,573 --> 00:03:55,763 คืออะไร? session state คือกระดาษทดของ agent 81 00:03:55,763 --> 00:03:56,860 (scratchpad) 82 00:03:56,860 --> 00:04:01,757 ที่มันใช้เก็บและอัปเดตรายละเอียดแบบไดนามิกที่ต้องใช้ระหว่างบทสนทนา 83 00:04:01,757 --> 00:04:04,724 คุณอาจนึกภาพเป็นที่เก็บคู่ key-value แบบ 84 00:04:04,724 --> 00:04:06,875 global ที่ sub-agent และ tool 85 00:04:06,875 --> 00:04:10,288 ทุกตัวเข้าถึงได้ครับ มันทำงานแบบนี้ ใน session 86 00:04:10,288 --> 00:04:13,552 คุณมีสององค์ประกอบคือ event กับ state นี่คือ 87 00:04:13,552 --> 00:04:16,000 user นี่คือแอปพลิเคชัน agent ครับ 88 00:04:16,000 --> 00:04:19,413 คำถามถัดไปคือเราจัดการ session เหล่านี้อย่างไร 89 00:04:19,413 --> 00:04:22,603 แอปพลิเคชันแบบ agent หนึ่งตัวมีได้หลาย user 90 00:04:22,603 --> 00:04:25,051 และ user แต่ละคนอาจมีหลาย session 91 00:04:25,051 --> 00:04:28,167 กับแอปพลิเคชันครับ เพื่อจัดการ session และ 92 00:04:28,167 --> 00:04:31,208 event เหล่านี้ ADK มี session service และ 93 00:04:31,208 --> 00:04:34,028 runner ให้ครับ session service ทำอะไร? 94 00:04:34,028 --> 00:04:37,069 มันคือชั้นการจัดเก็บ (storage layer) ครับ 95 00:04:37,069 --> 00:04:40,333 ดูแลการสร้าง การเก็บ และการดึงข้อมูล session 96 00:04:40,333 --> 00:04:42,040 และยังมี implementation 97 00:04:42,040 --> 00:04:44,933 หลายแบบสำหรับความต้องการที่ต่างกัน เช่น 98 00:04:44,933 --> 00:04:48,123 แบบเก็บในหน่วยความจำ แบบฐานข้อมูล แบบคลาวด์ 99 00:04:48,123 --> 00:04:49,236 ฯลฯ ส่วน runner 100 00:04:49,236 --> 00:04:52,055 อย่างที่ทุกคนรู้กันคือชั้นควบคุมจังหวะ 101 00:04:52,055 --> 00:04:53,984 (orchestration layer) ครับ 102 00:04:53,984 --> 00:04:57,248 มันดูแลการไหลของข้อมูลระหว่าง user กับ agent 103 00:04:57,248 --> 00:05:00,660 บำรุงรักษาประวัติบทสนทนาโดยอัตโนมัติ และจัดการ 104 00:05:00,660 --> 00:05:04,073 context engineering อยู่เบื้องหลังครับ พูดง่าย 105 00:05:04,073 --> 00:05:05,170 ๆ 106 00:05:05,170 --> 00:05:10,141 ก็คือ session เปรียบเหมือนสมุดบันทึก event เปรียบเหมือนรายการต่าง ๆ 107 00:05:10,141 --> 00:05:12,144 ในหนึ่งหน้า session service 108 00:05:12,144 --> 00:05:15,334 เปรียบเหมือนตู้เก็บแฟ้มที่เก็บสมุดเหล่านั้น 109 00:05:15,334 --> 00:05:16,432 และ runner 110 00:05:16,432 --> 00:05:19,622 เปรียบเหมือนผู้ช่วยที่บริหารการสนทนาครับ มา 111 00:05:19,622 --> 00:05:22,886 implement stateful agent ตัวแรกของเรากันเถอะ 112 00:05:22,886 --> 00:05:26,150 ขอรันโค้ดก่อนแล้วค่อยไล่ดูนะครับ ตรงนี้เรามี 113 00:05:26,150 --> 00:05:29,414 app name, user ID และ session โมเดลที่ใช้คือ 114 00:05:29,414 --> 00:05:32,827 Gemini 2.5 Flash-Lite ครับ ขั้นแรกคือสร้าง LLM 115 00:05:32,827 --> 00:05:36,017 agent ซึ่งเราทำเป็นอยู่แล้ว โดย description 116 00:05:36,017 --> 00:05:38,984 ระบุว่าเป็น chatbot ที่เรากำลังสร้างครับ 117 00:05:38,984 --> 00:05:41,877 ขั้นที่สองคือการจัดการ session ซึ่งจะมี 118 00:05:41,877 --> 00:05:45,290 service หนึ่งตัว เรานิยาม session service เป็น 119 00:05:45,290 --> 00:05:48,257 InMemorySessionService ครับ แล้วเราสร้าง 120 00:05:48,257 --> 00:05:51,373 runner ซึ่งโดยพื้นฐานแล้วต้องมี agent, app 121 00:05:51,373 --> 00:05:54,711 name และ session service ครับ แล้วเราก็ print 122 00:05:54,711 --> 00:05:56,937 ทั้งหมดนี้ออกมา เสร็จเรียบร้อย 123 00:05:56,937 --> 00:06:00,275 ขั้นถัดไปคือทดสอบ stateful agent ของเรา โดยใน 124 00:06:00,275 --> 00:06:03,614 runner เราถามสองคำถาม คือ หนึ่ง สวัสดี ผมชื่อ 125 00:06:03,614 --> 00:06:06,730 Sam เมืองหลวงของสหรัฐอเมริกาคืออะไร และสอง 126 00:06:06,730 --> 00:06:10,068 สวัสดี ผมชื่ออะไรครับ พอเสร็จขั้นตอนนี้ agent 127 00:06:10,068 --> 00:06:11,477 ควรจะจำชื่อ Sam ได้ 128 00:06:11,477 --> 00:06:14,890 มาดูกันว่ามันเกิดขึ้นอย่างไรครับ โอเค คำถามของ 129 00:06:14,890 --> 00:06:16,819 user คือ สวัสดี ผมชื่อ Sam 130 00:06:16,819 --> 00:06:20,231 เมืองหลวงของสหรัฐอเมริกาคืออะไร chatbot ตอบว่า 131 00:06:20,231 --> 00:06:23,050 สวัสดี Sam เมืองหลวงของสหรัฐอเมริกาคือ 132 00:06:23,050 --> 00:06:26,092 Washington DC ครับ จากนั้น user ถามต่อว่า 133 00:06:26,092 --> 00:06:29,208 สวัสดี ผมชื่ออะไรครับ chatbot ตอบกลับมาว่า 134 00:06:29,208 --> 00:06:30,840 ชื่อของคุณคือ Sam ครับ 135 00:06:30,840 --> 00:06:33,140 จากตรงนี้เราเข้าใจได้ว่า runner 136 00:06:33,140 --> 00:06:35,810 บำรุงรักษาประวัติบทสนทนาให้อัตโนมัติ 137 00:06:35,810 --> 00:06:39,223 แต่มีข้อควรระวังอยู่อย่างคือ in-memory session 138 00:06:39,223 --> 00:06:41,374 service ตัวนี้เป็นแบบชั่วคราว 139 00:06:41,374 --> 00:06:44,713 อย่างที่ระบุไว้ตรงนี้ครับ มันเก็บบทสนทนาไว้ใน 140 00:06:44,713 --> 00:06:46,938 RAM ซึ่งเป็นการเก็บแบบชั่วคราว 141 00:06:46,938 --> 00:06:49,090 ดังนั้นพอแอปพลิเคชันหยุดทำงาน 142 00:06:49,090 --> 00:06:51,464 ประวัติบทสนทนาทั้งหมดก็หายไปครับ 143 00:06:51,464 --> 00:06:54,505 ทีนี้มาทดสอบความขี้ลืมของ agent ตัวนี้กัน 144 00:06:54,505 --> 00:06:57,324 เราจะพิสูจน์ว่า agent ลืมบทสนทนาจริง ๆ 145 00:06:57,324 --> 00:07:00,218 โดยรีสตาร์ท kernel ด้วยการรันอันนี้ครับ 146 00:07:00,218 --> 00:07:03,408 แล้วเราจะรันเซลล์ก่อนหน้าทั้งหมดใน notebook 147 00:07:03,408 --> 00:07:06,672 นี้ ยกเว้นเซลล์รัน session ข้อ 2.5 ครับ โอเค 148 00:07:06,672 --> 00:07:08,897 ตอนนี้เราทำเสร็จแล้ว มันตอบว่า 149 00:07:08,897 --> 00:07:11,049 ผมถามอะไรกับคุณไปก่อนหน้านี้? 150 00:07:11,049 --> 00:07:14,165 คุณถามผมเรื่องเมืองหลวงของสหรัฐอเมริกา และ 151 00:07:14,165 --> 00:07:16,390 ชื่อผมคืออะไร คุณชื่อ Sam ครับ 152 00:07:16,390 --> 00:07:19,877 ทีนี้ผมย้อนกลับไปรันเซลล์ก่อนหน้าทั้งหมดตามปกติ 153 00:07:19,877 --> 00:07:22,473 เสร็จแล้ว แล้วผมจะข้ามส่วนนี้ไปครับ 154 00:07:22,473 --> 00:07:23,883 แล้วมาตรงส่วนนี้เลย 155 00:07:23,883 --> 00:07:25,960 คุณเห็นว่าคำตอบเปลี่ยนไปแล้ว 156 00:07:25,960 --> 00:07:28,112 ผมถามอะไรกับคุณไปก่อนหน้านี้? 157 00:07:28,112 --> 00:07:33,527 ขอโทษด้วย ผมจำบทสนทนาที่ผ่านมาไม่ได้ ผมไม่มีความจำเกี่ยวกับการสนทนาก่อน ๆ 158 00:07:33,527 --> 00:07:35,308 ของเรา และ ชื่อผมคืออะไร 159 00:07:35,308 --> 00:07:38,201 ผมไม่มีสิทธิ์เข้าถึงข้อมูลส่วนตัวของคุณ 160 00:07:38,201 --> 00:07:41,168 รวมถึงชื่อด้วย ผมเป็นแค่ chatbot ข้อความ 161 00:07:41,168 --> 00:07:43,542 และไม่ได้เก็บข้อมูลของผู้ใช้ครับ 162 00:07:43,542 --> 00:07:45,174 เพราะเราข้ามส่วนนั้นไป 163 00:07:45,174 --> 00:07:48,290 เรารันแบบนี้แล้วกระโดดมาที่ runner session 164 00:07:48,290 --> 00:07:51,406 ถัดไปตรง ๆ มันจึงลืมสิ่งที่เราคุยกันไปครับ 165 00:07:51,406 --> 00:07:54,818 แปลว่าทุกครั้งที่คุณรีเซ็ตแอปพลิเคชัน รีสตาร์ท 166 00:07:54,818 --> 00:07:56,896 kernel ประวัติก็หายวับไปครับ 167 00:07:56,896 --> 00:08:00,234 นี่เป็นปัญหาใช่ไหมครับ เพราะถ้าข้อมูล session 168 00:08:00,234 --> 00:08:03,647 ไม่ถูกเก็บอย่างถาวร มันก็ใช้งานในโลกจริงไม่ได้ 169 00:08:03,647 --> 00:08:04,744 user 170 00:08:04,744 --> 00:08:08,973 ควรจะอ้างอิงเรื่องราวในอดีตและกลับมาต่อบทสนทนาเดิมได้ครับ 171 00:08:08,973 --> 00:08:11,718 ด้วยเหตุนี้เราจึงไปกันต่อที่ส่วนถัดไป 172 00:08:11,718 --> 00:08:14,166 ซึ่งก็คือ persistent session ด้วย 173 00:08:14,166 --> 00:08:17,133 DatabaseSessionService ครับ ถ้าจำได้ เรา 174 00:08:17,133 --> 00:08:19,507 import service ตัวนี้ไว้แล้วครับ 175 00:08:19,507 --> 00:08:22,920 ขั้นแรกคือเลือก session service ที่ใช่ครับ ADK 176 00:08:22,920 --> 00:08:25,665 มี implementation ของ session service 177 00:08:25,665 --> 00:08:28,558 หลายแบบสำหรับความต้องการที่ต่างกัน โอเค 178 00:08:28,558 --> 00:08:30,932 ตัวแรกคือ InMemorySessionService 179 00:08:30,932 --> 00:08:34,344 สำหรับพัฒนาและทดสอบ ซึ่งข้อมูลหายเมื่อรีสตาร์ท 180 00:08:34,344 --> 00:08:37,015 และนี่คือโค้ดที่เราใช้ไปก่อนหน้าครับ 181 00:08:37,015 --> 00:08:39,092 ตัวถัดไปคือ database session 182 00:08:39,092 --> 00:08:42,356 สำหรับแอปที่ดูแลเอง มันอยู่รอดจากการรีสตาร์ท 183 00:08:42,356 --> 00:08:45,769 หมายความว่าจะไม่เสียประวัติไปครับ ตัวที่สามคือ 184 00:08:45,769 --> 00:08:49,107 Agent Engine session สำหรับ production บน GCP 185 00:08:49,107 --> 00:08:52,297 เป็นแบบ fully managed ระดับ enterprise ครับ 186 00:08:52,297 --> 00:08:55,710 สำหรับตอนนี้เพราะเราอยู่ใน sandbox อยู่ใน demo 187 00:08:55,710 --> 00:08:58,603 เราจะใช้ DatabaseSessionService กันครับ 188 00:08:58,603 --> 00:09:00,829 มาอัปเกรดกันด้วย SQLite กันเลย 189 00:09:00,829 --> 00:09:03,054 ซึ่งให้ความถาวรแบบ persistence 190 00:09:03,054 --> 00:09:06,467 โดยไม่ต้องมีเซิร์ฟเวอร์ฐานข้อมูลแยกต่างหากครับ 191 00:09:06,467 --> 00:09:08,915 สำหรับ demo นี้เราจะสร้าง chatbot 192 00:09:08,915 --> 00:09:11,957 ที่สามารถรับมือบทสนทนากับ user ได้ ประมาณ 193 00:09:11,957 --> 00:09:15,221 ChatGPT หรือ Gemini ครับ ทีนี้เรานิยาม agent 194 00:09:15,221 --> 00:09:18,262 กัน มี name และมี description ระบุว่าเป็น 195 00:09:18,262 --> 00:09:20,265 chatbot ที่มีความจำถาวรครับ 196 00:09:20,265 --> 00:09:22,565 แล้วเราสลับมาใช้บริการฐานข้อมูล 197 00:09:22,565 --> 00:09:25,904 และจะสร้างฐานข้อมูล SQLite ขึ้นมาโดยอัตโนมัติ 198 00:09:25,904 --> 00:09:28,871 เรามี DB URL ซึ่งเป็นไฟล์ในเครื่อง และมี 199 00:09:28,871 --> 00:09:30,726 session service โดยเราแทน 200 00:09:30,726 --> 00:09:32,729 InMemorySessionService ด้วย 201 00:09:32,729 --> 00:09:35,919 DatabaseSessionService ครับ ทีนี้รัน runner 202 00:09:35,919 --> 00:09:38,738 ก็สำเร็จเรียบร้อย อัปเกรดเสร็จแล้วครับ 203 00:09:38,738 --> 00:09:40,592 ต่อไปเราจะพิสูจน์ความถาวร 204 00:09:40,592 --> 00:09:43,931 คือเราจะรันอันนี้ก่อน มันถามว่า สวัสดี ผมชื่อ 205 00:09:43,931 --> 00:09:46,527 Sam เมืองหลวงของสหรัฐอเมริกาคืออะไร 206 00:09:46,527 --> 00:09:49,940 เราถามคำถามเดิมและได้คำตอบเดิมครับ ตรงนี้เรามี 207 00:09:49,940 --> 00:09:53,204 session ID ซึ่งก็คือ 01 ครับ ทีนี้มาดูกันว่า 208 00:09:53,204 --> 00:09:56,246 session นี้แยกส่วน (isolated) จริงหรือไม่ 209 00:09:56,246 --> 00:09:59,584 นี่คืออีก session หนึ่ง ถามว่า ผมชื่ออะไรครับ 210 00:09:59,584 --> 00:10:02,774 มันตอบว่า ผมไม่มีสิทธิ์เข้าถึงข้อมูลส่วนตัว 211 00:10:02,774 --> 00:10:04,258 รวมถึงชื่อของคุณด้วย 212 00:10:04,258 --> 00:10:07,151 ดังนั้นผมบอกชื่อคุณไม่ได้ครับ นี่แปลว่า 213 00:10:07,151 --> 00:10:10,267 session เป็นเรื่องส่วนตัวระหว่าง agent กับ 214 00:10:10,267 --> 00:10:13,679 user ครับ ในสอง session ข้อมูลไม่ถูกแบ่งปันกัน 215 00:10:13,679 --> 00:10:16,943 คุณรัน session นี้ด้วย session ID ที่ต่างกัน 216 00:10:16,943 --> 00:10:19,688 อย่างที่เห็นคือ 02 ส่วนอันก่อนเป็น 01 217 00:10:19,688 --> 00:10:22,878 มันก็จะบอกว่าไม่รู้ชื่อคุณครับ ส่วนถัดไปคือ 218 00:10:22,878 --> 00:10:26,068 context compaction ครับ ในส่วนนี้เราเห็นว่า 219 00:10:26,068 --> 00:10:28,220 event ทั้งหมดถูกเก็บแบบเต็ม ๆ 220 00:10:28,220 --> 00:10:32,671 ในฐานข้อมูล session และเพิ่มปริมาณเร็วมากครับ สำหรับงานยาว ๆ 221 00:10:32,671 --> 00:10:34,748 ซับซ้อน ลิสต์นี้อาจใหญ่โตมาก 222 00:10:34,748 --> 00:10:38,383 นำไปสู่ประสิทธิภาพที่ช้าลงและค่าใช้จ่ายที่สูงขึ้น 223 00:10:38,383 --> 00:10:41,722 แต่ถ้าเราสรุปอดีตที่ผ่านมาแบบอัตโนมัติได้ล่ะ? 224 00:10:41,722 --> 00:10:44,466 มาใช้ฟีเจอร์ context compaction กันดู 225 00:10:44,466 --> 00:10:46,692 ว่ามันลดบริบทที่เก็บใน session 226 00:10:46,692 --> 00:10:49,437 โดยอัตโนมัติอย่างไรครับ สำหรับส่วนนี้ 227 00:10:49,437 --> 00:10:52,553 เราจะสร้าง app สำหรับ agent โดยใช้ chatbot 228 00:10:52,553 --> 00:10:55,223 ตัวเดิมที่ใช้ใน session ก่อนหน้าครับ 229 00:10:55,223 --> 00:10:58,265 ขั้นแรกคือสร้าง object ชื่อ app แล้วสร้าง 230 00:10:58,265 --> 00:11:01,455 config ใหม่สำหรับทำ context compaction ครับ 231 00:11:01,455 --> 00:11:04,348 config นี้ก็คือ event compaction config 232 00:11:04,348 --> 00:11:07,241 ซึ่งมีตัวแปรหลักสองตัว คือ interval กับ 233 00:11:07,241 --> 00:11:10,580 overlap size ครับ interval นั้นบอก runner ให้ 234 00:11:10,580 --> 00:11:13,399 compact ประวัติหลังจากผ่านไปกี่บทสนทนา 235 00:11:13,399 --> 00:11:16,292 เพื่อให้มันเล็กลงครับ ส่วน overlap size 236 00:11:16,292 --> 00:11:20,298 กำหนดจำนวนบทสนทนาก่อนหน้าที่จะเก็บไว้ให้ซ้อนทับกันครับ 237 00:11:20,298 --> 00:11:23,562 ทีนี้เราจะมอบ app นี้ให้ runner เรานิยาม app 238 00:11:23,562 --> 00:11:25,862 ตรงนี้ก่อน นี่คือ app ของ agent 239 00:11:25,862 --> 00:11:28,755 โดยมีส่วนใหม่ตรงนี้คือ event compaction 240 00:11:28,755 --> 00:11:31,649 ซึ่งเป็น config ประกอบด้วย interval กับ 241 00:11:31,649 --> 00:11:34,839 overlap ตามที่กล่าวไปครับ เรากำหนด interval 242 00:11:34,839 --> 00:11:38,251 เป็นสาม หมายความว่าให้ trigger การ compact ทุก 243 00:11:38,251 --> 00:11:41,441 ๆ สามการเรียกใช้ และ overlap size เป็นหนึ่ง 244 00:11:41,441 --> 00:11:46,486 หมายความว่าเก็บบทสนทนาเทิร์นก่อนหน้าไว้หนึ่งเทิร์นเพื่อเป็นบริบทครับ 245 00:11:46,486 --> 00:11:49,824 เรากำหนด DB URL กับ session service แล้วสร้าง 246 00:11:49,824 --> 00:11:52,495 runner ใหม่สำหรับ app ที่อัปเกรดแล้ว 247 00:11:52,495 --> 00:11:55,537 ตั้งชื่อว่า compacting runner และ app คือ 248 00:11:55,537 --> 00:11:58,875 compacting app โดย session service เท่ากับตัว 249 00:11:58,875 --> 00:12:02,287 session service เดิมครับ รันโค้ดนี้ คุณเห็นว่า 250 00:12:02,287 --> 00:12:05,700 app ได้รับการอัปเกรดด้วย event compaction แล้ว 251 00:12:05,700 --> 00:12:08,148 ได้คำตอบออกมา ได้ print statement 252 00:12:08,148 --> 00:12:10,225 ตามต้องการครับ ต่อไปรัน demo 253 00:12:10,225 --> 00:12:13,044 แล้วดูว่ามันทำงานอย่างไร มาคุยกันยาว ๆ 254 00:12:13,044 --> 00:12:15,567 โดยมีเทิร์นที่หนึ่ง สอง สาม และสี่ 255 00:12:15,567 --> 00:12:18,608 หมายความว่ายาวพอจะ trigger การ compaction 256 00:12:18,608 --> 00:12:21,576 ได้ครับ และเมื่อเรารันชุดโค้ดนี้ ผลลัพธ์ 257 00:12:21,576 --> 00:12:23,876 (output) ควรดูเหมือนบทสนทนาปกติ 258 00:12:23,876 --> 00:12:26,620 เพราะเราตั้งค่า app ไว้แล้ว กระบวนการ 259 00:12:26,620 --> 00:12:28,549 compaction จะรันแบบเงียบ ๆ 260 00:12:28,549 --> 00:12:31,665 ในเบื้องหลังหลังการเรียกใช้ครั้งที่สามครับ 261 00:12:31,665 --> 00:12:34,707 ขอรันเลย เสร็จแล้ว และคุณเห็น demo ของการ 262 00:12:34,707 --> 00:12:37,822 compaction ครับ มีบทสนทนาทั้งหมดอยู่ตรงนี้ 263 00:12:37,822 --> 00:12:40,790 คำถามแรกคือ มีข่าวล่าสุดอะไรเกี่ยวกับ AI 264 00:12:40,790 --> 00:12:43,164 ในวงการสุขภาพบ้าง มันก็ตอบมาครับ 265 00:12:43,164 --> 00:12:45,835 ถัดไปเป็นเรื่อง มีความก้าวหน้าใหม่ ๆ 266 00:12:45,835 --> 00:12:47,244 ในการค้นพบยาหรือไม่ 267 00:12:47,244 --> 00:12:49,247 มีพัฒนาการใหม่ในด้านนั้นไหม 268 00:12:49,247 --> 00:12:51,695 คุณเห็นว่ามันตอบมายาวและดีมากครับ 269 00:12:51,695 --> 00:12:54,959 แล้วก็มีอันถัดไป ถ้าดูต่อไปมันคุยกันเรื่อย ๆ 270 00:12:54,959 --> 00:12:57,630 และคุณมีบทสนทนาทั้งหมดอยู่ตรงนี้ครับ 271 00:12:57,630 --> 00:13:00,746 ทีนี้เรามาตรวจสอบการ compaction และประวัติ 272 00:13:00,746 --> 00:13:02,230 session กันครับ โอเค 273 00:13:02,230 --> 00:13:04,529 นั่นหมายความว่าบทสนทนานี้ดูปกติ 274 00:13:04,529 --> 00:13:06,978 แต่ประวัติเปลี่ยนไปแล้วเบื้องหลัง 275 00:13:06,978 --> 00:13:10,019 มาดูกันว่าเกิดอะไรขึ้น เราจะรันอันนี้ครับ 276 00:13:10,019 --> 00:13:13,209 เราสามารถตรวจสอบ event ใน session ของเราได้ 277 00:13:13,209 --> 00:13:16,028 และกระบวนการ compaction ไม่ได้ลบ event 278 00:13:16,028 --> 00:13:18,328 เก่าทิ้ง แต่มันแทนที่ด้วย event 279 00:13:18,328 --> 00:13:20,850 ใหม่ตัวเดียวที่บรรจุสรุปย่อไว้ครับ 280 00:13:20,850 --> 00:13:23,966 มาดูกันตรงนี้ มันบอกว่าพบ compaction event 281 00:13:23,966 --> 00:13:25,301 ที่มี author กำกับ 282 00:13:25,301 --> 00:13:27,527 และมันบอกว่าเกิดอะไรขึ้นตรงนี้ 283 00:13:27,527 --> 00:13:30,198 คุณสามารถไล่อ่านดูได้ครับ มันจะ save 284 00:13:30,198 --> 00:13:33,536 กับอันที่สอง และมันพูดถึง AI ตรงนี้ใช่ไหมครับ 285 00:13:33,536 --> 00:13:36,726 และคุณต้องแค่ไล่อ่านต่อ คุณจะพบว่ามีการระบุ 286 00:13:36,726 --> 00:13:38,952 author กับข้อมูลที่ถูก compact 287 00:13:38,952 --> 00:13:40,732 พร้อมสรุปย่อที่ได้มาครับ 288 00:13:40,732 --> 00:13:43,254 ทีนี้สิ่งที่คุณทำสำเร็จแล้วคืออะไร 289 00:13:43,254 --> 00:13:46,444 ขอสรุปกันหน่อยครับ คุณได้ทำการทำงานแบบเงียบ 290 00:13:46,444 --> 00:13:49,783 (silent operation) คือเรารันบทสนทนาแบบมาตรฐาน 291 00:13:49,783 --> 00:13:52,379 จากมุมภายนอกดูเหมือนไม่มีอะไรต่างไป 292 00:13:52,379 --> 00:13:55,421 เหมือนบทสนทนาปกติของเรา แต่เบื้องหลังนั้น 293 00:13:55,421 --> 00:13:58,611 เราตั้งค่า app ด้วย event compaction config 294 00:13:58,611 --> 00:14:00,020 แล้ว runner ของ ADK 295 00:14:00,020 --> 00:14:02,691 เฝ้าติดตามความยาวบทสนทนาโดยอัตโนมัติ 296 00:14:02,691 --> 00:14:04,472 พอครบเกณฑ์ มันก็ trigger 297 00:14:04,472 --> 00:14:07,291 กระบวนการสรุปย่อขึ้นมาในเบื้องหลังครับ 298 00:14:07,291 --> 00:14:10,555 หลังจากนั้นเราก็ตรวจสอบผลลัพธ์ เราไปดู event 299 00:14:10,555 --> 00:14:13,300 ของ session และเราพบว่าสรุปย่อที่ LLM 300 00:14:13,300 --> 00:14:15,970 สร้างขึ้นมา ตัวนี้แทนที่เทิร์นเก่า ๆ 301 00:14:15,970 --> 00:14:22,796 ที่ยืดยาวกว่าในบริบทที่ agent ใช้งานอยู่ครับ ส่วนถัดไปคือตัวเลือก context engineering อื่น ๆ 302 00:14:22,796 --> 00:14:25,318 ใน ADK ครับ อย่างแรกคือ compaction 303 00:14:25,318 --> 00:14:28,359 อย่างถัดไปคือ gzip ครับ custom compaction 304 00:14:28,359 --> 00:14:31,549 พื้นฐานแล้วมีไว้สำหรับ use case ขั้นสูงกว่า 305 00:14:31,549 --> 00:14:34,739 คุณสามารถกำหนดเองได้โดยนิยาม custom sliding 306 00:14:34,739 --> 00:14:38,004 window compactor แล้วส่งเข้าไปใน config ครับ 307 00:14:38,004 --> 00:14:41,416 สิ่งที่ได้คือคุณควบคุม prompt ที่ใช้สรุปย่อได้ 308 00:14:41,416 --> 00:14:42,514 หรือจะใช้ LLM 309 00:14:42,514 --> 00:14:45,184 เฉพาะทางตัวอื่นสำหรับงานนี้ก็ได้ครับ 310 00:14:45,184 --> 00:14:48,152 อ่านเพิ่มเติมได้ในเอกสารทางการตรงนี้ครับ 311 00:14:48,152 --> 00:14:51,268 อย่างถัดไปคือ context gzip หมายความว่า ADK 312 00:14:51,268 --> 00:14:54,161 ยังมีความสามารถนี้เพื่อช่วยลดขนาด token 313 00:14:54,161 --> 00:14:57,425 ของคำสั่งกำกับแบบคงที่ (static instructions) 314 00:14:57,425 --> 00:15:00,689 ที่ป้อนเข้าไปให้ LLM โดยการบีบอัด (compress) 315 00:15:00,689 --> 00:15:04,028 ข้อมูลคำขอครับ อ่านเพิ่มเติมได้ที่ลิงก์ตรงนี้ 316 00:15:04,028 --> 00:15:07,366 ผมจะใส่ลิงก์ทั้งหมดนี้ไว้ในคำอธิบายวิดีโอครับ 317 00:15:07,366 --> 00:15:10,037 ตรงนี้เราจะพูดถึง session state ครับ 318 00:15:10,037 --> 00:15:12,114 ว่าการจัดการสถานะของ session 319 00:15:12,114 --> 00:15:15,230 ถูกสร้างขึ้นอย่างไร ส่วนย่อยแรกคือการสร้าง 320 00:15:15,230 --> 00:15:18,420 custom tool สำหรับจัดการ session state ครับ 321 00:15:18,420 --> 00:15:21,684 เราจะจัดการ session state ด้วยมือผ่าน custom 322 00:15:21,684 --> 00:15:23,835 tool กัน ในตัวอย่างที่จะ demo 323 00:15:23,835 --> 00:15:27,100 เราจะจับคุณลักษณะที่ถ่ายโอนได้ เช่น username 324 00:15:27,100 --> 00:15:29,473 กับประเทศของ user แล้วสร้าง tool 325 00:15:29,473 --> 00:15:31,847 ไว้เก็บและดึงข้อมูลเหล่านั้นครับ 326 00:15:31,847 --> 00:15:34,221 ทำไมต้องใช้ตัวอย่างนี้? username 327 00:15:34,221 --> 00:15:40,898 เป็นตัวอย่างที่สมบูรณ์แบบของข้อมูลที่แนะนำเข้ามาครั้งเดียวแต่ถูกอ้างอิงหลายครั้งใช่ไหมครับ 328 00:15:40,898 --> 00:15:43,198 แล้วมันควรคงอยู่ตลอดทั้งบทสนทนา 329 00:15:43,198 --> 00:15:45,646 เพราะมันแทนคุณลักษณะเฉพาะของ user 330 00:15:45,646 --> 00:15:48,688 ที่เพิ่มความเป็นส่วนตัว (personalization) 331 00:15:48,688 --> 00:15:51,729 ให้ครับ สำหรับ demo นี้เราจะสร้างสอง tool 332 00:15:51,729 --> 00:15:54,771 ที่สามารถเก็บและดึง username กับประเทศจาก 333 00:15:54,771 --> 00:15:57,664 session state ได้ครับ ข้อสังเกตคือ tool 334 00:15:57,664 --> 00:16:00,632 ทุกตัวเข้าถึง tool context object ที่เรา 335 00:16:00,632 --> 00:16:03,970 import ไว้ตั้งแต่ต้นตอนสร้าง notebook นี้ครับ 336 00:16:03,970 --> 00:16:07,012 คุณไม่จำเป็นต้องสร้าง tool แยกสำหรับทุก ๆ 337 00:16:07,012 --> 00:16:09,015 ชิ้นข้อมูลที่ต้องการแบ่งปัน 338 00:16:09,015 --> 00:16:12,056 จัดการทุกอย่างไว้ตรงนี้ได้เลยครับ ดังนั้น 339 00:16:12,056 --> 00:16:15,469 scope ของ username คือระดับ temp, user และ app 340 00:16:15,469 --> 00:16:17,323 ซึ่งนี่คือแนวปฏิบัติที่ดี 341 00:16:17,323 --> 00:16:19,772 และนี่คือสิ่งที่เรากำลังทำตามครับ 342 00:16:19,772 --> 00:16:23,184 จากนั้นคุณจะนิยาม user info โดยมี tool context 343 00:16:23,184 --> 00:16:26,300 เป็น string และ country ก็เป็น string ครับ 344 00:16:26,300 --> 00:16:29,490 ในอาร์กิวเมนต์เราระบุว่า username ถูกเก็บใน 345 00:16:29,490 --> 00:16:32,606 session state และ country คือชื่อประเทศของ 346 00:16:32,606 --> 00:16:35,944 user ครับ เราเขียนลง session state ตรงนี้ด้วย 347 00:16:35,944 --> 00:16:39,282 tool context กับ username และ country แล้วคืน 348 00:16:39,282 --> 00:16:41,063 status เป็น success ครับ 349 00:16:41,063 --> 00:16:44,475 ขั้นถัดไปคือเข้าใจวิธีอ่านข้อมูลจาก state ครับ 350 00:16:44,475 --> 00:16:46,553 เรากำลังดึงข้อมูลออกมาตรงนี้ 351 00:16:46,553 --> 00:16:47,962 เราเซฟข้อมูลไว้แล้ว 352 00:16:47,962 --> 00:16:51,152 ตอนนี้เราดึงข้อมูลกลับมาครับ ตรงนี้คือ tool 353 00:16:51,152 --> 00:16:54,268 สำหรับดึง username กับ country จาก session 354 00:16:54,268 --> 00:16:57,680 เราจะดึงจาก session state ตรงนี้ พร้อม country 355 00:16:57,680 --> 00:17:01,019 ครับ ถ้าไม่มีข้อมูล มันจะบอกว่า country ไม่พบ 356 00:17:01,019 --> 00:17:04,060 หรือ username ไม่พบ มิฉะนั้นมันจะคืนสถานะ 357 00:17:04,060 --> 00:17:06,954 success พร้อม username กับ country ครับ 358 00:17:06,954 --> 00:17:09,031 มาสร้าง tool กัน รัน snippet 359 00:17:09,031 --> 00:17:12,147 เสร็จเรียบร้อยครับ ขั้นถัดไปคือสร้าง agent 360 00:17:12,147 --> 00:17:15,411 สำหรับ session tool ครับ เรามี app, user id, 361 00:17:15,411 --> 00:17:18,527 model โดย agent เป็น text chatbot อีกครั้ง 362 00:17:18,527 --> 00:17:21,939 description คือ tool สำหรับจัดการบริบทของ user 363 00:17:21,939 --> 00:17:24,313 เพื่อบันทึก username และ country 364 00:17:24,313 --> 00:17:27,206 เมื่อได้รับข้อมูล ให้ใช้ save_user_info 365 00:17:27,206 --> 00:17:29,951 เพื่อบันทึก และใช้ retrieve_user_info 366 00:17:29,951 --> 00:17:33,216 เพื่อดึงข้อมูลครับ ส่วน tool ก็เป็น save กับ 367 00:17:33,216 --> 00:17:36,406 retrieve ตามปกติ เราตั้งค่า session service 368 00:17:36,406 --> 00:17:39,744 กับ runner ครับ ตรงนี้เราจะรัน agent นี้พร้อม 369 00:17:39,744 --> 00:17:42,415 session state tool ที่ตั้งค่าไว้ครับ 370 00:17:42,415 --> 00:17:45,679 ทีนี้เรามาทดสอบ session state กัน prompt คือ 371 00:17:45,679 --> 00:17:48,869 สวัสดี วันนี้เป็นอย่างไรบ้าง ผมชื่ออะไรครับ 372 00:17:48,869 --> 00:17:51,762 แล้วมันบอกว่า ผมชื่อ Sam ผมมาจาก Poland 373 00:17:51,762 --> 00:17:53,765 แล้วถามซ้ำอีกว่า ผมชื่ออะไร 374 00:17:53,765 --> 00:17:56,510 ผมมาจากประเทศไหนครับ มันทักว่า สวัสดี 375 00:17:56,510 --> 00:17:59,626 วันนี้เป็นอย่างไรบ้าง ผมชื่ออะไร มันตอบว่า 376 00:17:59,626 --> 00:18:02,445 สวัสดีครับ ผมสบายดี ขอบคุณที่ถามนะครับ 377 00:18:02,445 --> 00:18:06,599 ผมจำชื่อคุณไม่ได้เพราะไม่มีสิทธิ์เข้าถึงบทสนทนาที่ผ่านมา 378 00:18:06,599 --> 00:18:10,012 ถ้าอยากให้ผมจำ กรุณาบอกชื่อกับประเทศของคุณครับ 379 00:18:10,012 --> 00:18:13,202 ทีนี้เราบอกว่า ผมชื่อ Sam และผมมาจาก Poland 380 00:18:13,202 --> 00:18:15,650 มันตอบว่า ผมบันทึกชื่อคุณเป็น Sam 381 00:18:15,650 --> 00:18:17,801 และประเทศเป็น Poland แล้วครับ 382 00:18:17,801 --> 00:18:19,508 มีอะไรให้ช่วยอีกไหมครับ 383 00:18:19,508 --> 00:18:22,698 แล้วเราจึงถามคำถามเดิมซ้ำ มันตอบว่า คุณชื่อ 384 00:18:22,698 --> 00:18:25,813 Sam และคุณมาจาก Poland ครับ ทีนี้มาตรวจสอบ 385 00:18:25,813 --> 00:18:29,078 session state กันครับ ใน session state คุณมี 386 00:18:29,078 --> 00:18:32,119 username เป็น Sam และ country เป็น Poland 387 00:18:32,119 --> 00:18:34,567 นี่คือวิธีที่มันถูกป้อนเข้าไปครับ 388 00:18:34,567 --> 00:18:37,757 ต่อไปมาแยกส่วนกัน ตอนนี้เราแยกมันออก นี่คือ 389 00:18:37,757 --> 00:18:41,021 session ใหม่ที่แยกไว้ แล้วเราถามคำถามเดิมซ้ำ 390 00:18:41,021 --> 00:18:44,211 และมันบอกว่า ผมไม่แน่ใจว่าคุณชื่ออะไรนะครับ 391 00:18:44,211 --> 00:18:46,511 ช่วยบอกได้ไหม หมายความว่า agent 392 00:18:46,511 --> 00:18:49,701 จะไม่รู้จักชื่อนี้ เพราะนี่เป็นคนละ session 393 00:18:49,701 --> 00:18:53,114 กันครับ ทีนี้มาทำการแบ่งปัน state ข้าม session 394 00:18:53,114 --> 00:18:56,007 (cross-session state sharing) กัน ผมรัน 395 00:18:56,007 --> 00:18:58,900 snippet นี้ ซึ่งพื้นฐานแล้วบอกว่า คุณมี 396 00:18:58,900 --> 00:19:02,164 session ใหม่ และ session state คือ state ของ 397 00:19:02,164 --> 00:19:04,538 session ก่อนหน้า ดังนั้นใน state 398 00:19:04,538 --> 00:19:06,764 ใหม่จึงมีชื่อกับประเทศอยู่ครับ 399 00:19:06,764 --> 00:19:10,547 สุดท้ายเราจะล้างฐานข้อมูลที่มีอยู่แล้วเริ่มใหม่ครับ 400 00:19:10,547 --> 00:19:12,476 ตอนนี้ฐานข้อมูลถูกล้างแล้ว 401 00:19:12,476 --> 00:19:15,889 ล้างไฟล์ข้อมูลเก่าเรียบร้อยครับ ดังนั้นโดยสรุป 402 00:19:15,889 --> 00:19:18,856 ตอนนี้เรารู้แล้วว่า context engineering, 403 00:19:18,856 --> 00:19:22,269 session และ event, การเก็บถาวร, session state, 404 00:19:22,269 --> 00:19:25,607 การจัดการ state ด้วยตนเอง และระดับ production 405 00:19:25,607 --> 00:19:28,723 คืออะไรครับ ไปกันต่อที่วิดีโอถัดไปซึ่งเป็น 406 00:19:28,723 --> 00:19:31,468 Part 2 ของ Day 3 ที่เราจะรัน code lab 407 00:19:31,468 --> 00:19:34,584 เรื่องระบบความจำระยะยาว (long-term memory) 408 00:19:34,584 --> 00:19:35,919 กันครับ ขอบคุณครับ