1. 从“给模型写台词”到“给团队搭舞台”:提示词工程在多智能体系统里的天花板
做多智能体系统做到第五个版本,我最大的感触是:单智能体时代我们引以为傲的提示词工程,在多个Agent协作的场景里正在快速失效。你精心设计的一套提示词,放到两个智能体协作时还能勉强工作,放到三五个智能体组成的系统里,立刻变成一场灾难。
这不是提示词本身写得不好,而是多智能体系统的上下文流动方式已经彻底变了。单智能体场景下,提示词是核心,上下文只是填充进去的变量;多智能体场景下,提示词退化成每个智能体的“初始化配置”,真正决定系统行为的反而是上下文如何在智能体之间生成、流转、竞争和衰减。换句话说,提示词工程回答的是“单个智能体如何理解任务”,上下文工程回答的是“整个系统如何共享和演化知识”。
我经常用一句话跟团队解释这个区别:提示词工程是给每个演员写剧本,上下文工程是给整个剧组搭舞台。剧本写得再好,舞台上的灯光、道具、对手戏演员的走位都是乱的,这场戏照样演砸。
多智能体系统的“演出事故”往往集中在这几个地方:
- 上下文覆盖:Agent A产生的结果写进了共享上下文,Agent B在下一轮对话时把这段历史截断了,之前的成果全部丢失。
- 上下文冲突:两个Agent从各自的局部视角修改了同一份状态,互相覆盖对方的写入,结果数据变来变去。
- 上下文不可解释:系统最终输出了一个结果,但你完全说不清是哪个Agent、基于哪段上下文、通过什么推理路径得出的结论。出了问题只能靠猜。
- 上下文遗忘:系统跑的时间长了,早期的重要约束和用户意图被后续海量中间输出稀释,Agent开始偏离原始目标。
这些问题有一个共同根源:我们把上下文当成了“文本”,而不是当成了“架构的一部分”。文本是一次性的,用完就扔;架构是持续演化的,需要设计、管理和观测。这就是系列文章一直在讲的上下文工程——把上下文当作第一公民,像设计数据库Schema一样设计上下文的流转规则,像监控服务链路一样监控上下文的生命周期。
这篇是系列的第五篇,我不打算再从头介绍上下文工程是什么,而是聚焦一个实操层面最容易被忽略、又最能拉开系统上限的方向:如何把上下文工程落地成一套透明的架构引擎——让每个智能体的输入、输出、推理依据、状态变更都变得可观测、可追溯、可审计。
2. 上下文不只是“你说了什么”,而是“系统记住了什么”:生命周期五阶段模型
我把多智能体系统里的上下文定义为:任一时刻,影响任意一个智能体下一次决策的全部信息集合。这个定义比“对话历史”宽泛得多,它既包括用户的原始输入,也包括工具调用返回的数据、其他智能体的中间输出、系统的全局状态、甚至智能体内部遗忘机制的残留影响。
理解了这一定义,你就会发现上下文是动态的。它有自己的生命周期,我把它拆成五个阶段:生成、流转、竞争、衰减、归档。
2.1 生成:一段上下文从哪里来
上下文的生成源头多种多样。一个用户消息是上下文,一次数据库查询的结果是上下文,一个智能体调用工具后的结构化输出是上下文,另一个智能体发来的协作请求同样也是上下文。
这里最容易犯的错误是:只把“最终的那段文本”当作上下文。比如Agent A调研完市场数据,返回了一段总结文字,Agent B拿这段文字继续推理。看起来上下文在正常工作,但Agent A可能丢失了关键的中间信息——它查了多少个数据源、哪些数据源之间互相矛盾、它最终是怎么权衡取舍的。
正确的做法是上下文分两层记录:
- 结果层:Agent A产出的最终结论,这是给其他智能体消费的。
- 依据层:Agent A做决策时依赖的过程数据、置信度、备选方案,这是给调试和追溯用的。
用个类比:结果层是你在会议上说的话,依据层是你做PPT时查的资料和思考过程。同事需要你说话的内容,但如果你想让他真正理解你的判断标准,他需要看到你的依据。
2.2 流转:上下文在智能体之间怎么移动
一段上下文生成之后,要进入系统流转。常见的流转模式有三种:
- 广播模式:每个Agent都接收完整的上下文更新。系统简单,但上下文越长,传给每个Agent的冗余信息越多,推理开销呈线性甚至超线性增长。适合智能体数量少、信息相关性高的小型系统。
- 路由模式:根据上下文类型和Agent的职责,只把消息推送给相关的智能体。实现复杂,需要一个路由层做匹配,但信息精准度最高,适合异构智能体协作的复杂系统。
- 黑板模式:有一个全局共享存储,所有Agent往黑板上写内容、从黑板上读取内容。是广播和路由的中间态,适合需要多个Agent协作解决同一问题的场景。
我的实测经验是:不要一开始就追求路由模式。路由逻辑本身会成为新的复杂度和故障点。从广播或黑板模式起步,等到确认了智能体之间的真实通信模式,再做定向路由优化。这个结论我踩过坑——最开始我在一个三智能体系统里强行设计了细粒度路由,结果维护路由规则的代价远超省下的token开销。
2.3 竞争:上下文之间的冲突与优先级
多智能体系统里,多个Agent几乎必然会对同一份上下文产生竞争性写入。最典型的场景:Agent A负责制定计划,Agent B负责执行任务,两者都会更新“当前进度”这个全局状态。A认为应该按计划推进,B发现实际执行有偏差,两个Agent写入了相互矛盾的状态。
解决竞争的思路跟数据库领域的并发控制很像,我实践下来有三层手段:
- 版本化:每条全局上下文带上版本号,写入时校验版本,冲突则重读再写。
- 职责分域:明确每个智能体的“写权限”范围,A只能写计划域,B只能写执行域,交叉读取但不交叉写入。
- 冲突仲裁者:设置一个专门的“协调者”智能体,负责处理其他Agent的冲突状态,决定最终以谁为准。
三层手段的选用原则很简单:智能体数量少、共享状态简单时,用版本化就够;一旦系统复杂到出现“A依赖B的写入,B又反过来依赖A的输出”这种循环依赖,就必须引入仲裁者角色。这时你其实是在系统层面复刻人类团队的“定时对齐会议”。
2.4 衰减:上下文的时效性加权
多智能体系统跑得越久,上下文累积越多,但不是所有历史上下文都同等重要。用户开场说的“我要做一个电商平台”,跟用户两小时前说的“支付模块用微信支付”,对当前决策的影响权重完全不同。
我用的方法是时间衰减加权。给每条上下文打一个时间戳和权重分,参考公式:
当前权重 = 初始权重 × exp(-Δt / T)Δt:该条上下文距今的时长T:衰减半衰期,根据上下文类型设定初始权重:生成时根据上下文重要性指定的基准值
比如用户的核心目标信息,T设置得很长,让它在整个会话周期内都不衰减;工具调用的临时返回结果,T设置得很短,几分钟后权重就趋近于零。
这个方法的价值在系统层面体现得更明显:上下文衰减直接决定了“将被输入给Agent的信息总量”,而不需要在Prompt里写“请忽略掉无关历史”这种模糊指令。衰减策略比提示词约束可靠得多,因为它是在上下文进入模型之前就完成了筛选。
2.5 归档:上下文进入可查询的历史
一部分上下文经过衰减后依然有价值,但活跃权重已经很低。这时候不应该直接删除,而是归档到结构化存储中,供后期检索和追溯使用。
归档实践里我推荐加上三个元数据字段:
| 字段 | 含义 | 示例 |
|---|---|---|
| producer_agent | 该上下文的产生者 | market_researcher |
| context_type | 上下文类型 | tool_result / agent_message / user_intent |
| parent_hash | 父上下文的哈希,用于血缘追踪 | a3f8e2... |
有了这三个字段,你不仅能知道“系统当前记住了什么”,还能回答“这份上下文是谁写的、属于什么类型、推理链路上的前一环是什么”。这三个信息是透明架构的基础。
3. 上下文总线与共享内存:多智能体通信的关键工程决策
上下文生命周期的流转环节,在工程实现上会落到一个具体问题:用什么载体来承载共享上下文。我把它类比成计算机体系结构里的总线与共享内存——智能体之间的数据通路设计,直接决定系统的性能上限和纠错成本。
3.1 三类上下文载体的选型对比
我在不同项目里分别试过三类方案,各有适用场景:
内存对象共享(进程内/线程内)
最简单的方式,在同一个进程内共享一个上下文对象,Agent之间直接读写。优点是零延迟、实现简单;缺点是只能在单机单进程内跑,无法分布式扩展,而且共享对象被多Agent并发写容易出现脏数据。
消息队列(如Redis Stream、RabbitMQ、Kafka)
每个Agent绑定一个输入队列,生产者Agent把上下文消息发布到队列,消费者Agent从队列拉取。优点是完全解耦、天然支持分布式、有消息持久化能力;缺点是队列通信有延迟,而且Agent之间的协作模式变成了异步消息传递,调试时要脑补消息的先后顺序。
向量/状态存储库(如Redis、PostgreSQL、向量数据库)
共享上下文写入一个统一存储,Agent按需读取。优点是最接近“黑板模式”的设计,支持历史归档和语义检索;缺点是读写开销比内存高频通信更大,需要自己管理索引和缓存。
我的选型经验:
- 2~3个Agent的快速原型 →内存对象共享,先把逻辑跑通
- 4个以上Agent、需要异步协作 →消息队列 + 内存对象混合,核心决策链路走同步,外围任务走异步
- 需要历史追溯、多轮会话复用 →状态存储库必须引入,它是透明架构的记忆底座
3.2 共享内存上的三条“总线协议”
载体选好之后,还要定义总线上的“通信协议”。我在自己的框架里固定了三种消息类型:
- 意图型消息(Intent):Agent A向Agent B发起协作请求,如“请帮我分析这组销售数据的异常点”。
- 事实型消息(Fact):Agent输出一个结论或状态变更,如“平台当前在线用户数突破1万人”。
- 反馈型消息(Feedback):对某条事实的确认或纠偏,如“用户偏好数据不准,请重新核实”。
三种消息类型各打一个标签,在总线上流动时互不混淆。这个设计的价值在调试期会充分体现——你一眼就能看出来系统当前是在推进(Intent)、堆积结论(Fact)还是绕圈子(Feedback)。后者的循环一旦出现,往往意味着系统逻辑有死循环或冲突。
3.3 作用域隔离:避免全局上下文的“邻里污染”
我在前几篇里反复遇到一个问题,这里值得再强调一次:全局共享上下文会污染不相关的Agent。
假设你的系统里有三个Agent:产品规划师、技术架构师、市场分析师。产品规划师写了一条上下文“用户反馈登录流程太繁琐”,这一步没问题。但技术架构师在推理时看到这条信息,可能会错误地开始设计登录优化方案,而它此刻的任务本来是评估后端性能瓶颈。
解决方案是给上下文加作用域(scope):
scope: product→ 只对产品规划师可见scope: tech→ 只对技术架构师可见scope: global→ 所有Agent可见
作用域的设定一定要跟Agent职责严格对齐。宁可让跨Agent沟通走一条明确的消息通道,也不要图方便把所有信息都广播到全局。这条经验帮我减少了大大小小十几起上下文污染问题。
4. 透明性的两个核心支柱:决策可追溯与行为可观测
“透明架构引擎”这个提法,核心就是两个字:透明。多智能体系统跟单体AI系统最大的区别在于,单体AI你只有一个模型,出错了换提示词、换参数就能调;多智能体系统有多个模型、多个推理链路、多轮上下文交换,出错了你连“错误在哪一步”都很难定位。透明性不是一个锦上添花的功能,而是能不能把系统跑稳的必要条件。
4.1 决策可追溯:从最终输出链回每一步推理
我做的第一个多智能体系统上线后遇到一个诡异的问题:用户问“帮我推荐一个适合夜跑的运动耳机”,系统返回了一个防水等级很低的耳机。业务方来质问,我打开日志,发现推理链路上确实“有据可循”——但按时间顺序查了一遍,发现推荐结果是由市场分析师Agent根据“用户喜欢轻便”这条上下文最终拍板的,而“是否需要防水”这个关键约束在上下文流转过程中被冲掉了。
这个案例让我痛下决心实现决策追溯。具体做法:
每条Agent决策都记录一个decision_record:
{ "agent": "market_analyst", "timestamp": "2025-01-15T10:23:45Z", "input_context_ids": ["ctx_001", "ctx_002", "ctx_003"], "context_scores": {"ctx_001": 0.82, "ctx_002": 0.65, "ctx_003": 0.03}, "decision": "recommend_sports_earphone_X", "confidence": 0.74, "reasoning_trace": "基于轻便偏好和价格区间,排除降噪功能权重" }input_context_ids是这次决策消费了哪些上下文,context_scores是每个上下文在决策中的参考权重。有了这些记录,任何一次输出都能反查“它当时看了什么、重用了什么、忽略了什么”。你在UI上点击一条推荐结果,就能展开一整棵决策树,一路追到最初的用户输入。
4.2 行为可观测:用结构化日志记录Agent的每一步动作
决策追溯解决了“这条结果怎么来的”问题,行为可观测还要解决“这个Agent到底在忙什么”的问题。多智能体系统Debug最痛苦的时刻是:你看到Agent A一直在循环调用工具,但不知道它为什么停不下来。
我给每个Agent设计了标准化的行为日志字段:
| 字段 | 说明 |
|---|---|
| agent_name | 哪个智能体 |
| action_type | plan / call_tool / message / decide |
| action_input | 进入这次动作的输入摘要 |
| action_output | 动作的产出摘要 |
| tool_name | 如果是调用工具,用了哪个 |
| duration_ms | 动作耗时 |
| status | success / failed / retry |
这些日志做成结构化之后,配合分布式追踪工具,能够可视化还原出系统运行的整体时序。我用一套开源自建方案:结构化日志 → ClickHouse存储 → Grafana面板展示,跑了一次就发现了很多靠肉眼很难发现的问题,比如“Agent C在每次任务开始前都会重复调用同一个资料查询工具,白白增加延迟”。
4.3 透明性和性能的取舍:日志采样与全量记录
有人会问:每条决策都记录这么详细,开销会不会太大?我的回答是:运行时只记录摘要和索引,全量上下文按需存储。
具体操作:
- 决策记录的
input_context_ids存上下文ID,不存全文 - 上下文全文按
producer_agent和时间分区存储,保留3天 - 核心链路(用户意图→最终输出)做全量追踪,旁路信息做1/10采样
这样既能保证95%的排查场景有据可查,又不会让日志写入拖垮系统性能。如果你发现日志系统本身成了性能瓶颈,那说明你该优化的不是日志,而是上下文的广度过量——上下文工程做得越好,系统里值得记录的决策就越少。
5. 压缩、总结与选择性遗忘:让上下文保持“够用就行”
上下文工程做久了,你就会发现它其实是一门上下文的内存管理。模型窗口有限,系统的有效上下文容量有限,任何无限增长的上下文策略都必然失败。我把这个章节起名叫“够用就行”,是因为多智能体系统的目标不是记住所有上下文,而是记住“当前任务完成所需的最小充分信息集”。
5.1 滚动摘要:最朴素也最可靠的手段
滚动摘要的做法是:当上下文累积到一定阈值(比如5000 token),让一个专门的“摘要Agent”把较早的历史压缩成一段摘要,替换掉原文。
要注意的是,摘要Agent本身也会犯错。我实践下来的技巧是:
- 摘要里保留“用户显式表达过且未被后续信息推翻的约束”,作为不可丢失的上下文
- 摘要要带时间戳,后续如果发现摘要丢失了关键信息,可以回溯原文
- 摘要Agent的输入应该包括历史摘要链,而不是只压缩最新一段
这个方法看起来简单,却是目前最可靠的上下文压舱石。我在生产环境里跑了很久,它几乎没出过大错。
5.2 结构化截断:把上下文按类型分区管理
比滚动摘要更精细的做法,是把上下文按类型拆开,为每种类型设定不同的保留策略。比如:
| 上下文类型 | 保留策略 | 典型容量 |
|---|---|---|
| 用户核心意图 | 全量保留,不截断 | 500 token |
| 用户临时输入 | 每轮保留最近5条 | 1000 token |
| 工具调用结果 | 保留最近3次调用结果 | 1500 token |
| Agent间消息 | 保留最近10条关键消息 | 2000 token |
| 全局状态 | 只保留最新快照 | 300 token |
这种结构化截断比“模糊地保留对话历史”高效得多,因为不同类型的上下文对决策的贡献模式完全不同。用户核心意图需要全程在场,工具调用结果只需要最近几次,中期结论则看是否需要参与下一轮决策。
5.3 选择性遗忘:让不重要的上下文主动让位
选择性遗忘是我比较推崇的一种策略。它跟人类的记忆机制很像:不是所有信息都值得留存在长期记忆里,有些信息存在过就足够了。
我的实现方式:
- 每条上下文进入系统时,给它打上
importance_score(0到1) - 上下文经过一次衰减周期后,
current_score = importance_score × decay_factor - 当系统上下文总量超过阈值时,优先淘汰
current_score最低的上下文 - 被淘汰的上下文进入归档,不算彻底丢失
这个机制的意义在复杂系统里特别明显。管线里面同时跑着十几个Agent Agent,每个平均消费5000 token的上下文,如果全部保留,很快就把窗口撑爆。选择性遗忘保证了系统永远在“够用”的数据范围内运行。
5.4 检索式回顾:为遗忘开一扇“后门”
选择性遗忘的隐患是:某个被淘汰的上下文后续又被需要了怎么办?我的解法是检索式回顾。
保留一份精简的历史索引(只存上下文摘要、关键词、向量),当Agent的当前任务上下文匹配度不够时,系统自动发起“回顾查询”,把相关的历史上下文的完整版本捞回来。为了实现这一点,你的上下文归档不能只存纯文本,必须带矢量索引。这样“遗忘”和“回忆”才不是一对矛盾,而是互为备份。
用个生活化的比喻:你不记得上周五中午吃了什么,但你知道自己有那么一段经历,通过翻相册(检索索引)就能想起来。上下文工程也是这个道理,遗忘的是当前的活跃上下文,保留的是“可被回忆的索引”。
6. 实测经验:透明架构引擎落地时最值得盯住的六个参数
最后这一章分享一些我从实际项目中趟出来的调参经验,不一定放之四海而皆准,但值得你参考。这些参数都会直接影响系统的推理质量、可观测性和运行成本,我按重要性排序来讲。
6.1 每条上下文的活跃跨度上限
参数:context_ttl(上下文过期时间)经验值:用户核心意图不设TTL,工具结果TTL为5分钟,Agent中间消息TTL为20分钟。
这个参数决定了“过期的上下文还在影响当前决策”的几率。TTL设得太短,长任务会丢失中间状态;TTL设得太长,过时信息会污染后续决策。我的原则是:宁可丢也不能脏,丢了的上下文可以通过检索回顾找回,脏了的上下文会让Agent做出错误决策且难以排查。
6.2 全局上下文与局部上下文的隔离级别
参数:scope_isolation_level经验值:非关键状态全部设为Agent私有作用域,只有跨Agent协作必需的上下文才放入全局作用域。
我在第3.3节里提过作用域隔离,这里再说一个实际数字:我把一个系统的全局上下文比例从80%降到30%之后,Agent之间互相干扰的错误降低了大约一半。全局上下文越多,系统越容易出现“看似相关实则无关”的干扰。
6.3 决策日志的采样率
参数:trace_sample_rate经验值:核心决策链路100%采样,旁路Agent动作20%采样,测试环境100%采样。
全量采样在debug期很有用,但生产环境太贵了。核心链路指的是“用户输入→主推进Agent→最终输出者”这条主链,必须全量记录;其他Agent的动作、工具调用等做低比例采样,够发现问题就可以。
6.4 上下文压缩触发的阈值
参数:context_compaction_threshold经验值:模型最大窗口的40%作为硬阈值,到30%时启动预警压缩。
触发压缩太晚,容易在压缩完成的瞬间把窗口撑爆;触发太早,则频繁压缩浪费token和延迟。40%数值是我在多个模型上实测的平衡点。
6.5 Agent决策的历史快照保留数
参数:decision_snapshot_count经验值:每个Agent保留最近10次决策快照,超出部分进归档。
保留10次快照的目的,是复现“Agent在连续10轮决策中的推理路径是否稳定”。如果快照显示Agent的推理路径反复横跳,大概率是上下文有冲突,这时候需要回头查作用域和竞争处理逻辑。
6.6 上下文索引的更新延迟
参数:index_refresh_interval经验值:实时索引开销过大,延迟太长有问题。我的平衡点是:全局索引2秒刷新,Agent私有索引实时刷新。
由于“检索式回顾”依赖索引,索引延迟直接决定了Agent“回忆”历史上下文的时效性。私有索引实时刷新是因为Agent一旦需要立刻回顾自己的历史决策,等不了2秒;全局索引2秒刷新对大多数异步协作场景足够。
写在后面:透明架构不是一蹴而就的“功能”,而是一套思维习惯
做得越久,我越觉得上下文工程里最难的实操不是写代码,而是改变思考方式。以前做单Agent提示词工程,解决问题靠改Prompt;现在做多Agent系统,要习惯性地追问三个问题:
- 这个Agent当前“看到了什么”上下文?
- 这条上下文是从哪里来的、由谁产生的?
- 如果这次决策出错了,我能不能在30秒内定位到是哪个环节?
把这三个问题变成系统设计的一部分,“透明架构引擎”才算真正落地。我在实际操作中还有一个习惯,每个新版本上线前都会做一次“上下文走查”——模拟一次完整用户请求,跟踪它产生的每一条上下文从生成到归档的全过程,看哪些上下文在流转中丢失、哪些上下文被无谓复制、哪些决策环节缺乏足够的上下文支撑。这比跑任何测试用例都更能暴露系统的真实问题。
顺便说一句,如果你还没给系统加决策追踪,可以先用最简单的文本日志跑起来,不要追求一步到位。能定位问题比看起来高级重要得多。上下文工程这条路没有终点,把透明性刻进骨架,后面每加一个智能体都会轻松一分。