Marky又发新技术了!ARES Context Lifecycle Management:重新思考 Agent 的上下文生命周期管理 个人观点: context window is working memory, noy long-tern Memory 不要最大化上下文利用率,而应最大化上下文的信息有效密度,换言之不要给上下文里一个劲的塞垃圾,让垃圾埋没关键信息。 一、agent 上下文压缩阈值应该多少合适? 问大家一个问题,agent 设计中,上下文压缩的阈值多少合适呢? 我之前被人问到过,我当时的回答是,在ARES里上下文阈值设计是45% , 然后被质问“你不知道现在模型上下文很长吗? 现在agent app 的上下文压缩的阈值是多少你不知道吗?” , 我知道“80% 甚至90% +” 。 但我认为,这个问题本身可能存在一个认知偏差: Context Window 越大,并不意味着 Agent 就应该尽可能多地保留历史信息。模型能够**接收多少信息,与模型能否有效利用这些信息**,是两个不同的问题。 ARES 选择 45% 作为默认上下文治理阈值,并不是因为模型只能有效处理 45% 的上下文,也不是认为其他高阈值策略存在设计缺陷。 这是一次基于 Agent Runtime 长时间运行特征所做的工程取舍。 在 ARES 中,我更关注的不是 Context Utilization(上下文利用率),而是 Context Quality(上下文质量)。 这也是 ARES Context Lifecycle Management 的设计出发点。 二、Context Window 不等于有效上下文 想起个事,早在21年协助同事做一个需要批量生产文件的任务中,偷了个懒,需要短时间内生产上百GIB的数据作为测试用,就很头大,所以写了个循环,对文件进行批量的IO,写入大量的垃圾数据来完成测试需求,但是这成了一个心结,用垃圾数据进行填充,虽然快速完成任务,总觉得差点意思。那么回到这里现在大部分的agent 上下文利用率不也是如此吗? 垃圾数据太多,有价值的数据反而很少,在每一次LLM 摘要的时候宛如在沙里淘金。 言归正传,先阐述一个概念,Context Window 是模型的容量上限,不代表模型能够始终以**相同效率处理窗口内的所有信息**。 对于普通的聊天应用,上下文通常由用户输入和模型回复构成。但是agent 不同,充斥了大量的中间信息: * tool call 的请求与响应 * 中间推理结果 * RAG 检索内容 * 任务执行日志 * 失败重试记录 * 多 Agent 协作消息 * DAG 节点的执行结果 * 历史任务的上下文 这些信息在产生时可能具有较高价值,但随着任务推进,其中相当一部分会逐渐失去当前使用价值,但是如果不进行治理,这就会占据大量的context space ,这就带来了几个值得思考的问题: 2.1 Lost in the Middle:长上下文中的信息利用不均衡 长上下文研究中,一个经典现象是 Lost in the Middle。 研究发现(本人和EZ社区几个相关领域的博士讨论过),在部分长上下文任务中,模型对位于上下文开头和结尾的信息利用效果,可能明显优于位于中间位置的信息。 这意味着,即使模型能够接收 100K Token,也不代表它能够始终以相同效率利用这 100K Token。 上下文窗口扩大,解决的是**容量问题**。 但信息在长上下文中的有效利用问题,并不会因为窗口扩大而**自动消失**。 对于持续运行的 Agent,这个问题尤其值得关注。 因为 Agent 的上下文不是一次性构建完成的,而是随着任务执行不断增长。 2.2 Context Pollution 过程信息对工作上下文的污染 Agent 的一个典型特征是持续交互。如一个代码分析的agent 经历的过程可能如下: ```shell Task Initialization ---> Code Repository Analysis ---> Tool Calling---> Tool Result ---> RAG Retrieval---> Intermediate Reasoning ---> Retry / Validation ---> Final Result ``` 在这个过程中,每一次工具调用都可能产生新的消息。 问题在于: 工具调用产生的信息,不一定需要永久保留在当前上下文中。 例如: * 某个工具返回了大量源代码,但当前分析已经完成。 * 某次检索返回了几十条文档,其中只有两条真正有价值。 * 某次失败重试产生了大量调试信息,但问题已经解决。 * 某个 Agent 已经完成任务,其过程性消息对其他任务不再重要。 如果这些内容一直留在上下文中,就会形成 Context Pollution。它们并不一定是完全无用的信息,但不应该始终占据 Working Context。 2.3 Context Quality 不等于 Context Utilization Context Utilization 衡量上下文窗口被使用了多少。 Context Quality 则关注当前上下文中的信息是否与任务相关,是否完整,以及是否能够支持模型做出正确判断。 假设 Agent 当前拥有 1M Token 的上下文窗口。 方案 A: 保留 850K Token 的历史工具调用、执行日志、中间推理和当前任务信息。 方案 B: 只保留 400K Token,但经过治理后,主要由当前任务目标、关键事实、必要约束和相关执行状态构成。 我们不能仅凭 Token 数量就断言 B 一定比 A 好。 但至少可以明确: 上下文的容量利用率,并不能直接代表上下文的有效性。 对于长期运行的 Agent,我更倾向于优先保证**信息有效密度**,而不是尽可能填满 Context Window。 三、 为什么大部分的Agent 系统选择80%~90% 当然我并不认为 80%~90% 是错误的设计。相反,它在很多场景下具有合理性。 3.1 尽可能保留原始上下文 高阈值策略最大的优势,是允许 Agent 在更长时间内保留原始信息。 对于一些任务而言,历史信息确实具有很高的价值。 例如: * 复杂代码分析。 * 长文档研究。 * 多轮工具交互。 * 需要回溯早期决策的任务。 * 依赖大量原始证据的分析任务。 如果过早压缩,可能导致一些后续仍然需要的信息被提前摘要或移出上下文。 尤其是依赖 LLM 生成摘要的压缩策略,本身也存在信息损失风险。 因此,延迟压缩可以让模型保留更多原始上下文。 3.2 减少压缩的频率 高阈值意味着压缩发生得更少。这可以减少上下文治理操作的频率,也可以降低某些系统中的额外处理开销。 不同 Agent 框架采用的压缩方式并不相同。有些系统通过 LLM 生成摘要,有些采用消息裁剪,有些采用规则化处理,还有些采用外部存储与检索相结合的方式。 因此,不能把所有压缩策略视为同一种机制。 如: * Anthropic 的上下文压缩机制则允许通过 Token 阈值触发压缩。 这些方案的具体实现和预算定义并不完全相同,不能简单地把它们的百分比直接进行横向比较。 3.3 高阈值并不意味着架构落后 我想特别强调这一点。 **高阈值策略有自己的设计目标**: 尽可能延长原始上下文的驻留时间,保留更多信息,减少频繁治理带来的信息损失。 而 ARES 选择 45%,关注的是另一个问题: 如何让 Agent 在长时间运行过程中,**持续维持一个相对干净、相关且可用的工作上下文**。 这两种方案并不是简单的优劣关系,而是不同架构目标下的工程取舍。 真正值得讨论的不是 45% 和 90% 哪个数字更正确,而是: **你的 Agent Runtime 究竟希望如何管理上下文?** 四、ARES 设计的取舍 ARES 采用 45% 作为默认上下文治理触发阈值。这个设计不是为了追求更低的 Token 使用率,而是为了更早介入上下文生命周期管理。 我主要考虑了四个方面: 4.1 主动治理,而不是被动清理 假设一个 Agent 正在执行一个持续时间较长的任务。随着工具调用、检索和中间执行结果不断产生,上下文逐渐增长。如果等到 80%~90% 才开始治理,就意味着 Agent 在更长的一段执行过程中,都可能携带大量历史过程信息。 ARES 希望尽早处理那些已经失去当前使用价值的内容。 这里需要明确: 45% 并不是模型注意力退化的理论临界点。也不是说上下文达到 46% 就一定会发生注意力下降。它是一个工程默认策略(你甚至可以在yaml里配置到20%、66% etc)。 这么设计的原因是我希望通过更早的治理,缩短低价值历史信息在工作上下文中的驻留时间。 从这个角度来看: **45% 代表的是主动治理的介入时机,而不是模型能力的边界。** 4.2 Rule-based Compression:不依赖额外 LLM 摘要 这是 ARES 当前实现中一个比较重要的工程特点。 ARES 的 ContextCleaner 采用本地规则进行差异化上下文治理,而不是在每次压缩时额外调用一个 LLM 生成摘要。 其基本思路是: **不同角色、不同类型的消息,应该采用不同的处理策略**。 如: * tool result 的处理策略,提取 gist,保留 digest 引用 * 纯推理内容 则是根据规则截断 * 超出 Token Cap 的历史内容,则是通过 trimToTokenCap 进行兜底裁剪 核心实现包括: * ContextCleaner. Clean():负责角色差异化规则压缩。 * trimToTokenCap():在清理后仍然超出预算时进行兜底裁剪。 这种设计有几个特点: 1.不需要额外调用 LLM。压缩本身是本地 CPU 操作,不需要额外的模型推理请求。 2.额外消耗模型推理预算。与 LLM 摘要不同,规则化 Cleaner 不需要将整个历史上下文再次交给模型处理。 3.处理过程更容易测试和复现。规则明确,输入与输出之间的转换过程可以通过单元测试验证。 当然,Rule-based Compression 并不意味着完全没有信息损失。 规则提取和截断同样可能丢失细节,因此需要对关键消息、因果关系和执行状态设置保护机制。 但它避免了额外 LLM 摘要调用所带来的不确定性和成本。 4.3 高频小量治理:缩短上下文退化窗口 这是我认为 45% 策略最重要的设计考虑。我们可以对比两种治理方式。 方案 A:45% 主动治理:当上下文达到默认治理阈值时,提前清理低价值历史信息。 方案 B:80%~90% 延迟治理 允许 Agent 保留更多原始上下文,直到接近高占用区间再进行处理。 两者最核心的区别是治理介入的时机。 ARES 更倾向于: * 尽早清理已经失去当前价值的历史内容。 * 减少大量过程信息长期驻留。 * 避免一次性处理过多历史消息。 * 尽可能保护 Tool Call 与 Tool Result 之间的因果关系。 需要注意,高频小量治理并不天然意味着信息损失更小。如果 Cleaner 的规则不合理,即使 45% 就开始压缩,也可能反复丢失重要信息。 因此,真正重要的是治理策略与信息保护机制,而**不是单纯提高压缩频率。** 另外,Prompt Cache 也是一个值得考虑的因素。 如果上下文治理会改变缓存前缀,那么缓存命中率可能受到影响。 因此,未来可以考虑稳定前缀与可变工作上下文分离,尽可能减少对稳定前缀的修改。**但 45% 是否能带来更高的缓存命中率,需要结合具体模型供应商的缓存机制进行实验验证**,不
Marky碎碎念Arrogant and rude:面试是双向选择,不是单方面审判! 上周在社区里找到杭州一家公司(代号为A)招agent harness 开发。然后投递了简历,想以我自己开源的agent runtime 框架做一个敲门砖,彻底转到agent 开发,然后努力准备了许久…… (包括agent 调度设计,编排,RAG,存储,上下文压缩等模块做了很多的面试准备)周五的时候社区的负责人通知说简历审核通过了,约下周(也就是这周面试)同步收到了A 发送的面试邀约的邮件(时间暂定是某天下午两点),主要考核内容如下: 1. 通用型的业务问题和过往的项目经历 2. 价值观和软性技能也是A 选择人才的重要因素 本人于下午一点五十进入视频会议等候(出于个人教养早到等候) 两点面试官进来之后一直处于打哈欠、揉眼睛的状态(持续了很久),然后本人就开始做自我介绍(简单阐述工作履历以及做的项目)之后开始着重介绍ARES(自己设计的agent runtime 调度系统的细节以及区别于主流agent框架的差距) 注意:面试官大部分时间心不在焉,眼睛多次看向别处。(差不多十分钟)阐述结束之后,象征性的问了几个技术问题,我基本都作答。然后就聊到创业经历,问到了薪资,我说很低,然后解释说,创业的人是我的老领导,也是我的贵人,所以我选择无条件相信人家,跟着人家创业(言外之意报答人家的知遇之恩)。 面试官直接说;我完全不理解、搞不懂没有钱你为什么跟着别人创业。 之后继续agent 相关的问题,我阐述自己所设计的框架,以及如何规避可能存在的问题。基本上大差不差都回答上来了。 本人面试有个习惯,会在旁放置一个笔记本(干净页,没有任何的文字)方便记录面试过程中的技术难点作为复盘。该面试官直接嘲讽我的个人习惯,说完全搞不懂我有什么可记录的东西,并且强制要求我展示所记录的内容以及翻页。本人展示记录的内容(主要是面试流程以及询问到的几个比较难的问题)并且本人直言是不会也不屑于作弊。面试官还是抓住不放,继续评价我这个记录的习惯。 (题外话,我面试过很多公司,包括蚂蚁、外企、中小型公司)每一场面试都会记录问题,面试官都表示理解,也不会强制要求本人对记录内容进行展示甚至进行所谓的制高点评价。 出于个人教养一直忍到面试结束,面试官十分傲慢、不耐烦地说了一句你只能问我一个问题,我赶时间。我直接说技术栈是什么,他回答完之后我直接关闭面试界面。 ---- 这次面试给我的感受: 1. 面试官提前不会去了解候选人的背景以及项目资料的(最起码这家面试官如此)原因如下:a) ARES 的多个模块设计都是为了GA 算法,可遗传进化部分所服务的,面试官直接询问完全不知道我为什么如此设计。b) 项目是单yaml配置且定制化程度很高,他的很多问题都是问的为什么如此配置,甚至说高度一致吧。另外吐槽一下一家做agent的,深度接触AI的公司,为什么不让AI协助审查简历,了解候选人的开源项目呢? 2. 这家公司很忙,但是忙到面试官可以很傲慢的对待候选人。所以我只能大胆揣测,所谓招聘规范里的「价值观」到底是什么呢?傲慢对待所有的客?亦或是鸡蛋里挑骨头的风骨? 3. A的人完全代表的是公司,甚至一定程度上代表了该企业的用人标准以及企业文化。在本次面试过程中,A的人不止一次的粗鲁打断本人的回答,甚至多次催促(尤其是最的提问,不耐烦三个字基本都刻在脸上),把自己的不耐烦和不尊重人发挥的淋漓尽致…… 4. 不尊重你的人,完全可以否定你的努力,然后用他的傲慢进行尊严践踏。 5. 不适合的圈子真的没必要硬融进去,即便是这次侥幸面试通过,谁敢保证入职之后不会被穿小鞋、使绊子呢?见微知著。 --- 分享自己作为主导主持面试会议的一些事情: 本人一直秉持着相逢即是有缘的原则,能收到简历并且约面(线上/下都行)能在一起接触一个多小时,这是弥足珍贵的。这么短的时间需要了解一个人的技术、人品显然是做不到的。 按照本人的经验来说,具体考察候选人的几个特点: 1. 5年+ 经验的候选人直接考察对软件工程的理解,对于模块解耦、重构的功底(侧重于系统设计,提升系统稳定性)就这一点可以和候选人聊的有来有回 2. 刚毕业~ 3年+ 的候选人,则是考察编程语言的功底(不会手撕代码,更多是聊语言的设计,多语言对比等问题)亦或是面试之前给候选人一份未曾接触的技术文档,让其在面试的时候根据自己的理解进行讲解。以及遇到的问题和如何思考解决的(这个经验是很珍贵的,能保持独立思考,在未知领域快速构建知识体系)。 3. 品行,这是最重要的,只个不能问,只能观察小的细节。 --- 会议开始时,我一般都会先自我介绍(个人+公司),并且定性为技术研讨,以此来放松气氛,让候选人以满分状态参与进来(这一点是我从外企学来的,要把对方当做人,不是待剥削佃农)。 之后就是**平等**的进行技术上的探讨,有效果吗?有的,会议结束的时候,我会惯例问询,你是否愿意加入公司和我共事?(最起码80%的候选人当场是很认可的)。 总之,在外边做事情人代表的是自己的家庭,对外合作则代表的是组织/单位,做好自己,不能让别人觉得自己是个棒槌! --- 最后感谢平台提供的投递的渠道,也感谢负责人的安慰。如此傲慢的公司祝好吧,江湖不见!
MarkyHello World!hello~ 新人报个到,独立开发者,技术洁癖,性能钻牛角尖者。最近在搓自己的工具,能当知识图谱,也能验证AI编码效果的MCP…… 正在优化性能,之后会开源。嗯, 先说这么多吧……长图