做 AI Agent 开发一年多,我发现自己已经很少把焦虑放在模型能力上。Prompt 写得再完整、工具编排再细致,一旦进入生产环境,问题往往集中在两个地方:状态存在哪里,隔离够不够。第一个问题让 Agent 的对话上下文、任务状态、检索记忆全部压到了存储层;第二个问题则直接决定你敢不敢把多租户业务放上去。这两个点叠加到一起,选型矛盾就被推到了数据库这一层。
最近我为一个多租户 Agent 平台做基础设施选型,重点评估了 PolarDB Agent Express 这类“为 Agent 场景优化的云托管数据库形态”。它把关系型数据库能力、面向 Agent 的状态与语义检索增强、VM 级安全隔离、Serverless 弹性扩缩容整合在一个交付形态里。这篇我就把自己拆解和实操的过程完整记录下来,重点聊聊 VM 级安全隔离和 Serverless 弹性能力为什么对 Agent 场景这么重要,以及我们落地时踩过哪些坑。
不管你是技术负责人、后端工程师,还是刚接触 Agent 开发的初学者,只要你在思考怎么让 Agent 应用在真实流量下跑得稳、住得进、分得清,这篇应该能提供一些参考价值。
1. 选型背景:AI Agent 的工程瓶颈不在模型,在状态与隔离
1.1 Agent 跑起来之后,真正被放大的是存储压力
现在的 Agent 早就不是“一问一答”的简单闭环。支持多轮对话的客服 Agent,需要长期记忆保存用户偏好;运营类的 Agent,要把任务拆解成阶段状态,随时写入进度;RAG 类 Agent 除了检索文档,还要把中间结果缓存下来,避免下一轮重复计算。这些状态数据如果只放在内存里,进程一重启就全没了;如果全塞进常规业务表,SQL 的读写压力又会变得相当难看。
举一个我们实际遇到的场景:某个会话里 Agent 连续调用四个工具,每个节点都会产生状态变更,等于一次用户请求,背后至少有五次数据库写入。日活一上来,存储层的写入量就是普通业务接口的十几倍。所以在“AI Agent 如何搭建”这类实操问题上,我的回答一般会先绕开模型层,直接把话题带到数据层。模型能力可以调用外部大模型接口,编排框架可以由开源方案兜底,但数据库本身,很难在业务跑起来之后再轻松换掉。
我之前也做过把 Agent 状态直接塞 Redis 的原型验证,开发确实很快,但等到要支持历史回溯、语义检索、跨会话记忆时,Redis 的方案就捉襟见肘了。必须有一个能把关系数据、状态快照、向量检索放在一起的地方,这也是我这次选型最原始的需求起点。
1.2 多租户让“隔离”从安全要求升级为业务要求
如果我们只是给自己的内部工具做个 Agent,隔离问题其实可以往后放。但一旦做成 SaaS 服务,平台要同时服务多个租户,隔离就从合规层面的“最好有”,变成了商业上“必须有”的东西。
原因很简单:Agent 场景里,租户的数据边界比普通业务更敏感。普通账户系统隔离的是账号密码和个人资料,而 Agent 平台的隔离范围包括会话历史、向量记忆、工具调用返回的业务数据、RAG 的私有文档。这些都是高度机密的信息。并且 Agent 的编排逻辑还会跨工具、跨服务,如果底层数据库没有清晰隔离,任何一个租户的数据泄露,都不只是技术事故,可能直接动摇客户对平台整体的信任。
所以我在做平台选型时,直接圈定了一条硬性要求:提供计算与存储的多租户隔离能力,而且最好是基础设施层面的隔离,而不是应用层靠where tenant_id = ?硬扛。应用层过滤理应存在,但只有应用层过滤,始终像在一间共享办公室的分区里讨论机密方案,心理安慰大于实际保障。带着这条硬性要求,我们把目光锁定到了 VM 级安全隔离这个能力上。
2. PolarDB Agent Express 的定位与设计思路
2.1 它到底是什么:兼容 MySQL 语义 + Agent 工作负载增强 + 云托管
简单理解,PolarDB Agent Express 不是一个全新的数据库内核,而是基于云原生数据库能力,面向 AI Agent 场景做了一整套增强和交付形态调整的云托管服务。
第一层是兼容性。它保留了对 MySQL 协议的兼容,这意味着我们团队现有的 Java、Go、Python 数据访问层几乎不用改,只要把连接串换掉就能跑通。对正在做 AI Agent 开发、又不想重构后端存储的团队来说,这一条能省掉大量迁移时间。
第二层是针对 Agent 工作负载的增强。常规数据库更多围绕“事务”和“报表”设计,而 Agent 场景里有大量会话快照、向量检索、语义缓存、分布式任务状态这类偏“状态型”的数据。Agent Express 在存储和索引层为这些场景提供了更顺手的支持,比如面向向量和高频会话检索的索引能力,以及在写入路径上针对状态流转做了优化,减少 Agent 多轮调用时的重复写入开销。
第三层是云托管的交付形态。它不需要我们自己维护实例集群,扩缩容、备份、故障切换都由托管层处理。这一点对中小团队特别重要,我们不需要专门养一个专业的数据库运维团队,就能获得接近生产级的可靠性。换句话说,Agent Express 更像是“为 Agent 后端量身裁剪过一套配置的托管数据库”,而不是让你在不熟悉的底层组件上自己拼装。
2.2 为什么不用“通用 MySQL + 向量数据库”拼一套
很多人看到这种云托管数据库,第一反应是:我现在已经有 MySQL、Redis、一个独立向量库,业务也能跑,为什么要换?我一开始也是这种想法,但真正在 Agent 场景里把链路串起来之后,拼接式方案的问题就一个一个浮出来了。
最直接的问题是数据一致性。会话状态在 MySQL,向量在向量库,缓存状态在 Redis,每次 Agent 调用工具都要同时更新三个系统。任何一个节点成功、另一个节点失败,就会出现状态不一致。为了弥补,又得引入分布式事务或者大量补偿逻辑。这相当于为“拼装”的代价买单,而且这个代价会随着 Agent 节点数量增加而变高。
第二个问题是访问延迟和链路复杂度。Agent 的一次决策往往需要经过“查状态 → 检索相似记忆 → 决定下一步 → 写回新状态”四个步骤。在拆分方案里,这四步要跨至少两个网络连接,并且每个系统都有各自的连接池和超时策略,任何一个环节抖动,Agent 的响应时间都会被放大。
第三个问题是运维认知的碎片化。排查问题时,一条请求链路要同时看 MySQL 慢日志、Redis 命中率、向量库的召回结果,团队的学习成本也被拉高了。Agent Express 这类产品把这些问题打包收敛,让应用侧只面对一个存储入口,我评估下来,对多数中小团队其实是更省心的结构。
2.3 一次选型验证中的关键决策点
既然服务平台选型,光看白皮书不够。我们在验证阶段列了一张对照表,把“通用 MySQL + 自建向量库”和 “PolarDB Agent Express”放在同一个业务场景里跑了一遍。
| 对比维度 | 通用 MySQL + 自建向量库 | PolarDB Agent Express |
|---|---|---|
| 会话状态读写 | 多表 join,状态流转需要额外冗余字段 | 提供 Agent 状态模型,读写路径更匹配 |
| 向量检索 | 需要单独搭建向量库并维护索引 | 支持向量索引能力,同一个库内完成 |
| 多租户隔离 | 依赖应用层过滤或单独建库 | 可配置 VM 级隔离与网络策略 |
| 弹性扩缩容 | 需要预购容量,扩容要改配置重启 | 按负载自动伸缩,无需中断业务 |
| 运维成本 | 需要自行处理监控、备份、容灾 | 云托管形态,备份容灾由平台侧负责 |
这不是说拆分方案一无是处。大型企业如果已经有成熟的数据库平台团队,完全可以根据自身能力做很多自定义优化。但对多数做 AI Agent 开发的中小团队来说,把业务逻辑写在业务代码里,把基础设施复杂度交给托管服务,性价比更高。
3. VM 级安全隔离:把租户之间的“物理较真”变成引擎特性
3.1 VM 级隔离到底隔离了什么东西
先解释一下 VM 级安全隔离。VM,也就是虚拟机,有一套完整的虚拟化内核和独立资源配额,和同一个物理机上的另一个 VM 之间,在操作系统层面是互相不可见的。
在数据库托管场景里,VM 级隔离意味着每一个数据库实例或一组实例独占一个虚拟机资源域。分配给你的计算资源是这个 VM 配额内的,内存、CPU、网络带宽都按虚拟机边界划分,不会出现因为隔壁租户把内存占满,你的进程就被挤挂掉的情况。这个特性有点像精装公寓楼里的独立套房:水电燃气各走各的表,邻居装修再吵也不会砸到你家承重墙。
在偏传统的、共享池的数据库方案里,租户之间往往借着一层资源调度器共享底层资源。虽然大多数情况下不会出大问题,但遇到极端流量互相争抢,或者安全审计要求强逻辑隔离时,就会比较尴尬。VM 级隔离在这些方面明显更经得起推敲。
3.2 为什么 Agent 平台特别需要 VM 级隔离
Agent 场景对隔离的敏感度,比普通 CRUD 系统高很多。
第一,Agent 会“学会”业务数据。长期记忆、语义检索会把客户的核心信息沉淀到数据库里,这不是一个只读的报表库,而是一个正在不断吸收和串联业务知识的活数据体。一旦租户边界失衡,后果不只是某行数据被看到,而是整套记忆和上下文被推断出来。
第二,Agent 的调用模式是突发性的。用户可能在一次会话中连续触发多个工具,而每个工具又会产生多次数据库操作。这种节奏会产生明显的资源尖峰,如果没有隔离,一个租户的尖峰很可能影响另一个租户的响应速度。VM 级隔离相当于给每个租户加了“资源天花板”,同时避免了“坏邻居”效应。
第三,合规审计需要证据链。在面向 To B 客户时,客户经常会问:你们的数据隔离是应用层做做样子,还是有底层实现?这时候,VM 级隔离是一个可以直接写进 SLA 的特性,因为它有明确的资源边界,可以出具更清晰的资源与安全边界说明。
3.3 我们在安全隔离上做过的验证动作
理论说再多,不如动手测一遍。我们在测试环境里做了三个比较有代表性的验证。
第一个是网络隔离验证。我们为两个测试租户分别配置了不同的访问白名单,然后尝试从租户 A 的客户端访问租户 B 的内网地址,预期结果是连接被拒绝。这里要注意的是,白名单策略只是第一道门,真正起作用的是底层网络策略的强制分发,我们反复试过,未授权地址在 TCP 层就被挡掉了。
第二个是资源争抢验证。我们让租户 A 持续执行一个高消耗的聚合查询,把 CPU 打高,同时观察租户 B 的业务查询延迟。在 VM 级隔离下,租户 B 的 P99 延迟几乎没有变化。这说明资源配额不是写在纸上的,而是真实生效的。
第三个是数据残留验证。我们做了一个模拟租户退出的测试,删掉租户 A 的全部会话数据后,再由另一个测试账号尝试访问该租户的备份路径和回收站路径,确认无法看到任何残留数据。这类测试在传统共享存储方案里可能比较复杂,在 VM 级隔离的托管环境下,底层已经把存储边界拆开,验证起来顺手很多。
4. Serverless 弹性能力:从“按核付费”到“按请求风暴弹性”
4.1 弹性扩缩容的实际表现与关键参数
选择云托管数据库时,我最关注的是 Serverless 弹性能力,因为 Agent 业务的流量曲线比传统 Web 应用更加起伏不定。白天上班高峰期,用户集中发起问答和任务;夜里运维批量任务,又可能突然拉起一批 Agent 实例。如果数据库按固定规格预购,要么在高峰顶不住,要么在低谷白白浪费成本。
Agent Express 的 Serverless 形态,让我可以把这部分纠结交给平台。它按照实际负载来自动调整计算资源,不需要我想好“未来半年要买多少核多少内存”。在压力测试中,我们在短时间把并发连接从 200 拉到 2000,实例没有出现连接拒绝或超时,后台监控里能看到资源规格随负载自动向上调整。等测试流量撤掉,过一段时间,规格又自动降回低位。整个过程没有人工介入,也没有重启。
需要留意的是,“弹性”不是没有上限的。实际配置时,要设置好实例的弹性上下限,避免出现两种情况:一种是上限设得太高,某个租户流量异常时资源成本失控;另一种是下限设得太高,低谷期仍然按较高规格计费,失去 Serverless 的意义。我们最终的配置是下限 2 核 8G,上限 16 核 64G,这样既保证基础性能,又不会在极端流量下被打爆账单。
4.2 冷启动、连接池与超时权衡
Serverless 最让人担心的问题永远是冷启动。当实例长时间空闲,托管平台会回收计算资源,下一次请求到达时,需要重新拉起计算节点。如果连接池还是按“常驻连接”设计的,就可能遇到首次请求延迟飙升,甚至连接超时的情况。
我们踩过的坑是这样的:最初直接用了固定连接池,空闲半小时后再触发请求,第一条业务请求经常要等好几秒。后来调整了连接池策略,把连接池的“保活检查”打开,同时让 Agent 进程在启动时主动做一次轻量的连通性探测。对我们来说,还有一个更有效的办法:在低峰期保留一个定时执行的健康检查任务,每分钟发一次极轻量的查询,让计算节点不容易被完全回收,同时成本增加也非常有限。
如果应用对首次请求延迟特别敏感,另一个思路是在 Agent 网关侧做预热路由,让整个请求链路在业务高峰期开始前,先由定时任务把数据库连接和核心索引预热一遍。用一点低频的探活流量,换掉高峰期明显更刺眼的首次访问延迟,这笔账是划算的。
4.3 成本账:Serverless 适合谁,不适合谁
从成本角度看,Serverless 并不适合所有团队,但它和 Agent 场景的匹配度确实很高。
| 业务形态 | 流量特点 | 更适合的计费方式 |
|---|---|---|
| 固定流量 SaaS(如内部 OA、管理后台) | 起伏小、规律强 | 包年包月固定规格更划算 |
| 面向用户的 Agent 平台 | 有明显的日内高峰、活动期骤增 | Serverless 按量弹性更划算 |
| 批量 Agent 任务(夜间调度、数据处理) | 短时高并发、间歇性强 | Serverless 按量弹性更划算 |
| 高并发稳定业务 | 全天都是高位 | 包年包月大规格更可控 |
我们这类面向客户的多租户 Agent 平台,白天的高峰和夜间的批量任务落差很大,用 Serverless 后,整体资源成本比原先预估的固定规格方案低了不少。但也要注意,Serverless 对“单次执行时间特别长”的数据库操作并不友好,因为计费按实际资源占用时间算,一条 SQL 跑特别久,成本反而会比固定规格更贵。所以我们在改造时顺手优化了一批慢查询,把耗时很长的报表类查询拆成增量任务。
5. 实操:搭建一个多租户 Agent 后端(完整可复现)
5.1 创建实例与网络策略配置
以下步骤以我们团队实际使用的控制台流程为参照,不同云厂商的界面和术语可能略有差异,但整体逻辑是通用的。
首先在云控制台申请一个 PolarDB Agent Express 实例,数据库类型选 MySQL 兼容版,地域建议和 Agent 应用部署在同一区域,避免跨地域的网络延迟。创建过程中,最关键的是网络配置:为生产环境单独创建一个 VPC 和子网,不要使用默认网络,因为后续安全组、白名单、内网域名都在这个网络域内管理。
实例创建完成后,进入“白名单”配置页,把 Agent 后端服务所在的 VPC 网段加进白名单。这里有一个容易忽略的点:不要只填写应用层的公网 IP,因为云内网 IP 可能随扩容变化,直接放行应用所在子网的完整 CIDR,并精确限定端口范围,再用安全组二次收紧,是比较稳妥的策略。
随后为两个测试租户分别创建独立的数据库账号,并为每个账号只赋予对应 schema 的权限。在多租户结构里,账号隔离 + VM 级隔离是双保险,即使应用层某个逻辑出现了漏洞,数据库底层还能拦住越权访问。
5.2 核心表结构与 SQL 落地
我们为 Agent 平台设计了以下两张核心表,思路是把“会话状态”和“业务数据”分开存储,方便配合检索和隔离策略。
CREATE TABLE agent_session ( session_id BIGINT PRIMARY KEY AUTO_INCREMENT, tenant_id VARCHAR(64) NOT NULL, app_id VARCHAR(64) NOT NULL, title VARCHAR(255) DEFAULT '', status TINYINT DEFAULT 1, context_json JSON, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_tenant_updated (tenant_id, updated_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE agent_message ( message_id BIGINT PRIMARY KEY AUTO_INCREMENT, session_id BIGINT NOT NULL, tenant_id VARCHAR(64) NOT NULL, role VARCHAR(32) NOT NULL, content_type VARCHAR(32) DEFAULT 'text', content MEDIUMTEXT, token_count INT DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_session (session_id, created_at), KEY idx_tenant_time (tenant_id, created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;tenant_id是每个表都有的字段。虽然底层有 VM 级隔离,但应用层过滤依然是第一道防线,两条线并行,才是完整的隔离纵深。context_json字段专门用来存放 Agent 的运行时状态,比如当前节点、待执行工具列表、中间结果。这样在 Agent 意外中断后,从库里把 session 捞起来就能恢复现场,这在 LangGraph 等多 Agent 编排场景里尤其重要。
5.3 使用 Python 接入 Agent Express
后端服务用 Python 和 SQLAlchemy 接入非常直接,这里给出一个最小可用的接入示例。连接串里的地址和账号换成自己实例的信息即可。
from sqlalchemy import create_engine, text engine = create_engine( "mysql+pymysql://agent_svc:your_password@agent-express-cn-beijing-001.mysql.example.com:3306/agent_db?charset=utf8mb4", pool_size=10, max_overflow=5, pool_pre_ping=True, pool_recycle=3600, ) def fetch_recent_sessions(tenant_id: str, limit: int = 20): with engine.connect() as conn: rows = conn.execute( text(""" SELECT session_id, title, updated_at FROM agent_session WHERE tenant_id = :tenant ORDER BY updated_at DESC LIMIT :lim """), {"tenant": tenant_id, "lim": limit}, ).fetchall() return rows if __name__ == "__main__": for row in fetch_recent_sessions("tenant_001"): print(row)这里有几个参数值得专门说明。pool_pre_ping=True会在每次从连接池取出连接前先做一次轻量探活,避免把失效连接交给 Agent 请求,这个配置对 Serverless 场景尤其重要;pool_recycle=3600表示连接最多存活 1 小时后回收,防止数据库侧主动断开导致的“连接已关闭”报错。实际项目里连接池大小还要根据并发和数据库规格调整,不是越大越好,过大的池反而会堆积空闲连接,增加数据库侧的资源占用。
5.4 负载演练与扩容观测
搭建完成后,我们做了一轮接近生产形态的负载演练。用测试脚本模拟 50 个并发用户,每个用户连续发起 20 轮 Agent 对话,每轮对话包含 3 到 5 次数据库写入和 1 次语义检索。持续运行 30 分钟。
这轮演练暴露了一个有趣的现象:Agent 场景的写压力比读压力更突出。因为每次工具调用都会写一条消息,记一次状态变更,还可能更新一次会话的updated_at。这意味着如果只按传统“读多写少”的思路选型,很容易在写路径上翻车。Agent Express 在状态模型和写入路径上的优化,在这个时候体现得比较明显,整个压测过程中没有出现明显的写入排队。
从监控面板看,实例规格在压测启动后约 30 秒开始向上调整,期间 P99 延迟控制在可接受范围。压测结束后,规格在约 5 分钟内回落到低位。整体表现符合我们对 Serverless 弹性的预期:能上得去,也能下得来,中间不需要人工干预。
6. 常见问题与排查技巧实录
6.1 经典问题速查表
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
| 空闲后首次请求变慢 | 计算节点被回收,冷启动 | 客户端开启 pool_pre_ping,低峰期加低频健康检查 |
| 连接池报“Too many connections” | 应用侧连接数超过了实例规格上限 | 收缩连接池,确认是否触发了弹性上限 |
| 跨租户访问到了不该访问的数据 | 应用层 where 条件漏了 tenant_id | 检查 DAO 层公共查询模板,强制注入租户条件 |
| 深夜批量任务突然变慢 | Agent 任务触发了扩缩容,短暂抖动 | 为批量任务设置独立的资源上限和调度窗口 |
| 慢查询变多 | 语义检索或 JSON 字段没有走索引 | 检查执行计划,给高频条件建复合索引 |
6.2 三个值得写进代码评审的细节
第一个是事务范围。Agent 的一次流程可能涉及多张表的写入,但事务不宜开得过大,特别是不要在事务里执行远程工具调用。我们早期的代码里出现过“先开启事务,然后调用第三方 API 获取数据,再更新数据库”的写法,一次外部服务超时,数据库连接就被事务锁死半天。正确的做法是先获取外部数据,再以极短的事务提交状态变更。
第二个是 JSON 字段的使用边界。context_json确实方便,但不要在 SQL 里对 JSON 字段做频繁的模糊查询。如果经常要按状态字段筛选会话,应该把高频查询字段单独拆成普通列,并建立索引。JSON 适合存一次性快照,不适合当作主查询条件反复过滤。
第三个是重试逻辑必须带指数退避。Agent 调用链越长,底层数据库偶发错误被放大的概率越高。如果不加退避地立即重试,可能出现多个 Agent 节点同时重试,把数据库连接池打满。我们最终在重试层加了“最多 3 次,间隔 200ms、800ms、2000ms”的规则,实测能有效缓解瞬时抖动带来的连锁反应。
6.3 一个关于“弹性上限”的真实教训
最后说一个我们踩过的真实教训。一开始为了省事,我们把实例弹性上限直接拉到了系统支持的最大值,觉得反正按量计费,用多少算多少。结果在一次线上活动期间,某个租户的异常脚本把会话表刷了大量数据,数据库资源被一路顶上,那周的成本账单比平时高了一截。
复盘之后,我们把弹性策略改成了“分租户配额 + 实例总上限”的双层控制。每个租户在应用层有独立的频率限制,数据库层面的总上限控制在可接受账单范围内,宁可让极端流量被限流,也不要让失控流量直接转化为高额账单。这个教训让我对 Serverless 有了更清醒的认知:弹性是一把好刀,但刀柄要握在自己手里。
7. 最后说点实在的
做完这一轮选型和落地,我对 PolarDB Agent Express 这类托管数据库的适用范围有了更清晰的判断。如果你的 Agent 业务还停在原型阶段,用户量不大,数据隔离要求也不高,先用简单的内存状态加一个通用数据库也能撑一阵。但一旦要面向多个客户提供 Agent 能力,兼容 MySQL、能处理状态与向量检索、带 VM 级隔离和 Serverless 弹性的 Agent Express,会是值得认真评估的选项。
从个人经验来看,数据库选型最忌讳的是在业务最热闹的时候匆匆决定。最好把技术团队拉到一起,跑一轮真实的压测和故障演练,把安全隔离和弹性扩缩容都验证过,再基于账单和运维成本做最终选择。一次谨慎的选型,省下来的是后面几个月和半夜告警搏斗的精力。
这一篇就先写到这。如果你也在做类似的多租户 Agent 平台,或者正在对比一批托管数据库方案,欢迎在评论区聊一聊你的选型维度和踩坑记录。下一轮,我打算把手上的 LangGraph 多 Agent 编排实践整理成另一篇,到时候再一起复盘。