Agent 状态栏构成与更新
状态栏承载哪几类信息?
- 任务规划: 用 TODO 列表把任务分解为清晰步骤,放在轨迹末尾,不断提醒进展与后续目标,确保行动与总体规划一致;
- 任务规划的必要性: 轨迹变长时,Agent 容易过分关注当前局部子任务,忘记原始诉求、核心约束与后续工作;
- 侧信道信息(Side-channel Information): 为每个事件附加的辅助元数据——精确时间、地理位置、距上次 Agent 回复的时间间隔;
- 侧信道的用途: 帮助模型理解事件的时序关系与环境背景,做出更符合情境的决策;
- 环境当前状态的观察摘要: 动态环境信息(系统时间、工作目录)、异常操作提醒(“该工具已被重复调用 N 次”)、从隐式状态到显式观察的转换;
- 设计呼应: 这一原则同样适用于人类界面——CLI 与 GUI 都致力于让用户清晰感知系统当前状态;
- 更新节奏: 侧信道信息通常随对应事件一起追加;任务规划与环境状态随任务推进不断更新;
状态栏在 API 消息列表中位于哪里?
- 位置: 作为一条
user角色的消息插入到上下文末尾,而不是修改开头的 system 消息; - 原因: 修改 system 消息会破坏整个前缀的缓存(见 KV Cache 原理与约束);
- 术语澄清: 这里的 user 角色只是 API 协议层面的技术选择,不等同于第一章定义的真实终端用户输入;
- 实质: Harness 借用 user 消息槽位,注入由框架自动生成的系统状态信息;
- 标记: 内容用
<agent_status>标签包裹,便于模型识别其特殊性质;
messages: [
{ role: "system", content: "You are a customer service assistant..." } # 固定,缓存
{ role: "user", content: "Help me cancel my Xfinity plan" }
{ role: "assistant", tool_calls: [...] } # 轮次 1
{ role: "tool", content: "Call log..." }
...更多轮次
{ role: "user", content: "Can you call them again to follow up?" }
{ role: "user", content: "<agent_status> phone_call invoked 3 times (Xfinity: 3/3 max) ... </agent_status>" }
]
- 双重收益: 处于最末尾因而获得最高注意力权重;因为只是追加而非修改,前面所有已缓存内容都不受影响;
- 原则: 这正是“动态信息追加末尾、静态信息保持不动”在状态栏场景的直接应用;
状态更新有哪两种实现,缓存代价如何?
| 实现 | 做法 | 优点 | 缓存代价 |
|---|---|---|---|
| 每轮替换 | 移除上一轮状态消息,在末尾追加最新状态 | 上下文只有一份状态且永远最新 | 移除旧状态使其位置之后的所有缓存失效;失效范围只覆盖上次注入后新增的消息(通常一轮) |
| 持久追加 | 状态一旦注入就永久留在轨迹,每轮只追加新状态 | 只追加不修改,前缀始终稳定,缓存完全友好 | 陈旧状态在上下文累积,既占 token 也要求模型自己关注最新一条 |
- 实例: Claude Code 的
<system-reminder>采用持久追加——历史状态保留在会话记录(transcript)中,从不删改; - 替换实现的性质: 与本篇批评的“动态时间戳”是同一个失效机制,区别只在于状态消息位于末尾,整个前缀仍可复用;
- 取舍因素: 轨迹长度、状态消息大小、两次更新间新增的后缀长度、预计更新次数;
- 经验法则: 状态很小、两次更新间消息很多、会话长度受控时选持久追加;状态较大、更新频繁或轨迹很长时选每轮替换;
- 粗略模型: 设每条状态 token,两次更新间新增后缀 token,预计更新 次,缓存输入单价为普通输入的 倍;
- 代价公式: ;;
- 判据: 当 时倾向持久追加,否则倾向每轮替换;
- 模型局限: 未计上下文占用与陈旧状态带来的歧义,实际选择还应结合服务商缓存计费与实测命中率;
有哪五种可直接启用的状态栏技术?
| 技术 | 做法 | 关键收益 |
|---|---|---|
| 时间戳跟踪 | 以 [2025-09-14 10:30:45] 前缀加到用户消息与工具响应 | 理解时序关系;便于调试与审计;支持时间模拟 |
| 工具调用计数器 | 全局字典记录每个工具调用次数,标注 “Tool call #3 for 'read_file'” | 触发模式识别;实现隐式成本感知 |
| TODO 列表管理 | 提供 rewrite_todo_list 与 update_todo_status 工具 | 外部记忆,防遗漏子任务 |
| 详细错误信息 | 四层内容:错误类型与描述、完整参数 JSON、调用栈、针对性修复建议 | 从盲目重试转为有针对性分析 |
| 系统状态感知 | 注入当前时间、工作目录、操作系统、Shell 环境、Python 版本 | 上下文正确的平台相关决策 |
- 时间戳的硬约束: 必须加在用户消息和工具响应上,不能放进系统提示词,否则会破坏 KV Cache;
- 计数器的深层价值: 第一次失败后检查路径,第二次失败后列出目录,第三次主动放弃并寻找替代方案——Agent 能“意识到”自己在某个操作上已尝试太多次;
- TODO 的数据结构: 每项包含唯一标识符、内容、状态(pending/in_progress/completed/cancelled)和时间戳;
- TODO 的理论依据: 借鉴 Manus 的“通过复述操纵注意力”理念,从认知负荷理论看起的正是外部记忆作用;
- TODO 的量化效果: 启用后平均 15 次迭代完成任务,禁用时需要 21 次且经常遗漏子任务;
- 错误信息的效果: 启用后 Agent 在错误场景中找到替代方案的成功率从 60% 提升到 95%;
- 工作目录跟踪: 执行
cd后自动更新,确保后续操作在正确上下文执行; - 操作系统信息: 使 Agent 能做出平台相关决策(如 Linux 用
apt、macOS 用brew);
这些技术为什么会产生涌现效应?
- 定义: 单独使用时效果有限,组合起来却能产生超出预期的效果;
- 组合一: 时间戳 + 工具计数器 → 理解操作的频率与时间分布;
- 组合二: TODO 列表 + 系统状态 → 根据环境调整任务策略;
- 组合三: 详细错误信息 + 工具计数器 → 多次失败后不仅改变策略,还能理解失败原因;
- 行为变化: 完全启用后,Agent 遇到文件不存在会先检查目录、再列出可用文件,仍找不到就在 TODO 标记 cancelled 并添加替代任务;
- 判断: 这种自适应行为是任何单项技术都无法实现的;
状态栏技术有哪些工程优势?
- 可审查: 所有元信息以人类可读形式出现在上下文里,开发者随时可检查 Agent 获得了哪些信息、做了什么决定;
- 非侵入: 不需要微调,直接在任何语言模型上都能起效;
维护状态栏要注意哪两点?
- 维护方式: 尽量用代码维护;实在要用 LLM,也要逐条抽取、再由代码汇总,绝不要让它一次性批量统计;
- 信任风险: 实验发现模型几乎无条件地相信状态栏——你写“打了 3 次电话”,它就当真是 3 次,不会自己重算;
- 风险放大: LLM 做数量统计本就容易出错,因此 状态栏投毒 风险值得认真对待;
- 删除原始上下文的前提: 状态栏是对原始上下文的一次有损投影,只提前算了“预想会被问到”的那些维度;
- 可以删的场景: 状态栏够用时(计数、状态跟踪这类任务)可整段删掉原始记录只留状态栏,节省大量 token;
- 不可删的场景: 只要有一个问题涉及状态栏未计算的维度,仅保留状态栏就会导致准确率断崖式下降;
- 定位: Agent 状态栏本身属于 上下文压缩 技术之一——用算好的结论替换臃肿原始记录;