Skip to content

1.2 事件系统与四种派发模式

本章概览:DSH 中"事件就是扩展点"。本章讲清 Cordis 事件的声明方式、四种派发模式(emit / waterfall / parallel / serial)的语义与适用场景,以及 DSH 的三个事件域(会话事件、Agent 事件、能力事件)。

事件即扩展点

在 DSH 中,"事件就是扩展点,而选对事件域是大多数改动的第一个决定"(docs/architecture.md)。事件通过 TypeScript 声明合并(declaration merging)注册到类型系统,因此事件名、负载类型与派发模式都是编译期可检查的公共契约

四种派发模式

Cordis 的每个事件有且只有一种派发模式,且只能由对应方法派发:

模式是否等待派发顺序有返回值?典型用途
emit按注册顺序观察通知(如 agent/createdsession/event
waterfall否(不整体等待)按注册顺序观察中间件式包裹(如 tools/execute
parallel所有监听器并行观察并行扇出(如 session/flush 的持久化)
serial按注册顺序观察有序决策链(如 agent/turn-stopping

NOTE

waterfall 的关键语义:监听器收到 (...args, next),必须调用 next() 把(可能被包裹的)结果委托给下一个监听器;不调用 next() 直接返回即短路。值通过 next() 的返回值传播。协作型监听器通常改写共享的请求/决策对象后委托;策略型监听器(如允许/拒绝决策)通过短路来"拥有"决策。prepend: true 仅在监听器必须早于普通注册运行时使用。

出处:docs/cordis-primer.mddeepseek-harness-src/docs/cordis-primer.md

DSH 的三个事件域

选择事件域是"把新行为挂到哪"的第一个决定:

事件域载体用途例子
会话事件(session events)追加到日志、经 session/event 广播的持久事实重新加载后必须仍然存在的事实turn/*step/*user/messageassistant/*tool/*
Agent 事件agent/*携带活跃 Agent 的实时事件观察或拦截进行中的工作agent/pre-stepagent/requestagent/createdagent/turn-stopping
能力事件(capability events)seam 上的事件无需导入循环即可附加策略/适配器fs/*tools/*telemetry/*

出处:docs/architecture.mddocs/event-producer-consumer.md(事件生产方/消费方矩阵)

事件契约的工程规范

DSH 对事件有一整套仓库级规范(AGENTS.md):

  • 类型化事件使用声明合并,并采用可合并扩展的 map(SessionEventMapToolJobKindMap 等);
  • 事件 JSDoc 必须带 @mode 标注派发模式,生成目录会校验声明与派发点一致;
  • waterfall 监听器必须调用 next(),否则短路;
  • 会话事件是 SessionEventMap 成员,默认"读即必需"——构建时不知道其类型就拒绝写入日志(除非携带 ignorable: true);
  • 监听器异常必须被派发器包含(contained):一个坏订阅者不能破坏核心生命周期或饿死后续监听器(见 defensive patterns)。

实战视角:三个 waterfall 拦截点

在 DSH 的 agent loop 中,有四个关键 waterfall 事件,是插件最常见的拦截点:

事件干什么
agent/pre-step决定模型看到什么:改写已领取消息或拒绝
agent/request拦截/改写一次模型请求
llm/stream包裹每次流式模型调用(缓存、日志、路由)
tools/execute包裹工具规范化派发(超时、重试、指标)

出处:docs/architecture.mdpackages/core/tools/README.md

小结

事件系统是 DSH 的"神经系统":持久会话事件记录事实,agent/* 实时事件协调进行中的工作,能力事件为 seam 附加策略。理解四种派发模式,尤其是 waterfall 的"短路 vs 委托"语义,是读懂 DSH 流水线(2.3 工具执行流水线)的前提。

参考资料

本文档为学习用途的原创讲解,基于 MIT 许可的开源项目 deepseek-ai/deepseek-harness 编写;所有引用均注明出处。