SKILL.state:用显式执行状态替代对话历史,治理Agent上下文膨胀 在基于大语言模型构建智能体Agent时一个越用越明显的问题是对话历史会不断膨胀。用户指令、工具调用、中间推理、环境返回结果、异常重试记录全部被追加进上下文导致上下文越滚越长请求越来越慢成本越来越高模型的有效决策能力反而下降。Google 新论文提出的 SKILL.state核心方向就是用显式执行状态替代不断增长的智能体对话历史。这篇文章不打算复述论文里的公式和实验细节而是从工程角度拆解这个思路对话历史为什么会成为瓶颈显式执行状态到底需要存什么状态化改造在落地时有哪些坑以及它给后续智能体架构设计带来的启示。1. 对话历史为什么会成为智能体系统的瓶颈要理解 SKILL.state 的价值先要搞清楚对话历史在智能体系统里扮演了什么角色又是如何从“帮手”变成“负担”的。1.1 对话历史里究竟积累了什么在多轮智能体任务里所谓对话历史并不是只有用户和助手的聊天文本。一次普通的任务执行历史中会包含下面几类内容用户原始指令以及后续补充、纠正、澄清的语句。助手在每轮生成的中间推理也就是常说的 CoT 内容。工具调用的请求参数例如检索请求、SQL 查询、API 请求体。工具返回的结果例如查询结果、接口响应、错误码、超时提示。遇到异常后的重试过程包括重试次数、失败原因、降级方案。模型自己标记的提醒、待办约束、尚未完成的目标。这些内容全部拼接进下一轮请求的 prompt 中。表面上看历史越完整模型越能理解上下文。但实际的系统行为是历史越长每轮请求都要重新处理这些 token而真正对当前决策有用的信息可能只有一小部分。1.2 历史无限增长带来的三个直接问题可以把对话历史无限增长看成一种“线性放大”的副作用它会同时影响成本、延迟和效果。问题成因典型表现token 成本线性上升每轮请求都携带全部历史任务进行到第 20 轮时单轮输入 token 可能是第 1 轮的十几倍响应延迟上升输入序列变长预填充时间增加首 token 延迟明显变大用户等待时间增长决策质量下降模型需要在超长上下文中定位关键信息出现忽略早期约束、重复调用工具、忘记用户已确认信息等情况其中决策质量下降最容易被忽略。很多人以为“上下文越长模型记住的越多”但长上下文场景中模型对中段内容的关注度会下降关键约束一旦被淹没在工具返回片段里后续决策就会跑偏。1.3 为什么不能简单靠裁剪来解决问题既然历史太长有问题那直接把历史截断不就行了吗实践里几种常见做法都有明显代价直接截断把最早的内容丢弃但早期通常包含用户的核心目标和约束一旦丢失后续任务方向就可能错。摘要压缩用 LLM 把历史压缩成摘要摘要本身有信息损失而且摘要也是 token压缩到一定程度后边际收益有限。相关性检索只保留与当前轮相关的片段但“相关”的判断本身依赖语义检索质量跨多步的因果依赖很难被完整召回。这些方案本质上还是在“换一种格式保存历史”没有改变“所有上下文都堆在上下文窗口里”的结构。SKILL.state 的意义在于它试图把问题从“怎么压缩历史”变成“历史里到底哪些内容需要继续存在”。注意不是所有历史都该被丢弃而是要把“执行过程记录”和“当前任务状态”分开。执行过程记录可以归档当前任务状态必须长期可用。2. SKILL.state 的思路把“上下文”拆成“状态”SKILL.state 面向的场景是长周期、多步骤的智能体任务。论文提出的核心方向是用显式执行状态来替代不断增长的对话历史。下面从工程角度理解这个设计。2.1 从“重放历史”到“读取状态”传统多轮智能体的工作方式类似于“每次重新读一遍完整日志来推断现在进行到哪里”。模型需要在全部历史里推断出用户要什么、已经做到哪一步、还差什么、有哪些约束。这种方式在短对话里没问题但在长任务里就是一个不断累积的推理负担。显式执行状态的思路是系统维护一个结构化的“当前任务快照”每轮决策只需要读取状态而不是重放全部历史。这个过程非常像数据库里的“当前值”和“binlog 日志”的关系。日志负责追溯状态负责当前。智能体可以保存完整的执行日志用于审计和排查但每次请求只把当前状态注入上下文。换句话说历史回答的是“之前发生了什么”状态回答的是“现在处于什么位置、下一步该做什么”。后者才是模型每轮决策真正需要的基础。2.2 显式执行状态应该包含哪些信息一个能被模型直接使用的执行状态至少要覆盖以下几个维度状态维度说明示例任务目标用户最终要达成的结果提交一笔退款申请并通知用户已完成步骤已经执行成功的动作订单查询完成、退款原因已确认当前步骤正在进行的动作调用退款接口关键变量流程中产生的结构化数据订单号、退款金额、审批人约束条件必须遵守的规则退款金额不能超过订单实付金额待决策项需要用户确认或模型选择的分支退款方式原路退回或账户余额异常信息最近一次失败原因和重试策略接口超时已重试 2 次最多 3 次这些字段在设计上应该尽量结构化而不是一段长文本。结构化状态的好处是可以校验、可以版本化、可以映射到业务字段、可以被外部系统读取。2.3 它和记忆、缓存、工作流的区别状态化改造经常被拿来和记忆系统、工作流引擎、状态机做对比需要分清楚边界。长期记忆负责“跨任务的知识沉淀”比如用户偏好、领域知识它不描述当前任务进度。RAG 缓存负责“检索效率”它解决的是知识召回速度不是任务执行位置。工作流引擎负责“流程编排”它有定义好的节点和跳转条件但让 LLM 动态决定下一步时需要更灵活的状态模型。显式执行状态介于两者之间它是结构化、可持久化、面向当前任务的数据模型同时允许模型基于状态自主决定下一步动作。这里最容易误解的地方是显式状态不是要替代工作流引擎而是为“LLM 动态决策”这条路径提供更可靠的上下文输入。如果流程完全固定用工作流引擎就够了如果流程需要模型根据实际情况判断显式状态就比无限历史更合适。3. 用一个最小示例理解状态化智能体下面用一个订单处理场景对比“对话历史驱动”和“显式状态驱动”两种写法的差异。示例只用于说明思路实际项目要结合自己的字段和流程调整。3.1 场景设定一个订单退款智能体假设智能体要完成这样一件事用户提交退款请求智能体需要查询订单、核对金额、确认退款方式最后调用退款接口。整个过程可能涉及 5 到 8 轮工具调用期间用户还会补充信息。不采用状态化设计时第 6 轮请求的 prompt 会把前 5 轮的推理、工具返回、用户补充全部带上。模型每次都要从这些内容里重新推断“当前进行到哪一步”。3.2 对话历史写法 vs 显式状态写法对话历史写法下的 prompt 大致是用户我要退掉昨天买的那个订单。 助手请问订单号是多少 用户订单号是 20250601A。 助手好的正在查询订单信息...[工具调用] 工具返回订单金额 199.00 元状态为已发货。 助手当前订单已发货退款需要确认退款方式。请问您是原路退回还是退到账户余额 用户原路退回吧。到了下一轮模型需要从上面全部内容里提取订单号、金额、退款方式、当前动作是调用退款接口。这个提取过程在短上下文中没有压力但一旦前面混入多次查询失败、工具超时、用户澄清提取成本就会快速上升。显式状态写法下系统在每轮结束后维护一个结构化状态{ task_id: task_20250601_001, goal: 完成订单 20250601A 的退款申请, completed_steps: [ 查询订单, 确认退款金额, 确认退款方式 ], current_step: 调用退款接口, variables: { order_id: 20250601A, order_amount: 199.00, refund_method: original_channel }, constraints: [ 退款金额不能超过 199.00, 退款前需要再次核对订单状态为已发货 ], pending_decisions: [], error_info: null }下一轮请求只需要把这份 JSON 注入 prompt配合系统提示词描述“当前任务状态如下请根据状态执行下一步”模型就能直接基于状态决策而不需要重读所有历史。3.3 状态更新与恢复流程状态化智能体在工程上可以抽象为一个简单循环state load_state(task_id) while not state.is_finished(): action model.decide(prompt_system, state.prompt_view(), current_user_input) if action.type call_tool: result execute_tool(action.tool_name, action.params) state.record_tool_call(action, result) state state_transition(state, action, result) save_state(task_id, state) elif action.type ask_user: state.record_pending_question(action.question) save_state(task_id, state) break elif action.type finish: state.mark_finished(action.summary) save_state(task_id, state) break这里的关键点有三个每次工具调用完成后立刻更新状态并持久化避免进程崩溃后任务无法恢复。模型读取的是state.prompt_view()生成的精简文本而不是原始历史。完整的执行记录仍然写入日志只是不再进入 prompt。这样一个结构的好处是即使任务执行到一半服务重启恢复时只需要读取task_id对应的状态 JSON就能继续执行。对用户来说任务没有因为进程重启而丢失。注意状态保存频率需要权衡。每轮都保存会带来持久化开销但能显著提升恢复能力。生产环境至少要保证“每次工具调用之后”这个粒度。4. 状态化改造在工程落地时会遇到的难点把对话历史换成显式状态思路清晰但落地时会有不少工程问题。这些问题不解决状态化就只是概念上好看。4.1 状态粒度怎么定状态字段设计得过粗比如整个状态只存一段自然语言描述那状态就退化成“简化版摘要”信息损失和摘要方案没有本质区别。状态字段设计得过细比如把工具返回的每个字段都映射到状态里模型每轮都要处理大量无关字段而且 schema 变更频繁。实践中建议按“决策所需最小集”来确定粒度模型下一步决策需要哪些字段状态里就维护哪些字段。工具返回里与决策无关的内容不进入状态只进入归档日志。状态粒度优点风险过粗实现简单schema 稳定信息损失大模型仍需要从长文本里提取过细信息完整可精确映射业务字段schema 频繁变更更新逻辑复杂按决策最小集每轮真正需要的信息都在状态里需要持续评估字段是否仍然被使用一个常见做法是给状态增加raw_output和parsed_state两层结构。raw_output保存工具返回原文用于审计parsed_state只保存结构化后的关键字段用于决策。4.2 状态一致性怎么保证多轮任务可能涉及并发更新。比如用户在一个会话里同时发起两个操作或者异步工具回调先后返回这时候如果只是简单的“读取当前状态 - 修改 - 写回”容易出现覆盖问题。典型错误是后返回的旧结果覆盖先返回的新状态。解决方案包括给状态增加 version 字段每次更新前比较 version不匹配则拒绝写入。把状态更新设计成事件溯源模式只追加状态变更事件再通过事件投影生成当前状态。对同一个 task_id 的状态更新加锁保证同一时间只有一个协程在更新。生产环境中状态存储尽量选择支持原子更新的数据库或内存存储不要在应用层做“读改写”而不加任何并发控制。4.3 模型输出不稳定导致状态结构被破坏状态化方案有一个新问题状态更新的一部分由模型生成。模型输出 JSON 时偶尔会出现字段名漂移、多出字段、值类型错误。如果不加校验错误数据会进入后续决策形成错误的“事实基础”。必须在状态写入前增加一层校验{ task_id: task_20250601_001, status: invalid, errors: [ { field: refund_method, expected: original_channel, actual: original_channel_2 } ] }校验规则可以分成硬校验和软校验硬校验字段缺失、类型错误、枚举值不存在必须拒绝写入并触发模型重新生成。软校验字段值看起来可疑但无法直接判断错误例如退款金额接近订单上限可以标记 warning 并让后续步骤复核。缺少校验层的状态化系统表面上运行正常实际上状态里可能早已积累了模型幻觉造成的错误字段越到后期越难排查。5. 从 SKILL.state 看下一代智能体架构的工程方向SKILL.state 不只解决“上下文太长”这一个点它背后是一种架构分层思想上下文、状态、记忆分离开来各管一段。5.1 状态与历史分层推荐的分层结构是短时上下文只放当前轮的用户输入和模型输出用于生成本次决策。显式执行状态放当前任务的结构化进度、变量、约束跨轮持久化。执行日志放完整历史、工具返回、错误信息用于审计和排查不进入 prompt。长期记忆放跨任务的知识和用户偏好按需检索注入。这种分层的好处是每一层都有独立的生命管理策略。短时上下文可以随轮次清空执行状态按任务生命周期管理执行日志按保留策略归档长期记忆按相关性检索。整个系统不再是一根无限增长的上下文链条。5.2 生产环境落地建议如果团队准备在现有智能体项目里尝试状态化改造可以按下面这个清单推进明确任务的边界和生命周期确定状态从创建到销毁的完整流程。设计状态 schema 时先列出模型每轮决策需要的字段而不是把历史全部塞进去。为状态增加版本号避免并发更新覆盖。模型生成的状态更新必须经过硬校验校验失败时触发重试。状态持久化与工具调用结果写入放在同一事务边界内。完整执行日志保留在独立存储中prompt 里不再放入完整历史。加入状态快照和 diff 日志出现偏题时可以通过 diff 快速定位是哪一轮更新导致的问题。为状态恢复能力做演练模拟进程崩溃验证任务能否从最近一次持久化状态恢复。生产环境还要考虑状态字段的敏感信息脱敏避免订单号、手机号等信息进入日志。5.3 适合进一步验证的实践路径对大多数团队来说不需要一次性改造全部智能体。更稳妥的做法是选择一个有明确步骤边界、步骤数较多的任务作为试点比如工单处理、审批流、多轮信息收集。先写清楚状态 schema再让模型基于状态做决策持续观察状态更新质量和任务完成率。如果试点中频繁出现“状态字段缺失导致模型重新询问用户”“状态里关键变量错误导致后续操作失败”说明问题不在状态化本身而在状态 schema 设计和校验规则还不够完善。这时候应该回到 4.1 和 4.3 提到的粒度设计和校验层先把状态质量提上来。注意状态化改造不是要完全抛弃对话历史。历史仍然需要只是角色变了——从“每轮都被重复阅读的上下文”变成“仅在纠纷排查和审计时被查询的日志”。这个角色转换才是 SKILL.state 真正值得借鉴的地方。对于正在做智能体开发的团队现在就可以从一个小任务开始把“历史重放”改成“状态读取”。这个改动带来的不只是 token 成本下降更重要的是让长任务的执行过程变得可复现、可恢复、可审计这可能是比省 token 更重要的长期价值。