Android 向け AI Edge 関数呼び出しガイド

AI Edge Function Calling SDK(FC SDK)は、デベロッパーがオンデバイス LLM で関数呼び出しを使用できるようにするライブラリです。関数呼び出しを使用すると、モデルを外部ツールや API に接続し、モデルが現実世界のアクションを実行するために必要なパラメータを使用して特定の関数を呼び出せるようにできます。

FC SDK を使用する LLM は、テキストを生成するだけでなく、最新情報の検索、アラームの設定、予約などのアクションを実行する関数への構造化呼び出しを生成できます。

このガイドでは、FC SDK を使用して LLM 推論 API を Android アプリに追加する基本的なクイックスタートについて説明します。このガイドでは、デバイス上の LLM に関数呼び出し機能を追加することに焦点を当てます。LLM Inference API の使用方法については、Android 向け LLM 推論ガイドをご覧ください。

クイックスタート

Android アプリで FC SDK を使用する手順は次のとおりです。このクイックスタートでは、Hammer 2.1(1.5B)で LLM 推論 API を使用します。LLM 推論 API は、Google Pixel 8 や Samsung S23 以降などのハイエンド Android デバイス向けに最適化されており、デバイス エミュレータは確実にサポートしていません。

依存関係を追加する

FC SDK は com.google.ai.edge.localagents:localagents-fc ライブラリを使用し、LLM 推論 API は com.google.mediapipe:tasks-genai ライブラリを使用します。両方の依存関係を Android アプリの build.gradle ファイルに追加します。

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

Android 12(API 31)以降を搭載したデバイスの場合は、ネイティブ OpenCL ライブラリの依存関係を追加します。詳細については、uses-native-library タグに関するドキュメントをご覧ください。

AndroidManifest.xml ファイルに次の uses-native-library タグを追加します。

<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"/>

モデルのダウンロード

Hugging Face から 8 ビットの量子化形式で Hammer 1B をダウンロードします。使用可能なモデルの詳細については、モデルのドキュメントをご覧ください。

hammer2.1_1.5b_q8_ekv4096.task フォルダの内容を 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

関数の定義を宣言する

モデルで使用できる関数を定義します。このプロセスを説明するために、このクイックスタートには、ハードコードされたレスポンスを返す静的メソッドとして 2 つの関数があります。より実用的な実装では、REST API を呼び出す関数やデータベースから情報を取得する関数を定義します。

次に、getWeather 関数と getTime 関数を定義します。

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() {}
}

FunctionDeclaration を使用して各関数を記述し、名前と説明を指定して型を指定します。これにより、関数の機能と関数呼び出しのタイミングがモデルに通知されます。

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();

関数宣言を Tool オブジェクトに追加します。

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

推論バックエンドを作成する

LLM Inference API を使用して推論バックエンドを作成し、モデルのフォーマッタ オブジェクトを渡します。FC SDK フォーマッタ(ModelFormatter)は、フォーマッタとパーサーの両方の役割を果たします。このクイックスタートでは Gemma-3 1B を使用するため、GemmaFormatter を使用します。

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

詳細については、LLM 推論の構成オプションをご覧ください。

モデルをインスタンス化する

GenerativeModel オブジェクトを使用して、推論バックエンド、システム プロンプト、ツールを接続します。推論バックエンドとツールはすでにあるので、システム プロンプトのみを作成する必要があります。

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

GenerativeModel を使用してモデルをインスタンス化します。

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

チャット セッションを開始する

わかりやすくするため、このクイックスタートでは 1 つのチャット セッションを開始します。複数の独立したセッションを作成することもできます。

GenerativeModel の新しいインスタンスを使用して、チャット セッションを開始します。

var chat = generativeModel.startChat();

sendMessage メソッドを使用して、チャット セッションを介してモデルにプロンプトを送信します。

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

モデルのレスポンスを解析する

プロンプトをモデルに渡した後、アプリケーションはレスポンスを調べて、関数呼び出しを行うか自然言語テキストを出力するかを判断する必要があります。

// 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 response = chat.sendMessage(functionResponse);
} else if (message.hasText()) {
  Log.i(message.getText());
}

サンプルコードは、過度に簡素化された実装です。アプリケーションがモデルのレスポンスを検査する方法の詳細については、フォーマットと解析をご覧ください。

仕組み

このセクションでは、Function Calling SDK for Android のコアコンセプトとコンポーネントについて詳しく説明します。

モデル

Function Calling SDK では、フォーマッタとパーサーを含むモデルが必要です。FC SDK には、次のモデル用のフォーマッタとパーサーが組み込まれています。

  • Gemma: GemmaFormatter を使用します。
  • Llama: LlamaFormatter を使用します。
  • ハンマー: HammerFormatter を使用します。

FC SDK で別のモデルを使用するには、LLM 推論 API と互換性のある独自のフォーマッタとパーサーを開発する必要があります。

フォーマットと解析

関数呼び出しのサポートの重要な部分は、プロンプトのフォーマットとモデル出力の解析です。これらは 2 つの別々のプロセスですが、FC SDK は ModelFormatter インターフェースでフォーマットと解析の両方を処理します。

フォーマッタは、構造化された関数宣言をテキストに変換し、関数レスポンスをフォーマットし、会話ターンの開始と終了、およびターンのロール(「ユーザー」、「モデル」など)を示すトークンを挿入します。

パーサーは、モデル レスポンスに関数呼び出しが含まれているかどうかを検出します。パーサーは、関数呼び出しを検出すると、それを構造化データ型に解析します。それ以外の場合は、テキストを自然言語レスポンスとして扱います。

制約付きデコード

制約付きデコードは、LLM の出力生成をガイドして、事前定義された構造化形式(JSON オブジェクトや Python 関数呼び出しなど)に準拠させる手法です。これらの制約を適用することで、モデルは事前定義された関数とそれに対応するパラメータ型に合わせて出力をフォーマットします。

制約付きデコードを有効にするには、ConstraintOptions オブジェクトで制約を定義し、ChatSession インスタンスの enableConstraint メソッドを呼び出します。この制約を有効にすると、レスポンスに GenerativeModel に関連付けられたツールのみが含まれるように制限されます。

次の例は、制約付きデコードを構成して、ツール呼び出しへのレスポンスを制限する方法を示しています。これにより、ツール呼び出しは接頭辞 ```tool_code\n で始まり、接尾辞 \n``` で終わるように制約されます。

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

同じセッション内でアクティブな制約を無効にするには、disableConstraint メソッドを使用します。

chatSession.disableConstraint();