|
| 1 | +# contextCollapse 服务实现详细分析 |
| 2 | + |
| 3 | +## 概述 |
| 4 | + |
| 5 | +contextCollapse 是一个**智能上下文压缩系统**,它通过以下方式管理长对话: |
| 6 | +1. **分段压缩** - 将旧消息分组并生成摘要 |
| 7 | +2. **提交日志** - 持久化压缩记录(marble-origami-commit) |
| 8 | +3. **快照管理** - 保存待处理和进行中的压缩状态 |
| 9 | +4. **溢出恢复** - 当达到 token 限制时自动恢复 |
| 10 | + |
| 11 | +## 核心概念 |
| 12 | + |
| 13 | +### 1. Commit(提交) |
| 14 | +```typescript |
| 15 | +ContextCollapseCommitEntry = { |
| 16 | + type: 'marble-origami-commit' |
| 17 | + sessionId: UUID |
| 18 | + collapseId: string // 16位数字ID |
| 19 | + summaryUuid: string // 摘要消息的UUID |
| 20 | + summaryContent: string // <collapsed id="...">text</collapsed> |
| 21 | + summary: string // 纯文本摘要 |
| 22 | +} |
| 23 | +``` |
| 24 | + |
| 25 | +### 2. Snapshot(快照) |
| 26 | +```typescript |
| 27 | +ContextCollapseSnapshotEntry = { |
| 28 | + type: 'marble-origami-snapshot' |
| 29 | + sessionId: UUID |
| 30 | + staged: Array<{ |
| 31 | + startUuid: string |
| 32 | + endUuid: string |
| 33 | + summary: string |
| 34 | + risk: number // 风险评分 |
| 35 | + stagedAt: number // 时间戳 |
| 36 | + }> |
| 37 | + // 触发器状态 |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +### 3. 状态流转 |
| 42 | +``` |
| 43 | +消息列表 → 检测阈值 → 分段(stage) → 生成摘要 → 提交(commit) → 持久化 |
| 44 | + ↑ ↓ |
| 45 | + └────────── 溢出时恢复 ────────────────┘ |
| 46 | +``` |
| 47 | + |
| 48 | +## 需要实现的文件结构 |
| 49 | + |
| 50 | +``` |
| 51 | +src/services/contextCollapse/ |
| 52 | +├── index.ts # 主模块,导出核心API |
| 53 | +├── types.ts # 类型定义(可合并到index.ts) |
| 54 | +├── operations.ts # projectView等操作 |
| 55 | +├── persist.ts # 持久化/恢复 |
| 56 | +└── state.ts # 状态管理(可选) |
| 57 | +``` |
| 58 | + |
| 59 | +## 详细实现方案 |
| 60 | + |
| 61 | +### 1. index.ts - 主模块 |
| 62 | + |
| 63 | +```typescript |
| 64 | +// 核心状态 |
| 65 | +interface CollapseState { |
| 66 | + enabled: boolean |
| 67 | + commits: ContextCollapseCommitEntry[] |
| 68 | + snapshot: ContextCollapseSnapshotEntry | null |
| 69 | + staged: StagedSpan[] |
| 70 | + nextCollapseId: number |
| 71 | +} |
| 72 | + |
| 73 | +const state: CollapseState = { |
| 74 | + enabled: true, |
| 75 | + commits: [], |
| 76 | + snapshot: null, |
| 77 | + staged: [], |
| 78 | + nextCollapseId: 1, |
| 79 | +} |
| 80 | + |
| 81 | +// 导出函数 |
| 82 | +export function isContextCollapseEnabled(): boolean |
| 83 | +export async function applyCollapsesIfNeeded( |
| 84 | + messages: Message[], |
| 85 | + toolUseContext: ToolUseContext, |
| 86 | + querySource: QuerySource |
| 87 | +): Promise<{ messages: Message[] }> |
| 88 | + |
| 89 | +export function isWithheldPromptTooLong( |
| 90 | + message: Message, |
| 91 | + isPromptTooLongMessage: (m: Message) => boolean, |
| 92 | + querySource: QuerySource |
| 93 | +): boolean |
| 94 | + |
| 95 | +export function recoverFromOverflow( |
| 96 | + messages: Message[], |
| 97 | + querySource: QuerySource |
| 98 | +): { messages: Message[]; committed: number } |
| 99 | + |
| 100 | +export function resetContextCollapse(): void |
| 101 | + |
| 102 | +// TokenWarning 需要的统计 |
| 103 | +export function getStats(): CollapseStats |
| 104 | +export function subscribe(callback: () => void): () => void |
| 105 | +``` |
| 106 | + |
| 107 | +### 2. operations.ts - 视图操作 |
| 108 | + |
| 109 | +```typescript |
| 110 | +/** |
| 111 | + * 将提交日志投影到消息列表 |
| 112 | + * 用摘要替换已折叠的消息段 |
| 113 | + */ |
| 114 | +export function projectView(messages: Message[]): Message[] { |
| 115 | + const commits = getCommits() |
| 116 | + // 对于每个 commit,找到对应的原始消息范围 |
| 117 | + // 用 summaryContent 替换该范围内的消息 |
| 118 | + // 返回新的消息列表 |
| 119 | +} |
| 120 | +
|
| 121 | +/** |
| 122 | + * 注册摘要到持久化存储 |
| 123 | + */ |
| 124 | +export function registerSummary( |
| 125 | + summaryUuid: string, |
| 126 | + summary: string |
| 127 | +): void |
| 128 | +
|
| 129 | +/** |
| 130 | + * 获取所有摘要 |
| 131 | + */ |
| 132 | +export function getSummaries(): Map<string, string> |
| 133 | +``` |
| 134 | + |
| 135 | +### 3. persist.ts - 持久化 |
| 136 | + |
| 137 | +```typescript |
| 138 | +/** |
| 139 | + * 从日志条目恢复状态 |
| 140 | + */ |
| 141 | +export function restoreFromEntries( |
| 142 | + commits: ContextCollapseCommitEntry[], |
| 143 | + snapshot: ContextCollapseSnapshotEntry | null |
| 144 | +): void { |
| 145 | + // 恢复 commits 到 state.commits |
| 146 | + // 恢复 snapshot 到 state.snapshot |
| 147 | + // 重新计算 nextCollapseId |
| 148 | +} |
| 149 | +
|
| 150 | +/** |
| 151 | + * 保存当前状态到持久化存储 |
| 152 | + */ |
| 153 | +export function persistState(): void |
| 154 | +``` |
| 155 | + |
| 156 | +## 实现步骤 |
| 157 | + |
| 158 | +### 第一步:基础框架(可运行) |
| 159 | + |
| 160 | +1. 创建 `index.ts` 框架,所有函数返回 safe defaults |
| 161 | +2. 导出必要的类型 |
| 162 | +3. 确保构建通过 |
| 163 | + |
| 164 | +### 第二步:核心逻辑 |
| 165 | + |
| 166 | +1. 实现 `projectView` - 这是最核心的功能 |
| 167 | +2. 实现提交日志管理 |
| 168 | +3. 实现快照管理 |
| 169 | + |
| 170 | +### 第三步:高级功能 |
| 171 | + |
| 172 | +1. 实现 `applyCollapsesIfNeeded` |
| 173 | +2. 实现溢出恢复 |
| 174 | +3. 实现统计和订阅 |
| 175 | + |
| 176 | +### 第四步:集成测试 |
| 177 | + |
| 178 | +1. 测试与 query.ts 的集成 |
| 179 | +2. 测试与 TokenWarning 的集成 |
| 180 | +3. 测试持久化/恢复 |
| 181 | + |
| 182 | +## 最小可行实现(MVP) |
| 183 | + |
| 184 | +最简单的实现可以先让函数返回空值或原值,确保系统能运行: |
| 185 | + |
| 186 | +```typescript |
| 187 | +// index.ts MVP |
| 188 | +export const isContextCollapseEnabled = () => false |
| 189 | +export const applyCollapsesIfNeeded = async (messages) => ({ messages }) |
| 190 | +export const isWithheldPromptTooLong = () => false |
| 191 | +export const recoverFromOverflow = (messages) => ({ messages, committed: 0 }) |
| 192 | +export const resetContextCollapse = () => {} |
| 193 | +export const getStats = () => ({ collapsedSpans: 0, stagedSpans: 0, health: {} }) |
| 194 | +export const subscribe = () => () => {} |
| 195 | +
|
| 196 | +// operations.ts MVP |
| 197 | +export const projectView = (messages) => messages |
| 198 | +``` |
| 199 | + |
| 200 | +然后逐步添加真实逻辑。 |
| 201 | + |
| 202 | +## 依赖关系 |
| 203 | + |
| 204 | +``` |
| 205 | +query.ts |
| 206 | + ├── applyCollapsesIfNeeded |
| 207 | + ├── isContextCollapseEnabled |
| 208 | + ├── recoverFromOverflow |
| 209 | + └── isWithheldPromptTooLong |
| 210 | + |
| 211 | +TokenWarning.tsx |
| 212 | + ├── isContextCollapseEnabled |
| 213 | + ├── getStats |
| 214 | + └── subscribe |
| 215 | + |
| 216 | +REPL.tsx |
| 217 | + └── resetContextCollapse |
| 218 | + |
| 219 | +commands/context/ |
| 220 | + └── operations.projectView |
| 221 | + |
| 222 | +ResumeConversation.tsx |
| 223 | + └── persist.restoreFromEntries |
| 224 | +``` |
| 225 | +
|
| 226 | +## 关键决策点 |
| 227 | +
|
| 228 | +1. **何时触发折叠?** - 基于 token 阈值还是消息数量? |
| 229 | +2. **如何生成摘要?** - 使用 LLM 还是简单截断? |
| 230 | +3. **持久化策略?** - 文件存储还是内存存储? |
| 231 | +4. **并发处理?** - 是否允许多个折叠同时进行? |
| 232 | +
|
| 233 | +## 建议的实现顺序 |
| 234 | +
|
| 235 | +1. ✅ 基础 stub(已完成) |
| 236 | +2. 🔄 projectView 最小实现 |
| 237 | +3. 🔄 提交日志管理 |
| 238 | +4. ⏳ 摘要生成逻辑 |
| 239 | +5. ⏳ 完整 applyCollapsesIfNeeded |
| 240 | +6. ⏳ 溢出恢复 |
| 241 | +7. ⏳ 持久化/恢复 |
| 242 | +
|
| 243 | +## 相关文件参考 |
| 244 | +
|
| 245 | +- `src/services/compact/` - 类似的压缩逻辑可参考 |
| 246 | +- `src/services/compact/autoCompact.ts` - 自动压缩参考 |
| 247 | +- `src/services/compact/microCompact.ts` - 微压缩参考 |
0 commit comments