Guia de chamada de função de IA Edge para Android

O SDK de chamada de função (FC SDK) da IA de ponta é uma biblioteca que permite aos desenvolvedores usar a chamada de função com LLMs no dispositivo. Com a chamada de função, é possível conectar modelos a ferramentas e APIs externas, permitindo que eles chamem funções específicas com os parâmetros necessários para executar ações no mundo real.

Em vez de apenas gerar texto, um LLM que usa o SDK do FC pode gerar uma chamada estruturada para uma função que executa uma ação, como pesquisar informações atualizadas, definir alarmes ou fazer reservas.

Este guia mostra um início rápido básico para adicionar a API LLM Inference com o SDK do FC a um aplicativo Android. Este guia se concentra em adicionar recursos de chamada de função a um LLM no dispositivo. Para mais informações sobre como usar a API LLM Inference, consulte o guia de inferência de LLM para Android.

Guia de início rápido

Siga estas etapas para usar o SDK do FC no seu aplicativo Android. Este início rápido usa a API LLM Inference com o Hammer 2.1 (1,5 bilhão). A API LLM Inference é otimizada para dispositivos Android de alta qualidade, como Pixel 8 e Samsung S23 ou versões mais recentes, e não oferece suporte confiável a emuladores de dispositivos.

Adicionar dependências

O SDK do FC usa a biblioteca com.google.ai.edge.localagents:localagents-fc, e a API LLM Inference usa a biblioteca com.google.mediapipe:tasks-genai. Adicione as duas dependências ao arquivo build.gradle do seu app Android:

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

Para dispositivos com Android 12 (API 31) ou mais recente, adicione a dependência da biblioteca nativa OpenCL. Para mais informações, consulte a documentação sobre a tag uses-native-library.

Adicione as seguintes tags uses-native-library ao arquivo 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"/>

Fazer o download de um modelo

Faça o download do Hammer 1B em um formato quantizado de 8 bits no Hugging Face. Para mais informações sobre os modelos disponíveis, consulte a documentação de modelos.

Envie o conteúdo da pasta hammer2.1_1.5b_q8_ekv4096.task para o dispositivo 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

Declarar definições de função

Defina as funções que serão disponibilizadas para o modelo. Para ilustrar o processo, este guia de início rápido inclui duas funções como métodos estáticos que retornam respostas codificadas. Uma implementação mais prática definiria funções que chamam uma API REST ou recuperam informações de um banco de dados.

O seguinte define as funções getWeather e 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() {}
}

Use FunctionDeclaration para descrever cada função, a cada uma um nome e uma descrição, e especificando os tipos. Isso informa ao modelo o que as funções fazem e quando fazer chamadas de função.

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

Adicione as declarações de função a um objeto Tool:

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

Criar o back-end de inferência

Crie um back-end de inferência usando a API LLM Inference e transmita um objeto formatador para seu modelo. O formatador do SDK do FC (ModelFormatter) funciona como um formatador e um analisador. Como este guia de início rápido usa o Gemma-3 1B, vamos usar GemmaFormatter:

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

Para mais informações, consulte as opções de configuração de inferência de LLM.

Instanciar o modelo

Use o objeto GenerativeModel para conectar o back-end de inferência, o comando do sistema e as ferramentas. Já temos o back-end e as ferramentas de inferência. Portanto, só precisamos criar o comando do sistema:

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

Instancie o modelo com GenerativeModel:

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

Iniciar uma sessão de chat

Para simplificar, este guia de início rápido inicia uma única sessão de chat. Também é possível criar várias sessões independentes.

Usando a nova instância de GenerativeModel, inicie uma sessão de conversa:

var chat = generativeModel.startChat();

Envie comandos ao modelo pela sessão de chat usando o método sendMessage:

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

Analisar a resposta do modelo

Depois de transmitir um comando ao modelo, o aplicativo precisa examinar a resposta para determinar se deve fazer uma chamada de função ou gerar texto em linguagem natural.

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

O código de exemplo é uma implementação muito simplificada. Para mais informações sobre como um aplicativo pode examinar as respostas do modelo, consulte Formatação e análise.

Como funciona

Esta seção fornece informações mais detalhadas sobre os principais conceitos e componentes do SDK Function Calling para Android.

Modelos

O SDK de chamadas de função exige um modelo com um formatador e um analisador. O SDK do FC contém um formatador e um analisador integrados para os seguintes modelos:

  • Gemma: use o GemmaFormatter.
  • Llama: use o LlamaFormatter.
  • Martelo: use o HammerFormatter.

Para usar um modelo diferente com o SDK do FC, é necessário desenvolver seu próprio formatador e analisador compatíveis com a API LLM Inference.

Formatação e análise

Uma parte fundamental do suporte à chamada de função é a formatação de comandos e a análise da saída do modelo. Embora sejam dois processos separados, o SDK do FC processa a formatação e a análise com a interface ModelFormatter.

O formatador é responsável por converter as declarações de função estruturadas em texto, formatar respostas de função e inserir tokens para indicar o início e o fim das rodadas de conversa, bem como as funções dessas rodadas (por exemplo, "usuário", "modelo").

O analisador é responsável por detectar se a resposta do modelo contém uma chamada de função. Se o analisador detectar uma chamada de função, ele a analisará em um tipo de dados estruturado. Caso contrário, ele trata o texto como uma resposta de linguagem natural.

Decodificação restrita

A decodificação restrita é uma técnica que orienta a geração de saída dos LLMs para garantir que ela siga um formato estruturado predefinido, como objetos JSON ou chamadas de função Python. Ao aplicar essas restrições, o modelo formata as saídas de uma maneira que se alinha às funções predefinidas e aos tipos de parâmetros correspondentes.

Para ativar a decodificação restrita, defina as restrições em um objeto ConstraintOptions e invoque o método enableConstraint de uma instância ChatSession. Quando ativada, essa restrição limita a resposta para incluir apenas as ferramentas associadas ao GenerativeModel.

O exemplo a seguir demonstra como configurar a decodificação restrita para restringir a resposta a chamadas de função. Ele restringe a chamada de função para começar com o prefixo ```tool_code\n e terminar com o sufixo \n```.

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

Para desativar a restrição ativa na mesma sessão, use o método disableConstraint:

chatSession.disableConstraint();