两年前要是有人跟我说,Agent 的核心瓶颈不是模型能力而是记忆,我会觉得他在夸大其词。但今年带着团队把一套基于大模型 Agent 的问答系统从云端 SaaS 迁到企业内网私有化部署之后,我的看法彻底变了——真正决定 Agent 能不能在企业里长期跑下去的,不是它能调多少个工具、不是提示词写得有多花哨,而是它有没有一个靠得住的记忆系统。这也是我最近一直在琢磨"Memory OS"这个概念的原因:把记忆从 Agent 的附属功能提升为整个系统的核心基础设施。这篇文章想跟你聊聊我在这条路上的设计思考、踩过的坑,以及一套可以直接拿去做企业私有化 Agent 的落地框架。
适合读这篇的人大概是两类:一类是做 Agent 框架选型和架构设计的后端工程师,另一类是正在帮企业做大模型私有化落地的技术负责人。如果你只是写个个人玩具 Agent,那记忆随便放个 SQLite 就行;但一旦牵扯到企业内部的知识资产、多员工并发、权限边界,你会发现"带记忆的 Agent"和"记忆即 OS 的 Agent"是两个完全不同的工程命题。
1. 为什么 Agent 需要"记忆":从无状态编排到 Memory OS
1.1 一次真实的翻车现场
先说个我们自己的案例。早期版本我们做了一个"企业制度问答 Agent",它做的事很简单:用户提问,Agent 去向量库里检索制度文档,整理回答。上线第一周效果还行,第二周就开始被投诉——因为同一个问题,员工上午问一遍、下午改几个字再问一遍,Agent 每次都跟失忆了一样从头检索,回答还不一致。更麻烦的是,一个员工在对话里提到了"上次那个报销流程",Agent 根本不知道"上次"指的是什么。
这个问题的本质在于:我们做的是一个无状态 Agent。每次对话都是独立的,没有工作记忆,也没有跨会话的长期记忆。它像一台刚开机的电脑——跑完一个程序就清空内存,什么上下文都留不下来。这种 Agent 做简单问答没问题,做复杂的、需要连续协作的任务就会处处碰壁。
从那之后我开始意识到,企业场景下的 Agent 不应该被设计成"无状态的函数调用器",而应该更像一个"有状态的同事"。它会记住你是谁来着、你上次问过什么、你在这个项目里的角色是什么,然后在后续交互中复用这些信息。这就是我理解的 Memory OS 的起点。
1.2 把记忆拆成三层:工作记忆、情景记忆、语义记忆
认知科学里有个分类方法我觉得特别适合工程化:把记忆分成工作记忆、情景记忆和语义记忆。很多 Agent 框架其实只实现了最浅的一层,剩下两层都被忽略了。
工作记忆对应的是"当前这次任务过程中的上下文"。比如 Agent 正在帮你写一份合同,它需要记住合同里已经写到哪一条、刚才改过哪个金额。这层记忆通常由上下文窗口承担,但有一个工程上限——上下文塞得太满,模型回答质量会下降,推理速度也会变慢。所以我们对工作记忆做了一个滑动窗口管理,只保留最近 N 轮对话和当前任务的高优先级状态。
情景记忆是"过往交互事件"的存储。比如用户上周三让 Agent 分析过 Q3 财报,这个事件本身值得被记住。情景记忆解决的是"上次"、"之前那个"这类指代问题。在系统里,每次交互结束我们会生成一条结构化的事件记录,包含时间、用户、任务类型、涉及文档、结论摘要,写入情景记忆库。
语义记忆是最高价值但也最难做的一层,它要沉淀的是"经过提炼的知识"。员工问了一堆问题之后,Agent 能不能总结出这个团队的报销偏好是什么、这家公司常用的供应商有哪些、某个项目的进展卡在哪。语义记忆不是原始对话的堆砌,而是经过抽取、合并、冲突消解之后的长期知识沉淀。这一层做好了,Agent 才真正从"搜索引擎"进化为"专家助手"。
1.3 为什么企业场景比 C 端更需要 Memory OS
个人助手类的 Agent 没有记忆,用户顶多吐槽一句"真笨",然后就关掉了。企业场景完全不一样:员工会用同一个 Agent 处理报销、写周报、查制度、审合同,跨部门的 Agent 还会被同一个项目组共享。
企业在意的三件事,每件都跟记忆强相关。第一是知识留存——员工离职后他的经验能不能沉淀下来?第二是连续性——一个跨周、跨月的任务,Agent 能不能不靠用户反复澄清上下文就接上?第三是可审计——Agent 做的每一步决策是基于什么信息做出的?这三个需求都指向同一个架构判断:记忆不能是 Agent 的外置数据库,它必须成为整个系统的主干。你看看现在很多 Agent 产品的架构图,记忆只是旁边一个孤零零的向量库图标,实际的对话路由、工具选择、权限判断都没跟记忆打通。而 Memory OS 的思路是把记忆当作操作系统里的"文件系统",所有 Agent 都在这个文件系统之上读写。
2. 企业私有化部署的整体架构与技术选型
2.1 先算一笔自建 vs SaaS 的账
去年我们其实试过几家现成的 Agent SaaS 平台,用下来发现一个尴尬的事实:模型能力和工具编排是对方提供的,但企业最核心的资产——内部文档、客户数据、被调用的业务系统——都要传到对方的服务器上。安全合规部门直接一票否决。这不是信任问题,是法律和审计问题。医疗、金融、制造这类行业,数据不出内网是硬红线。
所以对企业来说,私有化部署不是"更省钱"的选项,而是"唯一能走通"的选项。但要算清楚的是另一笔账:私有化部署并不意味着从零开始造轮子,你需要清楚地划分哪些用开源框架、哪些自己写、哪些采购商业组件。我们最终的边界是这样划的:大模型底座用开源权重部署;Agent 编排框架自己控制核心逻辑;向量库用成熟商业或开源引擎;工具调用层和权限层完全自研。这样既保证可控性,又避免重复造轮子。
2.2 技术栈全景:模型层、记忆层、运行层、接入层
私有化 Agent 系统我习惯拆成四层来看:
模型层比较直接,就是部署在企业 GPU 集群上的 LLM。推理性价比目前看开源权重模型比闭源 API 更有优势,但后者在复杂指令跟随上有明显差距。建议按任务分级:简单分类抽取用小模型,复杂推理和工具编排用大模型。
记忆层是核心。这层包含向量数据库、关系型数据库、缓存三部分。向量库存语义向量和文档块,关系库存事件记录和实体关系,缓存存高频 KV。很多团队只上了向量库,但企业记忆除了语义检索还有大量结构化查询需求——"这个供应商上个月的报价记录是什么",这类问题靠向量检索是答不准的。
运行层是 Agent 的"执行环境"。包括上下文管理、工具注册与调度、多 Agent 协调、错误恢复、沙盒隔离。这一层我们一开始用 Python 写的,后来因为并发问题用 Rust 重写了一部分,后面会细说。
接入层负责跟企业现有系统的对接:飞书/钉钉/企业微信的消息 webhook、内部 OA 系统的 API、统一身份认证(LDAP/OAuth2)。这层的痛点不在技术,而在每个企业都不一样,必须预留充足的适配空间。
2.3 框架选型:LangGraph 之外的选择
框架选型上我们做了三轮调研。第一轮直接用 LangGraph 跑原型,第二轮试了 Semantic Kernel,第三轮用 Rust 自己写了编排内核。
先说说 LangGraph。诚实地讲,它确实是最快的启动方式:状态图、持久化检查点、多 Agent 协作这些概念都有现成实现。但问题是它绑定在 Python 生态里,而我们的工具调度器需要很强的并发能力,Python 的 GIL 锁和异步模型在高并发下很快就成了瓶颈。另一方面,LangGraph 的状态管理默认是存在本地/远端存储里的,对这个框架来说这也算特色,但你有更强的 Nakivik / ACL 需求时,那块相对薄弱。第三轮的出现,本质上是我们对这些限制的回应。
Semantic Kernel 走的是另一条路,更微软,更面向企业。它的插件(plugin)体系和函数调用设计比 LangChain 清晰,而且对 C#/Java 的支持比同体量的框架好。但在国内私有化场景里,社区资料和踩坑案例相对少,出了问题不太容易找到可参考的解决方案。
我们最终选择用 Rust 重写编排层,不是追求性能极客主义,是被并发问题逼的。Agent 一次会话过程中,会并行跑检索、模型推理、工具调用三个 IO 密集流程,Python 原方案在 10 个并发用户时还能撑住,到 30 个并发时上下文管理直接开始超时。Rust 的 async runtime(tokio)能轻松扛住远超这个量级的并发连接,配合上内存安全的优势,在私有化部署里不用为了防内存泄漏频繁重启。代价是开发速度确实慢了,但我们保住了稳定性。
注意:如果你对 LangGraph 的生态依赖很深(比如已经有一堆现成的节点和状态机定义),不建议全盘换掉。可以先只在并发瓶颈层用 Rust 写一个 sidecar 服务,通过 gRPC 跟 Python 编排层通信。我们就是这样过度过来的。
3. 记忆系统的核心实现:从向量库到记忆写入策略
3.1 向量数据库选型对比
记忆层的地基是向量数据库,选型的时候我们对比了 milvus、qdrant、pgvector 几个常见选项,还专门看了国内搭建时的运维复杂度。
milvus 功能最强,支持混合检索、动态 Schema、各种索引类型,特别适合大规模向量场景。缺点也明显:组件多,依赖 etcd、MinIO、Pulsar,部署一套至少 3-4 个服务,对运维来说相当于又多养了一条线路。
qdrant 相比之下轻很多,单个二进制文件就能跑,性能也非常好,Rust 写的,跟我们的技术栈还同源。它内置支付过滤和 payload 索引,做租户隔离友好。缺点是生态和商业支持没那么重,极端大规模场景需要自己调优。
pgvector 是把向量存到 PostgreSQL 里,最大的优势是复用现有数据库运维体系,事务一致性好。但向量索引性能在百万级数据量以上有明显的瓶颈,HNSW 查询时间会指数上升。
我们的选择是 qdrant 做语义向量主存储,PostgreSQL 存结构化记忆事件。原因很实际:数据量到不了 milvus 才能解决的问题层级,而 qdrant 的单一二进制部署对私有化交付来说省了太多事。
3.2 记忆写入的合并与去重
记忆写入比大多数人想的重要。很多团队的写入逻辑简单粗暴:每次对话结束就把整段内容塞进向量库,结果库里塞满了语义重复的知识碎片,检索出来全是同一句话的变体,回答质量直线下降。
我们的做法是三步走:先抽取、再合并、后冲突消解。
抽取环节用 LLM 从原始对话中提取结构化信息:实体(人、部门、文档、供应商)、事件(时间+动作+结果)、论断(某人对某事的观点)。这部分我们用了一个"记忆抽取"提示词模板,把输出强制为 JSON,方便下游处理。注意这一步要放一个小模型,大模型做抽取浪费资源,效果也不一定更好。
合并环节把新抽出的信息跟库里的现有实体做比对。如果库里已经存在同一个实体,就做字段级合并而不是新增一条。这里会涉及一个问题:两段信息互相矛盾怎么办?比如员工 A 说"项目延期了",员工 B 说"项目按计划推进"。我们在冲突消解时给每条记忆打个时间戳和来源可信度,默认是"时间越新、来源越权威的赢"。但不会物理删除旧记录,而是把它标记为"被推翻状态",保留审计追踪。
3.3 检索策略:混合检索与重排序
纯向量检索在记忆系统里远远不够,因为记忆相关性不完全是语义层面的。举个例子,用户问"上周那个合同的审批状态",向量层面可能找不到任何内容跟"审批状态"直接相关,但结构化数据里明确记录着上周某合同的状态变更历史。
所以我们用了混合检索方案:向量检索召回语义相近的文档块和记忆条目,同时用 SQL 从关系库召回结构化的实体事件。两侧结果合并后过一个 rerank 模型。rerank 这块没有用太重型的模型,选了 bge-reranker-base,在 GPU 上跑也就几十毫秒,但能把 Top-50 的召回压缩到 Top-5,质量提升非常明显。
还有一个细节:企业记忆里经常出现专有名词和缩写,比如"WBS"、"SOW"、"PO",向量检索对缩写非常不友好。我们额外维护了一个企业术语表,检索前先做查询改写,把缩写还原成全称,再拿去检索。这个点看起来小,但实际解决了很多"什么都搜不到"的问题。
3.4 记忆淘汰机制
记忆不是越多越好。给 LLM 的上下文里塞了大量过时或低价值的记忆,不但不会提升回答质量,还会稀释注意力、拉长时延、增加成本。我们在系统里设计了三个淘汰维度。
第一是时间衰减。超过 180 天没有触达的记忆,检索权重下调。第二是访问频次。高频访问的记忆会上浮到热区,进缓存;90 天没访问的降级到冷区,不进默认检索集。第三是价值判定。任务成功后我们会对本轮用到的记忆打个"命中标签",累积命中次数高的记忆会被归类为高价值,永久保留;反之低价值记忆滚动清理。
这套机制跑了一个月后,向量库的数据增长速度下降了 60%,而检索命中率反而上升了。这背后是个很朴素的道理:记忆系统也要做"信息架构"管理,不是所有信息都值得长期留在顶层。
4. 并发是 Agent 落地绕不开的坎
4.1 Agent 为什么比传统后端更难扛并发
很多人以为 Agent 服务就是个"调大模型的 HTTP 接口",并发能力按普通 Web 服务估算就行。真跑起来才发现完全不是一回事。一个 Agent 单次会话的耗时动辄 10-30 秒,高峰期 50 个员工同时问问题,相当于每秒钟有几十个长连接挂在服务上。传统 Web 服务是"请求-响应"模型,5 秒跑不完就超时;Agent 是"会话-操作序列"模型,一次交互里要串行或并行执行多个工具调用、多次模型推理。
更深层的瓶颈在于:Agent 的每个会话需要持续维护状态。某个会话的检索结果、工具返回、模型中间输出都要暂存在内存里,等下一轮推理时再拿出来。用 Python 写这种带状态的长连接服务,每个连接占用的内存和对象都在堆里堆积,GC 一响应开发环境就开始卡顿。
4.2 为什么 Python async 方案也不够
当然有人会用 FastAPI + async/await 硬扛,我们试过。但真正的瓶颈不在地毯代码层,而在线程安全模型。如果你的 Agent 编排逻辑里有一个共享的记忆管理器,多会话并发读写同一个记忆对象,Python 的 async 单线程模型下 CPU 密集型操作会被复用,而 memory 的连续读写也容易让对方等待全局状态。另外,LangChain 生态里的很多组件(比如某些工具回调)没有做协程友好设计,会阻塞事件循环。
实测下来:Python async 方案配合 uvloop + 连接池优化,最多扛到 25-30 个并发会话,再往上 P95 延迟飙升,因为 LLM 输出的 SSE 流在 Python 环境下解析和转发有明显开销。这个量级对中小企业勉强够用,但离我们对"企业级"的定义差距还不小。
4.3 用 Rust 重写编排层后的实测数据
我们最后把编排层核心拆出来用 Rust 重新实现了一遍,主要是上下文管理器、工具调度器和记忆访问网关三块。Motivation 很简单:tokio 让我能按住每秒上万级的空连接,同时每个会话的状态被封装在一个 Tokio::spawn 的 task 里,互不干扰。Rust 的所有权模型也帮了一个大忙:会话状态不会在并发访问时被意外修改,因为编译器层面就不允许你在多 task 之间共享可变引用。
贴一个小核心的内存管理结构示意:
struct SessionState { working_memory: VecDeque<MemoryItem>, // 滑动窗口的工作记忆 task_status: HashMap<String, TaskProgress>, context_budget: usize, // 上下文预算控制 } struct SessionManager { sessions: Arc<RwLock<HashMap<String, Arc<Mutex<SessionState>>>>>, }用RwLock做多读单写,读多写少的场景下并发性能是足够好的;写操作只发生在会话轮转和记忆提交时,频率可控。加上 LLM 调用本身是 IO 密集,Rust 这边的 CPU 占用率非常低,瓶颈转移到了模型推理端。
压测结果对比我们来贴一下:
| 压力场景(50 并发会话) | Python async 方案 | Rust 编排核心方案 |
|---|---|---|
| 单会话平均时延 | 12.5s | 11.8s |
| P95 时延 | 28.7s | 14.9s |
| 内存占用峰值 | 4.2GB | 1.6GB |
| 单次会话工具调用失败率 | 5.4% | 1.1% |
最大的提升不是平均时延——因为模型推理时间占了大部分——而是 P95 的断崖式下降。这说明性能瓶颈主要在支撑系统而不是计算本身。内存占用降了 2.5 倍,这个对私有化部署里的一台 32GB 机器来说就是能不能开多实例的区别。
提示:Rust 重写完成后,我们在 LangGraph 和 Rust 编排层之间做了一个 gRPC 桥接器。也就是说,LangGraph 里已有的图逻辑和工具注册可以保留,只是把状态管理和会话调度下沉到 Rust 侧。这个渐进式策略强烈建议采用,不要试图一次性推翻你手上能跑的方案。
5. 企业私有化 Agent 的安全边界与权限控制
5.1 工具调用失控的典型案例
很多人把 Agent 安全想成"提示词注入"一个点,但实际企业里更危险的是权限失控。我们最早的一个原型版本把工具调用设计得很开放:Agent 权限继承调用者的权限,用户能查什么库、调什么接口,Agent 就能做什么。上线试运行第一周,有个测试员工让 Agent "把所有能看到的合同都列出来",Agent 真把包含多部门合同的文档列表拉出来了。原因是这位员工的账号在公司群里是"全域可读"权限,Agent 在毫不知情的情况下继承了这个权限。
这个案例说明:Agent 的工具调用不能直接映射为"用户即管理员"。必须引入独立的 Agent 权限层,对工具调用做二次前置审批。你可以理解成公司里的"实习生"——实习生再怎么能干,也不能以你的名义去签重要文件,他们的每个跨权限动作都需要上级授权。
5.2 权限模型设计:Agent 不是全能管理员
我们在权限模型上参考了 K8s 的 RBAC 思路,把权限拆成三层:
身份层先确认调用者是谁、属于哪个组织单元、有什么角色。然后是工具权限层——Agent 能调哪些工具,由身份+可信等级+上下文共同决定。数据权限层最细粒度——即使能调"查合同"工具,也要按项目的合同可见范围过滤。
工具权限层里还有一个小的设计点:敏感工具需要"二次确认"。比如发送邮件、删除文件、修改数据库这类不可逆操作,必须走一个回调让用户确认。这个设计可以在框架层做,我们是在工具注册表里给每个工具加了一个requires_confirmation字段,Agent 在执行前检查到该字段为 true 就暂停流程,把待确认操作推给用户。这个小机制帮我们躲过了至少三次误操作。
5.3 记忆数据的脱敏与访问控制
记忆系统是另一个需要注意的安全面。你有没有想过一个问题:Agent 记住员工甲的项目偏好,那员工乙来问同一个项目的进展时,应不应该让乙看到这些记忆?如果不控制,记忆库就是变相的数据泄露。
我们的做法是给每条记忆打上多个标签:所属空间(部门级/项目级/个人级)、敏感等级(公开/内部/机密/绝密)、创建者、创建时间。检索时,过滤条件强制带上当前用户的权限标签。这部分我们在 qdrant 的 payload 索引里做了过滤,实际性能损耗在可接受范围内。
脱敏这块我们用了两个策略。一是在记忆抽取阶段做了实体识别,把身份证号、手机号、银行卡等 PII 字段自动替换为占位符,只保留对语义有用的类型信息。二是在敏感记忆展示给用户前,做过一次"最小化输出"检查,判断输出内容是否超出当前用户的知晓范围。例如 Java 公司的一件小事告诉我们:Agent 的记忆系统如果没做脱敏,一个"记性好"的 Agent 会比"记性差"的 Agent 危险十倍。
6. 实操记录:一个企业私有化 Agent 的完整部署
6.1 环境准备与依赖安装
如果你的企业想复现这套方案,先给你一套可直接抄的环境清单。我们生产环境用的是 4 张 A800 GPU 的节点跑 vLLM 服务,但最小可运行的开发环境其实一张 4090 就够了(跑 13B 级别模型)。
基础依赖我列一下:
# 模型推理服务 pip install vllm # 向量库 docker run -d --name qdrant -p 6333:6333 qdrant/qdrant # 编排层(Rust 侧) cargo add tokio serde prost tonic # 记忆抽取/改写(Python 侧) pip install langchain-community bge-reranker这里有一个我刚开始踩的坑:向量库和 PostgreSQL 的连接池千万不要放在同一台机器上裸奔,两层 IO 会互相抢资源。我们后来把 qdrant 挪到了独立容器,并且限制了它的内存到 16GB,情况才稳定下来。
6.2 最小可运行 agent:对话 + 记忆 + 工具
给你一个最小可运行的核心逻辑流程,从用户输入到记忆写入一共 7 步:
- 用户输入进来,先做会话恢复——从会话 ID 读取工作记忆和近期事件。
- 查询改写——把缩写、指代还原成完整问题。
- 混合检索——向量库检索语义记忆 + SQL 检索结构化事件。
- Rerank 召回 Top-5 记忆块,写入当前上下文。
- LLM 推理 —— 选工具、生成回复。
- 执行工具(带权限确认),结果写回上下文。
- 对话结束后异步做记忆抽取、合并、写库。
第 7 步有个小技巧:记忆写入不要同步阻塞主流程。用户问完问题,回复发出去了,再在后台慢慢写记忆。这样即使用户只是随便聊一句,也不至于因为写记忆失败导致回复失败。我们落库时用的是 tokio::spawn + 批量提交,每 30 秒刷一次批次。
6.3 从沙盒到生产环境的改造
开发环境和生产环境的差距比想象中大得多。沙盒里只有一个模拟用户,生产环境有几十个真实员工同时用不同的身份调 Agent,这时候你会发现两个问题:上下文污染和权限不足。
上下文污染指的是多个用户的对话状态在一个 Sanbox 进程里串了。这个问题的根源是我们最初用了一个全局 HashMap 存会话,键值只有 session_id 没有加 user_id 维度。后来改成(user_id, session_id)复合键,问题立刻解决。另一个是权限问题,沙盒测试不会走 LDAP 身份验证,所有用户默认都是超管;生产环境接入 LDAP 之后,很多工具调用的权限过滤逻辑才真正被激活,第一次上线当天就发现了 10 多个权限过滤不完整的边界情况。
还有一个容易被忽略的点:沙盒和生产的模型推理配置不一样。沙盒用max_tokens=2048就能跑通,但生产环境对话轮次多了之后,上下文膨胀得很快,必须配合上下文压缩/截断策略。我们在生产环境加了基于 token 预算的自动摘要:当窗口超过 12k token 时,把最早的一部分对话压成一段摘要,塞回窗口头部。
7. 常见问题与排查技巧实录
7.1 问题速查表
把我们在运维过程中最常见的问题整理成了一张速查表,可以直接贴给你团队的运维手册:
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
| Agent 回复"稍等"后无响应 | 工具执行超时 | 检查工具调用超时配置,增加单一工具 30s 超时 |
| 记忆检索结果全是无关信息 | 查询改写失效 | 检查企业术语表是否覆盖了当前领域专有词 |
| 高并发时 P95 飙升 | 会话状态锁竞争 | 检查是否用数据库存会话状态,改成内存+定期持久化 |
| 工具调用偶发"权限不足" | 权限标签不完整 | 审计记忆条目的归属空间和敏感等级标签 |
| Agent 上下文丢失 | 工作记忆滑动窗口太小 | 上调窗口阈值或增加自动摘要 |
| 向量查询耗时异常增长 | 索引失效 | 定期检查 qdrant 段合并状态,重建 HNSW 索引 |
| 多人对话中信息互相模糊 | 复合键缺少 user_id | 会话 ID 必须绑定用户身份 |
7.2 几个容易被忽视的坑
第一个坑是 Agent 沙盒初始化失败。我们在本地调试时经常遇到"Agent execution terminated due to error"这类报错,最后才发现是沙盒临时目录权限问题——Rust 侧的文件工具默认写入/tmp,而容器里/tmp挂载成了只读。改掉挂载配置就解决了。这种问题没什么技术含量,但会让你排查一整天。
第二个坑是模型推理的异常输入格式。LLM 输出偶尔会被截断,导致工具调用的 JSON 参数不完整。我们后来在模型侧加了一个"JSON 修复"后处理程序:捕获到截断时,用一个小模型做补全,而不是直接重跑整个 Agent 流程。这个改动把工具调用的失败率从 4% 降到了 0.5% 以内。
第三个坑跟记忆写入的异步化有关。我们刚开始做后台异步写记忆时,发现大量记忆写完之后没有关联到正确的会话,因为 tokio::spawn 里捕获的 session_id 是 move 前的旧值。这是典型的异步闭包变量捕获问题,排查很久才发现。现在所有会话变量在使用前都会强制 clone 一份,并在日志里打 trace_id 追踪。
第四个坑,也可以说是个原则:不要把企业 Agent 的沙盒做成完全封闭的。很多人为了安全把 Agent 的工具执行环境隔离在容器里,不让它访问内网。这确实安全,但 Agent 也废了——它需要访问内网的 OA 系统、文档库,才能做真正的业务操作。我们的方案是"半沙盒":限制 Agent 只能访问白名单内的内网服务,允许出网到特定的 API 网关,但禁止访问运维后台和管理接口。这个折中点需要在安全和业务价值之间反复拿捏,每个企业不一样。
最后再分享一点个人体会。做企业私有化 Agent 这一路下来,最深的体会是:技术难点往往不在单点,而在系统性。模型选型、框架选型、记忆设计、并发模型、权限控制,任何一个环节掉链子,整个系统的体验都会大打折扣。Memory OS 这个方向最吸引我的地方,就是它把记忆提升到了一个跨越所有组件的基础设施层——在这个架构里,Agent 不是一堆模型的拼装,而是一个真正有自己的经验和判断力的"数字同事"。如果你也在做类似的事,建议从单会话记忆开始,一点点往跨会话、跨用户、跨部门扩展,这条路没有捷径,但每一步走得稳,Agent 的价值就会指数级释放。