Experts.js是创建和部署OpenAI助手并将它们链接在一起的最简单方法,以创建具有扩展记忆和关注细节的专家系统的工具。
通过自定义墨水通过支持❤️|技术
OpenAI的新助手API设定了新的行业标准,超出了广泛采用的聊天完成API的发展。它代表了AI代理的可用性和工程师与LLMS互动的方式的重大飞跃。与尖端的GPT-4O Mini模型相结合,助手现在可以将附件和图像作为知识源称为托管上下文窗口中的知识来源。与自定义GPT不同,助手支持最多256,000个字符,与128个工具集成在一起,并利用创新的矢量商店API,每位助手最多10,000个文件进行有效的文件搜索。
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支持的助手,可以代表其父母助手或工具扮演专业角色或完成复杂的任务。允许复杂的编排工作流或编舞一系列紧密编织的任务。此处显示的是具有产品目录工具的公司助手的示例,该工具本身具有LLM支持的工具来创建OpenSearch查询。
通过NPM安装。用法非常简单,只有三个对象可导入。
npm install experts
Experts.js支持ES6导入语法和CONCORJS需要语句。
import { Assistant , Tool , Thread } from "experts" ;
我们助理立面对象的构造函数需要名称,描述和说明。第三个参数是一组选项,可直接映射到创建助理文档中概述的所有请求正文选项。专家中的所有例子。默认模型是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消息对象。这是专家的真正发光的地方。您永远不必直接管理运行对象或其运行步骤。
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节点的流帮助者,并浮出这些事件以及一些自定义的事件,使您的助手可以利用运行的完整生命周期。
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的服务器点事件不是异步/等待的友好。
如果您的听众需要以异步方式执行工作,例如重定向工具输出,请考虑使用我们对这些事件的扩展。运行完成后,它们被称为此顺序。可用的异步事件名称是: textDoneAsync
, imageFileDoneAsync
, runStepDoneAsync
, toolCallDoneAsync
和endAsync
。
如果您想在调用助手的create()
函数时懒惰地站立额外的资源,请在类中实现beforeInit()
函数。这是一种异步方法,在创建助手之前将被调用。
async beforeInit ( ) {
await this . # createFileSearch ( ) ;
}
同样,也可以使用afterInit()
函数。例如,将新创建的助手ID写入环境文件。
async afterInit ( ) {
// ...
}
所有助理活动都将获得额外的专家元数据论点。包含运行stream
对象。这使您可以使用OpenAi节点的助手功能,例如currentEvent
, finalMessages
等。
assistant . on ( "endAsync" , async ( metadata ) => {
await metadata . stream . finalMessages ( ) ;
} ) ;
使用助手作为工具是专家框架的核心焦点。工具是助手的子类,并封装了其父对象的接口。通过这种方式,experts.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引入了一种新资源,称为线程,其中存储在其中。本质上,线程是代理的托管上下文窗口(内存)。使用Experts.js创建新线程很容易:
const thread = await Thread . create ( ) ;
console . log ( thread . id ) // thread_abc123
您还可以创建一个带有消息,文件或工具资源的线程以开始对话。我们支持OpenAI的线程创建请求主体在其线程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
功能后面为您管理的。但是,您仍然可以通过以两种方式创建运行时使用的选项。
首先,您可以在助手的构造函数中指定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 : [ ... ] ,
} ,
} ) ;
要查看这些代码示例,以及其他行动,请查看我们的测试套件。
在概述部分中,我们展示了一个三层代理系统,可以回答以下类型的问题。这些示例使用了大多数(如果不是全部)Experts.js Framework的功能。
使用textDelta
事件的基本示例从Express路线流式响应。
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
) ;
使用矢量存储进行文件搜索很容易使用OpenAI的界面通过我们的第三个配置选项。您可以使用我们的beforeInit()
函数在高级功能中描述的vector Store按需创建矢量存储。
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调用和服务器仪事件的详细记录。 Delta事件可能有些冗长,默认情况下是禁用的。还请使用DEBUG_DELTAS=1
环境变量来打开这些变量。
该项目利用开发容器,这意味着您可以在任何支持IDE中打开它,以立即开始。这包括使用DEV容器的VS代码,这是推荐的方法。
在开发容器中打开后,使用OpenAI API键和postimage.org api密钥创建.env.development.local
。
OPENAI_API_KEY=sk-...
POST_IMAGES_API_KEY=...
现在您可以运行以下命令:
./bin/setup
./bin/test