前言¶
用 DSH 开发智能体,很快会碰到一个具体问题:会话之间是互相隔开的。新开一轮对话,agent 查不到上一次说过什么、调用过哪些工具。想自己写插件订阅 session/event 攒一份记录也不够——session/event 是”提交后追加”的事件流,恢复(resume)的会话不会重放 seed 历史事件,旧会话的内容拿不到。
dsh-session-index 补的就是这个缺口:把所有会话内容建成一份跨会话的全文索引,并暴露成模型可以直接调用的工具。下面介绍它的定位、工作方式和安装步骤。
这是什么¶
dsh-session-index 是 DeepSeek Harness 的会话全文索引插件,以组合包(dsh.bundle)形式分发,作者是 longyu065,当前版本 0.1.0,MIT 许可。
它监听 session/event 事件流,把会话内容抽取成可检索文档,构建跨会话倒排索引,再通过 session_search / session_index_stats 两个工具暴露给模型,让 agent 能检索之前任何一次对话里说过什么、做过什么。
索引的内容包括:user/message、assistant/message 的 text(reasoning 可选)、tool/call(工具名 + 参数)、tool/result、session/title。
核心功能¶
实时索引与历史回填¶
每个 session/event 追加立即入索引,按 sessionId#seq 幂等去重,回填与实时事件重叠时不会重复计数。
针对恢复会话不重放历史事件的缺口,插件启动时做两步回填:
- 先用
ctx.sessions.list()索引内存中的活动会话; - 再用
ctx.sessionPersistence.list()配合readFrom(id, watermark+1),按水位线增量补全落盘的旧会话。
水位线同时存在内存和持久化索引文件里,重启后只补增量。
混合检索¶
preferFts 开启(默认)时,FTS5 与内存索引并跑、按会话去重合并:
- 框架的
ctx.sessionQuery(SQLite FTS5)负责英文/词级召回; - 内置的二元组索引补足中文召回。FTS5 的
unicode61分词器不做中文切词,整串 CJK 是单个 token,”可以跨会话搜索历史了”匹配不到”跨会话搜索”,二元组召回正好互补。
engine 字段报告实际使用的检索引擎:memory / fts5 / hybrid。
富卡片¶
检索结果会投影成 Web UI 的 card:'search' 卡片:presentCall / presentResult 配合 output.presentationMeta,把命中按会话分组渲染成可展开列表,组头是会话 id 和标题,组内是命中片段。卡片数据随 tool/result 事件持久化,可回放。
持久化与清理¶
索引文档按 JSONL 追加到 $DSH_HOME/session-index/<sessionId>.jsonl,重启只补增量。会话销毁时(session/disposed),同步移除该会话的全部文档与索引文件。
隐私开关¶
两个配置项控制索引范围:
indexReasoning:默认关,不索引 CoT 思考文本;indexToolResults:默认开,控制是否索引工具返回结果。
安装与启用¶
源码在 GitHub 仓库(链接见文末),本地拿到仓库后分三步安装:
# ① 打包(在仓库根目录)
pnpm pack # 产出 dsh-session-index-0.1.0.tgz
# ② 装进 profile(web = 桌面端用的 profile)
dsh plugin --profile web add ./dsh-session-index-0.1.0.tgz
# ③ 重启桌面应用 / dsh web
为什么用 tarball 而不是 add ./目录:pnpm 对 link: 协议的本地目录包不会安装它的 dependencies(实测);pnpm pack 出的 tarball 是普通包,依赖会正常装进 profile 的 .pnpm 子树,bundle 内的 @deepseek-ai/* 导入才能解析。
组合包自带一份 cordis.patch.yml,做两件事:挂载插件本身;把框架自带的 FTS5(session-query-sqlite)从默认的 openAt: never 改成 openAt: first-search,持久化路径设为 $DSH_HOME/session-query.sqlite。这样 session_search 会自动进入混合检索。不想用这层覆盖,就在自己 profile 的 cordis.patch.yml 里再覆盖该行——patch 按层后写覆盖前写。
配置项¶
| 键 | 默认值 | 说明 |
|---|---|---|
dataDir |
''(自动 = $DSH_HOME/session-index) |
索引落盘目录,填自定义路径可改位置 |
maxResults |
20 |
session_search 默认最大命中数 |
maxSnippetChars |
240 |
摘要片段最大字符数(Unicode 码点) |
maxDocChars |
4000 |
单条文档索引的最大字符数,截断防工具结果膨胀 |
indexReasoning |
false |
是否索引 assistant 的 reasoning 思考文本 |
indexToolResults |
true |
是否索引工具返回结果 |
preferFts |
true |
是否优先用框架 FTS5 做混合检索,false 则纯内存索引 |
开发与测试¶
插件源码是单文件可擦除 TS(src/session-index.ts),Node 22.18+ 原生类型剥离即可加载。运行依赖 @deepseek-ai/cordis ^4.0.1、@deepseek-ai/dsh-tools ^0.1.0-rc.6、@deepseek-ai/schemastery ^3.18.1,随 tarball 正常装进 profile。
本地开发时,@deepseek-ai/* 依赖需要先做个符号链接:
mkdir -p node_modules && ln -sfn <dsh安装目录>/node_modules/@deepseek-ai node_modules/@deepseek-ai
<dsh安装目录> 通常是 ~/.npm/_npx/<hash>/,桌面端与 dsh web 共用同一份。tsc 类型检查还需要 @types/node。
测试和构建:
node test-index.mjs # 引擎独立测试,不启动 dsh,65 项断言
pnpm run build # tsc 编译 src → dist/ → index.js
适用场景与注意¶
适合的场景:希望 agent 能”回忆”之前任何一次对话内容的 DSH 用户——查上次的结论、翻之前的工具调用记录、跨会话延续上下文,都靠这份索引。
使用前注意几点已知限制:
- 索引目录应由单个 dsh 进程独占,多进程共享同一
dataDir未做并发保护; - 中文按二元组召回,单字查询(如”插”)只能命中孤立单字文档,建议至少输入两字;
- 框架 FTS5 不索引
session/title,kind='title'过滤走内存索引; - 卡片内暂无”跳转原会话”交互(框架 wire format 无 link/action 块),卡片携带会话 id 和标题,配合侧边栏定位;
session/disposed只清理插件自己的内存与 JSONL 文件,不影响原始会话日志。
另外,插件以当前 dsh 进程的权限运行。它的源码是单文件,安装前把 src/session-index.ts 过一遍、确认 MIT 许可符合预期,成本不高。
结尾¶
dsh-session-index 做的事不复杂:把所有会话内容建成一份可检索的索引,暴露成两个工具,agent 因此能查到之前任何一次对话里说过什么、做过什么。索引、回填、混合检索、卡片展示都在包内完成,MIT 许可,源码单文件可审。
- GitHub:https://github.com/longyu065/dsh-session-index
- 社区目录页:https://www.skillhub.cn/plugins/longyu065/dsh-session-index
社区目录是独立站点,与 DeepSeek / 幻方无官方从属关系,仅作插件索引使用。