💡はじめに
はじめまして。ニフティ株式会社のLinです。
台湾出身のモバイルアプリエンジニアとして、社内で「マイ ニフティ」のAndroidおよびiOS版の開発を担当しております。
2023年末に、GoogleがGemini APIをリリースしたことで、モバイルアプリでもAIサービスとの連携が容易になりました。
今回はGemini AIアプリの作り方をご紹介いたします。
❇️ Gemini AI
Gemini AIはGoogleが開発したマルチモーダルAIモデルで、2023年12月6日(米国時間)に初リリースされました。
マルチモーダルのため、テキストだけでなく、動画や画像、音声など様々な種類のデータを入力、出力として扱えることができます。
モデルは下記3種類があります:
Gemini Ultra | 非常に複雑で幅広いタスクに対して最も高いパフォーマンスを提供する、Gemini最大のモデル。 |
Gemini Pro | 強力なパフォーマンスを維持しつつ、コストやレイテンシがバランスよく最適化された、中規模のモデル。 |
Gemini Nano | デバイス上で実行用に設計された、最も効率的なモデル。 低メモリデバイス用にNano-1(18億パラメータ)、高メモリデバイス用にNano-2(32.5億パラメータ)が提供されます。 |
2024年現在、Gemini Proは1分間に60回のリクエストまで無料です。今回はGemini ProのAPIを使いましょう。
💻 事前準備
- APIキー
- Android Studio Preview
- Jellyfishから
Gemini API Starter
が追加されました。
- Jellyfishから
⏱️ 3分でAIアプリを作りましょう
Android Studio Preview(Jellyfish)を起動して、Gemini API Starter
を選択します。
プロジェクト情報を入力します。
先ほど作ったGemini APIキーを入力します。
- 入力したAPIキーは
local.properties
でのapikey
に保存され、BuildConfig
を経由して使われています。
ビルドが完了すると、デフォルトアプリが作成されました。
SDKの依存関係は、以下のようにモジュール(アプリレベル)のGradle設定ファイル(例:<project>/<app-module>/build.gradle.kts
)に設定されています。
1 2 3 4 5 6 7 8 |
... dependencies { ... // Gemini AI implementation("com.google.ai.client.generativeai:generativeai:0.1.2") } |
👨🏻💻 詳しい使い方
Gemini AIを呼び出すには、モデルとプロンプトの設置が必要です。
モデルは下記2種類を使えます:
- gemini-pro:文字のみの入力。
startChat
経由でチャット機能を実装できます。history
でチャット履歴の設置と確認が可能です。
- gemini-pro-vision:画像と文字の入力。
- 画像の入力は必須です。
文字のみの入力に対してはエラーが発生します。 - 画像のフォーマットはBitmapとなります。
- 画像の入力は必須です。
また、temperatureなどのコンフィグ設定と安全性設定もできます:
1 2 3 4 5 6 7 8 9 10 |
// 安全性設定 val harassment = SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.ONLY_HIGH) val hateSpeech = SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.MEDIUM_AND_ABOVE) // コンフィグ設定 val config = generationConfig { temperature = 0.99f // 生成するテキストのランダム性を制御 topK = 50 // 生成に使用するトップkトークンを制御 topP = 0.99f // 生成に使用するトークンの累積確率を制御 } |
プロンプトについては、モデルによって内容が異なります。
gemini-proの場合は以下の例をご参考にしてください:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// モデルを設置します val generativeModel = GenerativeModel( // 文字入力のみ modelName = "gemini-pro", apiKey = BuildConfig.apiKey ) val viewModel = GeminiViewModel(generativeModel) // ViewModelでプロンプトを設定します class GeminiViewModel( private val generativeModel: GenerativeModel ) : ViewModel() { private val _uiState: MutableStateFlow<UiState> = MutableStateFlow(UiState.Initial) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun respond(inputText: String) { _uiState.value = UiState.Loading // プロンプトを設置します // 文字(必須) val prompt = inputText viewModelScope.launch { try { val response = generativeModel.generateContent(prompt) response.text?.let { outputContent -> _uiState.value = UiState.Success(outputContent) } } catch (e: Exception) { _uiState.value = UiState.Error(e.localizedMessage ?: "") } } } } |
gemini-pro-visionの場合は以下の例をご参考にしてください:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// モデルを設置します val generativeModel = GenerativeModel( // 画像+文字入力 modelName = "gemini-pro-vision", apiKey = BuildConfig.apiKey ) val viewModel = GeminiViewModel(generativeModel) // ViewModelでプロンプトを設定します class GeminiViewModel( private val generativeModel: GenerativeModel ) : ViewModel() { private val _uiState: MutableStateFlow<UiState> = MutableStateFlow(UiState.Initial) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun respond(inputText: String, inputImageList: List<Bitmap>) { _uiState.value = UiState.Loading // プロンプトを設置します val prompt = content { // 画像(必須) inputImageList.forEach { image -> image(image = image) } // 文字 text(text = inputText) } viewModelScope.launch { try { val response = generativeModel.generateContent(prompt) response.text?.let { outputContent -> _uiState.value = UiState.Success(outputContent) } } catch (e: Exception) { _uiState.value = UiState.Error(e.localizedMessage ?: "") } } } } |
🚀 DIの導入
HiltなどのDI(依存性注入)ライブラリも導入したい場合、以下の例をご参考にしてください:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// GeminiModule.kt @Module @InstallIn(SingletonComponent::class) object GeminiModule { // 必要によるモデルのコンフィグ設定と安全性設定を追加します private val harassment = SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.ONLY_HIGH) private val hateSpeech = SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.MEDIUM_AND_ABOVE) private val config = generationConfig { temperature = 0.95f topK = 50 topP = 0.9f } @Provides @Singleton @GeminiPro fun provideGemini(): GenerativeModel { return GenerativeModel( modelName = "gemini-pro", apiKey = BuildConfig.apiKey, safetySettings = listOf( harassment, hateSpeech ), generationConfig = config ) } @Provides @Singleton @GeminiProVision fun provideGeminiVision(): GenerativeModel { return GenerativeModel( modelName = "gemini-pro-vision", apiKey = BuildConfig.apiKey, safetySettings = listOf( harassment, hateSpeech, SafetySetting(HarmCategory.DANGEROUS_CONTENT, BlockThreshold.ONLY_HIGH), SafetySetting(HarmCategory.SEXUALLY_EXPLICIT, BlockThreshold.MEDIUM_AND_ABOVE) ) ) } @Provides @Singleton fun provideGeminiRepository( @GeminiPro geminiProModel: GenerativeModel, @GeminiProVision geminiProVisionModel: GenerativeModel ): GeminiRepository = GeminiRepositoryImpl(geminiProModel, geminiProVisionModel) @Provides fun provideApplicationContext(application: Application): Context { return application.applicationContext } } @Qualifier @Retention(AnnotationRetention.BINARY) annotation class GeminiPro @Qualifier @Retention(AnnotationRetention.BINARY) annotation class GeminiProVision |
1 2 3 4 5 6 |
// GeminiRepository.kt interface GeminiRepository { fun getGeminiProModel(): GenerativeModel fun getGeminiProVisionModel(): GenerativeModel } |
1 2 3 4 5 6 7 8 9 |
// GeminiRepositoryImpl.kt class GeminiRepositoryImpl @Inject constructor( @GeminiPro private val geminiProModel: GenerativeModel, @GeminiProVision private val geminiProVisionModel: GenerativeModel ) : GeminiRepository { override fun getGeminiProModel(): GenerativeModel = geminiProModel override fun getGeminiProVisionModel(): GenerativeModel = geminiProVisionModel } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// GeminiViewModel.kt @HiltViewModel class GeminiViewModel @Inject constructor( geminiRepository: GeminiRepository ) : ViewModel() { // 文字のみのモデル private val geminiProModel = geminiRepository.getGeminiProModel() private val chat = geminiProModel.startChat(history = emptyList()) // チャット機能 // 画像+文字のモデル private val geminiProVisionModel = geminiRepository.getGeminiProModel() ... } |
これで、ViewModelで一度GeminiRepositoryを注入すると、2つのモデルを利用できるようになりました。
📱 試してみよう!
メッセージ画面を追加すると、以下のようなアプリを作れます。
下記サンプルは自由にクローンして試せます。
今後のモバイルアプリでは、ますますAIサービスが導入されます。
ぜひAI系のアプリ作りを体験しましょう!