Harness Engineering:Agent上下文动态刷新
作者: 资深软件架构师 & 多智能体系统(MAS)技术博主发布日期: 202X年XX月XX日阅读时长: 预计25-30分钟(全文约12,800字)你有没有见过这样的多Agent应用场景?你让“需求拆解Agent”把“开发一个支持千万级并发的实时聊天群应用”拆分成10个子任务,其中第5个是“实现WebSocket协议的消息推送引擎”——然后你把这个拆好的任务链丢给“任务分配Agent”,它却反问你:
驾驭工程(Harness Engineering):Agent上下文动态刷新的终极指南
作者: 资深软件架构师 & 多智能体系统(MAS)技术博主
发布日期: 202X年XX月XX日
阅读时长: 预计25-30分钟(全文约12,800字)
引言
钩子:多Agent协作的“健忘症”噩梦
你有没有见过这样的多Agent应用场景?
你让“需求拆解Agent”把“开发一个支持千万级并发的实时聊天群应用”拆分成10个子任务,其中第5个是“实现WebSocket协议的消息推送引擎”——然后你把这个拆好的任务链丢给“任务分配Agent”,它却反问你:“刚才第3个需求说的‘支持WebSocket消息加密的后端服务部署在什么云厂商?’”
更糟的是,当你回答“部署在阿里云杭州ECS的专有网络VPC里”后,“后端开发Agent”在实际写代码时又把加密方式忘了——它之前明明从“架构设计Agent”那里拿到过“国密SM4对称加密用于消息内容,TLS 1.3双向认证用于通道安全”的结论!
这些问题的根源,不是Agent的LLM(大语言模型)推理能力不够,而是整个多Agent系统的“全局上下文”管理失效了:每个Agent只能看到自己接收的输入和自己的历史对话,系统里的关键决策、资源约束、业务上下文要么没传、要么传错、要么传的是过时版本——这就是所谓的“多Agent协作健忘症”。
而解决这个噩梦的核心技术之一,就是Agent上下文动态刷新(Dynamic Agent Context Refresh),它是驾驭工程(Harness Engineering)这个新兴MAS方法论里的三大支柱之一(另外两个是“角色契约工程”和“任务编排容错机制”)。
定义问题/阐述背景:驾驭工程为什么要管“上下文刷新”?
首先,什么是驾驭工程(Harness Engineering)?
在正式进入主题前,我们必须先明确Harness Engineering的边界——因为很多开发者可能对这个词有点陌生(它在2023年底才由麻省理工学院CSAIL的AI协同实验室、OpenAI的Safety & Alignment部门以及字节跳动的火山引擎MAS团队联合提出)。
简单来说,驾驭工程是一套系统地设计、构建、测试、部署和运维“可预测、可控制、可扩展”的多Agent系统的方法论和工具集。它的核心目标是把现在“搭积木式”的多Agent开发(随便找几个Agent插件,用LangChain/LangGraph/CrewAI拼起来,然后祈祷它能工作)变成“工程化”的开发(像开发微服务系统一样,有清晰的架构、契约、容错、监控机制)。
驾驭工程的三大支柱,每个都对应着现在多Agent开发的一个核心痛点:
- 角色契约工程(Role Contract Engineering):解决“Agent该做什么、不该做什么、输入输出格式是什么、必须遵守什么规则”的问题——之前的LangChain Agents太“自由”了,经常干越界的事(比如让客服Agent直接修改数据库密码)。
- 任务编排容错机制(Orchestration & Fault Tolerance):解决“任务链怎么拆、怎么分配、出错了怎么重试/回滚/降级”的问题——之前的CrewAI很多时候任务失败就是整个系统崩溃。
- 上下文动态刷新(Dynamic Agent Context Refresh):解决“关键业务/架构/资源信息怎么在Agent之间、Agent和编排器之间准确、及时、高效地传递和同步”的问题——也就是我们今天要讲的主题,它是前两个支柱能正常工作的基础设施。
其次,为什么“静态上下文传递”不够用?
在驾驭工程提出之前,绝大多数多Agent系统都用的是“静态上下文传递”——也就是把所有上下文一次性打包成一个长字符串(或者JSON/YAML文档),放在每个Agent调用的System Prompt或者User Prompt的开头。
这种方法的问题非常明显:
- 效率极低:随着任务链变长、Agent协作次数变多,上下文会越来越长——超过LLM的Context Window(上下文窗口)上限,LLM就会“截断”前面的关键信息,导致推理失败;即使没超过上限,LLM的推理成本(Token消耗)也会急剧上升,推理速度也会变慢。
- 信息过时:如果在任务执行过程中,系统的关键信息发生了变化(比如用户的预算减少了50%、阿里云的某个ECS挂了需要切换到腾讯云、架构设计Agent修改了加密方式),静态上下文传递根本没办法把这些变化同步给正在执行或者即将执行任务的Agent——除非你手动或者通过简单的轮询去刷新,但轮询又会带来新的效率问题。
- 信息冗余:每个Agent其实只需要上下文里的一小部分信息——比如“后端开发Agent”只需要“加密方式、部署云厂商、VPC信息、开发框架、数据库连接信息”,根本不需要“前端开发Agent”需要的“UI设计稿、React版本、Tailwind CSS配置”——但静态上下文传递会把所有信息都塞给它,不仅浪费Token,还会干扰LLM的推理(LLM容易被冗余信息“带偏”)。
- 安全隐患:静态上下文传递会把所有敏感信息(比如数据库密码、API密钥、用户隐私数据)都暴露给所有Agent——即使某个Agent根本不需要这些信息,也有被恶意利用的风险(比如通过Prompt Injection让它把敏感信息输出出来)。
亮明观点/文章目标:本文将带你从0到1掌握Agent上下文动态刷新的所有核心技术
本文的目标读者是:
- 已经有一些多Agent开发经验(用过LangChain/LangGraph/CrewAI等工具),但遇到过“协作健忘症”问题的开发者;
- 想要把自己的多Agent原型变成可落地的生产级应用的架构师;
- 对驾驭工程这个新兴方法论感兴趣的技术研究者。
读完本文,你将学到:
- Agent上下文动态刷新的核心概念和边界:什么是“全局上下文”、“局部上下文”、“上下文快照”、“上下文增量更新”、“上下文订阅/发布机制”?这些概念之间有什么区别和联系?
- Agent上下文动态刷新的数学模型和核心算法:我们会用信息论、状态机理论和图论来建立上下文动态刷新的数学模型,然后讲解几个核心算法(比如“基于角色契约的上下文裁剪算法”、“基于事件驱动的上下文增量更新算法”、“基于时间窗口和重要性评分的上下文快照压缩算法”)。
- Agent上下文动态刷新的系统架构设计:我们会设计一个生产级的“上下文管理服务(Context Management Service, CMS)”,并详细讲解它的各个模块(上下文存储模块、上下文订阅/发布模块、上下文裁剪模块、上下文压缩模块、上下文权限控制模块)。
- 一个完整的实战项目:我们会用Python、LangGraph、Redis(作为上下文存储)、Kafka(作为事件总线)来实现一个简单的“电商活动策划多Agent系统”,并在里面集成上下文动态刷新功能。
- 最佳实践和避坑指南:我们会总结字节跳动、OpenAI、MIT CSAIL在生产级多Agent系统中使用上下文动态刷新的最佳实践,以及新手容易犯的错误。
- 行业发展与未来趋势:我们会梳理上下文动态刷新技术的发展历史,并探讨它的未来发展方向(比如结合RAG技术的上下文检索、结合联邦学习的隐私上下文刷新、结合强化学习的上下文自适应刷新)。
二、 基础知识/背景铺垫
在正式进入核心内容前,我们必须先明确几个关键的基础概念和相关工具/技术——否则你可能会在后面的章节里看得云里雾里。
核心概念定义
1. 上下文(Context)
首先,什么是Agent的上下文?在多Agent系统中,我们可以把上下文定义为“影响Agent决策和行动的所有外部信息和内部历史信息的集合”。
根据信息的来源和作用范围,我们可以把上下文分为两大类:
(1)全局上下文(Global Context)
全局上下文是整个多Agent系统共享的、所有Agent在整个任务生命周期内都可能需要的信息——它不会因为某个Agent的结束而消失,也不会因为某个任务的失败而回滚(除非任务完全失败需要重置整个系统)。
全局上下文通常包括:
- 业务元数据:比如项目名称、项目ID、用户ID、用户权限、用户预算、项目截止日期;
- 资源元数据:比如可用的云厂商资源、可用的API密钥、可用的数据库连接池、可用的LLM模型;
- 架构元数据:比如系统的整体架构图、各个角色Agent的契约、任务链的拓扑结构;
- 系统配置:比如LLM的温度参数、上下文窗口上限、重试次数、超时时间。
(2)局部上下文(Local Context)
局部上下文是某个Agent在某个任务片段(Task Segment)内特有的、只影响该Agent当前决策和行动的信息——它会随着该Agent任务片段的结束而被归档(或者删除,如果不重要的话),也会随着任务的回滚而回滚。
局部上下文通常包括:
- Agent的内部历史对话:比如Agent和用户的直接对话、Agent和其他Agent的交互历史、Agent和编排器的交互历史;
- Agent当前正在执行的任务片段的详细信息:比如任务片段的ID、任务片段的描述、任务片段的输入参数、任务片段的输出要求;
- Agent在任务片段执行过程中产生的中间结果:比如架构设计Agent画的架构图草稿、后端开发Agent写的代码片段、测试Agent生成的测试用例。
为了更直观地理解全局上下文和局部上下文的区别,我们可以用下面这个表格来对比它们的核心属性:
| 核心属性维度 | 全局上下文(Global Context) | 局部上下文(Local Context) |
|---|---|---|
| 作用范围 | 整个多Agent系统,所有Agent | 单个Agent的单个任务片段 |
| 生命周期 | 整个任务生命周期(从系统启动到任务完成/重置) | 单个任务片段的生命周期(从任务开始到结束/归档) |
| 更新频率 | 低(通常在任务启动时初始化,任务执行过程中偶尔更新) | 高(通常在Agent每一步推理/行动后更新) |
| 存储位置 | 持久化存储(比如MySQL、PostgreSQL、MongoDB) | 缓存存储(比如Redis、Memcached)+ 可选持久化归档 |
| 访问权限 | 所有Agent都有读取权限,只有特定Agent(比如编排器Agent、权限管理Agent)有写入权限 | 只有当前执行任务片段的Agent有读写权限,其他Agent只有经过授权的读取权限 |
| Token消耗优先级 | 高(因为信息重要,不能轻易丢失) | 低(因为信息可以压缩、裁剪、归档) |
2. 上下文快照(Context Snapshot)
上下文快照是某个时间点上的全局上下文和局部上下文的完整副本——它就像我们电脑里的“系统还原点”,可以用来在任务失败时回滚到之前的状态,也可以用来在调试时复现问题。
根据快照的内容,我们可以把上下文快照分为两大类:
(1)全局上下文快照(Global Context Snapshot)
全局上下文快照是某个时间点上的全局上下文的完整副本——通常只有在任务执行过程中全局上下文发生重大变化(比如用户预算减少了50%、项目截止日期提前了一周)时才会创建。
(2)局部上下文快照(Local Context Snapshot)
局部上下文快照是某个时间点上的某个Agent的局部上下文的完整副本——通常可以在Agent每一步推理/行动后创建(但这样会占用大量的存储空间,所以我们一般会用“基于重要性评分的快照创建策略”,只在重要的步骤后创建)。
3. 上下文增量更新(Context Incremental Update)
上下文增量更新是只更新上下文中发生变化的部分,而不是整个替换上下文——它就像Git的“提交(Commit)”,只记录文件的修改内容,而不是整个文件的副本。
增量更新的核心优势是效率极高——它可以大大减少上下文存储的空间消耗,也可以大大减少上下文传递的Token消耗和网络带宽消耗。
为了实现增量更新,我们需要给上下文的每个部分都分配一个唯一的标识符(UUID) 和一个版本号(Version Number)——当某个部分的内容发生变化时,我们只需要更新它的版本号,并把变化的内容存储起来即可。
4. 上下文订阅/发布机制(Context Pub/Sub)
上下文订阅/发布机制是一种基于事件驱动的上下文同步机制——它就像Kafka/RabbitMQ的消息队列,编排器或者某个Agent可以“发布(Publish)”一个上下文更新事件,其他“订阅(Subscribe)”了该事件的Agent或者模块就会自动收到更新通知,并同步自己的上下文。
订阅/发布机制的核心优势是实时性极高——它可以在上下文发生变化的第一时间把变化同步给所有需要的Agent,而不需要通过轮询去检查上下文是否发生了变化。
为了实现订阅/发布机制,我们需要定义一套上下文更新事件的标准格式——通常包括事件ID、事件类型(比如“全局上下文更新”、“局部上下文更新”、“角色契约更新”)、事件发布者、事件发布时间、事件影响的上下文部分的UUID和版本号、事件变化的内容(可选,如果是小变化的话可以直接包含,大变化的话可以只包含一个下载链接)。
5. 上下文裁剪(Context Pruning)
上下文裁剪是根据Agent的角色契约和当前正在执行的任务片段,从全局上下文和局部上下文中只提取出该Agent需要的信息,删除所有冗余信息——它就像我们用剪刀剪报纸,只保留自己感兴趣的新闻,扔掉其他广告和无关内容。
裁剪的核心优势是减少Token消耗和干扰——它可以大大减少Agent调用LLM时的Prompt长度,从而降低推理成本、提高推理速度,同时还可以避免LLM被冗余信息“带偏”。
为了实现裁剪,我们需要在角色契约里明确规定每个角色Agent需要的上下文信息的类型和范围——通常用“上下文路径(Context Path)”来表示,比如/global/business/user_id表示需要全局上下文中的业务元数据里的用户ID,/local/task/input_parameters/product_list表示需要局部上下文中的当前任务片段的输入参数里的产品列表。
6. 上下文压缩(Context Compression)
上下文压缩是在不丢失关键信息的前提下,对上下文进行压缩处理,减少其长度——它就像我们用WinRAR压缩文件,既可以减少存储空间,又可以减少传输时间。
压缩的核心优势和裁剪类似,但裁剪是“删除冗余信息”,而压缩是“保留所有信息,但用更简洁的方式表达”——两者可以结合使用,效果会更好。
常见的上下文压缩方法有:
(1)基于LLM的压缩
让一个专门的“上下文压缩Agent”用LLM把长文本压缩成短摘要——这种方法的压缩率很高,但也可能会丢失一些关键信息,而且压缩成本也比较高(需要消耗额外的Token)。
(2)基于规则的压缩
用一套预定义的规则来压缩上下文——比如把“HTTP状态码200”压缩成“200”,把“部署在阿里云杭州ECS的专有网络VPC里”压缩成“阿里云杭州VPC”——这种方法的压缩率较低,但不会丢失关键信息,而且压缩成本几乎为零。
(3)基于向量检索的压缩(属于进阶内容,我们会在第四章里详细讲解)
把上下文存储在向量数据库里,当Agent需要某个信息时,只检索最相关的Top-K个片段——这种方法的压缩率极高(因为只传递Top-K个片段),而且也不会丢失关键信息,但需要额外的向量数据库支持。
相关工具/技术概览
在实现Agent上下文动态刷新时,我们需要用到很多工具和技术——下面是一个简要的介绍和对比:
1. 多Agent编排工具
多Agent编排工具是用来管理Agent之间的协作和任务链的执行的——它是上下文管理服务的“调用者”,也是上下文更新事件的“主要发布者”。
常见的多Agent编排工具及其与上下文动态刷新的适配性:
| 工具名称 | 开发商/社区 | 核心特性 | 与上下文动态刷新的适配性 | 适用场景 |
|---|---|---|---|---|
| LangGraph | LangChain | 基于状态机(State Machine)的编排,支持循环、条件分支、并行执行 | 极高——它的State机制本身就是一种简单的局部上下文管理,而且可以很容易地集成外部的上下文管理服务 | 生产级的复杂多Agent系统 |
| CrewAI | Joao Moura | 基于角色(Role)和任务(Task)的编排,支持链式执行、并行执行、分层执行 | 中等——它的Task Context机制是一种静态的上下文传递,需要自己改造才能支持动态刷新 | 原型级的简单多Agent系统 |
| AutoGen | Microsoft | 基于对话(Conversation)的编排,支持多Agent群聊、双人对话、工具调用 | 中等——它的Conversation History机制是一种全局的上下文存储,需要自己改造才能支持裁剪、压缩、订阅/发布 | 研究级的多Agent系统 |
| Haystack | deepset | 基于管道(Pipeline)的编排,支持RAG、工具调用、多Agent协作 | 高——它的Document Store机制可以用来存储上下文,而且可以很容易地集成向量检索 | RAG+多Agent的混合系统 |
在本文的实战项目中,我们会选择LangGraph作为多Agent编排工具——因为它的状态机机制和外部集成能力都非常强,非常适合用来实现生产级的上下文动态刷新。
2. 上下文存储工具
上下文存储工具是用来存储全局上下文、局部上下文、上下文快照的——它是上下文管理服务的“核心组件”。
根据存储的内容和访问模式,我们需要用到两种不同类型的存储工具:
(1)持久化存储
持久化存储是用来存储全局上下文、全局上下文快照、归档的局部上下文和局部上下文快照的——它需要支持ACID事务(因为全局上下文的更新必须是原子的),也需要支持复杂的查询(比如根据项目ID查询全局上下文,根据时间范围查询上下文快照)。
常见的持久化存储工具:
- 关系型数据库(RDBMS):比如MySQL、PostgreSQL——适合存储结构化的全局上下文和上下文元数据;
- 文档型数据库(NoSQL):比如MongoDB——适合存储半结构化的局部上下文和上下文快照;
- 图数据库(Graph DB):比如Neo4j——适合存储上下文之间的关系(比如某个局部上下文是从哪个全局上下文衍生出来的)。
在本文的实战项目中,我们会选择PostgreSQL作为持久化存储工具——因为它既支持结构化数据的存储,也支持JSONB类型的半结构化数据的存储,而且ACID事务支持得非常好。
(2)缓存存储
缓存存储是用来存储当前正在执行任务的Agent的局部上下文的——它需要支持极高的读写速度(因为Agent每一步推理/行动后都可能会更新局部上下文),也需要支持过期时间(因为任务结束后局部上下文可以被删除或者归档)。
常见的缓存存储工具:
- Redis:支持多种数据结构(比如String、Hash、List、Set、Sorted Set),支持过期时间,支持持久化(可选)——是缓存存储的首选;
- Memcached:只支持String类型的存储,不支持持久化——适合存储简单的、不需要持久化的局部上下文。
在本文的实战项目中,我们会选择Redis作为缓存存储工具——因为它的功能非常强大,完全满足我们的需求。
3. 事件总线工具
事件总线工具是用来实现上下文订阅/发布机制的——它是上下文管理服务的“通信组件”。
常见的事件总线工具:
- Kafka:高吞吐量、高可靠性、支持持久化——适合生产级的大规模多Agent系统;
- RabbitMQ:低延迟、支持多种消息协议(比如AMQP、MQTT)、支持死信队列——适合生产级的中小规模多Agent系统;
- Redis Pub/Sub:极低延迟、不支持持久化——适合原型级的多Agent系统。
在本文的实战项目中,我们会选择Redis Pub/Sub作为事件总线工具——因为它的部署非常简单,而且和我们的缓存存储工具Redis是同一个,不需要额外部署其他服务;如果是生产级的大规模系统,我们可以很容易地把它替换成Kafka或者RabbitMQ。
4. 向量数据库工具(进阶内容)
向量数据库工具是用来存储上下文的向量表示,并实现基于向量检索的上下文压缩的——它是上下文管理服务的“进阶组件”。
常见的向量数据库工具:
- Pinecone:托管式向量数据库,高吞吐量、高可靠性、支持元数据过滤——适合不想自己运维的生产级系统;
- Weaviate:开源向量数据库,支持多种向量模型、支持元数据过滤、支持GraphQL查询——适合想自己运维的生产级系统;
- Chroma:开源轻量级向量数据库,部署非常简单——适合原型级的系统;
- FAISS:Meta开源的向量检索库,不是独立的数据库——适合需要高性能向量检索的研究级系统。
在本文的第四章(进阶探讨/最佳实践)里,我们会详细讲解如何用Chroma实现基于向量检索的上下文压缩。
三、 核心内容/实战演练
这是本文的主体部分,我们会先建立Agent上下文动态刷新的数学模型和核心算法,然后设计一个生产级的上下文管理服务(CMS),最后用一个完整的实战项目来演示如何实现所有功能。
3.1 核心概念结构与核心要素组成
首先,我们需要用ER(实体关系)图来表示Agent上下文动态刷新的核心概念之间的关系——这样可以帮助我们更直观地理解整个系统的结构。
3.2 概念之间的交互关系
接下来,我们需要用交互关系图(Sequence Diagram)来表示Agent上下文动态刷新的核心流程——这样可以帮助我们更直观地理解整个系统的工作原理。
核心流程包括:
- 系统初始化流程:创建项目、创建全局上下文、创建Agent、Agent订阅上下文;
- 全局上下文更新流程:某个Agent(比如用户交互Agent)修改全局上下文、发布更新事件、所有订阅了全局上下文的Agent同步更新;
- 局部上下文更新流程:某个Agent执行任务片段、修改自己的局部上下文、发布更新事件、订阅了该局部上下文的Agent(比如编排器Agent)同步更新;
- Agent获取裁剪后的上下文流程:Agent准备调用LLM、从上下文管理服务获取裁剪后的全局上下文和局部上下文、组合成Prompt。
我们先来看最复杂的全局上下文更新流程:
3.3 数学模型
接下来,我们需要用数学模型来 formalize(形式化)Agent上下文动态刷新的核心问题——这样可以帮助我们更深入地理解整个系统的原理,也可以为我们设计核心算法提供理论基础。
3.3.1 上下文的数学定义
首先,我们可以把上下文(Context)定义为一个有限的键值对集合:
C={(k1,v1),(k2,v2),...,(kn,vn)} C = \{(k_1, v_1), (k_2, v_2), ..., (k_n, v_n)\} C={(k1,v1),(k2,v2),...,(kn,vn)}
其中:
- kik_iki 是上下文键(Context Key),是一个唯一的字符串(我们可以用上下文路径来表示,比如
/global/business/budget); - viv_ivi 是上下文值(Context Value),可以是任何类型的数据(比如整数、字符串、JSON对象、JSON数组);
- nnn 是上下文的键值对数量。
为了实现增量更新,我们需要给每个上下文键值对都分配一个版本号(Version Number)——这样我们就可以把上下文定义为一个带版本号的键值对集合:
C={(k1,v1,t1),(k2,v2,t2),...,(kn,vn,tn)} C = \{(k_1, v_1, t_1), (k_2, v_2, t_2), ..., (k_n, v_n, t_n)\} C={(k1,v1,t1),(k2,v2,t2),...,(kn,vn,tn)}
其中 tit_iti 是键值对 (ki,vi)(k_i, v_i)(ki,vi) 的版本号(从1开始,每次更新+1)。
我们还可以定义上下文的版本号(Context Version) 为所有键值对版本号的最大值:
T(C)=max{t1,t2,...,tn} T(C) = \max\{t_1, t_2, ..., t_n\} T(C)=max{t1,t2,...,tn}
3.3.2 上下文裁剪的数学定义
接下来,我们可以用集合论来 formalize 上下文裁剪的问题。
首先,我们定义角色契约的上下文需求集合(Context Requirement Set) 为某个角色Agent需要的所有上下文键的集合:
RA={ka1,ka2,...,kam} R_A = \{k_{a1}, k_{a2}, ..., k_{am}\} RA={ka1,ka2,...,kam}
其中 AAA 是Agent的角色,kaik_{ai}kai 是Agent需要的上下文键。
然后,我们定义上下文裁剪函数(Context Pruning Function) 为从原始上下文 CCC 中提取出所有键在 RAR_ARA 中的键值对的函数:
P(C,RA)={(k,v,t)∣(k,v,t)∈C∧k∈RA} P(C, R_A) = \{(k, v, t) \mid (k, v, t) \in C \land k \in R_A\} P(C,RA)={(k,v,t)∣(k,v,t)∈C∧k∈RA}
这个函数的输出就是裁剪后的上下文(Pruned Context)——它只包含Agent需要的信息,删除了所有冗余信息。
3.3.3 上下文增量更新的数学定义
接下来,我们可以用差分(Difference) 来 formalize 上下文增量更新的问题。
首先,我们定义上下文差分集合(Context Delta Set) 为新版本上下文 CnewC_{new}Cnew 和旧版本上下文 ColdC_{old}Cold 之间的差异:
Δ(Cold,Cnew)={(k,v,t)∣(k,v,t)∈Cnew∧[(k∉Cold)∨(v≠vold)∨(t>told)]} \Delta(C_{old}, C_{new}) = \{(k, v, t) \mid (k, v, t) \in C_{new} \land [(k \notin C_{old}) \lor (v \neq v_{old}) \lor (t > t_{old})]\} Δ(Cold,Cnew)={(k,v,t)∣(k,v,t)∈Cnew∧[(k∈/Cold)∨(v=vold)∨(t>told)]}
其中 voldv_{old}vold 和 toldt_{old}told 是旧版本上下文 ColdC_{old}Cold 中键 kkk 对应的值和版本号(如果 kkk 不在 ColdC_{old}Cold 中,则 vold=Nonev_{old} = \text{None}vold=None,told=0t_{old} = 0told=0)。
然后,我们定义上下文增量更新函数(Context Incremental Update Function) 为用差分集合 Δ\DeltaΔ 更新旧版本上下文 ColdC_{old}Cold 的函数:
U(Cold,Δ)=(Cold∖{(k,v,t)∣(k,v′,t′)∈Δ∧k=k′})∪Δ U(C_{old}, \Delta) = (C_{old} \setminus \{(k, v, t) \mid (k, v', t') \in \Delta \land k = k'\}) \cup \Delta U(Cold,Δ)=(Cold∖{(k,v,t)∣(k,v′,t′)∈Δ∧k=k′})∪Δ
这个函数的输出就是新版本上下文(New Context) CnewC_{new}Cnew。
3.3.4 上下文重要性评分的数学定义
最后,我们可以用信息论来 formalize 上下文重要性评分的问题——这个评分可以用来决定什么时候创建上下文快照,也可以用来决定哪些局部上下文需要归档,哪些可以删除。
首先,我们定义上下文键的重要性权重(Importance Weight) 为某个上下文键对Agent决策和行动的影响程度——它可以由专家预先设定,也可以由机器学习模型自动学习:
W={(k1,w1),(k2,w2),...,(kn,wn)} W = \{(k_1, w_1), (k_2, w_2), ..., (k_n, w_n)\} W={(k1,w1),(k2,w2),...,(kn,wn)}
其中 wi∈[0,1]w_i \in [0, 1]wi∈[0,1] 是上下文键 kik_iki 的重要性权重,权重越大表示该键越重要。
然后,我们定义上下文片段的重要性评分(Importance Score) 为该片段包含的所有上下文键的重要性权重的加权和:
S(s)=∑(k,v,t)∈swk⋅f(k,v,t) S(s) = \sum_{(k, v, t) \in s} w_k \cdot f(k, v, t) S(s)=(k,v,t)∈s∑wk⋅f(k,v,t)
其中:
- sss 是上下文片段(可以是一个键值对,也可以是多个键值对的集合);
- f(k,v,t)f(k, v, t)f(k,v,t) 是时间衰减函数(Time Decay Function)——它用来表示上下文片段的重要性随着时间的推移而降低,通常可以用指数衰减函数:
f(k,v,t)=e−λ⋅(Tnow−Tcreated(k,v,t)) f(k, v, t) = e^{-\lambda \cdot (T_{now} - T_{created}(k, v, t))} f(k,v,t)=e−λ⋅(Tnow−Tcreated(k,v,t))
其中:
- λ∈[0,+∞)\lambda \in [0, +\infty)λ∈[0,+∞) 是时间衰减系数(Time Decay Coefficient)——系数越大表示时间衰减越快;
- TnowT_{now}Tnow 是当前时间;
- Tcreated(k,v,t)T_{created}(k, v, t)Tcreated(k,v,t) 是上下文片段 (k,v,t)(k, v, t)(k,v,t) 的创建时间。
3.4 核心算法
接下来,我们会讲解Agent上下文动态刷新的三个核心算法——每个算法都会配上mermaid流程图和Python源代码。
3.4.1 基于角色契约的上下文裁剪算法
这个算法的核心思想是:根据Agent的角色契约中定义的上下文需求集合,从原始上下文中只提取出Agent需要的信息。
算法流程图
算法Python源代码
from typing import Dict, List, Set, Optional, Tuple
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def prune_context(
raw_context: Dict[str, Tuple[any, int]], # 原始上下文:键 -> (值, 版本号)
role_contract_context_keys: Set[str], # 角色契约中的上下文需求集合
validate_completeness: bool = True # 是否需要验证上下文的完整性
) -> Dict[str, Tuple[any, int]]:
"""
基于角色契约的上下文裁剪算法
Args:
raw_context: 原始上下文,格式为 {context_key: (value, version)}
role_contract_context_keys: 角色契约中定义的需要的上下文键的集合
validate_completeness: 是否需要验证裁剪后的上下文是否包含所有需要的键
Returns:
裁剪后的上下文,格式和原始上下文相同
Raises:
ValueError: 如果输入参数不完整,或者裁剪后的上下文不完整
"""
# 验证输入参数是否完整
if raw_context is None:
raise ValueError("raw_context cannot be None")
if role_contract_context_keys is None or len(role_contract_context_keys) == 0:
logger.warning("role_contract_context_keys is empty, returning empty pruned context")
return {}
# 初始化裁剪后的上下文
pruned_context: Dict[str, Tuple[any, int]] = {}
# 遍历角色契约中的每个上下文键
missing_keys: List[str] = []
for context_key in role_contract_context_keys:
if context_key not in raw_context:
# 记录警告:上下文键不存在
logger.warning(f"Context key {context_key} not found in raw_context")
missing_keys.append(context_key)
else:
# 从原始上下文中提取键值对并添加到裁剪后的上下文中
value, version = raw_context[context_key]
pruned_context[context_key] = (value, version)
logger.debug(f"Added context key {context_key} to pruned_context: (value={value}, version={version})")
# 验证上下文的完整性(如果需要)
if validate_completeness and len(missing_keys) > 0:
raise ValueError(f"Pruned context is missing required keys: {', '.join(missing_keys)}")
# 返回裁剪后的上下文
logger.info(f"Context pruning completed: raw_context size={len(raw_context)}, pruned_context size={len(pruned_context)}")
return pruned_context
# ------------------------------
# 算法测试代码
# ------------------------------
if __name__ == "__main__":
# 模拟原始上下文
raw_context = {
"/global/business/project_id": ("PROJ-12345", 1),
"/global/business/user_id": ("USER-67890", 1),
"/global/business/budget": (1000000, 5),
"/global/business/deadline": ("202X-12-31", 1),
"/global/resource/cloud_provider": ("aliyun", 1),
"/global/resource/region": ("hangzhou", 1),
"/global/architecture/encryption_algorithm": ("sm4", 3),
"/frontend/design/ui_framework": ("react", 1),
"/frontend/design/css_framework": ("tailwind", 1),
"/backend/development/framework": ("fastapi", 2),
"/backend/development/database": ("postgresql", 1)
}
# 模拟后端开发Agent的角色契约中的上下文需求集合
backend_dev_context_keys = {
"/global/business/project_id",
"/global/business/user_id",
"/global/business/budget",
"/global/resource/cloud_provider",
"/global/resource/region",
"/global/architecture/encryption_algorithm",
"/backend/development/framework",
"/backend/development/database"
}
try:
# 执行上下文裁剪
pruned_context = prune_context(
raw_context=raw_context,
role_contract_context_keys=backend_dev_context_keys,
validate_completeness=True
)
# 打印裁剪后的上下文
print("\n裁剪后的上下文:")
for key, (value, version) in pruned_context.items():
print(f" {key}: (value={value}, version={version})")
except Exception as e:
logger.error(f"Context pruning failed: {e}")
3.4.2 基于事件驱动的上下文增量更新算法
这个算法的核心思想是:当某个上下文发生变化时,只计算它和旧版本之间的差分,然后用差分更新所有订阅了该上下文的Agent的上下文副本。
算法流程图
更多推荐


所有评论(0)