通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。
- 实际检查更改后的产品行为
- 收集截图或其他验证证据
- 编写验证计划和报告
说明如何在智能体执行步骤、工具调用、人工审批、上下文压缩和子智能体调用时注册钩子。钩子可观察事件、模拟本地工具结果,也可通过 Webhook 按配置策略允许或拒绝工具调用。
Use for agent lifecycle hooks, tool mocks, intervention, sub-agent calls and context compression.
实测评分 未实测
查看安装方法这份指南介绍如何为智能体的执行生命周期注册钩子,并处理不同阶段的事件。可观察的事件包括步骤开始与完成、工具调用及错误、人工审批、上下文压缩、子智能体调用和最终运行状态。本地处理函数可以模拟工具结果;Webhook 可以发送事件通知,也可以在配置了决策处理时允许或拒绝工具调用。指南还说明事件上下文中包含的运行 ID、工具信息和结果,以及 Webhook 的请求内容与投递错误行为。
如果要监控智能体运行过程或在运行中介入,可以参考这份指南。它也适用于测试时替换工具结果,以及追踪包含人工审批和子智能体的处理流程。
配置和使用 Webhook 需要目标 URL。若请求头使用环境变量,必须指定允许列表;秘密值只在发送时展开,不会在保存配置时展开。仅通过 Webhook 配置注册的钩子在进程重启后无法恢复函数处理程序。
本介绍由 AI(OpenAI Codex)根据公开的 SKILL.md 撰写。准确的规格请查看下方原文。
AI 根据 SKILL.md 的内容检查了需要注意的操作和使用前提。评分只依据实际运行的结果(实测对比)。
不计入评分。 由 AI(OpenAI Codex)于 2026年10月26日 评估。仅供参考,安装前请同时查看原文。
安装前可以查看交给 AI 代理的指令内容。
Register lifecycle hooks through execAgent({ hooks }). HookDispatcher stores them per operation and dispatches them in registration order.
16 hook types across 5 categories:
execAgent({ hooks })
│
├─ beforeStep ──────────── Before each step executes
│ │
│ ├─ [call_llm] LLM inference
│ │
│ ├─ [call_tool]
│ │ ├─ beforeToolCall ── Before tool executes (supports mocking)
│ │ ├─ (tool execution)
│ │ ├─ afterToolCall ─── After tool completes (optional result control)
│ │ └─ onToolCallError ─ Tool threw an exception
│ │
│ ├─ [request_human_approve]
│ │ ├─ beforeHumanIntervention ── Before agent pauses
│ │ ├─ afterHumanIntervention ─── After approve/reject + resume
│ │ └─ onStopByHumanIntervention ── User rejected, agent halted
│ │
│ ├─ [compress_context]
│ │ ├─ beforeCompact ──── Before compression starts
│ │ ├─ afterCompact ───── After compression completes
│ │ └─ onCompactError ─── Compression failed
│ │
│ ├─ [callAgent] (via execSubAgentTask)
│ │ ├─ beforeCallAgent ── Before sub-agent starts
│ │ ├─ afterCallAgent ─── After creation/start returns
│ │ └─ onCallAgentError ── Sub-agent failed
│ │
│ └─ afterStep ──────────── After step completes
│
├─ (next step...)
│
├─ onComplete ───────────── Operation reaches terminal state
└─ onError ──────────────── Error during execution
| File | Role |
|---|---|
packages/agent-runtime/src/types/hooks.ts | Event types and required/optional fields |
apps/server/src/services/agentRuntime/hooks/types.ts | Registration and webhook types |
apps/server/src/services/agentRuntime/hooks/HookDispatcher.ts | Registration, dispatch, local mocks, HTTP/QStash delivery |
apps/server/src/services/agentRuntime/hooks/webhookPayload.ts | Event projection and email enrichment |
apps/server/src/modules/AgentRuntime/adapters/toolCallHookContext.ts | Shared tool-event context builder |
apps/server/src/modules/AgentRuntime/adapters/ServerToolTransport.ts | Tool notifications and execution results |
apps/server/src/modules/AgentRuntime/RuntimeExecutors.ts | Tool, compression and human-intervention execution |
apps/server/src/services/agentRuntime/AgentRuntimeService/ (executeStep/, intervention.ts) | Step events and human-intervention continuation |
apps/server/src/services/agentRuntime/CompletionLifecycle/index.ts | Terminal events |
apps/server/src/services/aiAgent/subAgentRuns.ts | Sub-agent events |
const hooks: AgentHook[] = [
{
id: 'observe-step',
type: 'afterStep',
handler: async (event) => {
console.log(event.operationId, event.stepIndex);
},
},
];
await aiAgentService.execAgent({ agentId, prompt, hooks });
Completion cleans up registrations through hookDispatcher.unregister(operationId).
Webhook-only hooks deliver in both local and queue modes. For hooks with both handler and webhook, local dispatch calls the handler; queue dispatch uses the serialized webhook. Only webhook configuration is persisted, so functions are unavailable after a process restart.
const hook: AgentHook = {
id: 'tool-notification',
type: 'afterToolCall',
matcher: '^fs/readFile$',
webhook: {
url: 'https://example.com/hooks',
delivery: 'fetch',
timeout: 5, // seconds; default 30
headers: { Authorization: 'Bearer ${HOOK_TOKEN}' },
allowedEnvVars: ['HOOK_TOKEN'],
},
};
matcher is a regex against ${identifier}/${apiName}, supported only on tool events. Omitted, empty, or * matches all tools.allowedEnvVars; persist templates, never resolved secrets.beforeToolCall with responseHandling: 'toolCall' awaits an optional HTTP decision before tool execution. Return HTTP 2xx JSON with { "decision": "allow" } or { "decision": "deny", "reason": "禁止执行该操作" } to intervene. The type is { decision: 'allow' } | { decision: 'deny'; reason?: string }; a deny with an omitted or empty reason uses Blocked by beforeToolCall hook.. Denials preserve the reason in the tool result/card with classification hook_denied.afterToolCall also accepts responseHandling: 'toolCall'. It synchronously checks the full request/result before archival and model consumption; deny replaces the original result with the receiver's optional reason in both content and blocked state. A deny with an omitted or empty reason uses Blocked by afterToolCall hook., following the same Blocked by ${type} hook. rule as beforeToolCall. The reason enters model context and the tool card, as with beforeToolCall. It does not undo tool side effects, retries, or charges. See the result-control contract for deferred completions and runtime boundaries.ignore (default notifications) and toolCall (optional decisions for both before and after). Only subscribed events are enabled. Replace removed toolResult/toolCallAndResult values with toolCall; use the events list to select after-only checking. Existing after responses with a valid decision now control results.hookSpecificOutput) are notification responses, even with onError: 'block'. Continue checking subsequent hooks; do not treat a notification as permission to skip later policies. Explicit invalid decisions, non-2xx status, oversized bodies, invalid UTF-8, network failures, and timeouts follow onError: 'continue' | 'block' (default continue); blocking uses hook_control_error as the tool-result content and reason for both before and after hooks. Cancellation never grants permission.decision and a deny's optional string reason are consumed; updatedInput/additionalContext do not change tool arguments or conversation context.packages/types/src/agentHook.ts; response parsing: packages/types/src/agentHookResponse.ts; HTTP delivery: apps/server/src/services/agentRuntime/hooks/httpWebhook.ts.AGENT_HOOK_WEBHOOK_*) always come from the executing worker's current environment, including after human approval and deferred completion. Never persist their configuration or restore a previous environment's hooks.execAgent({ hooks })) retain the existing per-operation serialization and persistence. Recover these caller hooks from the runtime snapshot or durable operation record; preserve environment-variable templates without expanding secrets into storage.onError.toolResultControl pending/allowed/blocked metadata or hide raw output through history projections.MessageModel.resolveHumanApproval defaults to claim-only; only the runtime calls it with publishResult: true after result control. Stop/rejection receipts can publish directly because they contain no executed tool output.type: 'blocked' is not a hook verdict.| Event | Timing and payload |
|---|---|
beforeStep | Before a step; AgentHookEvent |
afterStep | After a step; content, tool calls/results and usage totals |
onComplete | Terminal state; reason such as done, error, interrupted, max_steps or cost_limit |
onError | Operation error; errorMessage, errorDetail and available error metadata |
beforeToolCall | Before tool execution; shared tool context and local mock() callback |
afterToolCall | After tool execution; structured result and mocked |
onToolCallError | Tool execution throws; shared tool context and error |
beforeHumanIntervention | Before approval; pendingTools, operationId, stepIndex |
afterHumanIntervention | Approval decision and continuation; action, optional toolCallId and rejectionReason |
onStopByHumanIntervention | Human rejection stops the run; optional toolCallId and rejectionReason |
beforeCompact | Before compression; messageCount, tokenCount, stepIndex |
afterCompact | After compression; groupId, messagesBefore, messagesAfter, summary |
onCompactError | Compression error; error, tokenCount, stepIndex |
beforeCallAgent | Before sub-agent creation; agentId, instruction |
afterCallAgent | Creation/start returns; agentId, subOperationId, success, optional threadId |
onCallAgentError | Sub-agent call fails; agentId, error |
CallAgent events dispatch on the parent operation identified by parentOperationId. An isolated child supplies threadId; shared group members use the shared conversation. Child completion is reported by the child's onComplete event.
Use buildToolCallHookContext() for the three tool events.
toolCallId, assistantMessageId, identifier, apiName, effective args, callIndex, stepIndex, operationId and executor.userId uses runtime.userId ?? origin.userId.toolSource identifies the tool's origin; executor identifies the server/client dispatch route. executionTarget and activeDeviceId describe the run's execution plan and device selection.afterToolCall.result carries content, success, optional executionTime and error/state data such as state.type: 'blocked'. mocked marks results supplied by a local hook.Sandbox and MCP tools use the same event path. Filter with identifier, apiName or toolSource.
dispatchBeforeToolCall() exposes event.mock(result). The first accepted mock wins and short-circuits the remaining mock handlers. The method returns { isMocked: true, result } for a mock, or null to continue execution.
event.mock({ content: '{"items":[]}', success: true });
Tool observation payloads use BeforeToolCallObservationEvent; the callback belongs to the in-memory handler event. dispatchBeforeToolCall() does not deliver webhooks.
createWebhookPayloadBuilder() selects eventFields, adds hookId/hookType, then merges webhook.body. The body determines the final value of overlapping fields. Tool-result notification payloads use redactResultForEvents() to trim raw skill Work data; controls receive the full result. Work registration retains the full in-process result. finalState is available to local handlers; serialized payloads carry the event's data fields.
When email is selected, the builder resolves userEmail from the final effective userId, replacing supplied email values. Email-only projections use the event ID. An explicit invalid ID, missing email or lookup error leaves email omitted.
Email queries share a per-dispatcher cache of up to 1,000 users for five minutes. Query timeouts belong to the database layer. The builder's optional fourth { signal } argument lets a waiter cancel independently; cancellation returns undefined so the caller can stop delivery. Fetch and QStash use the enriched payload.
Dispatch awaits each handler or webhook. Ordinary errors are logged and dispatch continues. Webhooks with fallback: 'none' accumulate a CriticalHookDeliveryError, which is thrown after sibling hooks have run. QStash delivery defaults to a fetch fallback; use fallback: 'none' for QStash-signed endpoints.
还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。
本技能指导在 LobeHub 中接入 AI 模型提供商,涵盖运行时、模型信息、设置、品牌展示、文档和验收,并帮助判断新增模型是否需要新的提供商集成。
本技能指导如何用服务器端环境变量设置用户偏好默认值,涵盖变量校验、服务器配置、写入用户状态,以及更新 `.env.example` 和自托管文档。
此技能用于为智能体设计事件驱动的后台工作流,划分来源、信号、动作和策略的职责,并处理异步交接、去重、执行范围、完成记录与运行观测。
本技能在 macOS 桌面聊天应用中测试机器人频道:打开应用、进入频道或联系人、发送消息、等待回复并截图。支持 Discord、Slack、Telegram、微信、飞书和 QQ;iMessage 使用 BlueBubbles 桥接。
按操作编号检查智能体运行记录,查看失败的工具调用、参数与结果、运行上下文及传给大语言模型的内容;还可查看上下文映射,并用其他模型重放记录以便比较。