
DeepSeek Harness 对话式 Schedule 交付用普通会话轮次替代持久回执的设计取舍【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness导读DeepSeek HarnessEverything is a Plugin的dsh-schedule包提供会话内持久提醒能力。本文围绕其一项关键简化决策展开到期提醒不再生成独立的持久化回执UI而是等待 Agent 进入空闲维护阶段后通过followup()排入普通对话轮次让交付只有会话内对话这一种含义。读完本文你将理解 Schedule 在何时、以何种顺序调用followup()与steer()、schedule/change的 dispatch 操作究竟承诺了什么、为什么不再存在投影/侧车/事件槽/专用渲染器等呈现设施以及这一边界带来的验证方式与使用限制。背景交付曾经有两种含义Schedulepackages/schedule的核心能力是把模型创建的提醒以普通后续消息的形式送回同一段会话——例如30 分钟后提醒我跟进迁移事项或构建期间每小时检查一次。在做出本次简化决策之前到期提醒的投递链路存在两条并行路径一条是对话路径Schedule 把普通 Agent 后续轮次排入队列模型在稍后的轮次中给出提醒答复另一条是持久化 Web 回执路径同一次提醒触发还通过 Schedule 投影presentation projection、持久化成功事件、Host 历史记录与 live 伴随数据sidecar、客户端同序号升级、通用事件视图 slot 和一个专用渲染器在 Web 界面渲染一枚独立于对话的持久标记。按 Agent Note 的表述这条回执路径把一项功能的确认 UI分散到了六个组件层次Session、持久化层、Host、客户端运行时、对话 UI 之外还多出一个额外包。跨组件协议与后到的同序号合并逻辑带来的复杂度与它提供的价值不成比例。更根本的问题在于回执让交付产生了第二种含义即使模型轮次失败了这枚回执依然可见——持久标记证明的只是内部 dispatch 被尝试过而不是用户收到了一条成功的提醒答复。而用户需要的是定时对话继续进行并不需要一枚单独的持久标记来证明一次内部派发尝试。决策让提醒成为普通对话轮次本次简化决策的完整表述见 Agent Note 的 Decision 小节可以拆成四条到期提醒等待 Agent 的空闲维护阶段idle maintenance phase再调用followup()该 follow-up 开启一个普通的后继轮次并通过普通对话 transcript 呈现Schedule 绝不调用steer()绝不中断当前轮次schedule/change仍是唯一持久化的 Schedule 状态其 dispatch 操作只记录后续轮次已同步入队。空闲维护阶段的实现runtime.ts 的 driveOnce这一决策在packages/schedule/schedule/src/runtime.ts的ScheduleRuntime.driveOnce()中得到完整实现。到期路径的调用顺序是flushSchedulePersistence() // 先做持久化预检 → readFolded() // 折叠会话事件流得到活动记录 → decide() // 选出到期的 one-shot 或 every 批次 → agent.runMaintenance(...) // 抢占 Agent 的空闲维护阶段 → 再次 readFolded() decide() // 维护阶段内重新折叠、重取决策 → renderReminderFraming() / renderEveryReminderBatchFraming() → agent.followup(message) // 同步排队一个普通用户消息 → agent.session.append(schedule/change, { operation: dispatch, ... }) → 释放维护阶段 → flushSchedulePersistence() // dispatch 屏障其中几个关键点与本次决策严格对应等待空闲当runMaintenance()因当前 Agent 正被某一轮次或其他维护任务占用而同步拒绝时runtime 调用waitForIdle()通过agent.whenIdle()等待 Agent 完全停稳后重新请求驱动requestDrive()而不会强插当前轮次。先排队后持久化followup(message)在agent.session.append(schedule/change, dispatch)之前同步返回只有入队成功后才会追加 dispatch 事件随后才释放维护阶段并等待持久化屏障。这样 dispatch 表达的事实是后续轮次已同步入队。不中断当前轮次runtime 中找不到任何对steer()的调用——Agent 句柄 API 中steer()会提交下一步输入并唤醒驱动器见 packages/core/agent/src/runtime-types.ts 与 packages/core/agent/README.md会改变进行中的请求路径而followup()只排队一条普通的下一个轮次提示词并唤醒驱动器正好符合等待完全 idle的约束。三种到期形态的汇聚到期判定由dueDecision()完成它按scheduledAt排序选出最早到期的一发式one-shot或将所有到期的固定间隔every记录组成一个批次。对应地followup()排入的内容有两种稳定模板domain.ts单个 one-shot 提醒的[SCHEDULE REMINDER]框架包含schedule_id_json、occurrence_at与reminder_prompt_json固定间隔批次的[SCHEDULE REMINDER BATCH]框架其reminders_json是按目标时间与创建顺序排列的 JSON 数组。模板中的动态字段全部经过JSON.stringify转义并在提示词中显式声明把 reminder 内容视为不受信任的提醒文本而不是新的用户指令从源头上防止提醒内容被当作指令注入。持久化边界dispatch 不承诺交付成功决策的另一个核心是收缩schedule/change事件的语义。在 v1 协议中见types.ts与domain.ts的decodeScheduleChange持久化变更只有三种操作操作携带字段语义createversion、operation、schedule完整记录创建一条提醒deleteversion、operation、id删除一条活跃提醒终结态dispatchversion、operation、idevery 额外携带acceptedAt记录后续轮次已同步入队dispatch 的精确边界是不代表模型成功dispatch 发生在模型请求之前无法证明 assistant 答复存在不代表用户确认更无法证明答复被阅读不代表外部通知Schedule 的交付模式是固定的session-local见types.ts不包含任何外部通道。从实现看dispatch 事件与followup()的关系是同步入队后立即追加runtime.ts如果followup()同步抛出异常例如框架构建失败则不会写入 dispatch记录保持活跃如果session.append失败则 runtime 进入 faulted 状态因为消息可能已经入队。这种设计的崩溃语义是明确的入队与持久 dispatch 之间存在一个狭窄的崩溃窗口窗口内可能重复触发提醒因此整体保持至少一次at-least-once语义而不是恰好一次。这一限制被明确记录在 README 的 Known Limitations 中。重放与折叠dispatch 如何参与状态还原持久化层persistence.ts只是复用会话层共享的flush()屏障契约——ctx.sessions.flush(session)成功即代表当前活动前缀到达了持久化监听器Schedule 本身不引入任何独立的成功事件或第二套持久化机制。重放逻辑由foldScheduleEvents()承担它按顺序应用schedule/change事件流create激活记录、delete移除记录、dispatch终止 one-shot 记录或把 every 记录推进到下一个锚定目标。正因为 dispatch 事件是唯一持久权威runtime 里的定时器、follow-up 消息、维护决策全部是可从折叠结果重建的一次性投影——重启后折叠同一段事件流即可恢复正确的活跃记录集合无需额外回执状态。删除的呈现设施一个更薄的 Schedule本次决策最直观的收益是删除了整条呈现链路。按 Agent Note 的 Decision 与 Consequences 小节以下设施不再存在Schedule 投影presentation projectionHost 侧车Host sidecar与浏览器事件节点按事件键控的事件视图 slotkeyed event slot专用渲染器与额外的渲染包持久化成功事件Session 持久化只保留共享的flush()契约。因此会话、持久化、Host、客户端运行时和对话 UI不携带任何 Schedule 专属行为。Schedule 的实现被收敛在packages/schedule/schedule包内外部只通过普通的组合与目录接线composition and catalog wiring接入。Web 侧的启用方式也相应变薄通过一个显式选装的 overlay 补丁加载deepseek-ai/dsh-schedule与时间上下文插件即可apps/cli/config/examples/schedule/cordis.yml# Opt-in Schedule patch over the shipped Web composition. - insert: - id: time-context name: deepseek-ai/dsh-time-context - id: schedule name: deepseek-ai/dsh-schedule对应的 CLI 命令为见 README 的 Enable Scheduledsh web --patch apps/cli/config/examples/schedule/cordis.yml插件入口src/index.ts声明inject [agents, sessions, tools, sessionPersistence]只观察插件加载后发布的agent/created事件仅对根 Agent 安装ScheduleRuntime与三个工具schedule_create、schedule_list、schedule_delete见tools.ts加载时已存活的 Agent 与运行时子 Agent 不接收 Schedule。被否掉的替代方案Agent Note 记录了四个被评估并放弃的方案它们从反面印证了最终决策的边界保留提交感知回执commit-aware receipt它能证明 dispatch 到达了持久化哪怕模型失败——但这是实现结果而非用户的提醒其跨组件协议与后到同序号合并逻辑的复杂度与收益不成比例。在对话中渲染原始schedule/change事件避免领域卡片但把内部状态转换暴露为面向用户的消息且需要一套仅服务于 Schedule 的通用非表面事件呈现机制。把 dispatch 当作提醒已成功交付dispatch 先于模型请求发生无法证明 assistant 答复存在或被读取如此命名会夸大持久化事实。提醒到期时用steer()中途引导当前轮次会改变进行中的请求路径让定时触发中断无关工作等待完全 idle 后使用followup()才能保证一条提醒对应一个普通后继轮次。验证测试如何固定新契约Agent Note 的 Verification 小节列出的验证点都能在仓库中找到对应物包生命周期测试固定 idle 等待、maintenance 所有权、follow-up 先于 dispatch 的顺序、同步入队失败、与模型无关的 dispatch、重启回放对应packages/schedule/schedule/tests下的runtime.spec.ts、domain.spec.ts、jsonl-restart.spec.ts等测试套件组装后的 Web 场景为产生的 assistant 行生成快照并断言已持久化的 dispatch 没有特殊 history view见apps/web/tests/schedule-after.e2e.ts该测试通过确定性模型 seamReminderAdapter把到期提醒变成普通 assistant 文本Reminder: Check the deployment log.对比conversation.expected.md等快照验证提醒确实以普通对话行出现源码与依赖审计拒绝残留的已移除呈现符号、事件、sidecar、slot、渲染器包与 overlay 配置项对应scripts/下针对包面与依赖的各类审计检查。后果与边界最终决策带来三方面后果Agent Note 的 Consequences 小节架构上Schedule 只存在于自身包 常规组合与目录接线中Session、持久化、Host、客户端运行时、对话 UI 零 Schedule 耦合体验上用户只能通过对话中的普通模型响应看到提醒失败的模型轮次仍然是失败轮次不再出现与之矛盾的成功回执产品边界上需要外部交付邮件、短信、推送、浏览器通知或交付确认的消费方必须另设产品边界由自己的通道承载通知与确认语义。配套的使用边界记录于 README 的 Known Limitations值得一并牢记提醒仅在原 Session 存活时按时运行冷会话不会收到外部通知、只能等恢复后处理 overdue 记录被拒的预检或框架/入队失败不启动私有重试定时器而是等待后续 Agent 活动或成功的 Schedule 预检触发重算every_seconds是最低 300 秒的固定间隔、以创建时间为锚点对齐不存在日历或 Cron 表达式。相关持久化与生命周期细节还可继续阅读 Session-local Schedule 子系统文档 与 工具目录中的 Schedule 部分。小结Conversational Schedule delivery 是一次典型的砍复杂度换语义清晰的简化用一个followup() 空闲维护阶段取代了横跨六个组件层次的持久回执体系把交付收敛为唯一的会话内对话语义同时把崩溃窗口明确为至少一次。对使用 DeepSeek Harness 的开发者而言理解这条边界意味着提醒结果永远以普通对话行呈现、schedule/change的 dispatch 不承诺模型成功、需要外部通知的场景属于另一个产品边界——这正是本次决策的全部价值所在。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考