OpenCode-DCP 动态上下文裁剪

· 9 分钟阅读
AI OpenCode Plugin

适用版本:v3.1.12+
仓库Opencode-DCP/opencode-dynamic-context-pruning
许可证:AGPL-3.0-or-later


1. 概述

DCP(Dynamic Context Pruning,动态上下文裁剪)是 OpenCode 专用插件,通过智能管理对话上下文来优化 token 使用量。它在不修改原始会话历史的前提下,用占位符替换已裁剪的内容,再发送给 LLM。

1.1 核心价值

问题DCP 方案
长对话 token 溢出自动压缩已完成的讨论段落
重复工具调用浪费 token去重策略,仅保留最新输出
错误输出占用上下文自动清理过期的错误输入
关键信息被误删多层保护机制(工具/标签/文件/用户消息)

1.2 技术栈

  • 语言:TypeScript (85.3%) + Python (11.1%)
  • npm 包名@tarquinen/opencode-dcp
  • 集成方式:OpenCode 插件系统(@opencode-ai/plugin API)

2. 架构设计

2.1 整体架构

graph TB
    subgraph OpenCode
        A[用户消息] --> B[OpenCode 会话管理器]
        B --> C{DCP 插件}
    end

    subgraph DCP 核心引擎
        C --> D[Compress 压缩工具]
        C --> E[Deduplication 去重策略]
        C --> F[PurgeErrors 错误清理策略]
        C --> G[Nudge 提醒系统]
    end

    subgraph 数据流
        D --> H[压缩摘要<br/>Block Summary]
        E --> I[去重后的<br/>工具输出]
        F --> J[清理后的<br/>错误记录]
    end

    H --> K[优化后的 Context]
    I --> K
    J --> K
    K --> L[LLM API 请求]

    style C fill:#f96,stroke:#333,stroke-width:2px
    style D fill:#69f,stroke:#333
    style E fill:#69f,stroke:#333
    style F fill:#69f,stroke:#333
    style G fill:#fc6,stroke:#333

2.2 工作流程

sequenceDiagram
    participant U as 用户
    participant OC as OpenCode
    participant DCP as DCP 插件
    participant LLM as LLM API

    U->>OC: 发送消息
    OC->>DCP: tool.execute.before hook
    
    Note over DCP: 检查上下文大小

    alt 上下文 < minContextLimit
        DCP-->>OC: 不干预(静默通过)
    else 上下文 ≥ minContextLimit 且 < maxContextLimit
        DCP->>OC: 注入轻量提醒(iteration/turn nudge)
    else 上下文 ≥ maxContextLimit
        DCP->>OC: 注入强压缩提示(context-limit nudge)
    end

    OC->>LLM: 发送请求(含 DCP 修改后的上下文)
    LLM-->>OC: 响应
    
    alt LLM 调用 compress 工具
        LLM->>DCP: compress(startId, endId, summary)
        DCP->>DCP: 替换消息范围为摘要
        DCP->>DCP: 执行去重 + 错误清理
        DCP-->>LLM: 压缩完成
    end
    
    OC-->>U: 返回响应

3. 核心机制

3.1 Compress 压缩

Compress 是暴露给模型的主动工具,模型根据任务完成状态自主决定何时压缩、压缩哪些内容。

两种压缩模式

graph LR
    subgraph "Range 模式(默认·稳定)"
        R1[消息 1] --> R2[消息 2]
        R2 --> R3[消息 3]
        R3 --> R4[消息 4]
        R1 -.->|整段压缩| RS[Block Summary]
        R2 -.->|整段压缩| RS
        R3 -.->|整段压缩| RS
        R4 -.->|整段压缩| RS
    end

    subgraph "Message 模式(实验性)"
        M1[消息 1] -.->|独立压缩| MS1[Summary 1]
        M2[消息 2] -->|保留| M2
        M3[消息 3] -.->|独立压缩| MS3[Summary 3]
        M4[消息 4] -->|保留| M4
    end
特性Range 模式Message 模式
粒度粗粒度(连续跨度)细粒度(逐条消息)
嵌套支持✅ 旧摘要可嵌套进新摘要❌ 无嵌套机制
碎片化
Token 效率
精确控制一般精确(外科手术式)
稳定性稳定版实验性

嵌套压缩(Range 模式独有)

graph TB
    subgraph "第一轮压缩"
        A1[msg 1-10] --> B1["Block b1<br/>(摘要 A)"]
    end
    
    subgraph "第二轮压缩"
        B1 --> C1["Block b2<br/>(摘要 B,内含 b1 占位符)"]
        A2[msg 11-20] --> C1
    end
    
    subgraph "展开后"
        C1 --> D1["b2 摘要展开时<br/>b1 被替换为完整内容<br/>→ 信息层级保留"]
    end

3.2 Deduplication 去重

识别重复的工具调用(相同工具 + 相同参数),仅保留最新一次的输出。

  • 触发时机:compress 工具运行时一起执行
  • 对 Prompt Cache 的影响:仅在压缩时才改变,不会频繁打断缓存

3.3 PurgeErrors 错误清理

清理报错的工具调用,在可配置的轮次后(默认 4 轮)移除其输入内容

  • 错误消息本身保留(保持调试上下文)
  • 仅移除可能很大的输入数据(如文件内容)
  • 触发时机:同样在 compress 工具运行时执行

3.4 Nudge 提醒系统

DCP 通过分级提醒机制引导模型适时压缩:

graph LR
    subgraph "上下文大小区间"
        Z1["0 ~ minContextLimit"] -->|无提醒| N0[静默]
        Z2["minContextLimit ~ maxContextLimit"] -->|轻量提醒| N1["turn-nudge<br/>iteration-nudge"]
        Z3["≥ maxContextLimit"] -->|强压缩| N2["context-limit-nudge"]
    end

    style Z1 fill:#9f9
    style Z2 fill:#ff9
    style Z3 fill:#f99
提醒类型触发条件行为
turn-nudge上下文 ≥ min 且最近 N 轮有工具调用每轮末尾温和提醒
iteration-nudge上下文 ≥ min 且距用户消息超过 N 条自上次用户消息后周期性提醒
context-limit-nudge上下文 ≥ maxnudgeFrequency 频率注入强制压缩指令

4. 保护机制

DCP 提供多层保护,确保关键信息不被裁剪:

graph LR
    subgraph "保护层级"
        P1["🛡️ 全局裁剪保护<br/>(不被删除/去重/清理)"]
        P2["🛡️ Compress 摘要保护<br/>(输出附加到摘要)"]
        P3["🛡️ Protect Tags<br/>(标签内文本原文保留)"]
        P4["🛡️ 用户消息保护<br/>(用户消息不压缩)"]
        P5["🛡️ 文件模式保护<br/>(匹配的文件操作不裁剪)"]
        P6["🛡️ 轮次保护<br/>(最近 N 轮不裁剪)"]
    end

    P1 --> T1["task, skill, todowrite, todoread,<br/>compress, batch, plan_enter,<br/>plan_exit, write, edit"]
    P2 --> T2["task, skill, todowrite, todoread"]
    P3 --> T3["&lt;protect&gt;...&lt;/protect&gt; 标签"]
    P4 --> T4["protectUserMessages: true"]
    P5 --> T5["protectedFilePatterns: ['**/*.config.ts']"]
    P6 --> T6["turnProtection: { turns: N }"]

默认保护工具列表:

全局裁剪保护(不被删除/去重/清理):

task, skill, todowrite, todoread, compress, batch, plan_enter, plan_exit, write, edit

Compress 摘要附加保护(输出原文写入压缩摘要):

task, skill, todowrite, todoread

可通过各级 protectedTools 配置数组追加自定义工具,支持 glob 通配符(如 "mcp_*")。


5. 推荐配置

以下是针对大上下文窗口模型(如 Claude 200K)优化的推荐配置,核心策略是前期放养、后期密集压缩、关键信息不丢

配置文件路径:~/.config/opencode/dcp.jsonc

{
  "$schema": "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/master/dcp.schema.json",

  // 精简通知,减少对话噪声
  "pruneNotification": "minimal",

  // 启用轮次保护(默认保护最近 4 轮)
  "turnProtection": {
    "enabled": true
  },

  "compress": {
    // 下限阈值:180K 以下完全不催压缩
    "minContextLimit": 180000,
    // 上限阈值:模型窗口的 30%
    "maxContextLimit": "30%",
    // 超上限后每 3 次请求提醒一次(更积极)
    "nudgeFrequency": 3,
    // 用户消息后第 10 条开始迭代提醒
    "iterationNudgeThreshold": 10,
    // 压缩时保留用户消息原文
    "protectUserMessages": true,
    // 启用 <protect> 标签保护
    "protectTags": true
  }
}
graph LR
    subgraph "上下文生命周期"
        A["0 ~ 180K tokens"] -->|完全自由| B["DCP 静默<br/>不干预"]
        B --> C["≥ 180K tokens"]
        C -->|所有 nudge 同时激活| D["turn-nudge<br/>iteration-nudge<br/>context-limit-nudge"]
        D -->|模型执行压缩| E["上下文回落"]
        E -->|循环| A
    end

    style A fill:#9f9
    style B fill:#9f9
    style C fill:#f99
    style D fill:#fc6
    style E fill:#69f

策略总结“放养到 180K,然后密集催压”

  • 前期充分利用大窗口模型的上下文容量
  • 接近上限时通过高频 nudge(每 3 次 fetch)和早期迭代提醒(第 10 条)快速压缩
  • 用户消息和 <protect> 标签内容始终不丢,保证关键信息完整

注意minContextLimit(180K)是所有 nudge 的硬性门槛。当 maxContextLimit 30% ≈ 60K 低于 minContextLimit 时,实际效果是 180K 以下一切静默,超过 180K 后所有 nudge(含强压缩)同时激活。


6. 配置参考

6.1 配置文件优先级

graph LR
    G["全局配置<br/>~/.config/opencode/dcp.jsonc"] --> C["自定义目录<br/>$OPENCODE_CONFIG_DIR/dcp.jsonc"]
    C --> P["项目级配置<br/>.opencode/dcp.jsonc"]
    P --> F["最终生效配置"]
    
    style P fill:#f96
    style F fill:#6f9

后者覆盖前者,项目级配置最优先。修改后需重启 OpenCode。

6.2 顶层配置

配置项类型默认值说明
enabledbooleantrue启用/禁用 DCP 插件
autoUpdatebooleantrue自动更新到 npm 最新版
debugbooleanfalse调试日志(输出到 ~/.config/opencode/logs/dcp/
pruneNotificationenum"detailed"裁剪通知级别:off / minimal / detailed
pruneNotificationTypeenum"chat"通知位置:chat(对话内)/ toast(系统通知)
protectedFilePatternsstring[][]文件保护 glob 模式(如 '**/*.config.ts'

6.3 commands 配置

配置项类型默认值说明
commands.enabledbooleantrue启用 /dcp 斜杠命令
commands.protectedToolsstring[][]/dcp sweep 时额外保护的工具(支持 glob)

6.4 manualMode 配置

配置项类型默认值说明
manualMode.enabledbooleanfalse手动模式(禁用自主上下文管理)
manualMode.automaticStrategiesbooleantrue手动模式下仍运行去重/错误清理

6.5 turnProtection 配置

配置项类型默认值说明
turnProtection.enabledbooleanfalse启用轮次保护
turnProtection.turnsnumber4保护最近 N 轮

6.6 experimental 配置

配置项类型默认值说明
experimental.allowSubAgentsbooleanfalse允许 DCP 在子代理会话中运行
experimental.customPromptsbooleanfalse启用用户可编辑的 prompt 覆盖

6.7 compress 配置

配置项类型默认值说明
compress.modeenum"range"压缩模式:range(范围)/ message(逐条)
compress.permissionenum"allow"权限:allow(自动)/ ask(询问)/ deny(禁用)
compress.showCompressionbooleanfalse在通知中显示压缩摘要内容
compress.summaryBufferbooleantrue活跃摘要 token 扩展有效 maxContextLimit
compress.maxContextLimitnumber | string100000上限阈值(支持数字或 "X%" 百分比)
compress.minContextLimitnumber | string50000下限阈值(支持数字或 "X%" 百分比)
compress.modelMaxLimitsobject按模型覆盖 maxContextLimit
compress.modelMinLimitsobject按模型覆盖 minContextLimit
compress.nudgeFrequencynumber5超过上限时 nudge 频率(1=每次,5=每5次)
compress.iterationNudgeThresholdnumber15用户消息后第 N 条开始迭代提醒
compress.nudgeForceenum"soft"提醒强度:strong / soft
compress.protectedToolsstring[][]输出附加到压缩摘要的工具(支持 glob)
compress.protectTagsbooleanfalse保留 <protect> 标签内文本
compress.protectUserMessagesbooleanfalse压缩时保留用户消息原文

6.8 strategies 配置

配置项类型默认值说明
strategies.deduplication.enabledbooleantrue启用去重策略
strategies.deduplication.protectedToolsstring[][]排除去重的工具(支持 glob)
strategies.purgeErrors.enabledbooleantrue启用错误清理策略
strategies.purgeErrors.turnsnumber4错误输入保留的轮次
strategies.purgeErrors.protectedToolsstring[][]排除错误清理的工具(支持 glob)

7. 使用指南

7.1 斜杠命令

命令功能
/dcp显示可用命令列表
/dcp context当前会话 token 使用分布(按类别)
/dcp stats所有会话累计裁剪统计
/dcp sweep [N]裁剪最近用户消息后的工具输出(可选数量 N)
/dcp manual [on|off]切换手动模式
/dcp compress [focus]手动触发一次压缩(可指定聚焦内容)
/dcp decompress <n>还原指定 ID 的压缩块
/dcp recompress <n>重新压缩已还原的块

7.2 Prompt 覆盖

DCP 暴露 6 个可编辑的 prompt(需先启用 experimental.customPrompts: true):

Prompt 文件用途
systemDCP 系统指令
compress-rangeRange 模式压缩指令
compress-messageMessage 模式压缩指令
context-limit-nudge超上限强压缩提示
turn-nudge轮次结束提醒
iteration-nudge迭代过程提醒
  • 默认文件位置:~/.config/opencode/dcp-prompts/defaults/
  • 覆盖方式:在 overrides 目录下创建同名文件
  • 重置方式:删除 overrides 目录下的对应文件

8. 附录:Prompt Cache 影响

graph LR
    subgraph "无 DCP"
        A1["请求 1: ABCDEF"] --> C1["缓存前缀 ABCDEF ✅"]
        A2["请求 2: ABCDEFGH"] --> C2["命中缓存 ABCDEF ✅<br/>仅计算 GH"]
    end

    subgraph "有 DCP(裁剪后)"
        B1["请求 1: ABCDEF"] --> D1["缓存前缀 ABCDEF ✅"]
        B2["请求 2: A[摘要]FGH"] --> D2["缓存失效 ❌<br/>前缀改变"]
    end

裁剪改变消息内容,导致缓存前缀失效(命中率 ~85% vs 无 DCP ~90%),但换来总 token 数减少、过期上下文导致的幻觉降低、可用对话长度延长。按请求计费(如 GitHub Copilot)或缓存与非缓存 token 同价(如 Cerebras)的场景不受影响。