Experten.JS ist der einfachste Weg, um die Assistenten von OpenAI zu erstellen, einzusetzen und sie als Tools zu verknüpfen, um ein System von Experten mit erweitertem Speicher und Liebe zum Detail zu erstellen.
Hergestellt durch Unterstützung ❤️ nach benutzerdefinierter Tinte | Technik
Die neue Assistenten -API von OpenAI legt einen neuen Branchenstandard fest, der über die weit verbreitete API der Chat -Abschlüsse hinausgeht. Es ist ein großer Sprung in der Verwendbarkeit von AI -Agenten und der Art und Weise, wie Ingenieure mit LLMs interagieren. In Kombination mit dem modernsten GPT-4O-Mini-Modell können Assistenten jetzt angehängte Dateien und Bilder als Wissensquellen in einem verwalteten Kontextfenster verweisen, das als Thread bezeichnet wird. Im Gegensatz zu benutzerdefinierten GPTs unterstützen Assistenten Anweisungen bis zu 256.000 Zeichen, integrieren in 128 Tools und verwenden die innovative Vektorspeicher -API für eine effiziente Dateisuche auf bis zu 10.000 Dateien pro Assistent.
Experten.JS zielt darauf ab, die Verwendung dieser neuen API zu vereinfachen, indem die Komplexität der Verwaltung von Laufobjekten entfernt und Assistenten als Tools miteinander verbunden werden können.
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
Noch wichtiger ist, dass Experte Jedes Tool ist ein von LLM unterstütztes Assistent, der im Namen ihres übergeordneten Assistenten oder Tools mit speziellen Rollen übernehmen oder komplexe Aufgaben erfüllen kann. Ermöglichen Sie komplexe Orchestrierungs -Workflows oder choreografieren eine Reihe von dicht strickenden Aufgaben. Hier ist ein Beispiel für einen Unternehmensassistenten mit einem Produktkatalog -Tool, das selbst über ein LLM -Hintergrund -Tool verfügt, um OpenSearch -Abfragen zu erstellen.
Installieren Sie über NPM. Die Verwendung ist sehr einfach, es gibt nur drei Objekte zum Importieren.
npm install experts
Experten.JS unterstützt sowohl die ES6 -Importsyntax als auch die CommonJS -Erforderliche Aussagen.
import { Assistant , Tool , Thread } from "experts" ;
Der Konstruktor unseres Assistant -Fassadenobjekts benötigt einen Namen, eine Beschreibung und Anweisungen. Das dritte Argument ist eine Reihe von Optionen, die alle in der Dokumentation erstellten Assistenten erstellten Anforderungskörperoptionen direkt überordnen. Alle Beispiele in Experten.Js sind zur Einfachheit in ES6 -Klassen geschrieben. Das Standardmodell ist 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 ( ) ;
Die Experten.JS Async Assistant.create()
Basis Factory -Funktion ist eine einfache Möglichkeit, einen Assistenten mit denselben Konstruktoroptionen zu erstellen.
const assistant = Assistant . create ( {
name : "My Assistant" ,
instructions : "..." ,
model : "gpt-4o-mini" ,
} ) ;
Wichtig
Das Erstellen von Assistenten ohne id
-Parameter erzeugt immer einen neuen Assistenten. Weitere Informationen finden Sie in unserem Bereitstellungsbereich.
Die ask()
-Funktion ist eine einfache Schnittstelle, mit der Sie Ihre Assistenten fragen oder anweisen können. Es erfordert eine Nachricht und eine Thread -Kennung. Mehr zu Threads unten. Die Nachricht kann ein Zeichenfolge oder ein nationales OpenAI -Nachrichtenobjekt sein. Hier glänzt Experten. Js wirklich. Sie müssen niemals direkte Objekte oder deren Auslaufschritte verwalten.
const output = await assistant . ask ( "..." , threadID )
const output = await assistant . ask ( { role : "user" , content : "..." } , threadID ) ;
Normale OpenAI -Tools und Funktionsaufrufe werden über unser Konstruktoren -Optionsobjekt über tools
und tool_resources
unterstützt. Experten.JS unterstützt auch Assistenten als Tools. Weitere Informationen zur Verwendung von Assistenten als Tools finden Sie im nächsten Abschnitt. Verwenden Sie die addAssistantTool
-Funktion, um einen Assistenten als Werkzeug hinzuzufügen. Dies muss nach super()
im Konstruktor Ihres Assistenten geschehen.
class MainAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Company Assistant" ,
instructions : "..." ,
} ) ;
this . addAssistantTool ( ProductsTools ) ;
}
}
Standardmäßig nutzen Experten.Js die Assistenten, die Ereignisse streamen. Diese ermöglichen es Ihren Anwendungen, über OpenAIS-Server-Send-Ereignisse Text-, Image- und Tool-Ausgänge zu empfangen. Wir nutzen die Stream-Helfer von Openai-Node und treiben diese Ereignisse zusammen mit einigen benutzerdefinierten Assistenten auf, um den gesamten Lebenszyklus eines Laufs zu nutzen.
const assistant = await MainAssistant . create ( ) ;
assistant . on ( "textDelta" , ( delta , _snapshot ) => {
process . stdout . write ( delta . value )
} ) ;
Alle OpenAI-Knoten-Streaming-Ereignisse werden über die Funktion unserer Assistenten on()
unterstützt. Die verfügbaren Ereignisnamen sind: event
, textDelta
, textDone
, imageFileDone
, toolCallDelta
, runStepDone
, toolCallDone
und end
Wichtig
OpenAIs Server-Send-Ereignisse sind nicht asynchron/warten freundlich.
Wenn Ihre Zuhörer asynchronisierte Arbeiten durchführen müssen, z. B. das Umleitungswerkzeugausgaben, sollten Sie unsere Erweiterungen zu diesen Ereignissen verwenden. Sie werden nach Abschluss des Laufs in dieser Reihenfolge aufgerufen. Die verfügbaren Async -Ereignisnamen sind: textDoneAsync
, imageFileDoneAsync
, runStepDoneAsync
, toolCallDoneAsync
und endAsync
.
Wenn Sie faul zusätzliche Ressourcen aufstellen möchten, wenn die Funktion eines Assistenten create()
aufgerufen wird, implementieren Sie die Funktion beforeInit()
in Ihrer Klasse. Dies ist eine asynchronisierte Methode, die aufgerufen wird, bevor der Assistent erstellt wird.
async beforeInit ( ) {
await this . # createFileSearch ( ) ;
}
Ebenso kann die afterInit()
-Funktion verwendet werden. Zum Beispiel, um neu erstellte Assistenten -IDs in eine Umgebungsdatei zu schreiben.
async afterInit ( ) {
// ...
}
Alle Assistentenveranstaltungen erhalten zusätzliche Experten von Metadatenargument. Ein Objekt, das den stream
des Laufs enthält. Auf diese Weise können Sie die Helferfunktionen des OpenAi-Knotens wie currentEvent
, finalMessages
usw. verwenden.
assistant . on ( "endAsync" , async ( metadata ) => {
await metadata . stream . finalMessages ( ) ;
} ) ;
Die Verwendung eines Assistenten als Werkzeug ist ein zentraler Schwerpunkt des Frameworks von Experten.js. Tools sind eine Unterklasse des Assistenten und verkapulieren die Schnittstelle für ihre übergeordneten Objekte. Auf diese Weise sind Experten.JS -Tools wiederverwendbare Komponenten in Ihrer Agentenarchitektur. Unsere Beispiele veranschaulichen ein grundlegendes Message -Passing -Muster für die Kürze. Sie sollten das gesamte Tool- und Funktionsfunktionen von OpenAI in vollem Umfang nutzen.
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" ] ,
} ,
} ,
} ,
] ,
} ) ;
}
}
Vorsicht
Es ist wichtig, dass der Funktionsname Ihres Tools in den gesamten Toolnamen seiner Eltern eindeutig ist.
Daher sind die Namen der Werkzeugklassen wichtig und helfen dabei, die Modelle von OpenAI zu entscheiden, welches Tool aufgerufen werden soll. Wählen Sie also einen guten Namen für Ihre Werkzeugkurs. Zum Beispiel wird ProductsOpenSearchTool
products_open_search
sein und hilft dem Modell klar, zusammen mit der Beschreibung des Tools, welche Rolle es spielt.
Tools werden Ihrem Assistenten über die addAssistantTool
-Funktion hinzugefügt. Diese Funktion fügt das Tool zum Tools -Array des Assistenten hinzu und aktualisiert die Konfiguration des Assistenten. Dies muss nach super()
im Konstruktor Ihres Assistenten geschehen.
class MainAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Company Assistant" ,
instructions : "..."
} ) ;
this . addAssistantTool ( EchoTool ) ;
}
}
Ihre Tool -Assistant -Antwort wird automatisch als Ausgabe für den übergeordneten Assistenten oder Tool eingereicht.
Standardmäßig werden Tools durch ein LLM model
gesichert und führen die gleichen Lebenszyklen -Ereignisse, Läufe usw. wie Assistenten durch. Sie können jedoch ein Tool erstellen, das keine der Funktionen des Kernassistenten verwendet, indem Sie die llm
-Option auf false
einstellen. Wenn Sie dies tun, müssen Sie die ask()
-Funktion in Ihrem Tool implementieren. Der Rückgabewert wird als Ausgabe des Tools eingereicht.
class AnswerTwoTool extends Tool {
constructor ( ) {
super ( {
// ...
llm : false ,
parentsTools : [ ... ] ,
} ) ;
}
async ask ( message ) {
return ... ;
}
}
In komplexen Workflows kann ein LLM -Hintertool verwendet werden, um menschliche oder andere LLM -Anweisungen in ausführbare Code umzuwandeln, und das Ergebnis dieses Code (nicht der LLM -Ausgabe) muss für die Ausgänge Ihres Tools übermittelt werden. Beispielsweise könnte der ProductsOpenSearchTool
Nachrichten in OpenSearch -Abfragen umwandeln, sie ausführen und die Ergebnisse zurückgeben. Unterklassen können die answered()
-Funktion implementieren, um die Ausgabe zu steuern. In diesem Fall wäre die output
eine OpenSearch-Abfrage und die Tools-Ausgänge enthalten nun die Ergebnisse dieser LLM-generierten Abfrage.
async answered ( output ) {
const args = JSON . parse ( output ) ;
return await this . opensearchQuery ( args ) ;
}
Alternativ können LLM -Backed -Tools ihre eigenen Toolausgänge an ihren übergeordneten Assistenten oder Tool zurückleiten. So ignorieren Sie die LLM -Ausgabe. Auf diese Weise können alle Tools -Toolausgaben als Ausgabe des Elternteils übermittelt werden. Mehr dazu, warum dies im unten stehenden Produktkatalog -Beispiel wichtig ist.
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 ) ) ;
}
}
OpenAIs Assistants API führt eine neue Ressource namens Threads ein, in der Nachrichten und Dateien gespeichert sind. Im Wesentlichen sind Threads ein verwaltetes Kontextfenster (Speicher) für Ihre Agenten. Das Erstellen eines neuen Threads mit Experten.js ist so einfach wie:
const thread = await Thread . create ( ) ;
console . log ( thread . id ) // thread_abc123
Sie können auch einen Thread mit Nachrichten, Dateien oder Toolressourcen erstellen, um eine Konversation zu starten. Wir unterstützen OpenAIs Thread Create -Anforderungskörper, die in ihrer Threads -API -Referenz umrissen wurden.
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
Standardmäßig hat jedes Tool in Experten.js einen eigenen Thread und Kontext. Dies vermeidet ein potenzielles Problem mit Thread -Sperren, das geschieht, wenn ein Tool einen Assistenten -Thread weitergeben sollte, der immer noch darauf wartet, dass die Werkzeugausgänge eingereicht werden. Das folgende Diagramm zeigt, wie Experten.js Threads in Ihrem Namen verwaltet, um dieses Problem zu vermeiden:
Alle Fragen an Ihre Experten erfordern eine Thread -ID. Für Chat -Anwendungen würde die ID auf dem Client gespeichert. Wie ein URL -Pfadparameter. Mit Expert.js sind keine anderen kundenseitigen IDs erforderlich. Wenn jeder Assistent ein LLM -Backed -Tool aufruft, findet oder erstellt er nach Bedarf einen Thread für dieses Tool. Experten.js speichert diese Eltern -> Kinderfadenbeziehung für Sie mit OpenAIs Thread -Metadaten.
Läufe werden für Sie hinter der ask
-Funktion des Assistenten verwaltet. Sie können jedoch weiterhin Optionen übergeben, die beim Erstellen eines Laufs auf zwei Arten verwendet werden.
Zunächst können Sie run_options
im Konstruktor des Assistenten angeben. Diese Optionen werden für alle vom Assistenten erstellten Läufe verwendet. Dies ist eine großartige Möglichkeit, das Modell zu zwingen, ein Tool über die Option tool_choice
zu verwenden.
class CarpenterAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
run_options : {
tool_choice : {
type : "function" ,
function : { name : "my_tool_name" } ,
} ,
} ,
} ) ;
this . addAssistantTool ( MyTool ) ;
}
}
Alternativ können Sie ein Optionsobjekt an die ask
-Methode übergeben, die für den aktuellen Lauf verwendet werden soll. Dies ist eine großartige Möglichkeit, um Single -Lauf -Optionen zu erstellen.
await assistant . ask ( "..." , "thread_abc123" , {
run : {
tool_choice : { type : "function" , function : { name : "my_tool_name" } } ,
additional_instructions : "..." ,
additional_messages : [ ... ] ,
} ,
} ) ;
Um Code -Beispiele dafür und mehr in Aktion zu sehen, schauen Sie sich bitte unsere Testsuite an.
Im Abschnitt Überblick haben wir ein dreistufiges Agentensystem angezeigt, das die folgenden Arten von Fragen beantworten kann. Die Beispiele verwenden die meisten, wenn nicht alle die Funktionen des Frameworks von Experten.js.
Grundlegendes Beispiel Verwenden des textDelta
-Ereignisses, um Antworten von einer Expressroute zu streamen.
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 ( ) ;
} ) ;
Die API des Assistenten unterstützt Nachrichten mit Bildern, die entweder die Inhaltstypen image_url
oder image_file
verwenden. Da unsere ask()
-Funktion Zeichenfolgen oder native OpenAI -Nachrichtenobjekte unterstützt.
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
) ;
Die Verwendung eines Vektorspeichers für die Dateisuche ist die Verwendung der OpenAI -Schnittstelle über unsere dritte Konfigurationsoption einfach. Sie können alternativ Ihren Vektor Store on-Demand mit unserer in erweiterten Funktionen beschriebenen Funktionen beforeInit()
erstellen.
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 ] ,
} ,
} ,
} ) ;
}
}
Mit der Streaming- und Ereignisfunktion zur Berichterstattung über die Nutzung von Token können Sie pro-assistierende Metriken haben.
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 } ) ;
}
}
Damit ein Assistent in einer Produktionsumgebung eingesetzt wird, empfehlen wir die folgenden Konfigurationen. Erstellen oder finden Sie die ID Ihres Assistenten. Die Zeichenfolge befindet sich im Format von asst_abc123
. Geben Sie dann diese ID in den Konstruktor des Assistenten oder Tools ein. Dadurch wird sichergestellt, dass derselbe Assistent für alle Bereitstellungen verwendet wird.
class MyAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
id : process . env . MY_ASSISTANT_ID
} ) ;
}
}
Sobald ein Assistent oder ein Tool per ID gefunden wurde, werden alle unterschiedlichen Remote -Konfigurationen von den lokalen Konfigurationen überschrieben. Bei Bedarf können Sie beispielsweise in einer Staging -Umgebung dieses Verhalten umgehen, indem Sie die Option skipUpdate
auf true
einstellen.
Sie können das Modell für alle Assistenten weltweit mit der Umgebungsvariablen EXPERTS_DEFAULT_MODEL
festlegen. Dies funktioniert nur, wenn Sie das Modell nicht explizit in den Konstruktor Ihres Assistenten festgelegt haben.
Um Ihren Assistenten zu debuggen, können Sie die DEBUG=1
-Umgebungsvariable festlegen. Dadurch wird die ausführliche Protokollierung aller API-Anrufe und Server-Send-Ereignisse ausgegeben. Delta -Ereignisse können etwas ausführlich sein und standardmäßig deaktiviert werden. Bitte verwenden Sie auch die Umgebungsvariable DEBUG_DELTAS=1
um diejenigen einzuschalten.
Dieses Projekt nutzt Dev Container, was bedeutet, dass Sie es in jeder unterstützenden IDE öffnen können, um sofort loszulegen. Dies beinhaltet die Verwendung von VS -Code mit Entwicklungscontainern, der den empfohlenen Ansatz ist.
Erstellen Sie mit Ihrem OpenAI -API -Schlüssel und postimage.org -API -Taste eine .env.development.local
-Datei in Ihrem Entwicklungscontainer.
OPENAI_API_KEY=sk-...
POST_IMAGES_API_KEY=...
Jetzt können Sie die folgenden Befehle ausführen:
./bin/setup
./bin/test