上下文窗口预算

上下文窗口是有限资源,有边际收益递减。需要系统化的预算管理策略:最近 N 轮原始消息 + 早期摘要 + 结构化状态 + 动态知识注入,在有限 token 内最大化信息密度。

#type / concept #status / evergreen #tech / ai

[!info] related notes

上下文窗口预算

上下文窗口是有限资源,且有边际收益递减——无关内容会削弱模型关注重点。需要系统化的预算管理策略,在有限 token 内最大化信息密度。

核心问题

Claude 官方在 context editing 文档里明确提到:

上下文是有限资源,且有边际收益递减;无关内容会削弱模型关注重点。

LangGraph 官方也提到,开启短期记忆后,长对话可能超过上下文窗口,常见方案包括 trim messages、delete messages、summarize messages。

分层策略

推荐的上下文分层:

┌──────────────────────────────────────────────┐
│  Layer 1: System Prompt + Rules              │  固定,每轮不变
├──────────────────────────────────────────────┤
│  Layer 2: Structured State                   │  结构化问诊状态
│  (consultation_state + user_profile)         │  每轮更新,精简
├──────────────────────────────────────────────┤
│  Layer 3: Recent Messages                    │  最近 6~10 轮原始消息
│  (raw messages, no summarization)            │  完整保留
├──────────────────────────────────────────────┤
│  Layer 4: Conversation Summary               │  早期对话摘要
│  (generated summary of older messages)       │  压缩后的文本
├──────────────────────────────────────────────┤
│  Layer 5: Retrieved Knowledge                │  动态注入 top 3~5
│  (RAG results, tool outputs)                 │  每轮重新检索
├──────────────────────────────────────────────┤
│  Layer 6: Current User Input                 │  当前用户消息
└──────────────────────────────────────────────┘

各层预算分配

假设总预算为 8000 tokens(可配置):

预算占比典型 token 数说明
System Prompt + Rules15%~1200固定开销
Structured State10%~800JSON 格式,精简
Recent Messages40%~3200最近 6~10 轮
Conversation Summary15%~1200早期对话压缩
Retrieved Knowledge15%~1200top 3~5 条知识
Current User Input5%~400当前消息

Recent vs Summary 的分界

消息数量 < 阈值(如 12 条):
  → 全部作为 recent_messages,无 summary

消息数量 ≥ 阈值:
  → 最近 8 条作为 recent_messages
  → 更早的 messages 生成 conversation_summary
  → summary 替代那些原始消息

Summary 生成策略

def generate_summary(older_messages: list[Message]) -> str:
    """把早期消息压缩成摘要"""
    prompt = f"""请将以下对话压缩成简洁的摘要,保留关键信息:
    - 用户的主诉和症状
    - 已确认和未确认的信息
    - AI 的建议和判断
    - 重要的转折点

    对话记录:
    {format_messages(older_messages)}

    请用中文输出,不超过 200 字。"""

    return llm.generate(prompt)

注意:Summary 不替代结构化状态。Summary 是辅助,帮助模型理解对话脉络;consultation_state 才是驱动逻辑的核心。

Summary 版本管理

每次生成新的 summary 时,增加版本号:

{
  "conversation_summary": "...",
  "summary_version": 3,
  "summary_generated_at": "2026-06-30T10:00:00Z",
  "summarized_message_range": ["turn_001", "turn_012"]
}

Token 估算

不同内容的 token 密度不同:

内容类型中文 token 密度说明
自然语言对话~1.5 字/token中文字符比英文多占 token
JSON 结构化数据~2 字/tokenkey 本身也占 token
代码~3 字/token英文为主
Summary~1.2 字/token压缩后的文本密度更高

粗略估算公式:

token_count ≈ chinese_chars / 1.5 + english_words * 1.3 + json_keys * 2

动态知识注入

知识库检索结果应该每轮动态注入,而不是固定塞入:

def retrieve_for_context(user_message, state, budget):
    """按需检索,控制 token 预算"""
    # 根据当前状态决定检索策略
    if state.stage == "collecting_details":
        query = f"{state.chief_complaint} {state.body_parts} 症状鉴别"
    elif state.stage == "preliminary_assessment":
        query = f"{state.chief_complaint} {state.symptoms} 康复方案"
    else:
        query = user_message

    results = knowledge_base.search(query, top_k=5)

    # 截断到预算内
    total_tokens = 0
    filtered = []
    for r in results:
        r_tokens = estimate_tokens(r.content)
        if total_tokens + r_tokens > budget:
            break
        filtered.append(r)
        total_tokens += r_tokens

    return filtered

工具结果处理

工具调用结果(如 RAG 检索、知识库查询)不应全量保留:

策略适用场景
只保留摘要长文档检索结果
保留 top N 条列表型结果
保留关键字段结构化查询结果
完整保留短结果(< 200 tokens)
def summarize_tool_result(result, max_tokens=300):
    if estimate_tokens(result) <= max_tokens:
        return result
    return llm.generate(f"请压缩以下内容到 {max_tokens} tokens 以内:\n{result}")

预算溢出处理

当总 token 超过预算时,按优先级裁剪:

优先级从高到低:
1. system_prompt(不裁剪)
2. current_user_input(不裁剪)
3. consultation_state(不裁剪)
4. recent_messages(从最旧的开始裁剪)
5. retrieved_knowledge(从相关度最低的开始裁剪)
6. conversation_summary(最后裁剪)
def trim_to_budget(context_bundle, token_budget):
    total = estimate_total_tokens(context_bundle)

    if total <= token_budget:
        return context_bundle

    # 先裁剪 recent_messages(从最旧开始)
    while total > token_budget and len(context_bundle.recent_messages) > 2:
        removed = context_bundle.recent_messages.pop(0)
        total -= estimate_tokens(removed)

    # 再裁剪 retrieved_knowledge(从最不相关开始)
    while total > token_budget and len(context_bundle.retrieved_knowledge) > 1:
        removed = context_bundle.retrieved_knowledge.pop()
        total -= estimate_tokens(removed)

    return context_bundle

测试用例

场景验证点
短对话(< 12 轮)无 summary,全量 recent
长对话(> 20 轮)有 summary,recent 只保留最近 8 轮
超长对话(> 50 轮)summary 更新,recent 可能进一步缩减
token 超限自动裁剪,不报错
大量知识检索结果只注入 top N,截断到预算内
空会话不报错,context 正常组装
创建于 2026/6/30 更新于 2026/7/15