# Agent Note: 统一会话查询服务

Status: implemented
Archived: 2026-07-26

[English](2026-07-23-unified-session-query-service.md) | 中文

## 问题

精确读取、语义过滤、关系追踪与全文搜索都作用于同一个实时源优先的会话语料库。将全文搜索暴露在第二个上下文键下，会让消费方与应用组合把同一项查询功能视为两个服务，尽管只有 SQLite 实现是后端特有的部分。

接口包已经拥有共享的记录、过滤、追踪、搜索请求、游标与错误契约。提供方注册表或协调器会引入运行时选择语义，而目前没有任何消费方支持这种语义。

## 决策

`SessionQueryService` 是注册为 `ctx.sessionQuery` 的唯一抽象服务。它通过后端无关的 `SessionCorpus` 具体实现列表查询、标题与事件读取、表层读取、过滤和关系追踪。仅有 `searchSessions()` 与 `searchEvents()` 两个方法为抽象方法。

`SessionQuerySqlite` 扩展该服务，并且是唯一的具体后端。因此，一个挂载实例便可通过 `ctx.sessionQuery` 暴露全部操作；其继承的精确操作使用共享的语料库实现，而由 SQLite 管理的生命周期负责观察数据源、对齐派生 FTS 索引、对匹配项排序并管理游标代际。接口包不提供独立的具体插件、搜索提供方注册表或第二个上下文键。

SQLite 的对齐过程是一个具备静止性保证的串行状态机。它将调用方的原始中止信号传给持久化快照列表与检查操作，直接等待每个已经启动的后端操作，并在每次等待后以及启动下一个数据源或索引操作前检查是否已取消。因此，即使后端忽略取消或正在配合清理，串行器也不会提前释放；观察到中止信号后，也不会再启动后续的列表、检查、对齐或查询操作。

后端配置除了自身的索引路径、日志模式、分页限制与文本片段长度上限外，还包含继承的 `readWindowMax` 设置。需要会话查询的第一方应用挂载 SQLite 后端，并将其可丢弃索引放在已配置的持久化根目录旁。

这一服务拓扑取代了[精确查询决策](../feature/2026-07-10-session-query-service.md)和 [SQLite 搜索决策](../feature/2026-07-10-sqlite-session-query-provider.md)中关于分离上下文键的部分；其中关于语料库、查询、分词器、对齐与安全性的决策仍然有效。

## 已考虑的替代方案

- **保留相互独立的 `ctx.sessionQuery` 与 `ctx.sessionSearch`**：不予采纳，因为二者都针对同一逻辑语料库提供操作，迫使消费方识别两个键，还可能让应用误挂载一组不完整的查询接口。
- **保留具体的基础服务，再由 SQLite 插件注册或修改两个搜索方法**：不予采纳，因为方法是否可用将取决于插件顺序与资源释放时机，而且该服务需要为唯一的实现定义一套提供方注册协议。
- **将所有查询实现移入 SQLite 包**：不予采纳，因为精确读取、过滤与追踪不需要索引，并且都属于应与提供方无关契约放在一起的共享行为。

## 后果

消费方只需注入一个服务，无需再次查找其他功能，便可组合精确操作与全文操作。生产环境的组合必须选择一个具体后端，即使当前某个消费方只调用继承的精确方法；如果后端行为不在测试范围内，测试可以使用最小子类。

统一后的对象有意保留两种内部观察策略：精确操作在每次调用时读取权威的实时源或持久化源，全文操作则使可丢弃索引与数据源对齐。共用上下文键不会让派生索引成为权威来源，也不会使精确读取的可用性依赖 FTS 查询。

排队阶段的取消仍会及时生效。在异步数据源观察已经开始后取消时，调用方会等待该操作完成清理后才收到拒绝；因此拒绝本身构成静止边界，并保证后续搜索仍按单一串行流程执行。同步 SQLite 语句无法在执行中被抢占，服务会在其前后检查中止信号。

单元测试在同一个键上同时固定继承实现与抽象方法的契约，SQLite 测试在具体后端上覆盖两类操作，真实 Loader 路径则验证单个导出的插件能够注册组合后的服务。
