Experts.JSは、OpenAIのアシスタントを作成および展開し、それらをツールとしてリンクする最も簡単な方法であり、メモリと細部への注意を払って専門家システムのパネルを作成します。
カスタムインク|によってサポートを介して作成されます技術
OpenAIの新しいアシスタントAPIは、新しい業界標準を設定し、広く採用されているチャット完了APIを超えて大幅に進歩しています。これは、AIエージェントの使いやすさと、エンジニアがLLMと対話する方法の大きな飛躍を表しています。最先端のGPT-4O MINIモデルと組み合わせて、アシスタントは、添付のファイルと画像を、スレッドと呼ばれる管理されたコンテキストウィンドウ内の知識ソースとして参照できるようになりました。カスタムGPTとは異なり、アシスタントは最大256,000文字の指示をサポートし、128のツールと統合し、アシスタントごとに最大10,000ファイルで効率的なファイル検索のために革新的なベクターストアAPIを利用します。
Experts.JSは、実行オブジェクトの管理の複雑さを削除し、アシスタントをツールとしてリンクできるようにすることにより、この新しいAPIの使用を簡素化することを目指しています。
import { Assistant , Thread } from "experts" ;
const thread = await Thread . create ( ) ;
const assistant = await Assistant . create ( ) ;
const output = await assistant . ask ( "Say hello." , thread . id ) ;
console . log ( output ) // Hello
さらに重要なことに、Experts.jsはアシスタントをツールとして紹介し、マルチAIエージェントシステムの作成を可能にします。各ツールは、親アシスタントまたはツールに代わって専門的な役割を引き受けたり、複雑なタスクを満たすことができるLLMバッキングアシスタントです。複雑なオーケストレーションワークフローを可能にしたり、一連のしっかりと編み物のタスクを振り付けます。ここに示されているのは、OpenSearchクエリを作成するためのLLMバックされたツールを備えた製品カタログツールを備えた会社アシスタントの例です。
NPM経由でインストールします。使用法は非常に簡単で、インポートするオブジェクトは3つしかありません。
npm install experts
Experts.jsは両方のES6インポート構文をサポートしており、CommonJSはステートメントを必要とします。
import { Assistant , Tool , Thread } from "experts" ;
アシスタントファサードオブジェクトのコンストラクターには、名前、説明、および指示が必要です。 3番目の引数は、Create Assistantドキュメントで概説されているすべての要求ボディオプションに直接マップするオプションのセットです。 Experts.jsのすべての例は、簡単にするためにES6クラスで記述されています。デフォルトモデルはgpt-4o-mini
です。
class MyAssistant extends Assistant {
constructor ( ) {
super ( {
name : "My Assistant" ,
instructions : "..." ,
model : "gpt-4o-mini" ,
tools : [ { type : "file_search" } ] ,
temperature : 0.1 ,
tool_resources : {
file_search : {
vector_store_ids : [ process . env . VECTOR_STORE_ID ] ,
} ,
} ,
} ) ;
}
}
const assistant = await MyAssistant . create ( ) ;
Experts.js Async Assistant.create()
ベースファクトリー機能は、同じコンストラクターオプションを使用してアシスタントを作成する簡単な方法です。
const assistant = Assistant . create ( {
name : "My Assistant" ,
instructions : "..." ,
model : "gpt-4o-mini" ,
} ) ;
重要
id
パラメーターなしでアシスタントを作成すると、常に新しいアシスタントが作成されます。詳細については、展開セクションを参照してください。
ask()
関数は、アシスタントを尋ねたり指示したりする簡単なインターフェイスです。メッセージとスレッド識別子が必要です。以下のスレッドの詳細。メッセージは、文字列またはネイティブのOpenAIメッセージオブジェクトにすることができます。これは、experts.jsが本当に輝く場所です。実行オブジェクトやその実行ステップを直接管理する必要はありません。
const output = await assistant . ask ( "..." , threadID )
const output = await assistant . ask ( { role : "user" , content : "..." } , threadID ) ;
通常のOpenAIツールと機能呼び出しは、 tools
とtool_resources
を介してコンストラクターオプションオブジェクトを介してサポートされます。 Experts.JSは、アシスタントをツールとして追加することもサポートしています。アシスタントをツールとして使用する詳細については、次のセクションにご覧いただけます。 addAssistantTool
関数を使用して、アシスタントをツールとして追加します。これは、アシスタントのコンストラクターでsuper()
後に発生する必要があります。
class MainAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Company Assistant" ,
instructions : "..." ,
} ) ;
this . addAssistantTool ( ProductsTools ) ;
}
}
デフォルトでは、Experts.jsはアシスタントのストリーミングイベントを活用します。これらにより、アプリケーションはOpenAIのサーバーセンドイベントを介してテキスト、画像、ツールの出力を受信できます。 Openai-Nodeのストリームヘルパーを活用し、これらのイベントといくつかのカスタムイベントを表現し、アシスタントが実行の完全なライフサイクルを活用できるようにします。
const assistant = await MainAssistant . create ( ) ;
assistant . on ( "textDelta" , ( delta , _snapshot ) => {
process . stdout . write ( delta . value )
} ) ;
すべてのOpenAIノードストリーミングイベントは、アシスタントのon()
関数を介してサポートされています。利用可能なイベント名は、 event
、 textDelta
、 textDone
、 imageFileDone
、 toolCallDelta
、 runStepDone
、 toolCallDone
、およびend
重要
Openaiのサーバーセンドイベントは、Async/待ち望みではありません。
リスナーがツールの出力のリダイレクトなど、非同期ファッションで作業を実行する必要がある場合は、これらのイベントの拡張機能を使用することを検討してください。実行が完了した後、この順序で呼び出されます。利用可能な非同期イベント名は、 textDoneAsync
、 imageFileDoneAsync
、 runStepDoneAsync
、 toolCallDoneAsync
、およびendAsync
です。
アシスタントのcreate()
関数が呼び出されたときに追加のリソースをゆっくりと止めたい場合は、クラスにbeforeInit()
関数を実装します。これは、アシスタントが作成される前に呼び出される非同期メソッドです。
async beforeInit ( ) {
await this . # createFileSearch ( ) ;
}
同様に、 afterInit()
関数を使用できます。たとえば、新しく作成されたアシスタントのIDを環境ファイルに書き出すこと。
async afterInit ( ) {
// ...
}
すべてのアシスタントイベントは、追加の専門家メタデータの引数を追加します。実行のstream
を含むオブジェクト。これにより、 currentEvent
、 finalMessages
などのOpenai-Nodeのヘルパー機能を使用できます。
assistant . on ( "endAsync" , async ( metadata ) => {
await metadata . stream . finalMessages ( ) ;
} ) ;
アシスタントをツールとして使用することは、Experts.jsフレームワークの中心的な焦点です。ツールはアシスタントのサブクラスであり、親オブジェクトのインターフェイスをカプセル化します。このようにして、JSツールはエージェントアーキテクチャの再利用可能なコンポーネントです。私たちの例は、簡潔にするために、基本的なメッセージの渡しパターンを示しています。 OpenAIのすべてのツールと機能の呼び出し機能を最大限に活用する必要があります。
class EchoTool extends Tool {
constructor ( ) {
super ( {
name : "Echo Tool" ,
instructions : "Echo the same text back to the user" ,
parentsTools : [
{
type : "function" ,
function : {
name : "echo" ,
description : description ,
parameters : {
type : "object" ,
properties : { message : { type : "string" } } ,
required : [ "message" ] ,
} ,
} ,
} ,
] ,
} ) ;
}
}
注意
ツールの関数名が、親のツール名全体で一意にすることが重要です。
そのため、ツールクラス名は重要であり、Openaiのモデルがどのツールを呼び出すかを決定するのに役立ちます。ツールクラスに適した名前を選択してください。たとえば、 ProductsOpenSearchTool
products_open_search
となり、モデルがどのような役割を実行するかとともに、モデルが推測するのを明確に支援します。
addAssistantTool
関数を介してツールがアシスタントに追加されます。この関数は、ツールをアシスタントのツールアレイに追加し、アシスタントの構成を更新します。これは、アシスタントのコンストラクターでsuper()
後に発生する必要があります。
class MainAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Company Assistant" ,
instructions : "..."
} ) ;
this . addAssistantTool ( EchoTool ) ;
}
}
ツールアシスタントの応答は、親アシスタントまたはツールの出力として自動的に送信されます。
デフォルトでは、ツールはLLM model
に裏付けられており、アシスタントと同じライフサイクルイベント、実行などをすべて実行します。ただし、 llm
オプションをfalse
に設定して、コアアシスタントの機能を使用しないツールを作成できます。そうするときは、ツールにask()
関数を実装する必要があります。返品値は、ツールの出力として送信されます。
class AnswerTwoTool extends Tool {
constructor ( ) {
super ( {
// ...
llm : false ,
parentsTools : [ ... ] ,
} ) ;
}
async ask ( message ) {
return ... ;
}
}
複雑なワークフローでは、LLMバックされたツールを使用して人間または他のLLM命令を実行可能コードに変換でき、そのコード(LLM出力ではなく)の結果をツールの親の出力に提出する必要があります。たとえば、 ProductsOpenSearchTool
はメッセージをOpenSearchクエリに変換し、それらを実行し、結果を返すことができます。サブクラスはanswered()
関数を実装して出力を制御できます。この場合、 output
OpenSearchクエリとなり、ツール出力にはそのLLM生成クエリの結果が含まれるようになりました。
async answered ( output ) {
const args = JSON . parse ( output ) ;
return await this . opensearchQuery ( args ) ;
}
あるいは、LLMバックされたツールは、独自のツール出力を親アシスタントまたはツールに戻すことを選択できます。したがって、LLM出力を無視します。これにより、すべてのツールツール出力を親の出力として提出することもできます。以下の製品カタログの例でこれが重要である理由の詳細について。
class ProductsTool extends Tool {
constructor ( ) {
super ( {
// ...
temperature : 0.1 ,
tools : [ { type : "code_interpreter" } ] ,
outputs : "tools" ,
parentsTools : [ ... ] ,
} ) ;
this . addAssistantTool ( ProductsOpenSearchTool ) ;
this . on ( "imageFileDoneAsync" , this . imageFileDoneAsync . bind ( this ) ) ;
}
}
OpenaiのアシスタントAPIは、メッセージとファイルが内部に保存されるThreadsという新しいリソースを導入します。基本的に、スレッドはエージェントのマネージドコンテキストウィンドウ(メモリ)です。 Experts.jsを使用して新しいスレッドを作成するのは、次のように簡単です。
const thread = await Thread . create ( ) ;
console . log ( thread . id ) // thread_abc123
また、メッセージ、ファイル、またはツールリソースを備えたスレッドを作成して、会話を開始することもできます。 OpenaiのスレッドCreate Request Bodyは、スレッドAPIリファレンスで概説されています。
const thread = await Thread . create ( {
messages : [
{ role : "user" , content : "My name is Ken" } ,
{ role : "user" , content : "Oh, my last name is Collins" } ,
] ,
} ) ;
const output = await assistant . ask ( "What is my full name?" , thread . id ) ;
console . log ( output ) // Ken Collins
デフォルトでは、Experts.jsの各ツールには独自のスレッドとコンテキストがあります。これにより、ツールが提出されるのをまだ待っているアシスタントのスレッドをツールが共有する場合に発生する潜在的なスレッドロックの問題が回避されます。次の図は、この問題を回避するために、あなたに代わってスレッドを管理する方法を示しています。
専門家へのすべての質問には、スレッドIDが必要です。チャットアプリケーションの場合、IDはクライアントに保存されます。 URLパスパラメーターなど。 Expert.jsでは、他のクライアント側IDは必要ありません。各アシスタントはLLMバックされたツールを呼び出すため、必要に応じてそのツールのスレッドを見つけたり作成したりします。 Experts.jsは、Openaiのスレッドメタデータを使用して、この親 - >子スレッド関係を保存します。
ランは、アシスタントのask
機能の背後で管理されます。ただし、2つの方法のいずれかで実行を作成するときに使用されるオプションを渡すことができます。
まず、アシスタントのコンストラクターにrun_options
指定できます。これらのオプションは、アシスタントによって作成されたすべての実行に使用されます。これは、 tool_choice
オプションを介してモデルにツールを使用させるのに最適な方法です。
class CarpenterAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
run_options : {
tool_choice : {
type : "function" ,
function : { name : "my_tool_name" } ,
} ,
} ,
} ) ;
this . addAssistantTool ( MyTool ) ;
}
}
または、現在の実行に使用されるオプションオブジェクトをask
メソッドに渡すこともできます。これは、シングルランオプションを作成する素晴らしい方法です。
await assistant . ask ( "..." , "thread_abc123" , {
run : {
tool_choice : { type : "function" , function : { name : "my_tool_name" } } ,
additional_instructions : "..." ,
additional_messages : [ ... ] ,
} ,
} ) ;
これらのコードの例とその他の動作を確認するには、テストスイートをご覧ください。
概要セクションでは、次の種類の質問に答えることができる3層エージェントシステムを示しました。この例では、すべてではないにしても、ほとんどの場合、Experts.jsフレームワークの機能を使用しています。
textDelta
イベントを使用して、エクスプレスルートから応答をストリーミングする基本的な例。
import express from "express" ;
import { MainAssistant } from "../experts/main.js" ;
const assistant = await MainAssistant . create ( ) ;
messagesRouter . post ( "" , async ( req , res , next ) => {
res . setHeader ( "Content-Type" , "text/plain" ) ;
res . setHeader ( "Transfer-Encoding" , "chunked" ) ;
assistant . on ( "textDelta" , ( delta , _snapshot ) => {
res . write ( delta . value ) ;
} ) ;
await assistant . ask ( req . body . message . content , req . body . threadID ) ;
res . end ( ) ;
} ) ;
アシスタントのAPIは、 image_url
またはimage_file
コンテンツタイプのいずれかを使用して画像を使用してメッセージをサポートしています。 ask()
関数は文字列またはネイティブOpenaiメッセージオブジェクトをサポートしているためです。
const output = await assistant . ask (
{
role : "user" ,
content : [
{ type : "text" , text : "Tell me about this image." } ,
{ type : "image_file" , image_file : { file_id : file . id detail : "high" } } ,
] ,
} ,
threadID
) ;
ファイル検索にベクトルストアを使用することは、3番目の構成オプションを介してOpenAIのインターフェイスを使用して簡単です。高度な機能で説明されているbeforeInit()
関数を使用して、ベクトルストアをオンデマンドで作成することもできます。
class VectorSearchAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Vector Search Assistant" ,
instructions : "..." ,
tools : [ { type : "file_search" } ] ,
temperature : 0.1 ,
tool_resources : {
file_search : {
vector_store_ids : [ process . env . VECTOR_STORE_ID ] ,
} ,
} ,
} ) ;
}
}
ストリーミング&イベント機能を使用してトークンの使用を報告すると、アシスタントごとのメトリックを持つことができます。
class MyAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
} ) ;
this . on ( "runStepDone" , this . # reportUsage . bind ( this ) ) ;
}
# reportUsage ( runStep ) {
if ( ! runStep ?. usage ?. total_tokens ) return ;
const iT = runStep . usage . prompt_tokens ;
const oT = runStep . usage . completion_tokens ;
const tT = runStep . usage . total_tokens ;
console . log ( { InTokens : iT , OutTokens : oT , TotalTokens : tT } ) ;
}
}
アシスタントを生産環境に展開するためには、次の構成をお勧めします。まず、アシスタントのIDを作成または見つけます。文字列はasst_abc123
の形式になります。次に、このIDをアシスタントまたはツールのコンストラクターに渡します。これにより、すべての展開で同じアシスタントが使用されることが保証されます。
class MyAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
id : process . env . MY_ASSISTANT_ID
} ) ;
}
}
アシスタントまたはツールがIDで見つかると、存在する異なるリモート構成はローカル構成によって上書きされます。必要に応じて、たとえばステージング環境では、 skipUpdate
オプションをtrue
に設定することにより、この動作をバイパスできます。
EXPERTS_DEFAULT_MODEL
環境変数を使用して、すべてのアシスタントのモデルをグローバルに設定できます。これは、アシスタントのコンストラクターにモデルを明示的に設定していない場合にのみ機能します。
アシスタントをデバッグするには、 DEBUG=1
環境変数を設定できます。これにより、すべてのAPI呼び出しとサーバーセンドイベントの冗長なロギングが出力されます。デルタイベントはやや冗長であり、デフォルトでは無効になります。また、 DEBUG_DELTAS=1
環境変数を使用してそれらをオンにしてください。
このプロジェクトは、開発者のコンテナを活用しています。つまり、サポートするIDEで開くためにすぐに開始できます。これには、推奨されるアプローチである開発コンテナを使用したVSコードの使用が含まれます。
開発コンテナで開いたら、OpenAI APIキーとPostimage.org APIキーを使用して.env.development.local
ファイルを作成します。
OPENAI_API_KEY=sk-...
POST_IMAGES_API_KEY=...
これで、次のコマンドを実行できます。
./bin/setup
./bin/test