本シリーズ前回の記事 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 プログラムに改造した様子をお伝えする予定です.