0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity Androidアプリ内でローカルLLM APIを実行する方法

0
Posted at

この記事では、Unity Androidアプリの中でローカルLLMを動かし、アプリ内APIとして呼び出す方法を整理します。

ここでいう「ローカルLLM API」は、クラウドのOpenAI APIやGemini APIを呼ぶのではなく、Android端末内に保存したLLMモデルを使って、Unityアプリからローカルに推論を実行する構成です。


この記事で作るもの

完成イメージは次のような構成です。

Unity Android App
        │
        │ C#から呼び出し
        ▼
Android Native Plugin / AAR
        │
        │ JNI
        ▼
llama.cpp / Local LLM Runtime
        │
        ▼
GGUF Model
Application.persistentDataPath/models/model.gguf

ユーザー視点では、次のように動きます。

初回起動
  ↓
ローカルLLMモデルが端末にあるか確認
  ↓
なければモデルをダウンロード
  ↓
端末内に保存
  ↓
UnityアプリからローカルLLMを実行
  ↓
2回目以降はダウンロードせず端末内モデルを利用

前提

この記事では、以下の構成を想定します。

  • Unity
  • Androidビルド
  • Android実機
  • llama.cpp
  • GGUFモデル
  • Android Native Plugin / AAR
  • Unity C# から AndroidJavaObject で呼び出し

クラウドAPIは使いません。
推論はAndroid端末上で完結します。


なぜHTTP APIサーバーではなくNative Plugin方式にするのか

ローカルLLMを「API」として呼ぶ方法には、大きく2つあります。

方式A:アプリ内でHTTPサーバーを起動する

Unity C#
  ↓ HTTP
127.0.0.1:8080
  ↓
llama-server

PCやMacでは、llama-server を立ち上げて http://127.0.0.1:8080/v1/chat/completions に投げる構成が簡単です。

しかし、Androidアプリ内でHTTPサーバーを同梱・起動するのは、次の理由でやや面倒です。

  • プロセス管理が必要
  • ポート管理が必要
  • Androidのライフサイクルと相性が悪い
  • バックグラウンド制限を考慮する必要がある
  • セキュリティ上、不要なローカルポートを開けたくない
  • Unityからのデバッグは楽だが、本番実装では複雑になりやすい

方式B:UnityからNative Pluginを直接呼ぶ

Unity C#
  ↓ AndroidJavaObject
Kotlin / Java
  ↓ JNI
C++ wrapper
  ↓
llama.cpp

Androidアプリ内で完結させるなら、こちらの方が自然です。

この記事では、方式B:Native Plugin方式 を中心に説明します。


全体構成

最終的には、以下のようなファイル構成になります。

UnityProject/
  Assets/
    Scripts/
      LocalLlmManager.cs
      ModelDownloader.cs
      LocalLlmBridge.cs

    Plugins/
      Android/
        localllm.aar

AndroidNativePlugin/
  app or library module
    src/main/java/com/example/localllm/LocalLlm.kt
    src/main/cpp/native-lib.cpp
    src/main/cpp/CMakeLists.txt
    src/main/cpp/llama.cpp/...

Unity側は、モデルの確認・ダウンロード・UI制御を担当します。

Android Native Plugin側は、llama.cppの初期化・推論・解放を担当します。


モデルファイルの保存場所

初回ダウンロードしたモデルは、Unityの Application.persistentDataPath に保存します。

string modelPath = Path.Combine(
    Application.persistentDataPath,
    "models",
    "model.gguf"
);

persistentDataPath は、アプリの実行間で保持したいファイルを保存するための場所です。

Androidでは、アプリを更新しても通常は残ります。
ただし、ユーザーがアプリデータを削除した場合は消えます。


モデル選定

Androidで動かす場合、最初は小さいモデルから始めるのが重要です。

端末RAM おすすめ
4GB 0.5B〜1B Q4
6GB 1B〜1.5B Q4
8GB 1B〜2B Q4
12GB以上 3B〜4B Q4も検討可能

MVPでは、以下がおすすめです。

モデル: 1B〜2B
形式: GGUF
量子化: Q4
用途: 短文生成 / NPC会話 / FAQ / 口調変換

スマホ上で7B以上を動かすのは、メモリ・発熱・速度の面でかなり厳しいです。


実装の流れ

実装は次の順番で進めます。

1. Unityでモデルファイルの存在確認
2. なければ初回ダウンロード
3. SHA256で破損チェック
4. Android Native Pluginを初期化
5. モデルパスをNative Pluginに渡す
6. Unity UIからプロンプトを送る
7. Native側でllama.cpp推論
8. 結果をUnity C#に返す
9. アプリ終了時に解放

1. Unity側:モデルを初回ダウンロードする

まず、モデルファイルが存在するか確認し、なければダウンロードする処理を作ります。

ModelDownloader.cs を作成します。

using System;
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class ModelDownloader : MonoBehaviour
{
    [SerializeField] private string modelUrl =
        "https://example.com/models/model.gguf";

    [SerializeField] private string modelFileName =
        "model.gguf";

    public string ModelPath =>
        Path.Combine(Application.persistentDataPath, "models", modelFileName);

    public IEnumerator EnsureModelDownloaded(
        Action<string> onReady,
        Action<float> onProgress,
        Action<string> onError
    )
    {
        string modelDir = Path.GetDirectoryName(ModelPath);

        if (!Directory.Exists(modelDir))
        {
            Directory.CreateDirectory(modelDir);
        }

        if (File.Exists(ModelPath) && new FileInfo(ModelPath).Length > 0)
        {
            onReady?.Invoke(ModelPath);
            yield break;
        }

        string tempPath = ModelPath + ".tmp";

        if (File.Exists(tempPath))
        {
            File.Delete(tempPath);
        }

        using (UnityWebRequest request = UnityWebRequest.Get(modelUrl))
        {
            request.downloadHandler = new DownloadHandlerFile(tempPath);
            request.SendWebRequest();

            while (!request.isDone)
            {
                onProgress?.Invoke(request.downloadProgress);
                yield return null;
            }

            if (request.result != UnityWebRequest.Result.Success)
            {
                if (File.Exists(tempPath))
                {
                    File.Delete(tempPath);
                }

                onError?.Invoke(request.error);
                yield break;
            }
        }

        if (File.Exists(ModelPath))
        {
            File.Delete(ModelPath);
        }

        File.Move(tempPath, ModelPath);

        onReady?.Invoke(ModelPath);
    }
}

ポイントは、DownloadHandlerFile を使っていることです。

LLMモデルは数百MB〜数GBになるため、DownloadHandlerBuffer でメモリに載せるのは危険です。
必ずファイルへ直接保存します。


2. SHA256でモデルの破損チェックをする

大きなモデルファイルは、途中でダウンロードに失敗したり破損したりする可能性があります。

本番では、SHA256チェックを入れます。

using System.IO;
using System.Security.Cryptography;
using System.Text;

public static class HashUtil
{
    public static string ComputeSha256(string filePath)
    {
        using var sha256 = SHA256.Create();
        using var stream = File.OpenRead(filePath);

        byte[] hash = sha256.ComputeHash(stream);

        var sb = new StringBuilder();

        foreach (byte b in hash)
        {
            sb.Append(b.ToString("x2"));
        }

        return sb.ToString();
    }
}

使い方です。

string hash = HashUtil.ComputeSha256(modelPath);

if (hash != expectedSha256)
{
    File.Delete(modelPath);
    Debug.LogError("モデルファイルが破損しています。再ダウンロードしてください。");
}

3. manifest.jsonでモデルを管理する

モデルURLやSHA256をアプリに直書きすると、後から差し替えが面倒です。

本番では、サーバー側に manifest.json を置くのがおすすめです。

{
  "version": "1.0.0",
  "modelName": "model.gguf",
  "url": "https://example.com/models/model.gguf",
  "sha256": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "size": 858993459
}

アプリ側の流れは以下です。

1. manifest.jsonを取得
2. ローカルに保存済みのversionと比較
3. バージョンが違えば再ダウンロード
4. SHA256チェック
5. 成功したらモデルを差し替え

これにより、アプリを更新せずにモデルを差し替えられます。


4. Android Native Pluginを作る

Unity C#から直接C++のllama.cppを呼ぶのではなく、Android Native Pluginを作ります。

構成は以下です。

Unity C#
  ↓ AndroidJavaObject
Kotlin / Java
  ↓ JNI
C++
  ↓ llama.cpp

Android StudioでAndroid Libraryモジュールを作成し、AARとしてビルドします。


Kotlin側のラッパー

LocalLlm.kt を作成します。

package com.example.localllm

class LocalLlm {
    external fun init(modelPath: String): Boolean
    external fun generate(prompt: String, maxTokens: Int): String
    external fun release()

    companion object {
        init {
            System.loadLibrary("localllm")
        }
    }
}

Unityからは、この LocalLlm クラスを呼び出します。


C++側のJNIラッパー

native-lib.cpp を作成します。

実際にはllama.cppのAPIを使って、モデルロード・コンテキスト作成・トークン生成を実装します。

ここでは構造が分かるように、簡略化した形で書きます。

#include <jni.h>
#include <string>
#include <mutex>

// 実際の実装では llama.cpp のヘッダーをincludeします。
// #include "llama.h"

static std::mutex g_mutex;

// static llama_model* g_model = nullptr;
// static llama_context* g_ctx = nullptr;

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_localllm_LocalLlm_init(
    JNIEnv* env,
    jobject thiz,
    jstring model_path
) {
    const char* path = env->GetStringUTFChars(model_path, nullptr);

    std::lock_guard<std::mutex> lock(g_mutex);

    // 疑似コード:
    //
    // llama_backend_init();
    //
    // llama_model_params model_params = llama_model_default_params();
    // g_model = llama_model_load_from_file(path, model_params);
    //
    // if (!g_model) {
    //     env->ReleaseStringUTFChars(model_path, path);
    //     return JNI_FALSE;
    // }
    //
    // llama_context_params ctx_params = llama_context_default_params();
    // ctx_params.n_ctx = 2048;
    // ctx_params.n_threads = 4;
    //
    // g_ctx = llama_init_from_model(g_model, ctx_params);
    //
    // if (!g_ctx) {
    //     llama_model_free(g_model);
    //     g_model = nullptr;
    //     env->ReleaseStringUTFChars(model_path, path);
    //     return JNI_FALSE;
    // }

    env->ReleaseStringUTFChars(model_path, path);
    return JNI_TRUE;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_localllm_LocalLlm_generate(
    JNIEnv* env,
    jobject thiz,
    jstring prompt,
    jint max_tokens
) {
    const char* input = env->GetStringUTFChars(prompt, nullptr);

    std::lock_guard<std::mutex> lock(g_mutex);

    // 疑似コード:
    //
    // 1. promptをtokenize
    // 2. llama_decodeでpromptを評価
    // 3. logitsから次トークンをsample
    // 4. max_tokensまで繰り返す
    // 5. tokenを文字列に戻す

    std::string output = "ここにLLMの生成結果が入ります。";

    env->ReleaseStringUTFChars(prompt, input);

    return env->NewStringUTF(output.c_str());
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_localllm_LocalLlm_release(
    JNIEnv* env,
    jobject thiz
) {
    std::lock_guard<std::mutex> lock(g_mutex);

    // if (g_ctx) {
    //     llama_free(g_ctx);
    //     g_ctx = nullptr;
    // }
    //
    // if (g_model) {
    //     llama_model_free(g_model);
    //     g_model = nullptr;
    // }
    //
    // llama_backend_free();
}

上記は骨組みです。
実際のトークン生成処理は、llama.cppのサンプルや llama-cli の実装を参考にして組み込みます。


CMakeLists.txtの例

Android Native側では、CMakeでllama.cppと自分のラッパーをビルドします。

cmake_minimum_required(VERSION 3.22.1)

project(localllm)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(llama.cpp)

add_library(
    localllm
    SHARED
    native-lib.cpp
)

target_link_libraries(
    localllm
    llama
    android
    log
)

実際のllama.cppの構成やバージョンによって、リンクするターゲット名は変わる場合があります。
ビルドエラーが出る場合は、利用しているllama.cppのCMake構成を確認してください。


5. AARをUnityに配置する

Android StudioでAARをビルドしたら、Unityプロジェクトに配置します。

Assets/Plugins/Android/localllm.aar

Unityでは、Android向けのJava/KotlinプラグインやAARを Assets/Plugins/Android に配置して利用します。


6. Unity C#からNative Pluginを呼ぶ

Unity側で、Android Pluginを呼び出すブリッジクラスを作ります。

LocalLlmBridge.cs です。

using System;
using UnityEngine;

public class LocalLlmBridge : IDisposable
{
#if UNITY_ANDROID && !UNITY_EDITOR
    private AndroidJavaObject llm;
#endif

    public LocalLlmBridge()
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        llm = new AndroidJavaObject("com.example.localllm.LocalLlm");
#endif
    }

    public bool Init(string modelPath)
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        return llm.Call<bool>("init", modelPath);
#else
        Debug.Log($"Mock Init: {modelPath}");
        return true;
#endif
    }

    public string Generate(string prompt, int maxTokens)
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        return llm.Call<string>("generate", prompt, maxTokens);
#else
        return $"Mock response: {prompt}";
#endif
    }

    public void Dispose()
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        llm?.Call("release");
        llm?.Dispose();
#endif
    }
}

Unity Editor上では実機のNative Pluginを呼べないため、モック応答を返すようにしています。


7. Unity側の管理クラスを作る

モデルダウンロード、初期化、推論をまとめる LocalLlmManager.cs を作ります。

using System;
using System.Threading.Tasks;
using UnityEngine;

public class LocalLlmManager : MonoBehaviour
{
    [SerializeField] private ModelDownloader modelDownloader;

    private LocalLlmBridge llm;
    private bool isInitialized;
    private bool isGenerating;

    public event Action<float> OnDownloadProgress;
    public event Action<string> OnStatusChanged;
    public event Action<string> OnError;

    private async void Start()
    {
        await InitializeAsync();
    }

    public async Task InitializeAsync()
    {
        OnStatusChanged?.Invoke("モデル確認中...");

        string modelPath = null;
        string error = null;

        await RunCoroutineAsTask(modelDownloader.EnsureModelDownloaded(
            onReady: path => modelPath = path,
            onProgress: progress => OnDownloadProgress?.Invoke(progress),
            onError: e => error = e
        ));

        if (!string.IsNullOrEmpty(error))
        {
            OnError?.Invoke($"モデルのダウンロードに失敗しました: {error}");
            return;
        }

        OnStatusChanged?.Invoke("ローカルLLM初期化中...");

        llm = new LocalLlmBridge();

        bool ok = await Task.Run(() => llm.Init(modelPath));

        if (!ok)
        {
            OnError?.Invoke("ローカルLLMの初期化に失敗しました。");
            return;
        }

        isInitialized = true;
        OnStatusChanged?.Invoke("準備完了");
    }

    public async Task<string> GenerateAsync(string prompt, int maxTokens = 128)
    {
        if (!isInitialized)
        {
            throw new InvalidOperationException("LLM is not initialized.");
        }

        if (isGenerating)
        {
            throw new InvalidOperationException("LLM is already generating.");
        }

        isGenerating = true;

        try
        {
            string result = await Task.Run(() =>
            {
                return llm.Generate(prompt, maxTokens);
            });

            return result;
        }
        finally
        {
            isGenerating = false;
        }
    }

    private Task RunCoroutineAsTask(System.Collections.IEnumerator coroutine)
    {
        var tcs = new TaskCompletionSource<bool>();
        StartCoroutine(WrapCoroutine(coroutine, tcs));
        return tcs.Task;
    }

    private System.Collections.IEnumerator WrapCoroutine(
        System.Collections.IEnumerator coroutine,
        TaskCompletionSource<bool> tcs
    )
    {
        yield return coroutine;
        tcs.SetResult(true);
    }

    private void OnDestroy()
    {
        llm?.Dispose();
    }
}

重要なのは、推論を Task.Run でバックグラウンド実行している点です。

LLM推論をUnityのメインスレッドで実行すると、UIが固まります。


8. UIから呼び出す

簡単なUI用スクリプトです。

using UnityEngine;
using UnityEngine.UI;

public class LocalLlmUI : MonoBehaviour
{
    [SerializeField] private LocalLlmManager llmManager;
    [SerializeField] private InputField inputField;
    [SerializeField] private Text outputText;
    [SerializeField] private Slider progressSlider;

    private void Awake()
    {
        llmManager.OnStatusChanged += status =>
        {
            outputText.text = status;
        };

        llmManager.OnDownloadProgress += progress =>
        {
            progressSlider.value = progress;
        };

        llmManager.OnError += error =>
        {
            outputText.text = error;
        };
    }

    public async void OnClickGenerate()
    {
        string prompt = inputField.text;

        outputText.text = "生成中...";

        try
        {
            string result = await llmManager.GenerateAsync(prompt, 128);
            outputText.text = result;
        }
        catch (System.Exception e)
        {
            outputText.text = e.Message;
        }
    }
}

9. プロンプト設計

スマホ上のローカルLLMでは、長文生成よりも短文生成に寄せるのが現実的です。

例えば、ゲーム内NPCなら以下のようにします。

あなたはファンタジーRPGの村人です。
次の状況に対して、30文字以内の自然な日本語セリフを1つだけ返してください。

状況: プレイヤーが初めて村に来た

長く自由に生成させるより、制約を強くした方が速度も品質も安定します。


10. Androidビルド設定

Unity側では、以下を確認します。

Platform: Android
Scripting Backend: IL2CPP
Target Architectures: ARM64
Internet Access: Require
Minify: 最初は無効推奨

ローカルLLM推論は32bit端末では現実的ではないため、基本的にARM64を対象にします。

モデルを初回ダウンロードするため、インターネット権限も必要です。

<uses-permission android:name="android.permission.INTERNET" />

UnityのPlayer Settingsで Internet AccessRequire にすれば、通常は付与されます。


11. メモリと発熱対策

Android端末では、メモリ・発熱・バッテリーが大きな制約になります。

以下のように設計します。

max_tokens: 64〜256
コンテキスト長: 1024〜2048
モデル: 1B〜2B Q4
同時生成: 1リクエストのみ
履歴: 必要最小限
生成結果: キャッシュする

特にゲーム用途では、毎回LLMに投げるのではなく、生成結果をキャッシュする設計が重要です。


12. アプリ内APIとして設計する

Unity側から見たローカルLLMを、アプリ内APIとして整理すると使いやすくなります。

例えば、以下のようなインターフェースを作ります。

public interface ILocalLlmApi
{
    Task InitializeAsync();
    Task<string> GenerateAsync(string prompt, int maxTokens);
}

実装クラスです。

public class LocalLlmApi : ILocalLlmApi
{
    private readonly LocalLlmManager manager;

    public LocalLlmApi(LocalLlmManager manager)
    {
        this.manager = manager;
    }

    public Task InitializeAsync()
    {
        return manager.InitializeAsync();
    }

    public Task<string> GenerateAsync(string prompt, int maxTokens)
    {
        return manager.GenerateAsync(prompt, maxTokens);
    }
}

こうしておくと、将来的に以下の切り替えがしやすくなります。

開発中: PC上のllama-serverへHTTP接続
本番: Android端末内のNative Pluginで推論
失敗時: クラウドAPIへフォールバック

13. 開発中はHTTP接続でモックする

Android Native Pluginの開発は大変なので、最初から全部作るより、Unity EditorではPC上の llama-server にHTTP接続する形でゲーム側の実装を進めると楽です。

Unity Editor
  ↓ HTTP
Mac / PC の llama-server

その後、Android実機ではNative Pluginに差し替えます。

Unity Android
  ↓ AndroidJavaObject
Android Native Plugin
  ↓
llama.cpp

これにより、UIやゲームロジックの開発を先に進められます。


UnityからOpenAI互換APIを叩くHTTP版

開発用として、PCの llama-server に投げるコード例も載せます。

using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

public class OpenAICompatibleClient : MonoBehaviour
{
    [SerializeField] private string endpoint =
        "http://127.0.0.1:8080/v1/chat/completions";

    public IEnumerator Generate(string prompt, System.Action<string> onResult)
    {
        string json = $@"
        {{
            ""model"": ""local-model"",
            ""messages"": [
                {{
                    ""role"": ""user"",
                    ""content"": ""{EscapeJson(prompt)}""
                }}
            ],
            ""max_tokens"": 128
        }}";

        using var request = new UnityWebRequest(endpoint, "POST");
        byte[] bodyRaw = Encoding.UTF8.GetBytes(json);

        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader("Content-Type", "application/json");

        yield return request.SendWebRequest();

        if (request.result != UnityWebRequest.Result.Success)
        {
            onResult?.Invoke(request.error);
            yield break;
        }

        onResult?.Invoke(request.downloadHandler.text);
    }

    private string EscapeJson(string s)
    {
        return s.Replace("\\", "\\\\").Replace("\"", "\\\"");
    }
}

これはAndroid端末内推論ではなく、開発中のモックとして便利です。

本番ではNative Plugin経由の LocalLlmBridge に切り替えます。


14. HTTPサーバー方式をAndroid内で使う場合

どうしても「Android端末内でOpenAI互換HTTP APIを立てたい」場合は、以下の構成になります。

Unity Android
  ↓ HTTP
127.0.0.1:ポート
  ↓
アプリ内HTTPサーバー
  ↓
llama.cpp

ただし、この方式は実装難度が上がります。

必要になるもの:

- Android内HTTPサーバー
- ポート管理
- サーバー起動・停止処理
- Androidライフサイクル対応
- セキュリティ対策
- バックグラウンド制限への対応

Unityアプリ内で自分だけが使うなら、HTTPサーバー方式より Native Pluginを直接呼ぶ方式 の方がシンプルです。


15. エラー対策

モデルが見つからない

原因:
- ダウンロードされていない
- パスが違う
- persistentDataPathが想定と違う

確認:

Debug.Log(Application.persistentDataPath);
Debug.Log(File.Exists(modelPath));

初期化に失敗する

原因:
- GGUFが壊れている
- llama.cppがそのモデル形式に対応していない
- メモリ不足
- ABIが違う

対策:

- SHA256チェックを入れる
- llama.cppを更新する
- 小さいモデルに変える
- ARM64のみでビルドする

UnityのUIが固まる

原因:
- メインスレッドで推論している

対策:

Task.Runでバックグラウンド実行する
生成中ボタンを無効化する
max_tokensを小さくする

Android実機でだけ落ちる

原因:
- Native PluginのABI不一致
- .soがAARに含まれていない
- ProGuard / Minifyの影響
- メモリ不足

対策:

- ARM64のみ対象にする
- AARの中にlib/arm64-v8a/*.soがあるか確認
- 最初はMinifyを無効にする
- logcatを見る

16. 最小MVP

最初に作るべきMVPは以下です。

1. 1B Q4 GGUFモデルを用意
2. Unityで初回ダウンロード
3. persistentDataPathに保存
4. AAR経由でNative Pluginを呼ぶ
5. llama.cppでモデル初期化
6. 固定プロンプトで短文生成
7. 結果をUnity UIに表示

プロンプト例:

あなたはゲーム内の村人です。
30文字以内で、プレイヤーを歓迎するセリフを1つ返してください。

出力例:

ようこそ、旅の人。ゆっくりしていってね。

まずはこのレベルから始めるのが現実的です。


まとめ

Unity Androidアプリ内でローカルLLM APIを実行するなら、基本構成は次のようになります。

Unity C#
  ↓
AndroidJavaObject
  ↓
Kotlin / Java AAR
  ↓
JNI
  ↓
llama.cpp
  ↓
GGUFモデル

重要なポイントは以下です。

  • モデルは Application.persistentDataPath に保存する
  • 初回利用時にダウンロードする
  • DownloadHandlerFile で直接ファイル保存する
  • SHA256で破損チェックする
  • Native Plugin / AARでllama.cppを呼ぶ
  • 推論は必ずバックグラウンドスレッドで実行する
  • Androidでは1B〜2B Q4程度から始める
  • 本番ではmanifest.jsonでモデルを管理する
  • 開発中はPC上のllama-serverにHTTP接続してモックすると楽

ローカルLLMをUnity Androidに組み込むのは簡単ではありませんが、構成を分ければ実装できます。

モデル管理: Unity C#
推論エンジン: llama.cpp
橋渡し: Android Native Plugin
UI制御: Unity

この役割分担にすると、スマホ上で完全オフラインのAI会話・NPC応答・FAQ・短文生成などを実装できます。


参考リンク

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?