2025-06-03
8分钟阅读

这是克里斯·贝尔(Chris Bell)的客座帖子敲
现在有很多关于建立AI代理商的谈话,但没有很多关于使这些代理商真正需要什么有用。
代理是一种旨在做出决策并执行行动以实现特定目标或一组目标的自主系统,而无需人工投入。
无论您的代理商在做出决策方面有多好,您都需要一个人来提供有关代理商目标的指导或投入。毕竟,一个无法互动或响应外部世界的代理商以及对其可以解决的问题受到限制的系统。
那是纽瓦的人的互动模式。进入在代理商可以继续执行任务之前,代理商的循环并需要该人的投入。

在这篇博客文章中,我们将使用 敲和Cloudflare 代理SDK为虚拟卡构建AI代理,以发行新卡时需要人为批准的虚拟卡。
您可以找到此示例的完整代码 在存储库中。
敲您可以在不编写任何集成代码的情况下,在应用程序内,电子邮件,SMS,PUSH和Slack上发送多通道消息。
通过敲门,您可以完全了解发送给用户的消息,同时还可以处理可靠的交付,用户通知首选项等。
您可以使用敲门量使用敲门来为您的代理商供电。 代理工具包这是一组工具,可将敲击的API和消息传递功能暴露于您的AI代理。
使用代理SDK作为我们AI代理的基础
Adents SDK为建立状态的实时代理提供了一个抽象耐用的对象使用嵌入式的,在全球范围内且持久状态,零延迟SQLITE数据库。
在使用代理SDK和CloudFlare平台之外构建AI代理意味着我们需要考虑WebSocket服务器,状态持久性以及如何水平扩展我们的服务。由于耐用的对象可以返回代理SDK,因此我们免费获得这些好处,同时拥有带有内置存储的全球可寻址计算,该计算完全无服务器并缩放为零。
在示例中,我们将使用这些功能来构建用户通过聊天实时与用户进行交互的代理,并且可以根据需要暂停和恢复。代理SDK是为异步代理工作流提供动力的理想平台,例如在人类互动中所需的工作流。
设置我们的敲门消息工作流程
在“敲门”中,我们使用Visual Workflow构建器设计批准工作流程,以创建跨通道消息传递逻辑。然后,我们使通知模板与要发送消息的每个通道关联。
敲门将自动应用 用户的偏好作为工作流执行的一部分,确保尊重您的用户通知设置。

您可以找到一个示例工作流程,我们已经在存储库中为此演示创建了一个示例。您可以通过 敲CLI将其导入您的帐户。
我们构建了AI代理作为聊天界面Aichatagent
来自CloudflareâS代理商SDK的抽象(文档)。这里的代理商SDK负责大部分复杂性,而我们将使用系统提示来实现LLM通话代码。
// src/index.ts从“代理/ai-chat-agent”中导入{aichatagent};从“@ai-sdk/openai”导入{OpenAi};从“ ai”导入{createAtaTaStreamResponse,streamText};出口类AIAGENT扩展了Aichatagent {异步OnChatMessage(onfinish){返回create createTatAstreamResponse({{执行:async(dataStream)=> {尝试 {const stream = streamText({{型号:Openai(“ GPT-4O-Mini”),系统:`您是金融服务公司的有益助手。您可以帮助客户发行信用卡。消息:this.messages,登陆Maxsteps:5,});stream.mergeintodataTastream(dataStream);} catch(错误){Console.Error(错误);}},,});}}
在客户端,我们使用Useagentchat
钩子代理/AI反应
包装为实时用户对代理聊天供电。
我们将代理建模为每个用户的聊天,我们使用该聊天使用
通过将过程的名称指定为用户身份
。
// src/index.ts从“代理/react”导入{useagent};从“代理/ai-react”中导入{useagentchat};函数聊天({userId}:{userId:string}){const agent = useAgent({agent:“ aiagent”,name:userId});const {消息,输入,handleinputChange,handlesubmit,isloading} = useAgentChat({agent});// ...}
这意味着我们有一个代理过程,因此是一个耐用的对象,每个用户。对于我们的人类用例,这在我们谈论恢复递延工具呼叫的过程中变得很重要。
我们通过暴露给代理商的卡发行能力ISSUECARD
工具。但是,我们自己没有编写批准流和跨通道逻辑,而是将其委派给我们,以通过将问题卡工具包裹在我们的requirehumaninput
方法。
现在,当用户要求请求新卡时,我们打电话敲门以启动我们的卡请求,该请求将通知组织中适当的管理员以请求批准。

要设置此问题,我们需要使用敲击代理工具包,该工具包暴露于与我们的AI Agent和Power跨通道消息中使用的方法。
来自“@nocklabs/agent-toolkit/ai-sdk”的导入{createknocktoolkit};从“ ai”导入{tool};从“ zod”导入{z};从“ ./index”导入{aiagent};从“ ./api”导入{issueCard};从“ ./constants”导入{base_url};异步函数onirectizetoolkit(代理:aiagent){const toolkit =等待createknocktoolkit({serviceToken:agent.env.knock_service_token});const issueCardTool =工具({描述:“向客户发出新的信用卡。”,参数:z.Object({customerid:z.String(),}),执行:async({customerId})=> {返回等待ISSUECARD(客户ID);},,});const {issueCard} = toolkit.requirehumaninput({issueCard:issuecardtool},{工作流程:“批准卡”,演员:agent.name,收件人:[“ admin_user_1”],元数据:{parreve_url:`$ {base_url}/card-Ind-Ind/批准`,recupl_url:`$ {base_url}/card-Indeed/recupt`,},,});返回{工具包,工具:{issueCard}};}
这里有很多事情要做,所以让我们走过关键部分:
我们包裹我们的
ISSUECARD
工具中的工具requirehumaninput
方法,暴露于敲击代理工具包我们希望将消息传递工作流程成为我们的
批准发行的卡
工作流程我们将代理商传递为
演员
该请求,该请求转换为用户ID我们将此工作流的接收者设置为用户
Admin_user_1
我们通过批准并拒绝URL,以便可以在我们的消息模板中使用它们
然后将包装工具返回为
ISSUECARD
在引擎盖下,这些选项传递给 敲门工作流触发API每次调用工作流程。此处列出的收件人集可能是动态的,也可以通过 敲门订阅API。
然后,我们可以将包装的发行卡工具传递给我们的LLM呼叫OnChatMessage
代理上的方法,以便可以将工具调用称为与代理商交互的一部分。
出口类AIAGENT扩展了Aichatagent {// ...其他方法异步OnChatMessage(onfinish){const {tools} =等待initializetoolkit(this);返回create createTatAstreamResponse({{执行:async(dataStream)=> {const stream = streamText({{型号:Openai(“ GPT-4O-Mini”),系统:“您是金融服务公司的有益助手。您可以帮助客户发行信用卡。”,消息:this.messages,登陆工具,Maxsteps:5,});stream.mergeintodataTastream(dataStream);},,});}}
现在,当代理商调用ISSUECARDTOOL
,我们援引敲门声发送我们的批准通知,在我们获得批准之前,将其推迟到发出该卡的工具呼叫。敲门工作流程会根据每个用户的偏好来指定,生成和传递消息的收件人,将消息发送到收件人集。
使用敲门 工作流程对于我们的批准消息,可以轻松构建跨渠道消息传递以根据其通信到达用户 偏好。我们也可以利用 延迟,,,, 油门,,,, 批处理, 和 状况策划更复杂的消息传递。
一旦消息发送给我们的批准者,下一步就是处理批准回来,将人类带入代理商的循环中。
批准请求是异步的,这意味着响应将来可以在任何时候出现。幸运的是,敲门会为您照顾繁重的举重,将活动通过 Webhook这跟踪与基础消息的互动。就我们而言,单击“批准”或“拒绝”按钮。
首先,我们设置了消息互动
Webhook处理程序在敲门仪表板中,将交互作用转发给我们的工人,并最终转移到我们的代理流程中。

在此处的示例中,我们将批准单击返回工人以处理,并将敲门消息ID附加到末尾pranve_url
和recult_url
跟踪与发送的特定消息的参与度。我们通过敲击中的消息模板内部的液体来执行此操作:{{data.approve_url}}?messageId = {{current_message.id}}。
这里有一个警告是,如果这是生产应用程序,我们很可能会在与该代理商运行的其他应用程序中单击我们的批准。我们仅出于此演示而在此处共同将其合作。
单击链接后,我们的工人中有一个处理程序,可以将消息标记为使用knockâs交互的消息 消息交互API,通过元数据的状态,以便以后可以使用。
从'@knocklabs/node'导入敲门;从“ Hono”导入{Hono};const app = new hono();const client = new stock();app.get(“/card-Inded/批准”,async(c)=> {const {messageId} = c.req.query();如果(!messageID)返回c.Text(“未找到消息ID”,{状态:400});等待client.messages.markassintacted(messageId,{状态:“批准”,});返回c.Text(“批准”);});
消息互动将通过我们设置的网络钩从敲门到我们的工人流动,以确保该过程完全异步。Webhook的有效载荷包括完整的消息,包括有关生成原始请求的用户的元数据,并保留有关请求本身的详细信息,在我们的情况下,其中包含工具调用。
导入{getagentbyname,routeAgentRequest}来自“ aDents”;从“ Hono”导入{Hono};const app = new hono();app.post(“/incoming/knock/webhook”,async(c)=> {const body =等待c.req.json();const env = c.env作为env;//从工具呼叫的呼叫用户中查找用户IDconst userId = body?.data?.Actors [0];如果(!userId){返回c.Text(“未找到用户ID”,{状态:400});}//为用户找到代理商const const contemagent =等待getagentByName(env.aiagent,userId);if(现有){//将请求路由到代理商进行处理const结果=等待现有。返回C.Json(结果);} 别的 {返回c.Text(“找不到”,{状态:404});}});
我们利用代理人的能力由指定的标识符解决,以将请求从工人路由到代理商。在我们的情况下,那是用户身份
。由于代理是由耐用的对象支持的,因此从传入的工人请求到查找和恢复代理的过程是微不足道的。
然后,我们使用有关原始工具调用的上下文,传递到敲击和圆形绊倒回代理,以恢复工具执行并发行卡。
出口类AIAGENT扩展了Aichatagent {// ...其他方法异步hangeincomingwebhook(身体:任何){const {toolkit} =等待initializetoolkit(this);const deferredtoolcall = toolkit.handlemessageInteraction(body);如果(!deferredtoolcall){返回{error:“没有给定的延期工具调用”};}//如果我们获得了“批准”状态,那么我们知道该电话已批准//因此我们可以恢复延期工具呼叫执行if(result.interaction.status ===“批准”){cont toolcallresult =等待Toolkit.ResumetoolExecution(Result.ToolCall);const {wendesp} =等待generateText({{型号:Openai(“ GPT-4O-Mini”),提示:`您被要求为客户签发卡。该卡现在已批准。结果是:$ {json.stringify(toolcallresult)}。});const messages = responseToassistantMessage(响应[0],result.toolcall,工具);//保存消息,使其显示给用户this.persistMessages([... this.messages,message]);}返回{状态:“成功”};}}
同样,这里发生了很多事情,所以让我们走过重要部分:
我们试图将身体(即从敲门载荷载荷)转换为通过
handlemessage Interaction
方法如果我们较早传递给互动调用的元数据状态具有批准状态,那么我们通过
RESUMETOLEXECTICTION
方法最后,我们从LLM中生成一条消息并坚持下去,以确保用户获悉批准的卡
有了最后一部分,我们现在可以要求发行新卡,从代理商处发出批准请求,发送批准消息,并将这些批准的路由交给我们的代理商进行处理。代理商将异步处理我们的卡问题请求,并使用很少的代码为我们恢复延期的工具调用。
防止重复批准
上述实施的一个问题是,如果有人多次单击批准按钮,我们很容易发行多个卡。为了纠正这一点,我们希望跟踪发出的工具调用,并确保最多一次处理呼叫。
为此,我们利用 代理商的内置状态,可以将其用于持久信息,而无需到达其他持久性商店(例如数据库或Redis),尽管我们希望我们绝对可以这样做。我们可以通过其ID跟踪工具调用,并在代理过程中捕获其当前状态。
键入ToolCallStatus =“请求” |“批准” |“被拒绝”;导出接口代理{工具节:记录<字符串,toolcallstatus>;}类aiagent扩展了aichatagent <env,agentState> {初始状态:AgentState = {工具节:{},};SettoolCallStatus(ToolCallId:String,状态:ToolCallStatus){this.setstate({...这个州,toolcalls:{... this.state.toolcalls,[toolcallid]:status},});}// ...}
在这里,我们创建工具调用的初始状态作为空对象。我们还添加了一种快速设置器助手方法,以使交互更容易。
接下来,我们需要记录要进行的工具调用。为此,我们可以使用OnafterCallknock
选项requirehumaninput
助理捕获已要求用户的工具调用。
const {issueCard} = toolkit.requirehumaninput({issueCard:issuecardtool},{//发送一旦发送到敲门后,请跟踪呼叫状态onafterCallKnock:async(toolcall)=>Agent.SetToolCallStatus(toolcall.id,“请求”),// ...和以前一样});
最后,我们需要检查状态何时我们处理传入的网络钩,并将工具调用标记为已批准(简短省略的某些代码)。
出口类AIAGENT扩展了Aichatagent {异步hangeincomingwebhook(身体:任何){const {toolkit} =等待initializetoolkit(this);const deferredtoolcall = toolkit.handlemessageInteraction(body);const toolcallid = result.toolcall.id;//确保这是可以处理的工具调用if(this.state.toolcalls [toolcallid]!==“请求”){返回{错误:“未要求工具调用”};}if(result.interaction.status ===“批准”){cont toolcallresult =等待工具包。ResumetoolExecution(result.toolcall);this.setToolCallStatus(toolCallid,“批准”);// ...像以前一样休息}}}
使用代理SDK和敲门,它很容易构建推迟工具调用的高级人类体验。
敲门的工作流构建器和通知引擎为您提供了构建块,以为您的代理商创建复杂的跨渠道消息传递。您可以轻松地创建通过SMS,推送,电子邮件或Slack发送消息的升级流,以尊重用户的通知偏好。敲门还使您可以完全了解用户收到的消息。
SDK下方的耐用物体抽象意味着我们获得了一个易于屈服并恢复到全球可寻址的代理过程。耐用对象中的持久存储意味着我们可以保留完整的聊天历史记录每个用户,以及代理过程中所需的任何其他状态以恢复使用代理(如我们的工具调用)。最后,基础耐用对象的无服务器性质意味着我们能够水平扩展以支持大量用户而无需努力。
如果您希望通过多人游戏体验来建立自己的AI代理聊天体验,那么您将从本指南中找到完整的代码 在Github可用。
Cloudflare的连接云保护整个公司网络,帮助客户建造有效的互联网规模应用程序,加速任何网站或互联网应用程序,,,,DDOS攻击的病房,保持黑客在海湾,可以帮助您您零信任的旅程。
访问1.1.1.1从任何设备开始使用我们的免费应用程序,该应用程序使您的互联网更快,更安全。