Flue useResponseStart/Finish 详解:拦截响应的每一刻
2026/8/31 20:10:48 网站建设 项目流程

Flue useResponseStart/Finish 详解:拦截响应的每一刻

【免费下载链接】flueThe sandbox agent framework.项目地址: https://gitcode.com/GitHub_Trending/flue1/flue

Flue这个轻量级 sandbox agent framework(沙箱智能体框架)中,useResponseStartuseResponseFinish是两枚埋点在响应生命周期两端的观测钩子:前者在响应的真正开始时精确触发一次,后者在响应的真正结束时精确触发一次。它们让你在毫秒级抓住 Agent 响应的每一次唤醒与收尾,把耗时、token 用量、应用标记等元数据直接"盖章"到响应消息上,供客户端在正文之外读取。

什么是响应生命周期钩子

Flue 的 Agent 是一个普通函数,函数体内通过一系列use*钩子声明能力:useModel选模型、useTool挂工具、useSkill挂载技能……而事件钩子则负责在生命周期的关键"接缝"上运行回调。

其中共有四个事件钩子,触发时机各不相同:

钩子触发时机可否异步
useResponseStart响应的真正开始,每个响应仅一次❌ 同步
useAgentStart每收到一条投递消息,运行一次✅ 可异步
useAgentFinish每个"本应停止"的点都会运行✅ 可异步
useResponseFinish响应的真正结束,每个响应仅一次❌ 同步

理解这对钩子的关键是"响应(response)"这个概念:一次响应可以吸收多条投递进来的消息(消息在轮次边界"加入"正在进行的响应)。此时useAgentStart会为每条消息重新触发,但响应本身只唤醒一次——useResponseStartuseResponseFinish只跟响应绑定,各触发一次,不多不少。

useResponseStart:在响应醒来的一瞬

它的执行时机极其靠前:在任何模型调用之前、在任何useAgentStart回调之前,同步地运行一次。

回调的签名很克制:接收一个上下文,返回一个普通对象或什么都不返回。

useResponseStart(({ metadata }) => ({ ...metadata, startedAt: Date.now() }));
  • 收到的ctx.metadata是本响应目前已累积的元数据(按声明顺序、来自更先的钩子);
  • 返回的对象会被深度合并(deep-merge)到响应消息的metadata字段上——这是 AI SDK 惯例中的信封字段,客户端可以在消息正文之外读取它;
  • 不返回任何东西则只观测、不附着,完全合法。

官方钩子实现就几行代码,可读性很高:use-response-start.ts,类型定义(ResponseStartContext等)在 message-output.ts。

useResponseFinish:在响应的真正结尾

useAgentFinish在每个"本应停止"的点运行,且可以追加信号让响应继续;而useResponseFinish运行在最后一轮收尾循环结束之后——此时响应真正落地,它拿到的response聚合数据是最终的

useResponseFinish(({ metadata, response }) => ({ finishedAt: Date.now(), elapsed: Date.now() - (metadata.startedAt as number), totalTokens: response.usage.totalTokens, toolCalls: response.toolCalls.length, }));

它的上下文比 start 侧多了一份response对象:

  • response.usage—— 整个响应跨所有轮次、所有重试的聚合 token 用量
  • response.toolCalls—— 该响应发起过的全部工具调用(来自持久化记录日志);
  • ctx.metadata—— 包含useResponseStart附着的内容(从持久化日志读取,因此重试后依然保留)。

实现见 use-response-finish.ts。

典型用法:给响应盖上"计量章"

把两个钩子组合起来,是最经典的模式——记录响应耗时与用量:

useResponseStart(() => ({ startedAt: Date.now() })); useResponseFinish(({ metadata, response }) => ({ elapsed: Date.now() - (metadata.startedAt as number), totalTokens: response.usage.totalTokens, }));

项目里就有现成的例子:评估场景中的服务状态 Agent 用useResponseFinish把用量与模型名写进元数据,供测试框架从回复上读取——见 service-status.ts 与 harness.ts。

在可观测性文档中,官方也推荐把useResponseFinish作为在响应元数据上盖 token 计量的正确位置:observability.md。

新手必知的 5 个行为细节 📌

  1. 同步且只观测:回调不能append、不能dispatch、拿不到 harness,也不能是异步——返回 Promise 会直接令提交失败。需要异步的启动工作请放进useAgentStart,异步收尾工作请放进useAgentFinish
  2. Fail-fast:回调抛异常会直接失败整个提交,没有重试、没有恢复。
  3. 元数据对模型不可见:写入的 metadata 永远不会进入模型提示词,也永远不会重新触发 Agent——纯粹是面向客户端的信封数据。
  4. 至少一次语义:钩子回调按 at-least-once 运行;中断后的重试会重跑,但持久化结果不会重复。
  5. 子 Agent 中不可用:在 subagent 渲染里调用这两个钩子会直接抛错——响应元数据只装饰面向客户端的公开对话。

延伸阅读

  • 钩子总览指南(含事件钩子完整示例):agent-hooks.md
  • API 参考(含useResponseStart()/useResponseFinish()的完整契约与合并规则):agent-hooks-api.md
  • 运行时钩子源码目录:packages/runtime/src/hooks/

从"唤醒"到"落地",useResponseStartuseResponseFinish用两行声明就帮你在响应的每一刻留痕——这正是 Flue 响应式钩子体系优雅的地方:像写组件一样写 Agent,观测与计量信手拈来。

【免费下载链接】flueThe sandbox agent framework.项目地址: https://gitcode.com/GitHub_Trending/flue1/flue

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询