NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    【Swift】ChatGPTを利用してAIチャットアプリを作ってみる

    概要

    OpenAI APIの中でChatGPTが利用できるので、そちらを利用してAIとチャットできるiOSアプリを試しに作ってみました。その中で作り方やpromptの調整による変化などを簡単に検証してみたので紹介したいと思います。
    またPythonのみで簡単にWebアプリを作成できる方法については以下の記事で紹介されていますのでよければそちらもご覧ください。

    tech.nri-net.com

    環境

    この記事は以下のバージョン環境のもと作成されたものです。
    【Xcode】14.2
    【iOS】16.2
    【macOS】Ventuta

    前置き

    ChatGPTに関しては昨年の12月ごろに「ChatGPTを使ったiOSアプリの開発方法をChatGPTに聞いて作ってみる」と言う事を試してみました。

    ただ思ったような回答は得られず、結果OpenAISwiftというライブラリを導入する事で検証することができました。

    OpenAISwiftの使用方法は以下リンク先のREADMEに記載されていますのでそちらを参考にして頂ければと思いますが、準備としてOpenAIにてアカウント登録を行い、画面右上のPersonal -> View API keys -> Create new secret keyよりsecret keyを発行する必要があります。

    発行したkeyはlet openAI = OpenAISwift(authToken: "TOKEN")のTOKEN部分に代入して使用します。

    github.com

    今回は上記のサードパーティのライブラリを使用せず作成してみました。

    ドキュメントを見る

    ChatGPTを使用したい場合のドキュメントは以下のリンク先にあります。

    platform.openai.com

    チャット形式のAPIを利用するにはhttps://api.openai.com/v1/chat/completionsをPOSTする事で利用できそうです。
    他にも画像やオーディオなどもあるのでまたどこかで触ってみたいと思います。
    パラメーターにmodelがあり、その中でgpt-3.5-turbogpt-3.5-turbo-0301を指定するようです。
    ちなみに今回使用するのはgpt-3.5-turboを使用しますが、gpt-3.5-turbo-0301との違いについては以下を参照ください。

    openai.com

    実際に検証してみる

    今回検証するにあたり以下のリポジトリのコードを参考にさせていただきました。

    github.com

    OpenAISwiftではなくChatGPTSwiftのリポジトリを参考にした理由はiOSだけでなく、macOSやWatchOSでも使用できる状態にあったのでこちらを参考にさせていただきました。

    コード全体としては上記のリポジトリを参考にして頂ければと思いますが、主要な部分は以下となります。

    リクエストのプロパティは以下の通りです。

    private var urlRequest: URLRequest {
        let url = URL(string: "https://api.openai.com/v1/chat/completions")!
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"
        headers.forEach {  urlRequest.setValue($1, forHTTPHeaderField: $0) }
        return urlRequest
    }
    

    headersのプロパティ。

    private var headers: [String: String] {
        [
            "Content-Type": "application/json",
            "Authorization": "Bearer \(apiKey)"
        ]
    }
    

    apiKeyやmodel、promptなどについてはinitで定義しています。

    init(apiKey: String, model: String = "gpt-3.5-turbo", systemPrompt: String = "あなたは優秀なアシスタントです。話し方は関西弁で返します。", temperature: Double = 0.5) {
        self.apiKey = apiKey
        self.model = model
        self.systemMessage = .init(role: "system", content: systemPrompt)
        self.temperature = temperature
    }
    

    OpenAI APIにリクエストを送信し、生成されたテキストを受け取る処理は以下の通りです。

    func sendMessageStream(text: String) async throws -> AsyncThrowingStream<String, Error> {
        var urlRequest = self.urlRequest
        urlRequest.httpBody = try jsonBody(text: text)
        let (result, response) = try await urlSession.bytes(for: urlRequest)
        guard let httpResponse = response as? HTTPURLResponse else {
            throw "Invalid response"
        }
        guard 200...299 ~= httpResponse.statusCode else {
            var errorText = ""
            for try await line in result.lines {
                errorText += line
            }
            if let data = errorText.data(using: .utf8), let errorResponse = try? jsonDecoder.decode(ErrorRootResponse.self, from: data).error {
                errorText = "\n\(errorResponse.message)"
            }
            throw "Bad Response: \(httpResponse.statusCode), \(errorText)"
        }
        return AsyncThrowingStream<String, Error> { continuation in
            Task(priority: .userInitiated) { [weak self] in
                guard let self else { return }
                do {
                    var responseText = ""
                    for try await line in result.lines {
                        if line.hasPrefix("data: "),
                           let data = line.dropFirst(6).data(using: .utf8),
                           let response = try? self.jsonDecoder.decode(StreamCompletionResponse.self, from: data),
                           let text = response.choices.first?.delta.content {
                            responseText += text
                            continuation.yield(text)
                        }
                    }
                    self.appendToHistoryList(userText: text, responseText: responseText)
                    continuation.finish()
                } catch {
                    continuation.finish(throwing: error)
                }
            }
        }
    }
    

    主要な部分は以上です。
    後は適当に画面を作るだけでできます。

    検証の様子

    適当にチャット形式で会話を行ってみました。
    内容はともかく、promptにて関西弁で返すように追記していますので関西弁で返してきます。

    またpromptを少し調整してUnitTestのサンプルコードを返すようにしてみました。

    まとめ

    今回はここまでとなります。非常に簡単にアプリにAIアシスタントを組み込むことができる点や、promptを調整する事で好みのAIアシスタントに近づけることができる点など非常に面白いと思いました。
    また既存のコードよりUnitTestを生成させる事については、promptの調整はもちろん、環境やコードを渡す際に一工夫必要かもしれません。まだ深く検証できてませんが、非常に便利な機能になりそうだなと思いましたので今後作ってみたいなと思いました。
    更にGPT-4も執筆中にリリースされ(日本時間3月15日の深夜2時ぐらいにメールが来ていた)、非常に成長がはやく、精度もどんどん良くなってきているので引き続き上手く付き合っていく方向を探っていきたいと思います。
    以上ChatGPTを利用してAIチャットアプリを作ってみるでした。

    執筆者岡優志

    iOSエンジニア
    iOSを専門とし、モバイルアプリの開発を行なっています。

    Twitter