사용자 정의 부조종사를 OpenBB 터미널에 통합하기 위한 예제 저장소에 오신 것을 환영합니다.
이 저장소는 사용자 정의 Copilot을 구축하고 OpenBB Copilot에 추가하는 데 필요한 모든 것을 제공합니다.
자신만의 부조종사를 만들고 싶어하는 몇 가지 일반적인 이유는 다음과 같습니다.
OpenBB 터미널에서 상호 작용할 수 있는 사용자 지정 Copilot을 통합하려면 터미널이 요청할 수 있는 백엔드 API를 생성해야 합니다.
사용자 정의 Copilot API는 SSE(서버 전송 이벤트)로 응답합니다.
참고: 빠르게 시작하려면 이 저장소의 일부로 포함된 예제 부조종사 중 하나를 실행하고 이를 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 터미널은 Copilot의 모든 반환 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"
}
},
...
],
...
}
사용자 정의 부조종사는 SSE(서버 전송 이벤트)를 사용하여 OpenBB 터미널의 요청에 응답해야 합니다.
OpenBB 터미널은 다음 SSE를 처리할 수 있습니다.
copilotMessageChunk
: 스트리밍된 Copilot 토큰(부분 응답)을 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
함수의 경우 유일한 필수 인수는 요청의 widgets
배열에 있는 UUID 중 하나에서 데이터를 검색할 위젯의 UUID인 widget_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
) 사용자 정의 Copilot을 OpenBB Terminal과 통합하려면 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
).