แอปอัจฉริยะ

ปลดล็อกความสามารถด้าน Agentic AI ในแอปด้วย GenAI ของ ML Kit

ลำโพง

เกี่ยวกับ

เกี่ยวกับการเสวนา

ยกระดับฟีเจอร์ AI ของ Android ไปอีกขั้นด้วยการสร้าง Agent ในอุปกรณ์ที่ทำงานโดยอัตโนมัติ ในเซสชันนี้ เราจะเจาะลึก Agentic API ใหม่ของ ML Kit ซึ่งสร้างขึ้นโดยตรงบน API การเข้าถึงโมเดลโดยตรงที่คุณรู้จักอยู่แล้ว

เมื่อใช้แอป Jetpacker แบบโอเพนซอร์ส คุณจะได้เรียนรู้วิธีเชื่อมโยงการดำเนินการหลายขั้นตอนในข้อมูลผลิตภัณฑ์ในพื้นที่ จัดการเซสชันแบบการสนทนาไปมาที่มีสถานะพร้อมบริบทที่แยกต่างหาก และเชื่อมต่อ Android MCP เพื่อขับเคลื่อนทั้งผู้ช่วยในแอปและแพลตฟอร์มระดับระบบ Android ด้วยฐานของโค้ดเดียว

สรุป

  • Gemini Nano 4 รองรับการเรียกใช้ฟังก์ชันในตัว ซึ่งช่วยให้โมเดลในอุปกรณ์แปลภาษาธรรมชาติเป็นการดำเนินการและขับเคลื่อน API ในเครื่องได้
  • ไลบรารี Jetpack AppFunctions กำหนดเครื่องมือผ่านคำอธิบาย KDoc ภาษาธรรมชาติ การเปลี่ยนระดับการเข้าถึงอาจแสดงฟังก์ชันเหล่านี้ต่อตัวแทนระบบ เช่น Gemini
  • ไลบรารี Agentic Flow ใหม่ (อัลฟ่า) ใน ML Kit GenAI ช่วยให้การสร้าง Agent ง่ายขึ้นด้วยการจัดการลูปการดำเนินการและการเรียกใช้เครื่องมือ
  • AgentRunner ประสานงานการดำเนินการของเครื่องมือและจัดเตรียมเมธอดเซสชันสำหรับการสนทนาแบบหลายเทิร์นโดยไม่สูญเสียบริบท
  • การเข้าถึงโดยตรงผ่าน Prompt API ยังคงพร้อมใช้งานสำหรับการควบคุมประวัติแบบง่ายหรือการตั้งค่าที่ซับซ้อน เช่น ระบบ Multi-Agent และระบบไฮบริด

ทักษะ

แหล่งข้อมูล

Gemini Notebook

หัวข้อ

  • ml-kit
  • on-device-ai
  • gemini-nano
  • ai-agents
  • การเรียกใช้ฟังก์ชัน
  • app-functions
  • aicore

ข้อความถอดเสียง

ข้อมูลเบื้องต้นเกี่ยวกับ AI ในอุปกรณ์และ Gemini Nano

  • ฉันชื่อ Jolanda Verhoef

  • ผมเป็นวิศวกรฝ่ายนักพัฒนาสัมพันธ์ที่ Android และวันนี้ผมอยู่กับ Victoria Lestari ซึ่งเป็นส่วนหนึ่งของทีมวิศวกร AICore

  • สวัสดีทุกคน

  • ขอบคุณที่มาร่วมเซสชันของเราในเช้าวันนี้

  • ทีมของเราจึงได้ร่วมมือกันอย่างใกล้ชิดมาเป็นเวลาหลายปีเพื่อนำโมเดล AI ที่ล้ำสมัยออกจากระบบคลาวด์และเรียกใช้โมเดลเหล่านั้นบนอุปกรณ์ Android โดยตรง

  • และเราได้จัดกลุ่มความพยายามเหล่านี้ไว้ในชุด GenAI ของ ML Kit

  • ในด้านหนึ่งของชุด GenAI ของ ML Kit นี้ เรามี API ระดับสูงสำหรับโซลูชันแบบเบ็ดเสร็จ เช่น การสรุปข้อความ คำอธิบายรูปภาพ และการจดจำเสียงพูด

  • ในทางกลับกัน เรายังเปิดให้เข้าถึงโมเดลในอุปกรณ์อย่าง Gemini Nano ได้โดยตรงด้วย

  • และวันนี้เราจะดำเนินการเพิ่มเติมอีกขั้น

  • ดังนั้น ด้วยการเข้าถึงโดยตรงนี้ เราจะอนุญาตให้คุณสร้าง Agent ในแอป ซึ่งเป็น Agent ที่สามารถให้เหตุผลในหลายๆ เทิร์น สามารถบันทึกบริบทการสนทนา และดำเนินการในแอปของคุณได้

  • แล้วเราหมายความว่าอย่างไรกันแน่ Victoria

  • ใช่

  • จนถึงตอนนี้ เมื่อพูดถึงกรณีการใช้งานในอุปกรณ์ เราจะนึกถึงงานที่สั้นกว่า ง่ายกว่า และแยกเป็นอิสระ เช่น การตรวจจับสแกมที่อาจเกิดขึ้น หรือการสรุปการโทร

  • และเมื่อเราต้องการสร้างสิ่งที่ซับซ้อนมากขึ้น เช่น การถามคำถามเกี่ยวกับแคตตาล็อกรูปภาพ หรือการถาม Google Maps เราจะอ้างอิงโมเดลระบบคลาวด์ขนาดใหญ่โดยอัตโนมัติ

  • ดังนั้น โมเดลระบบคลาวด์จึงมีข้อได้เปรียบด้านการประมวลผลที่มหาศาลและข้อมูลแบบเรียลไทม์ แต่ AI ในอุปกรณ์ก็เปิดโอกาสใหม่ๆ สำหรับเวลาในการตอบสนองต่ำและการประมวลผลข้อมูลในเครื่องแบบเรียลไทม์

  • Gemini Nano ได้ขยายขอบเขตสิ่งที่คุณทำได้ในอุปกรณ์แล้ว

  • ตัวอย่างหนึ่งคือแอป Pixel จำด้วยภาพ ซึ่งช่วยให้คุณค้นหาภาพหน้าจอในเครื่องและถามคำถามเกี่ยวกับภาพหน้าจอเหล่านั้นได้

  • ดังนั้นเมื่อเราเรียกใช้ Agent ในอุปกรณ์ นั่นหมายความว่าเราต้องก้าวกระโดดครั้งใหญ่ในด้านความสามารถของโมเดล และนั่นคือเหตุผลที่เราสร้าง Gemini Nano 4 ที่สามารถให้เหตุผลในขั้นตอนต่างๆ และดำเนินการจริงได้

  • แต่ความสามารถใหม่นี้จะมีความสำคัญก็ต่อเมื่อนักพัฒนาแอปมีอุปกรณ์จริงที่ต้องการกำหนดเป้าหมาย

  • ข่าวดีก็คือ Gemini Nano 4 พร้อมใช้งานแล้วในอุปกรณ์เรือธงบางรุ่น และคุณยังทดสอบในอุปกรณ์อื่นๆ อีกมากมายได้โดยใช้เวอร์ชันตัวอย่างสำหรับนักพัฒนาซอฟต์แวร์ AICore

  • และเรายังคาดว่าจะบรรลุเป้าหมาย 100 ล้านเครื่องในช่วงต้นปีหน้าด้วย

  • แล้วหมายความว่าอย่างไร

  • ซึ่งหมายความว่าคุณสามารถเริ่มสร้างและทดสอบประสบการณ์เหล่านี้ได้ตั้งแต่วันนี้ โดยรู้ว่าสิ่งที่คุณสร้างในวันนี้จะปรับขนาดให้เข้าถึงกลุ่มเป้าหมายจำนวนมากได้

  • เราได้พูดถึง Gemini Nano 4 และการเข้าถึงที่เป็นไปได้ไปแล้ว ตอนนี้เรามาพูดถึงความหมายที่แท้จริงของความสามารถด้าน Agentic AI กัน

  • ดังนั้น Agentic AI จึงสามารถวางแผนสำหรับงานที่ซับซ้อน แบ่งงานเหล่านี้ออกเป็นหลายขั้นตอน แล้วดำเนินการตามขั้นตอนเหล่านี้ในแอปของคุณ

  • สมมติว่าโมเดล Gemini Nano ในอุปกรณ์สามารถค้นหาฐานข้อมูลในเครื่อง ดึงข้อมูลจากแบ็กเอนด์ ให้เหตุผลและตอบคำถามที่ซับซ้อนของคุณ และยังทริกเกอร์เวิร์กโฟลว์ของแอปที่มาพร้อมเครื่องในอุปกรณ์ได้ด้วย

  • แต่ AI จะเปลี่ยนจากการวางแผนไปสู่การดำเนินการจริงได้อย่างไร

  • และนี่คือหัวข้อสำหรับวันนี้ ซึ่งก็คือการเรียกใช้ฟังก์ชัน

  • ซึ่งเป็นเครื่องมือหลักของความสามารถด้าน Agentic AI

  • โดยจะแปลภาษาธรรมชาติเป็นการดำเนินการของระบบ และขับเคลื่อนโมเดลจากการให้เหตุผลแบบพาสซีฟไปสู่การดำเนินการแบบแอ็กทีฟด้วยการเลือกและขับเคลื่อน API ในเครื่องที่จำเป็นต่อการแก้ไขงานที่ซับซ้อน

  • นอกจากนี้ เรายังยินดีที่จะประกาศว่า Gemini Nano 4 รองรับการเรียกใช้ฟังก์ชันในตัว

  • ปัจจุบันฟีเจอร์นี้พร้อมใช้งานในเวอร์ชันตัวอย่างสำหรับนักพัฒนาซอฟต์แวร์ AICore และจะเปิดตัวในอุปกรณ์ทั้งหมดที่รองรับ Nano 4 ในเร็วๆ นี้

  • ดังนั้นจึงเป็นเรื่องดีที่เรามีความสามารถประเภทนี้ในโมเดล แต่ความสามารถในโมเดลยังไม่ได้ให้ API แก่คุณในฐานะนักพัฒนาซอฟต์แวร์

  • ดังนั้นเราจึงพยายามเพิ่ม API ที่เหมาะสมลงในชุด GenAI ของ ML Kit เพื่อให้แน่ใจว่าการสร้าง Agent จะเป็นเรื่องง่าย

  • ดังนั้น สิ่งที่เราเริ่มต้นด้วยคือ API การเข้าถึงโดยตรงที่เรามีอยู่แล้ว และเราได้เพิ่มประเภทฟังก์ชันและประเภทประวัติการสนทนาลงไป

  • และด้วยความสามารถใหม่ 2 รายการที่เพิ่มเข้ามาใน Prompt API ของ ML Kit ตอนนี้คุณจะสร้างความสามารถด้าน Agentic AI ระดับต่ำนี้ลงในแอปได้แล้ว

  • อย่างไรก็ตาม มีสิ่งต่างๆ มากมายที่คุณต้องทำด้วยตนเองในจุดนั้น

  • ดังนั้น คุณจึงต้องเรียกใช้ฟังก์ชันด้วยตนเอง บันทึกประวัติการโทร และวนซ้ำการโทรหลายครั้งด้วย

  • ดังนั้น หากมีการเรียกหลายรายการที่คุณต้องทำ คุณจะต้องสร้างระบบวนซ้ำบางประเภทด้วยตนเอง และคุณต้องสร้าง Guardrail ของคุณเองสำหรับระบบ

  • ซึ่งก็ทำได้ แต่เราต้องการให้กระบวนการนี้รวดเร็วและง่ายดาย

  • ดังนั้น นอกเหนือจากไลบรารี Prompt API นี้แล้ว เรายังได้เพิ่มไลบรารี Agentic Flow ใหม่ด้วย

  • คุณสามารถค้นหาได้ด้วย com.mlkit.genai:agent-api และขณะนี้พร้อมใช้งานในเวอร์ชันอัลฟ่า

  • แต่ตอนนี้ API ใหม่นี้จะช่วยคุณทำงานหนักๆ ได้มากมาย

  • ดังนั้น แทนที่คุณจะต้องทำขั้นตอนต่างๆ ด้วยตนเองมากมาย คุณจะสร้าง Agent ได้ด้วยโค้ดเพียงไม่กี่บรรทัด

  • และนั่นคือสิ่งที่เราจะทำในวันนี้

การสาธิตเอเจนต์ Jetpacker และการเรียกใช้ฟังก์ชัน

  • เราจะมาดูวิธีการทำงานของ API เหล่านี้กับแอปตัวอย่าง Jetpacker กัน

  • คุณคงได้เห็น Jetpacker ในงาน Google I/O และในระหว่างการประชุมนี้แล้ว

  • แต่สิ่งที่คุณทำได้คือสแกนคิวอาร์โค้ดนี้ และหากสนใจ คุณสามารถดูโค้ด ชี้ Agent ไปที่โค้ด โต้ตอบกับโค้ด และดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่เราใช้ฟีเจอร์ทั้งหมดนี้ได้

  • Jetpacker เป็นผู้ช่วยด้านการเดินทางสมัยใหม่ที่เราสร้างขึ้นเพื่อแสดงฟีเจอร์อัจฉริยะต่างๆ มากมาย

  • ดังนั้นจึงมีการใช้โมเดลในอุปกรณ์และโมเดลระบบคลาวด์สำหรับ Prompt API ทุกประเภทอยู่แล้ว แต่สิ่งที่เราจะสร้างขึ้นในตอนนี้คือการเพิ่ม Agent เข้าไป

  • และสำหรับ Agent นี้ ซึ่งก็คือ Jetpacker สิ่งที่เรากำลังทำคือการใช้ API ของ Agent GenAI ของ ML Kit ใหม่ที่เราเพิ่งพูดถึง และ API นั้นจะใช้ระบบ AI Core พื้นฐานที่ทำงานบนอุปกรณ์ Android เพื่อพูดคุยกับโมเดล Gemini Nano

  • ตอนนี้เราพูดได้มากเท่าที่ต้องการ และเราจะพูดคุยกันค่อนข้างมาก แต่แน่นอนว่าเราต้องการแสดงให้เห็นถึงสิ่งนี้

  • เรามาเปลี่ยนไปใช้โทรศัพท์และสาธิตให้ดูจริงๆ กันดีกว่า

  • ฉันมี Pixel 11 Pro อยู่ที่นี่

  • และฉันคิดว่าเราสามารถเปลี่ยนไปใช้ Pro ได้ และคุณควรจะเห็นได้

  • เรียบร้อยแล้ว

  • และอย่างที่คุณเห็น เรามีแอป Jetpacker ที่ทำงานอยู่ที่นี่

  • มีทริปที่กำลังจะมาถึงหลายทริป และฉันสามารถดูทริปปัจจุบันได้ ซึ่งในทริปปัจจุบัน คุณจะเห็นว่าตอนนี้เป็นวันที่ 29 ตุลาคม ดังนั้นเราจึงมีเวิร์กช็อป AI ในอุปกรณ์ การสาธิต และพรุ่งนี้ฉันมีทริปล่องเรือ Golden Gate Bay และมีกำหนดการเข้าชมพิพิธภัณฑ์ที่กำลังจะมาถึง

  • ทั้งหมดนี้เป็นเรื่องดี

  • ตอนนี้เราสามารถไปที่หน้าจอค่าใช้จ่ายซึ่งเราใช้ติดตามงบประมาณของการเดินทางนี้ได้

  • และอย่างที่คุณเห็นที่ด้านขวาล่าง มีประกายระยิบระยับเล็กๆ แบบใหม่ ซึ่งก็คือ Agent ในอุปกรณ์ที่เราสร้างขึ้น

  • มาเริ่มกันที่คำสั่งที่ค่อนข้างตรงไปตรงมากันก่อน

  • ฉันคิดว่าเราขอให้เพิ่มค่าใช้จ่ายที่เฉพาะเจาะจงได้

  • สมมติว่าเพิ่มค่าใช้จ่ายสำหรับแท็กซี่ไป Mountain View เป็นเงิน 45 ดอลลาร์

  • เริ่มเลยนะคะ

  • กำลังเตรียมค่าใช้จ่ายและเพิ่มค่าใช้จ่ายแล้ว

  • เยี่ยมมาก

  • โอเค การสาธิต 1 ได้ผล

  • เรากลับไปที่สไลด์และดูวิธีการทำงานจริงๆ กันดีกว่า

  • ก่อนอื่นเลย อย่างที่คุณสังเกตเห็น ฉันใช้เสียงของฉัน และเสียงนั้นก็ใช้โมเดล Gemini Nano โดยใช้ API การจดจำคำพูดเพื่อถอดเสียงสิ่งที่ฉันพูดเป็นข้อความ

  • จากนั้นข้อความนั้นก็ยอดเยี่ยม และเราสามารถส่งข้อความนั้นไปยังโมเดล Gemini Nano ได้

  • แต่จะทำอะไรได้บ้าง

  • เช่น ฉันบอกให้เพิ่มบางอย่างลงในค่าใช้จ่าย ฉันบอกข้อมูลเกี่ยวกับเงิน $45 แต่โมเดลไม่รู้ว่าจะต้องดำเนินการอย่างไรใช่ไหม

  • ตอนนี้มันจะบอกแค่ว่า "ฉันไม่รู้ว่าคุณกำลังพูดถึงอะไร"

  • ดังนั้น สิ่งที่เราต้องการทำคือเพิ่มข้อมูลเล็กน้อยลงในพรอมต์นั้น

  • เราสามารถเพิ่มเครื่องมือลงในพรอมต์ได้ ดังนั้นเราจึงเพิ่มเครื่องมือได้ทุกประเภท ในกรณีนี้ ฉันจะเพิ่มการรับงบประมาณการเดินทาง แผนการเดินทาง ฯลฯ และหากคุณส่งต่อข้อมูลนี้ไปยังโมเดล Nano ด้วย API ของเรา โมเดลจะเข้าใจโดยอัตโนมัติว่าแทนที่จะแสดงผลข้อความ ตอนนี้โมเดลสามารถแสดงผลการเรียกใช้ฟังก์ชันได้แล้ว

  • ดังนั้นในกรณีของเรา ระบบจะพูดว่า "แทนที่จะตอบกลับด้วยข้อความ ฉันจะเพิ่มค่าใช้จ่าย $45 สำหรับค่าแท็กซี่ไป Mountain View"

  • ดีมากเลย แต่ Victoria แล้วมันทำงานยังไงล่ะ

  • เราจะสร้างสิ่งนี้ได้อย่างไร

  • ใช่

  • แล้วเราจะกำหนดเครื่องมือในโค้ดได้อย่างไร

  • เราจึงจะอธิบายเรื่องนี้

  • ดังนั้น สำหรับ Android MCP เราจึงใช้ไลบรารี Jetpack AppFunctions เพื่อกำหนดและเผยแพร่เครื่องมือเหล่านี้

  • ดังนั้น ไลบรารี AppFunctions จึงสามารถแสดงบริการ ข้อมูล และการดำเนินการของแอปในรีจิสทรีของระบบปฏิบัติการ Android เพื่อให้เราสามารถใช้ฟังก์ชันเดียวกันนี้ซ้ำเพื่อประกาศและอธิบายเครื่องมือต่างๆ และใช้เครื่องมือเหล่านั้นในแอปของคุณได้

  • และยังช่วยให้ผู้ใช้ทำงานนี้ให้เสร็จสมบูรณ์ได้ผ่านตัวแทนและการโต้ตอบระดับระบบ

  • เราจะยกตัวอย่างที่ชัดเจนในโค้ดให้คุณดู

    /**
     * Adds an expense to a trip.
     *
     * @param tripId Identifier of the trip (e.g., "paris-2026").
     * @param amount Expense amount in dollars.
     * @param description Short description of what the expense was for.
     */
    @AppFunctionDeclaration(isDescribedByKDoc = true)
    @AppFunctionAccessLevel(
      level = ACCESS_LEVEL_SELF,
      isCompatEnforcementEnabled = true,
    )
    suspend fun addExpense(tripId: String, amount: Double, description: String) {
      // Save expense to local DB / repository...
    }
  • คุณจะเห็นว่านี่เป็นตัวอย่างของAppFunctionDeclaration

  • คุณจะเห็นว่าความคิดเห็น KDoc ที่ด้านบนอธิบายสิ่งที่ฟังก์ชันทำและพารามิเตอร์ของฟังก์ชัน ทั้งหมดนี้เป็นภาษาธรรมชาติ

  • ดังนั้น แนวทางปฏิบัติแนะนำคือการให้คำอธิบายที่ชัดเจนที่สุดเท่าที่จะเป็นไปได้ เนื่องจากเราจะส่งคำอธิบายนี้ไปยังโมเดล

  • และคุณจะเห็นคำอธิบายประกอบ @AppFunctionAccessLevel อยู่ด้านล่าง

  • ดังนั้นจึงเป็นการกำหนดขอบเขตระดับการเข้าถึงของฟังก์ชัน

  • ในตัวอย่างนี้ เรามี ACCESS_LEVEL_SELF ซึ่งหมายความว่าฟังก์ชันที่เรากำลังเขียนนี้จะมองเห็นได้เฉพาะแอปของคุณ ซึ่งก็คือ Agent ของแอปเท่านั้น

  • จากนั้นเราจะกำหนดและใช้ฟังก์ชันระงับได้ และเราสามารถเขียนฟังก์ชันแอปได้มากเท่าที่ต้องการและจัดกลุ่มทั้งหมดไว้ในคลาสบริการเดียว

การสร้างและใช้งาน AI Agent Runner

  • ตกลง

  • ดังนั้น เราจึงมีเครื่องมือและสามารถเขียนเครื่องมือเหล่านั้นได้ ซึ่งเป็นเรื่องดี

  • แต่แน่นอนว่าในการสร้าง Agent เราต้องการมากกว่าแค่ AppFunctions เหล่านั้น

  • เรามีส่วนประกอบอีก 3 ประเภทที่สำคัญที่ควรทราบเมื่อคุณเขียน Agent

  • ก่อนอื่น เราจะกำหนด Agent

  • เราจึงกำหนดคำสั่งของระบบ กำหนดแนวทางป้องกัน การกำหนดค่า และวิธีทำงานร่วมกับระบบ

  • นอกจากนี้ เรายังมี AgentRunner ด้วย

  • AgentRunner คือสิ่งที่เรียกใช้ Agent ให้เรา

  • ดังนั้น วิธีนี้จะช่วยจัดการกลไกการวนซ้ำที่เราพูดถึง

  • และสุดท้าย เรามี AgentEvents ซึ่งเป็นวิธีที่ระบบหรือ API จะแจ้งให้เราทราบว่ากำลังทำอะไรอยู่

  • ดังนั้น โมเดลจะบอกเราเมื่อแสดงผลข้อความ เมื่อเรียกใช้เครื่องมือ ฯลฯ เพื่อให้เราแสดงผลข้อมูลนั้นใน UI ได้

  • ตกลง

  • มาดูโค้ดจริงกันเลย

  • ตกลง

  • ก่อนที่จะเขียนโค้ด เราต้องตรวจสอบก่อนว่าการเรียกใช้ฟังก์ชันพร้อมใช้งานในอุปกรณ์ของคุณหรือไม่ เนื่องจากการเรียกใช้ฟังก์ชันรองรับเฉพาะใน Gemini Nano 4 เท่านั้น

    val model = Generation.getClient()
    val features = model.getAvailableFeatures()
    if(FUNCTION_CALLING in features) {
      // Device is supported!
    } else {
      // Graceful fallback
    }
  • ก่อนอื่น เราต้องตรวจสอบโดยเรียกใช้โมเดล Generative getAvailableFeatures

  • และเราสามารถตรวจสอบได้ง่ายๆ ว่าการเรียกใช้ฟังก์ชันแสดงอยู่ในฟีเจอร์หรือไม่

  • และหากไม่รองรับ เราก็สามารถใช้การสำรองข้อมูลที่ราบรื่นได้

  • ตกลง

  • ตอนนี้เรามาดูโค้ดกันเลย

    if(FUNCTION_CALLING in features) {
      // 1. Build the agent configuration
      val agent = Agent.builder()
        .setName("Assistant")
        .setInstructions("You are a helpful on-device assistant.")
        .enableAppFunctions()
        .build()
    
      // 2. Initialize the runner
      val runner = AgentRunner(agent = agent)
    
      // 3. Create your prompt
      val prompt = content { text(userPrompt)}
    
      // 4. Start the agent
      runner.runSingleTurnAsync(prompt).textTokens().collect { responseMessage += it }
    
    }
  • เรารู้ว่าอุปกรณ์ของเรารองรับการเรียกใช้ฟังก์ชัน จากนั้นเราก็สามารถเขียนเพื่อเริ่มสร้าง Agent โดยใช้ Agent.builder ได้เลย

  • จากนั้นเราจะตั้งชื่อได้

  • เราสามารถsetInstructionsเพื่อกำหนดบริบทและตัวตนได้

  • และสุดท้าย เราจะเรียกใช้ enableAppFunctions

  • ดังนั้น ฟังก์ชันนี้จะเรียกใช้การวนซ้ำและค้นหา AppFunction ทั้งหมดที่เราเพิ่งระบุไว้ก่อนหน้านี้โดยอัตโนมัติ และเราเพิ่งสร้าง Agent ขึ้นมา

  • ถัดไป เราต้องเริ่มต้น AgentRunner

  • ดังนั้น Agent Runner นี้จะช่วยจัดการลูปการดำเนินการให้เรา

  • เราเรียกใช้โดยเรียกใช้ตัวสร้าง (Constructor) และส่ง Agent ไปยังตัวสร้าง

  • และสุดท้าย เราสามารถรวมคำสั่งเสียงที่ถอดเสียงไว้ในบล็อกเนื้อหาเพื่อสร้างพรอมต์ได้

  • จากนั้นเราจะโทรหาผู้จัดส่ง

  • เราเรียกฟังก์ชันนี้ว่า runSingleTurnAsync ในพรอมต์ ซึ่งจะเริ่ม Agent, เรียกใช้การอนุมาน และทำการอนุมาน AI ทั้งหมด

  • และหากเราต้องการสตรีมเฉพาะการตอบกลับข้อความสุดท้ายของโมเดลไปยัง UI เราก็เพียงแค่เรียกใช้ textTokens แล้วระบบจะรวบรวมคำตอบเมื่อได้รับ

  • ก็มีเท่านี้

  • โค้ดมีเพียงไม่กี่บรรทัดเท่านั้น เราไม่ได้พูดเล่น

  • ดังนั้น เพียงแค่โค้ดไม่กี่บรรทัดนี้ คุณก็จะมี Agent แล้ว

  • Agent ดังกล่าวจะทำสิ่งที่คุณต้องการ

  • มันจะให้-- ให้คำตอบเป็นข้อความแก่เรา

  • แต่แน่นอนว่าในการสาธิตครั้งแรกที่เราแสดงให้เห็น เราทำเพียงคำสั่งง่ายๆ เท่านั้นใช่ไหม

  • เช่น ผมป้อนคำสั่งหนึ่ง แล้วมันก็ดำเนินการอย่างหนึ่ง แล้วก็จบแค่นั้น แสดงว่ามันยังไม่ใช่ Agent เต็มรูปแบบใช่ไหม

  • มาเพิ่มสีสันกันสักหน่อย

  • ในส่วนถัดไป สิ่งที่ฉันต้องการทำคือการออกคำสั่ง แต่ฉันต้องการทำให้ Agent ทำงานยากขึ้นเล็กน้อย

สถาปัตยกรรมและการดำเนินการของ Agent แบบหลายขั้นตอน

  • งั้นเรากลับไปที่อุปกรณ์สาธิตของฉัน แล้วมาดูกันว่าเราจะทำให้มันยากขึ้นอีกหน่อยได้ไหม

  • เราอยู่ที่นั่นแล้ว

  • เยี่ยมมาก

  • อย่างที่บอกไป เรามีแผนจะไปพิพิธภัณฑ์กันพรุ่งนี้

  • และสำหรับการเข้าชมพิพิธภัณฑ์นั้น คุณจะเห็นว่าหากฉันเปิดขึ้นมา ราคาค่าเข้าชมจะอยู่ที่ $25

  • ตอนนี้เรามาดูกันว่า Agent ของเราจะสามารถค้นหาข้อมูลนั้นได้ด้วยตัวเองหรือไม่

  • ฉันจะกลับไปที่หน้าจอค่าใช้จ่ายและป้อนคำสั่งว่า "เพิ่มตั๋วพิพิธภัณฑ์ลงในค่าใช้จ่าย"

  • เริ่มเลยนะคะ

  • และตอนนี้ อย่างที่คุณเห็น มันกำลังตรวจสอบแผนการเดินทางของฉัน ค้นหาการเข้าชมพิพิธภัณฑ์ และเพิ่มลงในค่าใช้จ่ายของฉัน

  • ดังนั้น ในกรณีนี้จึงใช้ 3 ขั้นตอนเพื่อบรรลุเป้าหมายจริงๆ และยังให้คำตอบสุดท้ายเป็นข้อความด้วย

  • ขอย้ำอีกครั้งว่าทั้งหมดนี้ทำงานโดยอัตโนมัติ

  • คุณได้เห็นความเร็วที่แท้จริงของโมเดลนั้นในอุปกรณ์แล้ว

  • เรากลับไปที่สไลด์และดูวิธีการทำงานกัน

  • คุณจะเห็นว่าเราได้เปลี่ยนแปลงสิ่งต่างๆ เล็กน้อยเมื่อเทียบกับการสาธิตครั้งแรก

  • ในกรณีนี้ อย่างที่คุณเห็น ผมไม่ได้บอกโมเดลเกี่ยวกับพิพิธภัณฑ์ ไม่ได้บอกเกี่ยวกับค่าใช้จ่าย ดังนั้นเมื่อโมเดลได้รับพรอมต์และเครื่องมือ โมเดลจึงพยายามตัดสินใจว่าจะทำอะไรก่อน

  • ดังนั้นในกรณีนี้ โมเดลจะตัดสินใจว่าต้องทราบข้อมูลเพิ่มเติมเกี่ยวกับแผนการเดินทางปัจจุบันของฉัน จึงจะเรียกใช้ getItinerary

  • เมื่อได้รับคำตอบแล้ว ระบบก็จะรู้ว่ามีพิพิธภัณฑ์อยู่ในแผนการเดินทางนี้ ซึ่งน่าจะเป็นพิพิธภัณฑ์ที่ผู้ใช้พูดถึง

  • มาดูรายละเอียดของเหตุการณ์นั้นกัน

  • และระบบจะเห็นว่ามูลค่าคือ $25 จากข้อมูลเมตาอื่นๆ ทั้งหมดของเหตุการณ์นั้นๆ

  • ซึ่งโมเดลเข้าใจและย้อนกลับไป และเข้าใจว่าต้องเรียกใช้ฟังก์ชันเพิ่มค่าใช้จ่าย

  • ดังนั้น เราจะเห็นหลายขั้นตอนเกิดขึ้นทีละขั้นตอน

  • Victoria คุณช่วยอธิบายเรื่องนี้ให้ละเอียดยิ่งขึ้นได้ไหม

  • ใช่

  • สิ่งที่ Jolanda เพิ่งอธิบายไปเป็นตัวอย่างที่เป็นรูปธรรมของการสาธิตที่เธอเพิ่งแสดงให้เห็น แต่ในที่นี้ เราจะนำตัวอย่างดังกล่าวมาอธิบายในเชิงแนวคิดและเชิงนามธรรมมากขึ้น

  • ดังนั้น เหตุการณ์นี้จะเกิดขึ้นทุกครั้งที่เราเรียกใช้ฟังก์ชันแบบหลายขั้นตอน

  • ก่อนอื่นเลย นี่คือแอปของเรา และนั่นคือโมเดล Gemini Nano

  • และผู้ใช้จะเริ่มต้นด้วยการส่งคำค้นหาและรายการเครื่องมือที่ใช้ได้ไปยังโมเดล

  • ดังนั้น โมเดลจะใช้ข้อมูลนี้เพื่อตัดสินใจว่าจะส่งอะไรกลับมา

  • จากนั้น สมมติว่าในกรณีแรก ระบบจะส่งการเรียกใช้เครื่องมือกลับมา สมมติว่าเป็นการเรียกใช้เครื่องมือ 1

  • จากนั้นแอปจะดำเนินการเรียกใช้เครื่องมือนี้และได้รับการตอบกลับจากเครื่องมือ

  • จากนั้น สิ่งที่ระบบจะส่งกลับไปยังโมเดลคือประวัติการสนทนาทั้งหมด ซึ่งรวมถึงคำค้นหาต้นฉบับ รายการเครื่องมือ การเรียกใช้เครื่องมือครั้งแรก และการตอบกลับเครื่องมือครั้งแรกกลับไปยังโมเดล

  • จากนั้นโมเดลจะใช้ข้อมูลนี้อีกครั้งเพื่อตัดสินใจว่าจะทำอะไรต่อไป

  • ดังนั้น คุณจึงสามารถปรับไปมาได้จนกว่าจะได้ผลลัพธ์สุดท้าย

  • สมมติว่าโมเดลจะแสดงผลการเรียกใช้เครื่องมือที่ n จากนั้นแอปจะแสดงผลประวัติการสนทนาทั้งหมดอีกครั้ง

  • และเมื่อโมเดลพอใจแล้ว ก็จะรู้ว่าคำค้นหาได้รับการตอบสนองแล้ว และจะส่งคำตอบสุดท้ายกลับไปยังแอป จากนั้นแอปจะแสดงใน UI ว่าเสร็จแล้ว

  • ตกลง

  • ตอนนี้เราจะอธิบายวิธีใช้โค้ดนี้

    /**
    * Retrieves the itinerary events (ID, title, location) for a trip.
    * Note: Call getEventDetails(eventId) to retrieve ticket prices or notes.
    */
    @AppFunctionDeclaration(isDescribedByKDoc = true)
    @AppFunctionAccessLevel(level = ACCESS_LEVEL_SELF, isCompatEnforcementEnabled = true)
    suspend fun getItinerary(tripId: String): List<EventSummary> { ... }
    
    /**
    * Retrieves details for a specific event, including admission ticket prices.
    */
    @AppFunctionDeclaration(isDescribedByKDoc = true)
    @AppFunctionAccessLevel(level = ACCESS_LEVEL_SELF, isCompatEnforcementEnabled = true)
    suspend fun getEventDetails(eventId: String): EventDetails { ... }
  • คุณคงจำตัวอย่างจากโค้ดแบบขั้นตอนเดียวได้ และนี่คือโค้ดแบบหลายขั้นตอน ซึ่งดูคล้ายกันมาก และจุดที่สำคัญที่สุดคือการเขียน KDoc ให้ชัดเจนมากๆ

  • ดังนั้นในตัวอย่างนี้ ฟังก์ชัน getItinerary จะระบุอย่างชัดเจนว่าจะแสดงเฉพาะสรุประดับสูงของเหตุการณ์เท่านั้น

  • ดังนั้น เราจึงระบุด้วยว่าเราต้องเรียกใช้ getEventDetails พร้อม eventId หากต้องการรายละเอียดการเดินทางที่เจาะจงมากขึ้น

  • จากนั้น getEventDetails ยังมีความคิดเห็น KDoc ที่ระบุว่าสามารถดึงข้อมูลรายละเอียด เช่น ราคาตั๋วและหมายเหตุอื่นๆ ได้

  • ดังนั้น การใช้ความคิดเห็นโดยอธิบายความคิดเห็นอย่างชัดเจนจะช่วยให้เราแนะนำโมเดลให้ทราบว่าเครื่องมือต่างๆ โต้ตอบกันอย่างไรได้

  • ตกลง

  • นี่คือวิธีที่เราสร้าง Agent เพื่อทำลูปทั้งหมดนี้ ซึ่งก็คือลูป Agentic

    val agent = Agent.builder()
     .setName("Assistant")
     .setInstructions("You are a helpful travel assistant. Active tripId: $tripId")
     .enableAppFunctions()
     .setMaxTurns(5) // Guardrail against infinite tool loops
     .build()
    val runner = AgentRunner(agent = agent)
    // Automatically loops through all 3 tool calls in a single user turn!
    runner.runSingleTurnAsync(prompt).collect { event ->
     when (event) {
       is AgentEvent.FunctionCall -> showToolRunning(event.name)
       is AgentEvent.FunctionResponse -> showToolCompleted(event.name)
       is AgentEvent.TextMessage -> responseMessage += event.text
     }
    }
  • ซึ่งคล้ายกับตัวอย่างแบบขั้นตอนเดียวมาก โดยมีส่วนที่เพิ่มเข้ามาเพียง 2 ส่วนเท่านั้น

  • อย่างแรกคือเราต้องกำหนดจำนวนเทิร์นสูงสุด ซึ่งในตัวอย่างนี้เราจะกำหนดไว้ที่ 5 เทิร์น

  • แล้วฟีเจอร์นี้ทำอะไรได้บ้าง

  • ซึ่งจะช่วยป้องกันไม่ให้โมเดลทำงานวนซ้ำไม่รู้จบ

  • สมมติว่ามีพรอมต์ที่กำกวมหรืออาจมีการเรียกใช้เครื่องมือหนึ่งไปอีกเครื่องมือหนึ่งไปมาเรื่อยๆ หากเราตั้งค่านี้ไว้ ระบบจะหยุดและไม่วนซ้ำไปเรื่อยๆ

  • จากนั้นเราจะยังคงเรียกใช้ฟังก์ชันเดิมเพื่อเรียกใช้ runSingleTurnAsync

  • ดังนั้น แม้ว่าจะเรียกว่าแบบเทิร์นเดียว แต่จริงๆ แล้ว 1 เทิร์นหมายถึงคำถามของผู้ใช้ 1 รายการ

  • ดังนั้น ความคาดหวังจากผู้ใช้ปลายทางคือฉันส่งคำถามเดียว คุณแสดงคำตอบเดียว โดยไม่ต้องสนใจรายละเอียดมากนัก เช่น สิ่งที่เกิดขึ้นเบื้องหลัง เช่น จำนวนครั้งที่วนซ้ำ เป็นต้น

  • ดังนั้นเราจึงใช้ฟังก์ชันเดิมต่อไปได้

  • และเนื่องจากเครื่องมือการเรียกใช้เครื่องมือแบบหลายขั้นตอนใช้เวลามากกว่าเล็กน้อย คุณจึงเห็นในการสาธิตด้วยว่าเครื่องมือแสดงฟังก์ชันที่กำลังเรียกใช้อยู่ในขณะนั้น สิ่งที่กำลังดำเนินการอยู่ และแสดงเมื่อดำเนินการเสร็จแล้ว

  • ดังนั้นเราจึงต้องการแสดงความคืบหน้าแบบเรียลไทม์ใน UI

  • ดังนั้น แทนที่จะรวบรวมเฉพาะคำตอบที่เป็นข้อความ เราสามารถแสดงชื่อเครื่องมือ event.name ได้เมื่อใดก็ตามที่ AgentEvent แสดง FunctionCall หรือ FunctionResponse และเมื่อดำเนินการเสร็จแล้ว เราก็สามารถแสดงผล TextMessage ได้เหมือนเดิม

การจัดการเซสชันการสนทนาแบบต่อเนื่อง

  • เจ๋งมาก

  • เราจึงเรียกการเรียกใช้แบบไม่พร้อมกันนี้ว่าการเรียกใช้แบบไม่พร้อมกันแบบเทิร์นเดียว ซึ่งแน่นอนว่าเป็นการสมมติว่ามี API อีกประเภทหนึ่งด้วยใช่ไหม

  • ใช่

  • แน่นอนว่าการทำเพียงขั้นตอนเดียวไม่เพียงพอ

  • เราต้องการทำแบบหลายขั้นตอน

  • งั้นเรามาลองเซสชันแบบหลายเทิร์นกันต่อ และผมคิดว่าเราสามารถกลับไปที่เดโมของผมและทำให้ยากขึ้นอีกนิดได้

  • เราพูดถึงการเข้าชมพิพิธภัณฑ์ แต่เราก็มีการล่องเรือ Golden Gate Bay Cruise ด้วย

  • สำหรับทริปนั้น ฉันแค่อยากรู้ว่ามีงบประมาณเพียงพอไหมก่อนที่จะจองจริงๆ

  • มาลองถามคำถามกันเลย

  • มาลองดู

  • ฉันกำลังคิดว่าจะพาทีมไปล่องเรือ Golden Gate Bay Cruise พรุ่งนี้ และคิดว่าราคาอยู่ที่ประมาณ $180

  • ถ้าฉันทำแบบนั้น งบประมาณที่เหลืออยู่จริงๆ จะเป็นเท่าไร

  • แล้วฟีเจอร์นี้ทำอะไรได้บ้าง

  • ซึ่งจะตอบคำถามว่างบประมาณที่เหลืออยู่ของฉันจะเป็นเท่าใด

  • เพื่อให้เข้าใจแผนการเดินทางปัจจุบัน

  • ซึ่งมีข้อมูลดังกล่าวและสามารถบอกได้

  • แต่ฉันก็สามารถถามคำถามติดตามผลได้ที่นี่ด้วย

  • โอเค ดีเลย

  • ช่วยเพิ่มลงในค่าใช้จ่ายของฉันได้ไหม

  • เรียบร้อยแล้ว ระบบจะดำเนินการต่อ

  • อ๋อ ต้องใช้สกุลเงินด้วย เจ๋งมากเลย เป็นผลจากการสาธิต

  • โอเค สกุลเงินเป็นดอลลาร์

  • $180

  • เริ่มเลยนะคะ

  • นี่เป็นครั้งแรกที่เกิดเหตุการณ์แบบนี้ขึ้น เจ๋งมากเลย

  • แล้วฟีเจอร์นี้ทำอะไรได้บ้าง

  • โดยจะบอกว่าใช้เวลา 3 เทิร์นและเพิ่มค่าใช้จ่าย และยังช่วยให้ฉันรีเฟรชเซสชันนี้ได้ด้วย

  • ฉันจึงมีปุ่มรีเฟรชเล็กๆ ที่ด้านบน และหากกดปุ่มนั้น ฉันจะล้างประวัติทั้งหมดและเริ่มใหม่ได้

  • เมื่อทราบข้อมูลนี้แล้ว เรากลับไปดูสไลด์กันต่อได้เลย

  • เรามีเดโมที่ใช้งานได้ ซึ่งทั้งหมดทำสิ่งที่เราต้องการ ดังนั้นฉันคิดว่าสิ่งที่เราทำได้คือกลับไปที่โค้ด

  • ใช่

  • ซึ่งเป็นเรื่องที่เจ๋งมากๆ

  • คุณจะสังเกตเห็นว่า Jolanda ไม่ได้พูดถึงเรื่องนี้ โดยบอกแค่ว่า "โอ้ เป็นสกุลเงินดอลลาร์" แต่ไม่ได้บอกว่าเป็นตั๋วสำหรับ Golden Gate Bay Cruise

  • แล้วโมเดลจะรู้บริบททั้งหมดได้อย่างไร

  • ซึ่งแตกต่างจากที่แสดงให้คุณเห็นในข้อมูลโค้ด 2 รายการก่อนหน้าเล็กน้อย

    // 1. Get or create an isolated session for the current trip
    val session = runner.getOrCreateSession(tripId)
    
    // 2. Run a turn (history is preserved automatically across turns)
    session.runAsync(prompt).collect { event ->
      when (event) {
        is AgentEvent.FunctionCall -> showToolRunning(event)
        is AgentEvent.FunctionResponse -> showToolCompleted(event)
        is AgentEvent.TextMessage -> responseMessage += event.text
      }
    }
    
    // 3. Clear history when the user taps "New conversation"
    session.delete()
  • ดังนั้น แทนที่จะใช้ AgentRunner และ runSingleTurnAsync เราจำเป็นต้องสร้างเซสชันขึ้นมา

  • ดังนั้นในที่นี้เราจะเรียก runner.getOrCreateSession ด้วย tripId

  • แล้วทำไมเราจึงต้องสร้างเซสชัน

  • เนื่องจาก runSingleTurnAsync จะรีเซ็ตหน่วยความจำ จึงไม่มีบริบท

  • เราจะไม่สามารถสนทนาแบบหลายเทิร์นได้

  • ดังนั้นเมื่อคุณต้องการสนทนาแบบหลายเทิร์น เราจะใช้เซสชัน ซึ่งก็คือเซสชันของ Agent

  • จากนั้นเราจะส่งต่อ tripId

  • ทำไมเราจึงต้องทำเช่นนั้น

  • คุณเคยเห็นแอป Jetpacker มาก่อนแล้ว ซึ่งเป็นแอปที่จัดการการเดินทางหลายรายการ

  • มีทริป ADS, ทริปปารีส, อาจจะมีทริปโตเกียวด้วย

  • เราจึงต้องการเก็บบริบทไว้ในการเดินทาง

  • เราไม่อยากทำให้ใครเดือดร้อน คุณเข้าใจใช่ไหม

  • ดังนั้น แทนที่จะเรียก runSingleTurnAsync เราจึงมี runAsync

  • ซึ่งแตกต่างจากเดิม

  • ดังนั้นโค้ดนี้จะทำงานในเซสชัน และเราจะยังคงมีพรอมต์เดิมอยู่

  • และเรายังคงสามารถปล่อย AgentEvent, FunctionCall, FunctionResponse และ TextMessage ได้เหมือนเดิม

  • และเมื่อ Jolanda คลิกปุ่มการสนทนาใหม่ ปุ่มรีเฟรช เราก็สามารถมี session.delete ได้จริงๆ

  • ใช่

  • ตกลง

  • เราทำทั้งหมดนี้ด้วย Agentic API ใหม่ ซึ่งน่าทึ่งมากและจะทำงานได้อย่างน่าทึ่งสำหรับ Use Case หลายๆ อย่างของคุณ

  • แต่แน่นอนว่าเราต้องการทำให้สิ่งต่างๆ รวดเร็วหรือทำให้สิ่งต่างๆ เป็นไปได้ง่ายขึ้นเสมอ แต่เราก็ต้องการทำให้กรณีที่ซับซ้อนมากขึ้นเป็นไปได้สำหรับคุณด้วยเช่นกัน

  • ดังนั้นเราจึงต้องการให้แน่ใจว่าหากจำเป็น คุณจะยังคงใช้ API การเข้าถึงโดยตรงเพื่อสร้างเวอร์ชันระดับต่ำกว่าของสิ่งเหล่านี้ได้

  • Victoria คุณช่วยอธิบายได้ไหมว่าการเข้าถึงโดยตรงหมายความว่าอย่างไร และเราจะกลับมาใช้ระดับการเข้าถึงโดยตรงนี้อีกครั้งเมื่อใด

เวิร์กโฟลว์ Agentic ขั้นสูงและการผสานรวมเครื่องมือ

  • ได้เลย Jolanda

  • คุณอาจคุ้นเคยกับ Prompt API อยู่แล้วสำหรับการเข้าถึงโดยตรงนี้

  • ดังนั้นเราจึงรองรับการเรียกใช้ฟังก์ชันที่นี่ด้วย และเรามี Use Case 2 กรณีที่เราต้องการใช้ API นี้แทนโฟลว์ Agentic ที่ง่ายกว่า

  • กรณีแรกเป็นกรณีการใช้งานที่เรียบง่ายและไม่ซับซ้อน

  • คุณคงจำได้ว่าในโฟลว์แบบ Agentic เราจะส่งการสนทนาทั้งหมด คำค้นหา เครื่องมือ การเรียกใช้เครื่องมือ การตอบกลับของเครื่องมือ และทุกอย่างกลับไปยังโมเดล

  • และอาจมีบางกรณีการใช้งานที่เราไม่ต้องการทำเช่นนั้น เราแค่ต้องการส่งพรอมต์เท่านั้น

  • สมมติว่าเราต้องการส่งพรอมต์ไปยังโมเดลที่แสดงผลด้วยการเรียกใช้เครื่องมือ และเราไม่ต้องการแสดงผลการเรียกใช้เครื่องมือ เราแค่ต้องการส่งพรอมต์อื่นไปยังโมเดล เพื่อให้เราแก้ไขประวัติได้ เช่น เราควบคุมประวัติได้อย่างเต็มที่

  • เราจะยกตัวอย่างที่ชัดเจนขึ้นให้คุณดูที่นี่

    // Turn 1: Execute function directly without a second model call
    val turn1 = content(Content.Role.USER) {
      text("Change the background to a sunset")
    }
    val response1 = generativeModel.generateContent(
      generateContentRequest(turn1) { setTools(tools) }
    )
    executeFunctionCalls(response1)
    
    // Turn 2: Send only user prompts to save context tokens
    val customHistory = listOf(
      turn1,
      content(Content.Role.USER) { text("Oh no, I meant a beach") }
    )
    val response2 = generativeModel.generateContent(
      GenerateContentRequest.Builder(customHistory).setTools(tools).build()
    )
  • สมมติว่าเรามีแอปที่เปลี่ยนธีมพื้นหลังของอุปกรณ์ได้

  • และสมมติว่าผู้ใช้ส่งคำค้นหานี้ "เปลี่ยนพื้นหลังเป็นพระอาทิตย์ตก"

  • จากนั้นแอปของเราจะเรียกใช้ฟังก์ชันนี้ และพื้นหลังจะเปลี่ยนเป็นภาพพระอาทิตย์ตก

  • จากนั้นผู้ใช้เปลี่ยนใจและพูดว่า "โอ้ ไม่นะ ฉันหมายถึงชายหาด"

  • และสิ่งที่จะเกิดขึ้นคือเราไม่จำเป็นต้องส่งสถานะทั้งหมดที่อุปกรณ์ได้เปลี่ยนพื้นหลังเป็นพระอาทิตย์ตกแล้ว และตอนนี้ต้องการเปลี่ยนเป็นชายหาด

  • ไม่ เราไม่จำเป็นต้องทำเช่นนั้น

  • เราแค่ต้องส่งพรอมต์แรกและพรอมต์ที่ 2 เนื่องจากพรอมต์แรกมีข้อมูลของพื้นหลัง

  • เพื่อให้ระบบรู้ว่า "อ๋อ คำว่า 'นี้' หมายความว่าผู้ใช้ต้องการเปลี่ยนพื้นหลังเป็นชายหาด"

  • เราจึงใช้ Prompt API เพื่อดำเนินการนี้ได้

  • เราควบคุมประวัติได้อย่างเต็มรูปแบบ เราบันทึกโทเค็นบริบทไว้ในอุปกรณ์ เพราะอย่างที่ทราบกันดีว่าอุปกรณ์มีกรอบเวลาโทเค็นที่จำกัดกว่า และวิธีนี้ช่วยให้เราบันทึกโทเค็นได้มากขึ้น

  • และกรณีการใช้งานที่ 2 นั้นตรงกันข้ามอย่างสิ้นเชิง

  • ดังนั้น หากเรามีกรณีการใช้งานที่ซับซ้อนซึ่ง Agentic API ยังไม่รองรับ เช่น หากเรามีระบบ Multi-Agent ที่มี Agent ย่อย หลายตัวโต้ตอบกันเพื่อแก้ไขงานที่ซับซ้อน หรือระบบไฮบริดที่เรามีทั้งโมเดลในอุปกรณ์ แต่ก็ต้องการมีโมเดลระบบคลาวด์ด้วย หรือหากเราต้องการมีหน่วยความจำที่กำหนดเองและการบีบอัดบริบท

  • ซึ่งเกี่ยวข้องกับประวัติ เช่น การควบคุมประวัติอย่างเต็มรูปแบบที่เราพูดถึงก่อนหน้านี้

  • สมมติว่าเราต้องการ เนื่องจากหน้าต่างบริบทมีจำกัด เราจึงไม่ต้องการส่งทุกอย่างกลับไปยังโมเดล เราแค่ต้องการสรุปส่วนที่สำคัญที่สุด หรืออาจเป็นส่วนล่าสุด เพื่อให้เราควบคุมเรื่องนี้ได้อย่างเต็มที่ จากนั้นเราจึงไปใช้ API การเข้าถึงโดยตรง

  • และสำหรับ Use Case นี้ เราสามารถใช้เฟรมเวิร์ก เช่น Agent Development Kit หรือ ADK หรือ Crew หรือจะสร้างเฟรมเวิร์กการจัดการที่กำหนดเองก็ได้

  • ตกลง

  • ทั้งหมดนี้คือข้อมูลเกี่ยวกับ Agentic API

  • แต่ยังมีอีกเรื่องหนึ่งที่เราอยากพูดถึง

  • เราพูดถึง Agent ในแอปมาตลอด คุณจึงสร้างประสบการณ์การใช้ Agent ของคุณเองภายในแอปของคุณเอง แต่เราก็พูดถึงวิธีที่เราใช้ AppFunctions ด้วย และทำไมคุณจึงควรใช้ AppFunctions หากสิ่งเดียวที่คุณทำคือภายในแอปของคุณเอง

  • เราทราบดีว่านักพัฒนาแอปอย่างคุณไม่ชอบทำอะไรซ้ำๆ ใช่ไหม

  • เราไม่ชอบการคัดลอกและวางโค้ด และไม่ชอบการทำซ้ำตรรกะทางธุรกิจใดๆ

  • ดังนั้น AppFunctions จึงช่วยให้เราสามารถทำให้เครื่องมือของเราพร้อมใช้งานสำหรับตัวแทนระบบได้ด้วยการเปลี่ยนแปลงง่ายๆ แทนที่จะอนุญาตให้เฉพาะตัวแทนในแอปใช้เครื่องมือเหล่านั้น

  • แล้วโค้ดจะมีหน้าตาเป็นอย่างไร

  • ใช่

  • ดังนั้น AppFunction จึงช่วยให้เราเขียนโค้ดเพียงครั้งเดียว แล้วแสดงโค้ดเหล่านั้นในเลเยอร์ระดับต่างๆ ได้ตามต้องการ

  • คุณจำได้ไหมว่าก่อนหน้านี้เรามี ACCESS_LEVEL_SELF ซึ่งจะแสดงฟังก์ชันที่เราเขียนให้กับ Agent ในแอปเท่านั้น แต่เรายังเปลี่ยนระดับการเข้าถึงนั้นเป็น ACCESS_LEVEL_ANDROID_TRUSTED ได้ด้วย

  • ซึ่งจะแสดงฟังก์ชันต่างๆ ต่อตัวแทนที่เชื่อถือได้ในระบบปฏิบัติการ Android

  • คุณจึงไม่จำเป็นต้องเขียนฟังก์ชัน คำสั่ง หรือตรรกะฐานข้อมูลใหม่

  • AppFunctions ทำหน้าที่เป็นเสียงของแอปทั้งภายในและภายนอกอยู่แล้ว

  • เราต้องการดูการสาธิตเรื่องนั้นด้วยใช่ไหม

  • เอาล่ะ กลับไปที่โทรศัพท์กัน และสำหรับการสาธิตครั้งสุดท้าย เราจะออกจากแอป Jetpacker กันจริงๆ

  • เราจึงสามารถออกไปและเข้าไปใน Gemini ได้จริงๆ

  • ดังนั้นใน Gemini เราจึงเรียกใช้ @Jetpacker ได้

  • สิ่งหนึ่งที่ควรทราบคือฟีเจอร์นี้พร้อมใช้งานในเวอร์ชันเบต้าแบบจำกัดเท่านั้น ดังนั้นจึงอาจยังไม่พร้อมให้บริการแก่ทุกคนในวันนี้ แต่คุณสามารถทดสอบและลองใช้ได้

  • มาดูกันว่า Jetpacker จะเป็นยังไง

  • เราทำอะไรกันดี

  • อ๋อ เมื่อเช้าฉันดื่มกาแฟไปแล้วใช่ไหม

  • ช่วยเพิ่มค่ากาแฟ $7 ลงในค่าใช้จ่ายการเดินทางของ ADS ให้หน่อยได้ไหม

  • เริ่มเลยนะคะ

  • และคุณจะเห็นว่าแอปนี้เชื่อมต่อกับแอป Jetpacker

  • เท่านี้ก็เรียบร้อย

  • ระบบเพิ่มค่าใช้จ่ายให้ฉันแล้ว

  • เราได้เห็นวิธีใช้ทุกอย่างใน Jetpacker แล้ว

  • ข้อดีของ Jetpacker คือเราสามารถเปิดโหมดบนเครื่องบินได้ด้วย

  • เรายังไม่ได้แสดงให้เห็นในวันนี้ แต่เราสามารถเปิดโหมดบนเครื่องบินได้ และทุกอย่างที่คุณเห็นในการสาธิต และฉันอยากจะแสดงให้เห็น เพราะฉันต้องการให้ทุกคนเชื่อใจฉัน ดังนั้นเรามาปิด Wi-Fi กัน

  • มีใครมีตัวอย่างการสาธิตที่ดีไหม เช่น ค่าใช้จ่ายที่ฉันเพิ่มได้

  • มาลองดู

  • เราอาจจะทำอะไรได้บ้าง

  • อ๋อ เช่ารถก็ดีใช่ไหม

  • ตกลง

  • ลองพูดว่า "เพิ่มค่าใช้จ่าย $300 สำหรับค่าเช่ารถ"

  • เรียบร้อย และระบบก็เข้าใจ

  • ตกลง

  • เยี่ยมมาก

  • เอาล่ะ กลับไปที่สไลด์กัน

  • เราได้พูดถึง AppFunctions ไปแล้ว

  • ฉันรู้ว่าเราได้พูดถึงเรื่องนี้ไปแบบคร่าวๆ แล้ว

  • หากต้องการทราบข้อมูลเพิ่มเติม คุณสามารถดูการบรรยายอีกรายการจาก Dan และ Sarah ซึ่งจะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีเปิดใช้ วิธีทดสอบ เครื่องมือที่เรามี และอื่นๆ

  • วันนี้เราได้พูดถึงสิ่งต่างๆ มากมาย

  • เราได้เห็นขั้นตอนเดียว, หลายขั้นตอน, หลายรอบ และสุดท้ายเรายังได้เห็นวิธีใช้ AppFunction ของระบบด้วย

  • เรามีเคล็ดลับ 2-3 ข้อก่อนจะจากกันไป

แนวทางปฏิบัติแนะนำและบทสรุป

  • ใช่

  • ดังนั้น โปรดคำนึงถึงเคล็ดลับ 3 ข้อนี้เมื่อเราเริ่มสร้างประสบการณ์การใช้งาน Agent

  • ข้อแรกคือการมุ่งเน้นที่ชุดเครื่องมือ

  • ดังนั้นโปรดทราบว่าโมเดลในอุปกรณ์มีความสามารถที่จำกัดเนื่องจากมีขนาดเล็กกว่า

  • ดังนั้น แทนที่จะลงทะเบียนเครื่องมือหลายสิบรายการ การใช้เครื่องมือจำนวนน้อยลงจะดีกว่า

  • และอย่างที่บอกไปก่อนหน้านี้ ให้เขียนคำอธิบาย KDoc ที่ชัดเจน เพราะนั่นคือสิ่งที่โมเดลใช้เพื่อตัดสินใจว่าจะแสดงผลอะไร

  • จากนั้นเราควรกำหนดขอบเขตการเลี้ยวสูงสุดด้วย

  • นอกจากนี้ยังช่วยป้องกันไม่ให้โมเดลวนซ้ำไม่รู้จบหากเกิดความสับสน

  • ซึ่งหมายความว่าเราอาจทำให้แบตเตอรี่ของผู้ใช้หมดเร็วขึ้น

  • ดังนั้นหากมีสิ่งนี้ เราจะประหยัดแบตเตอรี่ของผู้ใช้ได้

  • และสุดท้าย เราต้องตรวจสอบความพร้อมใช้งานว่าอุปกรณ์รองรับการเรียกใช้ฟังก์ชันอยู่แล้วหรือไม่ และใช้การสำรองข้อมูลที่เหมาะสม

  • เช่น หากไม่รองรับ คุณสามารถอ้างอิงโมเดลระบบคลาวด์หรือ UI มาตรฐานได้

  • เอาล่ะ คิดว่าเราสามารถปิดการสนทนานี้ได้ด้วยแหล่งข้อมูลของเรา

  • เราจะแสดงคิวอาร์โค้ดให้คุณดูอีกครั้ง

  • โปรดดู

  • ฉันคิดว่าสิ่งหนึ่งที่ควรคำนึงถึงคือเรามี Android Skills สำหรับเรื่องนี้ด้วย

  • คุณจึงไม่ต้องเจาะลึกรายละเอียดเล็กๆ น้อยๆ ทุกอย่าง

  • เรามีเอกสารประกอบแบบเต็มและ Android Skill ที่จะมาพร้อมกับ Agentic API ใหม่นี้

  • ลองดูข้อมูลนั้นได้เลย

  • โปรดดาวน์โหลดเวอร์ชันตัวอย่างสำหรับนักพัฒนาซอฟต์แวร์ของ AICore เพื่อทดสอบสิ่งเหล่านี้ทั้งหมด และขอให้สนุกกับการเขียนโค้ด

รหัส

ข้อมูลโค้ด

ข้อมูลโค้ด Kotlin ที่ 09:53 ใน "การสาธิตตัวแทน Jetpacker และการเรียกใช้ฟังก์ชัน"
/**
 * Adds an expense to a trip.
 *
 * @param tripId Identifier of the trip (e.g., "paris-2026").
 * @param amount Expense amount in dollars.
 * @param description Short description of what the expense was for.
 */
@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(
  level = ACCESS_LEVEL_SELF,
  isCompatEnforcementEnabled = true,
)
suspend fun addExpense(tripId: String, amount: Double, description: String) {
  // Save expense to local DB / repository...
}
ข้อมูลโค้ด Kotlin ที่นาที 11:41 ใน "การสร้างและติดตั้งใช้งานรันเนอร์ของ AI Agent"
val model = Generation.getClient()
val features = model.getAvailableFeatures()
if(FUNCTION_CALLING in features) {
  // Device is supported!
} else {
  // Graceful fallback
}
ข้อมูลโค้ด Kotlin ที่เวลา 12:07 ใน "การสร้างและติดตั้งใช้งานรันเนอร์ของ AI Agent"
if(FUNCTION_CALLING in features) {
  // 1. Build the agent configuration
  val agent = Agent.builder()
    .setName("Assistant")
    .setInstructions("You are a helpful on-device assistant.")
    .enableAppFunctions()
    .build()

  // 2. Initialize the runner
  val runner = AgentRunner(agent = agent)

  // 3. Create your prompt
  val prompt = content { text(userPrompt)}

  // 4. Start the agent
  runner.runSingleTurnAsync(prompt).textTokens().collect { responseMessage += it }

}
ข้อมูลโค้ด Kotlin ที่ 17:23 ใน "สถาปัตยกรรมและการดำเนินการของ Agent แบบหลายขั้นตอน"
/**
* Retrieves the itinerary events (ID, title, location) for a trip.
* Note: Call getEventDetails(eventId) to retrieve ticket prices or notes.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(level = ACCESS_LEVEL_SELF, isCompatEnforcementEnabled = true)
suspend fun getItinerary(tripId: String): List<EventSummary> { ... }

/**
* Retrieves details for a specific event, including admission ticket prices.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(level = ACCESS_LEVEL_SELF, isCompatEnforcementEnabled = true)
suspend fun getEventDetails(eventId: String): EventDetails { ... }
ข้อมูลโค้ด Kotlin ที่ 18:23 ใน "สถาปัตยกรรมและการดำเนินการของ Agent แบบหลายขั้นตอน"
val agent = Agent.builder()
 .setName("Assistant")
 .setInstructions("You are a helpful travel assistant. Active tripId: $tripId")
 .enableAppFunctions()
 .setMaxTurns(5) // Guardrail against infinite tool loops
 .build()
val runner = AgentRunner(agent = agent)
// Automatically loops through all 3 tool calls in a single user turn!
runner.runSingleTurnAsync(prompt).collect { event ->
 when (event) {
   is AgentEvent.FunctionCall -> showToolRunning(event.name)
   is AgentEvent.FunctionResponse -> showToolCompleted(event.name)
   is AgentEvent.TextMessage -> responseMessage += event.text
 }
}
ข้อมูลโค้ด Kotlin ที่ 22:13 ใน "การจัดการเซสชันการสนทนาแบบหลายรอบ"
// 1. Get or create an isolated session for the current trip
val session = runner.getOrCreateSession(tripId)

// 2. Run a turn (history is preserved automatically across turns)
session.runAsync(prompt).collect { event ->
  when (event) {
    is AgentEvent.FunctionCall -> showToolRunning(event)
    is AgentEvent.FunctionResponse -> showToolCompleted(event)
    is AgentEvent.TextMessage -> responseMessage += event.text
  }
}

// 3. Clear history when the user taps "New conversation"
session.delete()
ข้อมูลโค้ด Kotlin ที่ 25:02 ใน "Advanced agentic workflows and tool integration"
// Turn 1: Execute function directly without a second model call
val turn1 = content(Content.Role.USER) {
  text("Change the background to a sunset")
}
val response1 = generativeModel.generateContent(
  generateContentRequest(turn1) { setTools(tools) }
)
executeFunctionCalls(response1)

// Turn 2: Send only user prompts to save context tokens
val customHistory = listOf(
  turn1,
  content(Content.Role.USER) { text("Oh no, I meant a beach") }
)
val response2 = generativeModel.generateContent(
  GenerateContentRequest.Builder(customHistory).setTools(tools).build()
)