本シリーズ前回の記事 1. ROS Service プログラムの文脈をふまえたチャット対応 では OpenAI の Chat Completion API を利用して過去のチャット履歴もふまえたチャットを行える ROS Service プログラムを作成した様子をお伝えしました.
今回の記事では Chat Completion API を利用した「文脈をふまえたチャット」をする ROS ソフトウェアを実装してみた2つ目の方法「2. ROS Topic を介した ChatGPT チャットプログラム」を作成した様子を紹介します.
前回,比較的短文のチャットを扱う Chat Completion API へのアクセスであれば ROS Service よりも ROS Topic を介したメッセージのやり取りの方が ROS ノード内でのチャット会話に限られず,より ROS に親和的でよりシンプルな構成になるのでは?という反省がありました.
今回の ROS Topic を用いたチャットプログラムの作成方針は次のようにしました.
/request
を購読してユーザの発言を得る/response
としてパブリッシュする/request
にパブリッシュする/response
を購読して得るROS Service プログラムの場合はチャット履歴をふまえたとしてもチャット機能提供側とユーザとの1者対1者でのやり取りでしたが,ROS Topic にすることでチャット機能提供側と複数のユーザの1者対他者でのやり取りも可能になる利点もあります.
ROS Topic を介した文脈をふまえたチャットプログラムで追加したファイルは次の2つです.
サービスの定義など考慮しなくて良いので非常にシンプルです.
以下,それぞれのファイル内のコードを記載して少し説明をします.
scripts / openai_chat_rostopic.py
#!/usr/bin/env python3 import rospy import openai from std_msgs.msg import String class Chatter: """ Chat with ChatGPT on ROS topics """ def __init__(self): # Get ROS parameters prompt = rospy.get_param('~prompt') self.model = rospy.get_param('~model') openai.api_key = rospy.get_param('~key') rospy.loginfo("For \'system\': %s" % (prompt)) # Set initial message with a prompt self.messages = [] self.messages.append({"role": "system", "content": str(prompt)}) self.sub = rospy.Subscriber('request', String, self.callback) self.pub = rospy.Publisher('response', String, queue_size=10) rospy.spin() def callback(self, data): rospy.loginfo("request: %s", data.data) # Add user's input to the history self.messages.append({"role": "user", "content": str(data.data)}) response = openai.ChatCompletion.create( model=self.model, messages=self.messages ) content = response["choices"][0]["message"]["content"] role = response["choices"][0]["message"]["role"] token = response["usage"]["total_tokens"] # Add GPT's response to the history self.messages.append({"role": str(role), "content": str(content)}) rospy.loginfo("%s(token:%d): %s" % (role, token, content)) self.pub.publish(content) if __name__ == "__main__": rospy.init_node('chat_rostopic', anonymous=True) chatter = Chatter()
/request
を購読(Subscribe)してトピックを受け取ったら callback()
メソッドを呼び出すcallback()
メソッド内で新たなリクエストをチャット履歴に追加/response
としてパブリッシュlaunch / openai_chat.launch
launch オプション service
を用いて前回の記事で紹介した ROS Service によるチャットプログラムと今回の ROS Topic を介したチャットプログラムのどちらを実行するかを切り替えるようにしています.
<launch> <arg name="key" default="$(env OPENAI_API_KEY)" /> <arg name="model" default="gpt-3.5-turbo" /> <arg name="service" default="false" /> <arg name="prompt" default="You are a helpful assistant." /> <node if="$(arg service)" pkg="openai_ros" type="openai_chat_server.py" name="openai_chat_service" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> </node> <node unless="$(arg service)" pkg="openai_ros" type="openai_chat_rostopic.py" name="openai_chat_topic" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> <param name="prompt" value="$(arg prompt)" /> </node> </launch>
文脈をふまえた ROS Topic を介したチャットプログラムを実行した例を以下に記載します.
ターミナル 1 : チャットノードの起動
Chat Completion API にアクセスして ROS Topic でやり取りする ROS Node を openai_chat.launch で起動しています.
robotuser@robotuser-PC:~$ source ~/openai_ws/devel/setup.bash robotuser@robotuser-PC:~$ export OPENAI_API_KEY="sk-..." robotuser@robotuser-PC:~$ roslaunch openai_ros openai_chat.launch ... logging to /home/robotuser/.ros/log/609f8d12-52cd-11ee-9968-6b3ff6703622/roslaunch-robotuser-PC-5035.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://robotuser-PC:40257/ SUMMARY ======== PARAMETERS * /openai_chat/key: sk-3JDluBbxsNuIhi... * /openai_chat/model: gpt-3.5-turbo * /openai_chat/prompt: You are a helpful... * /rosdistro: noetic * /rosversion: 1.16.0 NODES / openai_chat (openai_ros/openai_chat_rostopic.py) auto-starting new master process[master]: started with pid [5043] ROS_MASTER_URI=http://localhost:11311 setting /run_id to 609f8d12-52cd-11ee-9968-6b3ff6703622 process[rosout-1]: started with pid [5053] started core service [/rosout] process[openai_chat-2]: started with pid [5060] [INFO] [1694675254.662674]: For 'system': You are a helpful assistant. [INFO] [1694675266.017788]: request: Hello [INFO] [1694675267.401076]: assistant(token:27): Hello! How can I assist you today? [INFO] [1694675292.897892]: request: インターネットはどこにありますか? [INFO] [1694675300.168180]: assistant(token:183): インターネットは物理的な場所にあるのではなく,世界中のコンピューターがネットワークで結ばれている仮想的な空間です.インターネットは,データを送信し受け取るためのプロトコルとパケット交換技術を使用しています.つまり,インターネットは世界中のコンピューターネットワークの集合体です. [INFO] [1694675347.975610]: request: 世界中とは地球のことですか? [INFO] [1694675354.647098]: assistant(token:324): はい,正確に言えば,インターネットは地球上のコンピューターネットワークの集合体です.このネットワークには,地球上の様々な場所に設置されたサーバーやルーターなどの機器が含まれます.インターネットのデータ通信は,これらの機器を介して行われます. [INFO] [1694675387.510775]: request: あなたは誰ですか? [INFO] [1694675391.409440]: assistant(token:407): 私はAI(人工知能)ベースのヘルプフルなアシスタントです.質問や問題解決,情報の提供など,お手伝いできることがありましたらお知らせください. [INFO] [1694675436.047036]: request: どのようにヘルプフルですか? [INFO] [1694675447.019485]: assistant(token:645): 私は多くの情報と知識を持つAIですので,様々な質問や問題に対してお答えすることができます.例えば,旅行の予約や天気予報の確認,翻訳や研究のサポート,一般的な知識の提供など,さまざまな分野でお手伝いすることができます.また,自然言語処理の技術を利用して,あなたの質問や要求を理解し,最適な回答やソリューションを提供することも可能です.お困りのことや疑問があれば,いつでもお気軽にお知らせください. [INFO] [1694675485.687087]: request: 東京の明日の天気もわかりますか? [INFO] [1694675507.944942]: assistant(token:1064): もちろんです!東京の天気予報を調べてみましょう. 私はリアルタイムのデータにアクセスできるわけではありませんが,一般的に天気予報を提供する公式のウェブサイトやアプリを利用して,詳細な天気予報を確認することができます.天気予報は頻繁に更新されるため,事前に確認することをおすすめします.以下は一般的な天気予報サービスの利用方法です. - インターネット検索エンジンで「東京の天気予報」と検索すると,現在の天気情報と明日の天気予報を含む結果が表示されます. - スマートフォンやタブレットを使用している場合は,天気予報を提供するアプリをダウンロードしてインストールすることもできます.定番のアプリには「Weather」や「Weather Underground」などがあります. これらの方法を使用して,明日の東京の天気を確認してみてください.天気予報に関する詳細な情報を入手するためには,地元の気象庁や天気予報サービスの公式ウェブサイトを参照することもおすすめです. ^C[openai_chat-2] killing on exit [rosout-1] killing on exit [master] killing on exit shutting down processing monitor... ... shutting down processing monitor complete done robotuser@robotuser-PC:~$
ターミナル 2 : ROS Topic に発言をパブリッシュ
1つ目のターミナルで openai_chat.launch を起動したままの状態で2つ目のターミナルから ROS Topic をパブリッシュします.
robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "Hello" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "インターネットはどこにありますか ?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "世界中とは地球のことですか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "あなたは誰ですか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "どのようにヘルプフルですか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "東京の明日の天気もわかりますか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$
ターミナル 3 : チャットノードからの応答の ROS Topic を確認
3つ目のターミナルで ROS Topic /response
に Chat Completion API からの応答がパブリッシュされているかを確認します.コンソール出力では文字コード化して可読性がないですが Python で print()
や rospy.loginfo()
で出力すると ターミナル 1 のような読める日本語で表示されます.
robotuser@robotuser-PC:~$ rostopic echo /response data: "\u79C1\u306FAI\uFF08\u4EBA\u5DE5\u77E5\u80FD\uFF09\u30D9\u30FC\u30B9\u306E\u30D8\u30EB\ \u30D7\u30D5\u30EB\u306A\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u3067\u3059\u3002\u8CEA\ \u554F\u3084\u554F\u984C\u89E3\u6C7A\u3001\u60C5\u5831\u306E\u63D0\u4F9B\u306A\u3069\ \u3001\u304A\u624B\u4F1D\u3044\u3067\u304D\u308B\u3053\u3068\u304C\u3042\u308A\u307E\ \u3057\u305F\u3089\u304A\u77E5\u3089\u305B\u304F\u3060\u3055\u3044\u3002" --- data: "\u79C1\u306F\u591A\u304F\u306E\u60C5\u5831\u3068\u77E5\u8B58\u3092\u6301\u3064AI\u3067\ \u3059\u306E\u3067\u3001\u69D8\u3005\u306A\u8CEA\u554F\u3084\u554F\u984C\u306B\u5BFE\ \u3057\u3066\u304A\u7B54\u3048\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\ \u3002\u4F8B\u3048\u3070\u3001\u65C5\u884C\u306E\u4E88\u7D04\u3084\u5929\u6C17\u4E88\ \u5831\u306E\u78BA\u8A8D\u3001\u7FFB\u8A33\u3084\u7814\u7A76\u306E\u30B5\u30DD\u30FC\ \u30C8\u3001\u4E00\u822C\u7684\u306A\u77E5\u8B58\u306E\u63D0\u4F9B\u306A\u3069\u3001\ \u3055\u307E\u3056\u307E\u306A\u5206\u91CE\u3067\u304A\u624B\u4F1D\u3044\u3059\u308B\ \u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u307E\u305F\u3001\u81EA\u7136\u8A00\ \u8A9E\u51E6\u7406\u306E\u6280\u8853\u3092\u5229\u7528\u3057\u3066\u3001\u3042\u306A\ \u305F\u306E\u8CEA\u554F\u3084\u8981\u6C42\u3092\u7406\u89E3\u3057\u3001\u6700\u9069\ \u306A\u56DE\u7B54\u3084\u30BD\u30EA\u30E5\u30FC\u30B7\u30E7\u30F3\u3092\u63D0\u4F9B\ \u3059\u308B\u3053\u3068\u3082\u53EF\u80FD\u3067\u3059\u3002\u304A\u56F0\u308A\u306E\ \u3053\u3068\u3084\u7591\u554F\u304C\u3042\u308C\u3070\u3001\u3044\u3064\u3067\u3082\ \u304A\u6C17\u8EFD\u306B\u304A\u77E5\u3089\u305B\u304F\u3060\u3055\u3044\u3002" --- data: "\u3082\u3061\u308D\u3093\u3067\u3059\uFF01\u6771\u4EAC\u306E\u5929\u6C17\u4E88\u5831\ \u3092\u8ABF\u3079\u3066\u307F\u307E\u3057\u3087\u3046\u3002\n\n\u79C1\u306F\u30EA\ \u30A2\u30EB\u30BF\u30A4\u30E0\u306E\u30C7\u30FC\u30BF\u306B\u30A2\u30AF\u30BB\u30B9\ \u3067\u304D\u308B\u308F\u3051\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u304C\u3001\ \u4E00\u822C\u7684\u306B\u5929\u6C17\u4E88\u5831\u3092\u63D0\u4F9B\u3059\u308B\u516C\ \u5F0F\u306E\u30A6\u30A7\u30D6\u30B5\u30A4\u30C8\u3084\u30A2\u30D7\u30EA\u3092\u5229\ \u7528\u3057\u3066\u3001\u8A73\u7D30\u306A\u5929\u6C17\u4E88\u5831\u3092\u78BA\u8A8D\ \u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u5929\u6C17\u4E88\u5831\ \u306F\u983B\u7E41\u306B\u66F4\u65B0\u3055\u308C\u308B\u305F\u3081\u3001\u4E8B\u524D\ \u306B\u78BA\u8A8D\u3059\u308B\u3053\u3068\u3092\u304A\u3059\u3059\u3081\u3057\u307E\ \u3059\u3002\u4EE5\u4E0B\u306F\u4E00\u822C\u7684\u306A\u5929\u6C17\u4E88\u5831\u30B5\ \u30FC\u30D3\u30B9\u306E\u5229\u7528\u65B9\u6CD5\u3067\u3059\u3002\n\n- \u30A4\u30F3\ \u30BF\u30FC\u30CD\u30C3\u30C8\u691C\u7D22\u30A8\u30F3\u30B8\u30F3\u3067\u300C\u6771\ \u4EAC\u306E\u5929\u6C17\u4E88\u5831\u300D\u3068\u691C\u7D22\u3059\u308B\u3068\u3001\ \u73FE\u5728\u306E\u5929\u6C17\u60C5\u5831\u3068\u660E\u65E5\u306E\u5929\u6C17\u4E88\ \u5831\u3092\u542B\u3080\u7D50\u679C\u304C\u8868\u793A\u3055\u308C\u307E\u3059\u3002\ \n- \u30B9\u30DE\u30FC\u30C8\u30D5\u30A9\u30F3\u3084\u30BF\u30D6\u30EC\u30C3\u30C8\ \u3092\u4F7F\u7528\u3057\u3066\u3044\u308B\u5834\u5408\u306F\u3001\u5929\u6C17\u4E88\ \u5831\u3092\u63D0\u4F9B\u3059\u308B\u30A2\u30D7\u30EA\u3092\u30C0\u30A6\u30F3\u30ED\ \u30FC\u30C9\u3057\u3066\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3059\u308B\u3053\u3068\ \u3082\u3067\u304D\u307E\u3059\u3002\u5B9A\u756A\u306E\u30A2\u30D7\u30EA\u306B\u306F\ \u300CWeather\u300D\u3084\u300CWeather Underground\u300D\u306A\u3069\u304C\u3042\ \u308A\u307E\u3059\u3002\n\n\u3053\u308C\u3089\u306E\u65B9\u6CD5\u3092\u4F7F\u7528\ \u3057\u3066\u3001\u660E\u65E5\u306E\u6771\u4EAC\u306E\u5929\u6C17\u3092\u78BA\u8A8D\ \u3057\u3066\u307F\u3066\u304F\u3060\u3055\u3044\u3002\u5929\u6C17\u4E88\u5831\u306B\ \u95A2\u3059\u308B\u8A73\u7D30\u306A\u60C5\u5831\u3092\u5165\u624B\u3059\u308B\u305F\ \u3081\u306B\u306F\u3001\u5730\u5143\u306E\u6C17\u8C61\u5E81\u3084\u5929\u6C17\u4E88\ \u5831\u30B5\u30FC\u30D3\u30B9\u306E\u516C\u5F0F\u30A6\u30A7\u30D6\u30B5\u30A4\u30C8\ \u3092\u53C2\u7167\u3059\u308B\u3053\u3068\u3082\u304A\u3059\u3059\u3081\u3067\u3059\ \u3002" ---
ChatGPT に対して問い合わせる側が人間であれば応答から自分で文脈をふまえて次の会話をすると思いますが,クライアントプログラムの場合は文脈をふまえた会話をしたければクライアント側のソフトウェアも ROS Topic を拾って自分で文脈を記録して解釈する必要があります.
Chat Completion API との応答を ROS Topic を介してやり取りしているので,複数の Chat ノード(openai_chat_rostopic.py)を実行してトピックの remap
をして互いのノードの応答を自らのノードの入力にすれば ChatGPT 同士で会話を続けるようにすることも簡単にできます.
そのために openai_chat.launch を次のように変更しました.
<launch> <arg name="key" default="$(env OPENAI_API_KEY)" /> <arg name="model" default="gpt-3.5-turbo" /> <arg name="service" default="false" /> <arg name="opponent" default="false" /> <arg name="prompt" default="You are a helpful assistant." /> <node if="$(arg service)" pkg="openai_ros" type="openai_chat_server.py" name="openai_chat_service" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> </node> <group unless="$(arg service)"> <node pkg="openai_ros" type="openai_chat_rostopic.py" name="openai_chat_topic" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> <param name="prompt" value="$(arg prompt)" /> </node> <group ns="opponent" if="$(arg opponent)"> <node pkg="openai_ros" type="openai_chat_rostopic.py" name="openai_chat_topic"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> <param name="prompt" value="You are a good talker." /> <remap from="/opponent/request" to="/response" /> <remap from="/opponent/response" to="/request" /> </node> </group> </group> </launch>
opponent
で ChatGPT 同士の会話にするかを指定
output="screen"
はなしprompt
の設定でアシスタント同士だと会話が不自然な感じがしたので(とりあえず)2つ目のプロンプトは “You are a good talker.” としてみたremap
で /request
と /response
を入れ替えターミナル 1 : 2つのチャットノードの起動
openai_chat.launch の起動オプション opponent:=true
で2つのチャットノード実行とトピックの remap
を行います.
起動した状態では応答は何もないですが ターミナル 2 から ROS トピック /request
に最初のリクエストを1つパブリッシュすることで以後 ChatGPT 同士の会話が始まります.
robotuser@robotuser-PC:~$ roslaunch openai_ros openai_chat.launch opponent:=true ... logging to /home/robotuser/.ros/log/9cc9a1be-5dc8-11ee-9968-6b3ff6703622/roslaunch-robotuser-PC-35705.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://robotuser-PC:42615/ SUMMARY ======== PARAMETERS * /openai_chat_topic/key: sk-3JDluBbxsNuIhi... * /openai_chat_topic/model: gpt-3.5-turbo * /openai_chat_topic/prompt: You are a helpful... * /opponent/openai_chat_topic/key: sk-3JDluBbxsNuIhi... * /opponent/openai_chat_topic/model: gpt-3.5-turbo * /opponent/openai_chat_topic/prompt: You are a good ta... * /rosdistro: noetic * /rosversion: 1.16.0 NODES / openai_chat_topic (openai_ros/openai_chat_rostopic.py) /opponent/ openai_chat_topic (openai_ros/openai_chat_rostopic.py) auto-starting new master process[master]: started with pid [35714] ROS_MASTER_URI=http://localhost:11311 setting /run_id to 9cc9a1be-5dc8-11ee-9968-6b3ff6703622 process[rosout-1]: started with pid [35724] started core service [/rosout] process[openai_chat_topic-2]: started with pid [35731] process[opponent/openai_chat_topic-3]: started with pid [35732] [INFO] [1695882670.941923]: For 'system': You are a helpful assistant. [INFO] [1695882677.451265]: request: サッカーの盛んな国を1つ挙げてください. [INFO] [1695882679.335665]: assistant(token:60): ブラジルはサッカーの盛んな国として知られています. [INFO] [1695882705.653403]: request: そうですね,ブラジルは世界でも有名なサッカーの強豪国として知られています.ブラジルではサッカーは国民的なスポーツであり,多くの人々が熱狂的に応援しています. ブラジル代表チームは過去に5回のワールドカップ優勝を果たし,サッカーの歴史においても最も成功した国の一つです.有名な選手も多く輩出しており,ペレやジーコ,ロナウド,ロナウジーニョ,ネイマールなど,数々の伝説的なプレーヤーがブラジルから生まれています. ブラジルではサッカーの試合が行われると,町中が一体となって応援に熱が入ります.カラフルな応援旗やドラム,歌声,そして華麗なサンバの踊りなど,独特のエネルギーと情熱が試合会場を包みます. また,ブラジルには多くの有名なサッカークラブがあります.サンパウロのサンパウロFC,リオデジャネイロのフラメンゴ,サントス,リオグランデ・ド・スールのグレミオ,コリンチャンスなど,これらのクラブは強豪として名高いだけでなく,ファンの熱心さも有名です. ブラジルのサッカーは単なるスポーツ以上のものであり,国民の誇りやアイデンティティの一部となっています.サッカーを通じて,ブラジルの文化や人々の情熱を感じることができるでしょう. [INFO] [1695882713.741564]: assistant(token:754): その通りです.ブラジルのサッカーは国民の誇りであり,文化の一部として重要な役割を果たしています.多くの人々がサッカーに情熱を注ぎ,試合を熱狂的に応援する様子は見る価値があります.ブラジルのサッカーは世界中で愛され,その魅力は他の国にも広まっています.それだけに,ブラジルはサッカーの盛んな国として有名です. [INFO] [1695882744.670979]: request: そうですね,ブラジルのサッカーの魅力は世界中に広まっており,多くの人々がその情熱に共感しています.ブラジル代表チームやクラブチームの試合は,テレビやインターネットを通じて世界中に配信されており,多くのサッカーファンがその魅力に触れることができます. さらに,ブラジルのサッカー文化は技術,創造性,スピード,そしてリズム感を特徴としています.ブラジルのサッカー選手は驚くほど優れたテクニックを持ち,美しいプレーを見せることで知られています.彼らのキレのあるドリブル,正確なパス,そして豪快なシュートは,多くの人々に感動を与えます. ブラジルのサッカーの成功は,その国の熱狂的なサッカーカルチャーとも関連しています.子供たちは幼い頃からサッカーボールを蹴り,街角やビーチでプレーする様子をよく見かけます.サッカースクールやアカデミーも充実しており,若い才能は早いうちから育成されています. ブラジルのサッカーは単なるスポーツの一環ではなく,国民の誇りやアイデンティティの一部です.多くの人々が試合を通じて喜びや感動を共有し,サッカーを通じて結びついています.ブラジルのサッカー文化は他の国々にも影響を与え,彼らのスタイルやプレースタイルが憧れとなっています. ブラジルのサッカーは確かに盛んな国であり,その魅力は世界中に広がっています.それはブラジルの人々の情熱と才能,そしてサッカー文化の豊かさによるものです. [INFO] [1695882752.038028]: assistant(token:1519): 完全に同意します.ブラジルのサッカーカルチャーは,国民の情熱と才能,そして豊かなサッカー文化によって支えられています.その魅力は世界中に広まり,多くの人々がブラジルのサッカーに感動を覚えています.ブラジルのサッカーは間違いなく世界的な影響力を持っており,多くの国々で愛される存在です. [INFO] [1695882760.235372]: request: ありがとうございます.ブラジルのサッカーは確かに世界的な影響力を持っており,多くの人々に愛されています.その独特のスタイルと情熱は,他の国々のサッカーカルチャーにも大きな影響を与えています.ブラジルのサッカーは常に進化し,新たな才能が次々に生まれることで,さらなる魅力と成功を築いていくでしょう. ^C[opponent/openai_chat_topic-3] killing on exit [openai_chat_topic-2] killing on exit [rosout-1] killing on exit [master] killing on exit shutting down processing monitor... ... shutting down processing monitor complete done robotuser@robotuser-PC:~$
ターミナル 2 : 最初の話題投下
robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "サッカーの盛んな国を1つ挙げてください." publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$
ROS ノードグラフ
rqt の ROS Node Graph でノードとトピックの様子を確認してみると,2つのノード /openai_chat_topic
と /opponent/openai_chat_topic
とが互いの応答トピックを参照して循環していることが見て取れます.
ターミナル 1 の出力にも現れていますが段々と応答の文字数が互いに多くなる傾向があります.ChatGPT の token 数の上限に達して終わったりしますが,そうでない限りはずっと ChatGPT 同士で応答を続けるので終わらせたい場合は Ctrl-C で終わらせます.
文脈のデータを蓄積して多くなると Chat Completion API の token を消費してしまいますし,応答に時間がかかったりもします.一定時間会話がなかった場合やそれまでの文脈からがらりと話題を変える場合のために文脈を含めたメッセージデータを初期化するメソッドも運用上は必要かもしれません.
また,OpenAI 以外の大規模言語モデル(LLM)の API の ROS Topic ラッパがあれば(or を作れば)異なる LLM 間での会話も可能であろうと思います.
今回の記事はここまでです.
本シリーズ前回の記事 ChatGPT と ROS – 文書生成 ROS ラッパー生成編(Chat Completion API) では OpenAI の Chat Completion API を利用した ROS Service ラッパ Python プログラムを Web の方の ChatGPT の助けを借りて作成した様子を紹介しました.
しかし,前回のプログラムは Chat Completion API を用いているものの使い方は「1問1答」形式で,それは Completion API を利用しているのと大きく変わらず,「文脈をふまえたチャット」形式ではありませんでした.
そこで前回から発展させて Chat Completion API を利用した「文脈をふまえたチャット」をする ROS ソフトウェアを次の2つの方法で実装してみました.
今回の記事ではこれらのうち「1. ROS Service プログラムの文脈をふまえたチャット対応」を行った様子をお伝えします.
前回作成した Chat Completion API を利用しながらも「1問1答」形式だった ROS Service プログラムを「チャット」形式に対応させるて会話ができるように作り直します.
Chat Completion API を用いたチャット機能の実装については下記の Web 記事が ROS ではない Python プログラムですが大変参考になりました.
この記事によると Chat Completion API の応答を「チャット」の結果として機能させるためには「新しい問い」に一連の「チャットの全履歴」も含めて新たなメッセージとしたものを API に送ってリクエストする必要があります.
前回作成した ROS Service プログラムを発展させて,Chat Completion API を利用し履歴をふまえた ROS チャットソフトウェアを作成する場合,チャットの履歴を ROS Service のサーバ側で保持するか,サービスの呼び出し側で保持するか,システム的な最適解はさておき,プログラム的にはどちらの方法もあるかと思います.
今回作成した文脈をふまえたチャットに対応する ROS Service プログラムは Chat Completion API のシンプルな ROS ラッパーとしての位置づけは変えずにそのサービスを呼び出す ROS Node 側で履歴を保持して ROS Service にリクエストを送り Chat Completion API にアクセスする方法を採ってみました.
今回も Web サービスの ChatGPT の助けを借りてプログラムを作成したのですが,まるごと Python コードを作成するための問いの仕方が下手だったのか,ChatGPT の応答でしっくり来るようなソースコードは得られませんでした.そこで Python コードを記述する際の個別具体的な問題について ChatGPT の力を借りました.
例えば Chat Completion API へのメッセージやレスポンスを文字列(string)にしてやり取りする方法を聞きました.
User
Chat Completion API Python における引数 messages に渡すのは json 形式の text 文字列であっていますか?
ChatGPT
Chat Completion APIのmessages
引数に渡すものは,Pythonのリストとしてのデータ構造です.このリストは辞書のオブジェクトを要素として持ちます.通常,直接JSON形式のテキスト文字列を渡すのではなく,Pythonのデータ構造を使用します.
以下は,APIに渡すためのmessages
の例です:
messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Who won the world series in 2020?"} ]
もし,このリストがJSON形式の文字列として提供されている場合,Pythonのjson
モジュールを使ってデコードすることで,上記のようなPythonのデータ構造に変換することができます:
import json json_string = '[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Who won the world series in 2020?"}]' messages = json.loads(json_string)
OpenAIのAPIを直接使用する際や,HTTPリクエストを行う場合,リクエストのボディとしてJSON文字列を送信することがあります.その際には,上記のようにPythonのデータ構造をJSON文字列に変換(エンコード)したり,逆にJSON文字列からPythonのデータ構造に変換(デコード)したりする必要があります.しかし,多くのライブラリやSDKは,この変換を自動的に行ってくれるので,ユーザーが直接JSON文字列を扱う必要は少ないです.
User
json の python list を string に変換する場合はどうしますか?
ChatGPT
PythonのリストをJSON形式の文字列に変換するには,Pythonの標準ライブラリであるjson
モジュールのdumps
関数を使用します.
以下に具体的な手順を示します.
1. json
モジュールをインポートします.
import json
1. json.dumps
関数を使用してPythonのリストをJSON形式の文字列に変換します.
data = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Who won the world series in 2020?"} ] json_string = json.dumps(data) print(json_string)
この例では,data
という名前のPythonリストをJSON形式の文字列に変換し,それをjson_string
という変数に保存しています.
また,json.dumps
関数にはいくつかのオプション引数があります.例えば,indent
を指定することで,整形された(インデントされた)JSON文字列を取得することができます:
json_string_pretty = json.dumps(data, indent=4) print(json_string_pretty)
これにより,出力は読みやすい形式で表示されます.
User
単純に str(data) とするのと異なりますか?
ChatGPT
str(data)
とjson.dumps(data)
は,多くの場合において似た結果を返すことがありますが,実際には異なる振る舞いを持ち,それぞれ異なる目的に最適化されています.以下にその主な違いをいくつか示します.
1. Unicode Handling:
json.dumps()
は,Unicode文字列を適切にエンコードしてJSON互換のエスケープシーケンスにします.一方,str()
はPythonのUnicode文字列の内部表現をそのまま返すことがあります.
data = {"key": "あいうえお"} print(str(data)) # Python 2では "{'key': u'\u3042\u3044\u3046\u3048\u304a'}" print(json.dumps(data)) # '{"key": "あいうえお"}'
2. Boolean Values:
json.dumps()
は,PythonのTrue
とFalse
をそれぞれtrue
とfalse
としてJSONに変換します.str()
は,Pythonの表現をそのまま返します.
data = {"key": True} print(str(data)) # "{'key': True}" print(json.dumps(data)) # '{"key": true}'
3. Single vs Double Quotes:
Pythonの辞書を文字列として表現するとき,str()
はシングルクォート(‘)を使用します.しかし,JSON標準はプロパティ名や文字列値にダブルクォート(“)を使用するため,json.dumps()
はダブルクォートを使用します.
data = {"key": "value"} print(str(data)) # "{'key': 'value'}" print(json.dumps(data)) # '{"key": "value"}'
これらの違いから,PythonのデータをJSONとして扱いたい場合や,他のシステムとの互換性を確保したい場合は,json.dumps()
を使用するのが最適です.一方,デバッグ目的などでPythonの内部表現を文字列として見たいだけの場合は,str()
を使用することができます.
また,Chat Completion API へ送るメッセージデータの中のrole
の使い方についても ChatGPT に質問してみました.
Chat Completion API の渡し値の role における system と assistant の違いをおしえて?
ChatGPT
Chat Completion APIの文脈におけるrole
の値system
とassistant
は,入力メッセージのコンテキストを形成するためのものです.各role
は以下のような意味を持ちます:
1. system:
system
の役割は,環境やセッションの全体的な指示やコンテキストを設定するためのものです.{"role": "system", "content": "You are an assistant that speaks like Shakespeare."}
2. assistant:
assistant
の役割は,モデル自体の前の発言やレスポンスを示すためのものです.各役割は,APIを通じてモデルとの対話を形成するためのコンテキストを提供する役割を果たします.たとえば,モデルにシェイクスピアのように話すように指示するsystem
メッセージの後,ユーザーの質問に対するモデルの回答はシェイクスピアのスタイルで形成される可能性があります.
このように,role
の値を使用して,モデルとの対話のコンテキストや動作を微調整することができます.
文脈をふまえたチャット対応を行った ROS Service プログラムの作成や変更を行ったファイルは次のとおりです.
以下,それぞれのファイル内のコードを記載して少し説明をします.
srv / ChatCompletion.srv
messages
,受取るデータをresponse
というstring
型にしています.
string messages --- string response
scripts / openai_chat_server.py
chat_service
としてデータを JSON 形式の文字列として授受し,Chat Completion API へのアクセスを行っています.
#!/usr/bin/env python3 import rospy import openai import json from openai_ros.srv import ChatCompletion, ChatCompletionResponse def handle_chat_request(req): openai.api_key = rospy.get_param('~key') model = rospy.get_param('~model') res = ChatCompletionResponse() messages = json.loads(req.messages) response = openai.ChatCompletion.create( model=model, messages=messages ) return json.dumps(response) def chat_service(): rospy.init_node('chat_service') rospy.Service('chat_service', ChatCompletion, handle_chat_request) rospy.spin() if __name__ == "__main__": chat_service()
scripts / openai_chat_client.py
role
の「環境やセッションの全体的な指示やコンテキストを設定する」 system
に prompt
の内容を設定します.
ROS Node 内でユーザからのインプットを受け取り,これまでのメッセージ履歴に追加して,メッセージデータを JSON 文字列として ROS Service chat_service
に送っています.サービスからの応答データは JSON 文字列から Python リストに変換されて含まれていた返答メッセージはprint()
してメッセージ履歴にも追加することを繰り返しています.
#!/usr/bin/env python3 import sys, json import rospy from openai_ros.srv import ChatCompletion, ChatCompletionResponse def chat_client(prompt="You are a helpful assistant."): messages = [] messages.append({"role": "system", "content": str(prompt)}) rospy.wait_for_service('chat_service') try: chat_service_client = rospy.ServiceProxy('chat_service', ChatCompletion) except rospy.ServiceException as e: print ("Service call failed: %s" % e) while not rospy.is_shutdown(): # Get input from the user user_input = input("You: ") if user_input == "quit": exit() # Add user's input to the history messages.append({"role": "user", "content": user_input}) msg_string = json.dumps(messages) # Call the service try: res_msg = chat_service_client(msg_string) except rospy.ServiceException as e: print ("Service call failed: %s" % e) response = json.loads(res_msg.response) content = response["choices"][0]["message"]["content"] role = response["choices"][0]["message"]["role"] token = response["usage"]["total_tokens"] print("\n%s(token:%d): %s\n" % (role, token, content)) # Add GPT's response to the history messages.append({"role": str(role), "content": str(content)}) if __name__ == "__main__": if len(sys.argv) == 2: prompt = str(sys.argv[1]) else: prompt = "You are a helpful assistant." print("Type \'quit\' to quit.\n") print("For \'system\': %s\n" % (prompt)) rospy.init_node('chat_client') chat_client(prompt=prompt)
launch / openai_chat.launch
<launch> <arg name="key" default="$(env OPENAI_API_KEY)" /> <arg name="model" default="gpt-3.5-turbo" /> <node pkg="openai_ros" type="openai_chat_server.py" name="openai_chat" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> </node> </launch>
CMakeLists.txt
add_service_files( FILES Completion.srv ChatCompletion.srv ) generate_messages(
文脈をふまえたチャットへ対応させた ROS Service プログラムの実行例が次になります.
ターミナル1 : ROS Service サーバの起動
robotuser@robotuser-PC:~/openai_ws$ source ~/openai_ws/devel/setup.bash robotuser@robotuser-PC:~/openai_ws$ export OPENAI_API_KEY="sk-..." robotuser@robotuser-PC:~/openai_ws$ roslaunch openai_ros openai_chat.launch
ターミナル2 : ROS チャットノードの実行
robotuser@robotuser-PC:~/openai_ws$ source ~/openai_ws/devel/setup.bash robotuser@robotuser-PC:~/openai_ws$ rosrun openai_ros openai_chat_client.py Type 'quit' to quit. For 'system': You are a helpful assistant. You: hello assistant(token:27): Hello! How can I assist you today? You: ハロー assistant(token:56): こんにちは!どのようにお手伝いできますか? You: ボン・ニュイ assistant(token:115): ボン・ニュイ!夜にちょっと早い挨拶ですね.何かお手伝いできることはありますか? You: こんばんは をフランス語で言うと? assistant(token:163): 「こんばんは」をフランス語で言うと「Bonsoir」となります. You: bon nuit は日本語では? assistant(token:224): 「bon nuit」はフランス語で「良い夜」を意味しますが,日本語では「おやすみなさい」と訳されます. You: 間違えていました.ボンソワール assistant(token:338): 間違いありません.「ボンソワール」はフランス語で「こんばんは」という意味です.おやすみなさいの言い方は「ボンヌィ」となります.ごめんなさい,混乱を招いてしまいました.どうかお許しください. You: quit robotuser@robotuser-PC:~/openai_ws$
このように今回は ROS Node 内でのチャット会話になっています.
前回ベースにしている https://github.com/davesarmoury/openai_ros が1問1答形式の Completion API にアクセスする ROS Service を提供していたことから Chat Completion API にアクセスする ROS ソフトウェアも ROS Service 形式で作成してみました.
比較的長文で1問1答を行う Completion API であれば ROS Service や ROS Action (actionlib) のソフトウェアにするのも一理あるように思えます.しかし短文のチャットを扱う Chat Completion API へのアクセスであれば ROS Topic を介したメッセージのやり取りの方が ROS ノード内のチャット会話に限られず,より ROS 親和的かつよりシンプルな構成になるのでは?と思いました.
本シリーズ次回の記事では冒頭で述べました文脈をふまえたチャットを実現する2つの方法のもう一方の「ChatGPT と ROS – ROS Topic を介した ChatGPT チャットプログラム」を作成した様子を紹介する予定です.
今回の記事はここまでです.
本シリーズ前回の記事 ChatGPT と ROS – 文書生成 ROS ラッパー利用編(Completion API) では OpenAI API の ROS ラッパーの中で Completion API を利用している ROS1 の Python ラッパ https://github.com/davesarmoury/openai_ros を使ってみました.
今回は OpenAI の Chat Completion API を利用した ROS ラッパ Python プログラムを Web サービスの方の ChatGPT の助けをかりながら作成した様子を紹介します.
本シリーズ第1回の記事 ChatGPT と ROS – 調査編 でもふれましたが OpenAI の文書生成 API について簡単にまとめますと,「1問1答形式」の Completion API と,「対話した文脈を含むチャット対話形式」の Chat Completion API の2つがあります.
前回の記事ではこの2つのうち「1問1答」形式の Completion API を利用ました.今回の記事ではもう一方の「チャット対話」形式のインタフェースである Chat Completion API を ROS から利用してみます.
今回は Web サービスの ChatGPT に Chat Completion API を使ったプログラムを生成してもらいながら進めましたのでそれも含めて開発・実行環境の構成は次のようになっています.
まずは ChatGPT の Web サービスに ChatGPT の Python API にアクセスするための Python プログラムを書いてもらいました.
ChatGPT の Python API を用いたアクセスプログラムを書いてください.
ChatGPT
まず,OpenAIのPythonクライアントライブラリをインストールする必要があります.次のコマンドでインストールできます.
pip install openai
そして,以下はOpenAIのGPTを使用するための基本的なPythonコードです.この例では,プロンプトとして”Translate the following English text to French: ‘{}'”を使用し,その'{}’の部分に翻訳したい英文を挿入しています.
import openai openai.api_key = 'your-api-key' response = openai.ChatCompletion.create( model="gpt-4.0-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Translate the following English text to French: '{}'"} ] ) print(response['choices'][0]['message']['content'])
上記のコードは,APIキーを設定し,特定のモデル(この場合は”gpt-4.0-turbo”)を指定して,ChatCompletion APIを呼び出す基本的な構造を示しています.messagesパラメータは会話の履歴を表し,最初のメッセージは通常,モデルに役割(この場合は”helpful assistant”)を設定するためのシステムメッセージです.ユーザーメッセージはその後に続きます.
注意: ‘your-api-key’の部分は,OpenAIから提供される実際のAPIキーに置き換える必要があります.また,APIの使用は料金が発生しますので,公式ドキュメンテーションを確認して使用制限とコストを理解することが重要です.
なお,GPT-4のリリース後もAPIの基本的な使い方はこれまでと同様ですが,詳細な情報や更新内容についてはOpenAIの公式ドキュメンテーションをご覧ください.
次にこの ChatGPT にアクセスする Python コードを ROS の Service に対応させたプログラムを作成してもらいました.
この Python プログラムから ROS の service プログラムラッパーを作成してください.
ChatGPT
ROS (Robot Operating System)は,ロボットのためのフレームワークで,様々なハードウェアやソフトウェアの要素を組み合わせて使うことができます.ここでは,PythonのOpenAI GPT-4クライアントをROSサービスとしてラップする基本的な例を示します.
まず,適切なメッセージタイプを使用するためのsrvファイルを作成します.この例では,リクエストとレスポンスが共に一つの文字列を持つシンプルなサービスを定義します.
# GptService.srv string input --- string output
次に,このサービスを実装するPythonのROSノードを作成します.このノードは,指定された文字列を翻訳するサービスを提供します.
#!/usr/bin/env python import rospy from openai_ros.srv import GptService, GptServiceResponse import openai def handle_gpt_request(req): openai.api_key = 'your-api-key' response = openai.ChatCompletion.create( model="gpt-4.0-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Translate the following English text to French: '{}'".format(req.input)} ] ) return GptServiceResponse(response['choices'][0]['message']['content']) def gpt_service(): rospy.init_node('gpt_service') s = rospy.Service('gpt_service', GptService, handle_gpt_request) print("Ready to translate English text to French.") rospy.spin() if __name__ == "__main__": gpt_service()
このPythonスクリプトは,ROSノードgpt_serviceを作成し,それがGptServiceサービスを提供するようにします.サービスリクエストが来ると,handle_gpt_request関数が呼び出され,その中でOpenAIのGPT-4クライアントが使われてリクエストされたテキストを翻訳します.
このスクリプトをROSノードとして実行するためには,そのファイルに実行可能なパーミッションを与える必要があります.以下のコマンドで実行可能なパーミッションを与えることができます:
chmod +x gpt_service.py
また,OpenAIのAPIキーを設定する部分では,実際のキーに’your-api-key’を置き換える必要があります.そして,APIの使用は料金が発生しますので,公式ドキュメンテーションを確認して使用制限とコストを理解することが重要です.
なお,上記のコードはあくまで一例であり,具体的な使用シーンや要件によりますので,適宜修正や拡張を行ってください.
ChatGPT に最初に生成してもらった Python プログラムにおいてプロンプトの内容が固定されているため,それを基に生成した ROS ラッパープログラムも固定プロンプトへの応答結果を出すだけになってしまいました.
そこでプロンプトを変更可能なようにプログラムを変更してもらいました.
ChatGPT に問うプロンプトを変えられるようにプログラムを変更してください.
ChatGPT
もちろんです.以下に,GPTサービスリクエストにプロンプトを含めるように改変したROSサービスの例を示します.
まず,新たなsrvファイルを作成します.この例では,リクエストにプロンプトとインプットの2つの文字列を持つサービスを定義します.
# GptService.srv string prompt string input --- string output
次に,このサービスを実装するPythonのROSノードを作成します.
#!/usr/bin/env python import rospy from openai_ros.srv import GptService, GptServiceResponse import openai def handle_gpt_request(req): openai.api_key = 'your-api-key' response = openai.ChatCompletion.create( model="gpt-4.0-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "{} '{}'".format(req.prompt, req.input)} ] ) return GptServiceResponse(response['choices'][0]['message']['content']) def gpt_service(): rospy.init_node('gpt_service') s = rospy.Service('gpt_service', GptService, handle_gpt_request) print("Ready to handle GPT-4 requests.") rospy.spin() if __name__ == "__main__": gpt_service()
このPythonスクリプトは,ROSノードgpt_serviceを作成し,それがGptServiceサービスを提供するようにします.サービスリクエストが来ると,handle_gpt_request関数が呼び出され,その中でOpenAIのGPT-4クライアントが使われてリクエストされたプロンプトとテキストを処理します.
上記のコードを実行する前に,Pythonスクリプトに実行可能なパーミッションを与えることを忘れないでください.
ここまでの ChatGPT の応答で ROS の Python プログラムとして動きそうな感じのソースコードが生成されたように思いましたので実際に実行しながら修正してみることにしました.
今回は ChatGPT と ROS – 文書生成 ROS ラッパー利用編(Completion API) で利用した ROS パッケージ https://github.com/davesarmoury/openai_ros をベースに Chat Completion API を利用する機能を付け加えるかたちで進めました.
ChatGPT が生成した Chat Completion API を利用する ROS Python プログラムを使って Chat Completion API を利用できるよう ROS パッケージに変更を加えた箇所をまとめると次のようになります.
#!/usr/bin/env python
#!/usr/bin/env python3
openai.api_key = 'your-api-key'
openai.api_key = rospy.get_param('~key')
model="gpt-4.0-turbo",
model="gpt-3.5-turbo",
print("Ready to handle GPT-4 requests.")
print("Ready to handle GPT-3.5 requests.")
GptService.srv
の記述追加openai_chat_node.py
#!/usr/bin/env python3 import rospy from openai_ros.srv import GptService, GptServiceResponse import openai def handle_gpt_request(req): # openai.api_key = 'your-api-key' openai.api_key = rospy.get_param('~key') response = openai.ChatCompletion.create( # model="gpt-4.0-turbo", model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "{} '{}'".format(req.prompt, req.input)} ] ) return GptServiceResponse(response['choices'][0]['message']['content']) def gpt_service(): rospy.init_node('gpt_service') s = rospy.Service('gpt_service', GptService, handle_gpt_request) # print("Ready to handle GPT-4 requests.") print("Ready to handle GPT-3.5 requests.") rospy.spin() if __name__ == "__main__": gpt_service()
プログラムの本筋の部分は ChatGPT が生成したコードから修正の必要はありませんでした.
GptService.srv
# GptService.srv string prompt string input --- string output
CMakeLists.txt
add_service_files( FILES Completion.srv GptService.srv )
GptService.srv のサービスが利用できるように CMakeLists.txt に加筆しました.このあたりの修正箇所の洗い出しも ChatGPT に問うてみるのも修正規模が大きい場合にはありかもしれません.
openai_chat.launch
<launch> <arg name="key" default="$(env OPENAI_API_KEY)" /> <arg name="max_tokens" default="256" /> <arg name="model" default="gpt-4.0-turbo" /> <node pkg="openai_ros" type="openai_chat_node.py" name="openai_chat" output="screen"> <param name="key" value="$(arg key)" /> <param name="max_tokens" value="$(arg max_tokens)" /> <param name="model" value="$(arg model)" /> </node> </launch>
モデルを GPT-3.5 と GPT-4 で launch オプションで切り替えて使おうかと思っていたのですが,Web と API への課金は別らしく今回は API では GPT-3.5 のみ利用可能な状況でしたので openai_chat_node.py にモデル名を直書きしたまま使ってしまいました.
roslaunch openai_ros openai_chat.launch
を起動してからもう1つのターミナルで ROS サービスで rosservice call /gpt_service '{prompt: "(プロンプト)", input: "(内容)"}'
のように利用します.
output: "Mon nom est Robotuser."
ターミナル1
robotuser@robotuser-PC:~/openai_ws$ source ~/openai_ws/devel/setup.bash robotuser@robotuser-PC:~/openai_ws$ roslaunch openai_ros openai_chat.launch ... logging to /home/robotuser/.ros/log/9d61ced2-f54d-11ed-b5ca-c10df8d90fa9/roslaunch-robotuser-PC-41157.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://robotuser-PC:35595/ SUMMARY ======== PARAMETERS * /openai_chat/key: sk-3JDluBbxsNuIhi... * /openai_chat/max_tokens: 256 * /openai_chat/model: gpt-4.0-turbo * /rosdistro: noetic * /rosversion: 1.16.0 NODES / openai_chat (openai_ros/openai_chat_node.py) auto-starting new master process[master]: started with pid [41165] ROS_MASTER_URI=http://localhost:11311 setting /run_id to 9d61ced2-f54d-11ed-b5ca-c10df8d90fa9 process[rosout-1]: started with pid [41175] started core service [/rosout] process[openai_chat-2]: started with pid [41182] Ready to handle GPT-3.5 requests.
ターミナル2
robotuser@robotuser-PC:~/openai_ws$ source ~/openai_ws/devel/setup.bash robotuser@robotuser-PC:~/openai_ws$ rosservice call /gpt_service '{prompt: "Translate following to French:", input: "My name is Robotuser."}' output: "Mon nom est Robotuser." robotuser@robotuser-PC:~/openai_ws$ rosservice call /gpt_service '{prompt: "Translate following to Spanish:", input: "My name is Robotuser."}' output: "Mi nombre es Robotuser." robotuser@robotuser-PC:~/openai_ws$ rosservice call /gpt_service '{prompt: "Translate following to Japanese:", input: "My name is Robotuser."}' output: "\u79C1\u306E\u540D\u524D\u306F\u30ED\u30DC\u30C3\u30C8\u30E6\u30FC\u30B6\u30FC\u3067\ \u3059\u3002" robotuser@robotuser-PC:~/openai_ws$
このサービス利用例では “My name is Robotuser.” をフランス語,スペイン語,日本語に翻訳するよう各プロンプトを送りました.
日本語への翻訳指示した output が文字コード化していたので Unicode 変換すると次のようになりました.
私の名前はロボットユーザーです.
このように ChatGPT の Web サービスを利用してコードを生成してもらい,OpenAI の Chat Completion API を ROS から利用できるようになりました.
しかし今回のプログラムは Chat Completion API を用いているものの「1問1答」形式の使い方をしていて,それは Completion API を利用している場合と大きく変わらず, “文脈” をふまえた「チャット」形式ではありませんでした.
本シリーズ次回の記事では今回の Chat Completion API を利用する ROS サービスプログラムを文脈をふまえた「チャット」をする ROS プログラムに改造した様子をお伝えする予定です.
文書生成などで何かと話題の ChatGPT の ROS を介した利用について少し調べてみました.
本記事の筆者は AI に興味はあるものの利用したことがほとんどない立場ですが,ChatGPT などについて調べてみると AI 初心者でも OpenAI の API と ROS を介して文章を生成することができました.加えて ChatGPT の文章生成機能の力を借りてその API を使った ROS ラッパープログラムも作ることができました.その様子を次のようなトピックで複数回に分けて記事にします.
ChatGPT を提供しているのは OpenAI 社ですので “OpenAI” や “ChatGPT” でネット検索してみると OpenAI Gym の利用だったり,ChatGPT だったりといろいろとあるようでした.全く体系的・網羅的ではないと思うのですが,今回調査してみて得られた情報を大まかに分類してみると次のような構成になりました.
まず OpenAI Gym というのは強化学習を行うためのものでロボットの動作経路の学習などに用いられていて,今回の目的としていた文書生成とは異なる機能を提供しているようです.文書生成 API も Completion API と Chat Completion API と複数に分かれていました.
Web ページから ChatGPT-4 に Completion と Chat Completion の API の違いを聞いてみました.
Completion APIとChat Completion APIは,OpenAIのGPT(Generative Pretrained Transformer)をベースとしたテキスト生成APIです.しかし,それぞれの使用方法や特性は少し異なります.
Completion API:
Chat Completion API:
使用するAPIは,あなたがどのようなタスクを実行したいか,またはどのような出力を期待しているかによって異なります.
Chat Completion は名前の「チャット」のとおり文脈を含む対話形式で,Completion は1問1答形式のようです.
Completion と Chat Completion の違いについて対比的に解説されている記事もあり参考になりました.
ROS では OpenAI/ChatGPT がどのように使われているのか?ということで調べてみると,まず ROS Wiki にある openai_ros は強化学習の OpenAI Gym の機能を利用するパッケージのようでした.
また GitHub 内で “openai” と “ros” を組み合わせて検索してみると https://github.com/search?q=openai+ros&type=repositories&p=1 かなりヒットします.そのうち文書生成に関するリポジトリをピックアップしてリストにします.
これらの既存の OpenAI API の ROS ラッパーの中で Completion API を利用している ROS1 の Python ラッパ https://github.com/davesarmoury/openai_ros を使ってみた様子を次回の OpenAI と ROS の記事でお伝えする予定です.
今回の記事はここまでです.