文档教程人工智能大模型AI Agent【免费下载链接】12-factor-agentsWhat are the principles we can use to build LLM-powered software that is actually good enough to put in the hands of production customers?项目地址https://gitcode.com/GitHub_Trending/12/12-factor-agents点击查看免费下载导读本篇文章基于 12-factor-agents 仓库第 12 章工作坊内容workshops/2025-05/sections/12-humanlayer-webhook/README.md演示如何把「等待人类审批」从同步轮询模式改造成异步 Webhook 驱动的模式落地十二要素中的Factor 6Launch / Pause / Resume with simple APIs。读完本文你将掌握如何在 Express 服务中初始化 HumanLayer SDK、如何让/thread接口即发即回地异步处理请求、如何通过createHumanContact在需要人工介入时暂停 Agent、以及如何用/webhook端点接收人类回复并恢复线程运行最终构建一个适合生产负载的 LLM Agent 服务端。背景同步轮询模式的生产瓶颈前几章如 11-humanlayer-approval 章节使用 HumanLayer SDK 的同步模式每次等待人类审批时程序都会进入循环轮询直到收到人类回复为止。这在生产环境中很不理想长时间占用服务器进程和连接浪费资源单个 Agent 实例无法支撑高并发HTTP 请求长时间挂起容易触发超时与启动、查询、恢复、停止这些程序化管理的预期相悖。因此本节实现 Factor 6核心思路是服务器在联系人类后立即结束处理pause通过 Webhook 接收人类回复并恢复线程resume。Factor 6 的官方说明见 content/factor-06-launch-pause-resume.md其中强调Agent 应该能用简单的 API 启动在需要长耗时操作如等待人类时可以暂停外部触发器如 Webhook应能让 Agent 从上次暂停处继续而不需要与 Agent 编排器深度集成。前置准备安装依赖与环境变量本节基于前几章已经搭建好的 BAML AgentDetermineNextStep函数 加减乘除工具与 Express 服务。相关依赖声明在 workshops/2025-05/sections/12-humanlayer-webhook/package.json 中{ dependencies: { baml: ^0.0.0, express: ^5.1.0, humanlayer: ^0.7.7, tsx: ^4.15.0, typescript: ^5.0.0 } }其中humanlayer是本节的核心依赖。运行服务前需要准备两个环境变量环境变量用途缺失时的行为HUMANLAYER_API_KEYHumanLayer 云服务的 API 密钥getHumanlayer()直接抛出ErrorHUMANLAYER_EMAIL接收人类审批/澄清请求的邮箱地址getHumanlayer()直接抛出Error沿用第 11 章的做法也可以先关闭 BAML 日志以便观察更干净的输出export BAML_LOGoff第一步在服务器中初始化 HumanLayer在src/server.ts顶部引入humanlayer并新增一个getHumanlayer()工厂函数。它从环境变量读取配置、做参数校验然后返回配置好的 SDK 实例src/server.ts import { Thread, agentLoop, handleNextStep } from ../src/agent; import { ThreadStore } from ../src/state; import { humanlayer } from humanlayer; const app express(); const store new ThreadStore(); const getHumanlayer () { const HUMANLAYER_EMAIL process.env.HUMANLAYER_EMAIL; if (!HUMANLAYER_EMAIL) { throw new Error(missing or invalid parameters: HUMANLAYER_EMAIL); } const HUMANLAYER_API_KEY process.env.HUMANLAYER_API_KEY; if (!HUMANLAYER_API_KEY) { throw new Error(missing or invalid parameters: HUMANLAYER_API_KEY); } return humanlayer({ runId: 12fa-agent, contactChannel: { email: { address: HUMANLAYER_EMAIL } } }); }参数说明runId: 12fa-agent给当前运行中的 Agent 命名便于在 HumanLayer 云控制台区分多个 AgentcontactChannel.email.address人类联系渠道。本节使用邮箱人类通过邮件回复来响应请求SDK 会自动把邮件回复转换为 Webhook 回调事件该阶段对/thread端点只做变量重命名newThread→result不改变原有同步行为。本步骤的完整可运行版本见 workshops/2025-05/sections/12-humanlayer-webhook/walkthrough/12-1-server-init.ts。若想跳过手工编辑可直接执行cp ./walkthrough/12-1-server-init.ts src/server.ts。第二步改造/thread端点异步返回 创建人类联系这是整个改造的核心。原来的/thread端点会同步运行agentLoop直到出现终止事件才返回 HTTP 响应现在要改为异步处理请求立即返回用Promise.resolve().then(async () {...})把 Agent 循环放进后台任务主流程立刻res.json({ status: processing })客户端不再阻塞等待在request_more_information和done_for_now时创建人类联系当线程等待人类响应时调用hl.createHumanContact()把最后一条事件的消息和thread_id作为state随联系一起发送——这个thread_id稍后是 Webhook 恢复线程的定位依据。src/server.ts -import express from express; import express, { Request, Response } from express; import { Thread, agentLoop, handleNextStep } from ../src/agent; import { ThreadStore } from ../src/state; -import { humanlayer } from humanlayer; import { humanlayer, V1Beta2HumanContactCompleted } from humanlayer; const app express(); }); } - // POST /thread - Start new thread -app.post(/thread, async (req, res) { app.post(/thread, async (req: Request, res: Response) { const thread new Thread([{ type: user_input, }]); - const threadId store.create(thread); - const newThread await agentLoop(thread); - - store.update(threadId, newThread); // run agent loop asynchronously, return immediately Promise.resolve().then(async () { const threadId store.create(thread); const newThread await agentLoop(thread); store.update(threadId, newThread); - const lastEvent newThread.events[newThread.events.length - 1]; - // If we exited the loop, include the response URL so the client can - // push a new message onto the thread - lastEvent.data.response_url /thread/${threadId}/response; const lastEvent newThread.events[newThread.events.length - 1]; - console.log(returning last event from endpoint, lastEvent); - - res.json({ - thread_id: threadId, - ...newThread if (thread.awaitingHumanResponse()) { const hl getHumanlayer(); // create a human contact - returns immediately hl.createHumanContact({ spec: { msg: lastEvent.data.message, state: { thread_id: threadId, } } }); } }); res.json({ status: processing }); });Thread.awaitingHumanResponse()定义在 src/agent.ts 中当线程最后一条事件是request_more_information或done_for_now时返回trueawaitingHumanResponse(): boolean { const lastEvent this.events[this.events.length - 1]; return [request_more_information, done_for_now].includes(lastEvent.data.intent); }这两个 intent 来自 baml_src/agent.baml 中定义的HumanTools类型ClarificationRequest向人类请求澄清信息和DoneForNow汇报已完成的工作。agentLoop在遇到这两个 intent 时会把线程原样返回见 src/agent.ts此时线程处于暂停状态等待人类介入。关键点createHumanContact是立即返回的fire-and-forget它只负责把请求投递给 HumanLayer 云并触发邮件通知不阻塞任何线程。人类稍后回复邮件后HumanLayer 云会把完成事件推送到我们即将编写的/webhook端点。第三步移除旧/response轮询端点新增/webhook恢复端点现在把服务器升级为能处理request_clarification类响应。改造要点删除旧的/thread/:id/response端点及其ApprovalPayload/ResponsePayload类型——不再需要客户端主动轮询推送结果保留GET /thread/:id用于查询线程状态获取thread_id新增handleHumanResponse函数与/webhook端点处理人类回复。// GET /thread/:id - Get thread status -app.get(/thread/:id, (req, res) { app.get(/thread/:id, (req: Request, res: Response) { const thread store.get(req.params.id); if (!thread) { }); type WebhookResponse V1Beta2HumanContactCompleted; -type ApprovalPayload { - type: approval; - approved: boolean; - comment?: string; -} const handleHumanResponse async (req: Request, res: Response) { -type ResponsePayload { - type: response; - response: string; } -type Payload ApprovalPayload | ResponsePayload; app.post(/webhook, async (req: Request, res: Response) { console.log(webhook response, req.body); const response req.body as WebhookResponse; -// POST /thread/:id/response - Handle clarification response -app.post(/thread/:id/response, async (req, res) { - let thread store.get(req.params.id); // response is guaranteed to be set on a webhook const humanResponse: string response.event.status?.response as string; const threadId response.event.spec.state?.thread_id; if (!threadId) { return res.status(400).json({ error: Thread ID not found }); } const thread store.get(threadId); if (!thread) { return res.status(404).json({ error: Thread not found }); } - const body: Payload req.body; - - let lastEvent thread.events[thread.events.length - 1]; - - if (thread.awaitingHumanResponse() body.type response) { - thread.events.push({ - type: human_response, - data: body.response - }); - } else if (thread.awaitingHumanApproval() body.type approval !body.approved) { - // push feedback onto the thread - thread.events.push({ - type: tool_response, - data: user denied the operation with feedback: ${body.comment} - }); - } else if (thread.awaitingHumanApproval() body.type approval body.approved) { - // approved, run the tool, pushing results onto the thread - await handleNextStep(lastEvent.data, thread); - } else { - res.status(400).json({ - error: Invalid request: body.type, - awaitingHumanResponse: thread.awaitingHumanResponse(), - awaitingHumanApproval: thread.awaitingHumanApproval() - }); - return; if (!thread.awaitingHumanResponse()) { return res.status(400).json({ error: Thread is not awaiting human response }); } - - // loop until stop event - const result await agentLoop(thread); - - store.update(req.params.id, result); - - lastEvent result.events[result.events.length - 1]; - lastEvent.data.response_url /thread/${req.params.id}/response; - - console.log(returning last event from endpoint, lastEvent); - - res.json(result); });对/webhook端点做逐步拆解类型标注type WebhookResponse V1Beta2HumanContactCompleted这是humanlayer包导出的、表示人类联系已完成的 Beta 版本事件类型提取人类回复response.event.status?.response——Webhook 事件中该字段保证存在人类回复内容用可选链读取后再断言为字符串提取线程 IDresponse.event.spec.state?.thread_id——这正是第二步createHumanContact时写入的state.thread_id用于把回复路由回正确的线程守卫校验缺少thread_id返回 400线程不存在返回 404线程当前未处于等待人类响应状态也返回 400防止重复/乱序回调恢复线程把human_response事件 push 进线程后调用agentLoop(thread)继续驱动 LLM直到下一次暂停或done_for_now终止再把结果写回ThreadStore本步骤的 walkthrough 版本12a-server.ts在此留出了实现空间完整逻辑可参考第 10/11 章中handleNextStep与agentLoop的组合见 src/agent.ts。线程状态存储在 src/state.ts 的ThreadStore内存 Map crypto.randomUUID()注释明确说明可以替换为 redis / sqlite / postgres 等任意持久化方案——这正是 Factor 6 强调的用简单 API 管理 Agent 状态的落地点。本步骤的完整可运行版本见 workshops/2025-05/sections/12-humanlayer-webhook/walkthrough/12a-server.ts。若想跳过手工编辑可执行cp ./walkthrough/12a-server.ts src/server.ts。第四步启动服务并验证 Webhook 流程在另一个终端启动服务器npx tsx src/server.ts服务器默认监听process.env.PORT || 3000。确认运行后向/thread端点发送请求curl -X POST http://localhost:3000/thread \ -H Content-Type: application/json \ -d {message:can you multiply 3 and xyz}由于/thread已改为异步模式你会立刻收到{ status: processing }与此同时后台的agentLoop会让 BAML 的DetermineNextStep见 baml_src/agent.baml判断出需要澄清request_more_information随后createHumanContact向你的HUMANLAYER_EMAIL发送一封请求澄清的邮件。人类回复邮件后HumanLayer 云把完成事件 POST 到/webhook端点agentLoop带着human_response事件继续运行最终得到done_for_now的最终结果。对divide除法场景BAML 的工具类型见 baml_src/tool_calculator.bamlagentLoop对divideintent 的处理是直接返回等待人类审批src/agent.ts同理可通过 Webhook 完成审批后恢复执行done_for_now场景则直接把最终结果通过 Webhook 回调返回。三种场景共用同一条创建联系 → 暂停 → Webhook 恢复 → 继续循环的流水线。源码级的调用链验证为了确认上述行为与仓库源码一致可对照以下文件关注点源码/文档路径HumanLayer 初始化与/thread异步改造walkthrough/12-1-server-init.ts/webhook端点与WebhookResponse类型walkthrough/12a-server.tsawaitingHumanResponse()判定逻辑src/agent.tsAgent 循环在人类工具处的暂停行为src/agent.ts线程状态存储可替换为 Redis 等src/state.tsDetermineNextStep函数与 BAML 测试baml_src/agent.bamlBAML 测试用例如MathOperationPostClarification验证了澄清请求 → 人类回复 → 继续运算的完整语义可配合 Webhook 服务端实现理解整体行为。小结从轮询到事件驱动的范式转变本节把 HumanLayer SDK 从同步轮询切换到异步创建联系 Webhook 恢复直接落地的 Factor 6 三要素LaunchPOST /thread一触即发返回{ status: processing }PauseAgent 遇到request_more_information/done_for_now/divide时createHumanContact发出请求后立即结束处理不占住任何进程ResumeHumanLayer 云将人类回复以 Webhook 推送到/webhook通过state.thread_id精确路由回线程agentLoop无缝继续。相比第 10、11 章的轮询实现见 10-human-approval 与 11-humanlayer-approval这种模式让服务器从等待人类的阻塞中解放出来为生产环境的并发、超时与水平扩展扫清了障碍。仓库中还保留了更多进阶形态供参考例如将/webhook细化为/webhook/response并同时处理V1Beta2EmailEventReceived、V1Beta2FunctionCallCompleted等事件类型见 walkthrough/12b-server.ts感兴趣可在搭建好本节基础后继续探索。赞分享文档教程人工智能大模型AI Agent【免费下载链接】12-factor-agentsWhat are the principles we can use to build LLM-powered software that is actually good enough to put in the hands of production customers?项目地址https://gitcode.com/GitHub_Trending/12/12-factor-agents点击查看免费下载相关推荐12-Factor Agents 第六因子用简单 API 实现 Agent 的启动、暂停与恢复Launch/Pause/Resume12 Factor Agents 第六因子用简单 API 实现 Agent 的启动、暂停与恢复Launch/Pause/Resume 导读 12 Fac文档教程人工智能大模型AI Agent基于 Ascend C SIMT 与 SHMEM 的分布式哈希表容器指南distributed_embedding 算子原理、构建与双卡验证基于 Ascend C SIMT 与 SHMEM 的分布式哈希表容器指南distributed_embedding 算子原理、构建与双卡验证 导读 distr文档教程人工智能大模型AI Agent12-Factor Agents实时通信WebSocket和长轮询的实现12 Factor Agents实时通信WebSocket和长轮询的实现 概述为什么实时通信对AI Agent至关重要 在构建生产级LLM应用时实时通信能文档教程人工智能大模型AI Agent上一篇chartist性能瓶颈解决方案从算法到架构的全面优化下一篇Emscripten集成测试并行化加速测试套件执行创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考