最新n1n v2.0.1 正式上线!企业级大模型接口聚合平台 (LLM API Gateway),为您接入 500+ AI Models,价格低至 1 折, 立即尝试

将 Agentic RAG 应用迁移至 AWS Serverless 的五个关键架构决策与踩坑指南

作者
  • avatar
    姓名
    Nino
    职业
    Senior Tech Editor

将复杂的 Agentic RAG(检索增强生成代理)应用从单台虚拟机迁移到完全无服务器(Serverless)云架构,在理论设计上看似简单:将常驻服务替换为云厂商的托管服务、绑定事件触发器,即可将闲置基础设施成本降低至接近零。然而,在真实的云原生工程实践中,往往需要根据云厂商的具体限制调整既有方案。

在为 Agentic RAG 应用设计云原生后端时,除了像 n1n.ai 这样能大幅简化多模型接入的高效 LLM API 聚合平台外,开发者还需要解决底层云资源、计算节点与数据持久层之间的协同问题。

本文将深入探讨将一个运行在单台 EC2 实例(t4g.small,部署了 PostgreSQL、Streamlit、本地 PyTorch 向量模型及 Grafana)上的 Agentic RAG 应用,迁移至由 AWS Lambda、Amazon Bedrock、CloudFront、S3 Vectors 及 DynamoDB 构成的 Serverless 架构时所面临的五个关键重构决策。


架构演进对比:迁移前后

架构维度迁移前(单机 EC2 t4g.small迁移后(Serverless 目标架构)
计算与托管单台 VM 运行 Streamlit + Python 后端应用CloudFront + S3 静态前端;AWS Lambda 处理 API 请求
数据库与检索PostgreSQL(关系型)+ minsearch(TF-IDF)/tmp 目录下的 SQLite 文件 + Amazon DynamoDB
模型推理引擎本地 CPU 运行 PyTorch 模型(闲置成本 ~$15/月)Amazon Bedrock 托管 API(Claude 3.5 / Nova / Titan)
向量检索内存索引Amazon S3 Vectors + SQLite FTS5
数据同步管道本地同步运行脚本AWS Step Functions + Bedrock Batch 批处理作业

决策一:用 S3 挂载 SQLite 与 DynamoDB 替代 Aurora Serverless v2

遇到的坑

原计划采用 Amazon Aurora Serverless v2 替代传统 PostgreSQL,并通过 HTTPS 协议的 RDS Data API 供 AWS Lambda 调用,从而避免将 Lambda 放入 VPC 所带来的高额 NAT Gateway 费用。

然而,Aurora Serverless v2 设有 0.5 ACU(Aurora 容量单位)的最低运行下限,即使没有任何流量,每月固定支出也高达约 43 美元,是原单机 EC2 成本的三倍。虽然可以配置 0 ACU 自动暂停,但其触发暂停的最小间隔为 300秒,且冷启动恢复需要 15秒至 30秒。对于低频访问的 Demo 系统,首次请求面临 30秒延迟会严重破坏用户体验。

解决方案:只读 SQLite 数据库构件

分析业务数据发现,系统数据库仅包含 278个项目记录及数千条库元数据,总体大小约 88 KB。这些数据仅在数据摄入管道运行时重新生成,在前端查询阶段完全只读。

因此,架构进行了如下调整:

  1. 离线 AWS Step Functions 数据管道在构建阶段将数据编译为包含 FTS5 全文检索索引projects.sqlite 文件。
  2. 将该 SQLite 文件上传至 Amazon S3
  3. AWS Lambda 在冷启动时将 projects.sqlite 下载至本地 /tmp 临时目录。
  4. 动态写操作(对话历史、用户反馈、Token 消费账本)分离至 Amazon DynamoDB

整体数据库月度支出从约 43 美元骤降至不到 1 美元

+-------------------+      1. 构建数据库     +-------------------+
|  Step Functions   | --------------------> |   S3 存储桶       |
|  数据摄入管道     |                       | (projects.sqlite) |
+-------------------+                       +-------------------+
                                                      |
                                                      | 2. 冷启动下载
                                                      v
+-------------------+   3. 本地文件查询     +-------------------+
|  AWS Lambda API   | --------------------> | Lambda /tmp 目录  |
|  (只读查询路径)   |                       | (SQLite + FTS5)   |
+-------------------+                       +-------------------+

意外收获:检索质量提升

PostgreSQL 的全文检索函数 ts_rank_cd 仅基于词频和位置打分,缺乏全局逆文档频率(IDF)加权。而 SQLite 内置的 bm25() 函数能够对稀有词赋予更高的权重。迁移到 SQLite FTS5 后,关键词检索的匹配相关度甚至优于原 PostgreSQL 方案。

SQL 兼容性改写示例

针对 SQLite 不支持 PostgreSQL 独有 DISTINCT ON 语法的问题,使用了 GROUP BYMAX() 聚合函数进行等价替换:

-- 原 PostgreSQL 查询语句(使用 DISTINCT ON)
SELECT DISTINCT ON (author) repo, author, github_url, score, author_total_score
FROM projects 
WHERE author_total_score IS NOT NULL
ORDER BY author, author_total_score DESC;

-- 改写后的 SQLite 等价语句(使用 GROUP BY 与 MAX 聚合)
SELECT repo, author, github_url, score, MAX(author_total_score) AS author_total_score
FROM projects 
WHERE author_total_score IS NOT NULL AND author IS NOT NULL
GROUP BY author 
ORDER BY author_total_score DESC 
LIMIT ?;

决策二:放弃 Lambda 响应流式传输,改用 JSON 缓冲输出

矛盾点

AWS Lambda 支持响应流式传输(Response Streaming),允许将大模型的生成 Token 实时推送至前端。然而,AWS Lambda 的托管流式响应目前仅原生支持 Node.js 运行时。若要在 Python 环境下实现,必须构建自定义运行时 Layer 或引入 AWS Lambda Web Adapter。

架构权衡分析

在 Agentic RAG 系统中,后端 Agent 在生成最终回答之前,需要执行多达 6 轮的工具调用(Tool Calls)、查询改写(Query Rewrite)以及重排序(Rerank)。

[用户输入] -> [查询重写] -> [向量检索] -> [重排序] -> [Agent工具调用循环] -> [最终答案生成]
|<------------------------------ 非流式耗时阶段 ------------------------------>| |<- 流式输出区间 ->|

流式传输仅能优化最后一步文本生成的感知延迟,用户依然需要等待前期所有 Agent 链条执行完毕。

安全与计费陷阱

在 Serverless 环境下启用流式响应存在潜在的计费漏洞:如果客户端在流式传输过程中中断连接(例如关闭浏览器标签页),底层的 Lambda 函数与 Bedrock 模型的计算过程并不会立即终止,仍会继续扣减 Token 预算。对于公网开放的项目,这可能引发恶意刷量风险。

结论:采用标准的 JSON 缓冲响应形式,不仅简化了 Python 运行时的维护成本,还确保了在异常中断场景下的计费可控性。


决策三:绕过 CloudFront OAC 限制,设计源站证明请求头

遇到的问题

在标准架构中,CloudFront 与 Lambda Function URL 之间通常采用 Origin Access Control (OAC) 结合 AuthType: AWS_IAM 进行鉴权。

然而,CloudFront OAC 在处理 HTTP POST 请求时不会计算请求体 Payload 的哈希值,导致带有请求体的 POST 请求发送至 Lambda Function URL 时会触发 InvalidSignatureException 签名错误。由于 /api/ask/api/feedback 均依赖 POST 提交数据,OAC 无法直接在此类接口上生效。

若引入 Amazon API Gateway,虽然能解决鉴权问题,但 API Gateway 存在 29秒硬性超时限制,极易被多轮 Agent 循环触发。

解决方案:自定义源站验证头(Proof-of-Origin)

最终方案将 Lambda Function URL 配置为 AuthType: NONE,并利用 CloudFront 在转发请求时附加自定义 HTTP 请求头进行防盗刷校验:

[前端客户端] 
       |
       v HTTP POST
[Amazon CloudFront]
       |
       | 附加请求头: X-Origin-Secret: <Token>
       v
[AWS Lambda Function URL (AuthType: NONE)]
       |
       | 使用 secrets.compare_digest() 校验
       v
[执行 Agent RAG 业务逻辑]

Python 校验代码实现

为防止针对请求头的安全时序攻击,需要使用常数时间比较算法:

import os
import secrets
import json

ORIGIN_SECRET = os.environ.get("ORIGIN_SECRET_VAL")

def lambda_handler(event, context):
    headers = event.get("headers