☰
告别无限对话历史:显式执行状态如何解决智能体长任务漂移与Token成本问题
2026/10/20 18:23:20 网站建设 项目流程

这次我们来看 Google Research 团队提出的一篇新工作,名字叫 SKILL.state。它的目标非常直接:解决智能体(Agent)在长任务执行中,对话历史越攒越长、越跑越偏的问题。简单说,就是把“靠聊天记录猜状态”改成“用显式执行状态记录进度”。

如果你最近在搭智能体,或者被上下文超长、token 成本飙升、任务做着做着就忘了前面步骤这些问题折磨过,这篇文章值得收藏。下面我会先讲清楚 SKILL.state 到底做什么、和传统对话历史方案的核心差异,再给出一套技术上可以参考的实现与验证思路,最后整理常见坑和工程建议。

1. 核心能力速览

能力项说明
项目来源Google Research 团队提出,论文标题中提到用显式执行状态替代不断增长的智能体对话历史
核心思想将智能体运行过程中的“对话历史”压缩为结构化的显式执行状态,按需读取、按需更新
主要功能提升长任务稳定性、降低 token 消耗、减少上下文漂移、便于调试与回放
适用对象大模型 Agent、多智能体编排、工具调用、任务规划、代码生成与执行
硬件门槛取决于底层大模型;SKILL.state 本身是一种推理与状态管理方法,不单独要求特定显卡
支持平台与模型框架无关,可适配主流 Agent 框架,如 LangChain、Dify、自研 Agent 服务
API 能力可以采用 API 方式暴露状态查询与更新接口,方便外部系统接入
批量任务适合批量任务队列管理,每个任务维护独立状态对象
主要优势对话历史不再无限增长,状态可查询、可校验、可回放
潜在局限状态定义需要针对任务设计,不同任务可能有不同的状态字段

需要注意的是,论文标题中的“SKILL.state”并不是一个可以直接 pip install 的现成工具名。它更像一种方法论的命名,后续是否有配套开源代码,需要以 Google 官方仓库为准。本文讨论的核心是它背后的思路,以及我们怎么在现有 Agent 工程里落地类似设计。

2. 为什么“对话历史”会成为智能体瓶颈

现在的大模型智能体,最常见的架构是“循环读取历史 + 调用工具 + 继续推理”。每执行一步,就会把用户指令、模型推理、工具返回、中间结果全部追加到上下文里。短任务时没问题,一旦任务链路变长,问题就非常明显。

第一个问题是 token 爆炸。每一步工具返回可能是一大段 JSON、代码日志、文件内容。假设平均一步消耗 2000 token,执行 50 步就是 10 万 token。成本翻倍还是小事,很多模型的上下文窗口直接不够用。

第二个问题是信息稀释。模型需要从越来越长的历史里找到“现在进行到哪一步”“下一步该做什么”。当历史里充满了过时的中间结果、失败尝试、冗余日志,模型很容易被无关信息带偏。

第三个问题是状态不一致。对话历史只是文本,不是结构化数据。模型在读取历史时,可能会对某些字段产生误解。比如某个文件已经重命名,但历史里还有旧路径,模型就会继续引用旧路径。这种问题靠提示词很难根治。

SKILL.state 的思路,就是不再让模型每次翻聊天记录,而是维护一份“当前任务到底处于什么状态”的结构化快照。这个快照不随时间无限膨胀,只保存关键执行信息。

3. SKILL.state 核心思路拆解

从论文标题和摘要来看,SKILL.state 的核心是把智能体执行过程中的“状态”显式建模。它和传统“对话历史”的本质区别在于:

  • 对话历史是线性的、增长的、无结构的文本流。
  • 执行状态是结构化的、动态更新的、可查询的键值集合。

一个典型的显式执行状态可能包含:

状态字段说明示例
task_goal当前任务的最终目标将用户上传的 CSV 数据清洗并生成可视化报告
current_step当前已经执行到第几步第 3 步:数据清洗
completed_steps已经完成的关键步骤列表文件读取、字段映射
pending_steps待执行的步骤列表生成图表、导出 PDF
active_tool当前正在使用的工具code_interpreter
key_variables任务运行中需要长期保留的变量数据文件路径、维度字段名
last_error最近一次失败的错误信息内存不足,已重试 1 次
constraints用户指定的硬性约束输出文件必须为 PDF

模型在每一步决策前,不再读取整段历史,而是读取这个结构化的状态对象。需要更详细的中间过程时,再按需从日志或存储中取。

这样带来的直接收益是:

  1. 上下文长度可控,不会随着步数线性增长;
  2. 模型每次看到的都是当前任务的“摘要帧”,信息密度更高;
  3. 状态可以被外部系统读取,方便调试、监控、人工干预;
  4. 任务中断后可以基于状态恢复,而不是从头再来。

4. 显式执行状态 vs 对话历史:对比与权衡

4.1 信息完整性

对话历史保留了所有原始信息,但噪声也多。显式执行状态只保留关键信息,前提是状态设计者知道哪些字段重要。对于探索性任务,状态字段可能覆盖不全,需要设计成“关键字段 + 可扩展索引”。

4.2 上下文成本

维度对话历史方案显式执行状态方案
上下文长度随步数线性增长基本稳定,取决于状态字段数量
token 成本高低
长任务稳定性越往后越容易漂移相对稳定
调试难度需要翻完整日志直接看状态即可
实现复杂度低,追加消息即可高,需要状态定义与更新机制

4.3 状态一直保持最新吗

这是最关键的问题。显式执行状态如果更新不及时,模型看到的就是过期状态,反而比对话历史更危险。所以状态更新必须和工具调用结果强绑定:每次工具返回后,先更新状态,再让模型基于新状态决策。

4.4 什么时候该用,什么时候不该用

如果任务只有一两轮交互,直接走对话历史更简单。但如果任务是“写代码 -> 跑测试 -> 修 bug -> 再跑测试 -> 部署”,或者“读取多个文件 -> 汇总 -> 生成报告”,每一步都依赖前面产生的新信息,显式执行状态就非常值得引入。

5. 适用场景与使用边界

SKILL.state 的思想可以广泛应用在以下场景:

  • 长链路代码生成与执行:Agent 需要反复修改代码、执行、读取报错、再修改。
  • 数据分析任务:先读取数据,再清洗,再建模,再出图,每一步产出物要被后续步骤引用。
  • 多智能体协作:不同 Agent 负责不同模块,通过共享状态对象同步进度。
  • 自动化运维:执行安装、检查、补丁、验证等有明确前后依赖的运维流程。
  • 批量任务处理:每个任务维护独立状态,失败后单独恢复。

使用边界方面要特别注意:

  • 涉及用户隐私数据时,状态对象不能把敏感字段明文写入日志;
  • 工具返回结果可能包含版权内容时,需要先过滤再入库;
  • 状态恢复机制不能无限重试,避免对业务系统造成重复操作;
  • 不能因为状态压缩导致关键失败信息丢失,至少保留错误索引。

安全合规方面,任何智能体在执行文件操作、网络请求、代码执行等功能时,都必须严格校验权限,并在测试环境验证后再接入生产数据。

6. 技术实现思路与伪代码示例

虽然 SKILL.state 是论文方法,但我们完全可以在现有 Agent 框架里实现类似的显式状态管理。下面给出一套通用的实现思路,代码为示意模板,需要结合具体框架调整。

6.1 定义状态结构

from dataclasses import dataclass, field from typing import Any, Dict, List, Optional @dataclass class AgentState: task_id: str task_goal: str current_step: int = 0 completed_steps: List[str] = field(default_factory=list) pending_steps: List[str] = field(default_factory=list) key_variables: Dict[str, Any] = field(default_factory=dict) last_error: Optional[str] = None error_count: int = 0 status: str = "running" # running / success / failed def update_after_step(self, step_name: str, result: Dict[str, Any]) -> None: self.completed_steps.append(step_name) if step_name in self.pending_steps: self.pending_steps.remove(step_name) self.key_variables.update(result) self.current_step += 1 def to_prompt_block(self) -> str: """将状态转换为供模型读取的紧凑文本。""" lines = [ f"任务目标: {self.task_goal}", f"当前步骤: {self.current_step}", f"已完成: {', '.join(self.completed_steps)}", f"待完成: {', '.join(self.pending_steps)}", f"关键变量: {self.key_variables}", ] if self.last_error: lines.append(f"最近错误: {self.last_error}") return "\n".join(lines)

这段代码定义了一个最小的状态对象。实际任务可以按业务需要增加字段,比如当前使用的工具名称、依赖缓存路径、用户授权范围等。

6.2 状态更新与主循环

def run_agent_with_state(user_goal: str, executor) -> AgentState: state = AgentState( task_id="task_001", task_goal=user_goal, pending_steps=["parse_input", "execute_tool", "verify_result"], ) while state.status == "running" and state.current_step < 10: # 构建本次推理的上下文:状态块 + 最新事件,而不是全部历史 prompt = build_prompt_with_state(state) # 模型决策:选择工具和参数 action = executor.llm_decide(prompt) # 执行工具 try: result = executor.call_tool(action.tool, action.args) state.update_after_step(action.tool, {"last_result": result}) except Exception as e: state.last_error = str(e) state.error_count += 1 if state.error_count >= 3: state.status = "failed" break # 判断是否完成 if executor.check_goal_reached(state): state.status = "success" return state

这个循环的关键在于prompt每次只包含结构化状态和最新一步的事件,不再拼接全部历史。模型每次决策时看到的上下文较短,且信息集中。

6.3 状态存取与恢复

如果任务中途断掉,可以将状态持久化到 JSON 文件或数据库。

{ "task_id": "task_001", "task_goal": "清洗数据并生成报告", "current_step": 2, "completed_steps": ["parse_input", "execute_tool"], "pending_steps": ["verify_result"], "key_variables": { "file_path": "./data.csv", "schema": {"date": "string", "amount": "float"} }, "status": "running" }

恢复时直接加载 JSON 到AgentState,然后从current_step继续执行。这里要注意:恢复前必须确认之前的工具调用不会执行第二次,比如“发送邮件”这类操作不能重复触发。

7. 接口 API 与批量任务设计

状态化的设计对接口和批量任务非常友好。

7.1 状态服务接口

可以单独起一个状态管理服务,提供以下接口:

接口方法说明
/state/createPOST创建任务状态
/state/getGET查询任务状态
/state/updatePOST更新任务状态
/state/deleteDELETE清理任务状态

这样 Agent 主服务只负责推理和工具调用,状态由独立服务统一管理。多智能体协作时,每个智能体都可以读取同一个状态对象的不同字段。

7.2 Python 调用示例

import requests base_url = "http://127.0.0.1:8080" # 创建状态 state_payload = { "task_id": "task_002", "task_goal": "批量处理商品图片", "pending_steps": ["download_images", "resize", "upload"] } resp = requests.post(f"{base_url}/state/create", json=state_payload, timeout=10) print(resp.json()) # 查询状态 resp = requests.get(f"{base_url}/state/get", params={"task_id": "task_002"}, timeout=10) print(resp.json())

实际项目里建议加权限校验,防止状态被未授权访问。如果是内网部署,也要设置网络访问控制。

7.3 批量任务队列

批量任务场景下,每个任务一个状态对象,用 Redis 或数据库存储。队列消费时只读取状态对象,不读取长对话历史,内存和存储压力会小很多。

一个简单的批量任务设计:

batch_config: input_dir: ./bulk_inputs output_dir: ./bulk_outputs state_store: redis max_retry: 2 on_error: skip_and_log

每条任务独立记录失败原因,最后统一汇总。整个队列的并发度可以远高于传统方案,因为单个任务占用的上下文不再随步骤增长。

8. 资源占用观察与性能评估思路

SKILL.state 本身不消耗显存,真正的资源占用取决于底层大模型。但状态化改造会显著影响推理和内存使用,可以从以下维度观察。

8.1 上下文 token 变化

在任务执行过程中,记录每一步传给模型的 token 数量。对比“全量历史方案”和“显式状态方案”的 token 曲线。显式状态方案应该是一条接近水平的线,而全量历史方案是向上增长的斜线。

8.2 推理时延

虽然状态化以后单次输入 token 减少,但状态构建和更新会增加额外代码逻辑。观察平均每步推理延迟,确认收益大于开销。如果状态频繁更新导致额外 I/O,可以考虑批量更新或内存缓存。

8.3 显存占用

显存占用主要看模型参数量、量化方式和 batch size。如果使用同一个基座模型做对比,显存占用不会因为状态化而明显变化。但更大的收益是:由于上下文变短,可以运行更大的模型,或在同样的显卡上支持更高的并发。

8.4 稳定性指标

建议记录以下指标:

  • 任务完成率:执行完所有步骤并成功结束的比例。
  • 步骤回退率:模型因为状态不一致而被迫回退的步骤比例。
  • 无效工具调用率:调用工具参数错误或不符合当前状态的次数。
  • 平均每任务 token 消耗。
  • 平均每任务执行时长。

通过对比改造前后这些指标,可以量化 SKILL.state 思路的实际价值。

9. 常见问题与排查方法

问题现象可能原因排查方式解决方案
模型拿到过期状态状态更新在工具返回之后未及时同步检查工具调用后是否有update_after_step强制在每次工具返回后更新状态
状态字段缺失导致决策失败状态定义不完整,漏掉关键变量查看失败案例中模型缺少的信息补充字段,或设置默认值
token 消耗不降反升状态块过大,或仍拼接了大量历史打印实际送入模型的 prompt精简状态字段,历史改成按需检索
批量任务状态互相污染多个任务共用了同一个状态对象检查任务 ID 隔离每个任务创建独立状态实例
任务恢复后重复执行操作恢复逻辑没有判断副作用检查恢复路径是否跳过已完成的工具对不可重复操作设置幂等标识
接口返回状态慢状态存储频繁读写检查数据库/Redis 延迟加缓存,或批量提交更新
模型在长任务后期忘记目标目标字段被压缩在大量状态中查看状态块中任务目标是否在顶部将 task_goal 固定在 prompt 开头

10. 工程落地建议

如果要在自己的 Agent 项目中落地显式执行状态,建议按以下顺序推进。

第一,先找一个小型长任务用例,比如“读取 CSV -> 画图 -> 写结论”。在现有 Agent 代码里加入AgentState,只做状态记录,不改变原有决策逻辑。这一步的目的是观察状态和实际执行的匹配度。

第二,把模型决策的 prompt 改成“状态块 + 当前事件”。先保留全量历史作为兜底,对比几次任务的表现。确认状态化方案更稳定后,再逐步去掉历史拼接。

第三,把状态持久化做好。任务中断恢复是状态化方案最实用的场景之一。建议用数据库存储状态,带上版本号,避免并发更新冲突。

第四,接入批量任务。每个任务独立状态,异步处理,失败单独重试。批量任务队列的代码结构可以复用项目管理里的通用 task runner,降低改动成本。

第五,建立监控和日志。每次状态更新都打日志,方便回放和定位问题。自动记录 token 和耗时,方便对比优化。

这里再强调一次合规边界:如果智能体会执行代码、操作系统文件、访问网络,务必在沙箱环境测试;涉及人脸、声音、隐私数据或版权素材时,必须获得明确授权;批量任务中要设置速率限制和审计日志,避免对第三方系统造成压力。

11. 总结与后续关注重点

SKILL.state 最值得尝试的点,是它提供了一个比“无限拼接对话历史”更可控的Agent记忆方案。它不要求你更换大模型,也不需要重新训练,只需要在业务代码层引入结构化状态管理。

建议最先验证两个方向:一个是长任务稳定性是否提升,另一个是 token 消耗是否下降。如果这两个指标都有改善,就说明这套思路适合当前场景。最容易踩的坑是状态定义过粗或过细,过粗会丢信息,过细会变成另一种形式的长上下文。需要结合具体任务迭代调整。

后续可以继续关注 Google Research 是否放出源码和基准测试结果。如果论文公开了状态 schema 的推荐设计,可以直接借鉴。即使没有开源,我们也可以基于本文的实现思路,在自己项目里建立一套显式状态管理模块。这对长链路 Agent 的工程化来说,是值得投入的方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询