カスタム コパイロットを OpenBB ターミナルに統合するためのサンプル リポジトリへようこそ。
このリポジトリは、独自のカスタム コパイロットを構築して OpenBB Copilot に追加するために必要なものをすべて提供します。
独自の副操縦士を構築する一般的な理由をいくつか示します。
OpenBB ターミナルから対話できるカスタム コパイロットを統合するには、ターミナルがリクエストできるバックエンド API を作成する必要があります。
カスタム コパイロット API は Server-Sent Events (SSE) で応答します。
注: すぐに開始したい場合は、このリポジトリの一部として含まれているサンプル コパイロットの 1 つを実行し、それをカスタム コパイロットとして OpenBB ターミナルに追加することをお勧めします (各サンプル コパイロットには、その実行方法に関する手順が含まれています)。サンプル コパイロットのクローンを作成して変更することは、カスタム コパイロットを構築する優れた方法です。
理解すべき最も重要な概念は、コパイロット プロトコルがステートレスであるということです。これは、OpenBB ターミナルからコパイロットへのすべてのリクエストには、以前のすべてのメッセージ (AI の完了、人間のメッセージ、関数呼び出し、関数呼び出しの結果など) がリクエスト ペイロードに含まれることを意味します。
これは、カスタム コパイロットがリクエスト間で状態を維持する必要がないことを意味します。要求ペイロードを使用して応答を生成するだけです。
OpenBB ターミナルは会話状態の維持を単独で担当し、リクエスト ペイロード内のmessages
配列に応答を追加します。
OpenBB ターミナルは、 copilots.json
ファイルで定義されたquery
エンドポイントに対して POST リクエストを作成します (これについては後で詳しく説明します)。このリクエストのペイロードには、現在の会話のメッセージ、明示的に追加されたコンテキスト、現在アクティブなダッシュボード上のウィジェットに関する情報、取得する URL などのデータが含まれます。
実装する必要があるクエリ リクエスト スキーマの核心は次のとおりです。
{
"messages" : [ # <-- the chat messages between the user and the copilot (including function calls and results)
{
"role" : "human" , # <-- each message has a role: "human", "ai", or "tool"
"content" : "Hi there." # <-- the content of the message
}
],
"context" : [ # <-- explicitly added context by the user (optional)
{
"uuid" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" , # <-- the UUID of the widget
"name" : "<widget name>" , # <-- the name of the widget
"description" : "<widget description>" , # <-- the description of the widget
"data" : {
"content" : "<data>" # <-- the data of the widget
},
"metadata" : {
"<metadata key>" : "<metadata value>" , # <-- the metadata of the widget
...
}
},
...
],
"widgets" : [ # <-- the widgets currently visible on the active dashboard on Terminal Pro (optional)
{
"uuid" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" , # <-- the UUID of the widget
"name" : "<widget name>" , # <-- the name of the widget
"description" : "<widget description>" , # <-- the description of the widget
"metadata" : {
"<metadata key>" : "<metadata value>" , # <-- the metadata of the widget
...
}
},
...
],
}
これらの各フィールドについては、以下で詳しく説明します。
messages
これは、ユーザーと副操縦士間のメッセージのリストです。これには、ユーザーのメッセージ、副操縦士のメッセージ、関数呼び出し、関数呼び出しの結果が含まれます。各メッセージにはrole
とcontent
あります。
最も単純な例は、関数呼び出しが関与しない場合で、単にhuman
とai
メッセージの配列で構成されます。
OpenBB ターミナルは、(コパイロットからの) すべての返信ai
メッセージをフォローアップ リクエストのmessages
配列に自動的に追加します。
# Only one human message
{
"messages" : [
{
"role" : "human" ,
"content" : "Hi there."
}
],
...
}
# Multiple messages
{
"messages" : [
{
"role" : "human" ,
"content" : "Hi there."
},
{
"role" : "ai" ,
"content" : "Hi there, I'm a copilot. How are you?"
},
{
"role" : "human" ,
"content" : "I'm fine, thank you. What is the weather in Tokyo?"
}
],
...
}
Terminal Pro への関数呼び出し (ウィジェット データを取得するときなど)、およびそれらの関数呼び出しの結果 (ウィジェット データを含む) もmessages
配列に含まれます。関数呼び出しの詳細については、以下の「関数呼び出し」セクションを参照してください。
context
これは、ウィジェットがユーザーによって明示的にコンテキストとして追加されたときに OpenBB ターミナルによって送信されるウィジェット データのオプションの配列です。これは、ユーザーが OpenBB ターミナルのウィジェットの [コンテキストとして追加] ボタンをクリックしたときに発生します。
context
フィールドは次のように機能します。
{
...
" context ": [
{
"uuid" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" , # <-- each widget has a UUID
"name" : "Analyst Estimates" ,
"description" : "Contains analyst estimates for a ticker" ,
"data" : {
"content" : "<data>" # <-- the data of the widget could either be a JSON string or plaintext (you must choose how to handle this in your copilot)
},
"metadata" : { # <-- additional metadata about the widget
"symbol" : "AAPL" ,
"period" : "quarter" ,
"source" : "Financial Modelling Prep" ,
"lastUpdated" : 1728998071322
}
},
{
"uuid" : "8b2e5f79-3a1d-4c9e-b6f8-1e7d2a9c0b3d" , # <-- the context can have multiple widgets
"name" : "Earnings Transcripts" ,
"description" : "Contains earnings transcripts for a ticker" ,
"data" : {
"content" : "<data>" # <-- the data of the widget
},
"metadata" : {
"symbol" : "AAPL" ,
"period" : "quarter" ,
"source" : "Intrinio" ,
"lastUpdated" : 1728998071322
}
},
...
],
...
}
widgets
これは、現在 Terminal Pro のアクティブなダッシュボードに表示されているウィジェットの配列です。 *これは、カスタム コパイロットで関数呼び出しを実装することを計画している場合にのみ役立ちます(これは推奨されますが、必須ではありません)。これにより、OpenBB ターミナルでユーザーが現在アクティブなダッシュボードからウィジェット データを要求できるようになります。
{
...
" widgets ": [
{
"uuid" : "c276369e-e469-4689-b5fe-3f8c76f7c45a" ,
"name" : "Stock Price Quote Widget" ,
"description" : "Contains the current stock price of a ticker" ,
"metadata" : {
"ticker" : "AAPL"
}
},
{
"uuid" : "9f8e7d6c-5b4a-3c2e-1d0f-9e8d7c6b5a4b" ,
"name" : "Financial Ratios Widget" ,
"description" : "Displays key financial ratios for a company" ,
"metadata" : {
"ticker" : "AAPL" ,
"period" : "TTM"
}
},
...
],
...
}
カスタム コパイロットは、Server-Sent Events (SSE) を使用して OpenBB ターミナルのリクエストに応答する必要があります。
OpenBB ターミナルは次の SSE を処理できます。
copilotMessageChunk
: ストリーミングされたコパイロット トークン (部分応答) を OpenBB ターミナルに返すために使用されます。これらの応答は生成時にストリーミングできます。copilotFunctionCall
: データ (ウィジェット データなど) を要求するか、特定の機能を実行するために使用されます。これは、クライアント側でさらなるアクションを実行するように Terminal Pro に指示します。これは、カスタム コパイロットで関数呼び出しを実装する予定がある場合にのみ必要です。 copilotMessageChunk
メッセージ チャンク SSE の形式は次のとおりです。
event: copilotMessageChunk
data: {"delta":"H"} # <-- the `data` field must be a JSON object.
delta
文字列である必要がありますが、任意の長さにすることができます。 LLM から受け取った各チャンクは、 delta
として生成されたらすぐにストリーミング バックすることをお勧めします。
たとえば、「Hi!」というメッセージをストリームバックしたい場合は、次の SSE を送信します。
event: copilotMessageChunk
data: {"delta":"H"}
event: copilotMessageChunk
data: {"delta":"i"}
event: copilotMessageChunk
data: {"delta":"!"}
copilotFunctionCall
(関数呼び出しにのみ必要)関数呼び出し SSE の形式は次のとおりです。
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
繰り返しますが、 data
フィールドは JSON オブジェクトである必要があります。 function
フィールドは呼び出される関数の名前 (現在はget_widget_data
のみがサポートされています)、 input_arguments
フィールドは関数に渡される引数の辞書です。 get_widget_data
関数の場合、必要な引数はwidget_uuid
のみです。これは、(リクエストのwidgets
配列内の UUID の 1 つから) データを取得するウィジェットの UUID です。
コパイロットに関数呼び出しを追加すると、OpenBB ターミナルのユーザーの現在アクティブなダッシュボードに表示されるデータをリクエストできるようになります。
ユーザーのダッシュボードに現在表示されているすべてのウィジェットのリストが、リクエスト ペイロードのwidgets
配列でコパイロットに送信されます。
ウィジェットからデータを取得するには、コパイロットはウィジェット UUID を指定してcopilotFunctionCall
イベントで応答する必要があります。
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
copilotFunctionCall
イベントを発行した後、接続を閉じて、OpenBB ターミナルからの新しいクエリ要求を待つ必要があります。
copilotFunctionCall
イベントを受信すると、OpenBB ターミナルはデータを取得し、新しいクエリ要求を開始します。この新しいクエリ リクエストには、元の関数呼び出しと、 messages
配列内の関数呼び出しの結果が含まれます。
{
...
" messages ": [
...
{
"role" : "ai" ,
"content" : "{ " function " : " get_widget_data " , " input_arguments " :{ " widget_uuid " : " c276369e-e469-4689-b5fe-3f8c76f7c45a " }}"
},
{
"role" : "tool" ,
"function" : "get_widget_data" ,
"data" : {
"content" : "<data>"
}
}
]
}
次の点に注意してください。
messages
配列に含まれます。ai
メッセージのcontent
フィールドは、 copilotFunctionCall
イベントのdata
フィールドの逐語的に文字列エンコードされた JSON オブジェクトです (これは、コパイロットが必要とする場合に、関数呼び出しに関連する追加のメタデータを密輸するための非常に便利なメカニズムです)。現在、OpenBB ターミナルでサポートされている唯一の関数呼び出しは、特定のウィジェットからデータを取得するget_widget_data
です。
カスタム コパイロットは、OpenBB ターミナルから次のリクエストを受け取ります。
{
"messages" : [
{
"role" : " human " ,
"content" : " What is the current stock price of AAPL? "
}
],
"widgets" : [
{
"uuid" : " 38181a68-9650-4940-84fb-a3f29c8869f3 " ,
"name" : " Historical Stock Price " ,
"description" : " Historical Stock Price " ,
"metadata" : {
"symbol" : " AAPL " ,
"source" : " Financial Modelling Prep " ,
"lastUpdated" : 1728994470324
}
}
]
}
次に、応答を解析し、LLM へのメッセージをフォーマットします (使用可能なウィジェットに関する情報を含む)。コパイロットが、利用可能なウィジェットを使用してユーザーのクエリに回答できると判断し、データを取得するための関数呼び出しを生成すると仮定します。
副操縦士は次の SSE で応答し、接続を閉じます。
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"38181a68-9650-4940-84fb-a3f29c8869f3"}}
OpenBB ターミナルは指定された関数を実行し、カスタム コパイロットに新しいクエリ リクエストを作成します。
{
"messages" : [
{
"role" : "human" ,
"content" : "What is the current stock price of AAPL?"
},
{
"role" : "ai" ,
"content" : "{ " function " : " get_widget_data " , " input_arguments " :{ " widget_uuid " : " 38181a68-9650-4940-84fb-a3f29c8869f3 " }}"
},
{
"role" : "tool" ,
"function" : "get_widget_data" ,
"data" : {
"content" : "[{ " date " : " 2024-10-15T00:00:00-04:00 " , " open " :233.61, " high " :237.49, " low " :232.37, " close " :233.85, " volume " :61901688, " vwap " :234.33, " adj_close " :233.85, " change " :0.24, " change_percent " :0.0010274},{ " date " : " 2024-10-14T00:00:00-04:00 " , " open " :228.7, " high " :231.73, " low " :228.6, " close " :231.3, " volume " :39882100, " vwap " :230.0825, " adj_close " :231.3, " change " :2.6, " change_percent " :0.0114},{ " date " : " 2024-10-11T00:00:00-04:00 " , " open " :229.3, " high " :233.2, " low " :228.9, " close " :231.0, " volume " :32581944, " vwap " :231.0333, " adj_close " :231.0, " change " :1.7, " change_percent " :0.0074}, ... ]"
}
}
],
"widgets" : [
{
"uuid" : "38181a68-9650-4940-84fb-a3f29c8869f3" ,
"name" : "Historical Stock Price" ,
"description" : "Historical Stock Price" ,
"metadata" : {
"symbol" : "AAPL" ,
"source" : "Financial Modelling Prep" ,
"lastUpdated" : 1728994470324
}
}
}
次に、応答を解析し、データを処理し、LLM へのメッセージをフォーマットします。その後、LLM がユーザーのクエリに答えるためにトークンの文字列を生成すると仮定します。これらは、 copilotMessageChunk
SSE を使用してユーザーにストリーミングされて戻されます。
event: copilotMessageChunk
data: {"delta":"The"}
event: copilotMessageChunk
data: {"delta":" current"}
event: copilotMessageChunk
data: {"delta":" stock"}
event: copilotMessageChunk
data: {"delta":" price"}
event: copilotMessageChunk
data: {"delta":" of"}
event: copilotMessageChunk
data: {"delta":" Apple"}
event: copilotMessageChunk
data: {"delta":" Inc."}
event: copilotMessageChunk
data: {"delta":" (AAPL)"}
event: copilotMessageChunk
data: {"delta":" is"}
event: copilotMessageChunk
data: {"delta":" $150.75."}
copilots.json
)カスタム コパイロットを OpenBB ターミナルと統合するには、 copilots.json
ファイルを構成して提供する必要があります。このファイルは、カスタム コパイロットがサポートする機能やリクエストの送信先など、カスタム コパイロットがフロントエンドに接続する方法を定義します。
copilots.json 構成の例を次に示します。
{
"example_copilot" : { # <-- the ID of your copilot
"name" : "Mistral Example Co. Copilot" , # <-- the display name of your copilot
"description" : "AI-powered financial copilot that uses Mistral Large as its LLM." , # <-- a short description of your copilot
"image" : "<url>" , # <-- a URL to an image icon for your copilot
"hasStreaming" : true , # <-- whether your copilot supports streaming responses via SSEs. This must always be true.
"hasFunctionCalling" : true , # <-- whether your copilot supports function calling
"endpoints" : {
"query" : "<url>" # <-- the URL that Terminal Pro will send requests to. For example, "http://localhost:7777/v1/query"
}
}
}
copilots.json
ファイルは、 <your-host>/copilots.json
で提供される必要があります (例: http://localhost:7777/copilots.json
。