Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

交接架构设计

当一次会话结束、下一次会话启动,智能体如何“接住“上一次的工作?本文系统讲解跨会话 Handoff 的三组痛点、三种主流机制和混合式四层架构,并以 MANIFEST.json(清单文件) 为核心给出可落地的分阶段实施路径。

问题陈述:跨会话交接的三组核心痛点

智能体在单次会话内表现优秀,但跨会话协作时会出现三类问题。

痛点一:会话信息丢失。 OpenCode 已有 /handoff 命令和 DCP compress 工具,但交接依赖智能体自觉输出摘要,缺少强制 Schema(模式) 验证。新会话启动时,要么全量读取历史(Token(令牌) 爆炸),要么只读摘要(关键决策丢失)。某生产项目(代号 Scorpius,下文简称 Scorpius)的实践显示,文件式交接若缺少 JSON schema 验证和文件锁,current.md 会成为单点故障,并发会话还会互相覆盖。

痛点二:无持久化记忆,重复劳动。 当前会话管理依赖文件式 AGENTS.md 和上下文压缩,所有上下文都是临时的。一个 70K Token 的项目上下文,若不做记忆抽取,每次新会话都要重新加载,成本叠加严重——智能体反复理解项目结构、用户偏好、历史决策。

痛点三:上下文膨胀与检索矛盾。 随着对话轮次增加,上下文窗口快速膨胀,输出质量渐进退化。实践中观察到一个关键反差:被动上下文(AGENTS.md 等启动时加载的文件)几乎总被智能体使用,而主动工具MCP(模型上下文协议) 记忆检索)的调用率明显偏低——智能体不会主动调用记忆工具,除非规则强制路由。这一观察与业界“被动注入优于主动调用“的共识一致。

这三组痛点层层递进:第一组是信息丢失(交接质量差),第二组是成本浪费(重复劳动),第三组是机制失效(该用的工具不用)。三者叠加的结果是——智能体单次会话很强,跨会话就“失忆“,团队不得不在每个新会话重新喂上下文。

这组数据决定了本文的核心设计取向:Handoff 必须设计为被动加载,而非依赖智能体自觉。

三种 Handoff 机制对比

业界主流的会话交接机制可归为三类。

文件式交接:以 Scorpius 的 .handoff/current.md 模式为代表,配合 archive/ 归档旧文件。本书 多 Agent(智能体)协作 中的 WORKFLOW_STATE.md 也属此类——通过文件而非对话历史在 Agent 间传递状态。优点是可审计、可提交 Git、跨会话可恢复;缺点是无 schema 约束时格式漂移,并发会话容易互相覆盖。文件式交接的另一个陷阱是“全量读取“——智能体为了不遗漏信息,倾向于把 archive/ 下所有历史都读进来,结果 Token 瞬间冲到 70K+,反而触发上下文压缩。

命令式交接:OpenCode 的 /handoff 命令配合 DCP compress 工具,在会话结束时压缩上下文、生成摘要,新会话基于摘要启动。优点是 OpenCode 内置、无需重建;缺点是依赖智能体合规调用,且摘要质量参差。/handoff 的本质是“让智能体自己写自己的离任交接“——如果智能体这一轮表现不好,交接质量也跟着差。DCP compress 工具的详细用法见 上下文压缩与Token 预算

Hook 被动加载:Claude Code 的 SessionStart / PreCompact Hook 模式——会话启动或上下文即将压缩时,自动 cat 加载 CLAUDE.md 等“唤醒文件“。OpenCode 的等价物是 Plugin 生命周期的 session.created / experimental.session.compacting 钩子(详见 记忆系统设计)。优点是触发率 100%、不依赖智能体自觉;缺点是 Hook 机制本身需要宿主平台支持,且 Hook 触发的文件加载是“全量注入“——文件多大就吃多少 Token,缺少选择性阅读能力。

方案核心机制Token 效率可靠性集成度
文件式current.md + archive/中(全量读取)中(无 schema/锁)
命令式/handoff + DCP 压缩中(依赖合规)高(OpenCode 内置)
Hook 被动加载session.created / experimental.session.compacting(OpenCode)或 SessionStart / PreCompact(Claude Code)高(被动加载)高(自动触发)中(需 Hook 机制)

关键洞察:被动上下文几乎总被使用,而主动工具调用率明显偏低。这意味着 Handoff 不能依赖智能体主动调用——必须设计为被动加载,把交接信息像 AGENTS.md 一样“钉“在启动上下文里。

混合式四层架构

吸取三种机制各自的长板,可构成混合式四层架构。设计规格详见 docs/planning/specs/agent-handoff-memory-spec.md

层级机制存储位置解决的问题
L1 文件式交接.handoff/MANIFEST.json + current.md + archive/Git 仓库结构化交接、版本可追溯
L2 选择性阅读MANIFEST 声明每文件摘要 + Token 估算,按需读取同 L1Token 爆炸(70K → 1.5K)
L3 Schema 验证JSON schema 强制校验 + 文件锁(flock)防并发pre-commit hook格式错误、并发覆盖
L4 Hook 被动加载会话启动自动 cat MANIFEST + 压缩前紧急 dumpOpenCode Plugin 钩子(session.created / experimental.session.compacting)或 Claude Code Hook(SessionStart / PreCompact智能体不主动调用(被动加载覆盖)

下图展示四层架构的数据流,从会话启动到结束的完整闭环:

flowchart TB
    U[用户启动新会话] --> H1["L4: SessionStart Hook"]
    H1 --> M["L1: MANIFEST.json"]
    H2["L4: PreCompact Hook"] --> M
    M --> V["L3: JSON schema 校验"]
    V --> S["L2: 选择性阅读<br/>按 priority 读取"]
    S --> C["L1: current.md"]
    F["L3: flock 文件锁"] -.-> C
    C --> Agent["Agent 携带 ≤1.5K Token 启动"]
    Agent -->|会话结束| W["L1: /handoff 命令"]
    W --> M

    classDef agent fill:#4A90D9,stroke:#333,color:#fff
    classDef workflow fill:#FF9F43,stroke:#333,color:#fff
    classDef mcp fill:#A66CFF,stroke:#333,color:#fff
    class M,C,S,V,F mcp
    class H1,H2,W workflow
    class Agent agent

图中紫色节点表示外部状态存储(含 MANIFEST.json、current.md 等文件与 schema 校验/文件锁机制),非严格意义上的 MCP 服务器。这些文件归类为“外部存储“是因为它们独立于智能体上下文存在,由 Hook 或 /handoff 命令被动读写。

四层各自独立可用,可逐步叠加:先有 L1 就能跑,加 L2 解决 Token 问题,加 L3 解决可靠性,加 L4 解决使用率。

四层如何协作:一次完整的会话交接闭环是这样的——用户启动新会话时,L4 的会话启动 Hook(OpenCode 的 session.created 或 Claude Code 的 SessionStart)被动触发,读取 L1 的 MANIFEST.json;L3 的 schema 校验拦截格式错误的 MANIFEST,flock 文件锁防止并发会话同时写入;L2 根据 MANIFEST 中每个文件的 prioritytokens 字段,只加载 required 文件,把启动 Token 控制在 1.5K 以内;智能体携带精简上下文开始工作。会话结束时,/handoff 命令生成新的 MANIFEST 和 current.md,旧文件归档到 archive/,完成一次 Epoch 切换。整个闭环中,智能体不需要“记得“调用交接工具——Hook 在启动和压缩两个时机自动介入,把被动加载的优势发挥到最大。

MANIFEST.json 核心结构

MANIFEST.json 是整个架构的“目录索引“——智能体启动时先读它,再决定读哪些子文件。核心结构如下:

{
  "session_id": "uuid-2026-07-12-001",
  "created_at": "2026-07-12T10:30:00Z",
  "goal": "为登录模块添加 OAuth 支持",
  "files": [
    { "path": "current.md", "summary": "当前进展:API 已实现,前端待联调", "tokens": 800, "priority": "required" },
    { "path": "decisions.md", "summary": "关键决策:选择 Session+Redis 而非 JWT", "tokens": 300, "priority": "optional" },
    { "path": "next-actions.md", "summary": "下一步:完成 OAuth 回调与错误处理", "tokens": 200, "priority": "required" }
  ],
  "total_tokens": 1300,
  "schema_version": "1.0"
}

字段要点:

  • priorityrequired / optional 两档。L2 选择性阅读时只强制加载 requiredoptional 按需扩展。
  • tokens 是每文件的 Token 估算,用于 L2 决策“读了会不会爆“。
  • total_tokens 是所有 required 文件之和,目标控制在 ≤1.5K——这是会话启动的“过路费“。
  • schema_version 用于 L3 校验,schema 升级时旧 MANIFEST 会被识别并提示迁移。

Context Epoch:上下文纪元

Handoff 不仅是文件传递,更是上下文的“改朝换代“。引入 Context Epoch(上下文纪元) 概念,将每次交接视为一次受控的纪元切换,遵循三条规则:

  1. 不可变基线:每个 Epoch 的基线上下文(MANIFEST + required 文件)一旦写入就不可修改。需要更新时新开一个 Epoch,旧文件归档到 archive/,而非原地覆盖。
  2. 安全转换边界:Epoch 切换发生在 /handoff 命令触发的那一刻——这是唯一允许重置上下文的时机。会话中途不切换 Epoch,避免中途丢失上下文。
  3. 显式状态替换:新会话启动时,Hook 加载的 MANIFEST 内容替换而非追加到上下文——避免新旧 Epoch 内容混淆。实现上可通过 Hook 在 session.created 时清空旧 Epoch 文件再写入新 MANIFEST 来近似替换语义(OpenCode 当前上下文是累加模式,原生“替换“语义需通过文件管理间接实现)。

这三条规则把“会话交接“从模糊的“接着上次干“变成可审计的状态机:每个 Epoch 有明确 ID(session_id)、明确边界(created_at)、明确基线(files 列表)。

举个具体场景:会话 A 完成了 OAuth 登录的 API 层,触发 /handoff 生成 Epoch-001(current.md 记录“API 已实现“)。会话 B 启动时读取 Epoch-001,只加载 required 文件(约 800 Token),开始做前端联调。会话 B 结束时触发 /handoff 生成 Epoch-002,Epoch-001 的文件归档到 archive/epoch-001/。如果会话 B 出了问题需要回滚,只需让新会话读取 Epoch-001 而非 Epoch-002——这就是不可变基线的价值:历史不是负担,是安全网

实施建议:分阶段落地

不要一次性上四层架构——按价值递进落地:

Phase 1:OpenCode /handoff 基础。直接用内置命令生成 current.md,验证交接流程能跑通。此时已是 L1 文件式交接的雏形。

Phase 2:引入 MANIFEST.json。在 /handoff 输出基础上加 MANIFEST 索引,智能体启动时先读 MANIFEST 再选择性加载。Token 占用从 70K 级降到 1.5K 级。

Phase 3:加 Schema 验证与文件锁。定义 JSON schema,pre-commit hook 强制校验;为 current.mdflock 文件锁,杜绝并发会话互相覆盖。

Phase 4:接入 Hook 被动加载。配置会话启动 Hook(OpenCode 的 session.created 或 Claude Code 的 SessionStart)自动 cat MANIFEST,压缩前 Hook(OpenCode 的 experimental.session.compacting 或 Claude Code 的 PreCompact)在上下文即将压缩时紧急 dump 当前进展。至此被动加载覆盖到启动和压缩两个关键时机。

每个 Phase 都可独立交付、可演示、可回滚。Phase 1 失败不影响现有流程,Phase 4 失败可降级回 Phase 3。这种渐进式落地避免了“一步到位“的陷阱——团队可以在每个 Phase 验证实际效果再决定是否继续投入,而不是把四层架构一次性堆上去才发现某一层水土不服。

常见反模式

依赖智能体自觉输出摘要

现象/handoff 命令执行了,但生成的 current.md 内容空洞、丢失关键决策,新会话还是从零开始。

原因:把摘要质量交给智能体“自觉“——没有 schema 约束字段,没有 priority 标记,没有验证环节。

对策:L3 的 JSON schema 强制要求 goal / files / decisions 等字段非空;L1 的 archive/ 保留历史,可对比摘要质量并迭代提示词。

全量读取历史导致 Token 爆炸

现象:新会话启动时把 archive/ 下所有历史 current.md 都读进来“为了完整理解上下文“,Token 瞬间冲到 70K+。

原因:跳过了 L2 选择性阅读,把“可追溯“误用为“全加载“。

对策:MANIFEST 的 priority 字段是硬约束——required 文件必读,optional 文件按需。archive/ 仅供人工审计或显式检索,启动时一律不加载。

无 Schema 验证导致并发覆盖

现象:两个会话同时 /handoff,后写入的 current.md 覆盖了前一个,前一个会话的进展全部丢失。

原因:L1 文件式交接缺少 L3 的文件锁和 schema 校验,current.md 是单点故障。

对策flock 文件锁串行化写入;schema 校验拦截格式错误的 MANIFEST;archive/ 冗余保留每次交接的快照,即使覆盖也能从归档恢复。

关联章节