본문 바로가기

IT/Android

안드로이드 스튜디오 / GPT-3 API를 이용한 모바일 챗봇 만들기

반응형

구현 기능

  • gpt-3 api 통신을 통한 채팅 
  • temperature, frequency panalty 값 설정 
  • 채팅 화면 초기화
  • 채팅 내용 공유 (시스템 공유하기 사용)
  • 오프라인 환경에서도 채팅내용 불러오기

기술스택 & 라이브러리

  • IDE -> Android Studio
  • Language -> Kotlin
  • Network -> Retrofit,OkHttp
  • Coroutine
  • Dependency injection -> Dagger Hilt
  • Android Jetpack (ViewModel, LiveData, viewBidning, RecyclerView, Room, Navigation)

1. 프로젝트 구성

안드로이드 클린아키텍처를 구성하고자, data, domain, app 모듈을 분리하고, hilt로 의존성 주입을 하였다.

 

2.채팅창 화면 구현

Recyclerview를 이용하여 질문과 답변을 표현했다.

채팅창내 질문은 왼쪽에, 답변은 오른쪽에 가야 한다. textView에 gravity 값만 주면 적용이 안돼서, 양쪽에 View를 추가하였다.

Q(질문) 인 경우 textView의 gravity는 왼쪽, 오른쪽 View를 visible하게 설정하여 채팅이 왼쪽에 보일 수 있게 하였다.

// recyclerview adapter.kt
if(current.mode=="Q"){ // 질문
    txtChat.gravity = Gravity.START
    viewLeft.visibility = View.GONE
    viewRight.visibility = View.VISIBLE
}else{ // 답변
    txtChat.gravity = Gravity.END
    txtChat.setBackgroundResource(R.color.my_light_primary)
    viewLeft.visibility = View.VISIBLE
    viewRight.visibility = View.GONE
}

// xml
    <View
            android:id="@+id/view_right"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:visibility="gone"/>

스크롤시 리사이클러뷰 아이템에 지정해줬던 색깔이 다른 아이템에 적용되는 오류가 생겼다.

뷰를 재사용하다보니 생기는 오류라, position값을 고정하니 해결되었다.

// onBindViewHolder position 값 고정
override fun getItemViewType(position: Int): Int {
    return position
}
override fun getItemId(position: Int): Long {
    return position.toLong()
}

editText를 클릭하여 키패드를 올렸을때, 전송버튼이 가려지지 않기 위한 작업도 해주었다. 

https://dev-ej2.tistory.com/46

 

Android / SOFT_INPUT_ADJUST_RESIZE deprecated

SOFT_INPUT_ADJUST_RESIZE를 맨 하단에 있는 editText를 클릭했을때 soft keybord가 올라오면서 editText가 안가려지게 하기 위해 사용하고 싶었으나, 안드로이드30이상부터 deprecated 되었다. 30 이상은 setDecorFitsS

dev-ej2.tistory.com

3. gpt-3 응답값 가공

gpt-3 응답값에는 앞에 \n\n이 붙어서 온다. 

 

{ ..."choices":[{"text":"\n\nAndroid is a mobile operating system based on a modified version of the Linux kernel and other open source software, designed primarily for touchscreen mobile devices such as smartphones and tablets. Android is developed by a consortium of developers known as the Open Handset Alliance and commercially sponsored by Google. It was unveiled in 2007, with the first commercial Android device launching in September 2008.",...}],...}}

 

맨 앞에 있는 엔터는 필요가 없으니, 텍스트를 trim()을 이용하여 공백을 정리해주었다.

it.choices.text.trim()

4. gpt-3 키값 local.properties에 저장

git에 소스코드를 올려놨기때문에, 키값을 안전한 곳에 저장을 할 필요가 있었다.

local.properties에 키값을 저장하고, mudule 단위 gradle 파일에 buildConfigField를 추가하였다.

// local.properties
gpt_key= "Bearer ${gpt key}"

// module단위 gradle
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

 defaultConfig {
    // api key
    buildConfigField ("String", "GPT_KEY", properties['gpt_key'])
}

// 사용할 곳에서
 BuildConfig.GPT_KEY

5. Room 이용하여 채팅내용 저장

그동안, 로컬에 간단한 설정값 정도만 저장을 하였어서, Preference를 사용했었다.

이번엔 채팅내역 전체를 저장하기 위해 Room을 사용하여 로컬DB를 처음 프로젝트에 적용시켜보았다.

@Dao
interface ChattingDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertChatting(chatting: ChattingEntity)

    @Query("SELECT * FROM chatting")
    fun selectAllChatting(): List<ChattingEntity>

    @Query("DELETE FROM chatting")
    fun deleteAllChatting()

}

@Database(entities = [ChattingEntity::class], version = 1, exportSchema = false)
abstract class ChattingDatabase : RoomDatabase() {
    abstract fun chattingDao(): ChattingDao
}

@Entity(tableName = "chatting")
data class ChattingEntity (
    @PrimaryKey(autoGenerate = true)
    val idx: Int?,
    val mode: String,
    val text: String
)

6. Preference를 이용하여 설정값 저장

설정화면에서 temperature, frequency panalty 값을 설정하면, gpt-3 api를 요청할때 설정한 값을 같이 보내야 한다.

따라서, 설정값을 Preference에 저장을 하고, 요청할때 Preference에서 값을 불러왔다.

7. 공유기능 - Intent.ACTION_SEND

공유기능도 마찬가지로, 선택한 채팅을 preference에 넣어 값을 저장하고, 공유를 하였다. 

공유가 완료되면 preference에서 값을 삭제해 주었다.

시스템내 공유를 호출하기 위해 Intent.ACTION_SEND를 사용하였다.

// 다른앱으로 텍스트 공유
val intent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, ${공유할 텍스트})
}
val shareIntent = Intent.createChooser(intent, "공유하기")
startActivity(shareIntent)

넘블에서 진행한 GPT-3 API를 이용하여 2주동안 챗봇을 만드는 챌린지에 참여를 하였다.

일과 병행을 하느라 추가 기능을 구현하지 못한 점에 아쉬움이 남는다.

추후에, api 요청을 하면 답변을 받는 시간동안 사용자 인터랙션을 추가하고,

기존에 대화했던 내역을 같이 prompt에 보내어 대화형으로 만들어 기능을 확장해 나갈 예정이다.

 

https://github.com/LeeEunjeong1/GPTalk

 

GitHub - LeeEunjeong1/GPTalk

Contribute to LeeEunjeong1/GPTalk development by creating an account on GitHub.

github.com