写作

Agents 实践

在探索 Agents

上下文分层

上下文分层的第一目标是提升信息密度,其次才是解决上下文窗口有限的问题

临时性的信息和长期性信息混在一起会使得模型看到的内容过多,导致模型无法聚焦在重要的信息上

flowchart TB
    subgraph Outside[❌ 永不进入层]
        A1[Hooks 逻辑]
        A2[明确规则]
        A3[工具约束]
    end
    
    subgraph Context[✅ 模型上下文]
        direction TB
        
        subgraph Layer1[常驻层 - 每次会话必载]
            B1[身份定义]
            B2[项目约定]
            B3[安全围栏]
        end
        
        subgraph Layer2[按需加载层 - 需要时载入]
            C1[Skills]
            C2[领域知识]
            C3[使用说明]
        end
        
        subgraph Layer3[运行时注入层 - 动态注入]
            D1[时间/日期]
            D2[通道 ID]
            D3[用户偏好]
        end
    end
    
    subgraph Memory[💾 外部存储]
        E1[MEMORY.md]
        E2[跨会话记忆]
    end
    
    Outside -.->|不进入 | Context
    Memory -.->|按需读取 | Context
    
    style Outside fill:#ffe4e1,stroke:#333,stroke-dasharray:5 5
    style Context fill:#e8f5e9,stroke:#333
    style Memory fill:#fff3e0,stroke:#333
    style Layer1 fill:#c8e6c9
    style Layer2 fill:#dcedc8
    style Layer3 fill:#f0f4c3

五层说明:

层级内容加载时机示例
常驻层身份定义、项目约定、安全围栏每次会话”你是资深前端工程师”、“使用 TypeScript”
按需加载层Skills、领域知识、使用说明需要时才加载调用数据库技能时加载 DB 相关文档
运行时注入层时间、通道 id、用户偏好每次请求动态注入当前时间、用户 ID、主题偏好
记忆层MEMORY.md 跨会话累计的知识需要时读取用户工作风格、项目历史决策
永不进入层Hooks 逻辑、明确的规则永不进入通过工具而非 prompt 约束

对话流工程

对话流工程的核心目标是在有限的上下文窗口内,实现对话状态的可持续追踪和历史信息的按需访问。通过节点化的消息管理和分层汇总机制,让长程对话也能保持上下文的一致性。

核心概念

1. 消息节点(Message Node)

对话中的每条消息都是一个独立的节点,包含:

  • 消息内容(用户输入或模型输出)
  • 时间戳和序列号
  • 与前序节点的连接关系
  • 节点类型标记(普通消息/汇总节点/分支节点)

2. 汇总节点(Summary Node)

汇总节点是对话流中的关键锚点,承担三个职责:

  • 状态压缩:将之前的对话内容提炼为关键信息
  • 持久化存储:将摘要写入硬盘(如 MEMORY.md),跨会话保留
  • 指针标记:通过 last_summary_index 记录最后处理位置

3. 上下文窗口管理

┌─────────────────────────────────────────────────────────────┐
│                    完整对话历史 (硬盘)                        │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐   │
│  │ M1  │─│ M2  │─│ S1  │─│ M3  │─│ M4  │─│ S2  │─│ M5  │   │
│  └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘   │
│                              ↑                       ↑       │
│                              last_summary_index     当前     │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│              活动上下文窗口 (内存/Token)                      │
│  ┌─────┐ ┌─────┐ ┌─────┐                                     │
│  │ S2  │─│ M5  │─│ ... │                                     │
│  └─────┘ └─────┘ └─────┘                                     │
│    ↑       ↑                                                   │
│  锚点    最新                                                  │
└─────────────────────────────────────────────────────────────┘

对话流程

flowchart TD
    A([新消息]) --> B[创建 message 节点]
    B --> C{是否需要压缩?}
    
    C -->|超出 autocompact 阈值 | D[压缩工作流]
    C -->|否 | E[根据 last_summary_index<br/>获取历史消息]
    
    subgraph D [压缩工作流]
        D1[分级压缩<br/>信息权重评估] --> D2[历史信息处理<br/>关键信息保留]
        D2 --> D3[压缩策略选择<br/>摘要/删除/合并]
        D3 --> D4[更新 last_summary_index]
    end
    
    D4 --> E
    E --> F[构建上下文]
    F --> G[LLM 对话]
    G --> H[响应 message 节点]
    H --> I([输出])
    
    style D fill:#ffe4e1,stroke:#333,stroke-dasharray:5 5
    style D1 fill:#f9f,stroke:#333
    style D2 fill:#f9f,stroke:#333
    style D3 fill:#f9f,stroke:#333
    style D4 fill:#bbf,stroke:#333
    style E fill:#bbf,stroke:#333
    style F fill:#bbf,stroke:#333
    style G fill:#98FB98,stroke:#333
    style H fill:#ffd,stroke:#333

流程说明:

  1. 新消息接入:用户输入被包装为 message 节点,进入处理管道
  2. 压缩判断:检查当前上下文是否超出 autocompact 阈值
    • 阈值可基于 token 数、消息条数或时间窗口设定
  3. 压缩工作流(复杂模块):
    • 分级压缩:根据信息权重评估,决定哪些内容需要保留、压缩或删除
    • 历史信息处理:提取关键信息,识别长期有用的上下文
    • 压缩策略选择:动态选择摘要生成、内容删除或相似内容合并
    • 完成后更新 last_summary_index 指针
  4. 上下文构建:从 last_summary_index 位置开始拉取历史消息,组装完整上下文
  5. LLM 对话:发送上下文至模型,获取响应
  6. 响应输出:将模型响应包装为 message 节点,返回给用户

上下文加载策略

对话流的上下文采用分层加载策略,不同层次的信息在不同时机进入上下文:

sequenceDiagram
    participant User as 用户
    participant CM as 上下文管理器
    participant FS as 文件系统
    participant Model as LLM
    
    User->>CM: 发送新消息
    CM->>CM: 创建消息节点
    
    rect rgb(240, 248, 255)
        Note right of CM: 上下文组装阶段
        CM->>FS: 读取 last_summary_index
        FS-->>CM: 返回指针位置
        CM->>FS: 拉取指针后的消息节点
        FS-->>CM: 返回历史消息
        
        CM->>FS: 读取常驻层内容
        FS-->>CM: 身份定义/项目约定
        CM->>FS: 按需加载 Skills
        FS-->>CM: 返回相关技能文档
    end
    
    CM->>CM: 组装完整上下文
    CM->>Model: 发送上下文 + 新消息
    Model-->>CM: 返回响应
    CM-->>User: 输出结果
    
    rect rgb(255, 250, 240)
        Note right of CM: 可选:触发汇总
        CM->>CM: 判断是否需要汇总
        CM->>CM: 生成汇总节点
        CM->>FS: 持久化摘要
        CM->>FS: 更新 last_summary_index
    end

回退与分支机制

对话流支持回退到任意历史节点并开启新的分支:

flowchart LR
    S1((S1)) --> M3[M3]
    M3 --> M4[M4]
    M4 --> S2((S2))
    S2 --> M5[M5]
    S2 --> M5'[M5' 分支]
    M5' --> M6'[M6']
    
    M5 -.->|回退点 | M4
    M4 --> M4a[M4a 新路径]
    M4a --> M5a[M5a]
    
    style S1 fill:#f9f
    style S2 fill:#f9f
    style M5' fill:#ffd
    style M6' fill:#ffd
    style M4a fill:#dfd
    style M5a fill:#dfd

实现要点

  1. 指针管理last_summary_index 必须原子更新,避免指针与摘要内容不一致
  2. 节点 ID 生成:使用时间戳 + 哈希确保唯一性,支持快速定位
  3. 软连接实现:通过引用节点 ID 而非复制内容来保持上下文精简
  4. 汇总触发条件:可基于 token 数、消息数量、时间间隔等维度决策
最后更新