做 Agent 开发这件事最容易翻车的地方往往不是模型选型也不是 Prompt 写不好而是 Agent 跑起来之后怎么让它稳定地活下去。最近我基于 WeKnora 这个开源知识管理与 Agent 工作台项目配合 CubeSandbox 沙箱运行时搭了一套 Agent 持久化运行环境。今天把这套方案完整拆开讲清楚设计思路、核心机制、具体部署流程和实战中踩过的坑。如果你正在做的事已经不只是写个 Demo而是要支撑真正的业务场景——比如知识库问答、自动整理网页、定时抓取资料、多 Agent 协作——那这套东西基本是绕不开的基础设施。适合谁看对 Agent 有基本了解、想把它推上生产环境的开发者以及正在做知识管理 RAG 智能体集成的团队。1. 项目背景与整体设计思路1.1 为什么要做 Agent 持久化运行环境先聊一个很多人忽略的问题Agent 本质上是一个“有状态”的程序。它跟普通的 API 服务不太一样普通接口是无状态的请求来了处理完就走Agent 不一样它要记住用户说过什么、自己做过什么、中间产生过哪些中间结果然后基于这些历史信息继续推理和行动。我接手这个项目之前团队跑 Agent 的方式很简单粗暴每次触发生成一个新容器跑完就销毁。在 Demo 阶段没问题一旦涉及真实业务就露馅了。比如一个流程跑了一半模型调用超时任务中断用户重新发起时 Agent 什么都想不起来了。更麻烦的是那些长周期任务像“每天定时整理某个网页并生成简报”这种 Agent 需要跨小时甚至跨天记住自己的状态一旦重启就全部归零。打个比方就好比一个程序员每次重装系统装完发现自己的代码、笔记、环境变量全没了一切从头再来。Agent 如果只能活一次那它本质上还是一个“脚本”谈不上智能体。所以我当时给这个项目定了一个核心目标让 Agent 具备“忘记成本”的能力——杀掉不心疼重启能续上。这里的“续上”不是简单地把日志拉出来而是把会话状态、记忆、文件工作区、工具执行上下文全部恢复到中断前的样子。1.2 为什么选 WeKnora CubeSandbox 这套组合选型阶段我比较过不少方案。Dify 有工作流和知识库LangChain 生态够大CrewAI 做多 Agent 也顺手。但她们的侧重点各有不同Dify 更像一个现成的 LLM 应用平台强调快速的业务配置LangChain 是一套开发工具箱打包部署层面还要自己做CrewAI 更偏多角色编排知识管理能力偏弱。WeKnora 吸引我的点在于它的定位比较独特它本质上是一个开源的知识管理 Agent 工作台既管知识入库、文档解析、向量检索又管 Agent 的注册和工作流编排。换句话说它把“知识底座”和“Agent 管理面”放在一起了这对做知识类智能体特别友好。你不需要自己在两套系统之间导来导去。CubeSandbox 则是执行面。它提供沙箱运行环境负责真正跑 Agent 的代码和工具核心能力是隔离 快照 生命周期管理。Agent 在沙箱里可以做任意事情但碰不到宿主机和其他 Agent出了问题拿快照回滚就行。我画过一张架构草稿来对比不同组合的适配度结论是WeKnora 管知识、管会话、管编排CubeSandbox 管执行、管资源隔离、管持久化恢复两者职责不重叠互补得很干净。相对 Dify Docker 裸跑这类方案这个组合在“知识密集 状态敏感 需要隔离”的场景下有明显优势。1.3 整体架构拆解整套系统我分成四个层次来理解应用编排层WeKnora。负责知识库管理、RAG 检索、Agent 工作流编排、会话接口暴露以及和上层业务系统对接。沙箱执行层CubeSandbox。每个 Agent 实例对应一个沙箱环境里面运行 Agent 的主循环、工具调用和自定义脚本。持久化层这是整套方案里最关键的层。会话快照、工作区文件、内存记忆全部落到 Redis、对象存储和文件存储里保证任何一个沙箱崩溃都不丢数据。模型接入层模型 API、Embedding API 统一走网关沙箱内不直接持有密钥避免密钥泄露。这里有一个容易被忽略的设计点沙箱本身是“可摧毁”的但数据必须是“不可摧毁”的。CubeSandbox 挂载的持久化卷和 WeKnora 的状态存储是分离的杀掉沙箱只是丢了一个运行壳真正的记忆都沉淀在持久化层。这个思路保证了我后面处理故障时可以随意重启任何一个组件不用提着心怕出事。2. 核心机制Agent 持久化的四个关键问题2.1 会话状态持久化记忆不能只放在内存里先说最基础也最容易踩坑的会话状态。很多 Agent 框架默认把对话历史和上下文放在进程内存里服务一重启内存清空Agent 就失忆了。解决这个问题最基本的要求是把会话快照周期性地持久化到 Redis 里恢复时再加载回沙箱。我这边给每个 Agent 都分配了一个固定的 session_key格式类似agent_id:user_id:conversation_id。Agent 的每一步执行之后都会把当前的状态结构体序列化写入 Redis。结构体包含了当前对话轮次、工具调用记录、中间变量、任务进度等。在 WeKnora 里配置了会话快照的自动保存策略我会把 checkpoint 间隔调成每执行完一个工具就保存一次而不是整个任务结束才保存。为什么要这么频繁因为 Agent 的任务执行链路太长了中间任何一步都可能失败。你永远不知道下一次崩溃发生在哪唯一能做的就是让保存频率足够高把丢失成本降到最低。实际项目中我还会对重要任务做“双写”一份写 Redis一份写对象存储。Redis 管快速的会话恢复对象存储管审计和历史回溯。这里特别提醒一下持久化时一定要同时保存模型的调用上下文和 Agent 内部状态两者缺一不可。否则会出现“记忆里有这条消息但 Agent 不知道怎么把它接上”的尴尬情况。2.2 文件与工作区持久化Agent 创建的临时文件怎么保留Agent 在运行过程中一定会产生文件下载的 PDF、抓取的网页、生成的报告、中间的缓存数据。如果这些文件只存在沙箱的本地磁盘里沙箱一销毁之前的劳动成果就全没了。CubeSandbox 支持挂载持久化卷我把每个 Agent 的工作区挂到了一个独立目录结构大概是/workspace/{agent_id}/里面再按日期分子目录。这样即使沙箱重建Agent 还是能从同一个工作区里找到之前生成的文件。这里有一个很多人忽略的坑很多 Agent 框架在生成文件时用的是相对路径一旦沙箱重建后工作目录变了路径就会对不上。我在 Agent 启动脚本里规定了一条铁律所有文件操作必须基于工作区根目录的动态获取禁止写死绝对路径。项目里还加了一个文件清单机制Agent 每生成一个新文件就在内存状态里追加一条索引会话恢复时先扫一遍索引确保上下文跟文件对得上。顺便说一句对特别重要的文件项目我会做一个异步备份任务定期把工作区里新增的内容同步到对象存储。这个联动在 WeKnora 的文档解析流程里可以直接复用文档入库后原始文件和解析后的结构化数据会放在不同的存储桶里互不干扰。2.3 沙箱生命周期管理怎么做到“杀了不心疼重启能续上”沙箱生命周期管理是整个项目里技术含量最高的部分。CubeSandbox 的模型大概是这样的每个沙箱有一个定义好的初始镜像镜像里预装了 Agent 运行时、Python 环境、常用工具链以及连接 WeKnora 所必需的 SDK。沙箱可以基于快照创建也可以从一个干净的初始状态创建。实际操作中我会把沙箱状态分为三类running正常服务处理请求paused任务被临时挂起但内存状态还在terminated已销毁只保留持久化数据。当一个任务被判定为“可中断”的时候我会优先选择暂停而不是销毁。比如模型调用超时或者外部 API 临时不可用这时候把沙箱挂起来等障碍解除后继续跑比从头开始要划算得多。CubeSandbox 在这方面的体验做得不错暂停和恢复都是秒级的。真正需要销毁沙箱的场景只有两种一是任务彻底跑完正常清理二是沙箱内出现了异常比如被注入的恶意指令、资源泄漏、死循环这种情况必须立刻杀掉并回滚到上一个健康快照。我给这条逻辑起了一个名字能暂停不销毁能快照不重跑。靠着这个原则项目里一个原本要跑 40 分钟的长任务在意外赖掉重启后可以缩短到几分钟内恢复。2.4 安全隔离与资源限制提到沙箱就得说安全。Agent 执行环境的隔离级别决定了你能不能让它在生产环境放心跑。CubeSandbox 的隔离方案类似于把每个 Agent 放进一个封闭盒子文件系统、进程、网络默认都是隔离的。即使 Agent 被 Prompt 注入诱导执行了一段危险脚本它也拿不到宿主机和其他 Agent 的数据。资源限制也不必手软。我给每个沙箱设了三个硬指标CPU 配额通常 1-2 核、内存上限默认 2GB、文件写入体积上限默认 1GB。别小看这些数字Agent 跑崩大多是 out-of-memory 或者磁盘打满提前限好能省掉很多半夜救火的烦恼。网络访问方面我会给 CubeSandbox 配置白名单模式。能访问的域名列表由 WeKnora 的应用管理接口动态维护比如知识库抓取任务只允许访问目标站点模型 API 只允许走网关域名。这样即使 Agent 被诱导发出恶意请求也会第一时间被网络层拦下来。3. 实操过程从零搭建这套环境3.1 本地部署 WeKnora 的两种方式部署 WeKnora 我推荐用 Docker Compose一条命令拉起来比较省心。项目目录下我维护了一个 compose 文件核心服务包括 WeKnora 主服务、PostgreSQL、Redis 和对象存储。Redis 这里不是当成缓存用的而是作为会话快照的持久化存储所以启动参数里一定要开 AOF追加写文件否则重启丢数据。下面是一个简化版的示例结构方便理解服务之间的依赖关系具体镜像版本和参数以官方发布页为准services: weknora: image: weknora/weknora:latest environment: WN_DB_HOST: postgres WN_REDIS_HOST: redis WN_OIDC_ENABLED: true WN_STORAGE_BUCKET: agent-files ports: - 8080:8080 depends_on: - postgres - redis postgres: image: postgres:15 environment: POSTGRES_PASSWORD: weknora redis: image: redis:7 command: redis-server --appendonly yes如果你想用裸金属方式部署也是可以的但意味着要把 PostgreSQL、Redis、文件存储都自己装好精力成本高不少。对于绝大多数场景Compose 足够稳。我这边还做了一个小改进把 Redis 的持久化目录也映射到一个独立的 SSD 盘上因为会话快照的读写频率很高磁盘性能直接影响 Agent 的恢复速度。3.2 注册 Agent 服务并接入 CubeSandboxWeKnora 启动后第一步是初始化知识库把团队的文档、网页、历史问答数据导进去。这个流程在后台可以通过界面操作完成文档解析后自动做向量化之后 Agent 提问时就能通过 RAG 拿到相关知识。第二步是注册 Agent。WeKnora 里的 Agent 注册本质上是一个配置文件声明这个 Agent 叫什么、用哪个模型、挂在哪个沙箱模板下、会话超时时间是多少。我给 Agent 的配置里加了一个关键字段runtimesandbox指向 CubeSandbox 的接入端点。当前项目用的是 WeKnora 提供的 Agent SDK沙箱内启动时先通过 SDK 注册自己告诉控制面自己的状态然后开始监听任务。注册完成后CubeSandbox 会往里下发一个启动命令拉起来一个 Agent 运行时进程。这个进程负责执行大模型返回的动作序列——调工具、读文件、写文件、发请求全部在沙箱内完成。3.3 CubeSandbox 持久化卷与快照策略CubeSandbox 部署完成后需要给所有沙箱统一配置持久化策略。我的做法是为每个 Agent 类型维护一个 template模板里声明了三块挂载/workspaceAgent 的工作区绑定到宿主机上的持久化目录/var/cache模型调用和工具执行的临时缓存/etc/agent-secrets密钥目录运行时注入了 WeKnora 下发的短期访问凭据。快照策略也很重要。我设定成三种触发机制定时快照每隔 30 分钟自动打一个快照阶段快照Agent 完成一个核心工具调用后触发手动快照运维可以通过管理接口随时打。快照不是万能的它保存的是沙箱的瞬时状态能快速恢复到某一时刻。但快照也有体积和频率成本我刚开始图省事设成每 5 分钟打一次快照结果存储空间飞快被打满。后来调成 30 分钟 关键节点触发成本和恢复粒度基本平衡。3.4 部署验证跑一个带记忆的 Agent Demo环境搭好之后我验证持久化的方式非常简单写一个会“记仇”的 Agent。它每收到一条消息就把它追加到一个本地文件然后读出来回给用户。如果重启之后文件内容还在说明持久化链路生效了。核心代码如下逻辑不复杂但非常适合验证整条链路class MemoryAgent: def __init__(self, session_key, workspace): self.session_key session_key self.memory_path f{workspace}/memory_{session_key}.txt def remember(self, message): with open(self.memory_path, a, encodingutf-8) as f: f.write(message \n) def recall(self): try: with open(self.memory_path, r, encodingutf-8) as f: return f.read() except FileNotFoundError: return (no memory yet)我先跑一轮对话让 Agent 记住三条业务信息然后直接通过 CubeSandbox 管理接口销毁这个沙箱再从快照重建一个最后用同一个 session_key 继续对话。如果它还能把之前的三条信息说出来说明文件持久化、Redis 快照、沙箱恢复整条链路都是通的。实测下来这套验证流程大概 10 分钟就能跑完强烈建议你在接业务之前先把它过一遍。4. 常见问题与排查技巧实录4.1 重启后 Agent“失忆”了怎么办这是最常遇到的问题。我之前排查了一个小时最后发现根因是 session_key 没有跟着持久化。沙箱重建后Agent 又生成随机的 session_key自然找不回之前的记忆。排查思路要按顺序来检查会话状态是否写入 Redis用redis-cli keys看有没有对应 key检查工作区文件是否挂载销毁沙箱后重新启动查看/workspace下文件是否还在检查 Agent 启动时的 session_key 是否固定确保不是每次随机生成。我后来在 Agent 启动脚本里加了一道保险启动时如果检测不到 session_key就从 Redis 里按 agent_id 反查最近一次使用的 key找到就强制复用找不到才创建新的。这样即使业务方忘记传 session_key也不会马上丢状态。4.2 沙箱内网络不通沙箱里跑 Agent最常见的现象是知识库抓取任务全部失败。起初我以为是目标网站封锁了请求后来发现是沙箱网络模式的问题。CubeSandbox 默认出网是受限的需要在配置里显式放行出口域名。处理方式也比较直接把放行策略改成域名级别的白名单然后按业务域比如搜索、抓取、模型调用分三个组按需绑定到沙箱模板上。这个操作之后我再也没有遇到过“业务侧代码没毛病但沙箱里什么都做不了”的怪事。顺便提一句网络排查时不要忘了看 DNS。沙箱内如果用了自定义 DNS解析失败也会表现得像完全断网。4.3 Agent 高并发下资源争抢业务量上来之后并发 Agent 实例一多宿主机压力会非常大尤其是 CPU 和内存。我一开始没有开容器配额结果三个 Agent 同时跑抓取任务直接把宿主机干到了 100% 负载其他服务跟着遭殃。后来我在 CubeSandbox 的模板层强制加了配额CPU 限制 1.5 核内存限制 2GB文件写入不超过 1GB。还加了一个队列机制同一时间不会被调度到过多重型任务。加了配额之后高峰期的稳定性明显改善代价只是每个任务的平均执行时间变长了一点点这个代价完全可以接受。4.4 给新手的避坑清单我把这段时间踩过的坑整理成一个速查表照着检查能省很多事检查项常见坑对策Redis 持久化没开 AOF重启丢全部会话开启appendonly yessession_key随机生成导致失忆固定 agent_id 会话 ID 拼接工作区路径写死绝对路径动态获取/workspace/{agent_id}快照频率太高费空间太低丢数据30 分钟 关键节点触发沙箱网络默认受限配置域名白名单密钥管理密钥放在沙箱镜像里只放短期凭据随实例下发资源配额不限额拖垮宿主机CPU/内存/磁盘三线限制5. 进阶从单 Agent 到多 Agent 协作5.1 Multi-Agent 的状态隔离与共享单 Agent 跑顺之后自然会上多 Agent 协作。一套系统里同时存在多个 Agent有人负责检索有人负责生成报告有人负责定时触发任务。这里最需要注意的问题就是状态隔离。CubeSandbox 天然支持多实例隔离每个 Agent 一个沙箱互不干扰。但知识的共享怎么做WeKnora 在知识库层面做得比较通透多个 Agent 可以共享同一个知识库因为知识库是集中存储的Agent 只是通过接口去读。这样就形成了一个协作模型知识共享执行隔离。我实际搭建了一个双 Agent 的协作场景一个叫“研究员”负责检索和整理信息一个叫“编辑”负责把研究员的输出改写成结构化报告。这两个 Agent 跑在不同的沙箱里通过 WeKnora 的消息队列交换数据。“研究员”跑挂了“编辑”不受影响等到“研究员”恢复后继续接力。这个方案的恢复逻辑对用户是透明的用户只看到最终报告正常生成看不到背后曾经发生过一次崩溃。5.2 Agent Skill 与工具链的沙箱化多 Agent 场景里工具和技能化设计特别重要。Agent Skill 这个词现在很流行但很多人把它理解成“写一段工具代码”就完了。我的理解是Skill 应该是一个可以在沙箱里独立运行的子程序它有自己的输入输出协议可以被 Agent 在运行期动态调用。我团队里做了一个“网页转 Markdown”的 Skill它的完整执行链路是Agent 拿到网页 URL调用 Skill 接口Skill 启动一个子进程做抓取和转换转换后的 Markdown 文件存到当前 Agent 的持久化工作区并返回文件路径给 Agent。因为这个 Skill 被沙箱隔离了所以即使目标网页有恶意脚本也不会波及宿主。在 WeKnora 的应用配置里Skill 被注册为一个标准工具Agent 通过名称自动发现和加载。这套机制带来的好处是新增技能不需要重启 Agent只需要往 Skill 仓库里丢一个新的子程序Agent 下次用的时候自动识别。5.3 可观测性与监控Agent 持久化运行环境做好之后下一步必须解决可观测性。Agent 跟普通服务有一个很大的不同它的执行内容是动态的日志里全是模型调用记录和工具执行记录噪音很大难以直接定位问题。我的做法是给每个沙箱接入了标准的日志采集把日志按 agent_id、session_key、tool_name 打标签统一汇集到日志平台。同时在 WeKnora 这边配置了关键的监控指标比如会话恢复成功率、沙箱平均创建时间、快照恢复耗时、Redis 命中率。这些指标做成一个看板每天扫一眼就能发现潜在风险。有一次我发现某个 Agent 的会话恢复成功率从 99% 掉到了 92%顺着监控查下去发现是有几个大任务的工作区文件体积涨到了几个 GB导致快照恢复超时。优化了工作区清理策略之后成功率立刻回到健康水位。没有监控这种问题大概率要等用户投诉才查得到。最后分享一个心得Agent 持久化运行环境不是装上就能一劳永逸的基础设施它更像一个需要持续调教的系统稳定性是靠快照频率、资源配额、会话管理和监控告警这一整组合拳打出来的。尤其是刚开始接触 CubeSandbox 的朋友不要把注意力全放在“部署成功”那一刻多测几次“杀掉重建”的场景把恢复链路练熟真正上线时心里才有底。这套方案我从验证 Demo 到支撑多 Agent 协作改过的配置加起来可能有一桌子的笔记但最核心的那条原则一直没变Agent 可以被杀死但它的记忆必须留下来。
阅读完成 · 觉得有帮助?