ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DeepSeek Harness 事件分发 API 完全指南:context 中的 parallel / emit / serial / bail / waterfall

DeepSeek Harness 事件分发 API 完全指南:context 中的 parallel / emit / serial / bail / waterfall 人工智能AI AgentAgent 框架DeepSeek【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址https://gitcode.com/gh_mirrors/de/deepseek-harness点击查看免费下载事件系统是 DeepSeek Harness“一切皆插件”架构的神经中枢。每一个由 Cordis 构建的 context 都天然混入了事件分发 APIevent-dispatch API插件通过ctx.on()注册监听器、通过ctx.parallel()、ctx.emit()、ctx.serial()、ctx.bail()、ctx.waterfall()五种不同策略分发事件从而实现解耦的扩展与拦截。本文以 docs/cordis-api/events.md 为骨架结合其底层实现 vendor/cordis/src/events.ts 与 Harness 各子系统如 docs/subsystems/core.md中真实的事件声明系统讲解事件分发的五种模式、监听器生命周期、EventOptions过滤语义以及 Harness 中agent/*、internal/*等事件的真实用法。读完本文你将能准确判断“何时用哪种分发模式”并写出符合 Harness 扩展点约定的监听器代码。事件分发 API 的定位每个 context 的内置能力在 DeepSeek Harness 中事件分发 API 是混入mixin进每个 context的能力——不需要额外引入服务任何拿到ctx的地方都可以直接调用ctx.on()、ctx.emit()等方法。这一点在 docs/cordis-api/events.md 开头便明确该 API 混合到每一个上下文中而 Harness 各子系统声明的事件及分发模式会由 scripts/gen-cordis-catalog.ts 自动生成到各自所属的子系统页面生成命令为pnpm run gen-cordis-catalog对应中文侧文件为 docs/subsystems/core.zh.md。从源码结构看这套 API 的实际载体是一个事件总线服务安装为ctx.events同时以类型声明扩展Context接口见 vendor/cordis/src/events.ts// vendor/cordis/src/events.ts 中 declare module ./context.ts 的简化 parallel(name, ...args): Promisevoid emit(name, ...args): void serial(name, ...args): PromisifyReturnTypeEvents[K] bail(name, ...args): ReturnTypeEvents[K] waterfall(name, ...args): ReturnTypeEvents[K] on(name, listener, options?): () boolean once(name, listener, options?): () boolean注意每个方法都有一个带thisArg的重载如parallel(thisArg, name, ...args)显式传入this供监听器使用同时也作为 context 过滤的依据。所有事件名称与参数类型都由泛型K extends keyof Events约束保证编译期类型安全。DispatchMode五种事件分发策略EventOptions 与 DispatchMode 一节给出了核心概念——DispatchMode是事件服务使用的事件分发策略定义于 vendor/cordis/src/events.tstype DispatchMode emit | parallel | serial | bail | waterfall五种策略的语义依据原文档与源码注释模式语义是否等待异步提前终止返回值emit同步运行监听器不等待它们返回的 Promise否无忽略voidparallel并发运行所有监听器一起等待全部完成是无Promisevoid任一拒绝则抛AggregateErrorserial依次等待每个监听器直到其中一个“提前终止分发”是是异步可感知第一个 bail 值bail同步依次调用监听器直到第一个返回 bail 值否是仅同步第一个 bail 值waterfall围绕最终next回调组合监听器洋葱模型取决于监听器是不调用next()即否决最外层监听器返回值其中“bail 值bail value”有明确判定规则非null、非false、非undefined的值即为提前终止信号。该判定由 vendor/cordis/src/events.ts 中的isBailed()实现export function isBailed(value: any) { return value ! null value ! false value ! undefined }这意味着监听器返回true、0、、任意对象等都会触发 bail而返回null/false/undefined表示“继续传递”。五种分发方法逐个拆解ctx.parallel(name, ...args)并发分发parallelK extends keyof Events(name: K, ...args: ParametersEvents[K]): Promisevoid并行分发事件并发运行所有监听器返回的 Promise 在所有监听器都 settle 后兑现。若任一监听器拒绝parallel会抛出由所有拒绝原因组成的AggregateError实现见 vendor/cordis/src/events.tsasync parallel(...args: any[]) { const results await Promise.allSettled(this.dispatch(emit, args).map(async cb cb(...args))) const errors results.filter((result): result is PromiseRejectedResult result.status rejected) if (errors.length) throw new AggregateError(errors.map(error error.reason)) }参数说明name事件名称。args传递给每个监听器的参数。返回值一个 Promise在所有监听器均已完成后兑现rejected 时聚合所有错误。典型用途多个监听器彼此独立、都需要被执行且都需要等待结果例如日志上报、状态广播等“扇出”场景。ctx.emit(name, ...args)同步分发、忽略返回值emitK extends keyof Events(name: K, ...args: ParametersEvents[K]): void同步分发事件忽略监听器的返回值。实现上直接遍历监听器并调用、不处理返回的 Promisevendor/cordis/src/events.tsemit(...args: any[]) { this.dispatch(emit, args).map(cb cb(...args)) }参数说明name事件名称。args传递给每个监听器的参数。返回值无void。这是开销最低的“通知式”分发适合发出信号、触发副作用且不关心结果、不需要等待异步完成的场景。若监听器返回了 Promise它也不会被await注意由此产生的未处理拒绝。ctx.serial(name, ...args)串行分发、等待直到 bailserialK extends keyof Events(name: K, ...args: ParametersEvents[K]): PromisifyReturnTypeEvents[K]依次等待各监听器直到其中一个提前终止分发。与bail的区别在于serial会await每个监听器的返回值再判定vendor/cordis/src/events.tsasync serial(...args: any[]) { for (const cb of this.dispatch(serial, args)) { const result await cb(...args) if (isBailed(result)) return result } }参数说明name事件名称。args传递给每个监听器的参数。返回值第一个提前终止值非null、非false且非undefined若没有监听器 bail则解析为undefined。典型场景按注册顺序协商出一个结果且监听器可能是异步的。Harness 中agent/turn-stopping就采用serial模式见 docs/subsystems/core.md。ctx.bail(name, ...args)同步分发、遇到首个 bail 值停止bailK extends keyof Events(name: K, ...args: ParametersEvents[K]): ReturnTypeEvents[K]依次调用各监听器直到其中一个提前终止分发。与serial的区别是不做异步等待——同步调用每个监听器一旦返回非null/false/undefined的值就立即返回vendor/cordis/src/events.tsbail(...args: any[]) { for (const cb of this.dispatch(bail, args)) { const result cb(...args) if (isBailed(result)) return result } }参数说明name事件名称。args传递给每个监听器的参数。返回值第一个提前终止值非null、非false且非undefined。典型场景internal/listener就使用 bail 模式——监听器注册时若某个 bail 监听器返回非空结果则直接取代默认的注册逻辑见下文“内部事件”一节。ctx.waterfall(name, ...args)洋葱模型组合、next续接链waterfallK extends keyof Events(name: K, ...args: ParametersEvents[K]): ReturnTypeEvents[K]分发一个最后一个参数是next续接回调的事件。这是 Harness 中最重要的扩展模式每个监听器都会包装调用链的其余部分——调用next()会执行下一个监听器最终执行内置行为不调用next()则会否决veto后续执行包括内置行为。实现如下vendor/cordis/src/events.tswaterfall(...args: any[]) { const cbs this.dispatch(waterfall, args) const inner args.pop() // 最后一个参数是最内层 next const next () { const cb cbs.shift() ?? inner // 依次弹出监听器耗尽后落到内置行为 return cb(...args) } args.push(next) return next() // 从最外层监听器开始执行 }参数说明name事件名称。args监听器参数最后一个参数是最内层的next即内置行为。返回值最外层监听器的返回值。执行顺序是“由外向内”的洋葱结构最先注册/最靠前的监听器最外层它先拿到next每个监听器可以选择在调用next()之前/之后做前处理与后处理也可以直接返回、不调用next()从而否决整条链。waterfall 天然适合拦截-改写语义Harness 大量扩展点都采用它例如agent/pre-stepwaterfall拒绝提议的 step 或替换进入该 step 的消息调用next()保留当前消息docs/subsystems/core.mdagent/requestwaterfall替换冻结的模型调用配置await next()得到机器将使用的配置返回替换值来切换docs/subsystems/core.md工具注册表中对tools/ptc-dispatch-log的分发见 packages/core/tools/src/index.ts监听器可以改写工具分发的日志内容失败时回退到原始内容。监听器注册on/once与 fiber 所有权ctx.on(name, listener, options?)onK extends keyof Events(name: K, listener: Events[K], options?: boolean | EventOptions): () boolean注册一个归当前 fiber 所有的事件监听器name要监听的事件名称。listener使用分发参数调用。options监听器选项布尔值可作为prepend的简写true等价于{ prepend: true }。返回值一个用于移除监听器的资源释放函数disposer调用时若监听器仍处于注册状态则返回true否则返回undefined。关键特性是fiber 所有权与自动清理监听器作为 effect 注册在当前 fiber 上fiber 卸载unload时会自动移除所有监听器避免插件热重载或停用后留下悬挂回调。从实现看vendor/cordis/src/events.tsregister()通过ctx.fiber.effect()注册 effecteffect 清理时调用unregister()若 fiber 已销毁on()会抛出CordisError(INACTIVE_EFFECT)。此外监听器在注册时会经过ctx.reflect.bind(listener)绑定并通过internal/listener事件进行拦截校验见下文。ctx.once(name, listener, options?)onceK extends keyof Events(name: K, listener: Events[K], options?: boolean | EventOptions): () boolean与on()相同但监听器在首次调用后自行注销——最多被调用一次。实现上once()内部先通过on()注册一个包装函数包装函数第一件事就是调用 disposer 完成自移除然后再转发给原始监听器vendor/cordis/src/events.ts。适合一次性监听例如等待某个初始化事件、某个异步任务完成通知。EventOptionsprepend 与 globalctx.on()和ctx.once()接受的选项接口定义如下vendor/cordis/src/events.tsinterface EventOptions { /** 把监听器加到同一事件的现有监听器之前。 */ prepend?: boolean /** 无论 context 过滤检查结果如何都接收事件。 */ global?: boolean }prepend控制监听器在队列中的位置。默认push到末尾后注册的后执行为true时unshift到头部先执行。由于serial/bail/waterfall的执行顺序敏感prepend常用于“必须最先/最外层介入”的拦截器。global绕过 context 过滤。事件总线在分发时会根据thisArg携带的 context 过滤器Context.filter筛选监听器只调用与当前 context 匹配的监听器global: true的监听器无视过滤、总是被调用。这在 vendor/cordis/src/events.ts 的dispatch()中体现dispatch(type: string, args: any[]) { const thisArg typeof args[0] object || typeof args[0] function ? args.shift() : null const name: string args.shift() if (!name.startsWith(internal/)) { this.emit(internal/dispatch, type, name, args, thisArg) } const filter thisArg?.[Context.filter] return (this._hooks[name] || []) .filter(hook hook.global || !filter || filter.call(thisArg, hook.ctx)) .map(hook hook.callback.bind(thisArg)) }从这段实现还可以读出两个细节一是internal/*事件不触发internal/dispatch诊断避免递归二是thisArg为对象或函数时会被识别为显式this同时也用于过滤。内置事件Events接口与internal/*生命周期钩子除了各子系统声明的事件事件总线的核心还定义了一批内置框架事件全部声明在 vendor/cordis/src/events.ts 的Events接口中事件分发模式语义internal/pluginemit插件 fiber 被创建或销毁时 uid 被清除internal/statusemitfiber 生命周期状态变化接收 fiber 与旧状态internal/configwaterfall在 fiber 的 injections 生效后解析原始插件配置internal/serviceemit服务绑定时的拦截钩子无核心生产者internal/updatewaterfallfiber 配置更新正在应用跳过next()可否决internal/getwaterfall通过 context 代理读取服务时触发internal/setwaterfall通过 context 代理写入服务时触发internal/listenerbail监听器注册时触发非空返回值取代注册逻辑internal/dispatchemit事件分发到监听器之前触发仅非 internal 事件其中两个值得注意internal/listener是bail模式on()在注册前会先bail(internal/listener, ...)若返回非空结果则直接用该结果取代默认注册流程vendor/cordis/src/events.ts。事件服务自身正是用它把internal/update监听器存储到 fiber 专用列表里。internal/update是waterfall模式事件服务在其构造函数中注册了一个{ global: true, prepend: true }的监听器来按顺序驱动配置更新回调链vendor/cordis/src/events.ts。在 DeepSeek Harness 中的实战运用事件系统的完整图景是API 由 Cordis 核心提供本页事件声明按子系统分布在各自的子系统页面。因此在 Harness 中写监听器通常不需要发明新事件而是“挂接”既有扩展点。以下是从 docs/subsystems/core.md 摘取的典型示例agent 生命周期emitagent/created、agent/disposed、agent/error、agent/status、agent/session-start——用于观测 Agent 生命周期实现状态面板、监控、清理等。agent 流水线waterfallagent/pre-step改写进入 step 的消息、agent/request替换模型调用配置、agent/request-error改写错误处理——这是对 Agent 循环做“横切”扩展的主要通道。agent 决策serialagent/turn-stopping——串行询问各监听器是否应在无工具续接时终止回合。事件分类的全局视图见 docs/architecture.md 的“Events”部分各子系统声明的完整事件清单含参数类型与分发模式见 docs/subsystems/core.md、docs/subsystems/tools.md 等子系统页面。这些页面由 scripts/gen-cordis-catalog.ts 从源码声明自动生成确保文档与实现不脱节。结语按语义选择分发模式你的需求选择通知类副作用不关心结果ctx.emit多个独立监听器并发执行并等待全部完成ctx.parallel依次执行、允许异步、按顺序取第一个非空结果ctx.serial同步依次执行、取第一个非空结果ctx.bail拦截-改写、洋葱模型、可否决内置行为ctx.waterfall注册一次性监听器ctx.once配合ctx.on掌握了五种分发模式与EventOptions的prepend/global语义再对照 docs/cordis-api/events.md 中的类型签名与各子系统页面的mode标注你就能写出与 Harness 事件契约完全一致、可被自动清理、可被 scope 过滤的插件代码。赞分享人工智能AI AgentAgent 框架DeepSeek【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址https://gitcode.com/gh_mirrors/de/deepseek-harness点击查看免费下载相关推荐DeepSeek Harness 事件面精简实录移除 agent/steering 镜像 emit 的决策、实现与验证DeepSeek Harness 事件面精简实录移除 agent/steering 镜像 emit 的决策、实现与验证 本文以 DeepSeek Harnes人工智能AI AgentAgent 框架DeepSeeklowcode-engine 事件 APIevent完全指南on / prependListener / off / emit 与 setter 联动实战lowcode engine 事件 APIevent完全指南on / prependListener / off / emit 与 setter 联动实战前端低代码DeepSeek Harness 插件事件系统实战Cordis 事件的声明、分发模式与瀑布流拦截DeepSeek Harness 插件事件系统实战Cordis 事件的声明、分发模式与瀑布流拦截 事件是 DeepSeek Harness 插件体系Cord人工智能AI AgentAgent 框架DeepSeek上一篇RevokeMsgPatcher深度解析Windows平台二进制补丁技术实战指南下一篇HyperFrames v0.7.17 深度解读hyperframes/lint 浏览器端零依赖校验入口与 hyperframes/parsers 依赖收敛创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表