AI Agent长期任务执行技术深度解析:任务分解、持久化与恢复

当前大多数Agent只能执行单轮任务或短对话。当任务执行时间从1分钟增长到1小时甚至数小时,Agent需要解决任务分解、子Agent调度、状态持久化、错误恢复、中间结果验证等一系列工程问题。本文分析这些技术的实现原理。

一、Agent执行时间的数量级划分

Agent的能力范围可以根据执行时间大致划分:

时间量级示例任务典型技术栈已成熟度
秒级问答、代码补全、简单翻译单轮对话✅ 成熟
分钟级单文件编辑、简单接口调用工具调用、ReAct循环✅ 成熟
10分钟级多文件重构、单元测试、代码审查Agent循环+多工具链⚠️ 部分成熟
小时级PR提交、批量数据处理、完整功能开发任务分解+子Agent调度❌ 发展中
天级代码库迁移、全量测试修复、文档体系搭建多Agent协作+状态持久化❌ 早期

10分钟以内的任务,单Agent+ReAct循环基本够用。超过1小时的任务,需要专门的长期任务执行架构。

这个时间分界由大模型的上下文窗口决定。一个Agent运行10分钟产生的中间结果和工具调用记录通常不超过8K token。运行1小时,中间结果可以达到30-50K token。运行半天,中间结果可以超过128K token。

上下文窗口的膨胀有两个问题:一是推理成本线性增长,二是长上下文中的注意力分散导致准确率下降。所以长期任务执行的关键不在于扩大上下文窗口,而在于减少每轮循环中的无效信息。

二、任务分解(Task Decomposition)

2.1 分解粒度

一个"给项目添加用户登录功能"的任务,不同分解方式:

粗粒度分解(3步):
  └─ 写用户模型 → 写认证逻辑 → 写前端页面
  问题:每一步仍需模型自主拆解,中间步骤过多
​
中等粒度分解(12步):
  └─ 设计数据库Schema → 实现User模型和迁移 → 实现注册接口
    → 实现登录接口 → 实现JWT签发和验证 → 实现密码加密
    → 实现token刷新 → 实现中间件拦截 → 实现登录页面UI
    → 实现注册页面UI → 实现路由守卫 → 集成测试
  优点:每步可独立验证,子任务间无隐藏依赖
​
细粒度分解(30步):
  └─ ...(过于细碎,调度开销大于实际运行开销)

分解粒度的经验值:每个子任务的执行时间在3-10分钟之间。小于3分钟说明分解太细,调度和上下文切换的开销超过了任务本身的开销。大于10分钟说明分解太粗,子任务内部可能包含未预期的复杂性。每步的输入和输出都应该有清晰的接口约定。

2.2 分解策略

分层分解(Top-down):先写出总体目标,再拆成3-5个大阶段,每个大阶段再拆成子任务。每个子Agent只看到自己所处的子目标,看不到全局任务。可以减少每个Agent的上下文负担,但需要保证子目标的正确性传递。

逐步分解(Bottom-up):Agent执行第一步,根据第一步的结果决定第二步做什么。这是多数ReAct循环的自带能力。优点是不需要预先制定完整计划,但问题是——如果Agent执行到第5步时发现第2步做错了,回退成本很高。

混合策略:先分层分解制定计划,每层子任务内部用逐步分解执行。实际项目中90%用这个方案。

2.3 依赖图

子任务之间不一定是串行关系。以"给项目添加用户登录功能"为例,依赖图是:

        任务0:设计数据库Schema
         /               \
任务1:实现User模型    任务2:实现密码加密工具
         \               /
      任务3:实现注册接口
         |
任务4:实现JWT签发工具
         |
任务5:实现登录接口
      /        \
任务6:中间件   任务7:登录页面UI
      \        /
    任务8:集成测试

任务1和任务2可以并行执行。任务3依赖任务1和任务2的结果。任务6和任务7可以并行执行,但都依赖任务5。

并行子任务可以减少整体执行时间。在上面的例子中,如果所有任务串行需要8个时间单位,并行优化后只需要5个时间单位(时间单位=一个子任务的平均执行时间)。

依赖图解析本身也是一个Agent任务。将自然语言描述的需求转化为依赖图,需要模型理解任务之间的数据依赖关系。当前最好的做法是让Agent先生成依赖图JSON,再由调度器解析执行。依赖图的验证规则:如果子任务A的输入依赖子任务B的输出,则B必须在A之前完成。

三、子Agent调度(Sub-Agent Dispatch)

3.1 调度粒度

调度模式说明适用场景额外开销
串行调度一个Agent完成任务后传给下一个Agent线性依赖任务
并行调度多个Agent同时执行独立子任务并行依赖任务上下文切换
异步调度子Agent在后台运行,主Agent继续工作长时间运行的子任务状态合并
动态调度根据子任务进度实时调整分配不确定时长的任务

3.2 上下文隔离

每个子Agent不应该看到整个任务的全部上下文。上下文隔离的原则:

子Agent A(写单元测试)只需要知道:被测函数的接口签名、被测函数的输入输出规范、项目的测试框架配置。

子Agent A(写单元测试)不需要知道:数据库连接配置、其他模块的代码、部署配置。

上下文传递机制:

# 简化的上下文传递
class ContextBuilder:
    def build_context(self, task_info, global_context):
        """根据子任务的要求,从全局上下文中筛选相关信息"""
        needed_keys = task_info.get("context_keys", [])
        filtered = {k: global_context[k] for k in needed_keys if k in global_context}
        # 项目级上下文(项目结构、依赖文件、编码规范)
        filtered["project_skeleton"] = self._get_project_skeleton(task_info)
        # 任务级上下文(前序步骤的输出、本任务的输入接口)
        filtered["task_input"] = task_info.get("input_spec")
        return filtered

3.3 结果合并

并行子Agent执行完成后,需要将各自产生的结果合并到主任务中。

结果冲突:子Agent A修改了文件X的第10行,子Agent B修改了文件X的第20行。如果两个修改在同一行,需要冲突检测。文件级别的冲突可以用git merge-base检测,行级别的冲突需要更细粒度的diff检测。

2026年开源项目中的做法:每个子Agent工作在独立的git分支上,主Agent在合并时执行git merge,有冲突时由主Agent(或另启一个resolution Agent)解决。这比在同一分支上直接写入文件更安全——即使合并失败也不会污染主工作区。

四、状态持久化(State Persistence)

4.1 持久化什么

Agent长期运行中,需要持久化的状态包括:

状态类型大小保存频率恢复需求
已完成子任务列表小(几百字节)每步完成时
当前Agent上下文中(几K-几十K)每N步
所有中间结果大(文件系统)每步完成时
Agent执行日志持续
错误记录发生错误时

4.2 检查点(Checkpoint)机制

检查点是在执行关键步骤时保存Agent状态的快照。检查点的触发时机:

按步保存:每完成一个子任务保存一次。优点是恢复粒度细,缺点是IO开销与子任务数量线性相关。10小时的任务如果包含200个检查点,每次保存耗时0.5秒,总开销100秒(约2.8%的额外开销)。

按时间保存:每5分钟保存一次。优点是IO开销固定,不随任务复杂增长。缺点是两次检查点之间如果Agent崩溃,最多损失5分钟的工作。

按风险保存:在"修改文件前"、"执行命令前"、"调用外部API前"等操作前保存。优点是IO开销最小化,缺点是需要Agent自己识别什么是"高风险操作"。

4.3 序列化格式

状态序列化的核心问题:大模型的上下文不是简单的文本,而是消息数组(user/assistant/tool消息交替)。每个tool调用的结果可能很大。序列化时需要做压缩:

# 序列化压缩策略
def serialize_session(messages):
    # 只保留user和assistant消息,移除tool消息(结果太大)
    # tool调用的结果可以用文件路径引用替代
    condensed = []
    for msg in messages:
        if msg["role"] not in ("user", "assistant"):
            continue  # 跳过tool消息
        if msg["role"] == "assistant" and msg.get("tool_calls"):
            # 记录调用了哪些工具,但不保存结果
            condensed.append({
                "role": "assistant",
                "tool_calls": [tc["name"] for tc in msg["tool_calls"]],
                "content": msg.get("content", "")
            })
        else:
            condensed.append({
                "role": msg["role"],
                "content": msg["content"]
            })
    return json.dumps(condensed)

这种压缩方式可以将一个运行1小时的Agent状态从30K token压缩到8K token左右。恢复时,主Agent根据压缩后的状态重建上下文,然后重新请求中间结果(如果中间结果文件还在的话)。

五、错误恢复(Error Recovery)

5.1 错误类型

Agent长期执行中可能遇到的错误:

错误类型示例发生概率(长期任务)恢复策略
API错误模型API返回500重试(指数退避)
工具调用失败文件写入失败、Git冲突回滚到检查点
输出格式错误JSON解析失败重新生成
逻辑错误Agent产生了错误代码人工验证(最复杂)
上下文溢出超过token限制压缩上下文
模型退化模型输出质量下降重启会话

5.2 重试策略

固定重试不适用于Agent长期任务。一个子任务失败后,简单重试往往因为相同的原因再次失败。

指数退避的改进:每次重试时,不仅要等待时间加倍,还要修改重试策略。例如第一次失败是因为上下文太长,重试时压缩上下文。第二次失败因为是JSON格式错误,重试时加上format instruction。

def retry_with_strategy(task, attempt, error):
    if attempt == 1:
        # 第一次失败:简单重试
        return execute_task(task, max_tokens=4096)
    elif attempt == 2:
        # 第二次失败:压缩上下文
        task = compress_context(task)
        return execute_task(task, max_tokens=4096)
    elif attempt == 3:
        # 第三次失败:改用更小的模型执行该子任务
        task.model = "qwen-turbo"
        return execute_task(task, max_tokens=8192)
    elif attempt >= 4:
        # 第四次及以上:标记为人工介入
        return {"status": "skip", "reason": "failed 4 times, needs human review"}

5.3 降级策略

部分子任务失败时,整体任务不一定失败。降级策略:跳过该子任务(如果它是可选的),或使用简化版本的实现替代。

降级判断规则:

  • 该子任务是否是关键路径上的?如果是,不可降级,只能重试或报错。

  • 该子任务是否有默认/简化实现?如果有,使用简化实现。例如"写单元测试",如果写不出完整的测试,改为"只写核心功能的2-3个测试case"。

  • 降级后的结果是否仍能满足整体目标的80%?如果能,降级是可接受的。

六、中间结果验证(Intermediate Verification)

6.1 验证层次

每个子任务完成后,需要验证输出是否正确。验证分为三个层次:

语法验证:子任务的输出是否符合预期的格式。如果预期输出是JSON,验证是否可解析。如果预期是Python代码,验证语法是否正确。这个层次不需要理解语义,成本最低。

功能验证:子任务的输出是否实现了预期的功能。如果预期是写一个函数,运行它看返回值是否正确。如果预期是修改文件,检查diff是否匹配预期。这个层次需要运行代码,成本中等。

逻辑验证:子任务的输出是否符合整体架构设计。如果写了一个新模块,检查它是否和现有模块的接口兼容。如果做了代码重构,检查是否破坏了已有功能。这个层次需要理解全局上下文,成本最高。

6.2 验证失败处理

验证失败不等于子任务完成失败。需要区分失败的原因:

验证失败
├── 输出格式问题(语法错误、JSON格式错误)
│   └─ 修改输出格式,不重新执行子任务
│
├── 功能未满足(函数的输出和预期不匹配)
│   └─ 将验证结果作为反馈,让子Agent重新执行
│
├── 和依赖冲突(子任务B的修改覆盖了子任务A的修改)
│   └─ 需要主Agent介入,协调两个子任务的修改
│
└── 架构设计不合理(新代码和现有架构不一致)
    └─ 需要重新设计,回退到任务分解阶段

6.3 验证的自动化程度

验证内容自动化程度实现方式误报率
语法检查完全自动化编译器/解释器
单元测试完全自动化测试框架
接口兼容性自动化类型检查器
代码风格自动化linter
设计一致性半自动化LLM评审
性能达标自动化基准测试

语法检查、单元测试、linter可以完全自动运行,不需要LLM参与。设计一致性检查需要LLM评审,但LLM评审的误报率较高(约20-30%)。所以在长期任务中,设计评审建议加一个"如果LLM评审报错,让子Agent说明理由后再决策"的缓冲。

七、2026年的技术现状

项目时间范围任务分解持久化错误恢复验证
Claude Code分钟-10分钟内置有限
Codex CLI分钟-10分钟内置有限
OpenHands10分钟-1小时内置有限
AutoGPT 2.01小时+内置有限
Omnigent1小时+外部配置
Hermes Agent10分钟-1小时插件化有限

Claude Code和Codex CLI主要面向分钟级任务,状态只在单次会话中保持,Agent退出后状态丢失。OpenHands可以运行1小时左右的任务,有基本的检查点和重试机制。AutoGPT 2.0和Omnigent支持小时级任务,有完整的持久化和恢复机制。

长期任务执行在2026年仍然是一个正在解决的工程问题,没有标准方案。大部分实现是项目特定的,通用框架还在开发中。

八、总结

  • Agent执行时间从秒级到小时级的跨越,核心挑战是上下文管理和状态持久化

  • 任务分解的推荐粒度:每个子任务3-10分钟,输入输出有清晰接口约定

  • 依赖图解析让子任务可以并行执行,缩短整体执行时间

  • 上下文隔离:子Agent只接收执行本任务所需的信息,不共享全量上下文

  • 检查点按步保存(每子任务完成一次)或按风险保存(操作前),序列化时需要压缩中间结果

  • 错误恢复需要多阶段重试策略:前2次简单重试,第3次压缩上下文,第4次降级模型或标记人工介入

  • 三个验证层次:语法验证(成本最低)→ 功能验证(需运行代码)→ 逻辑验证(需LLM评审)

  • 语法检查、单元测试、linter可以完全自动运行,不需要LLM参与

  • 设计一致性验证的LLM评审误报率约20-30%,建议增加缓冲机制

  • 2026年长期任务执行没有标准方案,OpenHands和Omnigent的实现较为完整

  • 从秒级任务到小时级任务,核心不是模型能力提升,而是工程架构的升级

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐