尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SKILL.state解读:用显式任务状态替代完整对话历史,提升AI Agent长任务稳定性

SKILL.state解读:用显式任务状态替代完整对话历史,提升AI Agent长任务稳定性 SKILL.state 这个名字最近在做 AI Agent、多步任务自动化或者对话式智能体的开发者应该会注意到。它来自 Google 的一个新研究方向核心主张很直接把过去依赖“完整对话历史”来做上下文理解的做法换成一整套显式维护的任务状态。什么意思就是别让模型每一轮都去翻聊天记录猜“现在到底进行到哪一步”而是给任务准备一个结构化、可读写、能更新的状态记录本让模型看当前状态就能继续干活。这个改动听起来像一个工程小优化但它直接关系到长任务稳定性、token 成本、并发恢复和排错难度。这篇文章不打算把 SKILL.state 包装成什么颠覆性突破而是想把它拆成可以理解、可以验证、可以落到自己项目里的思路。适合下面几类人看正在折腾 Agent 应用的开发者想搞清楚上下文窗口不够用怎么办的技术博主以及做自动化流程设计、又不希望每次跑长任务都把聊天历史全部塞给模型的工程师。下面按我自己的理解从问题背景、状态设计、对照实验、批量落地、效果判断和踩坑顺序拆一遍。1. 先弄清楚 SKILL.state 回应的问题对话历史越长问题越多1.1 对话历史不是越多越好传统多步 Agent 的做法是把整个任务过程都保存成对话消息。模型每做一步都需要读取之前所有的用户输入、工具返回、中间结果、错误信息。短任务这样没问题可一旦任务超过十几个步骤麻烦是逐个冒出来的。首先是上下文窗口压力。模型输入长度有限就算支持很长上下文塞进去的内容越多推理成本越高响应延迟也越高。更麻烦的是并不是每一条历史都有用。早期用户说过的一个要求可能和后面 20 轮对话没什么关系但模型仍然要读完才能判断。其次是信息稀释。一个任务做到一半真正关键的信息可能只是“已经处理到第几个文件”“用户最后确认过某个参数”“某个接口已经调用成功”。这些信息被淹没在大段日志、中间输出和无关讨论里之后模型反而容易抓错重点。再者是自我矛盾。模型基于嘈杂的上下文做判断经常前后不一致。它在第 5 步选的方案到第 15 步时如果上下文里相似信息太多可能就忘了当初为什么选 A 而不是选 B最后给出另一个方向。那种“明明信息都在历史里模型还是搞错”的情况干过这类项目的人应该不陌生。最后是排错困难。任务失败后你要去翻上百条历史消息判断模型是在哪一步开始偏离任务的。如果系统崩溃或者任务需要重跑要么重新把完整历史交给模型要么只能从第一步全部重来。这种恢复成本在生产环境里很难接受。1.2 显式状态为什么更适合长任务SKILL.state 的改进思路是把“模型靠读历史来回忆状态”改成“系统主动维护一份状态数据”。模型每一步只读当前状态和当前输入不需要把全部历史再读一遍。这份状态不是自然语言的简单摘要而是结构化字段比如当前步骤编号、已完成事项、待处理事项、关键变量、临时文件路径、重试次数、错误信息。模型知道该做什么不再需要从历史正文里反向推理。这种方案有一个额外好处状态可以被程序检查、修改、备份和恢复。任何一步状态不对人工可以直接改动字段再让模型继续跑。它天然适配任务队列、断点续跑、批量执行和并发控制。我不太愿意把它理解成“压缩上下文”更准确的说法是“把任务记忆从模型内部挪到系统外部”。不过也要提醒一句显式状态不是银弹。它把“模型记不住”问题变成了“系统状态设计得好不好”问题。状态字段如果设计得糟糕漏了关键信息模型照样会跑偏。所以后续真正要花精力的是状态的结构和更新时机而不是简单把历史删掉。2. 显式状态不是简单摘要关键在状态结构和更新规则2.1 先区分三种上下文承载方式很多人在第一次接触显式状态时第一反应是“这不就是给对话历史做摘要吗”。摘要和显式状态看起来都是减少历史量但底层逻辑差异很大。承载方式内容形态更新方式稳定性输入 token 成本完整对话历史全部消息原样保留追加随长度下降最高对话摘要裁剪自然语言小结每次重新生成依赖摘要质量中等显式任务状态结构化变量、步骤编号、产物状态按规则更新字段可控低且稳定对话摘要裁剪的问题是它仍然是自然语言仍然要模型去理解“这段话到底覆盖了哪些业务约束”。一次两次生成摘要可以任务拉到很长以后摘要也会越写越长还会出现丢失细节、前后矛盾、重复概括的问题。显式状态则倾向于用变量和结构化字段表达比如 current_step、task_status、output_path、last_error。程序可以直接读取和判断模型只需要解释当前字段代表的含义不需要去自然语言里挖信息。根据我这段时间做多步任务的经验适合摘要裁剪的场景其实有限。只有模型本身只依赖语义灵活性、对精确状态判断要求不高的任务摘要才够用。一旦任务涉及文件路径、API 调用、数值计算、分支判断显式状态会可靠得多。2.2 显式状态要从每一步的结果抽取设计状态时我建议至少把状态分成几类不要全揉在一起。静态任务信息任务目标、输入文件地址、输出目录、最终验收条件。这些字段任务开始前写好过程中基本不变。动态执行状态当前进行到第几步哪几步已完成等待哪个外部结果失败了几次。这些字段是任务推进的节拍器。业务变量根据具体任务不同而变化比如正在处理哪个文件、当前选用的模型参数、已经生成的临时结果。这些字段是最容易被塞进历史里、又最容易丢失的部分。决策记录在执行到分支点时为什么选择这个路径。不一定要保存完整讨论过程但至少要把决策结论和理由摘要保存到一个结构里。更新时间或规则也值得重视。比较朴素的方案是每执行一步都整体覆盖 state 文件更稳一点的做法是记录一个 version 字段更新时做乐观锁校验。对并发跑批量的场景这一步后面会专门展开。3. 想动手验证这个思路环境条件和实验怎么搭3.1 准备什么环境SKILL.state 目前更像一个方向性研究概念如果你能找到官方源码可以按照官方文档跑。如果只是想在业务里验证原理不一定非要找到对应开源项目。你可以自己搭一个小型对照实验。环境层面要准备的东西并不是很多。如果你用模型 API需要有密钥和调用额度如果本地跑开源模型需要提前确认显存、内存和依赖版本。不管哪种最好准备一个能保存中间结果的目录和一个能记录日志的路径。这里有一个容易忽略的地方对实验来说更重要的是任务样本本身而不是模型。任务要具备三个特征足够多步骤、有明确中间产物、能判断结果好坏。比如“把一份数据清单按规则拆分逐项调用接口生成说明再汇总成表格”就比“帮我写一段代码”更适合做验证。3.2 设计一个最小对照实验最朴素的做法是同一任务跑三组方案A 组完整历史B 组摘要裁剪C 组显式状态。每组跑两三次统计结果。我给一个简化流程A 组每轮请求把历史全部拼进去。B 组每轮请求携带最近一轮消息和一轮自动摘要。C 组每轮请求只携带显式状态和当前输入。C 组可以先用一种很直观的伪代码结构去表达# 伪代码仅用于说明结构不是现成可运行的完整实现 state load_state(task_id) if state is None: state initial_state_for(task) while not is_done(state): step_input build_step_input(current_request, state) action model.predict(step_input) result execute_action(action) state update_state(state, action, result) save_state(task_id, state)这个结构的关键点在于save_state 不是把聊天记录整体存一下而是更新结构化字段。execute_action 可能是调用工具、写文件、发请求也可能只是继续生成下一步文本。任务从“循环读历史”变成了“读状态、执行、更新状态、保存”。实验过程里要记录的内容包括任务是否完成、完成到第几步开始出错、每轮输入 token 总量、出错的步骤能不能快速定位、人工改正次数。如果 C 组效果比 A 组还差通常不是方向有问题而是状态里的关键字段没归纳好或者更新状态时把历史顺序依赖丢了。3.3 怎么判断实验结果算成功C 组在短任务上不一定有优势。我甚至建议别拿三步以内的任务做这种实验因为历史很短的场景下维护状态反而像多余动作。真正有区分度的是 15 步以上、过程中有临时结果和分支取舍的任务。比较合理的成功标准是在任务完成率不下降的前提下C 组的输入 token 低于 A 组出错步骤比更清晰比如能直接说出“第 7 步状态里 expected_input 字段是空的”而不是“模型可能在某一步理解错了某人说的话”。如果你的任务需要模型长期记住早期用户偏好显式状态设计时要单独建一个 constraints 字段把用户表态放进去否则很可能会在状态替换后丢掉这个信息。我在实验里遇到过类似问题当时以为是状态方案不稳后来才发现是“全局约束”没有进入状态属于状态建模缺失。4. 从单任务到批量队列状态字段和持久化设计4.1 一个可用状态字段的基础清单如果你已经决定用一个显式状态替代部分历史逻辑从最简单的任务开始我建议至少包含以下字段字段示例作用task_idorder-2025-0101-001任务唯一标识批量恢复时依赖它step_index7当前执行到第几步避免重跑全部statusrunning / failed / done任务当前生命周期input_snapshotdata/input.json原始输入路径便于定位constraints“不能删除临时目录”来自用户或任务的全局约束variablesfile_name, retry_count当前业务运行变量artifactsoutput/report_001.md已生成产物的路径列表decision_log第3步选A方案原因…关键分支的轻量记录updated_at2025-05-05T12:00:00Z并发冲突时判断新旧这些字段不需要一步到位。如果你正在跑单任务只用 task_id、step_index、status、variables 和 output 就能跑通。批量化和并发恢复是在这个基础上逐步添加的。4.2 从单任务升到批量任务状态存储和并发是关键单任务场景下状态文件存本地 JSON 都行。多任务并行、多 worker 处理时“多进程同时更新同一份状态”就会变成问题。常见做法是给状态加版本号更新前先校验状态没有被人改过。没有这一步容易出现两个 worker 各跑一部分后写的一方覆盖先写的一方最后任务状态停留在错误阶段。批量任务还要想好输出命名。如果你接了一批任务所有结果都输出到同一个文件很容易互相覆盖。比较稳的是按目录层级组织结果根目录/任务 ID/具体产物。这样单个任务失败重跑时不会影响其他任务。队列层面也不要急着开高并发。显式状态确实会把单次输入 token 压下来但不代表程序可以无限并发。你需要关注模型 API 的限流、下游接口的承受能力、磁盘读写能力。建议先把并发数从 1 调成 2、4 逐步试观察成功率再决定上限。我见过很多项目在 1 并发时没问题一开 8 并发就反复失败原因基本都在外部接口和超时设置而不是模型本身。如果任务需要从失败中恢复状态设计里最好明确“支持从 failed step 继续”而不是整体重置。做法是记录当前失败发生在第几步保存当时输入和产出下次重试时直接从该步的输入状态开始。类似断点续跑可以大幅减少长任务的重复成本。5. 不要只看跑不跑得通效果要从四个维度判断5.1 稳定性、质量、成本和可维护性很多初稿实验只写一句“显式状态方案跑通了”这对实际工程没有意义。跑通一次和能持续产出是两个概念。我建议从四个维度记录稳定性连续跑同样任务多少次能完整完成中途必须人工介入多少次状态是否有损坏、覆盖、丢失。数量上至少跑 5 次以上取一个大致成功率而不是只看一次结果。质量任务输出是否完整是否和阅读完整历史的方案在验收标准上一致是否丢失早期约束。判断质量时要有一个明确验收清单比如“报告必须包含数据来源、统计口径、结论建议”。没有验收清单光靠肉眼扫一眼很容易误判。成本花费多少输入 token、输出 token、API 延迟。上下文压缩往往意味着成本下降但状态更新和日志写入也会占用资源需要一起算。可维护性一旦出问题能不能从日志和状态快照快速定位到第几步。这一项很难在前几次实验里体现只有等任务真正失败过几次后才知道。真正落地时建议在日志里同时写入 step_index、当前状态快照、模型给出的理由再进入下一步。没有这些后面只能靠猜。下面给一个适合自己记录实验结果的表格式样任务 ID方案是否完成总输入 token出错步骤人工介入备注T-001完整历史是120000无无速度慢T-001显式状态是45000无无状态 schema 清晰T-002完整历史否180000第10步2次上下文逻辑漂移T-002显式状态是60000第10步自动恢复0次可以从失败步骤继续不要把“输出看起来不错”作为唯一指标因为 Agent 类项目最大的风险不是单次好看而是长任务和批量执行时的不稳定。5.2 不同任务类型对显式状态的收益不同这里补一个边界判断。显式状态的收益和任务类型关系很大。如果任务步骤是线性的A 做完做 BB 做完做 C显式状态很合适。状态字段几乎和步骤一一对应出错后能定向重跑。如果任务需要频繁在多个不相干知识领域之间跳转显式状态的收益会下降。因为状态字段要覆盖的维度太多模型需要不断判断“当前知识领域的下一步”很容易把不同领域的状态混在一起。这种情况更稳妥的做法是维护“当前领域 ID”和每个领域自己的状态而不是做成一个大而全的 state。如果是代码生成和代码修改类任务显式状态会特别有价值。代码任务有明确对象比如文件路径、函数名、测试用例、编译结果。这些本身就适合结构化生成状态时也容易验证。SKILL.state 这个方向真正打动人的也比较偏向这类可控性更高的任务。6. 真正落地时容易踩的坑6.1 把所有历史改成一个超长自然语言小结这是最容易出现的问题。有人觉得显式状态就是“每轮把历史压缩成一句话”结果越存越长最后又变成了一个隐含的场景描述。状态的正确用法是能拆成字段就别用长文本能用枚举值就不用自由文字。比如“用户是否同意删临时文件”写成 is_allowed_delete_tempfilesTrue不要写成“用户在第 2 轮表示允许但语气上好像有些犹豫”。长文本唯一该放的位置是 decision_log 里的理由摘要而不是主状态流。因为理由摘要只是备查不需要每次参与模型决策。6.2 只建状态不保留日志轨迹状态是给当前任务用的快照日志是给你复盘用的轨迹。两者不能互相替代。状态记录“现在在第几步、变量是什么”日志记录“第几步收到了什么输入、模型做出了什么决定、工具返回了什么结果”。排查问题时我更建议先查日志找到“最后一次符合预期的动作”再回头对照 state 字段看哪个字段和第 N 步开始不匹配。如果你只有 state 没有日志会不知道状态是何时被改成错误的。6.3 把全局约束漏在状态之外一个高频翻车点用户最开始说的一句话比如“不要删除生成过程中的临时结果”你没有写进状态字段任务执行 8 步后模型因为某个文件清理逻辑把临时目录删掉了。这看着像模型执行错误根因其实是约束没有进入执行上下文。处理这个问题可以在任务开始前增加一个“抽取全局约束”的步骤让模型把用户要求中的限制性内容转成状态字段。如果状态里没有一个专门的约束区我一定不会开始处理长任务。6.4 状态被多个执行单元互相覆盖批量跑任务的时候如果你在多个线程或 worker 里更新同一个 task 状态没有加版本号或锁就会发生同时读到旧状态、互相把新字段写回的问题。排查现象通常是任务看起来只做了几步就结束或者状态一直停留在 running。排查顺序可以这样打开最新日志找到最后一次成功执行的步骤。对比 task_id、step_index、status、updated_at。确认状态文件或状态数据库能否正常读取、路径权限是否正确。检查有没有多个 worker 在更新同一个 task_id。最后再考虑是不是 prompt 策略问题。很多“Agent 行为异常”最后其实是状态系统问题并不是模型没有理解任务。6.5 过早引入重型持久化方案刚验证完一个小 Demo就急着上 Redis、上数据库、做复杂迁移很可能浪费大量时间。入门阶段状态直接放内存或本地文件就够了。只有在真正需要断点续跑、多 worker 并发的时候再把状态存储升级到 SQLite、PostgreSQL 或带原子更新的 Redis 方案。过度设计会让状态更新的逻辑变重反而拖慢迭代。7. 落地建议和参考顺序如果真的想把这个方向用到自己项目里我建议按下面顺序走不要倒过来。第一步先选一个已有 Agent 任务把它的历史拼接逻辑截断改成“状态加载、状态更新、状态保存”三个模块。不要一上来就重写架构先挑一个不太复杂的业务流程验证。第二步把任务跑 5 到 10 遍记录第 5 节的四类指标。如果你的任务不到 20 步可能看不出明显差异这不代表方向没用只能说明你的场景还不够长。第三步设计状态的持久化和恢复。先把单任务失败重跑做好再上并发。批量任务的核心不是“同时跑多少”而是“跑挂了能不能从挂掉那一步恢复”。先把“从 failed step 继续”做通比把并发调大更重要。第四步把状态 schema、输出目录、日志规范提前固定下来。一旦任务量起来再改字段老任务的状态恢复逻辑都要跟着改。你可以把 schema 当成接口来管字段变化时要保持向后兼容。踩过几次之后我自己的体会是很多 Agent 应用不稳定不是模型不够聪明而是模型既要推理又要记流水账记忆还全部堆在上下文里。显式状态就是把“记忆”从模型上下文里挪到系统代码和存储里。听起来没有用“更强的模型”那样直观但对长任务、批量任务、断点恢复和问题排查来说是更扎实的工程路径。如果你正在做多步 Agent 或者对话式自动化工具可以先用一个小实验验证一下思路再决定要不要把项目里的对话历史策略调整成显式状态方案。
返回列表