1、有完整的对话历史,Agent为啥还会忘记任务进度?
Agent在执行复杂任务时容易陷入无限循环、状态遗忘、目标偏离,究其根本就是缺乏对环境当前状态的感知以及对任务进展的追踪。模型的上下文学习更像检索而非推理,也就是说模型擅长从已有的内容中查找信息,但是不擅长主动归纳和总结。因此虽然每次任务执行都有完整的历史,但是Agent仍会陷入困难或者忘记最初的目标。
2、Agent状态栏是什么?
Agent框架把运行过程中需要的动态感知自身状态与任务进展等动态信息整理成结构化摘要并注入上下文,该机制称为Agent状态栏。状态栏通过在上下文中嵌入结构化的元信息,为Agent补上自我感知和自我调节的机制。
它的本质是把分散在上下文各处的隐式状态提炼为可直接使用的显式知识。另外,在长上下文场景中,模型的注意力资源也是有限的,随着上下文长度的增加模型必须将注意力分配给更多信息片段,关键信息可能得不到足够的权重。尤其是复杂的Agent轨迹中,早期的任务目标与关键约束容易被后续的大量工具调用结果所淹没,模型会过度专注最近的上下文内容,对于早期的信息产生注意力衰减现象。我们将关键的元信息以结构化的形式放置在上下文末尾时,这些信息在空间上更加接近模型即将生成的新token,因此能获得更高的注意力权重。
3、应该让Agent看到哪些状态?
Agent状态栏包含以下几种类型的信息:
(1)任务规划
当Agent处理复杂的多步骤任务时,轨迹会变得很长,容易关注当前的局部子任务,忘记了最初的任务、核心约束以及后续工作。引入TODO列表后,将任务分解为清晰地步骤,再将列表放在轨迹的末尾,不断提醒模型当前的任务进展和后续目标,确保行动与规划一致。
(2)事件的侧信道信息
侧信道信息是指不在主要的数据通道中传递、但是对理解事件很有帮助的辅助信息(精确的时间、地理位置、距上次Agent回复的时间间隔等)。这些信息能帮助模型理解事件的时序关系与环境背景,从而做出更符合情景的决策。
(3)环境当前状态的观察摘要
包括动态的环境信息比如系统时间和工作目录等、异常操作提醒、从隐式状态到显式观察的转换。
4、状态从哪里来,又由谁维护?
Agent状态栏不是修改开头的system消息,而是作为一条user角色的消息(伪user消息--往期内容提到的Few-shot也有使用)插入到上下文的末尾。
用<agent_status>标签包裹以便模型识别其特殊性质,同时放在上下文的末尾不影响前面的缓存。
5、怎样把状态更新提供给模型?
状态消息在任务执行迭代中是会过时的,更新状态的实现方式:
(1)每轮替换
每次API调用之前,从消息列表中移除上一轮的状态消息,在末尾追加最新状态。但是移除旧状态会使其位置之后的所有缓存失效(失效范围只覆盖上次注入后新增的消息,一般就是一轮)
状态较大、更新频繁或轨迹很长时可以选择。
(2)持久追加
状态消息一旦注入就永久留在轨迹中,每轮只在末尾追加新的状态。该方法对缓存完全友好,所有的信息只追加、不修改,前缀始终稳定。但是陈旧的状态会在上下文中累积,既占用token,也要求模型自己能关注最近的一条状态而忽略已过时的旧状态。
状态很小、两次更新间产生的消息很多,且会话长度受控时选择。
6、常见状态栏技术及维护
(1)几种常见的Agent状态栏技术
①时间戳跟踪
以[xxxx-xx-xx xx:xx:xx]格式作为前缀加入到用户消息与工具响应中,使得Agent能理解时序关系,也为调试和审计提供了信息。
②工具调用计数器
维护一个全局字典,记录每个工具被调用的次数并在响应中标注。这种计数能触发模型的模式识别能力(以读文件的工具read_file为例:首次失败后检查路径、二次失败后列出目录、第三次就主动放弃并寻找替代方案)
③TODO列表管理
提供rewrite_todo_list和update_todo_status工具,每个TODO项都包含唯一的标识符、内容、状态(pending/in_progress/completed/cancelled)和时间戳。
④详细错误信息
包含错误类型和描述、完整参数的JSON、调用栈信息和针对性的修复建议(从盲目重试转变为有针对性地分析和解决问题)。
⑤系统状态感知
注入当前时间、工作目录、操作系统类型、Shell环境和Python版本等信息。
以上技术融合使用会产生1+1>2的效果:时间戳和工具计数器的结合使 Agent 能够理解操作的频率和时间分布;TODO 列表和系统状态的结合使 Agent 能根据环境调整任务策略;详细错误信息和工具计数器的结合使 Agent 在多次失败后不仅能改变策略,还能理解失败的原因。
(2)状态栏维护的注意点
①状态栏尽量使用代码维护:不能让LLM自行统计汇总,因为模型几乎无条件相信状态栏,然而LLM做数据统计本身就容易出错,这也让状态栏投毒风险的发生有了契机。
②谨慎删除原始上下文:状态栏是对原始上下文的一次有损投影,他只会提前计算预判你以后会用到的维度。如果状态栏够用就可以把原始记录删除,只保留状态栏,节约大量的token;但是只要后续有一个问题涉及状态栏未计算的维度,仅保留的状态栏就会导致精度大幅下降。