首页 / DeepSeek Harness 入门教程 / 事件系统:三大扩展点

DeepSeek Harness 入门教程

事件系统:三大扩展点

本教程共 32 篇 · 第 12 篇 · 更新于 2026-08-15 · 约 6 分钟阅读

事件扩展点agent/*tools/*session/event

本节目标:分清三大事件域(会话 / Agent / 能力),学会发布与订阅,知道什么场景该选哪个域。

在 dsh 里,事件就是扩展点。想观察 Agent 在干什么?监听 agent/*。想给文件写操作加策略?监听 fs/*。选对事件域,是大多数改动的第一个决定。官方文档把事件分成三大域,各管一摊。

三大事件域

事件域代表事件特性什么时候用
会话事件turn/startstep/startuser/messageassistant/*tool/calltool/result追加进日志并广播,持久事实事实必须在重新加载后仍然存在
Agent 事件agent/pre-stepagent/requestagent/statusagent/turn-stopping携带活跃 Agent,实时状态与控制观察或拦截进行中的工作
能力事件tools/*fs/*llm/streamtelemetry/*向 seam 附加策略与适配器给能力缝挂策略,无需导入循环

可以想成三种沟通方式:会话事件是写进档案的纪要,Agent 事件是对讲机里的实时喊话,能力事件是贴在设备上的操作标签。

会话事件:持久事实

会话事件是持久事实:先追加进仅追加日志,再通过 session/event 广播。turn/*step/*user/messageassistant/*tool/* 都属于这一域。判断标准一句话:这个事实在重启后还必须存在吗?要,就用会话事件。

它们的消费方很多:持久化、遥测、会话标题、UI 渲染、SDK 回放都监听 session/event。会话事件不承载「实时控制」语义——它是账本,不是指挥棒。

新手最容易犯的错:该用会话事件记的事实,用 emit 广播完就完事——重启后什么都没留下。

Agent 事件:实时生命周期

Agent 事件(agent/*)携带活跃的 Agent,用于观察或拦截进行中的工作:

事件模式用途
agent/created / agent/disposedemitAgent 生灭
agent/statusemit状态变化
agent/inbox/inserted / claimed / discardedemit收件箱事件
agent/pre-stepwaterfall决定模型看到什么(可改写、可拒绝)
agent/requestwaterfall替换模型调用配置
agent/request-errorwaterfall请求错误处理(重试等)
agent/turn-stoppingserial轮次停止前拦截(无 next)

拦截和策略优先用事件;直接能力调用优先用服务方法——这是官方实践规则。想让某轮停止,监听 agent/turn-stopping;想改模型看到的上下文,在 agent/pre-step 上动手。

能力事件:给缝挂策略

能力事件无需导入循环即可向某个 seam 附加策略和适配器。典型例子:

  • fs/*fs/write-intentfs/edit-intent(waterfall,写前策略)、fs/observed(emit,写后观察)
  • tools/*tools/pre-executetools/executetools/post-execute(waterfall 执行管线)、tools/result(emit 结果)、tools/change(emit 注册表变化)
  • telemetry/*session-telemetry/record(waterfall)
  • 其他缝:llm/stream(waterfall,流式输出)、approval/request(waterfall,审批策略可代替用户作答)、system-prompt/assemble(waterfall)、credentials/updatedsettings/updatedskills/change

tools/pre-execute 是给工具执行加守门逻辑的入口;fs/write-intent 是文件写策略的家。它们与能力缝一一对应,结构上天然解耦。

发布与订阅

订阅用 ctx.on,发布用对应分发方法:

// 订阅(属于 effect,插件卸载时自动移除)
ctx.on('tools/result', (exec, result) => {
  console.log(`[tool-logger] ${exec.name}`)
})

// 发布(emit:同步广播,不等待、不收集返回值)
ctx.emit('stats/report', name, next)

分发模式是事件契约的一部分:emit(广播)、parallel(并行等待)、serial(按序、第一个非 null/false/undefined 值胜出)、bail(serial 的同步版本)、waterfall(环绕中间件)。每个事件有且只有一种模式,且只能通过对应方法分发——官方事件参考(subsystems/ 页面)会标注每个事件的模式。

waterfall 有一条纪律:只负责观察或标注的监听器必须调用 next(),不调用直接返回代表有意短路。日志监听器忘了 next(),会悄悄吞掉所有下游默认行为。

人话版:waterfall 是一条链,每个监听器都握着下一环。不调 next(),就是故意把链子剪断。

事件名用 namespace/action 约定保持扁平命名空间可读。类型通过声明合并注册:

declare module '@deepseek-ai/cordis' {
  interface Events {
    'stats/report'(name: string, count: number): void
  }
}

监听方用 import type {} from '...' 引入声明,即获得完整类型。

怎么查谁发谁听

官方文档有一张事件生产方与消费方矩阵(event-producer-consumer):每个 harness 事件列出模式、声明位置、派发方与监听方。例如 tools/resulttools 派发,hooks-claude-codeagent-instructions 等监听。想知道「这个事件动了会影响谁」,查矩阵;想知道「这个包发了哪些事件」,查它的子系统页面。

Tip

三个问题帮你在三大域里选型:重启后还要不要(会话事件);要不要实时拦在途工作(Agent 事件);是不是给某项能力缝挂策略(能力事件)。三者边界清晰,极少需要跨域。

Warning

版本基线 @deepseek-ai/dsh 0.1.0-rc.6 处于 Developer Preview,事件清单与模式标注会随版本变化,以官方 event-producer-consumer 矩阵与子系统页面为准。

小结

  • 事件就是扩展点,三大域:会话事件(持久)、Agent 事件(实时)、能力事件(挂策略)。
  • 订阅用 ctx.on,发布按模式选 emit / parallel / serial / bail / waterfall。
  • waterfall 监听器必须调 next(),否则悄悄吞掉下游行为。
  • 事件名用 namespace/action 约定,类型靠声明合并注册。
  • 选域三问:重启后还要不要?要不要拦在途工作?是不是给能力缝挂策略?