欢迎来到将自定义副驾驶集成到 OpenBB 终端的示例存储库。
该存储库提供了构建您自己的自定义副驾驶并将其添加到 OpenBB Copilot 所需的一切。
以下是您可能想要构建自己的副驾驶的一些常见原因:
要集成可从 OpenBB 终端与之交互的自定义 Copilot,您需要创建终端可向其发出请求的后端 API。
您的自定义 copilot API 将使用服务器发送的事件 (SSE) 进行响应。
注意:如果您希望快速入门,我们建议运行此存储库中包含的示例副驾驶之一,并将其作为自定义副驾驶添加到 OpenBB 终端(每个示例副驾驶都包含有关如何运行它们的说明)。克隆和修改示例副驾驶是构建自定义副驾驶的好方法。
要理解的最重要的概念是副驾驶协议是无状态的。这意味着从 OpenBB 终端到副驾驶的每个请求都将在请求负载中包含所有先前的消息(例如 AI 完成、人工消息、函数调用和函数调用结果)。
这意味着您的自定义 copilot 不需要在请求之间维护任何状态。它应该简单地使用请求有效负载来生成响应。
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
消息(来自您的 Copilot)附加到任何后续请求的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 的活动仪表板上可见的一系列小部件。 *仅当您计划在自定义 copilot中实现函数调用(建议但不是必需)时,这才有用,这允许它从 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"
}
},
...
],
...
}
您的自定义副驾驶必须使用服务器发送事件 (SSE) 响应 OpenBB 终端的请求。
OpenBB 终端可以处理以下 SSE:
copilotMessageChunk
:用于将流式副驾驶令牌(部分响应)返回到 OpenBB 终端。这些响应可以在生成时进行流式传输。copilotFunctionCall
:用于请求数据(例如,小部件数据)或执行特定功能。这指示 Terminal Pro 在客户端采取进一步的操作。仅当您计划在自定义 copilot 中实现函数调用时才需要这样做。 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
,它是要检索数据的小部件的 UUID(来自请求的widgets
数组中的 UUID 之一)。
通过向您的 copilot 添加函数调用,它将能够请求 OpenBB 终端中用户当前活动仪表板上可见的数据。
用户仪表板上当前可见的所有小部件的列表将发送到请求负载的widgets
数组中的副驾驶。
要从小部件检索数据,您的 copilot 应使用copilotFunctionCall
事件进行响应,并指定小部件 UUID:
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 然后生成一串标记来回答用户的查询。然后使用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
)要将自定义 copilot 与 OpenBB 终端集成,您需要配置并提供copilots.json
文件。此文件定义自定义 copilot 如何与前端连接,包括自定义 copilot 支持哪些功能以及应将请求发送到何处。
以下是 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
。