Ứng dụng thông minh

Khai thác khả năng của tác nhân AI trong ứng dụng bằng GenAI của Bộ công cụ học máy

Loa

Giới thiệu

Giới thiệu về bài thuyết trình

Nâng tầm các tính năng AI trên Android bằng cách xây dựng các tác nhân tự động trên thiết bị. Trong phiên này, chúng ta sẽ tìm hiểu sâu về các API tác nhân mới của Bộ công cụ học máy, được xây dựng trực tiếp trên các API truy cập mô hình trực tiếp mà bạn đã biết.

Thông qua ứng dụng Jetpacker nguồn mở, bạn sẽ tìm hiểu cách liên kết các hành động nhiều bước trên dữ liệu cục bộ, quản lý các phiên nhiều lượt có trạng thái với bối cảnh riêng biệt và kết nối MCP Android để hỗ trợ cả trợ lý trong ứng dụng và các nền tảng cấp hệ thống Android bằng một cơ sở mã duy nhất.

Tóm tắt

  • Gemini Nano 4 hỗ trợ sẵn tính năng Gọi hàm, cho phép các mô hình trên thiết bị dịch ngôn ngữ tự nhiên thành hành động và điều khiển các API cục bộ.
  • Thư viện AppFunctions của Jetpack xác định các công cụ thông qua nội dung mô tả KDoc bằng ngôn ngữ tự nhiên. Việc thay đổi cấp truy cập có thể khiến các chức năng này hiển thị với các tác nhân hệ thống như Gemini.
  • Thư viện luồng tác nhân mới (phiên bản alpha) trong Bộ công cụ học máy GenAI giúp đơn giản hoá việc xây dựng tác nhân bằng cách xử lý vòng lặp thực thi và lệnh gọi công cụ.
  • AgentRunner điều phối việc thực thi công cụ và cung cấp các phương thức phiên cho các cuộc trò chuyện nhiều lượt mà không làm mất ngữ cảnh.
  • Bạn vẫn có thể truy cập trực tiếp thông qua Prompt API để kiểm soát nhật ký đơn giản hoặc thiết lập phức tạp như hệ thống đa tác nhân và hệ thống kết hợp.

Kỹ năng

Tài nguyên

Gemini Notebook

Chủ đề

  • ml-kit
  • on-device-ai
  • gemini-nano
  • tác nhân AI
  • gọi hàm
  • app-functions
  • aicore

Bản chép lời

Giới thiệu về AI trên thiết bị và Gemini Nano

  • Tôi là Jolanda Verhoef.

  • Tôi là kỹ sư Quan hệ với nhà phát triển tại Android. Hôm nay, tôi có sự tham gia của Victoria Lestari, một thành viên trong nhóm kỹ thuật AICore.

  • Chào các bạn!

  • Cảm ơn các bạn đã tham gia cùng chúng tôi sáng nay.

  • Vì vậy, nhóm chúng tôi đã hợp tác chặt chẽ trong nhiều năm để đưa các mô hình AI tiên tiến ra khỏi đám mây và chạy trực tiếp trên thiết bị Android.

  • Những nỗ lực này được nhóm lại trong bộ GenAI của Bộ công cụ học máy.

  • Một mặt, trong bộ GenAI của Bộ công cụ học máy này, chúng tôi có các API cấp cao cho các giải pháp chìa khoá trao tay, chẳng hạn như tóm tắt văn bản, mô tả hình ảnh và nhận dạng lời nói.

  • Mặt khác, chúng tôi cũng đã mở quyền truy cập trực tiếp vào mô hình trên thiết bị, Gemini Nano.

  • Hôm nay, chúng tôi sẽ thực hiện thêm một bước nữa.

  • Vì vậy, với quyền truy cập trực tiếp này, chúng tôi sẽ cho phép bạn xây dựng các tác nhân trong ứng dụng, những tác nhân có thể suy luận qua nhiều lượt, có thể lưu ngữ cảnh trò chuyện và có thể thực hiện hành động trong ứng dụng của bạn.

  • Vậy chính xác thì điều đó có nghĩa là gì, Victoria?

  • Vâng.

  • Cho đến nay, khi nói về các trường hợp sử dụng trên thiết bị, chúng ta đang nghĩ đến những nhiệm vụ ngắn hơn, đơn giản hơn và riêng biệt, chẳng hạn như phát hiện một hành vi lừa đảo có thể xảy ra hoặc tóm tắt một cuộc gọi điện thoại.

  • Và khi muốn xây dựng một tính năng phức tạp hơn, chẳng hạn như đặt câu hỏi về danh mục ảnh hoặc hỏi Google Maps, chúng tôi thường tham khảo các mô hình đám mây lớn.

  • Vì vậy, các mô hình đám mây mang lại lợi thế về khả năng xử lý dữ liệu khổng lồ và theo thời gian thực, nhưng AI trên thiết bị mở ra những khả năng mới cho việc xử lý dữ liệu cục bộ, theo thời gian thực và có độ trễ thấp.

  • Như vậy, Gemini Nano đã mở rộng những việc bạn có thể làm trên thiết bị của mình.

  • Một ví dụ là ứng dụng Ảnh chụp màn hình trên Pixel, cho phép bạn tìm kiếm ảnh chụp màn hình trên thiết bị và đặt câu hỏi về ảnh chụp màn hình.

  • Vì vậy, khi chạy tác nhân trên thiết bị, chúng tôi cần một bước nhảy vọt về khả năng của mô hình. Đó là lý do chúng tôi xây dựng Gemini Nano 4, có khả năng suy luận qua các bước và thực hiện hành động thực tế.

  • Nhưng tính năng mới này chỉ quan trọng nếu nhà phát triển có thiết bị thực để nhắm đến.

  • Tin vui là Gemini Nano 4 đã có mặt trên một số thiết bị cao cấp và bạn cũng có thể thử nghiệm mô hình này trên nhiều thiết bị khác bằng cách sử dụng Bản dùng trước cho nhà phát triển của AICore.

  • Chúng tôi cũng đang trên đà đạt được mục tiêu 100 triệu thiết bị vào đầu năm sau.

  • Vậy điều này có nghĩa là gì?

  • Điều này có nghĩa là bạn có thể bắt đầu xây dựng và thử nghiệm những trải nghiệm này ngay hôm nay, biết rằng những gì bạn đang xây dựng hôm nay sẽ mở rộng đến một lượng lớn khán giả.

  • Chúng ta đã thảo luận về Gemini Nano 4 và phạm vi tiếp cận tiềm năng, giờ hãy nói về ý nghĩa thực sự của khả năng của tác nhân AI.

  • Vì vậy, AI tác nhân có thể lập kế hoạch cho các nhiệm vụ phức tạp, chia các nhiệm vụ này thành nhiều bước, sau đó thực hiện các bước này trên ứng dụng của bạn.

  • Giả sử mô hình Gemini Nano trên thiết bị có thể truy vấn cơ sở dữ liệu cục bộ, truy xuất dữ liệu từ phần phụ trợ, suy luận và trả lời các câu hỏi phức tạp của bạn, đồng thời kích hoạt quy trình công việc của ứng dụng gốc trên thiết bị.

  • Nhưng làm thế nào để AI thực sự chuyển từ việc lập kế hoạch sang thực hiện hành động thực tế?

  • Và đây là chủ đề của ngày hôm nay: Gọi hàm.

  • Đây là công cụ cốt lõi cho khả năng của tác nhân AI.

  • Mô hình này dịch ngôn ngữ tự nhiên thành lệnh thực thi hệ thống, đồng thời thúc đẩy mô hình từ suy luận thụ động sang thực thi chủ động bằng cách chọn và điều khiển các API cục bộ cần thiết để giải quyết các nhiệm vụ phức tạp.

  • Chúng tôi cũng rất vui mừng được thông báo rằng Gemini Nano 4 hỗ trợ sẵn tính năng Gọi hàm.

  • Mô hình này hiện có trong Bản dùng trước cho nhà phát triển của AICore và sẽ sớm được triển khai cho tất cả thiết bị hỗ trợ Nano 4.

  • Thật tuyệt khi chúng ta có những khả năng như vậy trong mô hình, nhưng khả năng trong mô hình chưa cung cấp cho bạn, với tư cách là nhà phát triển, các API.

  • Vì vậy, chúng tôi cũng đang nỗ lực bổ sung các API phù hợp vào bộ ML Kit GenAI để đảm bảo rằng việc xây dựng một tác nhân thực sự trở nên dễ dàng.

  • Vì vậy, chúng tôi bắt đầu bằng các API truy cập trực tiếp mà chúng tôi đã có, đồng thời thêm các loại hàm và loại nhật ký trò chuyện vào đó.

  • Và với hai tính năng mới này trong Prompt API của Bộ công cụ học máy, giờ đây, bạn có thể tích hợp các khả năng của tác nhân AI cấp thấp này vào ứng dụng của mình.

  • Tuy nhiên, có khá nhiều việc bạn cần tự làm tại thời điểm đó.

  • Vì vậy, bạn cần thực thi các hàm theo cách thủ công, lưu nhật ký cuộc gọi và lặp lại nhiều cuộc gọi.

  • Vì vậy, nếu cần thực hiện nhiều lệnh gọi, bạn sẽ phải tự xây dựng một hệ thống lặp và tự xây dựng các rào chắn bảo vệ cho hệ thống đó.

  • Giờ thì bạn có thể làm được điều đó, nhưng tất nhiên, chúng tôi muốn giúp bạn thực hiện việc này một cách nhanh chóng và dễ dàng.

  • Vì vậy, ngoài thư viện Prompt API này, chúng tôi cũng đã thêm một thư viện luồng tác nhân mới.

  • Bạn có thể tìm thấy thành phần này trong com.mlkit.genai:agent-api và hiện có sẵn ở phiên bản alpha.

  • Giờ đây, với API mới này, chúng tôi sẽ giúp bạn xử lý phần lớn công việc khó khăn.

  • Vì vậy, thay vì phải thực hiện nhiều bước thủ công, bạn chỉ cần viết vài dòng mã là có thể tạo một tác nhân.

  • Và đó chính là điều chúng ta sẽ làm hôm nay.

Bản minh hoạ về tác nhân Jetpacker và lệnh gọi hàm

  • Vì vậy, chúng ta sẽ xem cách các API này thực sự hoạt động với ứng dụng mẫu Jetpacker.

  • Giờ đây, bạn đã thấy Jetpacker tại Google I/O và trong suốt hội nghị này.

  • Nhưng bạn có thể quét mã QR này và nếu quan tâm, bạn có thể xem mã, bạn có thể trỏ tác nhân của mình vào mã, bạn có thể tương tác với mã và bạn có thể tìm hiểu thêm về cách chúng tôi thực sự triển khai tất cả các tính năng này.

  • Giờ đây, Jetpacker là một trợ lý du lịch hiện đại mà chúng tôi xây dựng để giới thiệu nhiều tính năng thông minh khác nhau.

  • Vì vậy, ứng dụng này đã sử dụng các mô hình trên thiết bị và mô hình đám mây cho mọi loại API câu lệnh, nhưng giờ đây, chúng ta sẽ thêm một tác nhân vào ứng dụng này.

  • Và đối với tác nhân này, đối với Jetpacker, chúng ta sẽ sử dụng API tác nhân GenAI mới của Bộ công cụ học máy mà chúng ta vừa nói đến, và API đó sẽ sử dụng hệ thống AI Core cơ bản chạy trên thiết bị Android để giao tiếp với mô hình Gemini Nano.

  • Bây giờ, tôi có thể nói tất cả những gì tôi muốn và chúng ta sẽ nói khá nhiều, nhưng tất nhiên, chúng tôi muốn giới thiệu điều này.

  • Giờ thì hãy chuyển sang điện thoại và xem bản minh hoạ thực tế.

  • Tôi có một chiếc Pixel 11 Pro ở đây.

  • Và tôi nghĩ chúng ta có thể chuyển sang gói Pro, và bạn có thể thấy gói này.

  • Được rồi, đây rồi.

  • Và như bạn thấy, chúng ta có ứng dụng Jetpacker đang chạy ở đây.

  • Ứng dụng này có một loạt chuyến đi sắp tới của chúng tôi và tôi có thể xem chuyến đi hiện tại. Trong chuyến đi hiện tại, bạn có thể thấy hôm nay là ngày 29 tháng 10, vì vậy, chúng tôi có hội thảo về AI trên thiết bị, buổi giới thiệu bản minh hoạ, sau đó vào ngày mai, tôi có một chuyến du ngoạn trên Vịnh Cầu Cổng Vàng và sắp tới tôi sẽ tham quan bảo tàng.

  • Tất cả những điều đó đều tuyệt vời.

  • Bây giờ, chúng ta có thể chuyển đến màn hình chi phí, nơi chúng ta theo dõi ngân sách của chuyến đi này.

  • Và như bạn có thể thấy ở dưới cùng bên phải, có một biểu tượng lấp lánh mới, đó là tác nhân trên thiết bị mà chúng tôi đã xây dựng.

  • Hãy bắt đầu bằng một lệnh khá đơn giản.

  • Tôi nghĩ chúng ta có thể yêu cầu Gemini thêm một khoản chi phí cụ thể.

  • Ví dụ: thêm một khoản chi phí cho chuyến taxi đến Mountain View, với số tiền là 45 đô la.

  • Chúng ta hãy bắt đầu.

  • Ứng dụng đang chuẩn bị khoản chi phí và đã thêm khoản chi phí đó.

  • Thật tuyệt vời!

  • Được rồi, bản minh hoạ 1 đã hoạt động.

  • Hãy quay lại trang trình bày và xem cách hoạt động thực tế của tính năng này.

  • Đầu tiên, như bạn thấy, tôi đã dùng giọng nói của mình và giọng nói đó cũng sử dụng mô hình Gemini Nano thông qua API nhận dạng lời nói để chép lời những gì tôi nói thành văn bản.

  • Sau đó, văn bản đó sẽ được gửi đến mô hình Gemini Nano.

  • Nhưng nó sẽ làm gì?

  • Ví dụ: tôi yêu cầu Gemini thêm một khoản vào chi phí, tôi nói với Gemini về khoản tiền 45 USD, nhưng Gemini không thực sự biết cách xử lý, đúng không?

  • Lúc này, nó sẽ chỉ nói với tôi rằng: "Tôi không biết bạn đang nói gì".

  • Vì vậy, chúng ta cần bổ sung một chút cho câu lệnh đó.

  • Chúng ta có thể thêm công cụ vào câu lệnh, vì vậy, chúng ta có thể thêm mọi loại công cụ. Trong trường hợp này, tôi sẽ thêm công cụ lấy ngân sách chuyến đi, hành trình, v.v. Và nếu bạn chuyển thông tin này đến mô hình Nano bằng API của chúng tôi, mô hình này sẽ tự động hiểu rằng thay vì trả về văn bản, giờ đây, mô hình này có thể trả về một lệnh gọi hàm.

  • Vì vậy, trong trường hợp của chúng ta, Gemini sẽ nói: "Thay vì trả lời bằng văn bản, tôi sẽ thêm một khoản chi phí 45 USD cho chuyến taxi đến Mountain View".

  • Thật tuyệt, nhưng Victoria ơi, tính năng đó thực sự hoạt động như thế nào?

  • Làm cách nào để xây dựng hệ thống này?

  • Vâng.

  • Vậy làm cách nào để xác định các công cụ trong mã?

  • Vì vậy, đây là điều tôi sẽ giải thích.

  • Vì vậy, với Android MCP, chúng tôi sử dụng thư viện AppFunctions của Jetpack để xác định và phát hành các công cụ này.

  • Vì vậy, thư viện AppFunctions có thể hiển thị các dịch vụ, dữ liệu và hành động của ứng dụng trong sổ đăng ký hệ điều hành Android để chúng ta có thể sử dụng lại chính xác các hàm đó nhằm khai báo và mô tả cho các công cụ và sử dụng chúng trong ứng dụng của bạn.

  • Điều này cũng cho phép người dùng hoàn thành nhiệm vụ này thông qua các tác nhân và hoạt động tương tác ở cấp hệ thống.

  • Vì vậy, tôi sẽ đưa ra một ví dụ cụ thể trong mã.

    /**
     * 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...
    }
  • Vì vậy, bạn có thể thấy đây là ví dụ về AppFunctionDeclaration.

  • Bạn có thể thấy nhận xét KDoc ở trên cùng mô tả chức năng của hàm và các tham số của hàm, tất cả đều bằng ngôn ngữ tự nhiên.

  • Vì vậy, phương pháp hay nhất là đưa ra nội dung mô tả rõ ràng nhất có thể vì chúng ta sẽ gửi nội dung này đến mô hình.

  • Và sau đó, bạn có thể thấy bên dưới có một chú thích, @AppFunctionAccessLevel.

  • Vì vậy, điều này sẽ xác định phạm vi hiển thị của hàm.

  • Trong ví dụ này, chúng ta có ACCESS_LEVEL_SELF, nghĩa là các hàm mà chúng ta đang viết chỉ hiển thị cho ứng dụng của bạn, các tác nhân của ứng dụng.

  • Sau đó, chúng ta có thể thực sự xác định và triển khai chính hàm tạm ngưng, đồng thời có thể viết nhiều hàm ứng dụng nhất có thể và nhóm tất cả các hàm đó vào một lớp dịch vụ duy nhất.

Xây dựng và triển khai trình chạy tác nhân AI

  • Được rồi.

  • Vậy là chúng ta có các công cụ và có thể viết các công cụ đó, thật tuyệt.

  • Nhưng tất nhiên, để xây dựng một tác nhân, chúng ta cần nhiều hơn là chỉ những AppFunctions đó.

  • Vì vậy, chúng ta có thêm 3 phần quan trọng cần biết khi viết một tác nhân.

  • Trước tiên, chúng ta sẽ xác định tác nhân.

  • Vì vậy, chúng tôi đang xác định các chỉ dẫn hệ thống, các rào chắn, cấu hình, cách làm việc với nó.

  • Và chúng tôi cũng có một AgentRunner.

  • AgentRunner là thành phần thực thi tác nhân cho chúng ta.

  • Vậy là chúng ta đã xử lý xong cơ chế lặp mà chúng ta đã nói đến.

  • Và cuối cùng, chúng ta có AgentEvents. AgentEvents là một cách để hệ thống, API, cho chúng ta biết hệ thống đang thực sự làm gì.

  • Vì vậy, nó sẽ cho chúng ta biết khi nào nó trả về văn bản, khi nào nó thực hiện lệnh gọi công cụ, v.v., để sau đó chúng ta có thể thực sự hiển thị thông tin đó trong giao diện người dùng.

  • Được rồi.

  • Hãy xem mã thực tế.

  • Được rồi.

  • Trước khi viết mã, trước tiên, chúng ta cần kiểm tra xem tính năng Gọi hàm có hoạt động trên thiết bị của bạn hay không, vì tính năng Gọi hàm chỉ được hỗ trợ trên Gemini Nano 4.

    val model = Generation.getClient()
    val features = model.getAvailableFeatures()
    if(FUNCTION_CALLING in features) {
      // Device is supported!
    } else {
      // Graceful fallback
    }
  • Trước tiên, chúng ta cần kiểm tra bằng cách gọi mô hình tạo sinh getAvailableFeatures.

  • Chúng ta chỉ cần kiểm tra xem tính năng Gọi hàm có trong danh sách tính năng hay không.

  • Nếu không, chúng ta có thể triển khai một phương án dự phòng linh hoạt.

  • Được rồi.

  • Bây giờ, hãy cùng tìm hiểu mã.

    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 }
    
    }
  • Chúng ta biết rằng tính năng Gọi hàm được hỗ trợ trên thiết bị, sau đó chúng ta có thể chỉ cần viết lệnh bắt đầu – tạo tác nhân bằng Agent.builder.

  • Sau đó, chúng ta có thể đặt tên cho nó.

  • Chúng ta có thể setInstructions để xác định bối cảnh và nhân vật.

  • Và cuối cùng, chúng ta gọi enableAppFunctions.

  • Vì vậy, hàm này sẽ gọi, lặp lại và tự động khám phá tất cả AppFunction mà chúng ta vừa liệt kê trước đó và chúng ta vừa tạo tác nhân.

  • Tiếp theo, chúng ta cần khởi chạy AgentRunner.

  • Vì vậy, trình chạy tác nhân này sẽ quản lý vòng lặp thực thi cho chúng ta.

  • Chúng ta chỉ cần gọi hàm khởi tạo của lớp Instructor và truyền đối tượng Agent vào đó.

  • Cuối cùng, chúng ta có thể gói lệnh thoại đã chép lời vào một khối nội dung để tạo câu lệnh.

  • Sau đó, chúng ta gọi trình chạy.

  • Chúng ta gọi hàm runSingleTurnAsync này trên câu lệnh. Hàm này sẽ khởi động tác nhân, chạy suy luận và thực hiện toàn bộ quá trình suy luận AI.

  • Nếu chỉ muốn truyền trực tuyến câu trả lời dạng văn bản cuối cùng của mô hình vào giao diện người dùng, chúng ta chỉ cần gọi textTokens và hàm này sẽ thu thập phản hồi khi phản hồi đến.

  • Vậy là xong.

  • Đúng là chỉ có vài dòng mã, nên chúng tôi không hề nói đùa khi nói như vậy.

  • Vậy là chỉ với vài dòng mã này, bạn đã có một tác nhân.

  • Tác nhân đó sẽ làm những gì bạn muốn.

  • Nó sẽ đưa ra câu trả lời bằng văn bản.

  • Nhưng tất nhiên, trong bản minh hoạ đầu tiên mà chúng tôi giới thiệu, chúng tôi chỉ thực hiện một lệnh rất đơn giản, đúng không?

  • Ví dụ: tôi đưa ra một lệnh và tác nhân thực hiện một hành động, vậy thôi. Như vậy thì chưa thực sự là một tác nhân hoàn chỉnh, đúng không?

  • Vậy nên, hãy thêm một chút gia vị cho cuộc trò chuyện.

  • Trong phần tiếp theo, tôi muốn đưa ra một lệnh, nhưng tôi muốn làm cho lệnh đó khó hơn một chút đối với tác nhân.

Cấu trúc và cách thực thi tác nhân nhiều bước

  • Giờ hãy quay lại thiết bị minh hoạ của tôi và xem liệu chúng ta có thể làm cho thiết bị này khó hơn một chút không.

  • Chúng tôi đã có mặt ở đó.

  • Thật tuyệt vời!

  • Như tôi đã nói, chúng ta có kế hoạch tham quan bảo tàng vào ngày mai.

  • Và đối với chuyến tham quan bảo tàng đó, bạn có thể thấy giá vé vào cửa là 25 đô la Mỹ nếu tôi mở ra.

  • Bây giờ, hãy xem liệu nhân viên hỗ trợ của chúng ta có thực sự có thể tự tìm ra điều đó hay không.

  • Vì vậy, tôi sẽ quay lại màn hình chi phí và ra lệnh cho Gemini: "Thêm vé bảo tàng vào chi phí của tôi".

  • Chúng ta hãy bắt đầu.

  • Và giờ đây, như bạn có thể thấy, Gemini đang kiểm tra hành trình của tôi, tìm thấy chuyến tham quan bảo tàng và thêm chuyến tham quan đó vào chi phí của tôi.

  • Vậy là mô hình này đã thực hiện 3 bước để đạt được mục tiêu và cũng đưa ra câu trả lời cuối cùng bằng văn bản cho tôi.

  • Xin nhắc lại, tất cả đều hoạt động tự động.

  • Bạn đã thấy tốc độ thực tế của mô hình đó trên thiết bị.

  • Bây giờ, hãy quay lại trang trình bày và xem cách hoạt động của tính năng này.

  • Như bạn có thể thấy, chúng tôi đã thay đổi một chút so với bản minh hoạ đầu tiên.

  • Trong trường hợp này, như bạn có thể thấy, tôi không cho mô hình biết về bảo tàng, cũng không cho biết về chi phí. Vì vậy, khi nhận được câu lệnh và các công cụ, mô hình sẽ cố gắng quyết định việc cần làm trước tiên.

  • Vì vậy, ở đây, Gemini quyết định rằng nó cần biết thêm về hành trình hiện tại của tôi, nên nó sẽ gọi getItinerary.

  • Sau khi nhận được câu trả lời, Gemini sẽ biết rằng có một chuyến tham quan bảo tàng trong hành trình này và đó có thể là chuyến tham quan mà cô ấy đang nói đến.

  • Vậy nên, hãy tìm thông tin chi tiết về sự kiện cụ thể đó.

  • Và trong số tất cả siêu dữ liệu khác của sự kiện cụ thể đó, hệ thống sẽ thấy giá trị là 25 đô la.

  • Nó hiểu, quay lại và hiểu rằng nó cần gọi hàm thêm chi phí.

  • Vì vậy, ở đây chúng ta thấy nhiều bước diễn ra lần lượt.

  • Victoria, bạn có thể chia sẻ sâu hơn về vấn đề đó không?

  • Vâng.

  • Những gì Jolanda vừa mô tả là một ví dụ cụ thể về bản minh hoạ mà cô ấy vừa trình bày, nhưng ở đây, tôi sẽ đưa ra một lớp trừu tượng, mang tính khái niệm hơn một chút.

  • Vì vậy, điều này sẽ xảy ra mỗi khi chúng ta thực hiện lệnh gọi hàm nhiều bước.

  • Trước tiên, đây là ứng dụng của chúng tôi và đó là mô hình Gemini Nano.

  • Và người dùng sẽ bắt đầu bằng cách gửi truy vấn và danh sách các công cụ có sẵn cho mô hình.

  • Sau đó, mô hình sẽ sử dụng thông tin này để quyết định nội dung cần gửi lại.

  • Sau đó, giả sử trong trường hợp đầu tiên, mô hình gửi lại một lệnh gọi công cụ, hãy gọi là Lệnh gọi công cụ 1.

  • Sau đó, ứng dụng sẽ thực thi lệnh gọi công cụ này và nhận được phản hồi của công cụ.

  • Sau đó, thông tin được gửi lại cho mô hình thực chất là toàn bộ nhật ký trò chuyện, bao gồm cả câu hỏi ban đầu, danh sách công cụ, lệnh gọi công cụ đầu tiên và phản hồi đầu tiên của công cụ.

  • Sau đó, mô hình lại sử dụng thông tin này để quyết định việc cần làm tiếp theo.

  • Vì vậy, quá trình này có thể lặp đi lặp lại cho đến khi chúng ta có kết quả cuối cùng.

  • Giả sử mô hình sẽ trả về lệnh gọi công cụ thứ n, sau đó ứng dụng sẽ trả về toàn bộ nhật ký trò chuyện một lần nữa.

  • Và khi mô hình hài lòng, tức là biết rằng truy vấn đã được thực hiện, mô hình sẽ gửi Phản hồi cuối cùng trở lại ứng dụng, sau đó ứng dụng có thể hiển thị trong giao diện người dùng rằng truy vấn đã hoàn tất.

  • Được rồi.

  • Bây giờ, tôi sẽ giải thích cách chúng ta triển khai việc này trong mã.

    /**
    * 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 { ... }
  • Bạn còn nhớ ví dụ về mã một bước chứ? Đây là mã nhiều bước, trông rất giống mã một bước. Điểm quan trọng nhất là phải viết KDoc thật rõ ràng.

  • Vì vậy, trong ví dụ này, hàm getItinerary nêu rõ rằng hàm này chỉ trả về thông tin tóm tắt cấp cao về các sự kiện.

  • Vì vậy, chúng tôi cũng đề cập rằng chúng ta cần gọi getEventDetails bằng eventId nếu muốn có thông tin chi tiết cụ thể hơn về chuyến đi.

  • Sau đó, getEventDetails cũng có chú thích KDoc, cho biết rằng lớp này có thể truy xuất thông tin chi tiết, chẳng hạn như giá vé và các ghi chú khác.

  • Vì vậy, bằng cách sử dụng chú thích, bằng cách mô tả rõ ràng chú thích, chúng ta có thể hướng dẫn mô hình biết cách các công cụ tương tác với nhau.

  • Được rồi.

  • Vì vậy, đây là cách chúng tôi xây dựng tác nhân để thực hiện toàn bộ vòng lặp này, vòng lặp tác nhân này.

    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
     }
    }
  • Ví dụ này rất giống với ví dụ về Một bước, chỉ có hai điểm bổ sung.

  • Đầu tiên, chúng ta cần đặt số lượt tối đa và trong ví dụ này, chúng ta chỉ đặt 5 lượt.

  • Vậy, công cụ này có chức năng gì?

  • Vì vậy, điều này giúp mô hình không lặp lại vô tận.

  • Giả sử nếu có một câu lệnh mơ hồ hoặc có thể nó liên tục gọi qua lại từ công cụ này sang công cụ khác rồi lại quay lại, thì nếu chúng ta đặt giá trị này, nó sẽ dừng lại, không lặp lại nữa.

  • Sau đó, chúng ta vẫn gọi cùng một hàm để chạy hàm đó, runSingleTurnAsync.

  • Vì vậy, mặc dù được gọi là một lượt, nhưng thực tế, một lượt có nghĩa là một truy vấn của người dùng.

  • Vì vậy, người dùng cuối mong đợi rằng tôi gửi một truy vấn, bạn trả về một câu trả lời, mà không thực sự quan tâm đến nhiều chi tiết như những gì đã xảy ra trong hệ thống, như số lần lặp lại, v.v.

  • Vì vậy, chúng ta có thể tiếp tục sử dụng cùng một hàm.

  • Ngoài ra, vì công cụ gọi công cụ nhiều bước mất nhiều thời gian hơn một chút, nên bạn cũng thấy trong bản minh hoạ rằng công cụ này thực sự cho biết hàm nào đang được gọi ngay bây giờ, hàm nào đang được thực thi ngay bây giờ và liệu hàm đó đã hoàn tất hay chưa.

  • Vì vậy, chúng ta muốn cho thấy tiến trình trực tiếp trong giao diện người dùng.

  • Vì vậy, thay vì chỉ thu thập câu trả lời dạng văn bản, chúng ta thực sự có thể hiển thị tên công cụ, event.name, bất cứ khi nào AgentEvent phát ra FunctionCall hoặc FunctionResponse, và khi hoàn tất, chúng ta có thể chỉ cần trả về TextMessage như trước.

Quản lý phiên trò chuyện nhiều lượt

  • Thật tuyệt!

  • Vì vậy, chúng ta gọi lệnh chạy không đồng bộ này là chạy không đồng bộ một lượt, tất nhiên, điều này giả định rằng cũng có một loại API khác, đúng không?

  • Vâng.

  • Vì vậy, tất nhiên chỉ thực hiện một bước là chưa đủ.

  • Chúng tôi muốn thực hiện nhiều bước.

  • Giờ hãy chuyển sang phiên nhiều lượt. Tôi nghĩ chúng ta có thể quay lại bản minh hoạ của tôi và tăng độ khó lên một chút.

  • Chúng ta đang nói về việc chúng ta có một chuyến tham quan bảo tàng, nhưng chúng ta cũng có chuyến du ngoạn Golden Gate Bay Cruise.

  • Vì vậy, tôi chỉ muốn biết liệu mình có đủ ngân sách cho chuyến đi đó hay không trước khi thực sự đặt vé.

  • Vậy hãy cùng đặt câu hỏi.

  • Xem nào.

  • Tôi đang nghĩ đến việc đưa nhóm của mình đi du thuyền Golden Gate Bay vào ngày mai và tôi nghĩ giá vé thực tế là khoảng 180 đô la.

  • Vậy nếu tôi làm như vậy, thì ngân sách còn lại của tôi thực sự là bao nhiêu?

  • Vậy thì nó đang làm gì?

  • Câu trả lời cho biết ngân sách còn lại của tôi.

  • để hiểu được hành trình hiện tại.

  • Nó có thông tin đó và có thể cho biết điều đó.

  • Nhưng sau đó, tôi cũng có thể đặt câu hỏi nối tiếp tại đây.

  • Được rồi, vậy là tốt.

  • Bạn có thể thêm khoản này vào chi phí của tôi không?

  • Được rồi, giờ thì chúng ta sẽ tiếp tục.

  • Ồ, cần phải có đơn vị tiền tệ, thật tuyệt, hiệu ứng minh hoạ.

  • Được rồi, vậy là đơn vị tiền tệ là đô la.

  • 180 USD.

  • Chúng ta hãy bắt đầu.

  • Đây là lần đầu tiên điều đó xảy ra, thật tuyệt.

  • Vậy thì nó đang làm gì?

  • Nó cho tôi biết rằng đã mất ba lượt và đã thêm vào chi phí, đồng thời cho phép tôi làm mới phiên này.

  • Vì vậy, tôi có một nút làm mới nhỏ ở trên cùng và nếu làm vậy, tôi sẽ xoá toàn bộ nhật ký và có thể bắt đầu lại.

  • Giờ thì chúng ta có thể quay lại trang trình bày.

  • Chúng ta đã có các bản minh hoạ hoạt động, tất cả đều hoạt động như mong muốn và vì vậy, tôi nghĩ chúng ta có thể quay lại mã.

  • Vâng.

  • Điều này thực sự rất tuyệt vời.

  • Bạn có thể thấy Jolanda không đề cập đến, cô ấy chỉ nói, ồ, đó là đô la, cô ấy không nói rằng đó là vé cho Golden Gate Bay Cruise.

  • Vậy làm sao mô hình biết được toàn bộ bối cảnh?

  • Vì vậy, điều này hơi khác so với những gì tôi đã cho bạn thấy trong hai đoạn mã trước.

    // 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()
  • Vì vậy, thay vì sử dụng AgentRunner và runSingleTurnAsync, chúng ta thực sự cần tạo một phiên.

  • Vì vậy, ở đây chúng ta gọi runner.getOrCreateSession bằng tripId.

  • Vậy tại sao chúng ta cần tạo một phiên?

  • Vì runSingleTurnAsync sẽ đặt lại bộ nhớ nên sẽ không có ngữ cảnh.

  • Chúng ta sẽ không thể trò chuyện nhiều lượt.

  • Vì vậy, khi bạn muốn có một cuộc trò chuyện nhiều lượt, chúng ta sẽ sử dụng phiên, phiên của tác nhân.

  • Sau đó, chúng ta truyền tripId.

  • Tại sao chúng ta cần làm vậy?

  • Vì bạn đã từng thấy ứng dụng Jetpacker, nên bạn biết rằng ứng dụng này quản lý nhiều chuyến đi.

  • Có một chuyến đi ADS, một chuyến đi Paris, có thể là chuyến đi Tokyo.

  • Vì vậy, chúng tôi muốn giữ ngữ cảnh trong chuyến đi.

  • Chúng ta không muốn gây rắc rối cho nhau, đúng không?

  • Vì vậy, thay vì gọi runSingleTurnAsync, chúng ta có runAsync.

  • Vì vậy, điều này là khác biệt.

  • Vì vậy, câu lệnh này được chạy trên phiên và sau đó chúng ta vẫn có cùng một câu lệnh.

  • Sau đó, chúng ta vẫn có thể phát hành AgentEvent, FunctionCall, FunctionResponse và TextMessage như trước đây, không có gì thay đổi.

  • Sau đó, khi Jolanda nhấp vào nút cuộc trò chuyện mới, nút làm mới, chúng ta có thể thực sự có session.delete.

  • Vâng.

  • Được rồi.

  • Chúng tôi đã làm tất cả những việc này bằng các API mới có khả năng tác nhân. Điều này thật tuyệt vời và sẽ mang lại hiệu quả đáng kinh ngạc cho nhiều trường hợp sử dụng của bạn.

  • Nhưng tất nhiên, chúng tôi luôn muốn giúp bạn thực hiện những việc nhanh chóng hoặc dễ dàng, đồng thời cũng muốn giúp bạn thực hiện những trường hợp phức tạp hơn.

  • Vì vậy, chúng tôi muốn đảm bảo rằng nếu cần, bạn vẫn có thể sử dụng các API truy cập trực tiếp để xây dựng phiên bản cấp thấp hơn của những tính năng này.

  • Vậy có lẽ Victoria có thể giải thích ý nghĩa của việc này và khi nào chúng ta sẽ quay lại cấp truy cập trực tiếp này?

Quy trình làm việc nâng cao dựa trên tác nhân và tích hợp công cụ

  • Chắc chắn rồi, Jolanda.

  • Có thể bạn đã quen thuộc với Prompt API, một phương thức truy cập trực tiếp.

  • Vì vậy, chúng tôi cũng hỗ trợ tính năng gọi hàm ở đây và có hai trường hợp sử dụng mà chúng tôi muốn dùng API này thay vì quy trình tác nhân dễ dàng hơn.

  • Trường hợp đầu tiên là một trường hợp sử dụng đơn giản, rất đơn giản.

  • Vì vậy, bạn nhớ rằng trong luồng tác nhân, chúng ta gửi toàn bộ cuộc trò chuyện, truy vấn, công cụ, lệnh gọi công cụ, phản hồi của công cụ, mọi thứ trở lại mô hình.

  • Và có thể có một số trường hợp sử dụng mà chúng ta không muốn làm như vậy, chẳng hạn như chúng ta chỉ muốn gửi câu lệnh.

  • Giả sử chúng ta muốn gửi một câu lệnh đến mô hình và nhận lại một lệnh gọi công cụ, nhưng chúng ta không muốn trả về lệnh gọi công cụ đó mà chỉ muốn gửi một câu lệnh khác đến mô hình. Khi đó, chúng ta có thể chỉnh sửa nhật ký trò chuyện, tức là chúng ta có toàn quyền kiểm soát nhật ký trò chuyện.

  • Tôi sẽ đưa ra một ví dụ cụ thể hơn ở đây.

    // 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()
    )
  • Giả sử chúng ta có một ứng dụng có thể thay đổi giao diện nền của thiết bị.

  • Giả sử người dùng gửi câu lệnh "Đổi nền thành cảnh hoàng hôn".

  • Sau đó, ứng dụng của chúng ta đã thực thi hàm này, nền được thay đổi thành cảnh hoàng hôn.

  • Sau đó, người dùng đổi ý và nói: "Ồ, không, ý tôi là bãi biển".

  • Và điều xảy ra ở đây là chúng ta không cần gửi trạng thái đầy đủ rằng thiết bị đã thay đổi nền thành cảnh hoàng hôn và giờ đây, thiết bị muốn thay đổi nền thành bãi biển.

  • Không, chúng ta không cần làm vậy.

  • Chúng ta chỉ cần gửi câu lệnh đầu tiên và câu lệnh thứ hai, vì câu lệnh đầu tiên chứa thông tin về nền.

  • Vì vậy, Gemini biết rằng, à, điều này có nghĩa là người dùng muốn thay đổi nền thành bãi biển.

  • Vì vậy, chúng ta có thể sử dụng Prompt API để làm việc này.

  • Chúng tôi có toàn quyền kiểm soát nhật ký, chúng tôi lưu token ngữ cảnh trên thiết bị vì bạn biết đấy, thiết bị có cửa sổ token hạn chế hơn và nhờ đó, chúng tôi có thể lưu nhiều token hơn.

  • Và trường hợp sử dụng thứ hai thực sự là một thái cực ngược lại.

  • Vì vậy, nếu chúng ta có các trường hợp sử dụng phức tạp mà API tác nhân chưa hỗ trợ, chẳng hạn như hệ thống đa tác nhân, trong đó có nhiều tác nhân phụ tương tác với nhau để giải quyết nhiệm vụ phức tạp, hoặc hệ thống kết hợp, trong đó chúng ta có cả mô hình trên thiết bị và mô hình đám mây, hoặc nếu chúng ta muốn có bộ nhớ tuỳ chỉnh và nén ngữ cảnh.

  • Vì vậy, điều này liên quan đến nhật ký, chẳng hạn như quyền kiểm soát hoàn toàn đối với nhật ký mà tôi đã đề cập trước đó.

  • Giả sử chúng ta muốn, vì cửa sổ ngữ cảnh bị giới hạn, nên chúng ta không muốn gửi mọi thứ trở lại mô hình, chúng ta chỉ muốn tóm tắt phần quan trọng nhất hoặc có thể là phần mới nhất, vì vậy chúng ta có toàn quyền kiểm soát việc này, sau đó chúng ta sẽ sử dụng API truy cập trực tiếp.

  • Và đối với trường hợp sử dụng này, chúng ta có thể sử dụng các khung như Bộ công cụ phát triển tác nhân (ADK) hoặc Crew, hoặc bạn cũng có thể tạo khung điều phối tuỳ chỉnh của riêng mình.

  • Được rồi.

  • Đó là tất cả những gì chúng tôi muốn chia sẻ về API tác nhân.

  • Tuy nhiên, có một điều nữa mà chúng tôi muốn đề cập đến.

  • Chúng ta đã nói về tác nhân trong ứng dụng, tức là bạn đang xây dựng trải nghiệm tác nhân của riêng mình trong ứng dụng của riêng bạn, nhưng chúng ta cũng đã nói về cách chúng ta sử dụng AppFunctions và lý do bạn nên sử dụng AppFunctions nếu việc duy nhất bạn làm là trong ứng dụng của riêng bạn.

  • Chúng tôi biết rằng các nhà phát triển như bạn không thực sự thích làm việc gì đó hai lần, phải không?

  • Chúng tôi không thích sao chép và dán mã, cũng không thích phải sao chép bất kỳ logic kinh doanh nào.

  • Vì vậy, với AppFunctions, thay vì chỉ cho phép tác nhân trong ứng dụng sử dụng các hàm này, chúng ta có thể thực hiện một thay đổi rất đơn giản để cung cấp các công cụ cho cả tác nhân hệ thống.

  • Vậy thì mã sẽ trông như thế nào?

  • Vâng.

  • Vì vậy, với AppFunction, chúng ta chỉ cần viết một lần, sau đó có thể hiển thị chúng cho các cấp độ lớp khác nhau theo ý muốn.

  • Bạn nhớ rằng trước đây chúng ta có ACCESS_LEVEL_SELF, chỉ hiển thị các hàm mà chúng ta đã viết cho tác nhân trong ứng dụng, nhưng chúng ta cũng có thể thay đổi cấp truy cập đó thành ACCESS_LEVEL_ANDROID_TRUSTED.

  • Và việc này sẽ hiển thị các hàm cho các tác nhân đáng tin cậy trong Hệ điều hành Android.

  • Vì vậy, bạn không cần phải viết lại các hàm, lệnh hoặc logic cơ sở dữ liệu.

  • AppFunctions đã đóng vai trò là tiếng nói của ứng dụng cho cả nội bộ và bên ngoài.

  • Chúng ta cũng muốn xem bản minh hoạ về điều đó, đúng không?

  • Được rồi, hãy quay lại điện thoại và trong bản minh hoạ cuối cùng, hãy thoát khỏi ứng dụng Jetpacker.

  • Vì vậy, chúng ta có thể thoát ra và truy cập vào Gemini.

  • Vì vậy, trong Gemini, chúng ta có thể gọi @Jetpacker.

  • Bây giờ, có một điều cần lưu ý là tính năng này chỉ được cung cấp dưới dạng bản thử nghiệm giới hạn, vì vậy, không phải ai cũng có thể sử dụng ngay hôm nay, nhưng bạn có thể thử nghiệm và dùng thử.

  • Vậy hãy xem Jetpacker có gì.

  • Chúng tôi nên làm gì?

  • Ồ, sáng nay tôi có uống cà phê đúng không?

  • Bạn có thể thêm một ly cà phê 7 đô la vào chi phí chuyến đi ADS không?

  • Chúng ta hãy bắt đầu.

  • Và bạn có thể thấy rằng nó kết nối với ứng dụng Jetpacker.

  • Vậy là xong.

  • Gemini đã thêm khoản chi phí đó cho tôi.

  • Vậy là chúng ta đã tìm hiểu cách sử dụng mọi thứ trong Jetpacker.

  • Điều tuyệt vời về Jetpacker là chúng ta cũng có thể bật chế độ trên máy bay.

  • Hôm nay, chúng tôi chưa thực sự cho các bạn thấy điều này, nhưng chúng ta có thể bật chế độ trên máy bay và mọi thứ bạn thấy trong bản minh hoạ, tôi muốn giới thiệu điều đó vì tôi muốn đảm bảo rằng tất cả các bạn đều tin tưởng tôi, vì vậy, hãy tắt Wi-Fi.

  • Có ai có ví dụ minh hoạ hay, một khoản chi phí mà tôi có thể thêm vào không?

  • Xem nào.

  • Có lẽ chúng ta có thể làm gì đó.

  • Ồ, thuê xe thì hay đấy nhỉ?

  • Được rồi.

  • Hãy thử nói "Thêm khoản chi phí 300 đô la cho việc thuê ô tô".

  • Được rồi, đã nhận dạng được.

  • Được rồi.

  • Thật tuyệt vời!

  • Được rồi, hãy quay lại trang trình bày.

  • Chúng ta đang nói về AppFunctions.

  • Tôi biết rằng chúng ta đã đề cập đến vấn đề này một cách rất, rất nhẹ nhàng.

  • Vì vậy, nếu bạn muốn biết thêm, thì cũng có một bài nói chuyện khác của Dan và Sarah, trong đó đi sâu hơn vào cách bạn có thể bật tính năng này, cách bạn có thể thử nghiệm tính năng này, công cụ chúng tôi có, v.v.

  • Hôm nay, chúng ta đã cùng tìm hiểu rất nhiều điều.

  • Chúng ta đã xem xét các mô hình Một bước, Nhiều bước, Nhiều lượt và cuối cùng là cách sử dụng AppFunction của hệ thống.

  • Giờ thì tôi nghĩ chúng ta có một vài mẹo trước khi kết thúc.

Các phương pháp hay nhất và kết luận

  • Vâng.

  • Vì vậy, hãy nhớ 3 mẹo này khi chúng ta bắt đầu xây dựng trải nghiệm dựa trên tác nhân.

  • Vì vậy, điều đầu tiên là tập trung vào bộ công cụ.

  • Vì vậy, hãy nhớ rằng các mô hình trên thiết bị có khả năng hạn chế do quy mô nhỏ hơn.

  • Vì vậy, thay vì đăng ký hàng chục công cụ, bạn nên chọn một số lượng công cụ nhỏ hơn.

  • Và như tôi đã nói trước đó, hãy viết nội dung mô tả KDoc rõ ràng vì đó là nội dung mà mô hình sử dụng để quyết định kết quả trả về.

  • Sau đó, chúng ta cũng nên đặt rào cản về số lượt tối đa.

  • Việc này cũng giúp mô hình không lặp lại vô tận nếu bị nhầm lẫn.

  • Điều này có nghĩa là chúng ta có thể làm hao pin của người dùng.

  • Vì vậy, nếu có tính năng này, chúng ta có thể tiết kiệm pin cho người dùng.

  • Cuối cùng, chúng ta cần kiểm tra xem tính năng Gọi hàm đã được hỗ trợ trên thiết bị hay chưa và triển khai một phương án dự phòng linh hoạt.

  • Ví dụ: nếu không được hỗ trợ, bạn có thể tham khảo các mô hình đám mây hoặc giao diện người dùng tiêu chuẩn.

  • Được rồi, tôi nghĩ chúng ta có thể kết thúc cuộc trò chuyện này bằng các tài liệu của chúng tôi.

  • Tôi sẽ cho các bạn xem mã QR một lần nữa.

  • Vui lòng xem qua.

  • Tôi nghĩ một trong những điều cần lưu ý là chúng tôi cũng có Android Skills cho việc này.

  • Vì vậy, bạn không cần phải đi sâu vào từng chi tiết nhỏ.

  • Chúng tôi có tài liệu đầy đủ và một Kỹ năng Android đi kèm với API tác nhân mới này.

  • Vì vậy, hãy xem xét vấn đề đó.

  • Hãy tải Bản dùng trước cho nhà phát triển AICore xuống để có thể thử nghiệm tất cả những tính năng này. Chúc bạn lập trình vui vẻ!

Mã

Đoạn mã

Đoạn mã Kotlin ở phút 09:53 trong video "Bản minh hoạ tác nhân Jetpacker và lệnh gọi hàm"
/**
 * 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...
}
Đoạn mã Kotlin ở phút 11:41 trong video "Xây dựng và triển khai trình chạy tác nhân AI"
val model = Generation.getClient()
val features = model.getAvailableFeatures()
if(FUNCTION_CALLING in features) {
  // Device is supported!
} else {
  // Graceful fallback
}
Đoạn mã Kotlin ở phút 12:07 trong video "Xây dựng và triển khai trình chạy tác nhân AI"
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 }

}
Đoạn mã Kotlin ở phút 17:23 trong video "Multi-step agent architecture and execution" (Kiến trúc và cách thực thi tác nhân nhiều bước)
/**
* 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 { ... }
Đoạn mã Kotlin ở phút 18:23 trong video "Kiến trúc và cách thực thi tác nhân nhiều bước"
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
 }
}
Đoạn mã Kotlin ở phút 22:13 trong video "Quản lý phiên trò chuyện nhiều lượt"
// 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()
Đoạn mã Kotlin ở phút 25:02 trong video "Advanced agentic workflows and tool integration" (Quy trình làm việc nâng cao dựa trên tác nhân và tích hợp công cụ)
// 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()
)