こんにちは、石原と申します。
今回のブログが初投稿となります。最近注目を集めているChatGPTと普段使いしているエディタのVSCodeを連携させることで、コード開発の効率が大幅にアップしたので、その方法を紹介していきたいと思います。流行りのChatGPTに関してn番煎じの記事ではありますが、初めて触れる方でも導入できるよう、わかりやすく解説しますので、ぜひご覧ください!
ChatGPTとは
「ChatGPTとは、OpenAIが開発した人工知能チャットボットの名称です。このチャットボットは、自然言語処理技術を用いて、人間との対話を模倣することができます。
ChatGPTは、大量のテキストデータを学習することで、人間のような応答を生成することが可能です。そのため、一般的な質問や会話に対して、適切な回答を返すことができます。
また、ChatGPTは、様々な分野において活用されています。例えば、カスタマーサポートや教育分野において、人手不足の解消や効率化に貢献しています。
今後も、ChatGPTの技術は進化し続け、より高度な対話システムの実現に向けて、研究が進められていくことが期待されています。」
以上はChatGPTによって作成された文章になります。
ChatGPTは様々な分野への応用が期待されている注目の技術です。しかし、問題点もいくつか挙げられています。必ずしも正確な応答をするわけではないことや、多くの仕事をAIによって奪われてしまうことへの危機感、AIの学習やAIの生成物に対する著作権の問題、入力内容の情報漏洩に関するセキュリティ課題などが挙げられています。
特に、法人組織がChatGPTを利用する上では機密情報の取り扱いに注意する必要があり、従業員へChatGPTの利用を禁止している企業も増えています。実際にSamusungで情報漏洩が発生したという事例も確認されています。
なお、今回紹介する拡張機能ではChatGPTのAPIを用いています。OpenAIのAPIのデータ取り扱いポリシーによると、APIを介して送信されたデータは原則、モデルのトレーニングや改善には使われないとされていますので、心配する必要はないようです。また、Web版のChatGPTでもオプトアウト申請が可能なので法人組織の方はこちらもチェックしておいてください。
ChatGPTとVSCodeの連携
VSCodeの拡張機能マーケットプレイスにはChatGPTをサポートしたものがいくつか存在しています。元々gencay.vscode-chatgptという拡張機能があったようですが、自動ログイン機能が利用規約に抵触していたようで、こちらは現在開発中止になっています。
今回はgencay.vscode-chatgptの開発者に代替として挙げられているgenieai.chatgpt-vscodeを使います。こちらは現時点(2023/04/05)で最新のモデルであるGPT-4にも対応しています。
導入
それではgenieai.chatgpt-vscodeの導入方法について説明していきます。
ChatGPTは登録の際に3ヵ月間の$18分の無料クレジットが利用可能になります。この記事の内容を動かすくらいなら無料分でも十分です。
1.ChatGPTにアカウントを登録
必要な情報はメールアドレスと電話番号です。
メールアドレスの認証とパスワードの登録を完了させます。
氏名と電話番号を登録します。
2.APIキーを作成
APIキーは一度表示されたら再び表示されませんので注意が必要です。必ずコピーしておいてください。
[Create new secret key]ボタンより新規APIキーの作成ができます。組織設定からAPIキーの所属する組織を設定することもできます。
以下の例では個人アカウントで実行することを想定しています。
コピー忘れ等でAPIキーを削除したい場合は、新しいAPIキーを作成する必要があります。
しかし、最初に生成されたAPIキー以外を使用すると、無料枠のクレジットが反映されないという報告があります(参照)。無料分で試してみようという方は注意してください。
3.VSCodeに拡張機能genieai.chatgpt-vscodeをインストール
サイドバーのアイコンをクリックするとメッセージを入力できるようになります。
何かメッセージを入力するとAPIキーを求められるため、コピーしておいたAPIキーを入力します。
これで導入は完了です。
ChatGPTが有効になると、コードを選択してから右クリックすることでいくつかのプロンプトを実行可能になります。(ショートカットキーでも実行可能)
4.genieaiのカスタム方法
VSCodeのsetting.jsonを変更することでカスタムプロンプトを作成できます。
setting.jsonを変更するためにコマンドパレットを起動します。
コマンドパレットは全OS共通で[F1]キー、または、Windows/Linuxでは[Ctrl]+[Shift]+[P]キー、macOSでは[Command]+[Shift]+[P]キーで起動します。
Genie: Show settings
と入力してください。すると設定画面が表示されます。
ここからお好みの設定に変更可能です。
日本語化したい場合は、デフォルトのプロンプトのメッセージGenieai › Prompt Prefix
の項目を日本語で入力することで応答が日本語化されます。また、プロンプトメッセージによって応答が変わる場合もありますので、お好みで文章を入力してください。
私のsetting.jsonを載せておきますので参考にどうぞ。カスタムプロンプトはよく使うものを登録するとよいと思います。
{
"genieai.promptPrefix.findProblems": "次のコードの問題点を見つけてください",
//findProblemsは右クリックのメニューではFind Bugsに該当します
"genieai.promptPrefix.addTests": "次のコードにテストを追加してください",
"genieai.promptPrefix.optimize": "次のコードを最適化してください",
"genieai.promptPrefix.explain": "次のコードを説明してください",
"genieai.promptPrefix.addComments": "次のコードにコメントを追加してください",
"genieai.promptPrefix.completeCode": "次のコードを完成させてください",
"genieai.openai.temperature": 0.5,
//返答を生成する際に使用する言葉の多様性がこの数値によって決定されます。
//プログラムコードの場合は、通常0.5~0.7の間の値が最適とされています。
//1に近いほど独創的な表現を用いるようになります。
"genieai.promptPrefix.customPrompt1": "次のコードを○○してください",
//よく使うプロンプトを入力してください。
"genieai.promptPrefix.customPrompt1-enabled": true,
"genieai.promptPrefix.customPrompt2": "次の文章を○○してください",
//コード以外にも選択した文章に対して使えます。
"genieai.promptPrefix.customPrompt2-enabled": true
}
機能の紹介
コードを選択した状態で右クリックすると、以下のプロンプトをデフォルトで使用できます
- Add tests: テストの実装
- Find bugs: 問題の発見
- Optimize: 最適化
- Explain: 説明
- Add Comments: コメントの追加
- Complete code: コードの完成
- Ad-hoc prompt: コードに対して任意のプロンプトを入力できる
- カスタムプロンプトを2つまで設定できます。
- 以下はカスタムプロンプトの例です。よく使うプロンプトを登録しておくとよいでしょう。
- 翻訳
- 文章の推敲
- 類似表現の提案
- 以下はカスタムプロンプトの例です。よく使うプロンプトを登録しておくとよいでしょう。
ChatGPTによるコード作成
genieai.chatgpt-vscode
拡張機能で追加されたプロンプトを使ってみます。
まず、ソースコードを「簡単なRPGのプログラムをpythonで作成してください。勇者やモンスターはCharacterBaseクラスを継承したクラスとして定義してください。」で作成してもらいました。
ChatGPTは非決定論的AIと呼ばれており、同じプロンプトを入力しても返答に揺らぎがありますので以下は参考までにどうぞ。
以下が生成されたコードです。
import random
class CharacterBase:
def __init__(self, name, hp, attack):
self.name = name
self.hp = hp
self.attack = attack
def is_alive(self):
return self.hp > 0
def take_damage(self, damage):
self.hp -= damage
if self.hp < 0:
self.hp = 0
def attack_target(self, target):
damage = random.randint(1, self.attack)
target.take_damage(damage)
print(f"{self.name} attacks {target.name} for {damage} damage!")
class Hero(CharacterBase):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def heal(self):
self.hp += 10
print(f"{self.name} heals for 10 HP.")
class Monster(CharacterBase):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def main():
hero = Hero("Hero", 30, 5)
monster = Monster("Goblin", 20, 3)
while hero.is_alive() and monster.is_alive():
# Hero attacks first
hero.attack_target(monster)
if not monster.is_alive():
break
# Monster attacks second
monster.attack_target(hero)
if not hero.is_alive():
break
if hero.is_alive():
print("You win!")
else:
print("Game over.")
if __name__ == "__main__":
main()
この時点で、かなりまともなコードを出力できていますね。
せっかく、作ったhealメソッドは呼び出されていないようですが、これはAIの遊び心といったところでしょうか。
では、genieの拡張機能で実装されているプロンプトを試していきます。
以下では、コメントを追加してもらいました。(Add Comments
)
import random
class CharacterBase:
def __init__(self, name, hp, attack):
self.name = name
self.hp = hp
self.attack = attack
def is_alive(self):
return self.hp > 0
def take_damage(self, damage):
self.hp -= damage
if self.hp < 0:
self.hp = 0
def attack_target(self, target):
damage = random.randint(1, self.attack) # 攻撃力を元にランダムなダメージを与える
target.take_damage(damage)
print(f"{self.name} attacks {target.name} for {damage} damage!")
class Hero(CharacterBase):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def heal(self):
self.hp += 10 # HPを10回復する
print(f"{self.name} heals for 10 HP.")
class Monster(CharacterBase):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def main():
hero = Hero("Hero", 30, 5)
monster = Monster("Goblin", 20, 3)
while hero.is_alive() and monster.is_alive():
# Hero attacks first
hero.attack_target(monster) # 勇者がモンスターを攻撃
if not monster.is_alive():
break
# Monster attacks second
monster.attack_target(hero) # モンスターが勇者を攻撃
if not hero.is_alive():
break
if hero.is_alive():
print("You win!") # 勇者が生き残った場合は勝利
else:
print("Game over.") # 勇者が死亡した場合はゲームオーバー
if __name__ == "__main__":
main()
日本語のプロンプトで入力したせいか、コメントも日本語で追加されています。
追加された場所を見てみると、なかなか適切な場所にコメントがついている感じがします。
それではこのコードへのテストコードを作成してもらいました。(Add tests
)
import unittest
from unittest.mock import patch
from character import Hero
class TestHero(unittest.TestCase):
def setUp(self):
self.hero = Hero("Test Hero", 30, 5)
def test_is_alive(self):
self.assertTrue(self.hero.is_alive())
self.hero.hp = 0
self.assertFalse(self.hero.is_alive())
def test_take_damage(self):
self.hero.take_damage(10)
self.assertEqual(self.hero.hp, 20)
self.hero.take_damage(100)
self.assertEqual(self.hero.hp, 0)
@patch('builtins.print')
def test_heal(self, mock_print):
self.hero.heal()
self.assertEqual(self.hero.hp, 40)
mock_print.assert_called_with("Test Hero heals for 10 HP.")
if __name__ == '__main__':
unittest.main()
見たところHeroクラスのテストコードのようです。
実行コマンドも教えてくれました。
python -m unittest test_character.py
test_character.py
で新規ファイルとして保存し実行してみます。
ERROR: test_character (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: test_character
Traceback (most recent call last):
File "**\********\*****\python\lib\unittest\loader.py", line 154, in loadTestsFromName
module = __import__(module_name)
File "**\****\****\*****\test_character.py", line 3, in <module>
from character import Hero
ModuleNotFoundError: No module named 'character'
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
実行してみたところ、テストに失敗してしまいました。
テストコードのバグ発見をお願いしてみます。(Find bugs
)
このコードには問題点は見つかりませんでした。
ただし、このコードが正しく動作するためには、characterモジュール内にHeroクラスが定義されている必要があります。
また、このテストコードを実行する前に、characterモジュールをインポート可能な場所に配置する必要があります。
とのことなので、characterモジュールを作成してもらいます。(Ad-hoc prompt「次のコードをcharacterモジュールとして別ファイルに分割してください」
)
import random
class CharacterBase:
def __init__(self, name, hp, attack):
self.name = name
self.hp = hp
self.attack = attack
def is_alive(self):
return self.hp > 0
def take_damage(self, damage):
self.hp -= damage
if self.hp < 0:
self.hp = 0
def attack_target(self, target):
damage = random.randint(1, self.attack) # 攻撃力を元にランダムなダメージを与える
target.take_damage(damage)
print(f"{self.name} attacks {target.name} for {damage} damage!")
class Hero(CharacterBase):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def heal(self):
self.hp += 10 # HPを10回復する
print(f"{self.name} heals for 10 HP.")
class Monster(CharacterBase):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
characterbaseクラスのモジュール化をお願いしたところ、
実行するmain.py
も同時に出力してもらえました。気が利いていますね。
from character import CharacterBase, Hero, Monster
import random
def main():
hero = Hero("Hero", 30, 5)
monster = Monster("Goblin", 20, 3)
while hero.is_alive() and monster.is_alive():
# Hero attacks first
hero.attack_target(monster) # 勇者がモンスターを攻撃
if not monster.is_alive():
break
# Monster attacks second
monster.attack_target(hero) # モンスターが勇者を攻撃
if not hero.is_alive():
break
if hero.is_alive():
print("You win!") # 勇者が生き残った場合は勝利
else:
print("Game over.") # 勇者が死亡した場合はゲームオーバー
if __name__ == "__main__":
main()
では、Heroクラスのテストを実行してみます。
python -m unittest test_character.py
結果は次のようになりました。
python -m unittest test_character.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
というわけでHeroクラスのテストが完了しました。
さらにmain.pyを最適化してもらいます。(Optimize
)
from character import CharacterBase, Hero, Monster
import random
class Game:
def __init__(self):
self.hero = Hero("Hero", 30, 5)
self.monster = Monster("Goblin", 20, 3)
def play(self):
while self.hero.is_alive() and self.monster.is_alive():
# Hero attacks first
self.hero.attack_target(self.monster)
if not self.monster.is_alive():
break
# Monster attacks second
self.monster.attack_target(self.hero)
if not self.hero.is_alive():
break
if self.hero.is_alive():
print("You win!")
else:
print("Game over.")
def get_damage(self, attack_power):
critical_hit_chance = 0.2
is_critical_hit = random.random() < critical_hit_chance
base_damage = attack_power * (2 if is_critical_hit else 1)
return base_damage
if __name__ == "__main__":
game = Game()
game.play()
最適化してもらったことでコード全体の抽象度があがり、再利用性が向上したように感じられますね。
途中でヒールを使ってもらいたいのでコメントだけ書いて完成させてもらいます。(Complete code
)
下記コードを入力しました。
def play(self):
while self.hero.is_alive() and self.monster.is_alive():
# Hero attacks first
random_value = random.randint(1, 10)
# TODO
# random_valueによってhealとattack_targetをランダムに選択する
self.hero.attack_target(self.monster)
if not self.monster.is_alive():
break
# Monster attacks second
self.monster.attack_target(self.hero)
if not self.hero.is_alive():
break
if self.hero.is_alive():
print("You win!")
else:
print("Game over.")
結果は次のようになりました。
def play(self):
while self.hero.is_alive() and self.monster.is_alive():
# Hero attacks first
random_value = random.randint(1, 10)
if random_value <= 7:
self.hero.attack_target(self.monster)
else:
self.hero.heal()
if not self.monster.is_alive():
break
# Monster attacks second
self.monster.attack_target(self.hero)
if not self.hero.is_alive():
break
if self.hero.is_alive():
print("You win!")
else:
print("Game over.")
きちんとお願いした通りに実装されているようです。
パラメータが7以下の場合というのも極端過ぎない良いバランスのように思います。
では完成したmain.pyの説明を書いてもらいました。(Explain
)
このコードは、`character.py`ファイルで定義された`CharacterBase`、`Hero`、および`Monster`クラスをインポートしています。また、ランダムなダメージ計算を行うための`get_damage`メソッドが含まれる`Game`クラスを定義しています。
`Game`クラスのコンストラクタでは、`Hero`と`Monster`オブジェクトを作成し、それらを`self.hero`と`self.monster`属性に割り当てます。
`play`メソッドは、`Hero`と`Monster`が生きている限り、攻撃を交互に行い続けます。`random_value`変数によって、`Hero`が攻撃するか回復するかが決定されます。`is_alive`メソッドは、`CharacterBase`クラスに定義されており、キャラクターが生きているかどうかを判断します。`attack_target`メソッドは、攻撃対象に対して攻撃を行います。攻撃対象が死んでいる場合は、ループから抜けます。
`get_damage`メソッドは、与えられた攻撃力に基づいてダメージを計算します。このメソッドは、攻撃がクリティカルヒットになる確率を計算し、クリティカルヒットになった場合はダメージを2倍にします。
最後に、`if __name__ == "__main__":`ブロックで、`Game`オブジェクトを作成し、`play`メソッドを呼び出してゲームを開始します。
以上です。
このように、簡単なプログラム開発ならばChatGPTを利用することで爆速で出来ます。
普段の作業でVSCodeを愛用していて、ChatGPTに興味がある方は動かしてみるとその性能に驚くと思います。ぜひお試しください。
エクスチュアはマーケティングテクノロジーを実践的に利用することで企業のマーケティング活動を支援しています。
ツールの活用にお困りの方はお気軽にお問い合わせください。