0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLM(Gemini)にSwitchBotを操作してもらう

Last updated at Posted at 2024-10-16

はじめに

Googleの生成AIであるGeminiのAPIを活用して、SwitchBotに登録されたデバイスをPythonで操作する仕組みを作成しました。
この仕組みの目的は、ユーザーのあらゆる言葉での指示を受けて、家電を操作することです。LLMはChatGPTにSwitchBotを操作してもらうにもある通り、高度な操作を行うことができると思っています。

自分なりに考えたロジックを今後見返す時のために作成しています。実装方法を全て書いていないので注意してください。

担当を分ける

プロジェクトでは、AIを「会話担当AI」と「SwitchBot取扱担当AI」の2つに分けています。この手法は段階的に処理を行うため、一種のCoT(Chain of Thought)手法とも言えるかもしれません。会話担当AIは、ユーザーからの自然な指示を受け取り、その意図を解釈してSwitchBot取扱担当AIに指示を出す役割を持っています。一方で、SwitchBot取扱担当AIは、その指示を受けてSwitchBotのAPIを操作します。

この分業によるメリットは、会話担当AIはSwitchBotの詳細な仕様を知らずに、ただ「SwitchBot取扱担当AIに指示を出す」という単純な説明を把握するだけでよくなります。そのため、余分なトークン消費を避けることができたり、スケーラブルにすることができます。また、GeminiのAPIとルールベースを組み合わせることで、トークン消費を抑えつつ、安定性を高めながら効率化を図っています。

つまり、マニュアル全体を一度に読むのではなく、必要な部分のみを適宜読み込むことにより、トークン消費の削減と迅速な対応を可能にしています。

簡単に言うと、会話担当AIがSwitchBot取扱担当AI(関数)を呼び出す仕組みです。

どのようなロジックか

SwitchBot APIの仕組みはわかりやすいですが、デバイスIDやマニュアルがそれぞれ決まっているので、ある程度ルールに則る必要があります。よって、SwitchBotを操作する仲介にGeminiを組み込むアプローチを試しています。

以下のような仕組みを取っています。記事に書いたのは赤い枠です(細くて見えづらいです)。
gemini_switchbot.png
赤い四角枠が「SwitchBot取扱担当AI」です。関数呼び出し用としての一つの関数になります。

そのため入力となる引数(オーダー)はGeminiが生成します。例えば、「リビングの電気をつける」や「寝室の気温を調べる」などがオーダーです。

デバイス情報リストは、ユーザーがあらかじめ登録してあるSwitchBotのデバイスリストです。

四角形は関数です。青色はGeminiが組み込まれています。紫色はルールベース(通常の関数)です。

唯一ある分岐はオーダーの趣旨が(Geminiによって判断されたモード)「ステータスの取得」か「デバイスの操作」です。

つまり、指示が操作または取得か判断し、選んだマニュアルに従って実行するのがSwitchBot取扱担当AIということです。
この流れであれば、GeminiはSwitchBot APIのパラメータを的確に指定してくれるはずです。

ロジックの補足

オーダーは毎回一つにされます。例えば、「リビングの気温を調べて、あと電気つけて」と言う場合、「リビングの気温を調べる」+「リビングの照明をつける」と二回に分けて関数呼び出しするようにします。
しかし問題ないです。会話担当AIが関数呼び出しする際、一つずつオーダーするように説明しておきます(OpenAPIのdescriptionに記載する)。
AIを使った関数は、生成AIではなく分類AIの方がいいと思いますが、ここではGeminiを使っています。

大まかな手順

手順はメインではありませんが、特徴的な部分だけ取り上げています。
GeminiやSwitchBot APIの使い方は省略します。
Google AI StudioSwitchBot API-GitHubが参考です。

以下はフローチャートにおける(1)の関数です。

def extract_device_name(order,device_list):
    # Create a list containing only deviceName
    device_name_list = [device["deviceName"] for device in device_list]

    input_content = f"""
    'deviceName' = Select the deviceName that「」is pointing to from [{str(device_name_list)}].
    'mode' = Select and input 'Get_status' or 'control_divice' that「」is pointing to.
    「{order}"""

    generation_config = {
        "response_schema": content.Schema(
            type = content.Type.OBJECT,
            properties = {
                "deviceName": content.Schema(type = content.Type.STRING),
                "mode": content.Schema(type = content.Type.STRING)}),
                "response_mime_type": "application/json",
    }

    response = switchbot_model.generate_content(contents=input_content, generation_config=generation_config)
    
    # 'Get_status' or 'control_divice' in mode
    response_dict = json.loads(response.text)

    for device in device_list: # searching
        if device["deviceName"] == response_dict["deviceName"]:
            break
            
    return device,response_dict["mode"]

device_listにはSwitchBot APIを使った SwitchBot API Get device listにある方法で取得したリストを調整したものです。device_name_listは、そこから名前だけ入れます。
JSONモードのGeminiから辞書列を取得しdeviceNameの一致する「デバイス情報」+「ステータスの取得かデバイスの操作か」を得ます。

以下はフローチャートにおける(2)の関数です。

def send_command_to_device(control_manual,order):

    input_content = f"""
        Extract or fill in the parameters from the user command '{order}' based on the following recommendation.
        Select the best fitting 'command', 'commandType', and 'commandParameter' from {json.dumps(control_manual, ensure_ascii=False)}.
    """

    generation_config = {
        "response_schema": content.Schema(
            type = content.Type.OBJECT,
            properties = {
                "deviceID": content.Schema(type = content.Type.STRING),
                "commandType": content.Schema(type = content.Type.STRING),
                "command": content.Schema(type = content.Type.STRING),
                "parameter": content.Schema(type = content.Type.STRING)
                }),
                "response_mime_type": "application/json"
        }
    
    response = switchbot_model.generate_content(contents=input_content, generation_config=generation_config)
    
    response_dict = json.loads(response.text)

    device_id = response_dict["deviceID"]
    command_type = response_dict["commandType"]
    command = response_dict["command"]
    command_parameter = response_dict["parameter"]

    response = control_divice(device_id,command,command_parameter,command_type)

    return response

この関数では、SwitchBot APIを操作するためのパラメータを指定するフェーズです。
ここでもGeminiをJsonモードにします。
control_diviceはSwitchBot APIを利用して実際に操作する関数です。(抜粋のため、定義はここにありません。)

まとめ

生成AIとルールベースを交互に組み、担当者を分けることで、トークン消費を抑えて安定させることに成功しました。この方法であれば、SwitchBot APIが大幅な変更をしない限り、マニュアルを追加するだけで、新製品にも対応できると考えています。エラーの対策があまりできていないため、改良していきたいです。また、フロントエンドも作成したいです。今後も様々な方法を試してみたいです。

記事に不備があれば、お知らせください。
ご覧いただきありがとうございました!

参考

Google AI Studio
SwitchBot API-GitHub

※この記事は一部ChatGPTと共同で作成しました。
ChatGPT

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?