ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

显式状态替代对话历史:SKILL.state 如何让 Agent 不“失忆”?

显式状态替代对话历史:SKILL.state 如何让 Agent 不“失忆”? Google 的 SKILL.state 研究为什么“显式状态”比“无限对话历史”更接近 Agent 的真相最近 Claude Code 的 Continuation 用法被大量讨论很多人都在搜“怎么保存对话历史”。另一个研究项目可能被低估了但它指向的问题其实更底层Agent 需要的不是保存更多上下文而是学会在不依赖历史的情况下把当前该做的事表达清楚。这就是 Google 的 SKILL.state 研究——以显式状态替代对话历史。在继续之前很清楚一点这不是要废除上下文也不意味着短期记忆没有用。它的核心改变是Agent 的“当前状态”不再寄存在对话历史这个隐式容器里而是被提取为一种显式的、可校验、可恢复的记录。这个研究适合谁正在做 Agent 工作流、开发复杂多步任务、或者被 LLM 上下文刷新搞到头大的人。对它的判断是如果要让 Agent 在多轮任务中不“失忆”显式状态基础设施比塞更多的历史 token 更可靠。本文会从状态为什么会丢、显式与隐式状态到底哪里不同、SKILL.state 带来哪些基本设计一直谈到这类系统在真实工程里如何落地存在的坑在哪里以及一个实际建议。1. 正在变得越来越贵的“上下文”1.1 表面问题Agent 会忘事在真实 Agent 系统里最常见的情况不是模型不够聪明而是做到第 5 步时它已经忘了第 1 步的目标。早期方案非常直觉把完整的对话历史全部塞给模型每一步都重新读一遍过去的每轮信息。效果看起来还行但成本呈线性甚至超线性上升。任务一旦复杂历史里就会混入大量执行噪音Agent 可能从第 3 步开始就已经分不清哪些历史内容是当前仍然生效的约束哪些只是过往的中间推导。Claude Code 里能通过续接对话来延续一个任务也是类似的逻辑在新的 Session 中导入上一步的所有历史消息让模型“接着干”。但同一个问题依然存在——历史越长模型提取当前有效状态就越吃力且每轮费用都跟着历史 token 在涨。1.2 这个问题为什么到了一个需要重新设计的节点当 Agent 只做单轮问答时上下文长短不是核心矛盾。一旦进入自动化工作流比如代码生成、数据处理流水线、带权限审批的多步操作Agent 会涉及工具调用、中间值、用户的临时反馈以及大量的日志片段。把这些都留在对话历史中会遇到三个具体瓶颈。第一是稳定性瓶颈。对话历史是线性增长的Agent 无法区分重要决策和临时输出一旦开头有一个无效内容被错误引用到后续步骤会影响整条链路。第二是成本瓶颈。每一步都重复发送全部历史回模型时间与费用都不可控。很多 Agent 应用在早期可行后期越来越慢很大原因就是历史太重。第三是恢复瓶颈。对话历史一般长在某个运行中的 Session 里Session 一旦断掉要重建状态就必须重新解析整段历史。对于一个执行了 20 步的复杂任务来说如果出现进程重启或网络中断从历史里“恢复现场”的难度很高。结论其实已经比较清楚上下文并不是越多越好关键是让 Agent 对“当前真正相关的状态”有一个明确的表达。这也是 SKILL.state 把研究重心从“上下文优化”挪到“状态管理”的原因。2. “状态”到底是什么理解 Agent 的短期任务记忆要理解 SKILL.state先要理解 Agent 系统中的状态。在传统软件工程里状态是一个程序或服务在某个时间点上保存下来的、能够影响未来计算的数据。对 Agent 系统来说状态包括三类东西目标状态当前任务最终要达成的结果。进度状态正在执行的计划到了哪一步已经完成的工作是什么。资源状态已经读到的文件、生成的代码、待处理的列表以及执行中的可用工具。在经典的单体程序中这些状态保存在内存变量或数据库里逻辑清晰可控。在 Agent 场景下状态长期以对话历史的形式存在于模型上下文里。模型要从一大段自然语言描述中自行判断当前状态这是容易出现偏差的。举一个日常例子。一个 Agent 被要求完成“在项目中新增用户注册接口包括 Controller、Service、Mapper 三层代码”。它很快完成了前两层但写到 Mapper 时因为网络问题中断了。如果重新启动一个新会话来继续它只能依靠人类手动把“已完成前两层、需要继续 Mapper”这个信息重新整理给模型。这就暴露出一个缺陷状态没有被结构化保存而是与各种历史噪音混在一起。SKILL.state 的切入点是与其让模型从历史中抽状态不如给 Agent 一个机制让它每一步都显式地记录“当前我在做什么、已经做到了什么、接下来要做什么”。为了理解它的意义可以做一次对比。普通对话历史就像一段视频信息完整但无法检索显式状态就像视频关键帧下方的结构化字幕它不需要完整还原每一秒只用简洁的字段告诉你当前发生的关键事件。这能减少模型的误导也让任务恢复更容易。3. SKILL.state 的关键设计显式状态如何接管 Agent 执行流程从研究介绍来看SKILL.state 并不只是一个新的提示词格式而是对 Agent 执行流程的一种重新设计方式。它把原本“模型内部隐式维持”的任务状态转化为跨步骤保存的、可读取和可验证的显式状态。3.1 基本执行单元一个典型的 SKILL.state 执行流程可以简化成以下循环Agent 读取显式状态文件。模型根据显式状态和当前输入生成下一步行为并附带新的状态更新。系统通过验证器检查新状态是否合规。验证通过后状态被持久化流程进入下一步。这个循环的重要之处在于模型不再需要从数千行对话历史中推断“当前进度”和“关键变量”而是在每一步的开始直接读取一个已更新的状态对象。过去Agent 的所有记忆压力都压在 LLM 的上下文窗口里采用显式状态替代后历史记录退居二线成为可查询的参考日志而不是唯一的决策依据。这种设计实际体现了“计算与记忆分离”的意图。对长期任务来说它是 Agent 工作流保持可控性的关键机制。3.2 状态是如何生成、读取与恢复的在引入显式状态之前一个任务的状态容易丢失。引入状态之后设计者需要回答三个问题状态从哪里生成如何被下一条指令读取任务中断后怎么恢复从逻辑上讲SKILL.state 的状态对象本质上是一段结构化数据可以是 JSON 或 YAML其内容由执行链路上的 Agent 生成也可以由系统在前一步完成后统一更新。读取代价远低于携带几千 token 的对话历史恢复逻辑也变成了直接加载最近的有效状态。可见这套机制和分布式任务调度中常见的“事件溯源”思路有相似之处但表达方式更贴近 LLM 的上下文窗口。4. 从对话历史到显式状态全局视角的比较4.1 为什么历史并不等于记忆很多人一想到“让 Agent 记住东西”第一时间就想到保留更长的对话历史。但从信息处理效率来看对话历史本质上是过程日志不是记忆。记忆意味着有取舍意味着数据的结构化程度更高意味着能够高效地重建。日志则保留过多不必要的过程细节它们在需要恢复“当前状态”时反而是噪音。显式状态的设计目标就是把“日志”和“有效记忆”分开。对话历史可以完整存档但在执行每个新步骤时Agent 只需要从显式状态中得到当前计划目标、已完成清单和最新关键参数而不必阅读所有 log。4.2 两者的核心差异这是一个值得反复对照的维度对比项对话历史显式状态数据形态自然语言消息流结构化字段与解释段长度增长随对话轮次持续膨胀基本保持动态稳定对 Token 的消耗高每轮都要重新发送较低只需传递有效状态信息噪声高模型需要自行过滤低系统已经做了过滤抗中断能力弱上下文丢失则任务中断强通过状态保存即可恢复人工介入难度需要在历史中翻找信息直接查看状态即可定位需要强调的是这并不意味着对话历史没有存在意义。它是一个重要的审计信息来源可以帮助复现问题和追溯行为。但在驱动 Agent 下一步行动这个功能上显式状态的效率确实高于原始历史。4.3 为什么“显式”两个字很重要在 LLM 系统中“显式”意味着让需要被判断的信息更直接地暴露给模型或系统。一个状态如果只存在于模型的隐层激活里就无法被检查和修正一旦它被改为显式文本或显式字段系统就有机会在进入下一步之前验证它的正确性。以人工操作来对比会更直观。两个工程师协作A 负责写接口B 负责写文档。如果 A 只在聊天中说过“这个接口我差不多快写完了”B 无法有效判断进度。等到 A 明确写出“已完成 Controller 和 Service还剩 Mapper 实现”后B 的工作才能开始。SKILL.state 也是这个原理它强制 Agent 在内部自己“做一次接口汇报”把当前进度和下一步计划结构化地写下来。这就是显式状态替代对话历史时真正的价值所在。5. 一个可落地的技术视角状态应如何存储与恢复虽然目前还不能直接通过官方代码下载运行 SKILL.state但从研究的设计思路来看它和很多 Agent 框架中的 Message Memory、长期状态持久化方案是相通的。这部分从工程视角给出一种通用实现方式供开发者在自己的 Agent 应用里参考。5.1 定义状态结构一份好的 Agent 状态可以分为四段任务目标、执行进度、新产生的关键输出、下一步计划。可以使用 JSON 统一保存例如下面的示例状态文件{ task_id: register-api-user, goal: 在项目中新增用户注册接口包括 Controller、Service、Mapper, current_step: 2, steps_done: [创建项目结构, 设计 User 实体与 Mapper 接口], current_output: { api_design: POST /api/user/register, field_check: email,password, nickname 已完成非空校验, pending_db_op: user 表插入语句待验证 }, next_plan: [实现 Service 层事务逻辑, 编写 Controller 入口, 补充集成测试] }在执行新的生成步骤前模型只需要读取这段结构即可获得任务全貌。每次状态更新时都应在事务性输出中同步写入新的 JSON 文件从而让状态文件本身成为可回滚的检查点。5.2 用 Python 维护一个最小状态管理器这里给出一个简化版实现重点在展示思路不是官方代码。它把“读取状态、更新状态、保存状态”做成了三个基础方法# 文件路径agent_state.py import json import os from datetime import datetime class AgentStateStore: def __init__(self, state_fileagent_state.json): self.state_file state_file self.state self._load() def _load(self): if os.path.exists(self.state_file): with open(self.state_file, r, encodingutf-8) as f: return json.load(f) return {} def get_state(self): return self.state def update_state(self, new_fields: dict): new_fields[updated_at] datetime.now().isoformat() self.state.update(new_fields) self._persist() def _persist(self): tmp_file self.state_file .tmp with open(tmp_file, w, encodingutf-8) as f: json.dump(self.state, f, ensure_asciiFalse, indent2) os.replace(tmp_file, self.state_file) def recover(self): return {k: v for k, v in self.state.items() if k ! updated_at}这个最小管理器里recover()方法可被用于恢复现场。关键点是先写临时文件再os.replace避免进程中断导致状态文件被截断这对应了状态持久化中较稳的实践。5.3 在 Prompt 中注入显式状态并完成恢复作为工程人员可以这样使用这个状态文件每执行一个新步骤时先把状态 JSON 注入到系统提示或工具输出中让模型在当前上下文中看到结构化状态而不是依赖旧的对话历史。# 文件路径agent_loop.py import json from agent_state import AgentStateStore def build_prompt_with_state(user_instruction: str, state: dict) - str: state_block json.dumps(state, ensure_asciiFalse, indent2) prompt ( 你是任务执行助手。在开始前请先阅读当前状态。\n 当前状态如下\n f{state_block}\n\n 用户新增指令\n f{user_instruction}\n\n 请基于状态决定下一步行动。 任务完成后请用 JSON 返回新一轮状态字段包括 steps_done, current_output, next_plan。 ) return prompt def run_agent_step(user_instruction: str, store: AgentStateStore): current store.get_state() prompt build_prompt_with_state(user_instruction, current) # 这里替换为实际模型调用 llm_response mock_llm_call(prompt) parsed_state parse_llm_state(llm_response) store.update_state(parsed_state) return parsed_state def mock_llm_call(prompt: str) - str: return json.dumps({ steps_done: [实现 Service 层事务逻辑], current_output: {service_file: UserService.java 已完成}, next_plan: [编写 Controller, 补充集成测试] }, ensure_asciiFalse) def parse_llm_state(response_text: str) - dict: return json.loads(response_text)以上的关键设计在于build_prompt_with_state不再直接把整个对话历史塞进上下文而是用一块结构化的状态作为上下文的核心。用户新增指令会被叠加在状态之上这样模型就不必同时消化几十轮旧消息只需处理“当前状态 当前指令”。5.4 执行与恢复验证当状态更新后可以运行简单脚本验证python agent_loop.py同时检查agent_state.json是否已经被更新。如果在第 N 步执行时进程崩溃只需要重新启动进程并调用recover()方法状态管理器就会从持久化文件中载入最近一次成功更新后的任务信息。这种恢复方式远比让用户在崩溃后手动重新粘贴所有历史更可靠。6. 显式状态是给模型减负吗隐藏的代价要把显式状态引入真实 Agent需要付出额外的设计成本。并不是每个场景都适合用它替代对话历史。6.1 强项在哪里显式状态最适合复杂多步任务任务步骤多需要的记忆点分散。长周期任务任务可能持续数天模型上下文窗口无法覆盖整个周期。可审计场景需要准确知道 Agent 每一步执行了什么操作。高失败恢复需求场景Agent 可能在执行途中遭遇 API 超时、进程重启等异常。多人协作场景多个人工审核者需要对同一 Agent 的进度有明确把握。如果任务保持在单轮问答或简单工具调用比如“今天天气怎么样”“把这段代码改成 Java 风格”显式状态带来的建模成本可能高于收益。对于这类任务原生对话历史的方案够用。6.2 潜在的实施风险引入显式状态后需要警惕几类问题。状态与模型输出不一致是最大的风险来源。模型声称已经完成了 Mapper但实际并没有生成对应文件。如果系统只以状态字段为准就可能提前执行下游任务。因此需要在状态持久化之前加入验证器比如检查文件是否存在、测试是否通过、返回的 JSON 是否符合 schema。另一个风险是状态文件本身也会成为新的瓶颈。如果状态设计得过于复杂包含全部中间日志Token 优化效果就会打折扣状态恢复的可读性也下降。状态必须维持“足够表达当前进度但不承载全部过程信息”的平衡。第三个风险是状态覆盖问题。在多 Agent 并行或人类手动修改任务进度时状态文件可能被并发覆盖。工程上建议引入版本号或基于版本号的乐观锁commit 时校验版本是否一致不一致就说明有其他流程改写过任务状态。这类并发控制对于带显式状态的 Agent 工作流非常重要。7. 常见问题与排查思路围绕“显式状态替代对话历史”开发中比较常见的问题集中在状态丢失、状态不一致、新会话无法继续任务这几类。这里整理成表格便于查找。问题现象可能原因排查方式解决方案新会话无法理解已有任务进度新会话仍只读取对话历史未加载显式状态检查启动逻辑是否调用了状态加载接口或读取状态文件将状态 JSON 显式注入到初始 PromptAgent 声称完成步骤但代码或文件缺失状态由模型单方面生成未经系统验证对比状态中的完成项与真实产物在状态写入前增加验证器确认文件或测试结果状态文件在进程中断后损坏采用直接覆盖写入未使用临时文件原子替换检查文件内容是否截断改为写临时文件后os.replace()多 Agent 并行导致状态互相覆盖缺少版本控制检查状态文件中的版本号或更新时间引入乐观锁回写前校验版本一致显式状态仍占用过多 Token状态里塞入过多历史细节或日志片段查看最终注入 Prompt 的状态体量对状态做裁剪只保留当前有效字段任务跨天恢复后状态过期外部环境已变化状态中的依赖信息未刷新运行恢复时检查任务相关时间戳在状态中加入敏感依赖项的失效标记必要时触发重新感知实际排查时建议遵循“先确认状态文件内容再确认 Prompt 注入逻辑最后确认工具执行结果”的顺序。一个可见且可读的状态文件本身就是问题定位的切入点。8. 最佳实践与工程建议如果准备在自己的 Agent 项目里落地显式状态方案下面这些实践值得借鉴。8.1 状态分层不要所有信息混在一处可以把状态分成三层第一层是用户目标层来自人工设定或系统入口在任务执行期间基本不变第二层是执行进度层由 Agent 每一步更新第三层是验证结果层存放测试通过、文件生成、API 调用成功等可查验的记录。每次新建任务时只重置第二层第三层逐次追加第一层保持不变。分层的好处是即使后续恢复只需要恢复进度层和验证结果层不会被旧目标信息干扰。8.2 重要状态变更执行前做合法性与权限确认状态一旦被持久化它就会被后续步骤信任。如果 Agent 具备写文件、改数据库、注册云资源等能力状态写入本身不应该直接作为动作触发的唯一凭据。工作流里应设置人工审批门禁或系统规则门禁例如“只有在测试通过前的状态不允许触发部署类动作”。权限最小化在这里极其重要不要让 Agent 在恢复状态时自动执行所有旧计划中的高风险操作除非用户在本次启动中明确同意。8.3 不要完全丢掉对话历史按这套思路对话历史更适合被当作“审计日志”而不是系统操作的主记忆。建议保留一条低成本日志流将每一步的原始 LLM 输出、工具调用和状态变更原样记录下来。这样一旦显式状态出现不一致可以在日志流中回溯是哪一步导致的。实现时可以只保留 JSONL 或统一格式文本不必对它做复杂的索引排序。8.4 状态 schema 要带上版本随着任务功能迭代状态里的字段必然演进。给状态 schema 加一个版本字段例如state_version: 2在读取时先匹配版本再进行字段迁移。这个做法是老式数据库版本迁移在 Agent 场景里的直接应用成本很低却能在长期任务中大幅减少兼容性灾难。8.5 使用“最小上下文”验证 Agent 恢复能力在开发阶段可以人为制造一次中断在任务进行到第 5 步时停止进程然后新开会话只加载状态文件不加载原对话历史观察 Agent 能否继续完成。如果每次都能恢复到正确位置说明状态设计有效如果恢复失败再审视状态字段是否足以支撑下一步决策。9. 这个趋势下开发者现在可以做什么要说得很直接SKILL.state 这种显式状态思路短期内不会取代大模型本身也不代表对话历史被全面废弃。它是在提醒所有 Agent 开发者一件事——当 Agent 开始处理真实业务任务时必须要从“聊天机器人的记忆方式”转向“软件系统的状态管理方式”。对话历史天然适合聊天但真实任务需要检查点、事务、恢复与审计。显式状态是把这些工程概念引入 Agent 的核心载体。Claude Code 等工具的对话保存能力解决的是“我想让聊天上下文延续”而 SKILL.state 代表的思路解决的是“我想准确知道任务做到哪一步下一步应该做什么以及怎么让新会话无缝接上”。对于读者下一步可以实践三件事在现有 Agent 工作流中加入一个简单状态管理器用 JSON 承载任务目标与进度。把关键步骤的 LLM 输出转成结构化状态后再写入持久化文件而不是直接依赖原始消息流。人为中断任务再恢复一次测一测你的 Agent 能否在不依赖旧历史的前提下继续干活。如果这三件事能跑通你对“上下文”的理解就已经超过了大多数只靠加长 Prompt 调模型的人。本轮研究的价值正在此处它提供了一个关于 Agent 记忆架构的重要转向依据。显式状态值得成为你后续搭建复杂 Agent 时的默认基础设施。
返回列表