dsh-temporary-messages:会过期的上下文消息与可执行模板

前言

在 DSH 里跑 Agent 时,经常需要在发给模型的上下文末尾挂一条「状态栏」:当前时间、余额、上下文占用比例,或来自其他插件的一次性提醒。若把这些内容写进普通用户消息,会一直留在历史里,既占 token,又容易过时。纯静态 system 提示 likewise 无法在每个推理步自动刷新。

dsh-temporary-messages(维护者 kazari-qwp)针对 DSH 提供一条可配置寿命的临时消息通道:支持在设置页用 JavaScript 表达式写动态模板,也支持其他插件通过 Cordis 接口 push / remove 注入文字。消息到期或被替换时,可选择用可扩展的压缩器处理受影响的上下文片段,而不必改 DSH 核心源码。当前版本为 0.1.10(MIT),第一版面向 DSH 0.2.2-rc.1 与 Node.js 22+;README 标明仍为实验版本,日志元数据依赖 DSH 内部实验接口,恢复能力有兼容性说明(见下文「中断与恢复」)。

这是什么

插件分类为模型推理:它在每次准备推理步骤时,把临时快照追加到 session 末尾,并在 token 或步数达到上限后撤下或刷新。模板侧用 {{ ... }} 求值;接口侧传入的 text 不经模板执行。二者可并存:设置页留空模板时,其他插件仍可通过接口发送临时消息。

核心功能

动态模板

在 设置 → 临时消息 编辑模板,可「运行预览」后保存。默认模板仅显示当前时间。表达式为普通 JS,支持 await、字段访问与格式化;null / undefined 输出为空,对象输出为 JSON。

当前时间:{{ new Date().toLocaleString("zh-CN", { hour12: false }) }}
当前会话:{{ session?.id }}

模板内可调用已注册工具(经 DSH 工具策略,不为求值额外调模型):

剩余余额:{{ (await tool("get_balance", {})).balance.toFixed(2) }} 元

get_balance 仅为 README 示例,需宿主已注册对应工具;设置页会列出实际工具。也可用 fetch() 等 Node 能力,或通过 ctx.get('服务名') 读取 Cordis 服务。预览会真实执行表达式与工具,宜选查询类工具。

模板可用参数包括 ctx、agent、session、modelSelection、signal、tool(name, args)。完整余额与上下文占比示例见仓库 examples/balance-context.txt。

token 与步数寿命

设置页可并列配置 token 寿命 与 步数限制 k:

  • 只填一项则仅该项生效。
  • 两项都填时,任意一项先到上限即撤下旧快照、重新执行模板,两计数一并重置。
  • 两项都留空时按一次性处理;token 为 0 或 k 为 0 同样只使用一次。
  • 默认行为仍是每次推理更新。保存模板或寿命后,下一步立即开始新周期。

接口 push() 的参数与页面模板寿命相互独立(外部调用不继承页面模板寿命);详见下文接口表。

给其他插件的接口

注入 temporaryMessages(及按需 agents)后,可在会话上下文中排队消息;push() 只排队,等下一步推理,不主动唤醒空闲 Agent。

import type { Context } from '@deepseek-ai/cordis'
import type {} from 'dsh-temporary-messages'

export const inject = ['temporaryMessages', 'agents']

export function apply(ctx: Context) {
  ctx.on('agent/created', agent => {
    ctx.temporaryMessages.push(agent.session, {
      text: '你有一条来自 Alice 的新消息,请按需查看。',
      ttlTokens: 0,
    })
  })
}

带 key 时,同会话同 key 的新消息会替换尚未消费或仍有效的旧消息;remove(session, id) 在下一次准备步骤生效。

参数 含义
text 直接文字,接口消息不会当模板执行
ttlTokens: 0 / k: 0 仅用于一次推理
ttlTokens: N 送达后累计模型新生成 N 个 output token 过期
k: N 送达后累计 N 次 agent/pre-step 过期
同时传 ttlTokens 与 k 任一到期即删除
某项为 false 显式关闭该项限制
key 同 key 替换
compressor 单条消息专用压缩函数,省略则用设置页当前压缩器

正数 token 寿命累计服务商报告的 outputTokens;k 统计 agent/pre-step,含同轮工具循环。撤下采用 surface replacement:撤下的是模型上下文,原始日志仍保留。

压缩器

设置 → 临时消息 → 压缩器 从注册表选择并保存,下次清理生效。内置「保留原文」(默认)与「清空全部」(测试用)。临时消息到期、撤回或替换时,截取特定范围内的历史交给压缩器;可返回原消息数组筛选保留项,或返回摘要字符串写回。

其他插件可注册压缩器,无需改设置页类型:

export const inject = ['temporaryMessageCompressors']

export function apply(ctx: Context) {
  ctx.effect(() => ctx.temporaryMessageCompressors.register({
    id: 'my-plugin/keep-last',
    name: '保留最后一条',
    description: '仅保留传入范围中的最后一条消息。',
    compress: async messages => messages.slice(-1),
  }))
}

安装与启用

从 Releases 下载已构建的 .tgz,在对应 profile 下安装(示例为 web):

dsh plugin --profile web add ".\dsh-temporary-messages-0.1.10.tgz"

也可在 DSH 插件管理页选择该 .tgz 路径。其他 profile 将 web 换成实际名称。安装后刷新页面;未启用 HMR 的宿主需重启。再在设置中编辑模板、配置寿命并保存。

插件在宿主进程执行模板与预览,权限与当前 DSH 进程一致;安装前应阅读源码与 MIT 许可证,确认工具调用与 fetch 符合你的安全策略。

典型用法

  1. 在设置页粘贴 examples/balance-context.txt 中的两行模板,预览时选择会话,查看余额与上下文占比。
  2. 在自有插件里于 agent/created 或渠道回调中 push 一次性提醒(ttlTokens: 0)。
  3. 对预算类状态使用 key: 'my-plugin/budget' 与 ttlTokens,避免重复堆叠。
  4. 需要到期后压缩历史时,注册自定义 temporaryMessageCompressors 或在单次 push 传入 compressor。

适用场景与注意

适合: 需要在模型上下文末尾展示动态状态、又不想长期污染对话历史的 DSH 插件或自用部署;需要按 token/步数自动刷新或撤下的推理链路。

注意:

  • 与 DSH 0.2.2-rc.1 对齐;更早 DSH 或未满足 Node 22+ 的环境需自行验证。
  • 若其他插件同样重写 llm/stream 消息顺序,需协调执行次序。
  • 移除或移动临时消息会改变缓存前缀,成本取决于模型服务商。
  • README「中断与恢复」一节说明:元数据使用 @deepseek-ai/dsh-session 的 appendPluginRecord,当前限定为仓库内实验接口;0.2.2-rc.1 上运行与恢复已实测,但正式发布前仍有接口兼容性待解。0.1.8 及更早半次清理缺少写回计划,不会自动推测修复。

结尾

dsh-temporary-messages 把「会过期的上下文消息 + 可执行模板 + 可注册压缩器」收到一个插件里,便于在不动 DSH 核心的前提下扩展 Agent 上下文行为。目录与文档:

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜