多智能体系统一旦部署到 Kubernetes,就会遇到一个现实问题:Pod 会重启、扩缩容和迁移,但智能体积累的研究材料不能随之消失。NVIDIA NeMo Agent Toolkit(NAT)的内存子系统允许开发者替换底层存储提供者,因此可以把 Amazon S3 Vectors 接入为持久向量记忆层,并将计算留在 Amazon EKS 中。
这套组合尤其适合投资研究类工作流:不同智能体分别检索公司公告、市场数据和新闻,最终由汇总智能体基于共享证据生成报告。关键不是简单地“保存聊天记录”,而是让智能体能够按语义找回带来源、时间和访问范围的研究证据。
记忆层在多智能体工作流中的位置
一个典型的投资研究任务可以拆成四类角色:
- 规划智能体:把“分析某公司近期风险”拆成财报、行业、市场和新闻任务。
- 文档智能体:提取年报、季报和监管文件中的事实。
- 市场智能体:处理价格、成交量或行业指标。
- 汇总智能体:检索各智能体写入的证据,生成带依据的结论。
NAT 负责智能体、工具和工作流编排,自定义 memory provider 则把统一的写入与检索操作映射到 S3 Vectors。部署在 EKS 中的 Pod 不保存权威状态,只在任务执行期间持有短期上下文。
建议至少区分三类数据:
- 短期上下文:当前一次调用的消息和工具输出,可留在进程内。
- 语义记忆:可跨任务复用的证据、摘要和结论,写入向量索引。
- 审计数据:原始文档、完整报告和调用轨迹,通常应存入独立的对象或日志系统,而不是只放在向量元数据中。
S3 Vectors 承担的是第二类职责。它不应被视为事务数据库,也不应成为原始文件的唯一副本。
自定义 memory provider 要解决什么
一个可用的适配层通常至少暴露 remember、recall 和删除或过期处理能力。写入时需要生成向量和稳定键,查询时则要控制命名空间、元数据过滤与返回数量。
推荐为每条记忆保存这些字段:
tenant_id:租户或组织边界。workspace_id:研究项目边界。agent_id:内容由哪个智能体产生。kind:例如filing_fact、market_signal或draft_conclusion。source_uri:原始证据位置。observed_at:信息对应的业务时间。embedding_model:生成向量的模型版本。
记忆键应尽量幂等。例如对 tenant_id + source_uri + chunk_id + embedding_model 做哈希,同一文档重试时就不会无限制造重复记录。
下面是一个可独立改造的 Python 适配器。它展示 S3 Vectors 客户端与 NAT memory provider 之间的核心边界;接入具体 NAT 版本时,需要把 remember 和 recall 方法注册到该版本提供的 memory 插件接口中。不同 boto3 版本的参数名称可能变化,运行前应升级 SDK 并核对当前 S3 Vectors API。
import os
import uuid
from typing import Any
import boto3
class S3VectorMemory:
def __init__(self) -> None:
self.client = boto3.client(
"s3vectors",
region_name=os.environ.get("AWS_REGION", "us-east-1"),
)
self.bucket = os.environ["S3_VECTOR_BUCKET"]
self.index = os.environ["S3_VECTOR_INDEX"]
def remember(
self,
vector: list[float],
text: str,
metadata: dict[str, Any],
memory_id: str | None = None,
) -> str:
key = memory_id or str(uuid.uuid4())
item_metadata = {**metadata, "text": text}
self.client.put_vectors(
vectorBucketName=self.bucket,
indexName=self.index,
vectors=[
{
"key": key,
"data": {"float32": vector},
"metadata": item_metadata,
}
],
)
return key
def recall(
self,
query_vector: list[float],
top_k: int = 5,
metadata_filter: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
request: dict[str, Any] = {
"vectorBucketName": self.bucket,
"indexName": self.index,
"queryVector": {"float32": query_vector},
"topK": top_k,
"returnDistance": True,
"returnMetadata": True,
}
if metadata_filter:
request["filter"] = metadata_filter
response = self.client.query_vectors(**request)
return response.get("vectors", [])
if __name__ == "__main__":
memory = S3VectorMemory()
# 示例索引必须使用 4 维向量;生产环境应替换为真实 embedding。
memory_id = memory.remember(
vector=[0.12, -0.31, 0.88, 0.09],
text="示例公司本季度自由现金流同比改善。",
metadata={
"tenant_id": "demo",
"workspace_id": "research-001",
"agent_id": "filing-agent",
"kind": "filing_fact",
"source_uri": "s3://research-docs/example-q2.pdf",
"embedding_model": "demo-4d",
},
)
print("stored:", memory_id)
results = memory.recall(
query_vector=[0.10, -0.28, 0.91, 0.11],
top_k=3,
metadata_filter={"tenant_id": "demo"},
)
print(results)
运行前安装新版 SDK,并设置已经创建好的向量桶和索引:
python -m venv .venv
source .venv/bin/activate
pip install --upgrade boto3 botocore
export AWS_REGION=us-east-1
export S3_VECTOR_BUCKET=agent-memory
export S3_VECTOR_INDEX=investment-research
python s3_vector_memory.py
示例使用四维向量只是为了说明调用边界。实际系统必须让索引维度、写入向量和查询向量完全一致,并固定距离度量与 embedding 模型。模型升级时,最好新建索引或显式记录版本,不要把不同向量空间混在一起。
在 EKS 中部署:身份比密钥更重要
EKS 工作负载不应通过 Kubernetes Secret 长期保存 AWS Access Key。更合适的方式是为 ServiceAccount 绑定 IAM 角色,让 Pod 获取短期凭证。下面的清单可直接改造;需要替换账户 ID、角色名、镜像地址以及环境变量。
apiVersion: v1
kind: ServiceAccount
metadata:
name: nat-research
namespace: agents
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/nat-s3-vectors-role
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nat-research
namespace: agents
spec:
replicas: 2
selector:
matchLabels:
app: nat-research
template:
metadata:
labels:
app: nat-research
spec:
serviceAccountName: nat-research
containers:
- name: nat
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/nat-research:1.0.0
env:
- name: AWS_REGION
value: us-east-1
- name: S3_VECTOR_BUCKET
value: agent-memory
- name: S3_VECTOR_INDEX
value: investment-research
resources:
requests:
cpu: "500m"
memory: 1Gi
limits:
cpu: "2"
memory: 4Gi
应用清单并检查 Pod 使用的身份:
kubectl create namespace agents --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deployment.yaml
kubectl -n agents rollout status deployment/nat-research
kubectl -n agents get pods -l app=nat-research
IAM 策略只应允许该角色访问指定的向量桶和索引,并限制为查询、写入以及确实需要的删除操作。具体动作名称应以当前 S3 Vectors 服务文档为准。多租户系统不能只依赖提示词要求智能体“不要访问其他租户”;必须在查询过滤、索引划分或 IAM 资源边界上落实隔离。
检索质量决定智能体是否真的“记得”
存进去不等于能正确找回来。投资研究中尤其要关注以下问题:
- 时间失真:旧财报与新财报语义相近,但结论可能已经失效。检索后应按
observed_at做时间约束或重排。 - 来源混淆:新闻评论不能与监管文件拥有相同证据权重。可用
kind和source_uri做过滤或评分。 - 重复证据:同一公告可能被多个智能体切分并写入,需要稳定键和去重策略。
- 提示注入:外部文档中的文字只是数据,不应被当作系统指令执行。
- 敏感信息泄漏:元数据也可能包含客户名称、投资组合或内部判断,必须纳入访问控制和日志脱敏范围。
可为每次检索记录查询文本哈希、过滤条件、返回键、距离和最终被模型引用的记忆。这样才能判断问题来自 embedding、过滤条件、索引数据,还是生成模型本身。
上线前检查清单
采用这套架构时,可以按以下顺序推进:
- 先用单个研究智能体验证写入、查询、过滤和重试。
- 固定 embedding 模型、维度、距离度量与分块策略。
- 使用确定性记忆键,验证任务重跑不会产生大量重复项。
- 通过 EKS ServiceAccount 和 IAM 角色提供凭证,不在镜像或 Secret 中保存长期密钥。
- 强制加入
tenant_id、workspace_id等范围字段,并测试跨租户查询会失败。 - 对命中率、查询延迟、空结果率和模型实际引用率建立监控。
- 为删除、保留期限、模型升级和索引重建准备运维流程。
NAT、EKS 与 S3 Vectors 的组合把智能体计算和持久记忆分离开来,但它不会自动解决记忆质量问题。真正可靠的实现依赖清晰的数据边界、稳定的向量版本、严格的访问控制,以及能够解释“这条结论从哪里来”的检索记录。