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

Day 3A: Agent Memory Explained — Sessions, Long-Term Context & Recall

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

สรุปย่อ

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

- **ช่อง:** 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 ครับ
02

คำแปลเต็ม

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

สวัสดีอีกครั้งครับ ยินดีต้อนรับสู่ 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) กันครับ ขอบคุณครับ

03

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

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

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

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

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

ศัพท์คำแปล / คำอธิบาย
sessionเซสชัน — ภาชนะบรรจุบทสนทนาของ user หนึ่งคนกับ agent หนึ่งตัว เรียงตามลำดับเวลา
stateful agentagent ที่มีสถานะ สามารถจำบทสนทนาก่อนหน้าและใช้ต่อได้
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 เพิ่มความเร็ว
EventCompactionConfigconfig ของการ 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 อ้างถึง
05

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

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

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
thai-subtitles.srt
SubRip — ใช้กับเครื่องเล่นวิดีโอส่วนใหญ่
↓ ดาวน์โหลด
thai-subtitles.vtt
WebVTT — ใช้กับเว็บ / YouTube
↓ ดาวน์โหลด
เปิดดูซับไตเติ้ลทั้งหมด (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
กันครับ ขอบคุณครับ