🐡

[2023年4月版]Auto-GPTのコードを解き明かす。驚異のAIハック!~その2

2023/04/18に公開

引き続き Auto-GPT のコードをざっくりと読み解いてみる

前回に引き続きローカルに構築したAuto-GPTが、どのようなことをやっているのか、元のPythonのコードに対してGPT-4にコメントをつけてもらいながら読み解いていきたいと思います。

各処理解説 の続き

3-2. AIの結果からアシスタントの思考を表示

AIからの応答を受けてアシスタントの思考をおこなう箇所です。この関数はmain.pyに定義されています。

scritps/main.py#L.61
def print_assistant_thoughts(assistant_reply):
    """アシスタントの思考をコンソールに出力する"""
    global ai_name
    global cfg
    try:
        try:
            # アシスタントの応答を解析し、出力する
            assistant_reply_json = fix_and_parse_json(assistant_reply)
        except json.JSONDecodeError as e:
            # JSONデコードエラーが発生した場合、JSONを修正して再度解析を試みる
            logger.error("Error: Invalid JSON in assistant thoughts\n", assistant_reply)
            assistant_reply_json = attempt_to_fix_json_by_finding_outermost_brackets(assistant_reply)
            assistant_reply_json = fix_and_parse_json(assistant_reply_json)

        # assistant_reply_json が文字列の場合、JSON オブジェクトに解析を試みる
        if isinstance(assistant_reply_json, str):
            try:
                assistant_reply_json = json.loads(assistant_reply_json)
            except json.JSONDecodeError as e:
                # JSONデコードエラーが発生した場合、JSONを修正して再度解析を試みる
                logger.error("Error: Invalid JSON\n", assistant_reply)
                assistant_reply_json = attempt_to_fix_json_by_finding_outermost_brackets(assistant_reply_json)

        # 必要な情報を抽出
        assistant_thoughts_reasoning = None
        assistant_thoughts_plan = None
        assistant_thoughts_speak = None
        assistant_thoughts_criticism = None
        assistant_thoughts = assistant_reply_json.get("thoughts", {})
        assistant_thoughts_text = assistant_thoughts.get("text")

        if assistant_thoughts:
            # アシスタントの思考から、理由、計画、批評、話す内容を取得
            assistant_thoughts_reasoning = assistant_thoughts.get("reasoning")
            assistant_thoughts_plan = assistant_thoughts.get("plan")
            assistant_thoughts_criticism = assistant_thoughts.get("criticism")
            assistant_thoughts_speak = assistant_thoughts.get("speak")

        # 各情報を出力
        logger.typewriter_log(f"{ai_name.upper()} THOUGHTS:", Fore.YELLOW, assistant_thoughts_text)
        logger.typewriter_log("REASONING:", Fore.YELLOW, assistant_thoughts_reasoning)

        if assistant_thoughts_plan:
            logger.typewriter_log("PLAN:", Fore.YELLOW, "")
            # リストの場合は文字列に結合する
            if isinstance(assistant_thoughts_plan, list):
                assistant_thoughts_plan = "\n".join(assistant_thoughts_plan)
            elif isinstance(assistant_thoughts_plan, dict):
                assistant_thoughts_plan = str(assistant_thoughts_plan)

            # 改行とダッシュを使って input_string を分割
            lines = assistant_thoughts_plan.split('\n')
            for line in lines:
                line = line.lstrip("- ")
                # 各行を整形して出力
                logger.typewriter_log("- ", Fore.GREEN, line.strip())

        logger.typewriter_log("CRITICISM:", Fore.YELLOW, assistant_thoughts_criticism)
        # アシスタントの思考を話す
        if cfg.speak_mode and assistant_thoughts_speak:
            speak.say_text(assistant_thoughts_speak)

        return assistant_reply_json
    # JSONデコードエラーが発生した場合、エラーメッセージを出力
    except json.decoder.JSONDecodeError as e:
        logger.error("Error: Invalid JSON\n", assistant_reply)
        if cfg.speak_mode:
            speak.say_text("I have received an invalid JSON response from the OpenAI API. I cannot ignore this response.")

    # その他のエラーは "Error: + エラーメッセージ" として返す
    except Exception as e:
        call_stack = traceback.format_exc()
        logger.error("Error: \n", call_stack)

この print_assistant_thoughts は、アシスタントの返信(assistant_reply)を受け取り、アシスタントの思考をコンソールに出力してます。だいたい処理の内容は以下の通りです。

  1. アシスタントの返信を解析し、JSONオブジェクトに変換
  2. 必要な情報(アシスタントの思考、理由、計画、批評、話す内容)を抽出
  3. 各情報をコンソールに出力します。計画の場合は、リストや辞書から適切な形式に変換して出力
  4. 設定が有効な場合、アシスタントの思考を話す機能を実行

アシスタントの返信を解析して、その思考や計画、批評などの情報をユーザーに情報提供します。

JSON のパース

JSONのパースにもちょっと工夫がありましたのでご紹介いたします。
まず、上記で使用されている fix_and_parse_json の実装を以下に示します。

scripts/json_parser.py#L.30
def fix_and_parse_json(
    json_str: str,
    try_to_fix_with_gpt: bool = True
) -> Union[str, Dict[Any, Any]]:
    """JSON文字列を修正して解析する"""
    try:
        json_str = json_str.replace('\t', '')
        return json.loads(json_str)
    except json.JSONDecodeError as _:  # エラーが発生した場合
        try:
            json_str = correct_json(json_str)
            return json.loads(json_str)
        except json.JSONDecodeError as _:  # エラーが再発した場合
            pass
    # 手動で何かしらの対処を行う
    # 時々、GPTは波括弧の前に何かを返すことがある
    # 例: "I'm sorry, I don't understand. Please try again."
    # {"text": "I'm sorry, I don't understand. Please try again.",
    #  "confidence": 0.0}
    # 最初の波括弧を見つけて、残りの文字列を解析しようとする
    try:
        brace_index = json_str.index("{")
        json_str = json_str[brace_index:]
        last_brace_index = json_str.rindex("}")
        json_str = json_str[:last_brace_index+1]
        return json.loads(json_str)
    # json_strに"{"や"}"がない場合、ValueErrorが発生することがある
    except (json.JSONDecodeError, ValueError) as e:  
        if try_to_fix_with_gpt:
            logger.warn("Warning: Failed to parse AI output, attempting to fix."
                  "\n If you see this warning frequently, it's likely that"
                  " your prompt is confusing the AI. Try changing it up"
                  " slightly.")
            # ai_functionsを使って修正を試みる
            ai_fixed_json = fix_json(json_str, JSON_SCHEMA)

            if ai_fixed_json != "failed":
                return json.loads(ai_fixed_json)
            else:
                # AIにエラーメッセージを反映させることで、通常は修正が行われる
                logger.error("Failed to fix AI output, telling the AI.")
                return json_str
        else:
            raise e

この関数は、与えられたJSON文字列(json_str)を修正して解析しようとしております。
最初に、文字列からタブ文字を削除し、JSONとして解析を試みます。解析に失敗した場合、correct_json 関数を使ってJSON文字列を修正し、再度解析を試みます。それでも解析に失敗した場合、手動でJSON文字列の最初と最後の波括弧を見つけて、それらの間の文字列を解析します。
それでも失敗した場合は最後に、GPTを使ってJSONを修正し、修正に成功した場合、修正されたJSONを返します。
GPTでの修正に失敗した場合、元のJSONを返却します。

GPTを使ってJSONを修正

AIを使ってJSONをパースさせるのがなかなか面白いので実装を以下に紹介いたします。

まず、ほしいJSONの型を定数として定義しておきます。

scripts/json_parser.py#L.10
JSON_SCHEMA = """
{
    "command": {
        "name": "command name",
        "args":{
            "arg name": "value"
        }
    },
    "thoughts":
    {
        "text": "thought",
        "reasoning": "reasoning",
        "plan": "- short bulleted\n- list that conveys\n- long-term plan",
        "criticism": "constructive self-criticism",
        "speak": "thoughts summary to say to user"
    }
}
"""

続いてこのスキーマに合うようにJSONをパース指せている処理が以下です。
これを呼び出す際に、上で定義したスキーマを引数に渡してます。

scripts/json_parser.py#L.78
def fix_json(json_str: str, schema: str) -> str:
    """与えられたJSON文字列を解析可能で、提供されたスキーマに完全に準拠するように修正する"""
    # GPTを使ってJSONを修正しようとする
    function_string = "def fix_json(json_str: str, schema:str=None) -> str:"
    args = [f"'''{json_str}'''", f"'''{schema}'''"]
    description_string = "Fixes the provided JSON string to make it parseable"\
        " and fully compliant with the provided schema.\n If an object or"\
        " field specified in the schema isn't contained within the correct"\
        " JSON, it is omitted.\n This function is brilliant at guessing"\
        " when the format is incorrect."

    # "`"で始まっていない場合は追加する
    if not json_str.startswith("`"):
        json_str = "```json\n" + json_str + "\n```"
    result_string = call_ai_function(
        function_string, args, description_string, model=cfg.fast_llm_model
    )

    try:
        json.loads(result_string)  # 有効性を確認するだけ
        return result_string
    except:  # noqa: E722
        # コールスタックを取得する
        # import traceback
        # call_stack = traceback.format_exc()
        # print(f"Failed to fix JSON: '{json_str}' "+call_stack)
        return "failed"

で、実際にGPTに処理をさせる箇所は以下のような実装になっております。

GPTに "Python関数" 役をさせる

そして呼び出されるのが以下の関数。

scripts/call_ai_function.py
# これは、ノーコードで何でもできる魔法の関数です。詳細は
# https://github.com/Torantulino/AI-Functions を参照してください。
def call_ai_function(function, args, description, model=None):
    """AI関数を呼び出す"""
    if model is None:
        model = cfg.smart_llm_model
    # 各引数について、Noneの場合は"None"に変換する
    args = [str(arg) if arg is not None else "None" for arg in args]
    # 引数をカンマ区切りの文字列に変換
    args = ", ".join(args)
    # あなたは次のPython関数になりました。`return`値のみで応答してください。
    messages = [
        {
            "role": "system",
            "content": f"You are now the following python function: ```# {description}\n{function}```\n\nOnly respond with your `return` value.",
        },
        {"role": "user", "content": args},
    ]
    # GPT の completion APIを呼ぶ
    response = create_chat_completion(
        model=model, messages=messages, temperature=0
    )
    return response

このように関数の外部仕様をしっかりと定義を与え、なんとなくやってほしい説明を記述するとそれ通りにGPTにふるまってもらえるまさに魔法の関数です。
GPTに”あなたは次のPython関数になりました”とPython関数の役割を与えることで、実装なしで"それっぽい"挙動をしてもらえるようになっております。
このようにJSONをパースする"振る舞い"を記述することで実装をすっ飛ばしてAIに結果を出力させることができます。
まさにAI。すんぎょい…

以上で、返答のJSONを(無理やり)パースして画面に表示するところまで完了です。

3-3. コマンドを取得

次に実行するべきコマンドを応答のJSONから抽出します。
これは上記で挙げたJSONのスキーマにコマンドが含まれているので、そこからコマンドを抜粋しているだけです。
一応、再掲しますと…

scripts/json_parser.py#L.10
JSON_SCHEMA = """
{
    "command": {
        "name": "command name",
        "args":{
            "arg name": "value"
        }
    },
...

このように元の返答JSONにすでに次のアクションが含まれておりまして、ここを取り出すだけなんですが…
一応、コマンドの抽出の実装をご紹介いたします。

scripts/commands.py#L.27
def get_command(response):
    """応答を解析し、コマンド名と引数を返す"""
    try:
        # 応答からJSONを修正し、解析する
        response_json = fix_and_parse_json(response)
        # 'command'オブジェクトがJSONにない場合はエラーを返す
        if "command" not in response_json:
            return "Error:", "JSONに'command'オブジェクトがありません"
        command = response_json["command"]
        # 'command'オブジェクトに'name'フィールドがない場合はエラーを返す
        if "name" not in command:
            return "Error:", "'command'オブジェクトに'name'フィールドがありません"
        # コマンド名を取得する
        command_name = command["name"]
        # 'command'オブジェクトに'args'フィールドがない場合は空の辞書を使用する
        arguments = command.get("args", {})
        return command_name, arguments
    # JSONのデコードエラーの場合はエラーメッセージを返す
    except json.decoder.JSONDecodeError:
        return "Error:", "無効なJSON"
    # その他のエラーの場合は "Error: + エラーメッセージ" を返す
    except Exception as e:
        return "Error:", str(e)

まぁ、スキーマ通りのJSONから command_nameargumentsを返却する関数となっております。
ここでも fix_and_parse_json が呼ばれてJSONのパースに失敗した場合はGPTによるJSONの補正が働くようになってますね。

コマンドの決定フェーズは以上です。

3-4. ユーザーからの入力を受け付ける

ユーザーからの入力受付の箇所はあまり特筆するべきところはありませんが、一応、実装コードを再掲しておきます。

scripts/main.py#L.360
        # キー入力を取得:ユーザーにEnterキーを押して続行するか、Escキーを押して終了するかを促す
        user_input = ""
        logger.typewriter_log(
            "NEXT ACTION: ",
            Fore.CYAN,
            f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL}  ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}")
        # ユーザーに次のアクションについての入力を促すメッセージを表示
        print(
            f"Enter 'y' to authorise command, 'y -N' to run N continuous commands, 'n' to exit program, or enter feedback for {ai_name}...",
            flush=True)
        while True:
            # ユーザーからの入力を取得し、入力プロンプトに色を付ける
            console_input = utils.clean_input(Fore.MAGENTA + "Input:" + Style.RESET_ALL)
            # 入力が 'y' の場合、次のコマンドを生成するよう指示する
            if console_input.lower().rstrip() == "y":
                user_input = "GENERATE NEXT COMMAND JSON"
                break
            # 入力が 'y -N' の形式の場合、N回連続でコマンドを実行するよう指示する
            elif console_input.lower().startswith("y -"):
                try:
                    next_action_count = abs(int(console_input.split(" ")[1]))
                    user_input = "GENERATE NEXT COMMAND JSON"
                # 入力が無効な形式の場合、エラーメッセージを表示してループを続ける
                except ValueError:
                    print("Invalid input format. Please enter 'y -n' where n is the number of continuous tasks.")
                    continue
                break
            # 入力が 'n' の場合、プログラムを終了するよう指示する
            elif console_input.lower() == "n":
                user_input = "EXIT"
                break
            # それ以外の入力の場合、その入力をフィードバックとして受け取り、コマンド名を "human_feedback" に設定する
            else:
                user_input = console_input
                command_name = "human_feedback"
                break

ユーザーにキー入力を促し、console_inputに入ってくるキーボードからの入力をパースし、整形したものをuser_inputとして次の処理に引き継いでおります。
yy -数字n、"自由入力"、の4パターンに対応してますね。

また、キーボード入力の取得処理は以下のようになっております。

scripts/utils.py#L.1
def clean_input(prompt: str=''):
    try:
        return input(prompt)
    except KeyboardInterrupt:
        print("You interrupted Auto-GPT")
        print("Quitting...")
        exit(0)

input関数でキー入力を取得しますが、中断の例外が発生した場合はプログラムの終了をしております。

ユーザーからの入力受付のパートは以上ですね。

3-5. ユーザーの入力に応じてコマンドを実行

いよいよコマンド実行の箇所です。
コマンド実施の箇所はわかりにくいですが以下のところが入り口です。

scripts/main.py#L.414
        result = f"Command {command_name} returned: {cmd.execute_command(command_name, arguments)}"

この returned: {cmd.execute_command(command_name, arguments)} がコマンド実行の呼び出し個所となっております。
execute_commandの実装は以下の通りです。

scripts/commands.py#L.1
def execute_command(command_name, arguments):
    """コマンドを実行し、結果を返す"""
    memory = get_memory(cfg)

    try:
        if command_name == "google":

            # Google APIキーが設定されている場合、公式の検索方法を使用
            # APIキーが設定されていないか、空白のみの場合は、非公式の検索方法を使用
            if cfg.google_api_key and (cfg.google_api_key.strip() if cfg.google_api_key else None):
                return google_official_search(arguments["input"])
            else:
                return google_search(arguments["input"])
        elif command_name == "memory_add":
            return memory.add(arguments["string"])
        elif command_name == "start_agent":
            return start_agent(
                arguments["name"],
                arguments["task"],
                arguments["prompt"])
        elif command_name == "message_agent":
            return message_agent(arguments["key"], arguments["message"])
        elif command_name == "list_agents":
            return list_agents()
        elif command_name == "delete_agent":
            return delete_agent(arguments["key"])
        elif command_name == "get_text_summary":
            return get_text_summary(arguments["url"], arguments["question"])
        elif command_name == "get_hyperlinks":
            return get_hyperlinks(arguments["url"])
        elif command_name == "read_file":
            return read_file(arguments["file"])
        elif command_name == "write_to_file":
            return write_to_file(arguments["file"], arguments["text"])
        elif command_name == "append_to_file":
            return append_to_file(arguments["file"], arguments["text"])
        elif command_name == "delete_file":
            return delete_file(arguments["file"])
        elif command_name == "search_files":
            return search_files(arguments["directory"])
        elif command_name == "browse_website":
            return browse_website(arguments["url"], arguments["question"])
        # TODO: ファイル入力を受け取るように変更して、コードを入力する代わりにファイルを使用
        # 非ファイルが与えられた場合、指示を返す "入力はPythonファイルパスである必要があります。コードをファイルに書き込み、もう一度試してください。"
        elif command_name == "evaluate_code":
            return ai.evaluate_code(arguments["code"])
        elif command_name == "improve_code":
            return ai.improve_code(arguments["suggestions"], arguments["code"])
        elif command_name == "write_tests":
            return ai.write_tests(arguments["code"], arguments.get("focus"))
        elif command_name == "execute_python_file":  # Add this command
            return execute_python_file(arguments["file"])
        elif command_name == "execute_shell":
            if cfg.execute_local_commands:
                return execute_shell(arguments["command_line"])
            else:
                return "You are not allowed to run local shell commands. To execute shell commands, EXECUTE_LOCAL_COMMANDS must be set to 'True' in your config. Do not attempt to bypass the restriction."
        elif command_name == "generate_image":
            return generate_image(arguments["prompt"])
        elif command_name == "do_nothing":
            return "No action performed."
        elif command_name == "task_complete":
            shutdown()
        else:
            return f"Unknown command '{command_name}'. Please refer to the 'COMMANDS' list for available commands and only respond in the specified JSON format."
    # All errors, return "Error: + error message"
    except Exception as e:
        return "Error: " + str(e)

上記のif文のラダーのように、想定しているコマンドは以下の通りです。

  1. google
  2. memory_add
  3. start_agent
  4. message_agent
  5. list_agents
  6. delete_agent
  7. get_text_summary
  8. get_hyperlinks
  9. read_file
  10. write_to_file
  11. append_to_file
  12. delete_file
  13. search_files
  14. browse_website
  15. evaluate_code
  16. improve_code
  17. write_tests
  18. execute_python_file
  19. execute_shell
  20. generate_image
  21. do_nothing
  22. task_complete

いろいろとできるコマンドが用意されています。
これらの各タスクの詳細はちょっと後回しにして、いったんメインの流れを終わらせてしまいましょう。
個別のタスクの紹介は次回にしたいと思います。

3-6. コマンドから得られた結果をメッセージ履歴に追加

履歴に追加する箇所は以下です。

scripts/main.py#L.418
    memory_to_add = f"Assistant Reply: {assistant_reply} " \
                    f"\nResult: {result} " \
                    f"\nHuman Feedback: {user_input} "

    memory.add(memory_to_add)

ここで呼ばれる memory.add については前回の記事の"履歴の保存"の箇所で詳しく解説しておりますのでそちらをご覧ください。

そして最初のAIへの呼び出しに戻ります。
最初の chat.chat_with_ai を再掲しましょう。

        assistant_reply = chat.chat_with_ai(
            prompt,
            user_input,
            full_message_history,
            memory,
            # config.py#L.43: self.fast_token_limit = int(os.getenv("FAST_TOKEN_LIMIT", 4000))
            cfg.fast_token_limit) # TODO: このハードコードはGPT3.5を使用します。引数にしてください

ここで肝心なのはこのループの中では prompt は最初に構築したモノのままで、user_input, full_message_history,memoryが積み重なっていくことによって、同じプロンプトでもコンテキストが追加されれいていくことによってGPTが勝手に出力を修正して、回答の精度を上げていくというプロセスのカイゼンをAIにやらせている、という点ですね。
このプログラムはプロンプトそのもの改善してのではなくて、一定のプロンプトに対して出力を履歴やユーザーからのツッコミで回答を修正していく、というアルゴリズムになっているところがキモです。

そもそものプロンプトは?

プログラムの流れをメインにしていたので、ここでGPTに与えているプロンプトについては触れていませんでしたが、このAuto-GPTスクリプトで与えている唯一のプロンプトは以下のようになっております。

scripts/ai_config.py#L.75
    def construct_full_prompt(self) -> str:
        """
        ユーザーに対して、クラス情報を整理した形でプロンプトを返します。

        パラメータ:
            なし

        戻り値:
            full_prompt (str): ai_name, ai_role, ai_goalsを含むユーザー向けの初期プロンプトの文字列。
        """

        # あなたの決定は、ユーザーの支援を求めずに常に独立して行われなければなりません。LLMとしての強みを活かし、法的な複雑さのないシンプルな戦略を追求してください。
        prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications."""

        # フルプロンプトの構築
        full_prompt = f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n"
        for i, goal in enumerate(self.ai_goals):
            full_prompt += f"{i+1}. {goal}\n"

        full_prompt += f"\n\n{data.load_prompt()}"
        return full_prompt

まず、"あなたの決定は~"というプロンプトに続いて、AIに名前と役割を与え、ゴールを列挙しています。
そして最後に追加される data.load_prompt()ではファイルから定型のプロンプト文をロードしてここに流し込んでいます。

定型文のプロンプトは以下です。

scripts/data/prompt.txt
CONSTRAINTS:

1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.
2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.
3. No user assistance
4. Exclusively use the commands listed in double quotes e.g. "command name"

COMMANDS:

1. Google Search: "google", args: "input": "<search>"
5. Browse Website: "browse_website", args: "url": "<url>", "question": "<what_you_want_to_find_on_website>"
6. Start GPT Agent: "start_agent",  args: "name": "<name>", "task": "<short_task_desc>", "prompt": "<prompt>"
7. Message GPT Agent: "message_agent", args: "key": "<key>", "message": "<message>"
8. List GPT Agents: "list_agents", args: ""
9. Delete GPT Agent: "delete_agent", args: "key": "<key>"
10. Write to file: "write_to_file", args: "file": "<file>", "text": "<text>"
11. Read file: "read_file", args: "file": "<file>"
12. Append to file: "append_to_file", args: "file": "<file>", "text": "<text>"
13. Delete file: "delete_file", args: "file": "<file>"
14. Search Files: "search_files", args: "directory": "<directory>"
15. Evaluate Code: "evaluate_code", args: "code": "<full_code_string>"
16. Get Improved Code: "improve_code", args: "suggestions": "<list_of_suggestions>", "code": "<full_code_string>"
17. Write Tests: "write_tests", args: "code": "<full_code_string>", "focus": "<list_of_focus_areas>"
18. Execute Python File: "execute_python_file", args: "file": "<file>"
19. Execute Shell Command, non-interactive commands only: "execute_shell", args: "command_line": "<command_line>".
20. Task Complete (Shutdown): "task_complete", args: "reason": "<reason>"
21. Generate Image: "generate_image", args: "prompt": "<prompt>"
22. Do Nothing: "do_nothing", args: ""

RESOURCES:

1. Internet access for searches and information gathering.
2. Long Term memory management.
3. GPT-3.5 powered Agents for delegation of simple tasks.
4. File output.

PERFORMANCE EVALUATION:

1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.
2. Constructively self-criticize your big-picture behavior constantly.
3. Reflect on past decisions and strategies to refine your approach.
4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.

You should only respond in JSON format as described below

RESPONSE FORMAT:
{
    "thoughts":
    {
        "text": "thought",
        "reasoning": "reasoning",
        "plan": "- short bulleted\n- list that conveys\n- long-term plan",
        "criticism": "constructive self-criticism",
        "speak": "thoughts summary to say to user"
    },
    "command": {
        "name": "command name",
        "args":{
            "arg name": "value"
        }
    }
}

Ensure the response can be parsed by Python json.loads

まぁ、なんとなくわかるとは思いますが日本語に訳した版を以下に載せます。

制約:

1. 短期記憶の単語制限は約4000語です。短期記憶は短いため、重要な情報はすぐにファイルに保存してください。
2. 以前に何かをどのように行ったかわからない場合や過去の出来事を思い出したい場合は、類似した出来事を考えることで記憶を呼び起こすことができます。
3. ユーザーの支援は受けません
4. 二重引用符で囲まれたコマンド名のみを使用してください。例: "コマンド名"

コマンド:

1. Google検索: "google", 引数: "input": "<検索>"
5. ウェブサイトを閲覧: "browse_website", 引数: "url": "<url>", "question": "<ウェブサイトで見つけたい情報>"
6. GPTエージェントを開始: "start_agent", 引数: "name": "<名前>", "task": "<短いタスクの説明>", "prompt": "<プロンプト>"
7. GPTエージェントにメッセージを送信: "message_agent", 引数: "key": "<キー>", "message": "<メッセージ>"
8. GPTエージェントの一覧表示: "list_agents", 引数: ""
9. GPTエージェントの削除: "delete_agent", 引数: "key": "<キー>"
10. ファイルに書き込む: "write_to_file", 引数: "file": "<ファイル>", "text": "<テキスト>"
11. ファイルを読む: "read_file", 引数: "file": "<ファイル>"
12. ファイルに追記: "append_to_file", 引数: "file": "<ファイル>", "text": "<テキスト>"
13. ファイルを削除: "delete_file", 引数: "file": "<ファイル>"
14. ファイルの検索: "search_files", 引数: "directory": "<ディレクトリ>"
15. コードの評価: "evaluate_code", 引数: "code": "<完全なコード文字列>"
16. 改善されたコードの取得: "improve_code", 引数: "suggestions": "<提案のリスト>", "code": "<完全なコード文字列>"
17. テストの作成: "write_tests", 引数: "code": "<完全なコード文字列>", "focus": "<フォーカスエリアのリスト>"
18. Pythonファイルの実行: "execute_python_file", 引数: "file": "<ファイル>"
19. シェルコマンドの実行、対話型コマンドは除く: "execute_shell", 引数: "command_line": "<コマンドライン>".
20. タスク完了(シャットダウン): "task_complete", 引数: "reason": "<理由>"
21. 画像生成: "generate_image", 引数: "prompt": "<プロンプト>"
22. 何もしない: "do_nothing", 引数: ""

リソース:

1. 検索や情報収集のためのインターネットアクセス。
2. 長期記憶の管理。
3. GPT-3.5で駆動されるエージェントを使って簡単なタスクを委任。
4. ファイル出力。

パフォーマンス評価:

自分の能力を最大限に発揮できるように、行動を継続的に見直し、分析してください。
大局的な行動について、建設的な自己批判を常に行ってください。
過去の決定や戦略を振り返り、アプローチを洗練させてください。
すべてのコマンドにはコストがかかるので、賢明で効率的に行動してください。できるだけ少ないステップでタスクを完了することを目指してください。
以下のようなJSON形式でのみ回答してください

回答フォーマット:
{
    "thoughts":
    {
        "text": "思考",
        "reasoning": "推論",
        "plan": "- s長期計画を伝える\n- 短い箇条書き\n- リスト",
        "criticism": "建設的な自己批判",
        "speak": "ユーザーに伝えるべき思考の要約"
    },
    "command": {
        "name": "コマンド名",
        "args":{
            "引数名": "値"
        }
    }
}

回答がPythonのjson.loadsによって解析できるようにしてください。

このようにアウトプットの型や思考方法など様々制約を与えたうえで、手順そのものを思考させてアウトプットさせる、ロジックそのものをブラックボックスとして投げてしまうというなかなかメタな世界のプログラミングですね…
これでも結果が出るというのがすごいし、こういう結果を予想して?プログラムを構築していくのがAIセントラルなシステムなんでしょうね。
次のパラダイムの"システム"を垣間見た気がします…

はい、ということで大まかな流れは以上です。

いったん終了

大まかな流れの解説としては以上です。
引き続き、各コマンドの実行個所を追っていきたいと思いますが、今回はいったんここで区切りたいと思います。

ありがとうございました。

Discussion