Hướng dẫn gọi hàm AI Edge cho Android

AI Edge Function Calling SDK (FC SDK) là một thư viện cho phép nhà phát triển sử dụng tính năng gọi hàm với các LLM trên thiết bị. Tính năng gọi hàm cho phép bạn kết nối các mô hình với các công cụ và API bên ngoài, giúp các mô hình gọi các hàm cụ thể bằng các tham số cần thiết để thực hiện các hành động trong thế giới thực.

Thay vì chỉ tạo văn bản, một LLM sử dụng FC SDK có thể tạo một lệnh gọi có cấu trúc đến một hàm thực thi một hành động, chẳng hạn như tìm kiếm thông tin mới nhất, đặt báo thức hoặc đặt chỗ.

Hướng dẫn này sẽ hướng dẫn bạn thực hiện một quy trình khởi động nhanh cơ bản để thêm LLM Inference API bằng FC SDK vào một ứng dụng Android. Hướng dẫn này tập trung vào việc thêm các chức năng gọi đến một LLM trên thiết bị. Để biết thêm thông tin về cách sử dụng LLM Inference API, hãy xem hướng dẫn về LLM Inference cho Android.

Bắt đầu nhanh

Hãy làm theo các bước sau để sử dụng FC SDK trong ứng dụng Android của bạn. Hướng dẫn bắt đầu nhanh này sử dụng LLM Inference API với Hammer 2.1 (1,5 tỷ). LLM Inference API được tối ưu hoá cho các thiết bị Android cao cấp, chẳng hạn như Pixel 8 và Samsung S23 trở lên, đồng thời không hỗ trợ trình mô phỏng thiết bị một cách đáng tin cậy.

Thêm phần phụ thuộc

FC SDK sử dụng thư viện com.google.ai.edge.localagents:localagents-fc và LLM Inference API sử dụng thư viện com.google.mediapipe:tasks-genai. Thêm cả hai phần phụ thuộc vào tệp build.gradle của ứng dụng Android:

dependencies {
    implementation 'com.google.mediapipe:tasks-genai:0.10.24'
    implementation 'com.google.ai.edge.localagents:localagents-fc:0.1.0'
}

Đối với các thiết bị chạy Android 12 (API 31) trở lên, hãy thêm phần phụ thuộc thư viện OpenCL gốc. Để biết thêm thông tin, hãy xem tài liệu về thẻ uses-native-library.

Thêm các thẻ uses-native-library sau vào tệp AndroidManifest.xml:

<uses-native-library android:name="libOpenCL.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-car.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-pixel.so" android:required="false"/>

Tải mô hình xuống

Tải Hammer 1B xuống ở định dạng được lượng tử hoá 8 bit từ Hugging Face. Để biết thêm thông tin về các mô hình hiện có, hãy xem Tài liệu về mô hình.

Đẩy nội dung của thư mục hammer2.1_1.5b_q8_ekv4096.task vào thiết bị Android.

$ adb shell rm -r /data/local/tmp/llm/ # Remove any previously loaded models
$ adb shell mkdir -p /data/local/tmp/llm/
$ adb push hammer2.1_1.5b_q8_ekv4096.task /data/local/tmp/llm/hammer2.1_1.5b_q8_ekv4096.task

Khai báo định nghĩa hàm

Xác định các hàm sẽ được cung cấp cho mô hình. Để minh hoạ quy trình này, hướng dẫn bắt đầu nhanh này bao gồm 2 hàm dưới dạng các phương thức tĩnh trả về các phản hồi được mã hoá cứng. Một cách triển khai thực tế hơn là xác định các hàm gọi API REST hoặc truy xuất thông tin từ cơ sở dữ liệu.

Sau đây là cách xác định các hàm getWeathergetTime:

class ToolsForLlm {
    public static String getWeather(String location) {
        return "Cloudy, 56°F";
    }

    public static String getTime(String timezone) {
        return "7:00 PM " + timezone;
    }

    private ToolsForLlm() {}
}

Dùng FunctionDeclaration để mô tả từng hàm, đặt tên và mô tả cho từng hàm, đồng thời chỉ định các loại. Việc này giúp mô hình biết các hàm làm gì và thời điểm thực hiện lệnh gọi hàm.

var getWeather = FunctionDeclaration.newBuilder()
    .setName("getWeather")
    .setDescription("Returns the weather conditions at a location.")
    .setParameters(
        Schema.newBuilder()
            .setType(Type.OBJECT)
            .putProperties(
                "location",
                Schema.newBuilder()
                    .setType(Type.STRING)
                    .setDescription("The location for the weather report.")
                    .build())
            .build())
    .build();
var getTime = FunctionDeclaration.newBuilder()
    .setName("getTime")
    .setDescription("Returns the current time in the given timezone.")

    .setParameters(
        Schema.newBuilder()
            .setType(Type.OBJECT)
            .putProperties(
                "timezone",
                Schema.newBuilder()
                    .setType(Type.STRING)
                    .setDescription("The timezone to get the time from.")
                    .build())
            .build())
    .build();

Thêm khai báo hàm vào đối tượng Tool:

var tool = Tool.newBuilder()
    .addFunctionDeclarations(getWeather)
    .addFunctionDeclarations(getTime)
    .build();

Tạo phần phụ trợ suy luận

Tạo một phần phụ trợ suy luận bằng LLM Inference API và truyền cho phần phụ trợ đó một đối tượng trình định dạng cho mô hình của bạn. Trình định dạng FC SDK (ModelFormatter) vừa đóng vai trò là trình định dạng vừa là trình phân tích cú pháp. Vì hướng dẫn bắt đầu nhanh này sử dụng Gemma-3 1B, nên chúng ta sẽ sử dụng GemmaFormatter:

var llmInferenceOptions = LlmInferenceOptions.builder()
    .setModelPath(modelFile.getAbsolutePath())
    .build();
var llmInference = LlmInference.createFromOptions(context, llmInferenceOptions);
var llmInferenceBackend = new llmInferenceBackend(llmInference, new GemmaFormatter());

Để biết thêm thông tin, hãy xem các lựa chọn về cấu hình Suy luận LLM.

Tạo thực thể cho mô hình

Sử dụng đối tượng GenerativeModel để kết nối phần phụ trợ suy luận, lời nhắc hệ thống và các công cụ. Chúng ta đã có phần phụ trợ và các công cụ suy luận, vì vậy, chúng ta chỉ cần tạo lời nhắc hệ thống:

var systemInstruction = Content.newBuilder()
      .setRole("system")
      .addParts(Part.newBuilder().setText("You are a helpful assistant."))
      .build();

Tạo thực thể cho mô hình bằng GenerativeModel:

var generativeModel = new GenerativeModel(
    llmInferenceBackend,
    systemInstruction,
    List.of(tool),
)

Bắt đầu phiên trò chuyện

Để đơn giản, hướng dẫn bắt đầu nhanh này sẽ bắt đầu một phiên trò chuyện duy nhất. Bạn cũng có thể tạo nhiều phiên hoạt động độc lập.

Sử dụng phiên bản mới của GenerativeModel, hãy bắt đầu một phiên trò chuyện:

var chat = generativeModel.startChat();

Gửi câu lệnh cho mô hình thông qua phiên trò chuyện bằng phương thức sendMessage:

var response = chat.sendMessage("How's the weather in San Francisco?");

Phân tích cú pháp câu trả lời của mô hình

Sau khi truyền một câu lệnh cho mô hình, ứng dụng phải kiểm tra phản hồi để xác định xem có nên thực hiện một lệnh gọi hàm hay xuất văn bản bằng ngôn ngữ tự nhiên.

// Extract the model's message from the response.
var message = response.getCandidates(0).getContent().getParts(0);

// If the message contains a function call, execute the function.
if (message.hasFunctionCall()) {
  var functionCall = message.getFunctionCall();
  var args = functionCall.getArgs().getFieldsMap();
  var result = null;

  // Call the appropriate function.
  switch (functionCall.getName()) {
    case "getWeather":
      result = ToolsForLlm.getWeather(args.get("location").getStringValue());
      break;
    case "getTime":
      result = ToolsForLlm.getWeather(args.get("timezone").getStringValue());
      break;
    default:
      throw new Exception("Function does not exist:" + functionCall.getName());
  }
  // Return the result of the function call to the model.
  var functionResponse =
      FunctionResponse.newBuilder()
          .setName(functionCall.getName())
          .setResponse(
              Struct.newBuilder()
                  .putFields("result", Value.newBuilder().setStringValue(result).build()))
          .build();
  var functionResponseContent = Content.newBuilder()
        .setRole("user")
        .addParts(Part.newBuilder().setFunctionResponse(functionResponse))
        .build();
  var response = chat.sendMessage(functionResponseContent);
} else if (message.hasText()) {
  Log.i(message.getText());
}

Mã mẫu là một cách triển khai quá đơn giản. Để biết thêm thông tin về cách một ứng dụng có thể kiểm tra các câu trả lời của mô hình, hãy xem phần Định dạng và phân tích cú pháp.

Cách hoạt động

Phần này cung cấp thông tin chi tiết hơn về các khái niệm và thành phần cốt lõi của Function Calling SDK cho Android.

Mô hình

Function Calling SDK yêu cầu một mô hình có trình định dạng và trình phân tích cú pháp. FC SDK chứa một trình định dạng và trình phân tích cú pháp tích hợp cho các mô hình sau:

  • Gemma: sử dụng GemmaFormatter.
  • Llama: sử dụng LlamaFormatter.
  • Búa: sử dụng HammerFormatter.

Để sử dụng một mô hình khác với FC SDK, bạn phải phát triển trình định dạng và trình phân tích cú pháp của riêng mình, tương thích với LLM Inference API.

Định dạng và phân tích cú pháp

Một phần quan trọng của tính năng hỗ trợ gọi hàm là định dạng của câu lệnh và phân tích cú pháp đầu ra của mô hình. Mặc dù đây là hai quy trình riêng biệt, nhưng FC SDK xử lý cả việc định dạng và phân tích cú pháp bằng giao diện ModelFormatter.

Trình định dạng chịu trách nhiệm chuyển đổi các khai báo hàm có cấu trúc thành văn bản, định dạng các phản hồi của hàm và chèn mã thông báo để cho biết điểm bắt đầu và kết thúc của các lượt trò chuyện, cũng như vai trò của các lượt đó (ví dụ: "người dùng", "mô hình").

Trình phân tích cú pháp chịu trách nhiệm phát hiện xem phản hồi của mô hình có chứa một lệnh gọi hàm hay không. Nếu phát hiện thấy một lệnh gọi hàm, trình phân tích cú pháp sẽ phân tích cú pháp lệnh gọi đó thành một loại dữ liệu có cấu trúc. Nếu không, mô hình sẽ coi văn bản đó là một câu trả lời bằng ngôn ngữ tự nhiên.

Giải mã bị hạn chế

Giải mã có ràng buộc là một kỹ thuật hướng dẫn quá trình tạo đầu ra của LLM để đảm bảo đầu ra tuân thủ một định dạng có cấu trúc được xác định trước, chẳng hạn như các đối tượng JSON hoặc lệnh gọi hàm Python. Bằng cách thực thi các ràng buộc này, mô hình sẽ định dạng đầu ra theo cách phù hợp với các hàm được xác định trước và các loại tham số tương ứng.

Để bật tính năng giải mã có ràng buộc, hãy xác định các ràng buộc trong một đối tượng ConstraintOptions và gọi phương thức enableConstraint của một thực thể ChatSession. Khi được bật, ràng buộc này sẽ giới hạn phản hồi chỉ bao gồm các công cụ được liên kết với GenerativeModel.

Ví dụ sau đây minh hoạ cách định cấu hình tính năng giải mã có ràng buộc để hạn chế phản hồi đối với các lệnh gọi công cụ. Thao tác này sẽ giới hạn lệnh gọi công cụ bắt đầu bằng tiền tố ```tool_code\n và kết thúc bằng hậu tố \n```.

ConstraintOptions constraintOptions = ConstraintOptions.newBuilder()
.setToolCallOnly( ConstraintOptions.ToolCallOnly.newBuilder()
.setConstraintPrefix("```tool_code\n")
  .setConstraintSuffix("\n```"))
.build(); chatSession.enableConstraint(constraintOptions);

Để tắt ràng buộc đang hoạt động trong cùng một phiên, hãy sử dụng phương thức disableConstraint:

chatSession.disableConstraint();