基于信息素网络的LLM上下文管理:每条记忆自主决定去留

发布时间:2026/10/25 21:06:10
基于信息素网络的LLM上下文管理:每条记忆自主决定去留

做过智能体应用的人,几乎都会在某个深夜遇到同一种崩溃:长期会话进行到后半段,用户之前提过的一个关键约束被硬生生挤出上下文窗口,于是智能体开始一本正经地胡说八道。我之前对付这个问题的方式很朴素——调大窗口、压缩历史、把早期对话塞进向量库,但每一种方案都在某个具体场景里露了怯。后来接触到 pheromone-network,它的思路和前面几种都不一样:把上下文当作一张会呼吸的信息素网络来管理,让每条记忆自己决定"该不该被想起"。这篇文章我会从核心机制讲起,再给出一套可以照着抄的接入方式,最后把调参时踩过的坑都摊开说清楚。

1. 先复盘"上下文爆炸"的三种死法:为什么常规方案各有短板

1.1 三种主流的上下文失效模式

做 LLM 应用做了大半年后,我对"上下文管理"这件事的理解已经从"把消息塞进去"变成了"怎么在塞进去之前就保住关键信息"。上下文窗口不是不够用,而是我们的组织方式错了。

最常见的失效模式有三种:

  • Token 超限:会话一长,消息列表直接顶破窗口上限,系统被迫截断或报错。这是最表层的问题,但也是最容易让人误判的——很多人以为"窗口开大一点"就解决了,实际上只是把爆发点往后推了推。
  • 相关性稀释:窗口没满,但早期对话被大量无关过程性内容压在底部。大模型不是机器,它对五轮之前的指令权重天然偏低,越长的前缀越容易让关键约束变成"背景噪音"。
  • 关键信息被截断:用户在第 5 轮说的"我家猫咪叫豆豆,对百合花过敏",第 20 轮已经被挤到窗口外面。等第 55 轮你问它"豆豆能不能喝牛奶",它一脸茫然。这种失效是最伤的,因为信息当时没被识别为重要,事后想捞都捞不回来。

你去看线上 AI 助手产品的用户反馈,大量"它怎么忘了""我刚才不是说过了吗",基本都能归到第二、第三种模式里。与其说是模型能力不行,不如说是上下文管理没有跟上会话的真实结构。

1.2 固定窗口、摘要压缩、向量检索各自的边界

主流的应对方案我也都试过,各有各的适用面,但边界都很清楚:

方案能解决的问题失效场景隐性成本
固定窗口(截取最近 N 轮)控制 token 上限,简单直接重要早期信息会被无差别淘汰几乎没有
摘要压缩(对旧历史分块摘要)保留语义主线,压缩体量细节大量丢失,摘要本身会引入错误归纳;每轮摘要要额外调用一次模型摘要模型的延迟和 token 消耗
向量数据库检索(按语义召回旧内容)找回"相似的过去"向量相似不等于"这条现在重要";没有时效概念,昨天的临时状态今天还会被捞回来嵌入计算、存储、召回链路复杂度

固定窗口的问题是它不看内容价值,只看时间先后;摘要压缩的问题是它在用自己的理解替你做取舍,而且是一次性的有损压缩;向量检索的问题是它把"上下文"当成静态文档库,忽略了对话记忆是有生命周期的。

一个让我印象很深的场景:用户第 2 轮说"我们预算大概是 5 万以内,别超",第 10 轮又聊了产品方案,第 30 轮开始讨论具体采购。用固定窗口,第 2 轮的预算约束大概率已经被挤掉了;用摘要压缩,摘要模型很可能把预算写成"5 万左右"这种模糊值;用向量检索,预算约束确实能召回,但也会把第 28 轮随便提到的另一个价格拉回来,因为它语义更接近"钱"。你会发现,常规方案要么不挑食,要么挑错食。

1.3 信息素视角:让每条记忆自己决定"该不该被想起"

我后来理解了一个更好的切入角度:上下文管理本质上不是存储问题,而是优先级问题。谁该留在窗口里、谁该降级、谁该被彻底忘记,应该由这条记忆本身的使用模式和时间共同决定。

这就是 pheromone-network 最打动我的地方。它把对话记忆建模成一张"信息素网络",每条记忆是网络里的一个节点,记忆之间的关联是边,每条边上都附着强度值——这个强度就是信息素。节点被读取、被引用得越频繁,信息素越强;长时间不被碰,就自然衰减。所有记忆都在一个统一的机制里互相竞争,不需要你手动去判断哪句话更重要。

听起来有点抽象,但等你看到后面的衰减公式和组装逻辑,会发现它其实就是在用一套显式的动态权重,解决大模型隐性注意力权重解决不了的长期依赖问题。

2. pheromone-network 的信息素模型:把记忆当路径而不是文件

2.1 从蚂蚁找食物说起:为什么"路径"比"文件"更贴切

信息素这个隐喻来自蚂蚁。蚂蚁觅食时会释放信息素标记走过的路径,后来的蚂蚁倾向于走信息素更浓的路,走的人越多,这条路的浓度越高;时间一长没人走,信息素挥发,这条路就慢慢消失。这个正反馈加时间衰减的机制,天然适合描述"哪些对话记忆值得被保留"。

传统上下文管理把每一条消息当文件存起来,调用时按文件名或内容相似度去翻。而 pheromone-network 把记忆当路径:一条记忆之所以重要,不是因为它写得好、存得规范,而是因为它在后续对话里被反复走到。比如"用户不吃香菜"这条约束,每一轮餐饮推荐都会命中它、强化它,它的信息素强度会一路走高;而"用户昨天说今天下雨"这种一次性状态,错过那段时间后就没人再引用,自然衰减沉底。

这种建模方式有个额外的好处:记忆与记忆之间的关联会被显式记录。你说"猫咪叫豆豆"和"豆豆对百合花过敏"天然应该靠在一起,如果里面还有一张去年拍的宠物照片,这张照片的节点也会和它们建立边。后续检索时,从"猫咪"这个入口出发,能顺着信息素边的走向把一组相关记忆一次性捞出来,而不是在消息列表里用关键词硬翻。

2.2 节点、边与三步更新机制:沉积、强化、挥发

结构上,pheromone-network 的核心抽象非常少,就三类:

  • ContextItem:一条上下文记忆。它包含内容(文本、工具调用结果、用户特征等)、元信息和当前信息素强度。
  • Edge:两个 ContextItem 之间的关联关系,同样携带信息素强度。
  • PheromoneNetwork:整个图结构的载体,负责所有更新和查询。

对这层结构的所有写操作,都可以归纳成三个动作:

  1. 沉积(deposit):新信息进入上下文时,写入一个节点并赋予初始信息素强度。可以是用户状态、任务目标、关键事实,也可以是工具返回结果。
  2. 强化(reinforce):当一条已有记忆在后续对话中被引用、命中、或触发相关检索时,对它的信息素进行增量加强。
  3. 挥发(evaporate):每一轮对话结束后,全网络所有节点的信息素按一定比例衰减。这是"遗忘"机制的来源。

对应到更新逻辑,可以简化成一句递推式:

S(t+1) = S(t) × (1 - η) + Δ

其中 S(t) 是当前信息素强度,η 是挥发率(取值 0 到 1),Δ 是这一次沉积或强化带来的增量。

举一组直观的数字:假设 η = 0.05,每轮对话结束后所有节点都衰减 5%,而某个节点每轮都会被强化 +1。这条节点的强度会稳定在一个高位;另有一条只出现过一次的孤立记忆,初始强度 1,没被强化,5 轮之后强度大约变成 1×0.95^5 ≈ 0.77,20 轮后只剩 0.36,再配合阈值清理,它就逐渐退出核心上下文了。是不是很像现实中"聊过一嘴"和"反复确认过"的事情在大脑里的留存差异?

2.3 遗忘不是删除:降级、归档与摘要层

有个容易被忽视的设计:信息素网络的"遗忘"并不等于物理删除。强度掉到阈值以下后,有三种处理路径:

  • 彻底丢弃:适用于临时的过程性内容,比如"这轮回答里提到的草稿编号",后面几乎不可能复用,直接删最省空间。
  • 压缩降级:把一组低强度但还有潜在价值的节点折叠成一个摘要节点。这相当于把散落的对话记录压缩成"某人某时提过某件事",强度重新给予一个中低档初始值,如果后续又被引用,再强化。
  • 长期归档:对于可能需要但眼前用不到的细节,比如用户三个月前提过的家庭地址,可以转存到外部存储,只在信息素网络中保留一个轻量索引节点。

这种"三档遗忘"比单纯删掉实用得多。我实际用下来的感觉是:它把"忽略"和"彻底忘记"区分开了。大模型提示词的 context 里可以没有某条细节,但信息素网络里还留着它的影子,哪天用户重新提起来,系统可以从影子节点出发把原始内容找回来。

实际落地时,我通常会把网络结构分成三层:核心上下文区(信息素高于高阈值的节点)、短期工作区(中等强度节点)、摘要归档区(低强度节点)。每一层的存储介质可以不一样,核心区放内存供快速调用,摘要区放本地库甚至远端向量库。这个分层方案不是 pheromone-network 强制要求的,但配合它的信息素强度做分层,收益最明显。

3. 环境准备与最小 Demo:让第一条记忆"活"起来

3.1 安装与运行环境

pheromone-network 目前以 Python 生态接入最顺手,建议在虚拟环境里装。官方推荐 Python 3.10 以上,安装命令很简单:

pip install pheromone-network

如果你是和我一样用 Poetry 或 uv 管理依赖,按项目习惯加进去就行。安装完可以用以下方式确认版本是否正常:

python -c "import pheromone_network; print(pheromone_network.__version__)"

需要注意,不同小版本的 API 命名可能有细微差异,下面代码我按当前稳定版整理,具体以你装到的版本文档为准。

3.2 最小上下文管理示例

光看概念容易飘,直接跑才是最直观的。下面这段代码做了三件事:写入两条记忆、建立关联边、更新一次信息素强度。

from pheromone_network import PheromoneNetwork, ContextItem # 初始化网络 net = PheromoneNetwork() # 写入第一条上下文:用户约束 item_budget = ContextItem( content="采购预算不超过 5 万,这是硬约束", meta={"type": "constraint", "source": "user"}, ) net.deposit(item_budget, initial_strength=8.0) # 写入第二条上下文:某次对话里提到的候选产品 item_product = ContextItem( content="用户提到过某品牌的一体机,价位在 4 万左右", meta={"type": "fact", "source": "chat"}, ) net.deposit(item_product, initial_strength=3.0) # 建立关联:产品候选挂在预算约束下 net.connect(item_budget.id, item_product.id, initial_strength=2.0) # 模拟一轮对话结束后做挥发 net.evaporate(rate=0.05)

这段代码就是一个最简单的完整生命周期。注意我给了不同的初始强度:约束类内容初始强度给到 8,纯事实类信息只给 3。从第一步开始就用强度差把重要级拉开,比所有节点一律平等高效得多。

3.3 检查信息素分布:验证网络真的在"记住"

写入后怎么确认它记住了?我一般直接打印网络状态:

# 查看当前全网络节点 for item in net.all_items(): print(item.id, item.strength, item.content)

输出大致会是:

a1b2c3... 7.6 采购预算不超过 5 万,这是硬约束 d4e5f6... 2.85 用户提到过某品牌的一体机,价位在 4 万左右

预算约束从 8.0 衰减到 7.6,候选产品从 3.0 衰减到 2.85,关联度(边强度)类似地按比例挥发。这时候如果你把候选产品对应的节点强化一次,它的强度会重新抬头。动态平衡就这么跑起来了。

我还习惯把网络拓扑结构定期导出成 GraphML 或 JSON,用来做离线分析:看看哪些节点频繁出现在高信息素区、哪些关联边长时间没人走。这一步对调参太重要了,能直接看出系统认为哪些信息重要,和用户实际关心哪些信息,是不是同一批。

4. 核心操作实操:强度设定、半衰期换算与上下文组装

4.1 初始强度怎么定:先给上下文分个"阶级"

初始强度是整个系统的地基。如果所有内容初始强度都设成 5,信息素机制就退化成单纯的 LRU,那还不如直接用固定窗口。

我自己长期调出来的初始强度参考值如下:

内容类型举例初始强度说明
硬约束"不要调用外部支付接口"、"预算上限 5 万"8~10不随对话轮次削弱,接受慢衰减
用户偏好"不吃香菜"、"喜欢简洁回复"6~7每次相关对话引用都会强化
事实快照"用户养了一只叫豆豆的猫"4~5需要长期留存但不需要每轮强调
临时状态"当前正在查看订单详情页"2~3短生命周期,快速挥发
过程推理模型中间分析、临时草稿1默认低权重,不参与核心组装

这个"阶级"不是拍脑袋拍出来的,而是根据一条记忆跨轮次被重复引用的概率倒推的。硬约束基本每一轮都要被模型看见,它必须信息素最高;过程推理只在当轮有意义,给 1 就够,配合阈值清理迅速淡出。

值得注意的是"用户偏好"类。它不像硬约束那样每轮必须出现,但一旦出现对应的场景(比如推荐餐厅),它就是决定性信息。所以它的初始强度我设在中高位,同时在每次命中时给一个不小的强化增量。实际跑出来的效果是:偏好信息不会占满窗口,但一旦相关,它的排名会瞬间上浮到组装列表前列。

4.2 挥发率怎么算:用半衰期,而不是拍脑袋

挥发率 η 直接控制"一条没人引用的记忆,多久以后基本被遗忘"。凭感觉调容易出问题——设得太大,用户刚说的事转头就忘;设得太小,旧记忆永远赖着不走,token 降不下来。

最有效的调法是用半衰期反推。半衰期的意思是:一条记忆在不被强化的情况下,强度降到初始一半所需的时间。连续衰减模型下,半衰期 T_half 和衰减系数 λ 的关系是:

λ = ln2 / T_half

如果是按对话轮次衰减而不是按真实时间衰减,可以把"半衰期"换成"半衰轮次"。举个例子:你想让一条临时状态在 15 轮之后强度减半,那每轮的衰减保留率 r 满足 r^15 = 0.5,解出来 r≈0.955,对应 η≈0.045。

我用 Python 做过一个简单的调参辅助函数:

import math def half_life_to_rate(half_life_rounds): # 返回每轮衰减率 eta return 1 - 0.5 ** (1 / half_life_rounds) # 例子:临时状态希望 15 轮后减半 print(half_life_to_rate(15)) # 约 0.045 # 例子:硬约束希望 200 轮后减半 print(half_life_to_rate(200)) # 约 0.0035

我的实际配置是:硬约束 η≈0.005,用户偏好 η≈0.02,临时状态 η≈0.05,过程推理 η≈0.1。每个类型一套挥发率,而不是全网络共用一个值。这个"分层挥发"是让信息素网络真正实用的关键——单一挥发率永远没法同时兼顾"重要约束不能忘"和"临时状态快速清空"两个诉求。

4.3 读取组装:按信息素强度把 token 预算花在刀刃上

信息素网络管理上下文的最终输出,要组装成一段给大模型的消息。组装逻辑其实就三步:

  1. 召回候选:从当前对话中提取查询向量或关键词,在信息素网络里做一轮子图检索,拿到若干候选节点。
  2. 强度排序:按节点信息素强度从高到低排序。这是和向量召回最本质的区别——向量召回只看语义近不近,信息素排序额外看"这条记忆在历史中被依赖的程度"。
  3. 预算分配:把可用 token 预算按排序依次分配,信息素高的先占位置。

示意代码如下:

def assemble_context(net, query, max_tokens=2000, reserved_tokens=600): # 1. 召回候选 candidates = net.search(query, top_k=50) # 2. 强度排序 ranked = sorted(candidates, key=lambda x: x.strength, reverse=True) # 3. 预算分配,这里简化成先拼后截断 assembled = [] used = 0 for item in ranked: cost = estimate_tokens(item.content) if used + cost > max_tokens - reserved_tokens: break assembled.append(item.content) used += cost # reserved_tokens 留给最近的用户输入和系统指令 return "\n".join(assembled)

为什么需要 reserved_tokens?因为信息素网络负责的是"长期记忆",而最近一轮的用户输入永远应该完整保留。如果你把所有 token 都花在历史记忆上,当前这轮的任务指令反而被挤掉了,这是本末倒置。我习惯预留 20%~30% 的窗口给当前对话内容。

一个容易踩的细节:排序之前必须剔除与当前任务冲突的节点。比如预算约束说 5 万,候选列表里恰好有一条几个月前的"预算可以到 8 万"的旧讨论,信息素强度还不低,如果你不做冲突检查直接拼进去,模型就会左右为难。信息素只能帮你解决"重要度"问题,不能替你解决"一致性"问题。

4.4 读取即强化:别让"使用"和"强度"分家

最后提一个机制层面的坑:读取操作必须顺带触发强化。信息素理论和实际人脑记忆一样,被调用得越多,记忆越牢。如果你只读不强化,那么一条被高频使用的记忆和一条被低频使用的记忆在强度上会逐渐同化,系统就失去了自适应能力。

所以在你封装检索函数时,标准做法是:

def load_context(net, item_id, query): item = net.load(item_id) # 读取本身是一次引用,顺手强化节点和关联边 net.reinforce(item_id, delta=0.5) for edge_id in net.edges_of(item_id): net.reinforce_edge(edge_id, delta=0.2) return item.content

强化增量不用给太大,让系统平滑调整。我一般节点增量 0.3~0.5,边增量 0.1~0.2。核心原则是:一次命中带来的提升,要小于这条记忆从"完全不被引用"状态恢复到可被召回状态所需的总强化次数,不然某一轮的偶然命中会让一条垃圾记忆的排名瞬间飙升。

5. 实战案例:做一个 60 轮不崩的多轮助手

5.1 场景需求拆解

为了说明这套机制的实际效果,我拿一个真实做过的场景举例:一个本地生活助理,用户和它连续聊了 60 轮,内容包括点餐、出行、宠物照顾、工作会议等穿插话题。

在第 2 轮,用户说"我家猫叫豆豆,千万别给它喂巧克力";第 8 轮,用户说"下午 3 点有个电话会议";第 23 轮,用户说"我不吃香菜,所有推荐都避开"。之后聊天还继续了三十多轮,期间夹杂了大量无关内容:某家外卖的优惠券失效、周末去哪玩、某首歌单曲循环了一天等等。到了第 60 轮,用户问"下午安排是什么?顺便帮我推荐个晚饭"。

这个场景的杀手之处在于:第 8 轮的会议信息早就滑出了固定窗口,第 2 轮的猫咪约束更是几十轮以前的事,但它们在语义上都和最后一轮的决策强相关——推荐晚饭要考虑香菜偏好,安排下午要考虑会议时间。

我对比了三种方案在同一场景的表现:

方案能否记住"会议 3 点"能否记住"不吃香菜"能否记住"猫叫豆豆"额外 token 开销
固定窗口(最近 10 轮)否否否无
每 5 轮一次摘要压缩可能,但摘要易模糊可能可能每次约 300~500 token
pheromone-network是是是检索+排序开销约 50~100 token

这个对比是真实跑出来的倾向性结论,三者都经过多轮测试。固定窗口最省钱但最容易"失忆";摘要压缩能留存一部分信息但会引入歧义——实测中摘要把"3 点电话会议"总结成了"下午有安排",导致模型在第 60 轮根本不知道具体时间;而信息素网络因为从始至终没有对原始内容做有损压缩,只做强度排序,所以时间、人名、食物禁忌这些精确细节都能原样保存下来。

5.2 接入代码的骨干逻辑

这个助手接入 pheromone-network 后,核心逻辑分两段:

每一轮对话结束后,写入/更新节点:

def after_turn(net, user_text, assistant_text, parsed): # parsed 里是意图识别和关键信息抽取结果 for fact in parsed["facts"]: item = net.find_by_content(fact["text"]) if item: net.reinforce(item.id, delta=fact["importance"]) else: net.deposit( ContextItem( content=fact["text"], meta={"type": fact["type"]}, ), initial_strength=fact["importance"], ) # 挥发一步 net.evaporate(rate=0.02)

每一轮对话开始前,调用信息素检索并组装上下文:

def before_turn(net, user_query): # 先用扩展后的查询跑一次信息素检索 related = net.search(user_query, top_k=30) ranked = sorted(related, key=lambda x: x.strength, reverse=True) # 把信息素强度高的硬约束和用户偏好固定放最前面 hard_context = [ c.content for c in ranked if c.meta.get("type") in ("constraint", "preference") ] return assemble_context(net, user_query, hard_context=hard_context)

这一段代码没什么高深技巧,真正让系统"不崩"的是第 2 轮和第 23 轮写入的那些节点,经过了三十多轮衰减依然保持在较高强度。因为它们各自被后续对话命中过:猫咪约束在第 31 轮用户提到"豆豆掉毛"时被强化过一次;香菜偏好在第 29 轮用户说"上次那家店放了好多香菜,烦死了"时又被命中过一次。这就是信息素正反馈周期的实际运作——经常被提的事情,系统会越来越记得住。

5.3 观察到的意外行为与解释

有意思的是第 8 轮的会议信息。它在第 10、12 轮都被用户主动提及("3 点之前把材料发我"),所以信息素很强;之后 20 轮没人再提它,强度开始缓降,但因为它初始强度高、半衰期长,第 60 轮组装时依然排在候选列表的前列。这说明信息素机制对"曾经重要、之后沉默"的信息有很强的保留能力——这正是固定窗口做不到的。

另一条低强度节点则被正确遗忘了:第 5 轮用户提到"今天外卖红包要过期",之后没人再提,到第 25 轮左右就低于阈值被清理了。它本来就是一次性状态,留着只会浪费 token,丢掉完全不影响对话质量。

那种传统的"把最近 N 轮全部塞进去"的做法,本质上是在用窗口位置当权重——近的必然重要,远的必然不重要。信息素网络则把这个假设打碎了,让权重从使用模式中自己长出来。

6. 调试记录:蒸发率、冷启动与并发一致性的坑

6.1 蒸发率过高还是过低:用现象反推

这个系统跑起来之后,第一个让我头疼的就是蒸发率。一开始图省事,全网络统一设 η=0.05,结果第 17 轮用户刚提的"今晚别订那家,已经关了"这种临时信息,第 19 轮就被我自己的系统忽略了,用户当场发火。后来把临时状态类的 η 调成 0.03,又发现太多三个月前的旧闲聊占着高信息素排名,检索结果老是被"陈年往事"干扰。

最终固定成按类型分层配置:硬约束和用户偏好慢衰减,临时状态快衰减,过程推理超快衰减。每个类型独立调,才能在同一套机制里同时实现"该记住的死活不忘"和"该忘的麻溜忘掉"。

判断标准很简单:如果用户在上一轮说的状态本轮就失效了,说明对应类型的 η 太大;如果一个月前的旧事实还长期霸占核心上下文、导致 token 降不下来,说明 η 太小。用现象反推参数,比自己闷头调快得多。

6.2 冷启动保护期:新节点最容易"出师未捷身先死"

更隐蔽的坑是冷启动问题。一条新写入的节点,初始强度就算给到 5,它也需要整整一轮对话后才能接受强化。如果挥发率是每轮 5%,它还没等到第一次被引用就已经掉到 4.75。在快速衰减类型里,初始强度给低了,可能都没机会熬到被命中。

我的解法是给所有新节点加一个免衰减倒计时。

# 示意:新节点在前 3 轮不参与 evaporate class ProtectedItem(ContextItem): def __init__(self, *args, protect_rounds=3, **kwargs): super().__init__(*args, **kwargs) self.remaining_protection = protect_rounds def tick(self): if self.remaining_protection > 0: self.remaining_protection -= 1 return # 本轮不衰减 super().tick()

保护期不能太长,3~5 轮足够了。保护期太长,垃圾信息也会赖着不走。这个机制看似简单,但有没有它,新信息的存活率差很远。

6.3 并发写冲突:信息素回退的诡异 Bug

还有一个让我排查了两天的 Bug:多智能体并行运行时,某个节点的信息素强度会神秘回退。查下来发现是并发更新没有做原子操作。两个智能体同时读取到 S=8.0,各自做增强后写成 S=8.5 和 S=8.3,后写的那个把先写的覆盖了,等于一次强化被吞掉。

解法不复杂,要么在更新时读改写整个动作加锁,要么用 Redis 的 INCRBYFLOAT 这类原子自增操作。pheromone-network 的单机版本默认线程安全程度取决于你用的存储后端,如果底层是普通字典,并发写就可能丢更新。我自己在背靠 Redis 的部署方案里,把强化操作改成了原子自增,再没出过回退问题。

6.4 与 LangChain / CrewAI 等框架集成时的定位问题

最后说一个集成层面的心得:信息素网络不应该试图取代你正在用的 agent 框架,而是作为框架里的一个"记忆服务"存在。

很多人第一反应是"我能不能整个把 messages 历史换成 pheromone-network 的输出"。我的建议是分而治之:模型输入里,最近几轮 raw messages 照常提供,信息素网络只负责补充"从长期记忆里捞出来的关键上下文"。它应该接管的是那些"离当前很远但很重要"的内容,而不是把对话系统的短时工作记忆也重构一遍。

和工具调用结合时,要注意把工具返回结果也当成上下文节点写进网络,而不是只写用户消息。比如"查了一下天气,明天有雨"这个工具结果,在后续讨论是否带伞时会成为关键信息。工具结果的节点如果被后续引用,同样要触发强化,这样工具产生的知识也能跨轮次留存在上下文里。

7. 多智能体协作场景:用共享信息素网络传递"谁更懂这块"

7.1 隔离与共享的结构设计

如果你在做多智能体系统,信息素网络还能再往前走一步:让多个智能体共享同一个信息素网络,但各自维护不同的视图。具体做法是给每个节点和边打上归属标签,每个智能体默认只能看到自己工作区里的节点,同时可以读取共享区节点的信息素强度。

这个机制解决了一个很实际的问题:团队里不同智能体(比如采购助理、财务助理、日程助理)各自负责一块,但都要知道"用户预算 5 万"这种公共约束。如果各存各的,就会各记各的版本;如果全量共享,又会让每个智能体都背上无关上下文。

我的做法是:公共约束写入共享区,各智能体本地节点通过关联边指向共享约束。财务助理在审批时,通过边的信息素强弱能判断"这条预算约束最近有没有被采购助理反复确认过"——如果信息素很强,说明它是活跃约束,必须严格遵守;如果已经在衰减,说明项目可能进入尾声,可以放松阈值。信息素在这里直接变成了协作信号。

7.2 协作信号的一个实例

举一个简化例子:采购助理发现某供应商报价变了,更新了一个"报价变化"节点,并对共享区的"供应商价格基准"节点做了一次强化。财务助理运行时检索到该节点,发现它的信息素强度在最近几轮明显抬头,于是做出判断:"价格基准最近被更新过,预算需要重新核对"。它不需要去翻采购助理的完整聊天记录,光是看信息素的变化趋势就已经获得了足够多的判断依据。

这种用强度变化趋势做协作信号的做法,是我认为 pheromone-network 在多智能体场景里最有价值的地方。单独一个智能体的上下文管理还只是"自我记忆",到了多智能体阶段,它就变成了群体记忆的公共黑板。

7.3 哪些场景不建议用信息素网络

当然,它也不是万能的。我已经把它的适用边界摸得比较清楚了:

  • 极短会话(少于 10 轮):线性消息列表就是最优解,引入信息素网络纯属增加复杂度。
  • 每一轮都强相关的密聊场景:比如代码审查对话,几乎所有信息都有用,该保留的都应该保留,信息素网络的分级反而可能误伤。
  • 没有长期记忆需求的一次性问答:完全没必要。

信息素网络最适合的是"会话长、话题杂、事实密集、真正需要遗忘"的场景。判断标准可以揣在兜里:你现在的 agent 是不是经常因为忘了早期信息被用户骂?如果是,那它大概率值得你花一个晚上接入试试。

最后再补一句我做这个方案的个人体会:不要一上来就想搞完整的历史全量归档。先把信息素网络用在强约束和用户偏好这两类节点上,跑通"写入→衰减→强化→检索"这个闭环,你就能直观感受到它和固定窗口的差异。之后再把工具结果、过程推理逐步加进去,每一步都比硬着头皮一步到位稳得多。上下文管理没有银弹,pheromone-network 提供的是一套让系统"自己判断该记什么"的机制,而怎么用好这套机制,最终还是取决于你对对话场景的理解深度。