Hawa Code 当前不再依赖 Agent 框架层上下文压缩机制,而是采用 Agent 自主上下文管理模式,历史消息交给模型自己管理,保证长时任务连续性,以下是其基础原理说明。
1、Agent 自主上下文管理 VS Agent 框架上下文管理
Agent 框架上下文管理
当前 Agent 上下文管理一般都在 Agent 框架层面,以上下文压缩为例,在执行模型请求的时候 Agent 框架会计算当前历史消息占用上下文的体积,当超出阈值时候通过另一个模型请求对历史消息进行摘要,原始模型请求发送摘要信息进行推理。
以上所有行为都是 agent 框架自主控制的,模型自身没有进行判断。
Agent 自主上下文管理
Agent 自主上下文管理,是通过给 Agent 提供操作历史消息的工具,让模型自主决定管理上下文,比如消息中很多的不再需要的过程消息,模型可以自主删除这些冗余消息。
之所以选择 Agent 自主模式,因为模型越来越“聪明”,在保证风险可控的情况下把月来越多的决策交给模型来判断。包括自身的上下文内容。
2、消息归档
2.1、冗余消息体
Agent 在工作过程中会存在很多的过程消息,比如用 coding agent 修复多个问题 ,coding 会执行多个 Grep 去寻找代码,在找到正确的代码位置前这些查询的消息对于其他问题修复是没有意义的,但是还是会存在上下文当中,占用上下文体积、并且导致 token 使用量指数增加。
如何去掉这些冗余消息?简单方式可以让 coding agent 尽量使用 subagent,这样主 agent 上下文冗余的过程信息就会减少,但是 subagent 粒度不可能一直这么小,subagent 应该是执行一个完整的任务。这样在 subagent 中同样存在这样冗余的过程消息。
2.2、工具定义
归档
定义 Message Archiving 归档工具,逻辑是让模型自己分析哪些消息是不需要了,传递 tool use id 进行归档,如果后续消息内容还是需要的可以回档重新召回历史消息。
摘要
如果归档中有部分是有用消息,可以进行摘要,同时也可以用于回档。如果要对大量的消息进行归档,可以配合下面的消息卸载,把归档内容总结内容保存到磁盘上。
const getInputSchema = memoize(() => { return z.strictObject({ action: z .enum(['archive', 'unarchive']) .describe('Whether to archive or unarchive the listed tool results'), toolUseIds: z .array(z.string()) .min(1) .describe('Array of tool_use_id strings to archive or unarchive'), summary: z .string() .optional() .describe( 'Optional summary of information from the archived tool results that is relevant to the current task. Omit if the archived content is not useful for the current task.', ), }) })
2.3、归档示例
tool use id 被设置为归档后,在发送消息的时候会被显示为占位符号,减少上下文使用。
以下 tool result 消息一经被归档,显示 system 提示消息。
{ "type": "tool_result", "content": [{ "type": "text", "text": "<system>This tool result has been archived. You can use the Message Archiving Tool to unarchive it if needed (tool_use_id: tool_iYma3J2aKnUxeJqx4jK1zqEt).</system>" } ] }
3、Agent 自主消息卸载
消息的卸载和记忆有些类似,在历史消息中留存消息的摘要,把真实总结内容放入到磁盘上,不是原始的历史消息是 agent 总结的消息,比如大段的中间总结过程,最终可以汇总为完整总结过程。
同时为了节约 Token 数量,消息卸载工具的入参会进行省略处理,这样历史消息就不会有冗余消息。消息卸载可以和消息归档配合使用。
3.1、工具定义
const getInputSchema = memoize(() => { return z.strictObject({ action: z .enum(['write', 'read', 'list', 'delete']) .describe('Memo operation: write, read, list, or delete'), key: z .string() .optional() .describe('Memo identifier. Required for write, read, and delete.'), summary: z .string() .min(1) .max(300) .optional() .describe( 'Short summary (≤300 characters). Required for write. Summarizes the content and helps locate the corresponding memo content.', ), content: z .string() .min(1) .optional() .describe( 'Full reusable content. Required for write. Must contain substantive analysis results (e.g. function responsibilities, call relationships, data flow, key findings).', ), }) })
3.2、消息卸载示例
{ "type": "tool_use", "id": "tool_UqEQXzf5nfaixOYoqzKGordJ", "name": "Memo", "input": { "action": "write", "content": "[FRAMEWORK_PLACEHOLDER — NOT REAL CONTENT]\nThe original Memo content has been persisted to disk by the framework.\n- persistedSize: 381 chars\n- key: \"mcp-ui-manager\"\n- summary: \"MCP 命令入口与 MCPServerManager UI 导航\"\n- recall: use Memo read key=\"mcp-ui-manager\"\nWARNING: Do NOT treat this placeholder as the memo content. The real content is on disk and must be read back with Memo read.", "key": "mcp-ui-manager", "summary": "MCP 命令入口与 MCPServerManager UI 导航" } }
3.3、幻觉
消息卸载工具的入参被省略掉了,会导致模型模型认为入参就是省略信息,导致调用工具出错,直接存入省略信息。通过以下两种方式解决:
- prompt 中强化提示模型当前的入参已经被省略掉了。
- 工具进行入参校验,如果传入摘要信息进行报错。
4、成本
通过消息归档等方式或者消息卸载的方式是否可以做到成本节约。这个要分两个方面来看。
Token 节约
消息归档可以大量的节约 Token ,对于长时任务至少可以节约 50 % 以上的 Token,避免的大量无效 Token 。这个角度上是可以大量节约成本。
Prompt Caching
修改历史消息会导致 Prompt Caching 失效,需要重新建立缓存,这里会导致成本增加。
综合考虑如果指示短任务,几次循环调用消息归档的意义会相对较小。设计消息归档和消息卸载主要还是着重考虑长时任务,任务可以长时间执行不需要被压缩后者很少次数压缩。
欢迎了解使用 Hawa Code https://www.hawacode.com/