背景
当前用户在使用 DeepCopilot 时频繁遇到 “上下文已达上限,请按 Ctrl+K 清空会话后重新提问。” 的错误,仅几轮工具调用就可能触发。对标顶级 agent 插件(Claude Code、GitHub Copilot Agent、Cursor),DeepCopilot 在上下文经济上有显著差距。
本 Epic 目标:任何场景下都不应强制用户重开会话以恢复对话能力,并在 token 经济上达到第一梯队。
现状诊断
| 位置 |
当前行为 |
问题 |
src/chat/compact.js TOOL_RESULT_KEEP=500 |
大工具结果只保留前 500 字符 |
一次截断丢失绝大部分内容,对结构化数据(grep、目录列表)粗暴 |
src/chat/compact.js autoCompactIfNeeded |
working.length <= keepTail+2 直接 early return |
单条超大消息(如一次读 100KB 文件)永远压不下来 |
src/chat/compact.js firstUser 锚消息 |
永不被截断 |
第一条用户消息若带超长附件,注定卡死 |
src/chat/agent-loop.js COMPACT_BUDGET = contextWindow * 0.6 |
触发阈值 60% |
被动触发,触发时已浪费 60% 窗口 |
src/chat/agent-loop.js emergency keepTail [6, 3] |
只试 2 次就报 CTX_LIMIT |
等价于 “放弃” |
src/chat/agent-loop.js ctxLimitHit = true; break |
直接终止本轮并要求用户清会话 |
核心痛点 — 死亡路径 |
src/chat/session-store.js MAX_API = 200 |
持久化时只按数量切断 |
下次加载就把 “满载” 状态带回,立刻再触发 |
src/tools/file-read.js |
整文件原样塞进历史 |
单次调用可瞬间打爆窗口 |
| Anthropic / OpenAI prompt caching |
未启用 |
重复 system prompt + 历史每次重新计算 |
| 用户自定义压缩规则 |
无 |
Claude Code 支持 CLAUDE.md 中的 Compact Instructions |
/compact 用户命令 |
无 |
无法手动压缩或聚焦 |
| 上下文用量可视化 |
无 |
用户感知不到何时接近上限 |
与顶级 Agent 对照
| 能力 |
Claude Code |
GitHub Copilot Agent |
DeepCopilot 现状 |
| 自动压缩 |
✅ |
✅ |
✅(但放弃过早) |
| 抖动 / 触底后优雅降级 |
✅ 提示用 subagent / 分块读 |
✅ |
❌ 直接报错并要求清会话 |
| 用户可自定义压缩内容 |
✅ /compact focus on ... + CLAUDE.md |
✅ |
❌ |
| 子 agent 隔离重上下文 |
✅ 文档主推 |
✅ |
⚠️ 有 spawn_agent,但模型未被强引导使用 |
| 工具定义按需加载 |
✅ MCP tool search |
✅ |
❌ 全量注入 |
| Prompt caching |
✅ 自动 |
✅ |
❌ |
| 上下文用量 UI |
✅ /context + status line |
✅ |
❌ |
| 会话恢复 / fork |
✅ |
✅ |
⚠️ 只有 resume,无 fork |
| 持久化前预压缩 |
✅ |
✅ |
❌ |
解决方案 — 分四阶段
Phase 0 · 止血(P0,最高优先级)
目标:让 “请清空会话” 错误永不出现。
- 核弹级兜底截断 (
src/chat/compact.js)
autoCompactIfNeeded 在 keepTail 缩到 1 时,对 firstUser 锚消息也做截断(保留前 800 字符 + (truncated) 标记)
- 新增
nuclearCompact():极端场景下只保留 [summary] + [last 1 user/assistant pair]
- 扩展紧急压缩重试 (
src/chat/agent-loop.js)
- keepTail 阶梯
[6, 3] → [8, 6, 4, 2, 1, 'nuclear']
- 删除
ctxLimitHit = true; break 死亡路径:兜底成功则继续;兜底失败则丢弃最旧 50% 后继续,绝不终止本轮
- 持久化前预压缩 (
src/chat/session-store.js)
append() 保存前调用 autoCompactIfNeeded(msgs, contextWindow * 0.4)
- 保证下次
loadApiMessages() 始终回到 “半载” 状态
- TOOL_RESULT_KEEP 提升 + 头尾保留:
500 → 头 600 + 尾 200,并保留首末行结构
Phase 1 · 智能压缩(P1)
- 工具结果结构感知截断(替换简单
slice(0, 500))
grep_search:保留行号 + 文件路径,去重重复片段
list_dir:保留树骨架,剪枝中间层
read_file:保留 head/tail + 中间 …[N lines omitted]… 占位
run_shell / bg-shell:错误优先 + 末尾保留
- 滚动摘要:每 N 轮主动做一次小压缩(增量摘要),而非等到触发
- 附件 / 文件去重:同一文件多次读 → 后续只保留 diff 或引用
Phase 2 · 主动节流(P2,拉开差距)
- Prompt caching
- Anthropic:
cache_control: { type: 'ephemeral' } 打在 system + 旧历史
- OpenAI / DeepSeek:利用前缀稳定性(按需调整
src/api/adapter.js 消息顺序)
- 目标:重复对话 token 计费下降 ≥ 70%
- 工具定义按需加载(tool search)
- 默认仅暴露
find_tools / load_tool 元工具
- MCP 工具按描述匹配后才注入到
tools 数组
- 子 agent 强化引导
- System prompt 显式:“读 > 5KB 文件、跨多文件分析、大目录扫描 → 用
spawn_agent”
read_file 超阈值时返回 hint:Consider spawn_agent for files > 5KB
read_file 自动分块:超阈值自动只返回前 N 行 + 后续行号提示
Phase 3 · UX 与定制(P3)
/compact [focus] 用户命令 — 模仿 Claude Code
.deepcopilot/compact.md — 项目级压缩偏好(兼容读取 CLAUDE.md)
- 上下文用量条:webview 顶部
XX% / Model Window 实时条
- 会话 fork:复制当前会话另存,便于分支实验
/context 命令:列出 system prompt / 历史 / 工具定义各占多少 token
验收标准
- A1:单会话连续 100 轮工具调用(含读取多个 50KB 文件)不出现 CTX_LIMIT 错误
- A2:第一条用户消息携带 100KB 文件后,再持续对话 50 轮可正常运行
- A3:相同任务 token 消耗较改造前下降 ≥ 40%(prompt caching + 按需加载贡献)
- A4:用户可通过
/compact 主动控制压缩聚焦点
- A5:webview 上下文用量条实时显示,准确度 ±10%
- A6:DeepSeek / Anthropic / OpenAI / Custom 四类 provider 全部生效
风险与回滚
- 压缩过度可能造成模型遗忘上下文 → 保留 LLM 摘要 + 结构化兜底双轨;保持
<compact-summary> 累积链不变
- Prompt caching 兼容性 → 仅在 provider 明确支持时启用,feature flag 默认关
- 持久化前预压缩需谨慎:保留原始 messages 至本地 jsonl 以便恢复
子任务(建议拆分子 Issue)
Phase 0
Phase 1
Phase 2
Phase 3
参考
背景
当前用户在使用 DeepCopilot 时频繁遇到 “上下文已达上限,请按 Ctrl+K 清空会话后重新提问。” 的错误,仅几轮工具调用就可能触发。对标顶级 agent 插件(Claude Code、GitHub Copilot Agent、Cursor),DeepCopilot 在上下文经济上有显著差距。
本 Epic 目标:任何场景下都不应强制用户重开会话以恢复对话能力,并在 token 经济上达到第一梯队。
现状诊断
src/chat/compact.jsTOOL_RESULT_KEEP=500src/chat/compact.jsautoCompactIfNeededworking.length <= keepTail+2直接 early returnsrc/chat/compact.jsfirstUser锚消息src/chat/agent-loop.jsCOMPACT_BUDGET = contextWindow * 0.6src/chat/agent-loop.jsemergency keepTail[6, 3]src/chat/agent-loop.jsctxLimitHit = true; breaksrc/chat/session-store.jsMAX_API = 200src/tools/file-read.jsCLAUDE.md中的 Compact Instructions/compact用户命令与顶级 Agent 对照
/compact focus on ...+ CLAUDE.mdspawn_agent,但模型未被强引导使用/context+ status line解决方案 — 分四阶段
Phase 0 · 止血(P0,最高优先级)
目标:让 “请清空会话” 错误永不出现。
src/chat/compact.js)autoCompactIfNeeded在keepTail缩到 1 时,对firstUser锚消息也做截断(保留前 800 字符 +(truncated)标记)nuclearCompact():极端场景下只保留[summary] + [last 1 user/assistant pair]src/chat/agent-loop.js)[6, 3]→[8, 6, 4, 2, 1, 'nuclear']ctxLimitHit = true; break死亡路径:兜底成功则继续;兜底失败则丢弃最旧 50% 后继续,绝不终止本轮src/chat/session-store.js)append()保存前调用autoCompactIfNeeded(msgs, contextWindow * 0.4)loadApiMessages()始终回到 “半载” 状态500→ 头 600 + 尾 200,并保留首末行结构Phase 1 · 智能压缩(P1)
slice(0, 500))grep_search:保留行号 + 文件路径,去重重复片段list_dir:保留树骨架,剪枝中间层read_file:保留 head/tail + 中间…[N lines omitted]…占位run_shell/bg-shell:错误优先 + 末尾保留Phase 2 · 主动节流(P2,拉开差距)
cache_control: { type: 'ephemeral' }打在 system + 旧历史src/api/adapter.js消息顺序)find_tools/load_tool元工具tools数组spawn_agent”read_file超阈值时返回 hint:Consider spawn_agent for files > 5KBread_file自动分块:超阈值自动只返回前 N 行 + 后续行号提示Phase 3 · UX 与定制(P3)
/compact [focus]用户命令 — 模仿 Claude Code.deepcopilot/compact.md— 项目级压缩偏好(兼容读取CLAUDE.md)XX% / Model Window实时条/context命令:列出 system prompt / 历史 / 工具定义各占多少 token验收标准
/compact主动控制压缩聚焦点风险与回滚
<compact-summary>累积链不变子任务(建议拆分子 Issue)
Phase 0
compact.js)ctxLimitHit死路径 + 扩展紧急压缩重试阶梯session-store.append()持久化前预压缩TOOL_RESULT_KEEP调整为头尾保留Phase 1
Phase 2
read_file自动分块Phase 3
/compact [focus]用户命令.deepcopilot/compact.md项目压缩指令/context命令参考