Context Mode 通过四个关键的架构决策(ADR)解决了从数据库并发到提示词工程的一系列核心问题。这些决策源于具体的技术缺陷:多进程下的 WAL 文件膨胀、LLM 对工具描述的错误解读、路由拒绝信息的误导性措辞,以及上下文节省率统计的失真。本文将依据项目文档,逐一拆解每个决策的背景、原理、具体实施步骤及验证方法,帮助开发者深入理解并应用这些设计。

Context Mode 的四个关键架构决策:多写入器 SQLite、工具描述语言规范、路由拒绝措辞与统计压缩公式

Context Mode 的稳定运行建立在一系列经过严格验证的架构决策之上。这些决策记录在架构决策记录(ADR)中,并非纸上谈兵,而是直接源于生产环境中发现的缺陷与用户反馈。理解这些决策,不仅有助于更高效地使用 Context Mode,也能为构建可靠的 AI agent 上下文管理系统提供宝贵参考。本文将从一个典型的并发写入问题切入,逐步剖析四个关键决策的来龙去脉与具体实施。

决策一:多写入器安全的 SQLite (ADR-0001)

背景与问题 早期版本(v1.0.128)为了修复一个特定的五个进程并发写入导致的 WAL 文件无限膨胀问题(可增长至 238MB+,并引发 ctx_search 卡顿),引入了严格的单写入器锁机制:一个基于文件的互斥锁 (acquireDbLock) 和 locking_mode = EXCLUSIVE 的 SQLite pragma。然而,这个方案“解决了错误的问题”。

真正的根因是两个独立的 bug:

  1. 僵尸进程:MCP 服务器升级后,旧进程未被完全终止,新旧进程同时运行。
  2. 路径误检测:适配器检测错误,导致多个用户会话共享了同一个数据库路径。

当这两个根源被修复后,正常的单项目多窗口使用模式(例如,两个 Claude 会话操作同一项目)却被锁机制拦截,造成了功能回退。

核心决策 SessionDBContentStore 底层使用的 SQLiteBase 必须是多写入器安全的。 这意味着:

  • 删除了整个 db-lock.ts 模块(acquireDbLock, releaseDbLock, DatabaseLockedError)。
  • 从构造函数中移除了 db.pragma(“locking_mode = EXCLUSIVE”)
  • 依赖 SQLite 原生的并发控制机制:通过 withRetry() 方法处理写冲突。该方法在创建数据库连接时设置 busy_timeout = 30000ms(30秒),并在一个有限循环中重试 SQLITE_BUSY 错误。

操作步骤与验证

  1. 配置与使用:作为用户或插件开发者,你无需进行特殊配置。此决策是 Context Mode 的内部实现。你只需确保你的 MCP 客户端(如 Claude Code)能正常连接到 Context Mode 服务即可。
  2. 验证多写入器能力:Context Mode 的测试套件中包含两个关键的回归验证测试(tests/util/db-base-platform-gate.test.ts):
    • 行为验证测试:在同一个数据库路径上打开两个 SessionDB 实例,并尝试通过两者写入数据。该测试断言两个实例都不会抛出 DatabaseLockedError
    • 源码钉测试:直接检查 src/db-base.tsSQLiteBase 类的源码,通过正则表达式断言其中不包含 acquireDbLocklocking_mode=EXCLUSIVE 这些已移除的标识符。 如果未来有代码变更试图重新引入单写入器锁,这两个测试会在 CI 中失败。

影响

  • 正面:合法的多窗口工作流得以恢复。从设计上简化了代码(删除约 265 行代码)。
  • 负面:如果未来进程管理层再次出现类似的多进程 bug,错误表现将不再是明确的“数据库已锁定”,而是表现为 30 秒超时后的 SQLITE_BUSY 错误。解决此类问题的重心应放在进程管理层(检查 sibling-mcp.ts),而非数据库层。
  • 关联设计ContentStore(跨会话的 FTS5 知识库)本身就是多写入器设计,此决策与其保持一致。关于 FTS5 搜索的更多细节,可参考 Context Mode 的 FTS5 搜索引擎

决策二:工具描述语言规范 (ADR-0002)

背景与问题 Context Mode 通过 server.registerTool() 注册了 11 个 ctx_* 工具。这些工具的描述文本会被所有宿主大语言模型(LLM)在工具选择时阅读。经过多个版本,描述文本逐渐充满了防御性、禁止性的词汇(如 MANDATORY, NEVER, Do NOT, REFUSAL RULES),因为每次出现工具误用,描述就会被加入更严厉的警告。

一项针对 38 轮 A/B 实验的审计发现了问题:

  • 严厉的禁止性措辞在某些工具上会降低工具选择的准确性。
  • 但在特定工具上(如 ctx_purge),强烈的措辞反而能提升参数填写的保真度(在 Haiku 模型上从 3/5 提升到 5/5)。
  • 某个工具描述中的单词 "blocked" 被 Opus 4.6 模型误解为网络或安全限制,导致模型放弃使用正确的工具。

核心决策 所有 ctx_* 工具描述必须遵循一个统一的、积极的结构:

<1 行标题,<= 120 字符,使用祈使-肯定语气>

WHEN:
  - <积极的使用触发条件,用破折号列表>

WHEN NOT:
  - <与兄弟工具的区分条件,用破折号列表>

RETURNS:
  <工具返回给 agent 的内容,1-3>

EXAMPLE: <一个包含真实参数的规范调用示例>

同时,建立了一份禁止使用的标记词列表,包括 MANDATORY:, BLOCKED, PREFER X OVER Y, Do NOT use, Never use 以及 emoji 符号(/)。这些词汇在不同 LLM 家庭中的分词和理解不一致,容易引发误解。

操作步骤与验证

  1. 工具描述开发指南:如果你正在为 Context Mode 开发新工具或修改现有工具描述,你必须遵循上述结构。WHEN:WHEN NOT: 使用 markdown 破折号列表,且部分标题必须为大写加冒号(如 WHEN:)。
  2. 特例处理:极少数工具(如 ctx_purge)根据实验数据保留了额外的强调部分(DESTRUCTIVE, SCOPES, CONTRACT),但这些部分必须与规范结构共存,并在测试的允许列表(ALLOWED_EXTRA_SECTIONS)中登记。
  3. 自动化验证:Context Mode 在 tests/core/server.test.ts 中有一个契约测试。该测试会解析所有 server.registerTool() 的代码块,自动检查:
    • 是否包含 WHEN: 部分。
    • 是否使用了禁止列表中的词汇。
    • 是否符合结构规范。 任何违反规范的代码变更都无法通过 CI。这确保了风格的一致性,防止了“描述漂移”。

影响

  • 统一、积极的描述结构提升了跨模型(Claude, GPT, Gemini, Llama)的工具选择可靠性。
  • 通过契约测试实现了风格约束的自动化,降低了维护成本。
  • 为未来工具开发提供了清晰的、基于实证的指南。

决策三:路由拒绝措辞:“重定向” ≠ “限制” (ADR-0003)

背景与问题 Context Mode 的钩子系统(hooks/core/routing.mjs)会拦截工具调用(如 Bash, Read, WebFetch),并返回拒绝原因。这些原因是给 LLM agent 看的,用于引导其下一步行动。问题在于,用于“将调用重定向到更高效工具”(CASE A)的拒绝信息,其措辞与用于“因安全策略限制”(CASE B)的拒绝信息区分不清。

具体案例:对于 WebFetch 调用,原本的拒绝理由是 "WebFetch blocked"。单词 "blocked" 被 Opus 4.6 模型强烈解读为“网络或安全限制”,导致模型放弃使用被推荐的替代工具 ctx_fetch_and_index,转而尝试绕过或放弃任务。

核心决策 必须严格区分两种拒绝场景的措辞:

  • CASE A - 路由重定向:操作被支持,但为了上下文效率会通过另一个工具执行。
    • 必须以动词 "redirected to <ctx_tool>” 开头。
    • 必须肯定替代工具的能力(例如,”<ctx_tool> has full network access”)。
    • 必须明确指出要调用的替代工具。
    • 必须以积极的重试提示结尾(针对瞬时网络错误)。
    • 绝对禁止使用 BLOCKED,或否定式短语(如 NOT a network restriction)。
  • CASE B - 安全/策略限制:操作因安全策略或功能限制被禁止。
    • 可以使用 deniedblocked by security policy 等动词。
    • 必须引用违反的规则或模式。

操作步骤与验证

  1. 理解路由上下文:作为用户,当你看到类似 "redirected to ctx_fetch_and_index" 的提示时,应明确知道这是 Context Mode 的优化引导,并非你的工具调用被安全策略阻止。按照提示使用替代工具即可。
  2. 开发者修改指南:修改 routing.mjs 中的拒绝信息时,必须根据上述 CASE A/B 进行分类。对于 CASE A,使用积极的重定向措辞;对于 CASE B,使用明确的限制性措辞。
  3. 自动化验证:与 ADR-0002 类似,Context Mode 已在测试中添加了对路由拒绝信息的契约检查,确保所有 CASE A 的信息都以 "redirected" 开头,且不包含 BLOCKEDNOT a network 等禁止短语。

影响

  • 消除了因措辞模糊导致 LLM 模型误解的风险,提升了工具调用的连贯性和成功率。
  • 为用户和开发者提供了清晰的信息语义:重定向是优化建议,限制是强制规则。
  • 统一了 routing.mjsctx_fetch_and_index 工具中对于瞬时网络错误的重试提示。

决策四:严格的上下文节省率统计公式 (ADR-0004)

背景与问题 ctx_stats 命令展示的“Without context-mode / With context-mode”进度条,其百分比计算存在语义错误。早期为修复一个极端情况(新会话中 bytesReturned 为 0 时显示 100%)而进行的“修补”(v1.0.134 SLICE B),无意中将基础设施数据量eventDataBytes,主要是钩子捕获的原始数据)计入了分子和分母。

这导致了严重的失真。在一个实际案例中:

  • bytesAvoided(真正的上下文节省)= 2,898 KB
  • bytesReturned(工具实际输出到上下文)= 140 KB
  • eventDataBytes(存入数据库的原始数据)= 2,136 KB(其中 84% 是重复的 CLAUDE.md 内容)

使用错误公式计算,节省率仅显示 56%。而用户基于实际数据量计算,期望看到的节省率应为 95.4%。两者相差 39 个百分点。

核心决策 每会话统计条(Section 1)必须使用严格的压缩率公式,并排除 eventDataBytes 公式如下:

if (bytesAvoided + bytesReturned === 0) {
  // 空状态:尚无可衡量的重定向活动。
  return “No measurable redirect activity captured yet — bars will appear once context-mode diverts its first payload.”;
} else {
  const Without = bytesAvoided + bytesReturned;
  const With = Math.max(1, bytesReturned);
  const pct = (1 - With / Without) * 100;
  return `${pct.toFixed(1)}% kept out`;
}

eventDataBytes 仅应在 ctx_stats捕获计数部分(如“1,000 things — files, errors, decisions, agent runs”)展示,因为它们代表了钩子层记录的数据量,而非进入模型上下文窗口的数据量。

操作步骤与验证

  1. 查看正确的统计:升级到 v1.0.148 及以上版本后,运行 ctx_stats。你会看到每会话的百分比反映了真实的上下文压缩效果(即 (1 - 实际进入上下文的数据量 / 理论上会进入上下文的数据量) * 100%)。首次查看时,该数字可能会有显著跳变,这是统计语义修正的结果,而非数据丢失。
  2. 理解空状态:对于全新的、未发生任何重定向的会话,你将看到一条明确的提示信息,而不是一个无意义的 0% 或 100% 进度条。
  3. 区分统计层级:请注意,ctx_stats 的总览部分(Section 3/4,如“14.7 MB kept out across 200 projects”)的计算逻辑改变,它包含了所有存储相关的字节。只有每会话的进度条百分比采用了新的严格公式。
  4. 开发者验证:在代码库的 tests/analytics/format-report*.test.ts 中,有四个固定的测试用例专门验证这个新的严格压缩公式的逻辑,包括空状态、混合数据、纯节省等场景。

影响

  • ctx_stats 的每会话百分比终于能够真实反映 Context Mode 为特定对话带来的上下文窗口节省效果,与用户的直觉预期一致。
  • 彻底解决了因“修补”引入的统计失真问题。
  • 明确了不同统计数据的边界:eventDataBytes 是存储指标,不是压缩率指标。

总结 这四个架构决策覆盖了从底层存储(SQLite 并发)、到模型交互层(工具描述、路由提示)、再到用户可观测性(统计指标)的完整链路。它们共同的特点是:源于具体缺陷、依赖实证数据验证、并通过自动化测试保障其持久有效。遵循这些决策,是构建可靠、高效且对 AI 友好的系统上下文管理器的关键。关于 Context Mode 如何适配不同 AI 平台的整体架构,可以进一步阅读 Context Mode 适配器系统全解

FAQ

Q: 多写入器 SQLite 的决策是否会影响数据库性能? A: 该决策本身不引入额外的性能开销。相反,它移除了单写入器锁带来的阻塞。性能主要依赖于 SQLite WAL 模式和合理的 busy_timeout 设置。Context Mode 还通过 ContentStore 中的定期优化循环来管理 WAL 文件大小。

Q: 如果我的 AI 助手模型没有遵循工具描述规范怎么办? A: Context Mode 的工具描述规范是基于对多个主流模型(包括 Claude, GPT, Gemini, Llama)的广泛测试得出的,旨在最大化跨模型的兼容性和准确性。契约测试确保了实现的一致性。如果遇到特定模型的异常行为,这通常是模型本身对指令遵循的边界情况,而非规范问题。你可以关注项目的 GitHub Issues,看是否有相关报告。