O Experts.js é a maneira mais fácil de criar e implantar os assistentes do OpenAI e vinculá -los como ferramentas para criar um painel de especialistas com memória expandida e atenção aos detalhes.
Feito via suporte ❤️ por tinta personalizada | Tecnologia
A nova API de assistentes da OpenAI define um novo padrão da indústria, avançando significativamente além da API de conclusão de bate -papo amplamente adotada. Representa um grande salto na usabilidade dos agentes de IA e na maneira como os engenheiros interagem com o LLMS. Emparelhado com o modelo GPT-4O Mini de ponta, os assistentes agora podem fazer referência a arquivos e imagens anexados como fontes de conhecimento em uma janela de contexto gerenciada chamada Thread. Diferentemente dos GPTs personalizados, os assistentes suportam instruções de até 256.000 caracteres, integram -se a 128 ferramentas e utilizam a inovadora API do Vector Store para pesquisa eficiente de arquivos em até 10.000 arquivos por assistente.
O especialista.js pretende simplificar o uso dessa nova API, removendo a complexidade de gerenciar objetos de execução e permitir que os assistentes sejam vinculados como ferramentas.
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
Mais importante, o Experts.JS apresenta os assistentes como ferramentas, permitindo a criação de sistemas de agentes de Multi AI. Cada ferramenta é um assistente apoiado por LLM que pode assumir funções especializadas ou cumprir tarefas complexas em nome de seu assistente ou ferramenta pai. Permitindo fluxos de trabalho de orquestração complexos ou coreografando uma série de tarefas de malha. Aqui está mostrado um exemplo de assistente de empresa com uma ferramenta de catálogo de produtos que possui uma ferramenta de apoio para LLM para criar consultas OpenSearch.
Instale via npm. O uso é muito simples, existem apenas três objetos para importar.
npm install experts
O Experts.js suporta a sintaxe de importação ES6 e o CommonJS requer declarações.
import { Assistant , Tool , Thread } from "experts" ;
O construtor de nosso objeto de fachada de assistente requer um nome, descrição e instruções. O terceiro argumento é um conjunto de opções que mapeiam diretamente todas as opções do corpo de solicitação descritas na documentação Criar Assistente. Todos os exemplos em especialistas. O modelo padrão é 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 ( ) ;
A função de fábrica de base do especialista Assistant.create()
const assistant = Assistant . create ( {
name : "My Assistant" ,
instructions : "..." ,
model : "gpt-4o-mini" ,
} ) ;
Importante
Criar assistentes sem um parâmetro id
sempre criará um novo assistente. Consulte nossa seção de implantação para obter mais informações.
A função ask()
é uma interface simples para perguntar ou instruir seu (s) assistente (s). Requer uma mensagem e um identificador de thread. Mais nos tópicos abaixo. A mensagem pode ser uma string ou objeto de mensagem nativo openai. É aqui que o especialista.js realmente brilha. Você nunca precisa gerenciar objetos de execução ou suas etapas de execução diretamente.
const output = await assistant . ask ( "..." , threadID )
const output = await assistant . ask ( { role : "user" , content : "..." } , threadID ) ;
As ferramentas normais do OpenAI e a chamada de funções são suportadas por meio do objeto Opções de construtores por meio de tools
e tool_resources
. O especialistas também suporta a adição de assistentes como ferramentas. Mais informações sobre o uso de assistentes como ferramentas podem ser encontradas na próxima seção. Use a função addAssistantTool
para adicionar um assistente como uma ferramenta. Isso deve acontecer após super()
no construtor do seu assistente.
class MainAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Company Assistant" ,
instructions : "..." ,
} ) ;
this . addAssistantTool ( ProductsTools ) ;
}
}
Por padrão, o Experts.js aproveita os assistentes que transmitem eventos. Isso permite que seus aplicativos recebam resultados de texto, imagem e ferramenta por meio de eventos de ser servidor do OpenAI. Aproveitamos os ajudantes de stream do OpenAi-Node e superamos esses eventos, juntamente com alguns personalizados, dando aos seus assistentes para explorar o ciclo de vida completo de uma corrida.
const assistant = await MainAssistant . create ( ) ;
assistant . on ( "textDelta" , ( delta , _snapshot ) => {
process . stdout . write ( delta . value )
} ) ;
Todos os eventos de streaming de nó do OpenAI são suportados por meio da função de assistente on()
. Os nomes de eventos disponíveis são: event
, textDelta
, textDone
, imageFileDone
, toolCallDelta
, runStepDone
, toolCallDone
e end
Importante
Os eventos de ser servidor do OpenAI não são assíncronos/aguardam amigáveis.
Se seus ouvintes precisarem executar o trabalho de maneira assíncrona, como redirecionar as saídas da ferramenta, considere usar nossas extensões para esses eventos. Eles são chamados nesta ordem após a conclusão da corrida. Os nomes de eventos ASYNC disponíveis são: textDoneAsync
, imageFileDoneAsync
, runStepDoneAsync
, toolCallDoneAsync
e endAsync
.
Se você deseja resistir aos recursos adicionais preguiçosamente quando a função create()
de um assistente é chamada, implemente a função beforeInit()
em sua classe. Este é um método assíncrono que será chamado antes que o assistente seja criado.
async beforeInit ( ) {
await this . # createFileSearch ( ) ;
}
Da mesma forma, a função afterInit()
pode ser usada. Por exemplo, para escrever os IDs dos assistentes recém -criados em um arquivo de ambiente.
async afterInit ( ) {
// ...
}
Todos os eventos assistentes recebem um argumento de metadados de especialistas extras. Um objeto que contém o stream
da corrida. Isso permite que você use as funções auxiliares do OpenAi-Node, como currentEvent
, finalMessages
, etc.
assistant . on ( "endAsync" , async ( metadata ) => {
await metadata . stream . finalMessages ( ) ;
} ) ;
Usar um assistente como ferramenta é o ponto focal central da estrutura de especialistas.js. As ferramentas são uma subclasse do assistente e encapsulam a interface para seus objetos pais. Dessa maneira, as ferramentas do especialista.js são componentes reutilizáveis em sua arquitetura Agentic. Nossos exemplos ilustram um padrão básico de passagem de mensagens, para a brevidade. Você deve aproveitar todas as ferramentas do OpenAI e os recursos de chamada de função ao máximo.
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" ] ,
} ,
} ,
} ,
] ,
} ) ;
}
}
Cuidado
É fundamental que o nome da função da sua ferramenta seja exclusivo em todo o conjunto de nomes de ferramentas de seus pais.
Como tal, os nomes das classes de ferramentas são importantes e ajudam os modelos do OpenAI a decidir qual ferramenta ligar. Portanto, escolha um bom nome para sua classe de ferramentas. Por exemplo, ProductsOpenSearchTool
será products_open_search
e claramente ajuda o modelo a inferir junto com a descrição da ferramenta qual função ela desempenha.
As ferramentas são adicionadas ao seu assistente através da função addAssistantTool
. Esta função adicionará a ferramenta à matriz de ferramentas do assistente e atualizará a configuração do assistente. Isso deve acontecer após super()
no construtor do seu assistente.
class MainAssistant extends Assistant {
constructor ( ) {
super ( {
name : "Company Assistant" ,
instructions : "..."
} ) ;
this . addAssistantTool ( EchoTool ) ;
}
}
Sua resposta ao assistente de ferramenta será enviada automaticamente como saída para o assistente ou ferramenta pai.
Por padrão, as ferramentas são apoiadas por um model
LLM e executam todos os mesmos eventos, execuções, etc. como assistentes. No entanto, você pode criar uma ferramenta que não use nenhum dos recursos do Assistente Core, definindo a opção llm
como false
. Ao fazer isso, você deve implementar a função ask()
em sua ferramenta. O valor de retorno será enviado como saída da ferramenta.
class AnswerTwoTool extends Tool {
constructor ( ) {
super ( {
// ...
llm : false ,
parentsTools : [ ... ] ,
} ) ;
}
async ask ( message ) {
return ... ;
}
}
Nos fluxos de trabalho complexos, uma ferramenta de backup LLM pode ser usada para converter instruções humanas ou outras LLM em código executável e o resultado desse código (não a saída LLM) precisará ser enviada para as saídas da sua ferramenta. Por exemplo, o ProductsOpenSearchTool
pode converter mensagens em consultas OpenSearch, executá -las e retornar os resultados. Subsses podem implementar a função answered()
para controlar a saída. Nesse caso, a output
seria uma consulta OpenEarch e as saídas das ferramentas agora contêm os resultados dessa consulta gerada por LLM.
async answered ( output ) {
const args = JSON . parse ( output ) ;
return await this . opensearchQuery ( args ) ;
}
Como alternativa, a LLM Backed Tools pode optar por redirecionar suas próprias saídas de ferramentas para o assistente ou ferramenta dos pais. Ignorando assim a saída LLM. Isso também permite que todas as saídas da ferramenta de ferramentas sejam enviadas como saída do pai. Mais sobre por que isso é importante no exemplo do catálogo de produtos abaixo.
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 ) ) ;
}
}
A API de assistentes do OpenAI apresenta um novo recurso chamado Threads, cujos mensagens e arquivos são armazenados. Essencialmente, os threads são uma janela de contexto gerenciada (memória) para seus agentes. Criar um novo tópico com especialistas.js é tão fácil quanto:
const thread = await Thread . create ( ) ;
console . log ( thread . id ) // thread_abc123
Você também pode criar um thread com mensagens, arquivos ou recursos de ferramentas para iniciar uma conversa. Apoiamos o corpo do encadeamento do OpenAI, o corpo de solicitação descrito em seus threads referência da 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
Por padrão, cada ferramenta em especialistas.js tem seu próprio thread & context. Isso evita um potencial problema de travamento do encadeamento que acontece se uma ferramenta compartilhar o encadeamento de um assistente ainda aguardando as saídas da ferramenta. O diagrama a seguir ilustra como o especialista.js gerencia threads em seu nome para evitar esse problema:
Todas as perguntas para seus especialistas exigem um ID de thread. Para aplicativos de bate -papo, o ID seria armazenado no cliente. Como um parâmetro de caminho da URL. Com o Expert.JS, não são necessários outros IDs do lado do cliente. Como cada assistente chama uma ferramenta de backup LLM, ele encontrará ou criará um thread para essa ferramenta, conforme necessário. Especialts.js armazena esse pai -> Relacionamento do thread infantil para você usando os metadados do tópico do OpenAI.
As execuções são gerenciadas para você por trás da função ask
do assistente. No entanto, você ainda pode passar as opções que serão usadas ao criar uma corrida de duas maneiras.
Primeiro, você pode especificar run_options
no construtor do assistente. Essas opções serão usadas para todas as execuções criadas pelo assistente. Esta é uma ótima maneira de forçar o modelo a usar uma ferramenta através da opção tool_choice
.
class CarpenterAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
run_options : {
tool_choice : {
type : "function" ,
function : { name : "my_tool_name" } ,
} ,
} ,
} ) ;
this . addAssistantTool ( MyTool ) ;
}
}
Como alternativa, você pode passar um objeto de opções para o método ask
a ser usado para a execução atual. Esta é uma ótima maneira de criar opções de execução única.
await assistant . ask ( "..." , "thread_abc123" , {
run : {
tool_choice : { type : "function" , function : { name : "my_tool_name" } } ,
additional_instructions : "..." ,
additional_messages : [ ... ] ,
} ,
} ) ;
Para ver exemplos de código destes e mais em ação, dê uma olhada no nosso conjunto de testes.
Na seção de visão geral, mostramos um sistema de agentes de três camadas que pode responder aos seguintes tipos de perguntas. Os exemplos usam a maioria, se não todos, os recursos da estrutura do especialista.js.
Exemplo básico usando o evento textDelta
para transmitir respostas de uma rota expressa.
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 ( ) ;
} ) ;
A API do assistente suporta mensagens com imagens usando os tipos de conteúdo image_url
ou image_file
. Como nossa função ask()
suporta strings ou objetos nativos de mensagem 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
) ;
O uso de um loja de vetores para pesquisa de arquivos é fácil usando a interface do OpenAI por meio da nossa terceira opção de configuração. Como alternativa, você pode criar seu armazenamento de vetores sob demanda usando nossa função beforeInit()
descrita em recursos avançados.
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 ] ,
} ,
} ,
} ) ;
}
}
O uso do recurso Streaming & Events para relatar o uso do token permite que você tenha métricas por assistente.
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 } ) ;
}
}
Para que um assistente seja implantado em um ambiente de produção, recomendamos as seguintes configurações. Primeiro, crie ou encontre o ID do seu assistente. A sequência estará no formato de asst_abc123
. Em seguida, passe este ID para o construtor do assistente ou das ferramentas. Isso garantirá que o mesmo assistente seja usado em todas as implantações.
class MyAssistant extends Assistant {
constructor ( ) {
super ( {
// ...
id : process . env . MY_ASSISTANT_ID
} ) ;
}
}
Depois que um assistente ou ferramenta é encontrado pelo ID, quaisquer configurações remotas presentes diferentes são substituídas pelas configurações locais. Se necessário, por exemplo, em um ambiente de preparação, você pode ignorar esse comportamento definindo a opção skipUpdate
como true
.
Você pode definir globalmente o modelo para todos os assistentes usando a variável de ambiente EXPERTS_DEFAULT_MODEL
. Isso só funciona se você não definiu explicitamente o modelo no construtor do seu assistente.
Para depurar seu assistente, você pode definir a variável DEBUG=1
ambiente. Isso produzirá o registro detalhado de todas as chamadas da API e os eventos de ser servidor. Os eventos da Delta podem ser um pouco detalhados e são desativados por padrão. Por favor, use também a variável de ambiente DEBUG_DELTAS=1
para ativá -las.
Este projeto aproveita os contêineres de dev, o que significa que você pode abri -lo em qualquer IDE de suporte para começar imediatamente. Isso inclui o uso de código VS com contêineres de dev, que é a abordagem recomendada.
Uma vez aberto em seu contêiner de desenvolvimento, crie um arquivo .env.development.local
com sua chave de API do OpenAI e PostImage.org API:
OPENAI_API_KEY=sk-...
POST_IMAGES_API_KEY=...
Agora você pode executar os seguintes comandos:
./bin/setup
./bin/test