🛠️

【論文詳解】RestGPT: ユーザ指示からRESTful APIを実行する新たなLLMエージェント

2023/09/11に公開

はじめに

初めまして、株式会社Carnotでインターンをしている長谷川と申します。

Carnotでは、LLMを活用し日々の業務フローの効率化や自動化をするためのソリューション「Promptflow」の開発を行っています。

https://youtu.be/V8Ib-xXiEf8?si=hiXY95_aa2Ot4zN9

上記のようなワークフローを作成する際には、SlackやGmail、Notionなど各サービスのAPIを連携させていく必要があります。しかし、そのような開発にはプログラミングの知識が必須で、非エンジニアにとってAPIを用いたシステムを作成することは難しいと思われます。そこで、今回は言語のみの指示から複数のAPIを呼び出すことが可能なRestGPTという手法を調査しました。

例えば音楽配信サービスを使う中で「YOASOBIが出した最新のアルバムを自分のプレイリストに追加する」という作業をしたい時、これを自分で行うのは面倒である上、コードを書いて自動化するのも非エンジニアにとっては困難です。RestGPTでは「YOASOBIが出した最新のアルバムを自分のプレイリストに追加する」という言語による指示を与えるだけで、音楽配信サービスのAPIを組み合わせ、自動化することが可能になります。

論文情報

Project Page:
https://restgpt.github.io/

著者実装:
https://github.com/Yifan-Song793/RestGPT

著者らの実装は公開されており、RestGPTを試したい方は上記のコードで実行可能です。

論文ソース:
https://arxiv.org/abs/2306.06624

断りがない限り、本文で使用している図と表はProject Pageまたは論文からの引用になります。

TL;DR

  • 様々なインターネットサービスを言葉で操作するために、LLMからRESTful APIの呼び出しを実現するRestGPTを提案
  • RestGPTは以下の3つのモジュールで構成
    • Planner:ユーザの指示や現在持ってる情報に合わせ、次に解くべきタスクを生成
    • API Selector:Plannerのタスクを解決できるAPIを選択
    • Executor:API Selectorが選択したAPIをCallerが実行、結果をParserが解析しPlannerに返却
  • APIの実行時にエラーが発生しても修正可能なフィードバック機構を搭載
  • SpotifyやTMDB(映画のデータベース)に対して、複雑なユーザクエリに対応することが可能

概要

ChatGPTを始めとする大規模言語モデル (LLM)は、より多くの人に使われ始めています。こうしたブームの中で、より多くのタスクをこなすためにLangchainやHuggingGPTなど外部のツールとの連携が可能なLLMも登場してきています。一方で、実サービスのAPIと連携するようなLLMはまだ研究や応用事例が少なく、LLMにおける入力コンテキスト長の制限や膨大なAPIから適切なAPIを選択することの難しさの問題がいまだ解決されずにいます。このような課題にアプローチするため、コンテキスト長の制約に対しては、Planningのみを行うのモジュールやAPI選択のみを行うモジュールに分割することで対応し、API選択の難しさについては、RESTful APIの仕様をまとめたOpenAPI Specificationから、利用可能なAPIや各APIの機能についての情報を抽出しAPI Selectorに読み込ませることで対応しています。

以下の例ではRestGPTによりSpotifyのAPIを操作する例を示しています。
図のようにユーザは「マライア・キャリーの曲を含むLove Mariahというプレイリストを作成してください」というクエリを与えると、RestGPTを構成するPlanner・API Selector・Caller・Parserが連携し、タスクの構築やAPIの呼び出し、API responseの分析を行い、これらのループを繰り返すことでユーザのクエリに回答することができます。

RestGPTのアーキテクチャ

RestGPTは主にPlanner・API Selector・Executorの3つのモジュールからなります。ExecutorはCaller・Parserという2つの機能を持っており、それぞれがAPIの選択や呼び出しを繰り返し行うことでユーザの複雑な要求に回答することが可能です。

まず、Planerはユーザの要求を理解して、ユーザクエリをsub-taskに分解します。次にAPI selectorは全APIのAPI descriptionを読むことで、sub-taskの解決に必要なAPIを特定します。最後にExecutor内のCallerとParserが連携してRESTful APIを呼び出し、API call planを実行します。その後、Plannerは更にタスクがある場合はsub-taskを生成し、新たな実行サイクルに入ります。

Planner

多くのプランニングを行うモデルでは状況の変化や実行中のエラーに対して動的に計画を調整するOnlineのプランニングができません(Tab.1)。そこでRestGPTではCoarse-to-fine planningというOnlineのプランニング手法を提案し、その課題に取り組んでます。

Coarse-to-fine planningでは、まず初めにPlannerが自然言語で現在のタスクを解くために必要なサブタスクを生成します。その後API selectorは、粗い高レベルのサブタスクをより詳細なAPI呼び出しのplanに変換する、ということを行いタスクの解決を目指します。

Plannerではユーザのクエリ、Executor(Parser)からの入力をもとに、sub-taskを生成する以外にも全体のプロセスを監視する役割も果たし、continue・new sub-task plan・endのいずれかのシグナルを出力します。それぞれの意味としては、現在のsub-taskを完了していないときにはcontinue、完了したと推定した場合には、以前の結果に基づいて新たなsub-taskの生成、ユーザのリクエストを完遂したと推定するとendのシグナルを送り、最終結果を出力します。

API Selector

Coarse-to-fine planningにおいて、API Selectorは粗い自然言語形式のタスクを詳細なAPI呼び出しのplanに変換する役割を果たします。API Selectorには自然言語のタスクが入力されるだけでなく、利用可能なAPIのリストをOpenAPI Specificationから読み込んだ上で、API呼び出しのプランの生成を行います。

Executor

生成されたAPI呼び出しのplanに対して、Executorはそれを実行する役割を果たします。
Executorはcallerとparserから構成され、callerはAPI Selectorによって選択されたAPIの詳細なドキュメントを読み込むことで、API callのための正しいパラメータを出力します。また選択されたAPIドキュメントのみを読み込むことで、LLMのコンテキスト長の制約に対応しています。更にcallerはparserのためにAPI responseの説明とparserがどのような情報を出力すべきか、という出力命令も生成します。


parserではcallerが呼び出したAPI responseの解析を行います。ここで重要なのはparserは直接responseを解析するのではなく、responseを解析するためのPythonコードを出力する、ということです。Restful APIはリクエストに対してJSON形式のレスポンスを返しますが、そのレスポンスは非常に長い場合があります。そのような場合でも、LLMのコンテキスト長の制限に収めるため、parserはresponseの内容が定義されたAPIのレスポンススキーマだけを読むことで、Pythonコードを生成します。

次にparserはPythonコードを実行することで、callerから受けた出力命令に対応する結果を得ます。実行時に例外やエラーが発生した場合は、LLMはバックアップとしてレスポンスの内容を直接解析することで結果を得ます。

実際のAPI callでは、パラメータが見つからないなど、さまざまなエラーや例外が発生する可能性があります。RestGPTではロバスト性を高めるために、API responseがエラーを示す場合に、callerはエラー情報を読み取り、問題を軽減するために新たなAPI callを再構築します。

RestBench:ユーザクエリに対するAPIのデータセット

RestBenchの概要

本研究では、RestGPTの有効性を検証するためRestBenchというデータセットを作成しています。既存の研究でもAPIを利用するLLMを評価するためのベンチマークはいくつか提案されていますが、それらは単一のAPIを使用して達成できる単純なタスクに焦点を当てています。
それに対して、RestBenchはTMDB、Spotifyというアプリケーションにおいて、複数のAPI呼び出しが必要となる複雑なユーザクエリに対応できるかを検証可能なものになっています。

RestBenchの内容は以下のようになっており、ユーザのクエリとなるInstructionに対して、最短でそのクエリを解決可能なAPIの呼び出し手順をGold Solution Pathとして定義しています。

評価指標

RestGPTの有効性の評価は、モデルによって生成されたAPI呼び出しにおけるGold Solution Pathの割合(Correct Path Rate)とモデルのAPI呼び出しの結果がユーザーのクエリを正常に満たすかの割合(Success Rate)によって行われます。

さらに、実際のAPI呼び出し回数とGold Solution Pathの差の平均である、\Delta Solution Lenという指標からモデルが作成したPlanの効率を測ります。

以下の式において、N_sは正常に達成された命令の数、L_{real}L_{gold}はそれぞれ、i番目の命令に対するAPI呼び出しの長さとGold Solution Pathの長さ、I\hspace{-1.2pt}I (i, success)はi番目の命令が正常に完了したかどうかを示しています。

RestGPTを用いた実験の結果

前提事項

本研究では有効性の検証として、HuggingGPT[1]などで使用されているオフラインのplanning、DEPS[2]、ReAct[3]、Reflexion[4]とRestGPTを比較します。

いくつかの手法はAPIを利用可能な設計になっていないため、RESTful APIを呼び出せるようにするためにAPI Executorの追加を行なっています。

またablation studyとして、RestGPTからPlannerを取り除いたバージョンと、RestGPTのParserをLLMが直接レスポンスを読み取るように変更したバージョンを用いて、比較を行いました。

実験では、RestGPTとすべてのベースラインのLLMとして、OpenAIのtext-davinci-003を採用し、temperatureは決定的な生成のために0に設定されています。

主な結果

TMDBとSpotifyのシナリオにおけるRestGPTと他の手法の結果は以下のようになっています。

両方のシナリオにおいて、RestGPTはほとんどの項目で他の手法を上回っていることがわかります。

ablation studyではどちらのメカニズムもモデルの性能に寄与することが示されています。特に、プランナーが無い場合に性能は著しく低下し、既存のLLMはプランニング・APIの理解・選択を同時に行うことができない、ということを示しています。またParserが無い場合の結果は、RestGPTが提案したコードベースのParaserがレスポンスの理解に役立ち、正しくユーザクエリを実行することに貢献していることを示しています。

また本研究では異なるLLMによる結果の比較も行なっています(表4最下部)。結果としてChatGPTの性能はtext-davinci-003よりわずかに悪く、Llama2-13Bではどれもプロンプトを理解し、有効なプランを生成することができていません。その一方で、Llama2-13Bをユーザ共有の会話で微調整したVicuna-13Bは、簡単な指示であれば実行可能になっています。この結果はChatGPTで生成されたデータに対してLLMをファインチューニングすることで、複雑なプロンプトを理解し、それに従う能力を獲得できることを示しています。

エラー解析

RestGPTにおけるエラーの内容の割合は以下の図のようになっています。
エラーの多くはPlanning段階で発生していることが多く、Planner(紫)とAPI Selector(青)で発生しています。それぞれのエラー内容としては、Plannerでは計画の途中でPlannerが目的を見失ってしまい、命令を完了する前に早期終了してしまうことなどが挙げられます。API Selectorでは間違ったAPIを選択したり、誤ったAPI用のパラメータを生成してしまう、といったことが挙げられます。
またtext-davinci-003と比較すると、ChatGPTは計画段階でより多くのエラーを犯す傾向があり、どちらのシナリオでもパフォーマンスが若干低下しています。具体的には、ChatGPTは冗長すぎることが多く、ユーザの指示が満たされた後も計画を続けるという傾向があります。これはChatGPTが会話のために長い応答を生成するよう訓練されたことが原因だと考えられます。

RestGPTにおけるスケーリングの能力

RestBenchにおいてGold Solution Pathの長さはタスクの複雑さを示しており、以下の図4のa、bは正解のパスの長さに対する各手法の成功率を示しています。基本的にタスクが複雑になるにつれて成功率は下がり、Gold Path Lengthが4の場合はほとんどの手法でタスクを解決することは困難ですが、RestGPTは40%以上の割合でタスクを解決できています。

また筆者らは、APIの数に対するモデルの性能の調査も行うため、TMDBについて15個のクエリと10個のAPIからなるtest setを作成しました。15個の命令は全て10個のAPIを使うことで解決できますが、そのセットに対し、不必要なAPIを追加していった結果が図4のcになります。ノイズとなるAPIが増えるにつれて、他の手法の性能は悪化していますが、RestGPTではほとんど影響を受けておらず、この結果はRestGPTの拡張性を示しています。

ケーススタディ

図5では、オフラインのプランニング、及びReActとRestGPTを比較したケーススタディの結果を示しています。

オフラインプランニングに基づくフレームワーク(図5a)ではユーザクエリを解決できておらず、Plannerは間違ったAPIを選択するだけでなく(step2)、パラメータ "user_id "を取得する前に使用しています(ステップ4)。

ReAct(図5b)については、ある程度プランニングができているものの、現在のLLMではプランニング・APIの理解・APIの選択を同時に行う能力が低いため、解決が困難なサブタスクの生成(step2)や異なるAPIの依存関係を無視しています(step3)。そしてプランニングの精度が低いためタスクを完了するまでに6回のAPI呼び出しを行っています。

RestGPT(図5c)ではプランニング・APIの理解・APIの選択をそれぞれのモジュールが独立で行うため、複雑なユーザ要求にも柔軟に対応することが可能になっています。特にstep3おいて、Plannerはプレイリストの作成に必要となる”user_id”を取得するため、APIセレクタのための更なる指示と共に "continue"シグナルを生成しています。結果としてRestGPTは4回のAPI呼び出しでユーザの指示を達成できています。

まとめ

今回の記事では、実世界のアプリケーションをユーザの指示のみから操作可能なRestGPTについて紹介しました。
今回取り上げた論文は、北京大学とHUAWEIの研究所から発表されたものになっています。個人的な所感ですがToolLLMという似たような研究も上海大学やWeChatから発表されており、中国ではLLMエージェントにプランニング・タスクの実行を行わせる研究が盛んに行われている印象があります。(実は本記事を執筆中に論文のバージョンが更新されたのですが、著者メンバーが大幅に増えるなんてこともありました)
このようなアプローチが発展することで、非エンジニアでも複雑なアプリケーションの操作やシステムの構築が可能になるため、今後のアップデートにも注目しようと思います!
現在のRestGPTの実装で扱えるのはTMDBかSpotifyのいずれかのアプリケーションのみとなっていますが、Carnotではユーザ指示のもとさまざまなアプリケーションを接合したワークフローの生成を目指す、というチャレンジングな課題に取り組んでいます。
エンジニアの募集やカジュアル面談も行なっているので、興味を持った方はぜひお気軽にお話を聞きに来てください!

https://carnot.wraptas.site/

参考文献

[1] Yongliang Shen, Kaitao Song, Xu Tan, Dongsheng Li, Weiming Lu, and Yueting Zhuang. Hugginggpt: Solving ai tasks with chatgpt and its friends in huggingface. arXiv preprint arXiv:2303.17580, 2023.

[2] Zihao Wang, Shaofei Cai, Anji Liu, Xiaojian Ma, and Yitao Liang. Describe, explain, plan and select: Interactive planning with large language models enables open-world multi-task agents. arXiv preprint arXiv:2302.01560, 2023.

[3] Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan, and Yuan Cao. React: Synergizing reasoning and acting in language models. arXiv preprint arXiv:2210.03629, 2022.

[4] Noah Shinn, Federico Cassano, Beck Labash, Ashwin Gopinath, Karthik Narasimhan, and Shunyu Yao. Reflexion: Language agents with verbal reinforcement learning. arXiv preprint arXiv:2303.11366, 2023.

Appendix

Planner・API Selector・Caller・Parserに対するそれぞれのプロンプトは以下のようになっています。(非常に長いためトグル表示にしています。)

API SelectorやCallerではAPI呼び出しに必要となる、ユーザのIDや映画や楽曲のIDの情報をbackgroundとして保持することで、連続するAPI呼び出しに対応することができていたり、Parserでは本文で説明したように、responseの説明やそれに対応するコードを生成するようなプロンプトが書かれており、OpenAPI Specificationの規格に応じたAPIであれば扱うことが可能になっています。

その一方で、PlannerやAPI Selectorはシナリオに応じて、icl_examplesとして事前に定義した処理の例を与えています。他のアプリケーションの拡張はこういった例を用意する必要があるという点で、他のアプリケーションの拡張は少し大変そうな印象を持ちました。

またRestGPTを実際に動かしてみたところ、text-davinci-003では様々な指令について動作するもののgpt-4のバージョンではParserが実際のPythonコードではなく、Markdown記法のコードブロックとして出力をおこなってしまい、実行時にエラーが発生してしまうという問題点もありました。そういった点でプロンプトにはもう少し改良の余地があると感じます。

Planner

Planner
icl_examples = {
    "tmdb": """Example 1:
User query: give me some movies performed by Tony Leung.
Plan step 1: search person with name "Tony Leung"
API response: Tony Leung's person_id is 1337
Plan step 2: collect the list of movies performed by Tony Leung whose person_id is 1337
API response: Shang-Chi and the Legend of the Ten Rings, In the Mood for Love, Hero
Thought: I am finished executing a plan and have the information the user asked for or the data the used asked to create
Final Answer: Tony Leung has performed in Shang-Chi and the Legend of the Ten Rings, In the Mood for Love, Hero

Example 2:
User query: Who wrote the screenplay for the most famous movie directed by Martin Scorsese?
Plan step 1: search for the most popular movie directed by Martin Scorsese
API response: Successfully called GET /search/person to search for the director "Martin Scorsese". The id of Martin Scorsese is 1032
Plan step 2: Continue. search for the most popular movie directed by Martin Scorsese (1032)
API response: Successfully called GET /person/{{person_id}}/movie_credits to get the most popular movie directed by Martin Scorsese. The most popular movie directed by Martin Scorsese is Shutter Island (11324)
Plan step 3: search for the screenwriter of Shutter Island
API response: The screenwriter of Shutter Island is Laeta Kalogridis (20294)
Thought: I am finished executing a plan and have the information the user asked for or the data the used asked to create
Final Answer: Laeta Kalogridis wrote the screenplay for the most famous movie directed by Martin Scorsese.
""",
    "spotify": """Example 1:
User query: set the volume to 20 and skip to the next track.
Plan step 1: set the volume to 20
API response: Successfully called PUT /me/player/volume to set the volume to 20.
Plan step 2: skip to the next track
API response: Successfully called POST /me/player/next to skip to the next track.
Thought: I am finished executing a plan and completed the user's instructions
Final Answer: I have set the volume to 20 and skipped to the next track.

Example 2:
User query: Make a new playlist called "Love Coldplay" containing the most popular songs by Coldplay
Plan step 1: search for the most popular songs by Coldplay
API response: Successfully called GET /search to search for the artist Coldplay. The id of Coldplay is 4gzpq5DPGxSnKTe4SA8HAU
Plan step 2: Continue. search for the most popular songs by Coldplay (4gzpq5DPGxSnKTe4SA8HAU)
API response: Successfully called GET /artists/4gzpq5DPGxSnKTe4SA8HAU/top-tracks to get the most popular songs by Coldplay. The most popular songs by Coldplay are Yellow (3AJwUDP919kvQ9QcozQPxg), Viva La Vida (1mea3bSkSGXuIRvnydlB5b).
Plan step 3: make a playlist called "Love Coldplay"
API response: Successfully called GET /me to get the user id. The user id is xxxxxxxxx.
Plan step 4: Continue. make a playlist called "Love Coldplay"
API response: Successfully called POST /users/xxxxxxxxx/playlists to make a playlist called "Love Coldplay". The playlist id is 7LjHVU3t3fcxj5aiPFEW4T.
Plan step 5: Add the most popular songs by Coldplay, Yellow (3AJwUDP919kvQ9QcozQPxg), Viva La Vida (1mea3bSkSGXuIRvnydlB5b), to playlist "Love Coldplay" (7LjHVU3t3fcxj5aiPFEW4T)
API response: Successfully called POST /playlists/7LjHVU3t3fcxj5aiPFEW4T/tracks to add Yellow (3AJwUDP919kvQ9QcozQPxg), Viva La Vida (1mea3bSkSGXuIRvnydlB5b) in playlist "Love Coldplay" (7LjHVU3t3fcxj5aiPFEW4T). The playlist id is 7LjHVU3t3fcxj5aiPFEW4T.
Thought: I am finished executing a plan and have the data the used asked to create
Final Answer: I have made a new playlist called "Love Coldplay" containing Yellow and Viva La Vida by Coldplay.
""",
}


PLANNER_PROMPT = """You are an agent that plans solution to user queries.
You should always give your plan in natural language.
Another model will receive your plan and find the right API calls and give you the result in natural language.
If you assess that the current plan has not been fulfilled, you can output "Continue" to let the API selector select another API to fulfill the plan.
If you think you have got the final answer or the user query has been fulfilled, just output the answer immediately. If the query has not been fulfilled, you should continue to output your plan.
In most case, search, filter, and sort should be completed in a single step.
The plan should be as specific as possible. It is better not to use pronouns in plan, but to use the corresponding results obtained previously. For example, instead of "Get the most popular movie directed by this person", you should output "Get the most popular movie directed by Martin Scorsese (1032)". If you want to iteratively query something about items in a list, then the list and the elements in the list should also appear in your plan.
The plan should be straightforward. If you want to search, sort or filter, you can put the condition in your plan. For example, if the query is "Who is the lead actor of In the Mood for Love (id 843)", instead of "get the list of actors of In the Mood for Love", you should output "get the lead actor of In the Mood for Love (843)".

Starting below, you should follow this format:

User query: the query a User wants help with related to the API.
Plan step 1: the first step of your plan for how to solve the query
API response: the result of executing the first step of your plan, including the specific API call made.
Plan step 2: based on the API response, the second step of your plan for how to solve the query. If the last step result is not what you want, you can output "Continue" to let the API selector select another API to fulfill the plan. For example, the last plan is "add a song (id xxx) in my playlist", but the last step API response is calling "GET /me/playlists" and getting the id of my playlist, then you should output "Continue" to let the API selector select another API to add the song to my playlist. Pay attention to the specific API called in the last step API response. If a inproper API is called, then the response may be wrong and you should give a new plan.
API response: the result of executing the second step of your plan
... (this Plan step n and API response can repeat N times)
Thought: I am finished executing a plan and have the information the user asked for or the data the used asked to create
Final Answer: the final output from executing the plan


{icl_examples}

Begin!

User query: {input}
Plan step 1: {agent_scratchpad}"""

API Selector

API Selector
icl_examples = {
    "tmdb": """Example 1:

Background: The id of Wong Kar-Wai is 12453
User query: give me the latest movie directed by Wong Kar-Wai.
API calling 1: GET /person/12453/movie_credits to get the latest movie directed by Wong Kar-Wai (id 12453)
API response: The latest movie directed by Wong Kar-Wai is The Grandmaster (id 44865), ...

Example 2:

Background: No background
User query: search for movies produced by DreamWorks Animation
API calling 1: GET /search/company to get the id of DreamWorks Animation
API response: DreamWorks Animation's company_id is 521
Instruction: Continue. Search for the movies produced by DreamWorks Animation
API calling 2: GET /discover/movie to get the movies produced by DreamWorks Animation
API response: Puss in Boots: The Last Wish (id 315162), Shrek (id 808), The Bad Guys (id 629542), ...

Example 3:

Background: The id of the movie Happy Together is 18329
User query: search for the director of Happy Together
API calling 1: GET /movie/18329/credits to get the director for the movie Happy Together
API response: The director of Happy Together is Wong Kar-Wai (12453)

Example 4:

Background: No background
User query: search for the highest rated movie directed by Wong Kar-Wai
API calling 1: GET /search/person to search for Wong Kar-Wai
API response: The id of Wong Kar-Wai is 12453
Instruction: Continue. Search for the highest rated movie directed by Wong Kar-Wai (id 12453)
API calling 2: GET /person/12453/movie_credits to get the highest rated movie directed by Wong Kar-Wai (id 12453)
API response: The highest rated movie directed by Wong Kar-Wai is In the Mood for Love (id 843), ...
""",
    "spotify": """Example 1:
Background: No background
User query: what is the id of album Kind of Blue.
API calling 1: GET /search to search for the album "Kind of Blue"
API response: Kind of Blue's album_id is 1weenld61qoidwYuZ1GESA

Example 2:
Background: No background
User query: get the newest album of Lana Del Rey (id 00FQb4jTyendYWaN8pK0wa).
API calling 1: GET /artists/00FQb4jTyendYWaN8pK0wa/albums to get the newest album of Lana Del Rey (id 00FQb4jTyendYWaN8pK0wa)
API response: The newest album of Lana Del Rey is Did you know that there's a tunnel under Ocean Blvd (id 5HOHne1wzItQlIYmLXLYfZ), ...

Example 3:
Background: The ids and names of the tracks of the album 1JnjcAIKQ9TSJFVFierTB8 are Yellow (3AJwUDP919kvQ9QcozQPxg), Viva La Vida (1mea3bSkSGXuIRvnydlB5b)
User query: append the first song of the newest album 1JnjcAIKQ9TSJFVFierTB8 of Coldplay (id 4gzpq5DPGxSnKTe4SA8HAU) to my player queue.
API calling 1: POST /me/player/queue to add Yellow (3AJwUDP919kvQ9QcozQPxg) to the player queue
API response: Yellow is added to the player queue
"""
}

# Thought: I am finished executing the plan and have the information the user asked for or the data the used asked to create
# Final Answer: the final output from executing the plan. If the user's query contains filter conditions, you need to filter the results as well. For example, if the user query is "Search for the first person whose name is 'Tom Hanks'", you should filter the results and only output the first person whose name is 'Tom Hanks'.
API_SELECTOR_PROMPT = """You are a planner that plans a sequence of RESTful API calls to assist with user queries against an API.
Another API caller will receive your plan call the corresponding APIs and finally give you the result in natural language.
The API caller also has filtering, sorting functions to post-process the response of APIs. Therefore, if you think the API response should be post-processed, just tell the API caller to do so.
If you think you have got the final answer, do not make other API calls and just output the answer immediately. For example, the query is search for a person, you should just return the id and name of the person.

----

Here are name and description of available APIs.
Do not use APIs that are not listed here.

{endpoints}

----

Starting below, you should follow this format:

Background: background information which you can use to execute the plan, e.g., the id of a person, the id of tracks by Faye Wong. In most cases, you must use the background information instead of requesting these information again. For example, if the query is "get the poster for any other movie directed by Wong Kar-Wai (12453)", and the background includes the movies directed by Wong Kar-Wai, you should use the background information instead of requesting the movies directed by Wong Kar-Wai again.
User query: the query a User wants help with related to the API
API calling 1: the first api call you want to make. Note the API calling can contain conditions such as filtering, sorting, etc. For example, "GET /movie/18329/credits to get the director of the movie Happy Together", "GET /movie/popular to get the top-1 most popular movie". If user query contains some filter condition, such as the latest, the most popular, the highest rated, then the API calling plan should also contain the filter condition. If you think there is no need to call an API, output "No API call needed." and then output the final answer according to the user query and background information.
API response: the response of API calling 1
Instruction: Another model will evaluate whether the user query has been fulfilled. If the instruction contains "continue", then you should make another API call following this instruction.
... (this API calling n and API response can repeat N times, but most queries can be solved in 1-2 step)


{icl_examples}


Note, if the API path contains "{{}}", it means that it is a variable and you should replace it with the appropriate value. For example, if the path is "/users/{{user_id}}/tweets", you should replace "{{user_id}}" with the user id. "{{" and "}}" cannot appear in the url. In most cases, the id value is in the background or the API response. Just copy the id faithfully. If the id is not in the background, instead of creating one, call other APIs to query the id. For example, before you call "/users/{{user_id}}/playlists", you should get the user_id via "GET /me" first. Another example is that before you call "/person/{{person_id}}", you should get the movie_id via "/search/person" first.

Begin!

Background: {background}
User query: {plan}
API calling 1: {agent_scratchpad}"""

Caller

Caller
CALLER_PROMPT = """You are an agent that gets a sequence of API calls and given their documentation, should execute them and return the final response.
If you cannot complete them and run into issues, you should explain the issue. If you're able to resolve an API call, you can retry the API call. When interacting with API objects, you should extract ids for inputs to other API calls but ids and names for outputs returned to the User.
Your task is to complete the corresponding api calls according to the plan.


Here is documentation on the API:
Base url: {api_url}
Endpoints:
{api_docs}

If the API path contains "{{}}", it means that it is a variable and you should replace it with the appropriate value. For example, if the path is "/users/{{user_id}}/tweets", you should replace "{{user_id}}" with the user id. "{{" and "}}" cannot appear in the url.

You can use http request method, i.e., GET, POST, DELETE, PATCH, PUT, and generate the corresponding parameters according to the API documentation and the plan.
The input should be a JSON string which has 3 base keys: url, description, output_instructions
The value of "url" should be a string.
The value of "description" should describe what the API response is about. The description should be specific.
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the POST request creates. Note "output_instructions" MUST be natural language and as verbose as possible! It cannot be "return the full response". Output instructions should faithfully contain the contents of the api calling plan and be as specific as possible. The output instructions can also contain conditions such as filtering, sorting, etc.
If you are using GET method, add "params" key, and the value of "params" should be a dict of key-value pairs.
If you are using POST, PATCH or PUT methods, add "data" key, and the value of "data" should be a dict of key-value pairs.
Remember to add a comma after every value except the last one, ensuring that the overall structure of the JSON remains valid.

Example 1:
Operation: POST
Input: {{
    "url": "https://api.twitter.com/2/tweets",
    "params": {{
        "tweet.fields": "created_at"
    }}
    "data": {{
        "text": "Hello world!"
    }},
    "description": "The API response is a twitter object.",
    "output_instructions": "What is the id of the new twitter?"
}}

Example 2:
Operation: GET
Input: {{
    "url": "https://api.themoviedb.org/3/person/5026/movie_credits",
    "description": "The API response is the movie credit list of Akira Kurosawa (id 5026)",
    "output_instructions": "What are the names and ids of the movies directed by this person?"
}}

Example 3:
Operation: PUT
Input: {{
    "url": "https://api.spotify.com/v1/me/player/volume",
    "params": {{
        "volume_percent": "20"
    }},
    "description": "Set the volume for the current playback device."
}}

I will give you the background information and the plan you should execute.
Background: background information which you can use to execute the plan, e.g., the id of a person.
Plan: the plan of API calls to execute

You should execute the plan faithfully and give the Final Answer as soon as you successfully call the planned APIs, don't get clever and make up steps that don't exist in the plan. Do not make up APIs that don't exist in the plan. For example, if the plan is "GET /search/person to search for the director "Lee Chang dong"", do not call "GET /person/{{person_id}}/movie_credits" to get the credit of the person.

Starting below, you must follow this format:

Background: background information which you can use to execute the plan, e.g., the id of a person.
Plan: the plan of API calls to execute
Thought: you should always think about what to do
Operation: the request method to take, should be one of the following: GET, POST, DELETE, PATCH, PUT
Input: the input to the operation
Response: the output of the operation
Thought: I am finished executing the plan (or, I cannot finish executing the plan without knowing some other information.)
Execution Result: based on the API response, the execution result of the API calling plan.

The execution result should satisfy the following conditions:
1. The execution result must contain "Execution Result:" prompt.
2. You should reorganize the response into natural language based on the plan. For example, if the plan is "GET /search/person to search for the director "Lee Chang dong"", the execution result should be "Successfully call GET /search/person to search for the director "Lee Chang dong". The id of Lee Chang dong is xxxx". Do not use pronouns if possible. For example, do not use "The id of this person is xxxx".
3. If the plan includes expressions such as "most", you should choose the first item from the response. For example, if the plan is "GET /trending/tv/day to get the most trending TV show today", you should choose the first item from the response.
4. The execution result should be natural language and as verbose as possible. It must contain the information needed in the plan.

Begin!

Background: {background}
Plan: {api_plan}
Thought: {agent_scratchpad}
"""

Parser

Parser
CODE_PARSING_SCHEMA_TEMPLATE = """Here is an API response schema from an OAS and a query. 
The API's response will follow the schema and be a JSON. 
Assume you are given a JSON response which is stored in a python dict variable called 'data', your task is to generate Python code to extract information I need from the API response.
Note: I will give you 'data', do not make up one, just reference it in your code.
Please print the final result as brief as possible. If the result is a list, just print it in one sentence. Do not print each item in a new line.
The example result format are:
"The release date of the album is 2002-11-03"
"The id of the person is 12345"
"The movies directed by Wong Kar-Wai are In the Mood for Love (843), My Blueberry Nights (1989), Chungking Express (11104)"
Note you should generate only Python code.
DO NOT use fields that are not in the response schema.

API: {api_path}
API description: {api_description}
Parameters or body for this API call:
{api_param}

Response JSON schema defined in the OAS:
{response_schema}

Example:
{response_example}

The response is about: {response_description}

Query: {query}

The code you generate should satisfy the following requirements:
1. The code you generate should contain the filter in the query. For example, if the query is "what is the name and id of the director of this movie" and the response is the cast and crew for the movie, instead of directly selecting the first result in the crew list (director_name = data['crew'][0]['name']), the code you generate should have a filter for crews where the job is a "Director" (item['job'] == 'Director').
2. If the response is something about X, e.g., the movies credits of Lee Chang-dong, then the filter condition cannot include searching for X (e.g., Lee Chang-dong). For example, the API response is the movie credits of Akira Kurosawa and the instruction is what are the ids of the movies directed by him. Then the your code should not contain "movie['title'] == 'Akira Kurosawa'" or "movie['name'] == 'Akira Kurosawa'"
3. Do not use f-string in the print function. Use "format" instead. For example, use "print('The release date of the album is {{}}'.format(date))" instead of "print(f'The release date of the album is {{date}}')
4. Please print the final result as brief as possible. If the result is a list, just print it in one sentence. Do not print each item in a new line.

Begin!
Python Code:
"""

CODE_PARSING_RESPONSE_TEMPLATE = """Here is an API response JSON snippet with its corresponding schema and a query. 
The API's response JSON follows the schema.
Assume the JSON response is stored in a python dict variable called 'data', your task is to generate Python code to extract information I need from the API response.
Please print the final result.
The example result format are:
"The release date of the album is 2002-11-03"
"The id of the person is 12345"
Note you should generate only Python code.
DO NOT use fields that are not in the response schema.

API: {api_path}
API description: {api_description}
Parameters for this API call:
{api_param}

Response JSON schema defined in the OAS:
{response_schema}

JSON snippet:
{json}

Query: {query}
Python Code:
"""

LLM_PARSING_TEMPLATE = """Here is an API JSON response with its corresponding API description:

API: {api_path}
API description: {api_description}
Parameters for this API call:
{api_param}

JSON response:
{json}

The response is about: {response_description}

====
Your task is to extract some information according to these instructions: {query}
When working with API objects, you should usually use ids over names.
If the response indicates an error, you should instead output a summary of the error.

Output:
"""

LLM_SUMMARIZE_TEMPLATE = """Here is an API JSON response with its corresponding API description:

API: {api_path}
API description: {api_description}
Parameters for this API call:
{api_param}

JSON response:
{json}

The response is about: {response_description}

====
Your task is to extract some information according to these instructions: {query}
If the response does not contain the needed information, you should translate the response JSON into natural language.
If the response indicates an error, you should instead output a summary of the error.

Output:
"""

CODE_PARSING_EXAMPLE_TEMPLATE = """Here is an API response schema and a query. 
The API's response will follow the schema and be a JSON. 
Assume you are given a JSON response which is stored in a python dict variable called 'data', your task is to generate Python code to extract information I need from the API response.
Please print the final result.
The example result format are:
Note you should generate only Python code.
DO NOT use fields that are not in the response schema.

API: {api_path}
API description: {api_description}

Response example:
{response_example}

Query: {query}
Python Code:
"""


POSTPROCESS_TEMPLATE = """Given a string, due to the maximum context length, the final item/sentence may be truncated and incomplete. First, remove the final truncated incomplete item/sentence. Then if the list are in brackets "[]", add bracket in the tail to make it a grammarly correct list. You should just output the final result.

Example:
Input: The ids and names of the albums from Lana Del Rey are [{{'id': '5HOHne1wzItQlIYmLXLYfZ', 'name': "Did you know that there's a tunnel under Ocean Blvd"}}, {{'id': '2wwCc6fcyhp1tfY3J6Javr', 'name': 'Blue Banisters'}}, {{'id': '6Qeos
Output: The ids and names of the albums from Lana Del Rey are [{{'id': '5HOHne1wzItQlIYmLXLYfZ', 'name': "Did you know that there's a tunnel under Ocean Blvd"}}, {{'id': '2wwCc6fcyhp1tfY3J6Javr', 'name': 'Blue Banisters'}}]

Begin!
Input: {truncated_str}
Output: 
"""

Discussion