第 30 讲:Session 查询与全文检索

DeepSeek Harness 源码解析 · 第 30 讲

DeepSeek Harness 源码解析系列 第 30 讲:Session 查询与全文检索 基于 DeepSeek Harness 源码 · 2026-09-13 💡 本讲一句话: Agent 跑了几百个 session,怎么让模型"搜到"之前干过什么?session-query 用一个 live-preferred corpus 把内存和磁盘统一成一份逻辑语料,再用 SQLite FTS5 双表(persisted + temp.live)做全文检索——关键是每次查询前做一次"稳定观察",保证索引不读到半截状态。 一、统一服务:一个 ctx.sessionQuery 管所有读操作 第 29 讲我们看了 session 怎么"写"——JSONL append、SQLite revision。这一讲看"读":精确读取、过滤、血缘追踪、全文检索,全部收敛到一个抽象服务 SessionQueryEngine 。它的设计哲学是:**后端无关的读操作写死在基类,只有全文检索留给子类实现**。 抽象引擎:具体行为 + 两个抽象方法 session-query/src/index.ts (第 81-127 行)定义了抽象类。注意 static inject = ['sessions'] ——它只依赖 session 注册表,不直接依赖持久化后端: 📄 packages/session-query/session-query/src/index.ts (第 81-127 行) export abstract class SessionQueryEngine extends Service { // 抽象基类:所有 session-query 实现的公共骨架 static inject = ['sessions'] // 只注入 sessions 注册表,不直接依赖持久化——后端是可选的 private readonly _readWindowMax: number // 事件窗口读取上限(默认 50),防止一次拉太多原始事件 private readonly _corpus: SessionCorpus // live-preferred corpus:内存优先、磁盘兜底的统一语料解析器 constructor(ctx: Context, config: Config = {}) { // 构造时校验配置并创建 corpus super(ctx, 'sessionQuery') // 注册服务名为 sessionQuery,挂载到 ctx.sessionQuery this._readWindowMax = config.readWindowMax ?? SESSION_QUERY_READ_WINDOW_MAX // 窗口上限取配置或默认值 50 if (!Number.isInteger(this._readWindowMax) || this._readWindowMax < 0) { // 非法配置直接抛错,fail loud throw new SessionQueryError( // 带稳定错误码的 typed error,调用方可按 code 路由 'session-query: readWindowMax must be a non-negative integer', 'SESSION_QUERY_INVALID_CONFIG', ) } const persistedInspectConcurrency = config.persistedInspectConcurrency // 批量读持久化日志时的并发度 ?? SESSION_QUERY_DEFAULT_PERSISTED_INSPECT_CONCURRENCY // 默认 4,避免磁盘 I/O 打满 if (!Number.isSafeInteger(persistedInspectConcurrency) || persistedInspectConcurrency < 1) { // 并发度必须 ≥1 throw new SessionQueryError( 'session-query: persistedInspectConcurrency must be a …

阅读全文:DeepSeek Harness 源码解析 第 30 讲