1. 从一场讲座说起:常驻智能体到底在解决什么问题
第一次听到“常驻智能体”这个词,很多人会下意识地把它理解成“一直开着的聊天机器人”。这个理解不算错,但远远不够。常驻智能体的核心不在于“一直在线”,而在于它拥有持续存在的状态——它能记住上一次交互发生了什么,能在没有人类实时指令的情况下继续推进任务,能在长时间跨度里维持一个稳定的目标。这跟传统那种“你问一句、它答一句、对话结束就失忆”的会话式交互,是完全不同的物种。
我在实际接触这类架构之前,也踩过一个很典型的坑:当时我试图用一个普通的对话式接口去模拟“持续工作”的效果,做法是把历史记录全部塞进上下文里。结果跑了不到二十轮,上下文就爆了,而且模型开始出现明显的“注意力涣散”——前面说过的约束,后面就忘了。这个失败经历让我意识到,常驻智能体不是靠“把历史堆进去”就能实现的,它需要一套专门的架构设计。
这场讲座之所以值得拿出来细讲,是因为它把常驻智能体拆成了几个可以独立理解的模块,并且重点讲了“上下文工程”这个容易被忽视但极其关键的环节。所谓上下文工程,说白了就是:在有限的上下文窗口里,决定放什么、不放什么、以什么形式放、什么时候更新。这件事听起来简单,做起来极难,因为它直接决定了智能体能不能在长时间运行中保持“清醒”。
这篇文章适合几类人看:一是正在做智能体相关项目的开发者,二是对长时任务自动化感兴趣的技术爱好者,三是想理解“为什么我的智能体跑着跑着就变傻”这个问题的从业者。我会尽量把架构层面的东西讲透,同时给出可以落地的思路和参数层面的参考,而不是停留在概念层面。
2. 常驻智能体的四层架构拆解
2.1 为什么不能把“记忆”简单等同于“历史记录”
很多人做智能体时的第一反应是:记忆嘛,不就是把之前的对话存下来,需要的时候再读出来?这个思路在小规模场景下能用,但一旦任务变长,就会暴露两个致命问题。
第一个问题是上下文预算的刚性约束。假设一个模型的上下文窗口是 128K token,听起来很大,但如果智能体每轮交互产生 2000 token 的记录,跑 60 轮就满了。而且这还没算上系统提示词、工具定义、当前任务描述这些固定开销。实际可用的“历史空间”可能只有一半。
第二个问题更隐蔽:历史记录里充满了噪声。智能体在探索过程中会产生大量试错、无效尝试、重复确认。这些内容如果原封不动地留在上下文里,会严重干扰模型的判断。我实测过一个案例,智能体在解决一个配置问题时,前面有七八轮都在尝试错误的路径,当我把这些错误路径全部保留时,模型在后续推理中反复“回退”到那些错误方案上,像是被锚定了一样。
所以常驻智能体的记忆设计,核心不是“存多少”,而是“怎么筛选和结构化”。讲座里提到的做法是把记忆分成几个层次,每一层承担不同的职责,而不是混在一起。
2.2 状态层、记忆层、规划层、执行层的分工
把常驻智能体的架构拆开看,我倾向于把它理解成四个协作的层次,每一层解决一个特定问题。
状态层负责维护“当前正在做什么”。它记录的是任务的即时状态:当前处于哪个阶段、上一个动作的结果是什么、下一步的预期是什么。这一层的内容更新频繁,但体量很小,通常几百 token 就够。它的作用是让智能体在每一轮开始时能快速“回到现场”,而不需要重新阅读全部历史。
记忆层负责长期信息的沉淀。这里存的不是原始对话,而是经过提炼的事实、结论、约束条件。比如“用户要求输出必须是 JSON 格式”“这个接口的速率限制是每分钟 60 次”“之前尝试过的方案 A 失败了,原因是权限不足”。这些信息以结构化的形式存储,需要时才检索注入。
规划层负责把大目标拆成可执行的步骤。常驻智能体面对的任务往往不是一步能完成的,规划层的作用是维护一个任务队列,并且在执行过程中根据实际情况动态调整。这里有个关键设计:规划层不应该把整个计划一次性写死,而是保持“可修订”的状态。
执行层负责实际调用工具、生成输出、与外部环境交互。这一层是最“薄”的,它不应该包含复杂的决策逻辑,而是忠实地执行规划层下发的指令,并把结果反馈回去。
这四层的关系可以这样理解:规划层是大脑,决定做什么;执行层是手脚,负责做;状态层是工作台,放着当前正在处理的材料;记忆层是档案柜,存放需要长期保留的资料。四者各司其职,才能让智能体在长时间运行中不混乱。
2.3 各层之间的数据流与更新时机
架构设计里最容易出问题的地方,往往不是单个模块本身,而是模块之间的数据流。我见过不少项目,每一层单独看都合理,但连起来跑就出问题,原因通常是更新时机没设计好。
状态层的更新应该发生在每一次动作执行之后,而且是立即更新。这样做的理由是:状态层是下一轮决策的直接依据,如果它滞后了,智能体就会基于过时信息做判断。更新内容要精简,只保留“当前阶段、上一步结果、下一步意图”这三个要素。
记忆层的更新则应该按需触发,而不是每轮都写。触发条件可以是:发现了一个新的约束条件、完成了一个子任务、或者一次尝试被明确判定为失败。写入记忆层之前要经过一次“提炼”,把原始事件压缩成一句可复用的事实。这个提炼过程本身可以交给模型来做,但要给它明确的格式要求。
规划层的更新频率最低,通常在一个子任务完成或整体方向需要调整时才更新。频繁改动计划会让智能体失去连贯性,但完全不改又会导致它在错误方向上越走越远。我的经验是设置一个“偏离阈值”:当连续 N 次执行结果都偏离预期时,强制触发规划层的重新评估。
执行层不维护状态,它每次从状态层和规划层读取输入,执行完把结果写回状态层。这种“无状态”设计让执行层可以并行化,也更容易测试。
3. 上下文工程:决定智能体“清醒度”的关键操作
3.1 上下文窗口的预算分配策略
上下文工程的第一件事,是给上下文窗口做预算分配。这就像装修房子,空间有限,你得决定每个功能区占多大。
我的做法是把上下文分成四个区块,每个区块有明确的配额上限:
| 区块 | 内容 | 建议占比 | 更新频率 |
|---|---|---|---|
| 系统区 | 角色定义、核心约束、输出格式要求 | 15% | 极低 |
| 任务区 | 当前目标、阶段说明、规划层摘要 | 20% | 中 |
| 记忆区 | 检索到的相关事实与结论 | 30% | 高 |
| 工作区 | 最近几轮的交互记录与工具返回 | 35% | 极高 |
这个分配不是固定的,要根据任务类型调整。比如做代码生成任务时,工作区的占比可以调高,因为最近的代码上下文很重要;做长流程决策任务时,记忆区的占比应该调高。
关键原则是:任何区块都不能无限增长。一旦某个区块接近配额上限,就必须触发压缩或淘汰机制。系统区通常最稳定,但如果约束条件越来越多,也要定期合并同类项。
3.2 什么该放进上下文,什么必须拿出去
这是上下文工程里最考验判断力的部分。我的经验法则是:放进去的是“影响下一步决策的最小充分信息”,拿出去的是“已经消化过的原始素材”。
具体来说,以下几类内容应该放进上下文:
- 当前任务的明确目标和验收标准
- 与当前步骤直接相关的约束条件
- 最近一次动作的结果(成功或失败)
- 从记忆层检索到的、与当前决策相关的结论
以下几类内容应该拿出去或压缩:
- 完整的对话历史(只保留最近几轮的关键交互)
- 已经得出结论的探索过程(保留结论,丢弃过程)
- 重复出现的确认性信息(合并成一条)
- 工具返回的原始数据(提炼成关键字段)
我踩过的一个坑是:早期我舍不得丢弃任何东西,觉得“万一后面用得上呢”。结果上下文里塞满了“万一”,真正重要的信息反而被淹没了。后来我强制自己执行一条规则:任何信息如果连续三轮没有被引用,就移出上下文。这条规则执行下来,智能体的表现明显更稳定。
3.3 上下文压缩的三种实操手法
压缩不是简单地截断,那样会丢失关键信息。我常用的有三种手法,各有适用场景。
摘要压缩适合处理长段落的交互历史。做法是让模型把最近 N 轮交互总结成几句话,保留决策节点和结果,丢弃中间的试错细节。这里要注意,摘要的 prompt 要明确要求“保留所有约束条件和失败原因”,否则模型容易把重要的负面信息也一起丢掉。
结构化提取适合处理工具返回的数据。比如一个接口返回了一大段 JSON,但智能体只需要其中三个字段,那就直接提取这三个字段,其余丢弃。这种做法比摘要更精确,因为它不依赖模型的概括能力。
引用替换适合处理重复出现的长文本。比如系统提示词里有一段很长的格式要求,每次都要占很多 token,可以把它替换成一个短引用标记,在需要时再展开。不过这种做法要谨慎,因为模型对引用标记的理解可能不稳定,我一般只在格式要求非常固定的场景下使用。
3.4 上下文污染的识别与清理
上下文污染是常驻智能体最隐蔽的敌人。它指的是上下文里混入了错误、过时或误导性的信息,导致模型做出错误判断。
常见的污染源有几个:一是失败尝试的残留,模型看到之前试过某个方案,会倾向于避开它,哪怕那个方案其实是对的;二是过时的约束,比如用户中途改了需求,但旧需求还留在上下文里;三是工具返回的异常数据,比如一次超时返回的空结果,被模型当成了“确实没有数据”。
识别污染的方法,我通常用“一致性检查”:让模型在每轮决策前,先复述一遍当前的有效约束和目标,如果复述内容与预期不符,就说明上下文里有干扰信息。清理的办法是维护一个“有效信息白名单”,每轮只把白名单里的内容注入上下文,其余一律不注入。
4. 长时运行中的稳定性问题与应对
4.1 目标漂移:智能体为什么会“跑偏”
目标漂移是常驻智能体最典型的问题。表现是:智能体一开始目标很明确,跑着跑着就开始做一些与原始目标无关的事情,或者把某个中间步骤当成了最终目标。
造成漂移的原因,我观察下来主要有两个。一是规划层的计划太粗,没有明确的阶段验收标准,智能体不知道“什么时候算完成了一个阶段”,就会一直做下去。二是记忆层没有及时记录“原始目标”,当上下文被工作区的内容挤占后,模型就忘了最初要干什么。
应对办法是在系统区里永久保留一条“原始目标”声明,并且要求智能体在每次进入新阶段时,显式地对照这条声明做一次校验。这个校验不需要很复杂,一句话就够:“当前动作是否直接服务于原始目标?”如果答案是否定的,就触发规划层重新评估。
4.2 死循环与重复尝试的检测机制
死循环的表现是智能体反复执行同一个动作,或者在不同方案之间来回切换但始终不推进。这个问题在长时运行中非常常见,因为模型有时会陷入“再试一次说不定就好了”的模式。
检测死循环的机制,我建议在状态层里加一个“动作指纹”记录。每次执行动作时,把动作的关键参数哈希成一个短字符串存下来。如果连续三次动作的指纹相同,就判定为死循环,强制中断并触发规划层重新规划。
还有一种更隐蔽的情况是“伪进展”:智能体每次动作略有不同,但本质上是在同一个问题上打转。这种情况用指纹检测不出来,需要引入“进展度量”。比如对于一个信息收集任务,进展可以定义为“新增的有效事实数量”;如果连续几轮这个数量都是零,就说明没有实质进展。
4.3 状态回滚与检查点设计
长时运行中,出错是难免的。关键不是避免所有错误,而是出错后能快速恢复到正常状态。这就需要检查点机制。
检查点的设计原则是:在关键决策点保存完整状态。所谓关键决策点,包括规划层更新后、一个子任务完成后、以及任何不可逆操作执行前。保存的内容包括状态层的当前状态、记忆层的有效事实列表、规划层的当前计划。
回滚的时候,不是简单地恢复到检查点,而是要保留失败信息。也就是说,回滚后要把“上次尝试失败的原因”写入记忆层,避免智能体重蹈覆辙。这一点很重要,我见过不少项目回滚后智能体又走了同一条错路,就是因为失败信息被一起回滚掉了。
5. 从讲座到落地:我的实操配置参考
5.1 一个可复用的上下文模板结构
基于上面的思路,我整理了一个可以复用的上下文模板结构。它不是某个具体框架的配置,而是一种组织方式,你可以根据自己的技术栈去实现。
[系统区] 角色:你是一个持续运行的智能体,负责完成长时任务。 原始目标:{original_goal} 核心约束: - {constraint_1} - {constraint_2} 输出格式:{output_format} [任务区] 当前阶段:{current_phase} 阶段目标:{phase_goal} 验收标准:{acceptance_criteria} 规划摘要:{plan_summary} [记忆区] 有效事实: - {fact_1} - {fact_2} 失败记录: - {failure_1}:{reason} [工作区] 最近动作: - 动作:{last_action} - 结果:{last_result} - 状态:{success/failure}这个模板的关键在于:每个区块都有明确的边界,区块内的内容有固定的格式。这样模型在读取时能快速定位信息,也方便程序化地更新和压缩。
5.2 参数调优:温度、轮次与超时设置
常驻智能体的参数设置跟普通对话场景不太一样,我分享几个实测下来比较稳的配置。
温度建议设低一些,0.2 到 0.4 之间。原因是常驻智能体需要的是稳定和一致,而不是创意。温度太高会导致它在相似情况下做出不一致的决策,增加不可预测性。如果某个子任务确实需要发散思维,可以临时调高,但任务完成后要调回来。
最大轮次要设一个硬上限,防止无限运行。具体数值取决于任务复杂度,我一般设 50 到 100 轮。超过上限后,不是直接终止,而是触发一次“总结并请求人工介入”的流程。
单步超时建议设短一些,比如 30 秒。常驻智能体最怕的是某一步卡住导致整个流程停滞。超时后应该记录失败原因并进入下一步或触发重试,而不是无限等待。
重试次数建议不超过 3 次,而且每次重试要改变策略。如果三次都失败,就写入失败记录并跳过,不要死磕。
5.3 日志与可观测性:怎么知道智能体在干什么
常驻智能体跑起来之后,最大的挑战是“不知道它在干什么”。因为它可能连续运行几个小时,你不可能一直盯着。所以日志和可观测性设计非常重要。
我的做法是记录三类日志。决策日志记录每一轮的核心决策:当前目标是什么、选择了哪个动作、理由是什么。状态日志记录状态层的变化:阶段切换、检查点保存、回滚发生。异常日志记录所有失败、超时、死循环检测触发的事件。
这三类日志分开存储,查询时按需组合。决策日志用于复盘智能体的思路,状态日志用于排查流程问题,异常日志用于发现系统性问题。我还会在每轮结束时输出一行简短的“心跳”信息,包含当前轮次、阶段、累计耗时,方便快速判断智能体是否还在正常推进。
6. 几个容易忽视的细节与个人体会
6.1 记忆检索的时机比检索算法更重要
很多人花大量精力优化记忆检索的算法,比如用向量相似度、用关键词匹配、用混合检索。这些当然重要,但我实际用下来发现,检索时机的设计比检索算法本身影响更大。
什么意思呢?如果你在每一轮都做一次全量检索,不仅浪费算力,还会把不相关的记忆注入上下文,造成干扰。更好的做法是按需检索:只有当智能体明确表示“我需要回忆某个信息”时,才触发检索。触发的方式可以是在规划层的输出里加一个标记,或者让模型在决策时输出一个检索请求。
我试过两种方式,按需检索的上下文干净度明显更高,智能体的决策也更聚焦。代价是偶尔会漏掉一些“应该想起来但没想起来”的信息,但这个代价比上下文被污染要小得多。
6.2 不要让智能体自己管理自己的上下文
这是一个反直觉的建议。很多人觉得,既然智能体很聪明,那就让它自己决定上下文里放什么。但实测下来,让智能体自己管理上下文,几乎必然导致上下文膨胀。因为模型天然倾向于“保留更多信息”,它没有“预算”的概念。
正确的做法是:把上下文管理交给外部程序,智能体只负责“请求信息”和“输出结果”。外部程序根据预设的规则决定注入什么、压缩什么、淘汰什么。智能体可以提出请求,但最终决定权在程序手里。这种分工让上下文管理变得可预测、可测试、可调优。
6.3 常驻不等于永不停止
最后说一个概念上的澄清。“常驻”指的是状态持续存在,而不是说智能体必须一刻不停地运行。实际上,好的常驻智能体应该知道什么时候该停下来。
停下来的时机包括:目标已完成、遇到无法解决的阻塞、需要人工确认、或者资源消耗达到上限。在这些时机,智能体应该保存状态、输出总结、然后进入休眠。下次唤醒时,从保存的状态恢复,继续推进。这种“可中断、可恢复”的设计,比“一直跑到底”要实用得多,也更符合真实场景的需求。
我在实际项目里把“主动休眠”作为一个明确的能力来做,效果很好。它让智能体在遇到不确定情况时不会硬着头皮乱试,而是停下来等待输入。这比让它自由发挥要安全得多,也更容易获得使用者的信任。