OpenClaw 上下文窗口管理
OpenClaw 上下文窗口管理
OpenClaw 实现了多层上下文窗口管理系统,通过多种互补策略处理超出模型上下文限制的对话。
整体协作流程图
flowchart TB
subgraph INIT["🚀 初始化阶段"]
A0[runEmbeddedPiAgent 入口<br/>📄 run.ts:192-334] --> A1[执行钩子<br/>before_model_resolve<br/>before_agent_start]
A1 --> A2[解析模型<br/>resolveModel<br/>📄 run.ts:294-306]
A2 --> A3[解析上下文窗口<br/>resolveContextWindowInfo<br/>📄 context-window-guard.ts:21-50]
A3 --> A4{窗口 < 16K?}
A4 -->|是| A5[❌ 阻塞运行<br/>FailoverError]
A4 -->|否| A6{窗口 < 32K?}
A6 -->|是| A7[⚠️ 发出警告]
A6 -->|否| A8[✅ 继续]
A7 --> A8
A8 --> A9[解析认证配置<br/>resolveAuthProfileOrder]
A9 --> A10[获取 API Key<br/>getApiKeyForModel]
end
subgraph RUNLOOP["🔄 运行循环"]
B0[runLoopIterations++<br/>最大 32-160 次] --> B1[runEmbeddedAttempt<br/>📄 run/attempt.ts]
B1 --> B2[历史轮次限制<br/>limitHistoryTurns<br/>📄 history.ts:15-36]
B2 --> B3[安装工具结果保护<br/>installToolResultContextGuard<br/>📄 tool-result-context-guard.ts:297-336]
B3 --> B4{上下文修剪<br/>已启用?}
B4 -->|是| B5[上下文修剪<br/>pruneContextMessages<br/>📄 pruner.ts:220-341]
B4 -->|否| B6[跳过修剪]
B5 --> B5a{使用率 >= 30%?}
B5a -->|是| B5b[软裁剪工具结果<br/>保留头尾各 1500 字符]
B5a -->|否| B5c[跳过软裁剪]
B5b --> B5d{使用率 >= 50%?}
B5d -->|是| B5e[硬清除工具结果<br/>替换为占位符]
B5d -->|否| B5f[跳过硬清除]
B5e --> B6
B5f --> B6
B5c --> B6
B6 --> B7[Agent.transformContext<br/>工具结果截断保护<br/>📄 tool-result-context-guard.ts:269-295]
B7 --> B8[发送到模型]
end
subgraph OVERFLOW["🔥 溢出处理"]
C0{检测溢出错误<br/>isLikelyContextOverflowError<br/>📄 run.ts:671-687} --> C1{已有尝试压缩?}
C1 -->|是| C2[overflowCompactionAttempts++]
C1 -->|否| C3[继续]
C2 --> C3
C3 --> C4{尝试次数 < 3?}
C4 -->|是| C5{内存刷新<br/>已启用?}
C4 -->|否| C6[跳过压缩<br/>尝试工具结果截断]
C5 -->|是| C7{达到阈值?<br/>shouldRunMemoryFlush<br/>📄 memory-flush.ts:113-144}
C5 -->|否| C8[跳过内存刷新]
C7 -->|是| C9[执行内存刷新<br/>保存到 memory/*.md]
C7 -->|否| C8
C8 --> C10
C9 --> C10[执行压缩<br/>compactEmbeddedPiSessionDirect<br/>📄 compact.ts:248-744]
C10 --> C11[触发 before_compaction 钩子]
C11 --> C12[Safeguard 扩展处理]
end
subgraph SAFEGUARD["🛡️ Safeguard 处理"]
D1[验证模型和 API Key<br/>📄 compaction-safeguard.ts:206-226] --> D2{历史超出预算?<br/>newContentTokens > maxHistoryShare}
D2 -->|是| D3[修剪历史<br/>pruneHistoryForContextShare<br/>📄 compaction.ts:339-401]
D2 -->|否| D4[跳过历史修剪]
D3 --> D5[摘要被丢弃消息<br/>summarizeInStages]
D4 --> D6
D5 --> D6[计算自适应块比例<br/>computeAdaptiveChunkRatio<br/>📄 compaction.ts:129-148]
D6 --> D7[渐进式摘要<br/>summarizeInStages<br/>📄 compaction.ts:276-337]
D7 --> D8[收集工具失败信息<br/>collectToolFailures<br/>📄 compaction-safeguard.ts:74-117]
D8 --> D9[收集文件操作信息<br/>computeFileLists<br/>📄 compaction-safeguard.ts:133-155]
D9 --> D10[读取 AGENTS.md 关键内容<br/>readWorkspaceContextForSummary<br/>📄 compaction-safeguard.ts:162-189]
D10 --> D11[拼接最终摘要<br/>+ 工具失败 + 文件操作 + AGENTS.md]
end
subgraph RESULT["📊 结果处理"]
E1[返回压缩结果] --> E2{成功?}
E2 -->|是| E3[触发 after_compaction 钩子]
E3 --> E4[更新会话消息]
E4 --> E5[重试原始请求]
E2 -->|否| E6{有超大工具结果?}
E6 -->|是| E7[截断超大工具结果<br/>truncateOversizedToolResultsInSession<br/>📄 tool-result-truncation.ts:155-280]
E6 -->|否| E8[❌ 报告溢出错误]
E7 --> E9{仍然溢出?}
E9 -->|是| E8
E9 -->|否| E5
E5 --> E10{成功?}
E10 -->|是| E11[✅ 完成]
E10 -->|溢出| E12{运行循环次数<br/>超过限制?}
E12 -->|是| E13[❌ 报告重试限制错误]
E12 -->|否| B0
end
INIT --> RUNLOOP
RUNLOOP -->|溢出| OVERFLOW
OVERFLOW --> SAFEGUARD
SAFEGUARD --> RESULT
style INIT fill:#e1f5fe
style RUNLOOP fill:#e8f5e9
style OVERFLOW fill:#fff3e0
style SAFEGUARD fill:#fce4ec
style RESULT fill:#f3e5f5
详细时序图
sequenceDiagram
participant User as 用户消息
participant Runner as runEmbeddedPiAgent
participant Hook as 钩子系统
participant Model as 模型解析
participant Auth as 认证配置
participant Attempt as runEmbeddedAttempt
participant History as 历史限制
participant Pruning as 上下文修剪
participant Guard as 工具结果保护
participant API as 模型 API
participant Flush as 内存刷新
participant Compact as 压缩引擎
participant Safeguard as Safeguard 扩展
User->>Runner: 发送消息
Note over Runner: 1. 初始化阶段
Runner->>Hook: before_model_resolve
Hook-->>Runner: providerOverride/modelOverride (可选)
Runner->>Model: resolveModel()
Model-->>Runner: model 对象
Runner->>Runner: resolveContextWindowInfo()<br/>📄 context-window-guard.ts:21-50
alt 窗口 < 16K
Runner-->>User: ❌ FailoverError (阻塞)
end
alt 窗口 < 32K
Runner->>Runner: ⚠️ 发出警告
end
Runner->>Auth: resolveAuthProfileOrder()
Auth-->>Runner: 配置文件列表
Runner->>Auth: getApiKeyForModel()
Auth-->>Runner: API Key
loop 运行循环 (最大 32-160 次)
Note over Runner,Attempt: 2. 尝试阶段
Runner->>Attempt: runEmbeddedAttempt()
Note over Attempt: 3. 预处理阶段
Attempt->>History: limitHistoryTurns()<br/>📄 history.ts:15-36
History-->>Attempt: 限制后的消息
Attempt->>Guard: installToolResultContextGuard()<br/>📄 tool-result-context-guard.ts:297-336
opt 上下文修剪已启用
Attempt->>Pruning: pruneContextMessages()<br/>📄 pruner.ts:220-341
Pruning->>Pruning: 计算 contextUsageRatio
alt ratio >= 30%
Pruning->>Pruning: 软裁剪工具结果<br/>保留头尾各 1500 字符
end
alt ratio >= 50%
Pruning->>Pruning: 硬清除工具结果<br/>替换为占位符
end
Pruning-->>Attempt: 修剪后的消息
end
Note over Attempt: 4. 发送到模型
Attempt->>Guard: transformContext()
Guard->>Guard: enforceToolResultContextBudgetInPlace()<br/>📄 tool-result-context-guard.ts:269-295
Guard->>API: 发送请求
alt 溢出错误
API-->>Attempt: ContextOverflowError
Attempt-->>Runner: 溢出检测
Note over Runner: 5. 溢出处理
Runner->>Runner: isLikelyContextOverflowError()<br/>📄 run.ts:671-687
opt 尝试次数 < 3
opt 内存刷新已启用且达到阈值
Runner->>Flush: shouldRunMemoryFlush()<br/>📄 memory-flush.ts:113-144
Flush-->>Runner: true
Runner->>Flush: 执行内存刷新
Flush->>Flush: 写入 memory/YYYY-MM-DD.md
end
Runner->>Compact: compactEmbeddedPiSessionDirect()<br/>📄 compact.ts:248-744
Compact->>Safeguard: session_before_compact 事件
Note over Safeguard: 6. Safeguard 处理
Safeguard->>Safeguard: 收集工具失败信息<br/>📄 compaction-safeguard.ts:74-117
Safeguard->>Safeguard: 收集文件操作信息<br/>📄 compaction-safeguard.ts:133-155
alt 历史超出预算
Safeguard->>Safeguard: pruneHistoryForContextShare()<br/>📄 compaction.ts:339-401
Safeguard->>Safeguard: summarizeInStages(被丢弃消息)
end
Safeguard->>Safeguard: computeAdaptiveChunkRatio()<br/>📄 compaction.ts:129-148
Safeguard->>Safeguard: summarizeInStages(保留消息)<br/>📄 compaction.ts:276-337
Safeguard->>Safeguard: readWorkspaceContextForSummary()<br/>📄 compaction-safeguard.ts:162-189
Safeguard-->>Compact: 返回摘要
Compact->>Compact: 替换旧消息为摘要
Compact-->>Runner: 压缩结果
Runner->>API: 重试请求
end
alt 仍然溢出
API-->>Runner: ContextOverflowError
Runner->>Guard: 截断超大工具结果<br/>📄 tool-result-truncation.ts:155-280
Runner->>API: 再次重试
end
else 成功
API-->>Attempt: 模型响应
end
end
Runner-->>User: 返回结果
各层协作关系图
graph TB
subgraph PROACTIVE["🟢 主动层 - 运行前预防"]
P1[历史轮次限制<br/>limitHistoryTurns]
P2[上下文修剪<br/>pruneContextMessages]
P3[内存刷新<br/>memoryFlush]
end
subgraph REACTIVE["🔴 反应层 - 溢出响应"]
R1[溢出检测<br/>isLikelyContextOverflowError]
R2[压缩摘要<br/>compactEmbeddedPiSessionDirect]
R3[工具结果截断<br/>enforceToolResultContextBudgetInPlace]
end
subgraph CORE["🔵 核心层 - 基础设施"]
C1[上下文窗口解析<br/>resolveContextWindowInfo]
C2[Token 估算<br/>estimateMessagesTokens]
C3[消息分块<br/>chunkMessagesByMaxTokens]
C4[渐进式摘要<br/>summarizeInStages]
end
subgraph SAFEGUARD["🛡️ 保护层 - 智能处理"]
S1[Safeguard 扩展<br/>compactionSafeguardExtension]
S2[历史预算修剪<br/>pruneHistoryForContextShare]
S3[工具失败收集<br/>collectToolFailures]
end
C1 --> P1
C1 --> P2
C2 --> P3
C2 --> R2
C3 --> C4
C4 --> S1
P1 --> R1
P2 --> R1
P3 --> R2
R1 --> R2
R2 --> S1
S1 --> S2
S2 --> C4
S1 --> S3
R2 --> R3
style PROACTIVE fill:#e8f5e9
style REACTIVE fill:#ffebee
style CORE fill:#e3f2fd
style SAFEGUARD fill:#fce4ec
渐进式摘要
总览图
flowchart TB
subgraph Stage1["阶段1: 判断处理方式"]
A[输入消息] --> B{需要分块?}
B -->|否| C[直接摘要]
B -->|是| D[按token分割]
end
subgraph Stage2["阶段2: 分块处理 (如需要)"]
D --> E[对每块独立摘要]
E --> F[收集部分摘要]
F --> G[转为消息]
G --> H[合并摘要]
end
subgraph Stage3["阶段3: 摘要生成 (summarizeWithFallback)"]
C --> I{尝试完整摘要}
H --> I
I -->|成功| J[返回摘要]
I -->|失败| K[过滤超大消息]
K --> L[只对小消息摘要]
L --> M{成功?}
M -->|是| N[返回部分摘要 + 备注]
M -->|否| O[返回失败提示]
end
subgraph Stage4["阶段4: 分块摘要 (summarizeChunks)"]
P[chunkMessagesByMaxTokens] --> Q[遍历chunks]
Q --> R[generateSummary + 重试]
R --> S[累积摘要]
end
I --> P
L --> P
J --> Z([输出最终摘要])
N --> Z
O --> Z
style Stage1 fill:#e3f2fd
style Stage2 fill:#fff3e0
style Stage3 fill:#e8f5e9
style Stage4 fill:#f3e5f5
完整图
flowchart TB
subgraph Input["📥 输入参数"]
I1["messages: AgentMessage[]"]
I2["model, apiKey, signal"]
I3["contextWindow"]
I4["maxChunkTokens"]
I5["reserveTokens"]
I6["parts? 默认=2"]
I7["minMessagesForSplit? 默认=4"]
I8["previousSummary?"]
I9["customInstructions?"]
end
Start([开始 summarizeInStages]) --> CheckEmpty{"messages.length === 0?"}
CheckEmpty -->|是| ReturnDefault["返回 previousSummary<br/>或 'No prior history.'"]
CheckEmpty -->|否| CalcParams["计算参数:<br/>• minMessagesForSplit = max(2, minMessagesForSplit ?? 4)<br/>• parts = normalizeParts(parts ?? 2)<br/>• totalTokens = estimateMessagesTokens(messages)"]
CalcParams --> CheckNeedSplit{"需要分块处理?<br/><br/>parts <= 1<br/>OR messages.length < minMessagesForSplit<br/>OR totalTokens <= maxChunkTokens"}
%% 直接处理路径
CheckNeedSplit -->|否: 直接处理| DirectPath["调用 summarizeWithFallback(params)"]
%% 分块处理路径
CheckNeedSplit -->|是: 分块处理| SplitMsgs["splitMessagesByTokenShare(messages, parts)<br/>按 token 比例分成 N 块"]
SplitMsgs --> FilterEmpty1["过滤空 chunk"]
FilterEmpty1 --> CheckSplitsCount{"splits.length <= 1?"}
CheckSplitsCount -->|是| DirectPath
CheckSplitsCount -->|否| LoopChunks["遍历每个 chunk"]
%% 循环处理每个 chunk
LoopChunks --> ChunkSummarize["summarizeWithFallback({<br/> messages: chunk,<br/> previousSummary: undefined<br/>})"]
ChunkSummarize --> PushPartial["partialSummaries.push(result)"]
PushPartial --> MoreChunks{"还有更多 chunk?"}
MoreChunks -->|是| LoopChunks
MoreChunks -->|否| CheckPartialCount{"partialSummaries.length === 1?"}
CheckPartialCount -->|是| ReturnSingle["返回 partialSummaries[0]"]
CheckPartialCount -->|否| ConvertToMsgs["将摘要转为消息:<br/>summaryMessages = partialSummaries.map(s => ({<br/> role: 'user',<br/> content: s,<br/> timestamp: Date.now()<br/>}))"]
ConvertToMsgs --> BuildMergeInstr["构建合并指令:<br/>'Merge these partial summaries into<br/>a single cohesive summary.<br/>Preserve decisions, TODOs,<br/>open questions, and any constraints.'"]
BuildMergeInstr --> MergeSummarize["summarizeWithFallback({<br/> messages: summaryMessages,<br/> customInstructions: mergeInstructions<br/>})"]
%% summarizeWithFallback 详细流程
subgraph SummarizeWithFallback["summarizeWithFallback 详细流程"]
direction TB
SWF_Start([开始]) --> SWF_StripDetails["stripToolResultDetails(messages)<br/>移除工具结果中的敏感详情"]
SWF_StripDetails --> SWF_TryFull["第一层: 尝试完整摘要<br/>summarizeChunks(全部消息)"]
SWF_TryFull --> SWF_CheckFull{成功?}
SWF_CheckFull -->|是| SWF_ReturnFull["返回完整摘要"]
SWF_CheckFull -->|否| SWF_LogWarn["log.warn('Full summarization failed')"]
SWF_LogWarn --> SWF_Separate["分离消息:<br/>遍历每条消息"]
SWF_Separate --> SWF_CheckOversized{"isOversizedForSummary?<br/>(tokens > contextWindow * 0.5)"}
SWF_CheckOversized -->|是: 超大消息| SWF_AddNote["oversizedNotes.push(<br/> '[Large role (~XK tokens)<br/> omitted from summary]'<br/>)"]
SWF_CheckOversized -->|否: 正常消息| SWF_AddSmall["smallMessages.push(msg)"]
SWF_AddNote --> SWF_NextMsg{"还有更多消息?"}
SWF_AddSmall --> SWF_NextMsg
SWF_NextMsg -->|是| SWF_Separate
SWF_NextMsg -->|否| SWF_CheckSmall{"smallMessages.length > 0?"}
SWF_CheckSmall -->|是| SWF_TryPartial["第二层: 尝试部分摘要<br/>summarizeChunks(smallMessages)"]
SWF_TryPartial --> SWF_CheckPartial{成功?}
SWF_CheckPartial -->|是| SWF_ReturnPartial["返回: 部分摘要 + oversizedNotes"]
SWF_CheckPartial -->|否| SWF_FinalFallback
SWF_CheckSmall -->|否| SWF_FinalFallback["第三层: 最终回退<br/>'Context contained X messages<br/>(Y oversized).<br/>Summary unavailable due to size limits.'"]
SWF_FinalFallback --> SWF_ReturnFinal["返回失败提示"]
end
DirectPath --> SummarizeWithFallback
MergeSummarize --> SummarizeWithFallback
%% summarizeChunks 详细流程
subgraph SummarizeChunks["summarizeChunks 详细流程"]
direction TB
SC_Start([开始]) --> SC_Chunk["chunkMessagesByMaxTokens(messages, maxChunkTokens)<br/>按最大 token 分块"]
SC_Chunk --> SC_LoopChunks["遍历每个 chunk"]
SC_LoopChunks --> SC_Retry["retryAsync(<br/> generateSummary,<br/> { attempts: 3,<br/> minDelayMs: 500,<br/> maxDelayMs: 5000 }<br/>)"]
SC_Retry --> SC_Accumulate["累积摘要:<br/>summary = 新摘要<br/>(传入之前的 summary)"]
SC_Accumulate --> SC_MoreChunks{"还有更多 chunk?"}
SC_MoreChunks -->|是| SC_LoopChunks
SC_MoreChunks -->|否| SC_Return["返回最终累积摘要"]
end
SWF_TryFull --> SummarizeChunks
SWF_TryPartial --> SummarizeChunks
%% chunkMessagesByMaxTokens 详细流程
subgraph ChunkByMax["chunkMessagesByMaxTokens 详细流程"]
direction TB
CM_Start([开始]) --> CM_CalcMax["effectiveMax = maxTokens / SAFETY_MARGIN<br/>(SAFETY_MARGIN = 1.2)"]
CM_CalcMax --> CM_LoopMsgs["遍历每条消息"]
CM_LoopMsgs --> CM_Estimate["estimateCompactionMessageTokens(msg)"]
CM_Estimate --> CM_CheckAdd{"currentTokens + msgTokens > effectiveMax<br/>且 currentChunk 不为空?"}
CM_CheckAdd -->|是| CM_PushChunk["chunks.push(currentChunk)<br/>重置 currentChunk"]
CM_CheckAdd -->|否| CM_AddMsg
CM_PushChunk --> CM_AddMsg["currentChunk.push(msg)<br/>currentTokens += msgTokens"]
CM_AddMsg --> CM_CheckOversized{"msgTokens > effectiveMax?<br/>(单条消息超大)"}
CM_CheckOversized -->|是| CM_PushOversized["chunks.push(currentChunk)<br/>超大消息单独成块<br/>重置 currentChunk"]
CM_CheckOversized -->|否| CM_NextMsg
CM_PushOversized --> CM_NextMsg{"还有更多消息?"}
CM_NextMsg -->|是| CM_LoopMsgs
CM_NextMsg -->|否| CM_PushFinal["if (currentChunk.length > 0)<br/>chunks.push(currentChunk)"]
CM_PushFinal --> CM_Return["返回 chunks"]
end
SC_Chunk --> ChunkByMax
%% 输出
SWF_ReturnFull --> End([结束])
SWF_ReturnPartial --> End
SWF_ReturnFinal --> End
ReturnDefault --> End
ReturnSingle --> End
SC_Return --> End
%% 样式
style Start fill:#e3f2fd
style End fill:#ffebee
style CheckNeedSplit fill:#fff3e0
style CheckEmpty fill:#fff3e0
style CheckSplitsCount fill:#fff3e0
style CheckPartialCount fill:#fff3e0
style MoreChunks fill:#fff3e0
style SummarizeWithFallback fill:#e8f5e9
style SummarizeChunks fill:#f3e5f5
style ChunkByMax fill:#fce4ec
历史消息和最新消息处理机制
flowchart TB
subgraph Step1["步骤1: 计算预算"]
A["maxContextTokens = 模型上下文窗口<br/>(如 128K)"]
B["budgetTokens = maxContextTokens × maxHistoryShare<br/>= 128K × 0.5 = 64K"]
end
subgraph Step2["步骤2: 循环丢弃"]
C{"当前消息 tokens > budgetTokens?"}
C -->|是| D["splitMessagesByTokenShare(messages, 2)<br/>分成 2 部分"]
D --> E["[最早的一半, 最新的一半]"]
E --> F["摘要最早的一半"]
F --> G["保留最新的一半"]
G --> C
C -->|否| H["保留剩余消息"]
end
A --> B --> C
决策树:上下文管理策略选择
flowchart TD
Start[消息处理开始] --> Check1{上下文窗口<br/>解析完成?}
Check1 -->|否| Resolve[resolveContextWindowInfo<br/>📄 context-window-guard.ts:21-50]
Resolve --> Guard1{窗口 < 16K?}
Guard1 -->|是| Block[❌ 阻塞运行]
Guard1 -->|否| Guard2{窗口 < 32K?}
Guard2 -->|是| Warn[⚠️ 警告但继续]
Guard2 -->|否| Continue[✅ 继续]
Warn --> Continue
Check1 -->|是| History[limitHistoryTurns<br/>📄 history.ts:15-36]
Continue --> History
History --> PruneCheck{上下文修剪<br/>已启用?}
PruneCheck -->|否| Transform[transformContext<br/>📄 tool-result-context-guard.ts:269-295]
PruneCheck -->|是| Prune[pruneContextMessages<br/>📄 pruner.ts:220-341]
Prune --> Ratio1{使用率 >= 30%?}
Ratio1 -->|是| SoftTrim[软裁剪<br/>保留头尾各 1500 字符]
Ratio1 -->|否| Ratio2
SoftTrim --> Ratio2{使用率 >= 50%?}
Ratio2 -->|是| HardClear[硬清除<br/>替换为占位符]
Ratio2 -->|否| Transform
HardClear --> Transform
Transform --> Send[发送到模型]
Send --> Result{结果?}
Result -->|成功| Done[✅ 完成]
Result -->|溢出| OverflowCheck{尝试次数 < 3?}
OverflowCheck -->|否| TruncateCheck{有超大工具结果?}
OverflowCheck -->|是| FlushCheck{内存刷新<br/>已启用?}
TruncateCheck -->|是| TruncateLarge[截断超大工具结果<br/>📄 tool-result-truncation.ts:155-280]
TruncateCheck -->|否| Error[❌ 报告溢出错误]
TruncateLarge --> FinalCheck1
FinalCheck1{仍然溢出?}
FinalCheck1 -->|是| Error
FinalCheck1 -->|否| Done
FlushCheck -->|否| Compact
FlushCheck -->|是| FlushThreshold{达到阈值?<br/>📄 memory-flush.ts:113-144}
FlushThreshold -->|否| Compact
FlushThreshold -->|是| FlushExec[执行内存刷新<br/>写入 memory/*.md]
FlushExec --> Compact
Compact[compactEmbeddedPiSessionDirect<br/>📄 compact.ts:248-744] --> BeforeHook[before_compaction 钩子]
BeforeHook --> Safeguard[Safeguard 处理<br/>📄 compaction-safeguard.ts:191-371]
Safeguard --> HistoryCheck{历史超出预算?<br/>maxHistoryShare}
HistoryCheck -->|是| PruneHistory[pruneHistoryForContextShare<br/>📄 compaction.ts:339-401]
PruneHistory --> SummarizeDropped[摘要被丢弃消息]
HistoryCheck -->|否| AdaptiveRatio
SummarizeDropped --> AdaptiveRatio[computeAdaptiveChunkRatio<br/>📄 compaction.ts:129-148]
AdaptiveRatio --> Summarize[summarizeInStages<br/>📄 compaction.ts:276-337]
Summarize --> AppendInfo[附加信息<br/>工具失败 + 文件操作 + AGENTS.md]
AppendInfo --> AfterHook[after_compaction 钩子]
AfterHook --> Retry[重试请求]
Retry --> RetryResult{结果?}
RetryResult -->|成功| Done
RetryResult -->|溢出| LoopCheck{运行循环<br/>超过限制?}
LoopCheck -->|是| LoopError[❌ 重试限制错误]
LoopCheck -->|否| History
style Start fill:#e3f2fd
style Done fill:#e8f5e9
style Error fill:#ffebee
style LoopError fill:#ffebee
style Compact fill:#fff3e0
style Safeguard fill:#fce4ec
触发条件汇总
| 组件 | 触发条件 | 文件:行号 |
|---|---|---|
| 上下文窗口警告 | tokens < 32,000 |
context-window-guard.ts:71 |
| 上下文窗口阻塞 | tokens < 16,000 |
context-window-guard.ts:72 |
| 软裁剪 | contextUsageRatio >= 0.3 |
pruner.ts:259 |
| 硬清除 | contextUsageRatio >= 0.5 |
pruner.ts:298 |
| 内存刷新 | totalTokens >= contextWindow - reserveTokensFloor - softThresholdTokens |
memory-flush.ts:129 |
| 历史修剪 | newContentTokens > contextWindow * maxHistoryShare |
compaction-safeguard.ts:250 |
| 工具结果截断 | currentChars > contextBudgetChars |
tool-result-context-guard.ts:286 |
| 溢出压缩重试 | 最多 3 次 | run.ts:511 |
| 运行循环限制 | 32-160 次 (基于认证配置数量) | run.ts:111-121 |
| 超大工具结果截断 | textLength > contextWindowTokens * 0.3 * 4 |
tool-result-truncation.ts:71-76 |
关键常量
| 常量 | 值 | 说明 | 文件:行号 |
|---|---|---|---|
CONTEXT_WINDOW_HARD_MIN_TOKENS |
16,000 | 最小上下文窗口 | context-window-guard.ts:3 |
CONTEXT_WINDOW_WARN_BELOW_TOKENS |
32,000 | 警告阈值 | context-window-guard.ts:4 |
MAX_OVERFLOW_COMPACTION_ATTEMPTS |
3 | 溢出压缩最大尝试次数 | run.ts:511 |
BASE_RUN_RETRY_ITERATIONS |
24 | 基础重试次数 | run.ts:111 |
RUN_RETRY_ITERATIONS_PER_PROFILE |
8 | 每个认证配置增加的重试次数 | run.ts:112 |
MIN_RUN_RETRY_ITERATIONS |
32 | 最小重试次数 | run.ts:113 |
MAX_RUN_RETRY_ITERATIONS |
160 | 最大重试次数 | run.ts:114 |
BASE_CHUNK_RATIO |
0.4 | 基础分块比例 | compaction.ts:11 |
MIN_CHUNK_RATIO |
0.15 | 最小分块比例 | compaction.ts:12 |
SAFETY_MARGIN |
1.2 | 安全边际 (20%) | compaction.ts:13 |
SUMMARIZATION_OVERHEAD_TOKENS |
4096 | 摘要开销 Token | compaction.ts:81 |
MAX_TOOL_RESULT_CONTEXT_SHARE |
0.3 | 单个工具结果最大上下文占比 | tool-result-truncation.ts:11 |
HARD_MAX_TOOL_RESULT_CHARS |
400,000 | 工具结果硬性字符限制 | tool-result-truncation.ts:19 |
CONTEXT_INPUT_HEADROOM_RATIO |
0.75 | 上下文输入保留比例 | tool-result-context-guard.ts:5 |
数据流向图
flowchart LR
subgraph Input["📥 输入"]
M1[用户消息]
M2[工具结果]
M3[助手回复]
end
subgraph Pipeline["⚙️ 处理管道"]
F1[历史限制]
F2[上下文修剪]
F3[工具结果保护]
F4[压缩摘要]
end
subgraph Output["📤 输出"]
O1[发送到模型]
O2[摘要消息]
O3[内存文件]
end
M1 --> F1
M2 --> F1
M3 --> F1
F1 --> F2
F2 --> F3
F3 --> O1
O1 -->|溢出| F4
F4 --> O2
F4 --> O3
O2 --> F3
O3 --> O1
style Input fill:#e3f2fd
style Pipeline fill:#fff3e0
style Output fill:#e8f5e9
核心函数调用链
runEmbeddedPiAgent (run.ts:192)
├── resolveContextWindowInfo (context-window-guard.ts:21)
├── evaluateContextWindowGuard (context-window-guard.ts:57)
├── resolveAuthProfileOrder (model-auth.ts)
└── 运行循环 (run.ts:538)
└── runEmbeddedAttempt (run/attempt.ts)
├── limitHistoryTurns (history.ts:15)
├── installToolResultContextGuard (tool-result-context-guard.ts:297)
├── pruneContextMessages (pruner.ts:220) [可选]
└── Agent.transformContext
└── enforceToolResultContextBudgetInPlace (tool-result-context-guard.ts:269)
溢出时:
└── compactEmbeddedPiSessionDirect (compact.ts:248)
├── shouldRunMemoryFlush (memory-flush.ts:113) [可选]
└── session.compact
└── compactionSafeguardExtension (compaction-safeguard.ts:191)
├── collectToolFailures (compaction-safeguard.ts:74)
├── computeFileLists (compaction-safeguard.ts:133)
├── pruneHistoryForContextShare (compaction.ts:339)
├── computeAdaptiveChunkRatio (compaction.ts:129)
├── summarizeInStages (compaction.ts:276)
└── readWorkspaceContextForSummary (compaction-safeguard.ts:162)

浙公网安备 33010602011771号