Skip to content

67. 上下文长度与规划缺陷 ​

难度 P1 高频 · 岗位 应用 · 频率 ★★★★☆ · 预计阅读 8 min 公司 OpenAI · Anthropic 相关题 Q32 记忆架构 · Q60 设计模式 · Q65 多智能体

本题阅读地图 ​

  1. 面试场景还原 — 1 min
  2. TL;DR 速记 — 30 sec
  3. 图解 — 30 sec
  4. 详细解析 — 5 min
  5. 常见踩坑与反例 — 1 min
  6. 面试官可能继续追问 — 1 min

面试场景还原 ​

�面试官:你在做一个能够自动完成复杂任务的 Agent,比如「调研竞争对手并输出 20 页分析报告」。用户反馈说 Agent 跑到一半就开始胡言乱语,或者忘记了最初的目标。你知道是什么原因吗?

🙋‍♂️我:可能是长任务导致的上下文丢失?LLM 上下文窗口有限,执行多步后前面的信息被挤掉了。

👔面试官:对,这是 Agent 长任务的核心瓶颈。你能具体讲讲这个问题的机制吗?以及有哪些解决方案?

🙋‍♂️我:可以把重要信息存到外部,比如数据库或者向量存储,不依赖上下文。

👔面试官:很好。还有其他的策略吗?比如怎么压缩历史信息?怎么设计 Prompt 结构?


TL;DR 速记 ​

  • 核心问题:上下文窗口有限 + 长任务执行 = 历史信息丢失 → 规划失效
  • 失效链:上下文限制 → 早期信息丢失 → 目标遗忘 → 规划崩溃 → 任务失败
  • 解决方案 1:外部状态存储(数据库、文件系统、向量存储)
  • 解决方案 2:历史摘要(滚动窗口、关键信息提取、层次摘要)
  • 解决方案 3:结构化 Prompt(目标置顶、分块组织、优先级排序)
  • 解决方案 4:分阶段执行(阶段独立、阶段间传递摘要而非完整历史)
  • 工程原则:不把状态完全放在上下文里

图解 ​

图 1:上下文撑满导致规划失效 ​

图 2:解决方案架构 ​


详细解析 ​

问题本质:为什么上下文会撑满? ​

Agent 上下文增长模型:

步骤新增内容大致 Token 数
系统 Prompt角色定义、工具描述500-2000
用户请求任务描述100-500
每步思考Thought / Reasoning200-500
每步工具调用Tool name + parameters100-300
每步工具结果API 返回 / 搜索结果500-5000
每步观察Observation / 总结200-500

示例计算:

假设一个需要 20 步的复杂任务:

系统 Prompt: 1500 tokens
用户请求: 300 tokens
每步消耗: Thought(300) + Action(200) + Observation(1000) = 1500 tokens

总消耗 = 1500 + 300 + 20 × 1500 = 31,800 tokens

对于 4K 上下文窗口,第 3 步就溢出了。 对于 32K 上下文窗口,第 20 步左右溢出。 即使 200K 上下文,100 步以上的长任务也会出问题。

溢出后果:

后果表现
目标遗忘忘记最初要做什么
重复工作重复执行已完成的步骤
规划偏离中途走偏,与目标无关
幻觉增加基于不完整上下文产生错误

规划失效的表现 ​

长期规划稳定性问题:

LLM 在长期任务中的表现远不如人类预期:

  1. 目标漂移(Goal Drift)

    • 初始目标:「调研 AI 编程工具市场」
    • 第 10 步后变成:「介绍 GitHub Copilot 功能」
    • 范围收窄,忘记宏观目标
  2. 循环陷阱(Loop Trap)

    • 在相似步骤间循环
    • "搜索信息 → 发现不足 → 再搜索 → 还是不足..."
  3. 过早收敛(Premature Convergence)

    • 基于部分信息就下结论
    • 没有充分调研就给出答案
  4. 行动遗忘(Action Forgetting)

    • 忘记已经做了什么
    • 重复调用相同工具

解决方案一:外部状态存储 ​

核心思想: 把任务状态、中间结果、待办事项存到外部存储,不占用 LLM 上下文。

存储层次:

python
class ExternalStateManager:
    """外部状态管理器"""

    def __init__(self):
        self.db = Database()  # 结构化数据
        self.vector_store = VectorStore()  # 语义检索
        self.file_system = FileSystem()  # 大文件

    async def save_state(self, session_id: str, state: dict):
        """保存任务状态"""
        # 关键元数据存数据库
        await self.db.save({
            "session_id": session_id,
            "goal": state["goal"],
            "progress": state["progress"],
            "current_step": state["current_step"],
            "timestamp": datetime.now()
        })

        # 大文本内容存文件
        if state.get("large_content"):
            await self.file_system.save(
                f"/tmp/agent/{session_id}/content.txt",
                state["large_content"]
            )

        # 中间结果存向量库(可检索)
        if state.get("findings"):
            for finding in state["findings"]:
                await self.vector_store.add(
                    text=finding["content"],
                    metadata={"session_id": session_id, "type": "finding"}
                )

    async def load_relevant_context(self, session_id: str, query: str) -> str:
        """按需加载相关上下文"""
        # 只加载与当前查询相关的信息
        relevant = await self.vector_store.search(query, k=5)

        # 格式化为紧凑的上下文
        context = "\n".join([r.content for r in relevant])
        return context

使用示例:

python
async def agent_with_external_state(task: str):
    session_id = generate_id()
    state_manager = ExternalStateManager()

    # 初始化状态
    await state_manager.save_state(session_id, {
        "goal": task,
        "progress": 0,
        "findings": [],
        "todo_list": ["搜索背景", "收集数据", "分析", "撰写"]
    })

    while not is_complete(session_id):
        # 加载精简状态(不是完整历史)
        state = await state_manager.load_compact_state(session_id)

        # 构建 Prompt(只包含必要信息)
        prompt = f"""
        目标:{state['goal']}
        已完成:{state['completed_steps']}
        当前任务:{state['current_task']}
        关键发现:{state['key_findings']}

        请执行下一步。
        """

        # LLM 决策
        action = await llm.decide(prompt)

        # 执行并更新外部状态
        result = await execute(action)
        await state_manager.update(session_id, action, result)

优势:

  • 上下文只包含必要信息
  • 可以处理超长任务(100+ 步)
  • 任务状态可持久化、可恢复
  • 支持跨会话恢复

解决方案二:历史摘要与压缩 ​

滚动摘要(Rolling Summary):

python
class RollingSummarizer:
    """滚动历史摘要器"""

    def __init__(self, window_size: int = 5):
        self.window_size = window_size  # 保留最近 N 步详细历史
        self.summary = ""  # 更早历史的摘要

    async def add_step(self, step: dict) -> str:
        """添加新步骤,返回压缩后的历史"""
        # 保留最近窗口的详细历史
        recent_steps = self.get_recent(self.window_size)

        # 如果超出窗口,摘要旧历史
        if len(self.history) > self.window_size:
            old_steps = self.history[:-self.window_size]
            self.summary = await self.summarize(old_steps)
            self.history = recent_steps

        # 组合:摘要 + 近期详细
        context = f"""
        历史摘要:{self.summary}

        最近 {self.window_size} 步详细:
        {format_steps(recent_steps)}
        """

        return context

    async def summarize(self, steps: list) -> str:
        """对历史步骤进行摘要"""
        prompt = f"""
        请将以下 Agent 执行步骤摘要为关键信息:
        - 已完成的主要任务
        - 获得的关键发现
        - 当前的待办事项
        - 任何需要注意的问题

        步骤列表:{steps}
        """
        return await llm.generate(prompt)

层次摘要(Hierarchical Summary):

python
class HierarchicalMemory:
    """层次化记忆系统"""

    def __init__(self):
        self.short_term = []  # 最近 3 步(详细)
        self.medium_term = ""  # 最近 10 步摘要
        self.long_term = ""    # 整体任务摘要

    async def add(self, step: dict):
        self.short_term.append(step)

        # 短期 -> 中期
        if len(self.short_term) > 3:
            old_steps = self.short_term[:-3]
            self.medium_term = await self.summarize(old_steps, self.medium_term)
            self.short_term = self.short_term[-3:]

        # 中期 -> 长期
        if len(self.medium_term) > 2000:  # token 数阈值
            self.long_term = await self.summarize_to_long(self.medium_term, self.long_term)
            self.medium_term = ""

    def get_context(self) -> str:
        return f"""
        任务概况:{self.long_term}
        近期进展:{self.medium_term}
        当前步骤:{format_recent(self.short_term)}
        """

解决方案三:结构化 Prompt 设计 ​

分块组织原则:

python
def build_structured_prompt(goal: str, state: dict, history: str) -> str:
    """构建结构化 Prompt"""

    # 优先级 1:目标和约束(永远置顶)
    goal_section = f"""
    === 任务目标 ===
    {goal}

    === 约束条件 ===
    - 输出格式:{state['output_format']}
    - 截止时间:{state['deadline']}
    """

    # 优先级 2:关键状态(紧凑)
    state_section = f"""
    === 当前状态 ===
    进度:{state['progress']}%
    当前步骤:{state['current_step']}
    待办:{state['todo']}
    """

    # 优先级 3:关键发现(摘要)
    findings_section = f"""
    === 关键发现 ===
    {state['key_findings_summary']}
    """

    # 优先级 4:历史(压缩后)
    history_section = f"""
    === 执行历史(摘要)===
    {history}
    """

    # 优先级 5:当前决策
    decision_section = """
    === 你的任务 ===
    基于以上信息,决定下一步行动。
    思考:...
    行动:...
    """

    # 按优先级组合(重要内容在前,避免被截断)
    return "\n".join([
        goal_section,
        state_section,
        findings_section,
        history_section,
        decision_section
    ])

关键信息置顶策略:

python
class PriorityContext:
    """优先级上下文管理"""

    PRIORITY_ORDER = [
        "goal",           # 最高:目标不能忘
        "constraints",    # 约束条件
        "current_state",  # 当前状态
        "critical_findings",  # 关键发现
        "recent_history",     # 近期历史
        "detailed_history"    # 详细历史(最易被截断)
    ]

    def build_context(self, components: dict, max_tokens: int) -> str:
        """按优先级构建上下文,确保重要信息不被截断"""
        context = []
        used_tokens = 0

        for priority in self.PRIORITY_ORDER:
            component = components.get(priority, "")
            component_tokens = estimate_tokens(component)

            if used_tokens + component_tokens <= max_tokens:
                context.append(component)
                used_tokens += component_tokens
            else:
                # 空间不足,压缩或截断
                remaining = max_tokens - used_tokens
                compressed = self.compress(component, remaining)
                context.append(compressed)
                break

        return "\n".join(context)

解决方案四:分阶段执行 ​

阶段划分原则:

python
class StageExecutor:
    """分阶段执行器"""

    def __init__(self):
        self.stages = [
            {"name": "research", "description": "信息收集阶段", "max_steps": 10},
            {"name": "analysis", "description": "数据分析阶段", "max_steps": 8},
            {"name": "writing", "description": "报告撰写阶段", "max_steps": 12},
            {"name": "review", "description": "审核校对阶段", "max_steps": 5},
        ]

    async def execute(self, task: str) -> str:
        # 初始规划:拆解为阶段
        stage_plan = await self.plan_stages(task)

        accumulated_results = {}

        for stage in self.stages:
            print(f"=== 进入阶段:{stage['name']} ===")

            # 阶段独立执行(上下文重置)
            stage_result = await self.execute_stage(
                stage=stage,
                original_goal=task,
                previous_results=accumulated_results,
                stage_plan=stage_plan[stage['name']]
            )

            # 保存阶段结果(摘要形式)
            accumulated_results[stage['name']] = {
                "summary": await self.summarize_stage_result(stage_result),
                "key_outputs": self.extract_key_outputs(stage_result),
                "full_result": stage_result  # 存外部,不放进上下文
            }

        # 最终整合
        return await self.synthesize_final(task, accumulated_results)

    async def execute_stage(self, stage: dict, original_goal: str,
                           previous_results: dict, stage_plan: str) -> str:
        """执行单个阶段(独立上下文)"""

        # 构建阶段专用 Prompt(上下文重置)
        stage_prompt = f"""
        原始任务:{original_goal}

        当前阶段:{stage['name']} - {stage['description']}
        阶段目标:{stage_plan}

        前置阶段关键产出:
        {format_previous_results(previous_results)}

        请完成本阶段任务。
        """

        # 本阶段内使用 ReAct 循环(上下文不会累积到下一阶段)
        return await react_agent.execute(stage_prompt, max_steps=stage['max_steps'])

阶段间信息传递:

python
class StageHandoff:
    """阶段交接管理"""

    async def create_handoff_package(self, stage_result: dict) -> dict:
        """创建阶段交接包(精简信息)"""
        return {
            "stage_name": stage_result["name"],
            "summary": await self.generate_summary(stage_result),
            "key_findings": self.extract_findings(stage_result),
            "deliverables": self.extract_deliverables(stage_result),
            "open_questions": self.extract_questions(stage_result),
            "reference_materials": stage_result.get("references", []),
            # 不包含:详细执行步骤、工具调用历史、中间思考过程
        }

常见踩坑与反例 ​

踩坑 1:完全依赖上下文记忆 ​

错误做法: 把所有状态都放上下文,不设计外部存储。

后果: 长任务必然失败,上下文溢出后信息丢失。

踩坑 2:摘要质量差 ​

错误做法: 用简单的截断代替智能摘要,丢失关键信息。

正确做法: 使用 LLM 进行语义摘要,保留目标和关键发现。

踩坑 3:阶段划分不合理 ​

错误做法: 阶段之间依赖过强,无法真正独立执行。

正确做法: 阶段要有明确的输入输出定义,松耦合。

踩坑 4:忽视状态恢复 ​

错误做法: 没有持久化机制,任务中断后无法恢复。

正确做法: 定期 checkpoint,支持从任意阶段恢复。

踩坑 5:过度优化 ​

错误做法: 为了省 token 过度压缩,导致信息不足决策错误。

正确做法: 平衡压缩率和信息完整性,关键信息保留详细。


面试官可能继续追问 ​

  • 追问 1:怎么评估摘要是否保留了足够信息? 答题要点:下游任务成功率(用摘要能否正确决策)、信息覆盖率(关键信息是否都在)、摘要压缩率(压缩前后 token 比)。

  • 追问 2:外部状态存储用数据库还是向量存储? 答题要点:结构化状态用关系型数据库(进度、待办),非结构化内容用向量存储(可语义检索),大文件存对象存储,混合使用。

  • 追问 3:分阶段执行时,阶段间怎么保证连贯性? 答题要点:明确的阶段接口定义、阶段交接包(摘要 + 关键产出)、原始目标贯穿始终、最终整合阶段统一风格。

  • 追问 4:200K 上下文模型还需要这些优化吗? 答题要点:仍然需要。虽然能处理更长任务,但长上下文增加延迟和成本,且模型对长距离依赖的处理能力仍有局限。优化策略可以适度放宽,但原则不变。


面试总结 ​

上下文长度与规划缺陷是 Agent 长任务的核心瓶颈。面试时强调三点:

  1. 问题本质:上下文有限 → 长任务历史撑满 → 早期信息丢失 → 规划失效。这是 Agent 系统的根本性约束。
  2. 四大解决方案:外部状态存储(不把状态放上下文)、历史摘要(压缩信息)、结构化 Prompt(重要信息置顶)、分阶段执行(阶段独立)
  3. 工程原则:不把状态完全放在上下文里——这是解决长任务问题的核心指导思想

记住:上下文优化是 Agent 系统工程化的基础能力。无论模型上下文多大,合理的架构设计(外部存储 + 智能摘要)都是必要的。

章节首页 · ← Q66 · Q68 →

最后更新2026-05-05
难度P1
频率high
阅读8 min
主题agent / context-window / memory
觉得有帮助?把这个链接转给正在求职的朋友 · 用 Ctrl + K 全站搜索其它题