两阶段会话题词:会话进行中即时关键词题名,空闲后再调用最经济的预算模型精修,并附带一句话摘要。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:JohnXu22786/session-titler
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本。请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
为 DeepSeek Harness(dsh)提供两阶段会话题词(自动命名):会话进行中先用关键词即时生成标题,空闲后再调用最经济的模型精修。全程后台运行,不打断主流程,不为标题多花一分冤枉钱。
用户发消息 ──► [阶段一] 关键词即时题词(零成本、毫秒级)
│
└─► 会话空闲 5 秒 ──► [阶段二] 预算模型精修 + 一句话摘要
│
└─► 写入 session/title(可追溯)
解决的问题
Harness 的新会话默认只有时间戳编号,长会话列表中难以分辨。已有方案要么只做「等空闲再生成」(用户等待期间没有标题),要么每轮都调用模型(成本高)。本插件把两个思路拆成互补的两段:
- 阶段一 · 即时题词:用户消息一落地,立即从最新消息提取关键词,标题几乎零延迟出现,不调用任何模型;
- 阶段二 · 空闲精修:会话安静下来后,用一次辅助调用把标题从「关键词串」升级为通顺的短语,并顺带产出一句话会话摘要(同一请求内完成,不额外计费);
- 成本护栏:精修默认只使用注册模型中最便宜的一档(
flash/haiku/mini等),可显式指定模型,也可完全关闭阶段二。
功能特性
- 两阶段流水线:即时关键词题词 → 空闲预算模型精修,标题随会话演进;
- 成本控制:预算路由按名称模式从已注册模型目录中挑选最便宜模型并缓存,模型拓扑变化自动失效;
- 原创关键词算法:噪声剥离(代码块/URL/Markdown)→ 脚本检测(拉丁/CJK)→ 停用词与虚词过滤 → 词序保持/字符预算截断,中英日韩均可用;
- 多语言:标题语言跟随消息语言,拉丁标题按词数、CJK 标题按字符数控制长度;
- 标题去重:同一标题不重复写入;跨会话重名时自动追加编号后缀(
Fix Login Bug (2)); - 摘要联动:精修时顺带生成一行会话摘要,以
session/caption-note事件写入会话日志,供列表 UI、导出工具等消费; - 尊重人工标题:用户手动改名后自动生成完全停止,绝不覆盖(包括精修调用进行中的改名,也不会被回写);
- 零配置可用:默认值即可运行,全部行为可调。
安装
在 DSH 中安装(从 GitHub)
从 GitHub 安装最新版本到 profile:
dsh plugin --profile demo add github:JohnXu22786/session-titler
移除:
dsh plugin --profile demo remove dsh-session-caption
本插件是一个标准 dsh bundle(配置层 + 插件代码),通过 dsh plugin 安装到 profile:
# 从本地目录安装(开发/自用)
dsh plugin --profile web add /path/to/dsh-session-caption
# 或打包后安装(tarball / git 引用同理)
npm pack
dsh plugin --profile web add ./dsh-session-caption-0.1.0.tgz
安装时 pnpm 会把包链入 profile 的 node_modules,dsh 识别 package.json 中的 dsh.bundle 声明,将 cordis.patch.yml 层加入 dsh.profile.bundles。重启后生效:
dsh --profile web --dump-config # 应能看到 "session-caption" 行
dsh web
加载原理(给 harness 开发者)
- Bundle 清单:
package.json中dsh.bundle.patch指向cordis.patch.yml——这是 bundle 唯一必需的元数据; - 配置层:
cordis.patch.yml先按行 id 停用内置的单阶段标题提供方(会话标题服务同一时刻只接受一个提供方),再插入本插件的配置行; - 入口文件:
lib/src/index.js导出标准 Cordis 插件契约——name(session-caption)、inject(['sessionTitle', 'sessions', 'llm'])、Config(schemastery 校验 schema)、apply(ctx, config); - 能力注册:
apply中把两阶段流程注册为ctx.sessionTitle的唯一提供方(automatic: 'all-user-messages'),同时监听session/event与llm/adapters-updated;所有监听器、定时器、注册项随插件卸载自动回收。
注意:
session-title服务是单提供方设计。若其他插件也注册了标题提供方,二者会互相取代;本插件的 bundle 层默认已停用内置的session-title-llm行。
配置
所有字段可选,默认值见下。全局配置写入 $DSH_HOME/cordis.patch.yml,按行 id 覆盖:
# $DSH_HOME/cordis.patch.yml(home 级,作用于所有 profile)
- id: session-caption
config:
instant:
enabled: true # 阶段一开关
prefix: '' # 即时题词前缀,如 '⚡ '
maxWords: 6 # 拉丁标题最大词数
maxCjkChars: 14 # CJK 标题最大字符数
refine:
enabled: true # 阶段二开关
maxWords: 5 # 精修标题目标词数(拉丁)
maxCjkChars: 10 # 精修标题目标字符数(CJK)
maxInputBytes: 4096 # 精修输入消息字节上限(JSON 框架后)
maxOutputTokens: 64 # 精修输出 token 上限
timeoutMs: 60000 # 精修单次调用超时
budget:
preferCheap: true # 只从低成本模型目录挑选
# patterns: [...] # 低成本模型名称模式(按性价比排序)
summary:
enabled: true # 摘要联动开关
maxChars: 120 # 摘要最大字符数
timing:
idleDelayMs: 5000 # 空闲判定延迟(精修触发点)
activityWindowMs: 1500 # 事件后的活跃窗口
modelCacheMs: 120000 # 预算路由缓存时长
model:
provider: '' # 显式精修路由(与 model 成对)
model: '' # 如 deepseek-official / deepseek-v4-flash
dedup:
enabled: true # 标题去重(同标题跳过 + 跨会话编号)
suffix: '({n})' # 编号后缀模板,必须含 {n} 占位符(从 2 起)
debug: false # 调试日志
关闭
instant.enabled后,阶段一不再产出关键词标题,但阶段二依然只在空闲窗口运行(忙碌期间的自动生成请求被跳过,标题等会话安静后由定时器驱动生成),不会变成「每条消息都调用模型」。
模型选择优先级(阶段二)
model.provider+model.model显式配置;budget.preferCheap开启时,扫描所有可配置提供方的模型目录,按budget.patterns中的模式名匹配并取最优(默认顺序:flash→haiku→lite→mini→nano→fast→ …,同档取名字最短者),结果缓存timing.modelCacheMs;- 会话自身的模型路由(
request.route); - 都不可用时跳过精修,保留即时题词。
接口
提供方(Provider)
注册于 ctx.sessionTitle,id 为 session-caption,自动模式 all-user-messages:
| 字段 | 值 | 说明 |
|---|---|---|
id |
session-caption |
写入 session/title 事件的来源标识 |
automatic |
all-user-messages |
每条新用户消息触发一次生成 |
generate(request) |
— | 活跃 → 即时题词;空闲 → 精修 |
generate 输入 { session, messages, route?, signal },输出 { title, messageSeqs, model? }。以下情况抛 CaptionSkippedError(服务端保留现有标题,不视为故障):
- 用户已手动改名(
source.kind === 'user'); - 即时阶段无可提取的关键词;
- 精修结果与当前标题相同(去重,同时标记会话为稳定,避免反复生成);
- 无可用的模型路由。
事件
| 事件 | 类型 | 说明 |
|---|---|---|
session/title |
log-only(Harness 自带) | 每次接受的标题快照,含来源与消息 seq |
session/caption-note |
log-only(本插件贡献) | 精修时的一句话摘要:{ title, note, messageSeqs } |
caption-note 与 title 一样不进入模型上下文;不识别该事件的回放器可安全跳过(信息性记录)。
目录结构
src/
├── index.ts # 插件入口:name / inject / Config / apply
├── config.ts # 配置 schema 与运行时校验
├── context.ts # 结构化 Harness 上下文类型
├── flow.ts # 两阶段编排(generate / 事件喂入 / 去重 / 摘要)
├── keywords.ts # 阶段一:关键词题词引擎
├── refine.ts # 阶段二:预算模型精修 + 摘要
├── budget.ts # 成本路由:最便宜模型选择与缓存
├── pacemaker.ts # 空闲节拍器:活动感知 + 精修定时
├── normalizer.ts # 标题清洗、限长、比较
├── language.ts # 拉丁/CJK 脚本检测
├── events.ts # 自定义事件声明
└── errors.ts # CaptionSkippedError:跳过的修订
开发
npm install # 开发依赖(含 dev/pkgs 下三个转发包,file: 引用,可重装)
npm run typecheck # tsc 类型检查
npm test # vitest 单元与流程测试(86 例)
npm run build # 编译到 lib/src/
运行时依赖说明:插件在运行期使用
@deepseek-ai/dsh-llm、@deepseek-ai/dsh-session、@deepseek-ai/dsh-session-title,它们由 dsh 安装本身提供,未在 manifest 中声明为 dependencies/peerDependencies——这三个包的 npm 传递依赖链目前不完整(一个传递包未发布),声明会导致安装失败;若装入不含这些包的自定义 profile,加载时会报ERR_MODULE_NOT_FOUND,把包装进 profile 的node_modules即可。本地开发镜像:
stubs/目录是这三个包的最小 API 镜像(与发布版 rc.1 的已消费成员逐一核对);dev/pkgs/下是三个薄转发包(file:依赖),供 tsc 解析与本地测试,不随插件发布(files只含lib/src、配置层与文档)。
许可
MIT — 见 LICENSE。
链接
同类插件
Anionex/dsh-turn-rewind★ 64
对话回退:基于持久 Change Ledger 回滚会话与工作区状态。
Nwflower/dsh-chat-import★ 46
把 13 家 coding agent(Claude Code、Codex、ChatGPT、Cursor、Gemini、opencode 等)的完整对话历史导入为可续聊的 DeepSeek Harness 会话,并支持反向导出回 Claude Code。
whyihaveyou/dsh-suite#plugin-session-export★ 38
把 append-only 会话日志导出为按轨迹来源分组的可读 Markdown 或 HTML。
Chinesezjc/dsh-interconnect★ 28
跨实例互联:经 interconnect 服务在多个 DSH 实例间转发消息与事件。
Moeblack/dsh-message-edit★ 24
基于分支的消息编辑、reroll、重试与版本时间线。
hellodigua/dsh-share★ 20
一键分享你的对话。