Web APIを通じてRからウェブサービスを利用する方法と、その例としてOpenAI APIを利用してChatGPTをRから利用する方法を述べる。
APIとはapplication programming interfaceの略で、アプリケーション同士が互いに情報を送信しあうためのインターフェース(入り口)である。 Webを通じて提供されるものをWeb APIという(Webを省略して単にAPIと呼ばれることも多い)。
Webの仕組みについて軽く述べておく。
ウェブサイト(例えばGoogleのトップページ www.google.com )にアクセスする際は、ユーザーがブラウザ(Google Chromeなど)を通じてサーバーにWebページの情報を求めるリクエストを送り、サーバーが情報を返すという処理が行われる。 ブラウザのことをWebクライアントと呼ぶこともある。Webはこうしたクライアントとサーバーのやりとりで実現されている。
なお、サーバーが返してくる情報についてもう少し具体的に述べると、Webページはhtmlという形式のテキストファイルを中心として構成されているため、サーバーはhtmlなどを送信する。 htmlは、Google Chromeで閲覧中のページを右クリックして「ページのソースを表示」のメニューをクリックしたときに出てくる
などと書かれているファイルである。
Webページの場合、基本的に人間が見ることを想定して開発されるものであるため、サーバーはhtmlなどを返す。 しかしWeb APIのユーザーは人ではなくアプリケーションであるため、基本的に機械にとって扱いやすいようなインターフェース(送受信するデータの形式)とする。
よく使われる形式のひとつはJSON形式である。JSONは次のような形式になっている。
JSONはキーと対応するバリュー(値)の集合になっており、多くのプログラミング言語に用意されているキー・バリュー・ストア(Rだと名前付きのリスト
list(key = "value")
のこと)に変換して扱うことができる。例えば{jsonlite}
パッケージを使うことでJSONをlistに変えることができる。
json_text <- '{
"status": "OK",
"values": [100, 200, 300]
}'
data <- jsonlite::fromJSON(json_text)
data["values"]
## $values
## [1] 100 200 300
WebサーバーとはHTTPという通信方法を用いて通信する。
リクエストを送る際はメソッドというものが9種類存在し、状況に応じて使い分けられている。
例えば情報を閲覧したい際はGET
メソッドを使う事が多く、テキストなど比較的大きめの情報を送信したい場合はPOST
メソッドを使うことが多い。
Rの場合、{httr}
パッケージを使うことでHTTP通信を行うことができる。
HTTPリクエストに対するサーバーからのレスポンスにはステータスコード(status code)とボディ(body)が含まれる。
## [1] 200
ステータスコードは3桁の数字で、例えば次のようなものがある。
ステータスコード | 名前 | 意味 |
---|---|---|
200 | OK | 正常に通信できた |
404 | Not found | ページが見つからない |
500 | Internal Server Error | サーバー側で想定外のエラーが発生した |
ボディはレスポンス内容の本文に相当するものであり、Webページであればhtml等が、Web APIであればJSON等が送られる。
## {html_document}
## <html itemscope="" itemtype="http://schema.org/WebPage" lang="ja">
## [1] <head>\n<meta content="世界中のあらゆる情報を検索するためのツールを提供しています。さまざまな検索機能を活用して、お探しの情 ...
## [2] <body bgcolor="#fff">\n<script nonce="jHuCV0hHYt3HF50DV2ftPQ">(function() ...
上記はGoogleのトップページ部分のhtml(をR側で見やすく表示したもの)である。
利用者登録が不要で手軽に利用できるWeb APIの例として、国土交通省の土地総合情報システムのAPIを使って試していく。
土地総合情報システムはアンケート調査によって得た不動産価格を公開しているWebサイトで、このAPIを利用することで不動産価格のデータをRから直接取得することができる。
APIの操作説明を見ると2つAPIが存在するが、今回は「不動産取引価格情報取得API」を利用する。
操作説明にはURLとパラメータをどう指定すればよいのかが書かれている。
パラメータとはURLの?
以降にkey=value
形式書かれるもので、httr::GET()
ではquery
という引数にlist()
形式で渡していく。
res <- GET(
# APIのURL
url = "https://www.land.mlit.go.jp/webland/api/TradeListSearch",
# クエリパラメータ
query = list(
from = 20224, # 何年の第何四半期以降の取引を取得するか
to = 20231, # 何年の第何四半期までの取引を取得するか
area = 13 # 都道府県コード(東京は13)
)
)
まずステータスコードを確認する。200であればリクエストに成功している。
## [1] 200
レスポンスからbodyを取り出す。
結果はbody$dataに含まれているため、取り出してデータフレームに変換する
このようなデータが取得できた
## # A tibble: 10,563 × 27
## Type MunicipalityCode Prefecture Municipality DistrictName TradePrice
## <chr> <chr> <chr> <chr> <chr> <chr>
## 1 中古マンシ… 13101 東京都 千代田区 飯田橋 13000000
## 2 中古マンシ… 13101 東京都 千代田区 飯田橋 44000000
## 3 中古マンシ… 13101 東京都 千代田区 飯田橋 70000000
## 4 宅地(土地と… 13101 東京都 千代田区 飯田橋 130000000
## 5 中古マンシ… 13101 東京都 千代田区 飯田橋 150000000
## 6 中古マンシ… 13101 東京都 千代田区 一番町 190000000
## 7 中古マンシ… 13101 東京都 千代田区 一番町 350000000
## 8 宅地(土地と… 13101 東京都 千代田区 一番町 430000000
## 9 中古マンシ… 13101 東京都 千代田区 一番町 52000000
## 10 中古マンシ… 13101 東京都 千代田区 一番町 47000000
## # ℹ 10,553 more rows
## # ℹ 21 more variables: FloorPlan <chr>, Area <chr>, BuildingYear <chr>,
## # Structure <chr>, Use <chr>, Purpose <chr>, CityPlanning <chr>,
## # CoverageRatio <chr>, FloorAreaRatio <chr>, Period <chr>, Renovation <chr>,
## # Region <chr>, LandShape <chr>, Frontage <chr>, TotalFloorArea <chr>,
## # Direction <chr>, Classification <chr>, Breadth <chr>, PricePerUnit <chr>,
## # UnitPrice <chr>, Remarks <chr>
e-StatやRESASといった公的統計のサイトはAPIを公開している。
これらも{httr}
パッケージでアクセスすることが可能である。
例えば、矢内(2016)はe-Stat APIにhttrパッケージでアクセスする方法を解説している。
しかし、APIによってはhttpリクエストを送ってbodyを整形する処理があらかじめ関数にまとめられパッケージとして提供されていることもある。 例えばe-Stat APIであれば{estatapi}パッケージが存在し、R側から関数を呼び出す形でAPIにアクセスし、tibbleのデータフレームで結果を返してくれるようになっている。こうしたパッケージを使うことでAPIをより手軽に扱うことができる。 (ただし、こうしたパッケージは公式のものもあれば第三者が公開しているものもあるため、有償APIを使う際は注意する)
e-statやRESASの他にも行政のAPIは多数存在し、e-Gov APIカタログなどで検索することができる。
ChatGPTはWebサイトだけでなくWeb APIも提供されているため、ChatGPTを例にとっていく。 利用登録が必要でハードルがやや高いため、参考として載せておく。
OpenAI APIは本来は有料のサービスであるが、アカウントを作成してから3ヶ月間は18ドル分の無料枠があり、気軽に使うことができる。 (なお、有料と言ってもGPT-3.5は1000トークンあたり0.002ドル程度とかなり安い)
OpenAIにログインして右上の自分のプロフィール部分をクリックして開くメニューの「Veiw API Keys」をクリックする。 すると自分のAPIキー(APIで本人認証するための鍵となる情報)の管理画面が表示される。 まだキーを作ったことがなければ何も存在しない。
下部の「+Create new secret key」ボタンをクリックして新規作成する。 名前の入力欄が出てくるが任意の設定項目なので無視して先に進んでも問題ない。
作成するとキー(半角英数字の羅列)が出てくるため、コピーしておく。 (APIキーが万一流出してしまうと第三者に好き勝手にAPIを使われてしまうため、扱いには注意する。もしクレジットカード情報をOpenAI に登録して有料ライセンスを保有している場合は特に慎重に扱うこと)
まずAPIキーの扱いについて述べる。APIキーのような機密情報はソースコード中に直接書き込まないことが望ましいため、環境変数(environment variable)に設定してRから読み込むことにする。環境変数はOSなどプログラムを動かす環境に設定した変数であり、環境変数を利用することでプログラム側(R側)ではあらかじめ定義済みの変数を呼び出すだけでよいことになる。
環境変数を定義する方法は複数あり、例えばSys.setenv(変数名="value")
のように書いてその場で実行したり、ホームディレクトリに.Renviron
ファイルを作ってそちらに設定したりする。
今回は簡易的にSys.setenv()
を使う方法を紹介する。Rのスクリプトではなく、RStudioの左下の「Console」タブに、次のように打ち込んで実行する。
なお環境変数を読み込むときはSys.getenv("変数名")
と書けばよい。
OpenAIのChatAPIのドキュメントを見るとPOST
メソッドを使用する必要があることがわかる。
POSTリクエストを送るには、httr::POST()
を使用すればよい。
まず、先にリクエストボディを作成しておく。APIがJSON形式を受け取る仕様であるため、list()
で記述していく。
body <- list(
# 使用する言語モデルを指定する
model = "gpt-3.5-turbo",
# メッセージを指定する
messages = list(
# role: user ならユーザーがAIに対して発したメッセージを意味する
# content: メッセージ本文
list(role="user", content="Hello!")
)
)
res <- POST(
url="https://api.openai.com/v1/chat/completions",
# 認証情報などの付加的な情報
config = add_headers(
"Content-Type" = "application/json", # JSON形式で送ることを明示
"Authorization" = str_c("Bearer ", Sys.getenv("OPENAI_API_KEY")) # 認証情報
),
body = body, # リクエストの本文
encode = "json" # リクエストはjsonで送るため、list形式で渡している
)
ステータスコードが200の場合、成功している。 401や403が返ってくる場合はAPIキーの情報をうまく渡せていない可能性が高いため、コードを今一度確認したほうがよい。
成功すると次のように返答が返ってくる。
Hello! How can I assist you today?
role = "system"
としてシステム側からの指示を与え、ChatGPTの役割を明確化したり、どのように返答するかの例を与えたりすることもできる。
body <- list(
model = "gpt-3.5-turbo",
messages = list(
# role: systemならシステムがAIに対して与えた指示になる
list(role="system", content="あなたは英語の先生です。メッセージ内の英語に誤りがあれば訂正し、日本語で解説をしてください。"),
list(role="user", content="This is pen.")
)
)
res <- POST(
url="https://api.openai.com/v1/chat/completions",
# 認証情報などの付加的な情報
config = add_headers(
"Content-Type" = "application/json", # JSON形式で送ることを明示
"Authorization" = str_c("Bearer ", Sys.getenv("OPENAI_API_KEY")) # 認証情報
),
body = body, # リクエストの本文
encode = "json" # リクエストはjsonで送るため、list形式で渡している
)
This is a pen.
「This is pen.」という表現には誤りがあります。正しくは、「This is a pen.」と言います。英語では、不特定の物を表す場合は冠詞「a」(または「an」)を使います。したがって、この文では「a pen」という具体的な物の名前の前に「a」を置く必要があります。
APIキーの不正利用を防ぐ手段のひとつは、使わなくなったキーを無効化して使えなくしておくことである。 必要になればすぐに新しいキーを作れるので、一通り試し終わったら無効化しておくことを推奨する。
まず、再びAPI keysページに向かう。 API keyはゴミ箱のアイコンをクリックすれば削除できるが、2つ以上のキーが存在する状態でなければ無効化できない制限があるようなので、「Create new secret key」から再度新たなキーを作り、今回作った方を削除すればよい。
ChatGPTからの応答を人間に向けたフォーマットではなく機械に向けたフォーマットとして、ChatGPT自身に外部のツールを操作させる方法も存在する。その方法の一つが、ChatGPTのAPIへのリクエストボディにfunctions
という情報を記入し、操作したいツールの情報を記載することである。
Function calling and other API updates
上記のページの例では、「What’s the weather like in Boston right now?」という質問に対して、
という3ステップを経て最終的に「The weather in Boston is currently sunny with a temperature of 22 degrees Celsius.」と返答させている。