見出し画像

[生成AI]共感型カウンセラーボットを作成する - Androidアプリ編

割引あり

今回は、Kotlinと生成AIを使って、共感型カウンセリングアプリを作成する方法をご紹介します。人工知能(AI)を活用したアプリ開発は、今や大きな注目を集めています。この記事を通して、AIアプリ開発の世界に触れてみませんか?

このチュートリアルでは、Android Studioを使ってAndroidアプリを作成します。最新のUI開発ツールであるJetpack Composeを用いて、LINE風のチャットアプリのUIを実装していきます。Jetpack Composeは、宣言的UIの概念を採用しており、より直感的かつ効率的にUIを構築できます。プログラミング経験者の方は、この新しいツールに興味を持っていただけるでしょう。

また、バックエンドでは生成AIのAPIを利用して、ユーザーとの対話を実現します。生成AIは学習した大量のデータをもとに、自然な会話を生成することができます。これを活用することで、共感型のカウンセリングアプリを作成し、ユーザーにとって親身で寄り添うようなエクスペリエンスを提供することを目指します。

これから作るアプリのイメージはこちらです。LINE風チャットアプリのUIを採用して、生成AIと対話を進める中で問題解決に導きます。

図1 完成イメージ

免責事項

本記事で提供するプログラムコードや手順は、あくまで参考例です。開発環境などの違いにより、記事通りに動作しない可能性があります。特に、Android Studio、Kotlin、Jetpack Compose、生成AIの仕様はアップデートされるため、UIの変更や設定方法の変更などにより、記事の内容と異なる部分が出てくる可能性があります。本記事は、2023年3月時点の情報に基づいて作成されています。

また、本記事で提供するプログラムを実行した結果、発生した損害や問題について、著者は一切の責任を負いません。各種サービスの利用料金などは、読者ご自身の責任で管理してください。特に、生成AIのAPIは、使用量に応じて課金されるサービスです。APIの呼び出し回数やデータ転送量によっては、高額な料金が発生する可能性がありますので、ご注意ください。

本記事で作成するアプリケーションは、あくまでサンプルコードです。より少ない手順で生成AIを使ったアプリを動作させることを目的として作成されています。実際に運用する場合は、セキュリティ面や性能面でさらなる改善が必要です。必要に応じて、より強固な認証方式を検討してください。

各種のアカウントやアクセスキーの管理は、読者ご自身の責任で行ってください。アクセスキーを紛失したり、第三者に漏洩したりすることがないよう、十分にご注意ください。

以上の点を予めご了承の上、本記事をご活用ください。本記事が、読者の皆様のアプリケーション開発の一助となれば幸いです。


記事概要

本記事は、プログラミング初心者向けに、Kotlinと生成AIを使った共感型カウンセリングアプリの作成方法を解説します。記事は3部構成になっており、各部で以下の内容を扱います。

第1部では、Androidアプリ開発の基礎を学びます。Android Studioの概要や新規プロジェクトの作成方法を説明した後、Jetpack Composeを使ったユーザーインターフェースの作成方法を解説します。また、シミュレーターを使ったコードのデバッグ方法も紹介します。

第2部では、生成AIとの連携方法を説明します。APIの概念を簡単に説明した後、生成AIアカウント(OpenAI、 Anthropic、 Google Cloud Platforms)の作成とAPIキーの取得方法を解説します。次に、生成AI(ChatGPT、Claude、Gemini)との連携を管理するクラスと、実際の連携を実装する方法を示します。これにより、AIカウンセラーとしての機能を実現します。

第3部では、カウンセリング結果を読み上げる機能の実装方法を説明します。AndroidのTTS(Text-to-Speech)機能を活用し、テキストを音声に変換して読み上げます。TTSを管理するクラスの作成方法と、生成AIからのレスポンスを読み上げる方法を解説します。

本記事を通して、プログラミング初心者の方でもKotlinと生成AIを使ったAndroidアプリ開発の一連の流れを理解することができます。共感型カウンセリングアプリの作成を通して、生成AIの活用方法やアプリ開発の基礎を学ぶことができるでしょう。

第1部 Androidアプリ開発を開始する

Androidアプリ開発の世界へようこそ!このセクションでは、Googleが提供する統合開発環境(IDE)であるAndroid Studioを使って、Androidアプリ開発の第一歩を踏み出します。

Android Studioについて

Android Studioは、Android アプリ開発のための統合開発環境(IDE)です。コーディング、デバッグ、テスト、そしてアプリのビルドとデプロイを行うための強力なツールセットを提供しています。Android Studioは、IntelliJ IDEAをベースにしており、Androidアプリ開発に特化した機能を追加しています。

Android Studioを使用することで、効率的かつ生産的にアプリ開発を進めることができます。コードエディタは、コード補完、構文ハイライト、リアルタイムのエラーチェックなどの機能を備えており、開発者の作業をサポートします。また、ビジュアルエディタを使用して、アプリのユーザーインターフェース(UI)を直感的に設計することもできます。

Android Studioを起動する

Android Studioを起動するには、まずAndroid Studioをダウンロードしてインストールする必要があります。Android Studioの公式ウェブサイトにアクセスし、自分のオペレーティングシステムに合わせたバージョンをダウンロードしてください。本稿と同じ環境で作業したい方は、原稿執筆時の最新バージョンである「Android Studio Iguana | 2023.2.x」を選ぶことをお勧めします。バージョンが異なると、UIの配置や一部の設定項目が変更されている可能性があり、本稿の手順通りに進めない場合があることにご注意ください。

インストールが完了したら、Android Studioを起動します。起動時に、「Welcome to Android Studio」画面が表示されます。ここから、新規プロジェクトの作成や既存のプロジェクトを開くことができます。

新規プロジェクトの作成する

図2 Welcome to Android Studio

Android Studioを起動すると、「Welcome to Android Studio」ウインドウが表示されます。このウインドウでは、「新規プロジェクトの作成」、「既存のプロジェクトを開く」、または「Gitレポジトリをクローンする」を選択できます。ここで、「New Project」を選んで、新規プロジェクトを作成します。(「File」メニューから「New」を選択し、「New Project」をクリックする方法もあります。)

図3 プロジェクトテンプレート

プロジェクトのテンプレートを選択する画面が表示されます。ここで、開発したいアプリケーションタイプに合わせてテンプレートを選択します。例えば、Androidアプリを開発する場合は、「Phone and Tablet / Empty Activity」を選択し、「Next」をクリックします。(メニューが紫のものが、Jetpack Composeのテンプレートです。旧来のXMLレイアウトを使うときは、緑のテンプレートを使います。)

図4 プロジェクトの設定

次は、プロジェクトの設定を行います。Nameフィールドは「CounselingApp」を、Package nameフィールドは「com.example.counselingapp」を入力して下さい。こうすることで、これから示すコードをそのままコピペするだけで、動作確認ができるようになります。(もし所有しているドメインがある方は、com.mydomain.counselingappとしてください。)

Save locationは、新規プロジェクトを保存する場所を選びます。著者はユーザーディレクトリ直下に「AndroidStudio」フォルダーを作成し、そこにすべてのプロジェクトを保存しています。この方法は、ターミナルからのアクセスが容易であり、プロジェクト管理を簡単にするため、初心者にも推奨されます。

Minimum SDKは、アプリがサポートする最小のAndroidバージョンを指定し、より多くのデバイスとの互換性を保ちつつ、必要な機能へのアクセスを決定します。ここでは、広範なデバイスサポートと現代的なAndroid機能のバランスを取るために初期値「API 24」を推奨します。Bundle configuration languageでは、プロジェクトの依存関係や設定を管理するために、推奨される「Kotlin DSL」を選んでください。これは、Kotlinに慣れ親しんでいる開発者にとってより直感的な設定管理を可能にします。「Finish」をクリックすると、新しいプロジェクトが作成されます。

図5 新しいプロジェクト

Android Studioで新しいプロジェクトが開かれたら、まずはその画面構成について簡単に理解しましょう。Android Studioの画面は大きく分けて3つの主要なエリアから構成されています。

プロジェクトビューアー: 画面の左側に位置するプロジェクトビューアーからは、プロジェクト内のファイルとフォルダに簡単にアクセスできます。ここでは、アプリのソースコードやリソースファイル、Gradleスクリプトなど、開発に必要なすべてのファイルを見つけることができます。特定のファイルを探している場合、このビューアーを利用して迅速にファイルを見つけることが可能です。

エディタエリア: 画面の中央には、エディタエリアがあります。ここでは、選択されたファイルの内容を表示し、編集することができます。コードの記述、リソースの編集、XMLレイアウトのビジュアル編集など、アプリ開発に必要なほとんどの作業がこのエリアで行われます。また、エディタエリアは複数のタブを開くことができるため、同時に複数のファイルを編集することも可能です。

ツールウィンドウ: 画面の下部や右側には、様々なツールウィンドウが配置されています。これには、Logcat(ログ出力ビューア)、Build(ビルド出力ビューア)、TODO(タスクリスト)などがあります。これらのツールウィンドウを使用することで、アプリのデバッグ、ビルドプロセスの監視、プロジェクト内のタスクの管理など、開発作業を効率的に行うことができます。

Android Studioの画面構成を理解することは、開発プロセスの効率化につながります。各エリアの機能と役割を覚えておくことで、よりスムーズに開発作業を進めることが可能になります。

 次のセクションでは、最新のUI開発ツールであるJetpack Composeについて説明します。

Jetpack Composeについて

Jetpack Composeは、AndroidのモダンなUI開発ツールキットです。宣言的なUIの構築を可能にし、より簡潔で読みやすいコードでUIを記述できます。Jetpack Composeは、Kotlin言語の特徴を活かして設計されており、Kotlinの言語機能を最大限に活用できます。

Jetpack Composeでは、UIの構成要素をComposable関数として定義します。Composable関数は、UIの一部を表現し、再利用可能なコンポーネントとして機能します。これらのComposable関数を組み合わせることで、複雑なUIを構築できます。

Jetpack Composeは、リアクティブプログラミングの原則に基づいています。UIの状態が変更されると、自動的にUIが更新されます。これにより、UIとデータの同期を簡単に保つことができ、アプリのパフォーマンスと応答性が向上します。

Jetpack Composeは、Android Studioと緊密に統合されており、開発者はビジュアルエディタを使ってUIを設計したり、プレビュー機能を使ってリアルタイムにUIの変更を確認したりすることができます。

これで、Androidアプリ開発を始めるための準備が整いました。次のセクションでは、実際にJetpack Composeを使ってLINE風のチャットアプリのUIを実装していきます。

ユーザーインターフェースの実装:MessageItem

このセクションでは、実践的なプログラミングを通じてJetpack Composeの基礎を学んでいきます。LINE風のチャットアプリは、そのよい題材として選定されました。

初めに、チャット画面の部品を作成します。LINEのように角丸四角でメッセージを囲むコードを実行を作成します。送信メッセージは右に、受信メッセージは左に表示して見分けやすいようにします。このコードは、MessageItemという名前の新しいファイルに記述して下さい。

新規ファイルを作成するには、以下の手順を実行します。

図6 新規ファイルの作成

新規ファイルを作成するには、プロジェクトナビゲーターで「app > kotlin+java > com.example.counselingapp」を右クリックしてください。表示されたメニューから「New > Kotlin Class/File」を選ぶことで、クラス名を決めるモーダルウインドウがホップアップします。

図7 ファイルの名前

新しいファイル名「MessageItem」を入力します。ファイルのタイプは「File」を選んで、エンターキーで確定してください。エディタエリアに、新しいファイルが開かれます。以上で新規ファイルの作成は完了しました。

次に、UI実装のコードを書いていきます。新しく作成したファイル(MessageItem.kt)に次に示すコードを記述して下さい。コード全体をコピーして、貼り付けます。

package com.example.counselingapp

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

data class Message(val text: String, val isSent: Boolean) // (0)

@Composable
fun MessageItem(message: Message) {
    Box( // (1)
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        contentAlignment = if (message.isSent) Alignment.CenterEnd else Alignment.CenterStart
    ) {
        Box( // (2)
            modifier = Modifier
                .widthIn(max = LocalConfiguration.current.screenWidthDp.dp * 0.9f)
                .background(
                    if (message.isSent) Color(0xFF1E88E5).copy(alpha = 0.2f) else Color.Gray.copy(alpha = 0.2f),
                    shape = RoundedCornerShape(16.dp)
                )
                .padding(16.dp)
        ) {
            Text(message.text) // (3)
        }
    }
}

@Preview
@Composable
fun MessageItemPreview() { // (4)
    MessageItem(Message(" こんにちは", true)) // (5)
    MessageItem(Message(" こんにちは", false)) // (6)
}

このコードは、チャットアプリのメッセージアイテムを表示するためのComposable関数MessageItemとそのプレビューを定義しています。

コードの理解を深めるために、コメントの番号部分を詳しく解説していきます。これにより、コピー&ペーストしたコードの機能や、その記述がなされた背景を理解するようにして下さい。

(0)では、Messageという名前のデータクラスを定義しています。Kotlinにおけるdata classは、データを保持する目的でよく使用される特別なクラスの一種です。textプロパティは、メッセージの本文を保持します。String型であり、テキストメッセージの内容を表します。isSentプロパティは、メッセージが送信されたものか(true)、受信されたものか(false)を表します。Boolean型で、メッセージがユーザーによって送信されたものなのか、それとも相手から受信したものなのかを区別するために使用されます。

(1)の最初のBoxコンポーネントは、メッセージアイテムのコンテナ(入れもの)として機能します。modifierは、fillMaxWidth()を使用してアイテムの幅を親の幅いっぱいに広げ、padding(16.dp)で周囲に余白を設定しています。dpという単位はDensity-independent Pixelsの略で、AndroidにおいてUI要素のサイズや位置を指定するための単位です。dpは、異なる画面サイズや解像度のデバイスで一貫したレイアウトを実現するために使用されます。

contentAlignmentは、メッセージの送信者に応じてアイテムの配置を決定します。送信メッセージ(message.isSentがtrue)の場合はAlignment.CenterEnd(右寄せ)、受信メッセージの場合はAlignment.CenterStart(左寄せ)になります。

(2)の内側のBoxコンポーネントは、メッセージの背景とスタイルを定義します(これが中身)。modifierのwidthIn(max =)でボックスの幅がデバイスサイズの90%に制限しています。backgroundは、メッセージの送信者に応じて背景色を設定します。送信メッセージの場合はColor(0xFF1E88E5)(青色)、受信メッセージの場合はColor.Gray(灰色)を使用し、copy(alpha = 0.2f)で透明度を20%に設定しています。

shapeは、RoundedCornerShape(16.dp)を使用して、メッセージの角を丸くしています。padding(16.dp)で、メッセージの内容とその背景の間に内側の余白を設定しています。

(3)のTextコンポーネントは、メッセージのテキスト内容を表示します。message.textを使用して、Messageオブジェクトのテキストプロパティを表示しています。

(4)の@Previewアノテーションは、MessageItemPreview関数がプレビュー用の関数であることを示しています。このアノテーションを使うことで、アプリを実行せずにJetpack ComposeのUIをプレビューして、効率よく開発を進められます。

MessageItemPreview関数内で、MessageItem関数を2回呼び出しています。最初の呼び出しでは、送信メッセージ(isSentがtrue)としてメッセージを表示します(5)。
2回目の呼び出しでは、受信メッセージ(isSentがfalse)としてメッセージを表示します(6)。

以上で、MessageItem関数のコードの説明を終了します。これで、Jetpack Composeを使用してメイン画面を作成する基本的な流れを理解できたと思います。次に、Jetpack Composeで実装したUIを簡単にチェックする方法を紹介します。このコードのUIをAndroid Studioのプレビューエリアで確認すると、次のようになります。

図8 メッセージ部分のプレビュー

図8は、Android Studioでメッセージ部分のプレビューを行ったときのスクリーンショットです。右上のアイコンで、「コードのみ/コード+プレビュー/プレビューのみ」に変更ができます。プレビューエリアに受信メッセージと送信メッセージが設計通り表示されていることが確認できました。以上で、MessageItemについての説明を終わります。

次は、チャット画面全体を作成します。メッセージが上から順番に並ぶレイアウトをChatScreen関数に実装します。「ChatScreen」という名前のファイルを先ほどと同じ手順で作成して下さい。

ユーザーインターフェースの実装:ChatScreen

ChatScreen.ktが作成できたら、次に示すコードを記述して下さい。

package com.example.counselingapp

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.counselingapp.ui.theme.CounselingAppTheme
import kotlinx.coroutines.launch

@Composable
fun ChatScreen(initialMessages: List<Message> = emptyList()) {
    var messages by remember { mutableStateOf(emptyList<Message>()) } // (1)
    var inputText by remember { mutableStateOf("") } // (2)
    val listState = rememberLazyListState() // ここから(3)
    val coroutineScope = rememberCoroutineScope() // ここまで(3)
    val focusManager = LocalFocusManager.current // (4)

        fun sendMessage() { // (5)
        if (inputText.isNotBlank()) {
            messages = messages + Message(inputText, true)
            messages = messages + Message(inputText, false)
            coroutineScope.launch { // ここから(6)
                listState.animateScrollToItem(messages.size - 1)
            }
            focusManager.clearFocus() // ここまで(6)
            inputText = "" // (7)
        }
    }

    Column( // (8)
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
    ) {
        LazyColumn( // (9)
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f),
            state = listState
        ) {
            items(initialMessages + messages) { message ->
                MessageItem(message) // (10)
            }
        }

        Spacer(modifier = Modifier.height(8.dp)) // (11)

        Row( // (12)
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp, vertical = 8.dp)
        ) {
            TextField( // (13)
                value = inputText,
                onValueChange = { inputText = it },
                modifier = Modifier
                    .weight(1f)
                    .padding(end = 16.dp)
                    .border(BorderStroke(1.dp, Color.Gray), shape = RectangleShape)
                    .heightIn(min = 100.dp),
                label = { Text("メッセージを入力") },
                keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send), // ここから(14)
                keyboardActions = KeyboardActions(
                    onSend = {
                        sendMessage()
                    }
                ) // ここまで(14)
            )

            Button( // (15)
                onClick = {
                    sendMessage()
                }
            ) {
                Text("送信")
            }
        }
    }
}

@Preview(showBackground = true) // (16)
@Composable
fun ChatScreenPreview() {
    val messages = listOf( // (17)
        Message("こんにちは", true),
        Message("はい、こんにちは!", false),
        Message("調子はどうですか?", true),
        Message("良いですよ。ありがとうございます!", false)
    )

    CounselingAppTheme {
        ChatScreen(messages)
    }
}

(1)は、チャットアプリのメッセージリストを管理するための状態変数messagesを定義しています。remember { mutableStateOf(...) }の使用により、messagesの状態はアプリの再描画が行われても保持されます。これは、Composeがリコンポジション(コンポーネントの再描画)を行う際に、指定された値を「覚えておく」ためのものです。mutableStateOfは、値が変更されたときにリコンポジションをトリガーするComposeの特別な状態ホルダーです。emptyList<Message>()は、初期状態としてメッセージリストが空であることを示します。

(2)で、入力テキストの状態管理inputTextを定義しています。ユーザーがUI上で入力するテキストを保持するための変数です。この変数もrememberとmutableStateOfを使用しているため、ユーザー入力がリコンポジションを通して保持され、変更をトリガーできます。初期値として空の文字列が設定されており、ユーザーが何も入力していない状態を表します。

(3)は、スクロール状態listStateとコルーチンスコープcoroutineScopeを定義しています。これらの変数は、メッセージがたくさん表示されたときに、画面をスクロールして最新メッセージが表示されるように実装するときに使います。

ememberLazyListState()は、LazyColumnで使用されるリストのスクロール位置を記憶するためのものです。これにより、アプリがリコンポジションされたときにユーザーのスクロール位置が維持されます。
rememberCoroutineScope()は、コルーチンを利用するためのスコープを提供します。コルーチンは非同期処理を簡潔に扱うためのKotlinの機能です。このスコープは、非同期タスク(リストの自動スクロール)を実行する際に使用されます。

(4)は、フォーカス管理focusManagerを定義しています。キーボードが自動で閉じる実装で使用します。この行では、LocalFocusManager.currentを使用して現在のフォーカスマネージャーを取得します。フォーカスマネージャーは、キーボードフォーカスを管理するためのオブジェクトで、テキストフィールドなどのUI要素にフォーカスを与えたり、フォーカスを外したりすることができます。

(5)は、メッセージ送信するメソッドです。sendMessageメソッドは、ユーザーが入力したテキストが空でない場合(inputText.isNotBlank())、そのテキストを使って新しいMessageオブジェクトを作成し、messagesリストに追加します。ここでは、同じテキストで送信メッセージと受信メッセージの両方を表現しています。(次の第2部で、受信メッセージは、ChatGPTからの応答に変更されます)

(6)は、送信ボタンを押した後のUIの変化を実装しています。coroutineScope.launchを使用して、メッセージリストの最下部にスクロールする非同期タスクを開始します。これは、新しいメッセージが追加されたときにリストの最下部を表示するために使用されます。focusManager.clearFocus()で、現在のフォーカス(例えば、テキストフィールドから)をクリアします。これにより、キーボードが非表示になり、メッセージが確認しやすくなります。最後に、inputText = ""で入力フィールドをクリアし、次のメッセージ入力の準備をします。

(7)で、inputText = ""で入力フィールドをクリアし、次のメッセージ入力の準備をします。これにより、ユーザーが毎回手動で入力フィールドをクリアする手間が省け、より快適にチャットアプリケーションを使用できるようになります。

(8)は、画面全体のレイアウトをしています。画面上部にメッセージを下部に入力フィールドを配置します。Columnは、その子要素を縦方向に並べるコンテナです。Modifier.fillMaxSize()は、Columnが親コンテナ(この場合はデバイスの画面)の最大サイズを埋めるようにします。つまり、画面全体に広がります。.imePadding()は、ソフトキーボードが表示されている時に、キーボードによってコンテンツが隠れないように余白を自動的に調整するモディファイアです。これにより、ユーザーが入力フィールドをタップしてキーボードが表示された際に、入力フィールドがキーボードによって隠れることがなくなります。

(9)は、メッセージリストを表示します。LazyColumnは、項目を遅延して表示するリストを構築するためのコンポーネントです。これは、大量のデータを持つリストを効率的に表示するために使用されます。表示されていない項目はメモリに保持されず、スクロールに応じて動的に生成されます。

Modifier.fillMaxWidth()は、LazyColumnが親コンテナの最大幅に合わせて幅を広げるようにします。.weight(1f)は、Column内でLazyColumnが利用可能なスペースを他の要素と比較して柔軟に占めるようにします。この場合、可能な限り多くの空間を占めるようになります。state = listStateで、LazyColumnのスクロール状態を管理します。これにより、例えばメッセージが追加された時にリストの末尾に自動スクロールするなど、リストの挙動を制御できます。

(10)は、LazyColumn内でメッセージを一覧表示するために使用されます。items関数は、リスト内の各項目に対して実行され、ここではinitialMessagesとユーザーによって追加されたmessagesの両方を結合しています。
各メッセージに対して、MessageItemコンポーネント(このコード例では定義されていませんが、メッセージを表示するためのカスタムコンポーネントと想定されます)が呼び出され、リスト内にメッセージが表示されます。

(11)のSpacerは、コンポーネント間に空間を追加するために使用されます。この場合、Modifier.height(8.dp)を使って、縦方向に8dpの高さを持つ空間を作成しています。これにより、UI要素間の視覚的な区切りが明確になり、ユーザーインターフェイスが読みやすくなります。

(12)は、入力フィールドと送信ボタンを表示します。owコンポーネントは、その子要素を横方向に並べます。ここでは、メッセージ入力フィールドと送信ボタンがRow内に配置されています。
Modifier.fillMaxWidth()は、Rowが親コンテナの最大幅に合わせて幅を広げるようにします。.padding(horizontal = 16.dp, vertical = 8.dp)は、Rowとその子要素の周囲に水平方向に16dp、垂直方向に8dpのパディングを追加します。これにより、要素と画面端または他の要素との間に適切な余白が生まれ、UIの見た目が整います。

(13)は、入力フィールドです。TextFieldコンポーネントはユーザーがテキスト入力を行うためのUI要素です。ここでは、ユーザーがメッセージを入力するためのフィールドを表しています。

value = inputTextは、テキストフィールドの現在の値を指定します。inputTextはユーザー入力を保持する状態変数です。onValueChange = { inputText = it }は、テキストフィールドの内容が変更されたときに実行されるコールバックです。ユーザーがテキストを入力するたびにinputTextの値が更新されます。label = { Text("メッセージを入力") }で、テキストフィールドが空のときに表示されるラベル(プレースホルダー)を指定しています。

(14)は、キーボードの設定です。keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send)で、キーボードの送信ボタンを有効にしています。これにより、キーボードから直接メッセージを送信できます。keyboardActionsで、キーボードの送信アクションがトリガーされたときの処理を定義しています。ここでは、sendMessage関数が呼び出され、メッセージが送信されます。

(15)は、送信ボタンを表示します。Buttonコンポーネントは、ユーザーがタップすることでアクションをトリガーするUI要素です。ここでは、ユーザーがメッセージを送信するためのボタンを提供しています。
onClick = { sendMessage() }は、ボタンがクリックされたときに実行されるコールバック関数を指定します。ここでは、sendMessage関数が呼び出され、入力されたメッセージの送信処理が行われます。
Text("送信")は、ボタン上に表示されるテキストを定義しています。

(16)で、プレビュー機能を有効にしています。アプリを実行せずに、実装中のUIを確認できるので開発の効率が上がります。@Previewアノテーションを使用して、Android Studioのプレビュー機能にこのコンポーザブル関数を表示させます。showBackground = trueで、プレビューに背景を表示させることができます。

(17)は、プレビュー用のサンプルメッセージです。ChatScreenPreview関数で使用されるサンプルメッセージのリストを定義しています。これは、UIをデザインする際にどのように見えるかを確認するために使用されます。

以上で、ChatScreen関数の説明を終わります。チャットアプリのような複雑なUIが比較的簡単なコードで実装できました。プレビューエリアで確認した図を下に示します。

図9 ChatScreen関数のプレビュー

図9を見ると、Android Studioのプレビューエリアにチャットアプリの画面が表示されています。設計通り、角丸で囲まれたテキストが左右に振り分けて表示されていることが確認できました。

もしメッセージテキストの背景色を変えたいなど、UIを修正したいときは、コードエディタの該当部分を修正してみましょう。すると、プレビューエリアで結果をすぐに確認できます。

以上で、ChatScreen関数の説明を終わります。次は、MainActivityアクティビティの修正を行います。次に示すコードをMainActivity.ktにコピペして下さい。

ユーザーインターフェースの実装:MainActivityの修正

package com.example.counselingapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.example.counselingapp.ui.theme.CounselingAppTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) { // (1)
        super.onCreate(savedInstanceState) // (2)
        setContent { // (3)
            CounselingAppTheme { // (4)
                // A surface container using the 'background' color from the theme
                Surface( // (5)
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ChatScreen() // (6)
                }
            }
        }
    }
}

このコードは、Androidアプリケーションのメインアクティビティを定義しており、Jetpack Composeを使ったUIの実装例です。各部分の解説は以下の通りです。

(1)のonCreateは、Activityのライフサイクル中で最初に呼ばれるメソッドで、アクティビティの初期化処理を行います。このメソッド内でUIのセットアップや、他の初期化処理を実行します。onCreateメソッドはBundle?型の引数を取ります。このBundleは、以前の状態を保存していた場合にそれを渡すために使用されます(例えば、デバイスの回転後に状態を復元する場合など)。

(2)では、ComponentActivityのonCreateメソッドを呼び出しています。これは、Activityの初期化処理を行うために必要です。superキーワードは、親クラスのメソッドやプロパティにアクセスするために使用されます。

(3)のsetContentは、このアクティビティのUIをJetpack Composeを使用して定義します。このブロック内で宣言されたコンポーザブル関数がアクティビティのUIとして描画されます。

(4)のCounselingAppThemeは、アプリケーションのテーマを適用するためのコンポーザブル関数です。これは、カスタムテーマやMaterial Designのスタイルをアプリケーション全体に適用するために使用されます。このコードでは、CounselingAppTheme内でUIの定義が行われます。

(5)のSurfaceは、画面の表面を表現するためのコンポーザブル関数です。ここでは、アプリケーションの背景として機能します。modifier = Modifier.fillMaxSize()は、このSurfaceが画面全体に広がるようにするモディファイアです。color = MaterialTheme.colorScheme.backgroundは、Surfaceの背景色をテーマの背景色に設定しています。MaterialThemeは、現在適用されているMaterial Designのテーマにアクセスするためのオブジェクトです。

(6)で、ChatScreenコンポーザブル関数が呼び出されています。ChatScreenは、チャットアプリケーションのメイン画面を構築するためのコンポーザブル関数でしょう。このコードでは具体的な実装は省略されていますが、おそらくメッセージのリスト表示やメッセージ送信機能などを含むUIが定義されていると考えられます。

以上で、UI実装は終了です。Androidエミュレーターで動作確認をしてみましょう。

ユーザーインターフェースの実装:デバッグ

図10 Androidエミュレーター

図10は、Android Studioを使って、Androidエミュレーターでデバッグした際のスクリーンショットです。ツールバー中央で、エミュレーターの種類を選びます。▶︎実行ボタンをクリックすると、エミュレーターが表示され、アプリが起動します。入力フィールドにテキストを入力して、送信ボタンをタップすると、メッセージが表示されます。以上で、デバッグは終了です。

初期設定だと、日本語入力ができないはずです。付録にAndroidエミュレーターの設定を変更して日本語入力できるようにする方法を解説しました。

次に私たちが取り組むミッションは、Androidアプリを生成AIと連携させることです。具体的には、ユーザーからの質問に対して、カウンセラーのように回答を返すアプリを作成します。このプロジェクトを通じて、あなたはAPIの使用方法を学び、アプリ開発のさらなるステップに進むことができるようになります。

第2部 生成AIとの連携

このセクションでは、私たちのAndroidアプリが生成AIと会話できるように、APIを介して二つを連携させる方法について学びます。しかし、まずAPIとは何か、そしてそれがどのように機能するのかについて簡単に説明しましょう。

APIとは?

図11 APIについて

APIは「Application Programming Interface」の略で、アプリケーション間でデータをやり取りするための仕組みです。あるアプリが別のアプリに対して情報を要求(リクエスト)し、要求された情報を受け取る(レスポンス)ための方法と言えます。

例えば、天気予報アプリがあったとします。このアプリがユーザーの位置情報に基づいて最新の天気を表示するためには、外部の天気予報サービスからデータを取得する必要があります。ここでAPIが活躍します。アプリは、指定されたAPIを通じて天気予報サービスにリクエストを送り、サービスはそのリクエストに応じた天気情報をAPIを通じてアプリに返します。

APIを利用するメリットは以下の5つです。

  1. 開発効率の向上: 複雑な処理をAPIに任せることで、開発者はアプリの機能に集中できます。

  2. 高度な機能の実現: 人工知能や機械学習などの高度な機能を、専門知識なしで実装できます。

  3. コスト削減: APIが提供する共通機能を利用することで、開発時間と費用を抑えられます。

  4. スケーラビリティの向上: APIサーバーが多数のリクエストを処理するため、利用者増加にも対応しやすくなります。

  5. 最新技術へのアクセス: API提供者が最新技術を導入することで、開発者は常に最新の機能を活用できます。

APIを活用することで、開発者は複雑な処理を簡単に実装でき、高度な機能を実現できます。また、開発コストの削減、スケーラビリティの向上、最新技術へのアクセスなど、多くのメリットがあります。APIを上手く活用することが、効率的で高品質なアプリケーション開発につながります。

APIの理解ができたところで、次は、生成AIのAPIを利用するために必須の作業である「生成AIサービス(ChatGPT、Claude、Gemini)のアカウントとAPIキーの作成」を行います。(APIキーの作成がお済みの方は、この部分は飛ばして、ChatGPT連携まで進んでください)

初めに、ChatGPT APIを使用するために必要なOpenAI APIキーを作成する手順を説明します。

OpenAI APIキーの作成

OpenAI APIキーを作成するには、以下の手順を実行します。次に示す図は、OpenAIのウェブサイトで新しいAPIキーを作成している様子です。Nameとして、用途や分類を登録しておくと、APIキーの管理に役立ちます。

図12  OpenAI APIキーの作成
  1. OpenAIのウェブサイト(https://openai.com/)にアクセスし、アカウントを作成またはログインします。

  2. ダッシュボードにアクセスし、「API keys」セクションに移動します。

  3. 「Create new secret key」ボタンをクリックして、新しいAPIキーを生成します。

  4. 生成されたAPIキーをコピーし、安全な場所に保存します。このキーは後で使用するので、忘れないようにしてください。

APIキーを作成したら、アプリケーションからChatGPT APIを呼び出すことができます。次に、Anthropic APIキーとGoogle AI APIキーの作成手順を説明します。

Anthropic APIキーの作成

Anthropic APIキーを作成するには、以下の手順を実行します。次の図は、Anthropicのウェブサイトで新しいAPIキーを作成している様子です。Nameとして、用途や分類を登録しておくと、APIキーの管理に役立ちます。

図13 Anthropic APIキーの作成
  1. Anthropicのウェブサイト(https://www.anthropic.com/)にアクセスし、アカウントを作成またはログインします。

  2. ダッシュボードにアクセスし、「API」セクションに移動します。

  3. 「Generate API Key」ボタンをクリックして、新しいAPIキーを生成します。

  4. 生成されたAPIキーをコピーし、安全な場所に保存します。

Google AI APIキーの作成

Google AI APIキーを作成するには、以下の手順を実行します。初回のAPIキー作成時には、新しいGCP(Google Cloud Platform)プロジェクトを作成する必要があります。2回目以降のAPIキー作成時には、次の図に示すように既存のプロジェクト「Generative Language Client」を選択してください。

図14 Google AI APIキーの作成
  1. Google Cloud Platformのウェブサイト(https://console.cloud.google.com/)にアクセスし、アカウントを作成またはログインします。

  2. Googel AI Studioのウェブサイト(https://aistudio.google.com/)にアクセスして、左サイドバーから「Get API Key」セクションを選びます。

  3. 「Create API Key in new project」を選択します(初回のみ)。

  4. 生成されたAPIキーをコピーし、安全な場所に保存します。

  5. 2つ目以降のAPIキーを作成するときは、「Create API Key」から、プロジェクト名「Generative Language Client」を選んでください。現時点では、APIキーに識別のための名前をつけることはできないので、作成したキーの用途などを別途メモしておくことをおすすめします。

これで、3つの生成AIのAPIキーが作成されました。次に、Android Studioでの作業に戻り、AndroidアプリをChatGPT APIと連携させるためのコードを記述していきましょう。

ChatGPT連携:AndroidManifest.xmlを修正

ここから、ChatGPT連携のためのプログラムの説明に入っていきます。第1部と同じように、コードをコピペして、素早くサンプルアプリを完成させるアプローチで進めていきましょう。まず、アプリ全体の設定を管理するAndroidManifest.xmlを修正します。AndroidManifest.xmlファイルは、プロジェクトナビゲーターで、「app > manifests」の下に位置しています。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- インターネットアクセスのパーミッションを追加 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        // 以下略

このコードは、Androidアプリケーションでインターネットアクセスの許可を求める方法を示しています。特に、AndroidManifest.xmlは、Androidアプリケーションの中心的なファイルで、アプリの名前、アイコン、使用するパーミッション、起動時に実行されるアクティビティなど、アプリの基本的な設定を記述します。このファイルはアプリケーションのルートディレクトリに置かれ、アプリがインストールされる際にシステムによって読み込まれます。

追加した1行は、アプリがインターネットにアクセスするためのパーミッション(許可)を要求しています。Androidでは、ユーザーのプライバシーを保護するため、アプリが特定の機能(例えばインターネットアクセス、カメラの使用など)を利用する際には、明示的なパーミッションが必要です。uses-permissionタグを使って、そのアプリがどのようなパーミッションを必要とするかを宣言します。

アプリがインターネットアクセスを必要とする場合(例えばウェブAPIからデータを取得する場合)、このパーミッションがAndroidManifest.xmlに含まれていることを確認する必要があります。これにより、アプリのインストール時にユーザーに対して必要なパーミッションについて情報が提供され、ユーザーはそのアプリを安全に使用できるようになります。

次は、アプリをビルドを管理するbuild.gradle.ktsを編集します。プロジェクトナビゲーターの「Gradle Scripts」の下に配置されている「build.gradle.kts (Module:app)」を開いてください。

ChatGPT連携:build.gradle.ktsを修正



dependencies {

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)

    // 追加した依存関係
    implementation("io.ktor:ktor-client-core:2.3.2") // (1)
    implementation("io.ktor:ktor-client-android:2.0.0") // (2)
    implementation("io.ktor:ktor-client-content-negotiation:2.3.2") // (3)
}

 このコードは、Androidアプリケーションのbuild.gradle.ktsファイルの一部で、アプリの依存関係を定義しています。build.gradle.ktsファイルはKotlin DSL(Domain Specific Language)を使用してGradleビルドスクリプトを記述するためのファイルです。GradleはAndroidアプリケーションのビルドシステムで、このファイルを通じてアプリのビルド設定や依存するライブラリを管理します。

 build.gradle.ktsは、プロジェクトレベルまたはアプリモジュールレベルで使用されるGradleビルドスクリプトです。Kotlin DSLを使用することで、より安全で読みやすく、効率的なビルド設定が可能になります。このファイル内でライブラリの依存関係を定義することで、Gradleは必要なライブラリを自動的にダウンロードし、プロジェクトに組み込みます。

 追記された部分では、アプリがCoroutine、ViewModel、LiveData、Gsonなどの機能を使用するための依存関係が追加されています。追加した依存関係の解説をします。

(1)は、Ktorクライアントのコア機能を提供します。HTTPリクエストの送信、レスポンスの受信、およびクライアント設定の基本を含んでいます。プラットフォーム非依存なので、共通コードで使用できます。

(2)は、ndroid固有のKtor HTTPクライアントエンジンを提供します。これにより、Androidアプリケーション内でHTTPリクエストを効率的に処理できるようになります。特に、Androidプラットフォームの特性を活かした非同期処理やネットワーク通信の最適化が行われます。

(3)は、コンテンツネゴシエーションのサポートを追加します。これは、クライアントとサーバー間でデータ交換する際に使用されるデータ形式(例:JSON, XMLなど)を自動的に扱うための機能です。特にAPIとの通信でJSONなどの特定のフォーマットを扱う場合に有用です。

 これらのライブラリをプロジェクトに追加することで、アプリケーションの開発において非同期処理、アーキテクチャの設計、データ処理が容易になり、より効率的かつ安全なコードの実装が可能になります。Gradleはビルド時にこれらの依存関係を解決し、必要なライブラリをダウンロードしてプロジェクトに組み込むことで、開発者がこれらの機能を直接使用できるようにします。

 上記コードを追記すると、Android Studioのエディタエリア上部に「Sync now」の表示が出ますので、クリックして同期します。これで、ビルド時に各種ライブラリが自動で組み込まれるようになります。

 以上で、ビルドの設定が完了しました。次は、ChatGPTとの連携処理を記述するクラスを作成します。

ChatGPT連携:ChatGPTService

図15 新しいObjectを作成する

「ChatGPTService」という名前の新しいファイルを作成します。ファイルのタイプを選ぶモーダルウインドウで「Object」を選んでください。

Kotlinでは、「Object」はシングルトンオブジェクトを定義するために使用されます。シングルトンとは、クラスのインスタンスが1つしか存在しないことを保証するデザインパターンです。シングルトンの主な目的は、グローバルな状態や共有リソースの管理、コードの簡素化、遅延初期化などです。

ファイルが作成できたら、次のコードをコピペして下さい。

ここから先は

31,761字 / 13画像

この記事が気に入ったらサポートをしてみませんか?