之前跑一个长流程Agent任务整整执行了四十分钟眼看就到倒数第二步结果一次工具调用超时整个进程直接崩溃。重启之后它居然从头开始跑又是四十分钟又是几万token然后在同一个地方再次超时。那一刻我意识到光有一个能跑通Demo的Agent框架根本不叫做完了真正要命的是运行机制上下文怎么管、检查点怎么落、中断怎么恢复、循环怎么控、资源怎么分。这篇内容就是围绕Agent运行机制里的五件核心事展开的——上下文、检查点、任务恢复、循环执行、资源管控。不是讲某个单一框架的API怎么用而是把这些机制背后为什么要这么设计实际跑起来会踩什么坑完整拆一遍。适合正在做Agent开发、接Dify/RestCloud/LangChain这类工作流、或者被上下文超长任务中断执行卡死折磨的读者参考。1. 上下文机制Agent的工作记忆是如何构成和耗尽的1.1 上下文的真实构成不只是聊天记录很多人以为上下文就是把用户说的话和模型的回复拼在一起其实在大模型Agent里上下文的构成比这复杂得多。一次完整的Agent任务上下文中至少包含这几类内容系统提示词、用户目标描述、工具定义function calling的schema、工具执行返回的结果、中间的规划与思考过程、历史交互记录、以及外部检索到的知识片段。以我常用的一个自动化周报生成Agent为例用户输入只有一句生成上周的研发团队周报但模型实际拿到的上下文包含了系统提示词里对周报格式的要求、工具定义里的四个函数描述查git提交、查项目进度、查bug统计、发邮件、每一步函数返回的JSON结果、最后生成了初稿之后用户的确认信息。全部加起来一次任务的token消耗大约在8000到15000之间。很多人以为上下文只是对话轮次这会导致一个严重的误判你以为上下文还够其实早就被工具结果和中间过程悄悄吃满了。1.2 1M上下文到底意味着什么最近各家模型都在卷上下文窗口动辄1M token也就是大约100万token换算成中文大概相当于两三百万字。听起来窗口变大了不是想塞多少塞多少吗但实际使用中长窗口有三个不为人知的隐性代价。第一是性能衰减。上下文越长模型对中间部分的关注力越弱这在业界叫lost in the middle中间内容遗忘率远高于开头和结尾。实测中即使能塞进1M token当你让模型回答位于上下文中部的问题时准确率可能比短上下文低一截。第二是成本非线性增长。API计费按输入token数算上下文翻倍单轮成本直接翻倍而Agent这种多轮调用的场景输入token是每轮都要重新计算一次的。第三是延迟上升。长上下文的prefill阶段耗时明显增加用户等反馈的体感差距是很直观的。所以我的建议是上下文窗口够用就好不要盲目追求长。真实工程里1M上下文的价值在于处理超长文档类任务而不是让Agent无限堆对话历史。1.3 上下文窗口用完了怎么办这是Agent开发里被问得最多的一个问题。窗口满了不是无解的。应对策略按优先级排序我通常这么处理第一结构化摘要。把早期的对话历史压缩成结构化摘要放在上下文最前面摘要保留关键信息已完成的目标、已获取的关键数据、仍未解决的问题。比如连续五轮讨论选择数据源最终定了用ClickHouse摘要里就写数据源已确定为ClickHouse原因查询性能不要重复讨论。这个策略可以把上下文占用直接砍半。第二工具结果瘦身。很多工具返回的结果是冗长的JSON模型真正需要的可能只有几个字段。在Agent框架里加一个结果规整层把工具返回的原始数据统一做字段裁剪和摘要化再注入上下文。比如git log返回了200条提交记录注入前先聚合成共200次提交涉及12个模块其中登录模块变更最频繁38次。第三滑动窗口历史归档。近期对话保留完整原文早期对话替换为摘要。这个策略适合多轮长会话。第四外置记忆。把中间产物写入向量库或KV存储上下文里只放检索到的相关片段而不是全量。这一条其实是检查点机制的雏形下一章会详细展开。注意摘要策略要谨慎对待指令类信息。如果早期对话里有用户明确提出的格式要求或偏好这类信息不能丢必须保留到最终上下文里否则模型会在后续执行中偏离用户意图。2. 检查点机制断点持久化与状态快照的设计思路2.1 为什么Agent必须做检查点LLM本身是无状态的它不保存任何执行状态。每次调用模型都是把当前上下文全部传给它让它基于那一刻的信息输出下一步动作。这意味着Agent的状态其实是分布式存在于每一步的输出里。如果进程崩溃、网络断开、API报错导致执行中断整个Agent就丢失了当前进行到哪里的信息。不做检查点的后果就是开头说的那种惨痛经历从头再来。而且对于接入了外部系统的Agent从头再来不仅仅是时间和token的浪费还有真实业务风险——比如创建订单这个操作如果你重试了一次就产生了两个订单。所以检查点不只是保存进度是Agent工程的必需品。2.2 检查点里到底要保存什么设计检查点时首先要区分两层状态框架层状态和外部副作用记录。框架层状态包括当前执行到第几步、已完成的步骤ID列表、每个步骤的输入输出变量、规划结果比如那个步骤依赖图DAG、上下文状态当前用的是完整历史还是摘要策略、当前使用的模型参数。这些信息描述的是Agent自己进行到哪里了。外部副作用记录指的是已经成功调用过的外部工具及其返回值尤其是那些有真实影响的工具比如发送邮件、创建工单、发起支付。恢复时这些工具结果不能重放否则会造成重复操作。解决思路是给每个工具调用分配一个幂等键调用前先生成调用后记录在检查点里恢复时看到幂等键已存在就直接复用记录结果不再真正执行。2.3 检查点粒度与存储选型检查点的粒度直接影响恢复效率。在Agent场景里粒度基本是每一步工具调用完成后落一个检查点。LLM调用之间的间隔往往很长因为真实工具执行耗时定时快照完全抓不住有效时机必须做步骤级快照。存储选型上我见过三种方案。最简单的是写本地JSON文件适合单机调试缺点是并发和容灾差。第二种是写入Redis或MongoDB适合多实例部署按checkpoint_id存储天然支持横向扩展。第三种是基于对象存储的版本化检查点适合超长任务或需要审计的场景。生产环境我一般推荐第二种兼顾速度和可靠性。一个典型的检查点记录结构长这样{ checkpoint_id: task_20250421_001, task_id: weekly_report_task_0418, step_index: 7, total_steps: 12, step_status: { 1: completed, 2: completed, 3: completed, 4: completed, 5: completed, 6: completed, 7: completed, 8: in_progress }, variables: { git_aggregate: 共200次提交12个模块, bug_stats: 残留bug 34个中优先级以上9个 }, context_snapshot: { mode: summary_with_recent, recent_turns: 6, summary_hash: a3f9c2d8e1 }, side_effects: { tool_send_email: { request_id: req_c3f9a11, status: not_executed } }, timestamp: 2025-04-21T15:32:07Z }注意context_snapshot只保存摘要的哈希和最近几轮原文的引用不存全量上下文否则检查点文件会变得非常大。恢复时从消息存储里按引用重新组装上下文远比复制一份全量文本进检查点合理。3. 任务恢复与循环执行从中断中恢复的完整链路3.1 恢复流程的四步走任务恢复的完整流程我总结为四步加载检查点、重建上下文、校验副作用、决定继续点。加载检查点后重建上下文是第一步。具体做法是把摘要信息放最前后面紧跟未完成部分的原文上下文再接一句你已经执行到第8步共12步以下是已完成的中间结果请继续执行。这里要特别注意不要把已完成的步骤的输出原样全部塞回上下文只塞精简后的关键变量。否则上下文会膨胀得失控。校验副作用是最容易被忽略的一步。恢复时先查检查点里的side_effects列表对每个已执行过的工具调用确认它们在外部系统里的真实状态。比如发送邮件这个动作检查点里记录的状态是not_executed但外部日志显示邮件已发出这时恢复逻辑就必须跳过重发直接标记为completed。这个校验没法靠Agent框架自动完成通常需要你为关键工具写一个state_check函数返回这个请求在外部系统的实际状态。决定继续点之后还有个细节问题重试策略。对于临时性错误超时、限流、503用指数退避重试对于永久性错误权限不足、参数错误直接放弃该步骤标记为failed并切换策略不要硬重试。3.2 循环执行Agent运转的核心形态Agent的底层是一个无限循环感知环境、规划下一步、调用工具、观察结果、再规划。这本质上就是感知-规划-行动-观察的循环结构。实际工程里循环执行的实现通常是包一层while循环把step_index一直递增到total_steps或显式结束条件触发。循环执行最大的坑是活锁模型在同一个步骤上反复尝试相同或相似的操作没有任何进展。这在大模型Agent里太常见了原因通常是模型对某一步的结果不满意但又不具备换个方向的能力。我见过一个极端案例一个Agent在调用数据库查询接口这一步反复重试了23次每次都是同一个错误消耗了几万token才被外部超时终止。防止活锁的手段主要有三层。第一层是最大重试次数比如同一工具调用最多重试3次。第二层是语义去重比较当前这一步的输入和最近N步的输入如果相似度超过阈值判定为无进展强制停止或换策略。第三层是熔断器连续M次同一类型错误后后续一段时间内直接拒绝执行该工具。这三层建议配合使用单靠一层都不够稳妥。3.3 任务恢复后的上下文重建与摘要策略这里补充一个恢复时的关键细节恢复后上下文里已完成的步骤到底放什么。我的实践经验是放三样东西已完成步骤的编号与名称清单、每一步抽出的关键输出每个不超过50个字、以及当前未完成的步骤本身需要的局部上下文。不要放完整步骤输出否则恢复后第一轮调用就可能把上下文打爆。举个例子周报Agent恢复时重建后的上下文是已完成1-7步git数据已聚合共200次提交bug统计已获取残留34个项目进度已收集完成度87%。当前步骤汇总数据生成周报初稿。这样模型可以基于中间结果继续执行而不是什么都不知道地重新跑一遍。注意摘要时不要丢用户中途修改过的需求。如果有周报格式从表格改成要点式这类指令必须在摘要里单独标注为highest_priority指令防止恢复后模型按旧格式执行。4. 资源管控上下文预算、并发上限、循环次数与边界条件4.1 资源管控的五个维度Agent的资源管控不是只盯着模型API的token消耗。完整来看至少有五个维度必须纳入管理token预算、时间预算、并发预算、存储预算、金额预算。token预算指的是每个环节能用多少token以及总上下文窗口怎么分配。时间预算指的是每个步骤的超时限制和整个任务的总时长限制。并发预算处理的是同时跑多少个Agent任务以及模型API的并发上限这两个问题。存储预算约束的是检查点和中间产物的磁盘占用。金额预算则是给整体运行设置成本红线。这五个维度里token和时间是最容易出问题的因为它们在执行期动态变化不像存储和金额可以在设计时预估。4.2 按环节划分的上下文预算表我在实际项目里会给不同环节划分上下文预算而不是让模型随便用。一个典型的预算分配如下表环节预算占比说明系统提示词 用户目标10%必须保留是行为的宪法工具定义10%必要的函数schema无关工具不要注入全局摘要20%压缩后的历史信息随执行轮次增长近期步骤原文20%最近3-5轮完整记录支持模型理解当前局面当前步骤的输入输出30%本步骤的核心工作区预留余量10%应对模型输出和意外情况这套方案的思路是金字塔分层全局信息尽量压局部信息尽量留。很多Agent慢、乱就是因为全局信息占太多当前步骤的工作区反而很小模型只能在一堆历史里翻找关键信息。4.3 双模型策略与快慢分离资源管控里性价比最高的策略是双模型分离。规划、总结、工具选择这类对推理深度要求高的环节用强模型工具结果规整、日志摘要、格式化输出这类重复性强的环节用便宜快速的小模型。实测下来周报Agent里git log规整这个环节用强模型跑要5秒、0.3元用小模型只要1秒、0.02元输出质量差别不大。整条任务下来成本大概能省30%到40%延迟体感也能降一小截。另一个好用的小技巧是给模型预算脚注。在系统提示词的最后一行加类似你已完成11/20步剩余可用token约8000请优先执行关键步骤。这个脚注对模型的行为有真实的收敛作用能减少无效的反复试探。不少框架没有自动注入预算提示的机制我一般是在封装模型调用层时手动拼进去。4.4 Harness资源管控的载体热词里有人问harness和agent的区别放在资源管控的语境下最容易讲明白。Agent是决策单元它负责做什么Harness是承载Agent运行的外壳它负责能不能安全地做。资源配额、工具路由、权限边界、检查点落盘、循环控制、上下文注入这些都不该由Agent自己决策而是由Harness强制实施。拿权限边界举例Agent的目标是从git仓库取数据但工具列表里恰好有个删除仓库的函数定义模型可能在某个奇怪的情况下调用它。Harness的作用就是在模型输出调用删除仓库时检查权限策略直接拒绝并返回无权限。没有Harness这层控制任何模型失误都可能变成真实事故。所以我的观点是在工程化里Harness的质量比Agent本身更重要因为Agent的能力上限由模型决定但行为下限由Harness兜底。回到循环控制的场景Harness还负责把循环执行的边界条件具象化。我习惯把边界条件直接写成状态机初始→运行中→用户中断→失败重试→完成。每次模型返回一个动作Harness就做一次状态迁移检查如果当前状态不允许该动作就直接拦截。这套机制比在提示词里写请遵守规则可靠得多。5. 一次完整故障复盘从上下文撑爆到自动恢复5.1 问题现象与初步定位用上面的框架解释一个真实的故障案例。当时我运行一个数据采集Agent任务是去20个数据源拉取信息并汇总成报告。跑了大约三分之一的进度时日志显示上下文窗口即将耗尽然后进程在下一步工具调用时报错退出。初步定位时我先看了检查点文件——幸好每步骤快照是有的最后一步checkpoint记录在第7步。问题出在上下文策略这个任务没配置摘要策略全部历史原文都被塞进上下文。随着数据源越来越多单个数据源的工具结果越来越大上下文在第7步时已经到了窗口上限的90%。5.2 恢复策略与验证过程当时我临时做了两件事。第一加载第7步的检查点但重建上下文时启动摘要策略把第1到第6步的完整工具输出全部压缩成结构化摘要。第二把后续步骤的工具结果做了瘦身配置每个数据源的返回只保留核心字段。恢复之后Agent继续执行从第8步往下跑没有再触碰窗口上限。整个过程恢复耗时大约40秒比从头再跑一遍省了至少二十分钟关键是避免了重新拉取前7个数据源的外部请求。这个案例说明检查点和上下文策略必须配合使用。检查点保证你能从断点恢复上下文策略保证恢复后真能跑完缺一个都不行。5.3 复盘后的常驻改进这个故障之后我做了三处常驻改进。一是所有Agent项目默认开启结构化摘要这是硬性默认项。二是检查点里增加上下文健康度字段记录每一步执行后的上下文占用率如果超过80%就在日志里预警而不是等到撑爆才被动处理。三是给关键的采集类工具写副作用校验函数下次恢复时可以自动确认这个数据源的请求之前是否真的成功过避免因为网络抖动导致重复拉取或漏拉取。这三处改进后来在多个项目里都派上了用场。尤其是上下文健康度预警效果最直接——它在问题发生之前就把信号给出了。6. 循环执行中的异常防护活锁、重试风暴与幂等控制6.1 活锁检测不只是简单的重复判断前面提到活锁是Agent循环里最常见的异常但要真正做到检测活锁不能只靠连续几步动作是否相同这种粗糙判断。模型在活锁状态下每一步的措辞可能都在变但执行的工具和参数却高度相似。我常用的方案是对工具名核心参数摘要做归一化处理然后用字符串相似度或embedding相似度去比较最近N步的指纹超过阈值就判定为无进展。阈值设置上经验值是相似度高于0.85且连续3步无外部进展就触发熔断。这里要特别说明无外部进展的定义工具调用了但返回的状态码都是错误或者返回值与上一步完全一致。如果每一步都成功获取了新数据哪怕动作相似也不该判活锁。6.2 指数退避、抖动与重试风暴重试是恢复机制的一部分但乱重试会引发更严重的问题——重试风暴。当外部API因为负载高开始返回503时你如果立即重试所有Agent实例同时重试会把API彻底打垮。标准解法是指数退避加抖动第一次重试等待1秒第二次4秒第三次9秒依此类推同时每次等待时间加一个随机偏移。实现很简洁核心逻辑就是每次重试的等待时间等于base_delay乘2的幂次方再加上一个随机数import random import time def exponential_backoff(attempt, base_delay1, max_delay60): delay min(base_delay * (2 ** attempt), max_delay) jitter random.uniform(0, delay * 0.3) time.sleep(delay jitter)加抖动的意义在于让多个实例的重试节奏错开避免同一时刻挤爆API。这个细节看着小但在并发场景下是质变。6.3 幂等控制恢复安全的关键幂等控制要聊得再深一点。给工具调用分配request_id只是第一步真正的幂等要分三个层次去检查调用前检查检查点里是否已有此请求的成功记录、调用中检查API层面是否支持幂等键、调用后检查外部系统的状态查询接口确认结果。三层全过才算真正的幂等闭环。我之前见过一个只有第一层检查的案例Agent调用创建Jira工单检查点里没有记录所以重新发起但其实第一次调用已经成功创建了工单只是写检查点这一步崩溃了。第二层和第三层检查的价值就在这里——它们能发现记录没有但外部系统已有结果这种不一致状态。所以我在生产环境里的策略是对所有带外部副作用的工具强制要求实现第三层状态检查函数。这增加了一点点开发量但换来的是恢复操作的安全性。7. 资源管控的量化配置一个可直接套用的参考模板7.1 任务级资源配置文件给一个可以直接套用的资源配置文件模板字段按照我前面讲的五维管控来设计task: name: weekly_report_agent max_total_steps: 20 max_total_duration_sec: 1800 max_total_cost_usd: 2.0 model: planner_model: gpt-4o # 强模型规划、生成 summary_model: gpt-4o-mini # 弱模型摘要、规整 max_input_tokens: 32000 max_output_tokens: 4096 checkpoint: save_per_step: true storage: redis ttl_days: 7 context_health_warning: 0.8 loop_control: max_retries_per_step: 3 max_consecutive_similar_actions: 3 similarity_threshold: 0.85 circuit_breaker_max_errors: 5 circuit_breaker_cooldown_sec: 300 exponential_backoff_base_sec: 1 side_effects: require_state_check: true idempotency_key_header: X-Request-ID这套配置对应的是一个中型Agent任务最多20步、半小时内完成、成本上限2美金、上下文上限32K。实际项目里根据任务大小调整数字即可结构可以直接照搬。7.2 提示词侧的资源意识资源管控不只在工程层面提示词层面也有文章可做。在系统提示词里加一段执行纪律类提示能显著降低无效token消耗。我常用的措辞是你是任务执行Agent。每执行一步前先确认该步是否会推进任务目标。如果某工具连续出错两次停止重试改用其他可行方案。未知不确认的信息不要编造标注为待确认。这不是玄学机制上是在有限的上下文里给模型一个更明确的目标函数不要为了执行而执行而要为了推进目标而执行。配合前面说的活锁检测双管齐下循环失控的概率会明显下降。7.3 观察与指标资源管控的最后一环最后补充一个实践建议给Agent运行加观测指标至少要上报五类数据——每步耗时、每步token消耗、上下文占用率、重试次数、熔断触发次数。这些指标就是资源管控的反馈信号。没有它们你只能等故障发生再复盘有了它们你能在故障发生前就看到趋势。我最常用的方式是给每步执行打一条结构化日志包含步骤编号、工具名、耗时、token数、上下文占用率。跑个十几条任务之后用这些数据做个简单统计哪些环节token消耗最高、哪些步骤容易重试一目了然。后续优化就有数据依据不用靠感觉猜。这套运行机制整个串起来之后Agent才真正从实验室里的玩具变成生产环境里的工具。上下文管不好就烧钱检查点不做就丢进度恢复不校验副作用就出事故循环不控制就卡死资源不管控就失控——每一环都是基础设施级的功课值得认真对待。