SKILL.state:用显式执行状态替代历史,解决智能体长任务痛点 SKILL.state 这个方向解决的是大模型智能体在长任务里越跑越慢、越跑越容易跑偏的问题。Google 新论文提出用显式执行状态替代不断增长的智能体对话历史简单说就是不再让智能体把所有聊天记录一次性塞进上下文而是把任务进度、变量、结果、错误信息统一抽成一份结构化状态每次只带当前状态继续推进。这个思路对正在做智能体开发、研究 Agent 工作流、在 Dify 或 Coze 上搭建智能体的同学值得认真看一遍。先说结论这是一个典型的工程化思路不是单纯靠更强模型解决长任务而是通过改变信息组织方式来控住上下文、降成本、提升稳定性。下面按“问题在哪、思路是什么、怎么落地、怎么验证、还有哪些边界”拆开讲。1. 智能体对话历史为什么正在成为瓶颈1.1 历史越长模型越容易“记了又忘”做过智能体开发的人都清楚目前绝大多数 Agent 的运行方式是把系统提示词、用户输入、历史对话、工具返回结果全部拼在一起交给大模型推理。任务简单时没问题一问一答上下文很干净。可一旦任务变成多步骤执行比如让智能体先抓数据、再清洗、再汇总、再生成报告历史就会一层一层堆上去。我实测过一个八步左右的文档处理任务前三步还很正常到第六七步时模型开始重复执行已经完成的步骤甚至把某一步的输出当成另一步的输入。原因不复杂上下文太长之后模型对“当前到底在哪一步”的感知会变弱。它不是记不住而是被大量历史消息稀释了。对话历史越长还会带来三个直接问题。第一是 token 成本上升。每一步都携带全部历史累积消耗会非常快。任务越复杂历史越膨胀钱花得越冤枉。第二是延迟上升。每次请求的输入长度变大模型处理时间会明显增加。对用户来说就是智能体“越用越卡”。第三是状态一致性变差。智能体每一步的判断都依赖前面的对话内容但对话内容里既有关键信息也有大量无关交互。模型并不知道哪些真实有效容易被噪音带偏。1.2 截断、摘要、RAG 都不能根治问题很多人遇到上下文太长第一反应是截断。把最早的对话砍掉只保留最近几轮。这个做法在短任务里有效但在长任务里很危险。早期关键信息一旦被截掉智能体就会失忆后面只能靠猜。第二种常见方案是摘要。用大模型把过去的对话总结成一小段再放进上下文。摘要确实能压缩长度但代价是细节丢失。比如“原始文件路径”“已经修正过的错误记录”“用户明确提出的格式要求”这些信息一旦被概括掉后面就会出问题。而且摘要本身也是一次模型调用也有成本和延迟。第三种方案是 RAG也就是检索增强。很多同学遇到 Agent 记不住就想着把历史塞进向量数据库用相似度检索找回来。这个思路适合外部知识比如文档库、知识库、FAQ。但它不适合任务执行过程的内部状态。因为执行状态不是“知识”而是“当前进度”。进度信息具有很强的时序性和因果关系向量检索没法保证每次都把最关键的那一步找出来。截断、摘要、RAG 本质都是“在历史视角下做压缩”。它们都没有回答一个问题智能体执行任务时到底哪些信息必须存在哪些信息可以丢掉。SKILL.state 的切入点就在这里。2. SKILL.state 的核心思路用显式状态替代历史2.1 从“重放对话”到“读取状态”对话历史记录的是“发生了什么”。它是一份流水账里面有用户说的话、模型回复、工具调用、报错信息、中间结果。这些东西混在一起模型每次都要自己判断哪些重要。显式执行状态记录的是“现在是什么情况”。它是一份结构化快照包含任务目标、当前步骤、已完成步骤、待办步骤、关键产物、错误记录。模型每次调用时不需要把整本流水账读一遍只需要读这份快照。这个转变很关键。在传统模式里一个执行了十步的任务第十一步的输入可能是几千甚至上万 token 的对话历史。在状态化模式里第十一步的输入只是一份可能几百 token 的状态对象。上下文长度不再随任务步数无限增长而是保持在一个相对固定的范围内。从“重放历史”变成“读取状态”本质上是把智能体的记忆从隐性记忆切换成显式状态。记忆不再依赖模型从历史里推理出来而是直接以结构化字段的形式交给模型。2.2 显式执行状态包含什么按照目前公开的方向一个典型的显式执行状态至少应该包含四类信息。状态对象的第一类是任务元信息。包括任务 ID、任务目标、创建时间、优先级。它用来区分不同任务也方便系统在多个任务之间做调度和恢复。第二类是执行进度。当前执行到哪个步骤哪些步骤已经完成哪些步骤还在等待哪些步骤失败过。这类信息是状态对象的核心它让模型知道自己“站在哪里”。第三类是产物和变量。已经生成的文件路径、中间结果、关键指标、用户偏好设置。这些信息不需要以对话形式重复出现只需要用字段保存。下游步骤要用的时候直接读取字段即可。第四类是错误和重试信息。最近一次错误是什么发生在哪个步骤已经重试了几次。这类信息对调试和恢复非常重要。传统历史模式下错误信息可能已经被后续对话淹没状态化模式下错误信息始终挂在状态对象上不会被遗忘。下面给一个简单的状态对象示例方便理解{ task_id: sales_report_20250701, goal: 整理本月销售数据并生成日报, current_stage: clean_data, completed_stages: [collect_data, validate_input], pending_stages: [aggregate, summarize, render], artifacts: { raw_file: /data/july.csv, validated_rows: 12580, output_path: /output/report.md }, errors: [], retry_count: 0 }这份状态对象里没有任何完整对话历史只有当前必需的执行事实。每一步执行完后系统更新对应的字段然后进入下一步。2.3 与 Dify、Coze 平台上“变量”的关系如果你用过 Dify、Coze 这类智能体平台会发现里面已经有变量、全局参数、会话上下文、节点输入输出等概念。这些能力和显式执行状态非常接近但很多人在实际搭建时并没有真正用起来。常见做法是在工作流里加几个节点让大模型自己记着前面节点的结果在提示词里写“根据前面的对话继续执行”。这样确实能跑但本质上还是在依赖历史上下文。一旦节点一多模型就会开始“乱接话”。更接近 SKILL.state 的做法是把关键信息全部落到节点变量或全局变量里。每个节点只读取自己需要的变量而不是直接去看前面所有节点的原始输出。这样即使某个节点失败重跑也不会污染整个执行上下文。换句话说很多智能体平台已经提供了状态化的“硬件设施”缺的是状态建模的“使用习惯”。3. 不靠论文源码也能落地这个思路3.1 先定义任务状态结构论文源码公开与否其实不影响我们把这个思路用到自己的项目里。因为显式执行状态本质上是一个软件工程方案核心是自己定义状态结构。第一步把任务拆成固定步骤。比如“销售日报生成”可以拆成收集数据、校验数据、清洗数据、汇总指标、生成文案、渲染文件。每个步骤都对应一个状态字段。第二步定义每个步骤的输入和输出。输入可以来自用户、来自上游产物、来自配置文件。输出要明确落到哪个字段存到哪个文件或数据库。第三步确定状态对象由谁维护。可以由代码维护也可以由智能体自己返回新的状态。我建议前几步先由代码维护等逻辑稳定了再让模型尝试修改状态字段。否则模型一改状态字段值很容易不符合预期。状态结构不需要一开始就设计得很复杂。可以先从最核心的几个字段开始当前步骤、已完成列表、待办列表、关键产物、错误记录。跑通了再逐步增加字段。3.2 用结构化状态跑通一个最小任务用一个 Python 类来管理执行状态是比较轻量的做法。from dataclasses import dataclass, field from typing import Any, Dict, List dataclass class ExecutionState: task_id: str goal: str current_step: str completed: List[str] field(default_factorylist) pending: List[str] field(default_factorylist) artifacts: Dict[str, Any] field(default_factorydict) errors: List[str] field(default_factorylist) def mark_done(self, step: str, result: Any): if step in self.pending: self.pending.remove(step) self.completed.append(step) self.artifacts[step] result if self.pending: self.current_step self.pending[0] else: self.current_step done这个类做的事情很简单记录当前步骤完成一步就把它从待办移到已完成并把结果存到产物字典里。用 dataclass 的好处是结构清晰、容易序列化成 JSON、可以落数据库。每次调用模型前把状态对象转成 JSON 放进提示词。模型完成当前步骤后返回结构化结果代码更新状态。这样模型不需要记住整段历史它只需要知道当前状态。如果你不想写 Python也可以用 JSON 文件、Redis、数据库表来存储状态。重点不是技术选型而是不要让状态散落在对话历史里。3.3 状态化 Prompt 怎么写状态化改造之后Prompt 也要跟着调整。传统 Prompt 会写“根据以上对话继续执行”状态化 Prompt 应该写成“根据当前状态执行当前步骤”。一个比较通用的模板是这样你是任务执行代理。 任务目标{goal} 当前执行状态{state} 当前步骤{current_step} 请只处理当前步骤不要提前执行后续步骤。 完成后返回 1. 当前步骤的结果 2. 更新后的状态对象这个模板的核心是限制模型行为。它不要求模型回忆历史而是要求模型聚焦当前步骤。很多长任务跑偏不是因为模型不够聪明而是因为在历史里看到了太多冗余信息。状态化 Prompt 直接把冗余信息拿掉模型反而更专注。这里有一个细节更新后的状态对象最好由代码校验而不是完全信任模型。比如 pending 列表里有三个步骤模型返回时却移除了两个代码需要检查这是否符合预期。校验不通过就重试或告警。3.4 从单任务扩展到批量任务单任务跑通之后批量任务才是真正拉开差距的地方。批量任务的核心是状态隔离。每个任务必须有自己的 task_id有自己的状态对象不能共用同一个状态实例。很多批量任务的 bug都是因为多个任务写到了同一个变量或同一个文件里导致状态互相覆盖。建议把状态对象持久化到外部存储。简单场景用 SQLite复杂场景用 Redis 或 PostgreSQL。持久化的目的有两个一是任务中断后可以恢复二是方便查看任务执行的完整轨迹。失败重试也要基于状态来设计。任务失败时先读取状态对象确认失败步骤。然后只重新执行失败步骤而不是把整个任务重跑一遍。这样可以节省大量时间和 token。批量任务还需要考虑输出命名。每个任务的输出文件建议包含 task_id 或时间戳避免被覆盖。状态对象里保存输出路径后续汇总阶段再统一读取。4. 状态化改造后的验证、评估与排查4.1 先看这几个指标状态化改造不是把代码写出来就行还需要用指标验证它真的有效。我建议重点看四个指标。第一个是上下文长度曲线。用全历史模式和状态化模式分别跑同一个多步骤任务记录每一步的输入 token 数量。全历史模式通常是线性增长状态化模式应该保持在一个相对平台期。如果状态化之后 token 仍然快速增长说明状态对象里塞了太多不必要的内容。第二个是任务成功率。把任务重复跑多遍统计最终完成目标的比例。长任务尤其要看“步骤顺序是否稳定”也就是模型有没有跳过步骤、重复步骤、顺序颠倒。第三个是恢复能力。在任务执行到中途时手动中断然后从状态对象恢复看它能否从断点继续而不是从头开始。这个指标直接反映状态化方案是否真正落地。第四个是资源占用。状态对象存储在内存、数据库、文件系统中的大小以及任务执行过程中的内存变化。正常情况下状态对象应该远小于同等步数的对话历史体积。4.2 状态丢失和状态冲突怎么排查状态化改造之后常见的问题不再是上下文溢出而是状态本身出了问题。我这里列几条排查顺序。第一步先看状态是否更新。如果任务重复执行同一个步骤先检查代码是否调用了 mark_done或者模型返回的更新后状态是否被正确写入。很多时候是状态对象没有回写到存储导致下一次读取到旧状态。第二步看状态是否有冲突。多个任务共用同一个状态文件、同一个全局变量时容易出现交叉污染。排查时先看 task_id 是否唯一再看读写状态的地方是否存在并发问题。第三步看状态对象是否过大。如果状态里塞了完整的历史记录、长文本内容、Base64 图片状态对象本身会变得很大上下文长度问题会重新出现。状态对象应该只保存“路径、摘要、关键字段”而不是保存全量原始数据。第四步看模型是否忽略了状态。如果提示词已经给了当前状态模型仍然输出历史内容那可能是状态放在提示词里不够醒目或者格式太复杂。可以简化状态字段把当前步骤放到最前面。4.3 哪些场景不适合硬套状态化状态化不是万能的。有些场景仍然需要完整的对话历史。比如开放式的头脑风暴场景。用户和智能体来回讨论想法每一步的可能方向都依赖于前面讨论的细节。这种场景下如果把历史压缩成状态反而会丢失讨论脉络。再比如需要严格保留原文表述的任务。法律文书、合同审阅、文学创作等场景用户往往需要看到完整的前后文而不是摘要。强行用状态对象替换历史产出质量会下降。还有一种情况是状态对象本身非常复杂。比如每一步都有大量中间产物每个产物都需要完整保存。如果状态对象已经膨胀到和历史差不多大那状态化的优势就不存在了。这时候应该重新做任务拆解把大步骤拆成更小的独立任务而不是继续往状态对象里塞东西。判断标准很简单如果任务执行过程是“有明确步骤、有清晰产物、可以断点续跑”就适合状态化。如果任务是“自由探索、强依赖上下文语气和细节”就不适合。5. 从一篇论文到工程实践落地建议5.1 先做小规模原型不要直接重构看完论文思路后最忌讳的是立刻把所有智能体工作流全部改成状态化。更稳妥的做法是选一个重复性最高的任务做小规模原型。比如周报生成、销售数据整理、多文档归纳这些任务步骤固定、产物明确非常适合状态化。先用代码搭一个最小状态管理器再挑三条真实任务跑一遍。重点看两个东西一是状态化之后模型的输出质量有没有下降二是 token 成本和任务耗时的变化。如果小规模验证效果好再考虑接入正式流程。如果只是学习不需要一开始就追求完美。把状态对象用 JSON 文件保存把提示词模板写好已经能明显感受到上下文变短带来的变化。5.2 在成熟智能体平台上怎么试如果你主要使用 Dify、Coze 这类平台不写代码也可以实现简化版的状态化。思路是这样的把工作流中的关键节点输出全部写入变量或全局参数。后续节点只引用这些变量而不是引用“会话历史”。比如一个做销售分析的智能体收集节点把原始文件路径写入变量清洗节点把清洗后的数据行数写入变量汇总节点只读取这两个变量。这种做法的好处是每个节点都只依赖自己需要的变量不依赖模型从前面的对话中自己找信息。平台已经支持变量和状态记忆关键是你有没有主动把信息“落下”来。在平台上做状态化还有个额外好处调试时可以直接看变量值不用翻聊天记录。任务失败时也能更快定位到是哪个节点的输入出了问题。5.3 多智能体协同时的状态同步问题状态化思路在多智能体场景下更有价值。多智能体系统里每个子智能体如果都携带完整对话历史整个系统的 token 消耗会非常恐怖。更合理的做法是把全局状态放在一个共享状态库每个子智能体只需要读写自己负责的那部分状态。比如一个“数据采集智能体”负责读取文件一个“数据分析智能体”负责统计指标一个“报告智能体”负责生成文案。它们之间不需要共享全部对话只需要共享任务状态和关键产物。数据采集智能体完成采集后把文件路径写入状态库。分析智能体读取路径处理完把结果写入另一个字段。报告智能体最后读取汇总字段。这种模式下每个智能体的上下文都保持在很小的范围任务却可以无缝衔接。多智能体真正需要的不是多一个对话窗口而是统一的状态同步协议。5.4 我建议的关注顺序整个思路真正落地时最该盯住的不是模型选型也不是 Prompt 技巧而是状态结构的设计、持久化和恢复机制。先定义状态字段再写状态更新逻辑。先保证状态能存下来再考虑让模型直接修改状态。先跑通一个任务再扩展批量。每一步都围绕“显式执行状态”来组织历史对话反而变成了辅助信息。SKILL.state 本质上是在提醒我们智能体不需要记住所有东西它只需要知道自己现在在哪、要去哪、手里已经有什么。把这个事实用结构化的方式固定下来长任务才会真正稳定下来。如果你正在做智能体开发遇到长任务容易跑偏、上下文很快用完、成本一直压不下去可以试试这个思路。不一定要等论文源码先从一个任务的状态对象开始改大概率会有立竿见影的体感变化。

相关新闻

最新新闻

基于SpringBoot的昆仑旅行社信息管理系统(源代码+文档+PPT+调试+讲解)

基于SpringBoot的昆仑旅行社信息管理系统(源代码+文档+PPT+调试+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/9/1 12:56:55
基于SpringBoot的快递物流跟踪预警信息系统(源代码+文档+PPT+调试+讲解)

基于SpringBoot的快递物流跟踪预警信息系统(源代码+文档+PPT+调试+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/9/1 12:56:55
基于大语言模型的网络安全异常检测实践指南

基于大语言模型的网络安全异常检测实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:56:55
基于SpringBoot的垦一新区车库管理系统(源代码+文档+PPT+调试+讲解)

基于SpringBoot的垦一新区车库管理系统(源代码+文档+PPT+调试+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/9/1 12:56:55
基于51单片机与HX711的电子秤设计及Proteus仿真

基于51单片机与HX711的电子秤设计及Proteus仿真

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:56:55
优必选算法岗笔试复盘:从KMP到PID的机器人算法知识全解析

优必选算法岗笔试复盘:从KMP到PID的机器人算法知识全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:51:55