DeepSeek Harness session_search 超时:中文搜不到与一个坏会话拖垮全库

故障排查发布于 2026-10-17作者: DeepSeek Plugin 插件市场
DeepSeek Harnesssession_searchSESSION_QUERY_ABORTEDFTS5CJK 分词会话索引SQLite
DeepSeek Harness 的 session_search 一律 30 秒整超时、中文子串搜不到、新会话一写入就让全部历史索引失效,三者同源于一个 SQLite 检索后端:内存 FTS 冷建、unicode61 不切汉字、历史语料 revision 随任一写入整体变更。

DeepSeek Harness(DSH)的会话检索看起来是一个「工具」,实际背后是一个可选的 SQLite 检索后端;它坏起来会同时给出三种完全不像同一件事的症状:session_search 一律恰好 30 秒超时(换关键词无效、重启无效、日志干净);中文子串搜不到(搜「刑事」找不到含「请分析这个刑事案件」的会话);升级之后侧边栏已经恢复,但 session_search 依旧 30 秒超时。 这三种表现分别对应三个机制:索引是内存态冷建的(path: ':memory:' + 单事务末尾提交,工具超时的中止信号一到就整次回滚),FTS5 默认分词器不切汉字(unicode61 把连续汉字当整 token),以及历史语料 revision 随任一写入整体变更(一个当前会话写入就让全部历史被重读重写)。前两条修起来很直接(落盘 + 调超时、换分词器重建索引),第三条要把迁移与建索引一次性做完(#9695、#9064、#8783)。

先纠一个常见前提:这套检索是可选的实验性 bundle(dsh-experimental-session-search / optional-session-query-sqlite 一类),不是所有安装的默认行为。看到 30 秒超时时,先确认你确实启用了它。

DeepSeek Harness 现象一:session_search 一律 30 秒整超时

一句话结论:这不是「语料太大所以慢」,而是「每次搜索都在从零冷建索引、而每次都建不完」——一个必然失败的结构,与查什么词无关。 症状特征很干净(#9695):

  1. 报错文本固定为 tool call timed out after 30000ms,时间总是 30 秒整;
  2. 换任何查询词都一样——因为失败发生在建索引阶段,还没到匹配阶段;
  3. 重启无效、err.log 干净——中止是设计内的信号,不产生崩溃栈。

DeepSeek Harness 机制:内存 FTS + 单事务 + 中止即回滚

据报告者与社区核对源码,链路如下(#9695):

  1. 该检索后端被配成 path: ':memory:',即索引只活在内存里;
  2. 每次搜索都要把整个语料冷建进内存 FTS,并且构建过程包在一个事务里、在末尾才提交;
  3. 工具的 30 秒预算一到就 abort,中止信号直达后端,返回 SESSION_QUERY_ABORTED 并回滚;
  4. 回滚之后索引等于不存在,下一次搜索又从零开始——只要一次建不完,就永远建不完。

DeepSeek Harness 编号修复:两处同改,缺一不可

只改一处没用:只调大超时会让每次搜索都慢到 30 秒以上;只落盘而不调超时,首次冷建仍可能被 30 秒掐断。步骤如下:

  1. 找到该检索插件的配置,把索引路径从内存改成磁盘上的持久文件:
yaml
# 在该插件(session-query-sqlite 类)的配置行里
path: "C:/Users/<你>/.dsh/storages/session-query/index.db"
#   原来是 ':memory:'
  1. 同时把搜索超时调大,给首次冷建留够时间:
yaml
searchTimeoutMs: 120000
#   默认与工具预算同为 30000 量级,首次建索引时不够用
  1. 重启 DSH,让配置生效。
  2. 验证:在会话里让 agent 调一次 session_search,预期第一次耗时较长(在冷建索引)、之后明显变快;再跑第二次同样查询,预期稳定在秒级而不是 30 秒整。
  3. 观察日志里是否还出现 SESSION_QUERY_ABORTED;不再出现即修复成功(#9695)。

有一份实测记录值得参考:有用户在后续版本上完成「落盘 + 调大超时」两处修改并复现修复,说明这条路是通的;关键是两处同改(#9695)。

DeepSeek Harness 现象二:中文(CJK)子串搜不到

一句话结论:FTS5 的默认分词器把连续汉字当成一个 token,所以「刑事」这种子串永远匹配不到「请分析这个刑事案件」。 这是分词问题,不是索引损坏(#9064)。

DeepSeek Harness 三步确认与修复

  1. 做一次对照:用同一段文本里的完整词去搜(如完整的四个字),预期能命中;再用其中的两字子串去搜,预期命中不了。这个差异是分词问题的指纹(#9064)。
  2. 换分词器:把 FTS5 的分词从 unicode61 换成基于 trigram 的分词(或 ICU 分词)。trigram 会把文本切成三字符窗口,从而支持中文子串匹配;讨论中提到社区提供了面向中文的检索后端 dsh-session-query-sqlite-cjk 可直接替换使用(#9064)。
  3. 重建索引:分词器换了必须重建,否则旧索引仍是按旧分词建的——这一步是最容易漏的。重建时建议先落盘(见上一节的 path 配置),避免重建过程又被 30 秒掐断。
  4. 验证:重跑第 1 步的两字子串查询,预期这次能命中;再确认原有的完整词查询仍然正常。

讨论中也给了不换分词器的临时办法(插入保留非字符 / 自定义分隔符后重建一次索引),但它属于「改数据形状」的绕过,不如换分词器干净(#9064)。

DeepSeek Harness 附带一条:一个不可读会话会让整次搜索失败

同一份讨论里还有第二层机制:读取器遇到不可读会话时不是跳过它,而是让整次查询失败。 报错是 SESSION_QUERY_PERSISTENCE_FAILED(而不是「搜索未启用」那类码),表现为「搜索功能整体坏掉」,实际原因是「库里有一条会话读不出来」(#9064)。

处置步骤:

  1. 先做区分,不要直接怀疑检索插件:用离线会话检查工具逐个看会话的健康状态,把不可读的那条(或那几条)找出来。
  2. 隔离或修好它,然后重试搜索;预期搜索恢复。
  3. 长期诉求:上游应当把不可读会话显式跳过并在结果里报告,而不是让一次查询整体失败——这也是讨论中提出的修复方向(#9064)。

DeepSeek Harness 现象三:新会话一写入,全部历史索引就失效

一句话结论:历史语料的 revision 被算成了「整个 store 的哈希」,所以哪怕历史成员一个没变,只要有任何一个当前会话写入,全部历史 revision 就会同时变化,检索于是每次都重读重写全部 legacy 会话。 这解释了「升级后侧边栏恢复了、但 session_search 依旧 30 秒超时」这个矛盾(#8783)。

DeepSeek Harness 编号恢复:迁移 + 一次性建索引 + 持久 path

  1. 确认是这一条:症状是侧边栏正常、session_search 每次 30 秒整超时,且报 SESSION_QUERY_PERSISTENCE_FAILED;同时你在近期写入过任何会话(几乎必然成立)(#8783)。
  2. 完成历史会话的 v4 迁移(这一步是「一次性」的,别在迁移未完成的状态下反复搜索)。
  3. 一次性建好索引,并给它配持久的落盘 path(同现象一的配置);这可以避免「每次搜索都在重建」的循环。
  4. 验证:建好之后连做两次搜索——第一次允许较慢,第二次预期秒级返回;再写入一个新会话后重试,预期仍然秒级(如果又变慢,说明 revision churn 这一层仍在起作用,需等核心侧修复)(#8783)。
  5. 可复现的特征值得记下来:同一进程内对历史语料后缀取哈希是一致的,而跨进程不一致——这是判断「revision 计算口径错了」的旁证(#8783)。

DSH插件 排查注意事项

三条都落在同一个检索后端上,排查顺序建议是:先确认装没装、再看超时形状、再看搜索词语言、最后才怀疑数据。 八条要点:

  1. 先确认这是可选的实验 bundle,不是所有安装都有;别在没启用的环境里找问题(#9695)。
  2. 「30 秒整」是关键指纹:任何「恰好等于工具预算」的超时,都优先怀疑「一次做不完、每次重来」的结构,而不是性能不足(#9695)。
  3. path 与 searchTimeoutMs 必须同改,只改一个基本没用(#9695)。
  4. 换分词器必须重建索引,否则改了等于没改(#9064)。
  5. 中文搜不到别当索引坏了:先用「完整词能中、子串不中」的对照确认是分词问题(#9064)。
  6. 一个坏会话会拖垮整库搜索:看到 SESSION_QUERY_PERSISTENCE_FAILED 先从会话健康度查起(#9064)。
  7. 侧边栏好 ≠ 索引好:这两条路径的修复进度不同步,别用侧边栏正常来排除检索问题(#8783)。
  8. 修复状态如实:三条在讨论所指版本里都未修;落盘、调超时、换分词器、隔离坏会话都是用户侧处置,核心侧的 revision 计算口径与「不可读会话跳过」仍未改。

排查这类「工具莫名超时」的问题,很容易先怀疑插件冲突。如果你想先把插件变量排掉,与其在各处手敲命令,不如用 DSH Plugin Hub 这类可视化插件市场——已安装列表、装卸历史与系统日志都在一个面板里,确认「这套检索 bundle 到底装没装、最近动过什么」会快很多。

已安装插件列表

来源:Discussion #9695、Discussion #9064、Discussion #8783。

常见问题

DeepSeek Harness 的 session_search 每次都刚好 30 秒超时、换关键词也没用,是什么原因?

DeepSeek Harness 的 session_search 每次都 30 秒超时,是因为索引在内存态冷建、每次搜索都从零开始。这个后端被配成 path: ':memory:',检索时把整个会话语料冷建进内存 FTS 并在单事务末尾才提交,而工具的 30 秒预算一到就 abort 到后端,触发 SESSION_QUERY_ABORTED 回滚,于是下次又从头开始,形成必然死锁。修法是把 path 落到磁盘并同时调大 searchTimeoutMs,两处都要改,首次冷建成功后之后都是增量。

DeepSeek Harness 的 session_search 为什么搜中文搜不到,比如搜「刑事」找不到对应会话?

DeepSeek Harness 搜中文搜不到,是因为 FTS5 默认使用 unicode61 分词器,它把连续的汉字当成一个整 token,所以子串匹配不到。可行的做法是换成基于 trigram 的分词或 ICU 分词器,并按 trigram 重建一次索引;讨论中提到社区提供了面向中文的检索后端可作为替代。注意分词器换掉后需要重建索引,否则旧索引仍然是按旧分词建的。

DeepSeek Harness 只要开着一个坏会话,所有 session_search 都报 SESSION_QUERY_PERSISTENCE_FAILED,为什么?

DeepSeek Harness 的这个报错,是因为读取器在遇到不可读会话时会直接失败整次查询,而不是跳过它。先做区分:用离线会话检查工具逐个看健康状态,把不可读的那条找出来;如果确实存在,就隔离或修好它,搜索随即恢复。这也是为什么这类故障看起来像「搜索坏了」而实际是「库里有一条会话读不出来」。

DeepSeek Harness 升级到 0.2.1-alpha.2 后侧边栏恢复了,但 session_search 还是 30 秒超时,是没修好吗?

DeepSeek Harness 升级后仍 30 秒超时,是另一条机制:历史语料 revision 随任一写入整体变更。据社区逐行核对源码,迁移所需的历史会话 revision 被并进了整个 store 的哈希,所以任何一个当前会话写入都会让全部历史 revision 同时变化,导致每次搜索都重读重写全部 legacy 会话。恢复路径是完成官方 v4 迁移、一次性建好索引,并给该检索插件配一个持久的落盘 path。

相关术语

session_search
session_search 是 DeepSeek Harness 提供的会话检索工具,让 agent 在历史会话里做全文搜索。它由一个可选的 SQLite 检索后端(session-query-sqlite)支撑,属于实验性 bundle,不是所有安装都默认启用。— https://github.com/deepseek-ai/deepseek-harness/discussions/9695
SESSION_QUERY_ABORTED
SESSION_QUERY_ABORTED 是会话检索后端在收到中止信号后回滚当前事务并返回的错误码。当检索把全部语料放在一个事务里冷建索引时,工具超时触发的中止会让整次构建作废,形成「每次搜索都从零开始」的循环。— https://github.com/deepseek-ai/deepseek-harness/discussions/9695
FTS5 unicode61 分词
unicode61 是 SQLite FTS5 的默认分词器,按 Unicode 规则切分单词边界。它对拉丁文本有效,但把连续的汉字视为单个 token,因此「刑事」这类子串查询无法命中「请分析这个刑事案件」。— https://github.com/deepseek-ai/deepseek-harness/discussions/9064
historicalCorpusRevision
historicalCorpusRevision 是会话检索后端用来判断历史语料是否变化的一个 revision 值,理想上它应只反映历史会话的成员与内容变化。当前实现把整个 store 的哈希并了进来,导致任一当前会话写入都会让全部历史 revision 一起变化。— https://github.com/deepseek-ai/deepseek-harness/discussions/8783

来源