Willkommen im Beispiel-Repository zur Integration benutzerdefinierter Copiloten in das OpenBB-Terminal.
Dieses Repository bietet alles, was Sie zum Erstellen und Hinzufügen Ihrer eigenen benutzerdefinierten Copiloten zu OpenBB Copilot benötigen.
Hier sind einige häufige Gründe, warum Sie möglicherweise Ihren eigenen Copiloten bauen möchten:
Um einen benutzerdefinierten Copiloten zu integrieren, mit dem Sie über das OpenBB-Terminal interagieren können, müssen Sie eine Backend-API erstellen, an die das Terminal Anfragen stellen kann.
Ihre benutzerdefinierte Copilot-API antwortet mit Server-Sent Events (SSEs).
Hinweis: Wenn Sie schnell loslegen möchten, empfehlen wir Ihnen, einen der in diesem Repository enthaltenen Beispiel-Copiloten auszuführen und ihn als benutzerdefinierten Copiloten zum OpenBB-Terminal hinzuzufügen (jeder Beispiel-Copilot enthält Anweisungen zu deren Ausführung). Das Klonen und Modifizieren eines Beispiel-Copiloten ist eine großartige Möglichkeit, einen benutzerdefinierten Copiloten zu erstellen.
Das wichtigste zu verstehende Konzept ist, dass das Copilot-Protokoll zustandslos ist. Das bedeutet, dass jede Anfrage vom OpenBB-Terminal an Ihren Copiloten alle vorherigen Nachrichten (z. B. KI-Abschlüsse, menschliche Nachrichten, Funktionsaufrufe und Funktionsaufrufergebnisse) in die Anforderungsnutzlast einbezieht.
Dies bedeutet, dass Ihr benutzerdefinierter Copilot keinen Status zwischen den Anforderungen aufrechterhalten muss. Es sollte einfach die Anforderungsnutzlast verwenden, um eine Antwort zu generieren.
OpenBB Terminal ist allein für die Aufrechterhaltung des Konversationsstatus verantwortlich und hängt die Antworten an das messages
in der Anforderungsnutzlast an.
OpenBB Terminal stellt POST-Anfragen an den query
, der in Ihrer copilots.json
Datei definiert ist (mehr dazu später). Die Nutzlast dieser Anfrage enthält Daten wie die Nachrichten der aktuellen Konversation, explizit hinzugefügten Kontext, Informationen zu Widgets im aktuell aktiven Dashboard, abzurufende URLs usw.
Der Kern des Abfrageanforderungsschemas, das Sie implementieren müssen, lautet wie folgt:
{
"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
...
}
},
...
],
}
Im Folgenden gehen wir näher auf jedes dieser Felder ein.
messages
Dies ist die Liste der Nachrichten zwischen dem Benutzer und dem Copiloten. Dazu gehören die Nachrichten des Benutzers, die Nachrichten des Copiloten, Funktionsaufrufe und Funktionsaufrufergebnisse. Jede Nachricht hat eine role
und content
.
Das einfachste Beispiel ist, wenn kein Funktionsaufruf beteiligt ist, der einfach aus einem Array von human
und ai
-Nachrichten besteht.
OpenBB Terminal hängt automatisch alle ai
(von Ihrem Copiloten) an das messages
jeder Folgeanfrage an.
# 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?"
}
],
...
}
Funktionsaufrufe an Terminal Pro (z. B. beim Abrufen von Widget-Daten) sowie die Ergebnisse dieser Funktionsaufrufe (die die Widget-Daten enthalten) sind ebenfalls im messages
enthalten. Informationen zum Funktionsaufruf finden Sie im Abschnitt „Funktionsaufruf“ weiter unten.
context
Dies ist ein optionales Array von Widget-Daten, das vom OpenBB-Terminal gesendet wird, wenn Widgets vom Benutzer explizit als Kontext hinzugefügt wurden. Dies geschieht, wenn der Benutzer in einem Widget im OpenBB-Terminal auf die Schaltfläche „Als Kontext hinzufügen“ klickt.
Das context
funktioniert wie folgt:
{
...
" 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
Dabei handelt es sich um eine Reihe von Widgets, die derzeit im aktiven Dashboard von Terminal Pro sichtbar sind. * Dies ist nur dann sinnvoll, wenn Sie planen, einen Funktionsaufruf in Ihrem benutzerdefinierten Copiloten zu implementieren (was empfohlen, aber nicht erforderlich ist), der es ihm ermöglicht, Widget-Daten vom aktuell aktiven Dashboard des Benutzers auf dem OpenBB-Terminal anzufordern.
{
...
" 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"
}
},
...
],
...
}
Ihr benutzerdefinierter Copilot muss auf die Anfrage des OpenBB Terminals mithilfe von Server-Sent Events (SSEs) antworten.
OpenBB Terminal kann die folgenden SSEs verarbeiten:
copilotMessageChunk
: Wird verwendet, um gestreamte Copilot-Tokens (Teilantworten) an das OpenBB-Terminal zurückzusenden. Diese Antworten können gestreamt werden, während sie generiert werden.copilotFunctionCall
: Wird verwendet, um Daten anzufordern (z. B. Widget-Daten) oder eine bestimmte Funktion auszuführen. Dadurch wird Terminal Pro angewiesen, weitere Maßnahmen auf Kundenseite zu ergreifen. Dies ist nur erforderlich, wenn Sie planen, Funktionsaufrufe in Ihrem benutzerdefinierten Copiloten zu implementieren. copilotMessageChunk
Der Nachrichtenblock SSE hat das folgende Format:
event: copilotMessageChunk
data: {"delta":"H"} # <-- the `data` field must be a JSON object.
Das delta
muss eine Zeichenfolge sein, kann aber beliebig lang sein. Wir empfehlen, jeden Chunk, den Sie von Ihrem LLM erhalten, zurückzustreamen, sobald er als delta
generiert wurde.
Wenn Sie beispielsweise die Nachricht „Hallo!“ zurückstreamen möchten, würden Sie die folgenden SSEs senden:
event: copilotMessageChunk
data: {"delta":"H"}
event: copilotMessageChunk
data: {"delta":"i"}
event: copilotMessageChunk
data: {"delta":"!"}
copilotFunctionCall
(nur für Funktionsaufruf erforderlich)Der Funktionsaufruf SSE hat das folgende Format:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Auch hier muss das data
ein JSON-Objekt sein. Das function
ist der Name der aufzurufenden Funktion (derzeit wird nur get_widget_data
unterstützt), und das Feld input_arguments
ist ein Wörterbuch mit Argumenten, die an die Funktion übergeben werden sollen. Für die Funktion get_widget_data
ist das einzige erforderliche Argument widget_uuid
. Dabei handelt es sich um die UUID des Widgets, für das Daten abgerufen werden sollen (von einer der UUIDs im widgets
-Array der Anforderung).
Durch das Hinzufügen von Funktionsaufrufen zu Ihrem Copiloten kann dieser Daten anfordern, die auf dem aktuell aktiven Dashboard eines Benutzers im OpenBB-Terminal sichtbar sind.
Eine Liste aller derzeit auf dem Dashboard eines Benutzers sichtbaren Widgets wird im widgets
Array der Anforderungsnutzlast an Ihren Copiloten gesendet.
Um die Daten von einem Widget abzurufen, sollte Ihr Copilot mit einem copilotFunctionCall
Ereignis antworten und die Widget-UUID angeben:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Nachdem Sie ein copilotFunctionCall
Ereignis ausgegeben haben, müssen Sie die Verbindung schließen und auf eine neue Abfrageanforderung vom OpenBB-Terminal warten.
Wenn ein copilotFunctionCall
Ereignis empfangen wird, ruft OpenBB Terminal die Daten ab und initiiert eine neue Abfrageanforderung. Diese neue Abfrageanforderung enthält den ursprünglichen Funktionsaufruf sowie das Ergebnis des Funktionsaufrufs im 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>"
}
}
]
}
Beachten Sie Folgendes:
messages
enthalten.content
der ai
Nachricht des Funktionsaufrufs ist ein wörtlich mit einer Zeichenfolge codiertes JSON-Objekt des data
des Ereignisses copilotFunctionCall
(dies ist ein sehr nützlicher Mechanismus zum Schmuggeln zusätzlicher Metadaten im Zusammenhang mit dem Funktionsaufruf, falls Ihr Copilot diese benötigt). Derzeit ist der einzige vom OpenBB-Terminal unterstützte Funktionsaufruf get_widget_data
, der Daten von einem bestimmten Widget abruft.
Ihr benutzerdefinierter Copilot erhält die folgende Anfrage vom OpenBB-Terminal:
{
"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
}
}
]
}
Anschließend analysieren Sie die Antwort und formatieren die Nachrichten für Ihr LLM (einschließlich Informationen darüber, welche Widgets verfügbar sind). Nehmen wir an, Ihr Copilot stellt fest, dass die Anfrage des Benutzers mithilfe des verfügbaren Widgets beantwortet werden kann, und generiert einen Funktionsaufruf zum Abrufen der Daten.
Ihr Copilot antwortet dann mit folgendem SSE und schließt die Verbindung:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"38181a68-9650-4940-84fb-a3f29c8869f3"}}
OpenBB Terminal führt dann die angegebene Funktion aus und stellt eine neue Abfrageanforderung an Ihren benutzerdefinierten Copiloten:
{
"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
}
}
}
Anschließend analysieren Sie die Antwort, verarbeiten die Daten und formatieren die Nachrichten für Ihr LLM. Nehmen wir an, dass das LLM dann eine Zeichenfolge von Token generiert, um die Anfrage des Benutzers zu beantworten. Diese werden dann mithilfe des copilotMessageChunk
SSE an den Benutzer zurückgestreamt:
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
) Um Ihren benutzerdefinierten Copiloten in OpenBB Terminal zu integrieren, müssen Sie eine copilots.json
Datei konfigurieren und bereitstellen. Diese Datei definiert, wie Ihr benutzerdefinierter Copilot eine Verbindung zum Frontend herstellt, einschließlich der von Ihrem benutzerdefinierten Copilot unterstützten Funktionen und wohin Anfragen gesendet werden sollen.
Hier ist ein Beispiel für eine copilots.json-Konfiguration:
{
"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"
}
}
}
Ihre copilots.json
Datei muss unter <your-host>/copilots.json
bereitgestellt werden, zum Beispiel http://localhost:7777/copilots.json
.