我们从 Lambda 开始。这里出了什么问题。
Source: Dev.to
请提供您希望翻译的完整文本内容,我将为您翻译成简体中文并保留原有的格式、Markdown 语法以及技术术语。谢谢!
Lambda 看起来非常适合 AI 工作负载
单一职责函数,自动扩展,仅为实际使用付费。我们在意识到错误之前已经构建了 7 个。
第一个 Lambda – 文档摘要器
import { APIGatewayProxyHandler } from 'aws-lambda';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const { document } = JSON.parse(event.body || '{}');
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: 'Summarize the following document in 2-3 sentences.'
},
{
role: 'user',
content: document
}
],
max_tokens: 150
});
return {
statusCode: 200,
body: JSON.stringify({
summary: response.choices[0].message.content
})
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: error.message })
};
}
};
简洁。简单。它运行得很好……直到它不再工作。
Source: …
29秒墙
我们在构建一个能够 分析复杂文档 的代理时遇到了第一个重大问题。该代理需要:
- 从文档中提取文本
- 分析关键主题
- 生成标签
- 创建摘要
- 推荐相关资源
每一步都需要 3–7 秒。总运行时间约为 ~25 秒。在 Lambda 的 15 分钟限制内,对吧?
错误。
2024-02-15 14:32:18 START RequestId: abc-123-def
2024-02-15 14:32:18 Calling OpenAI for document analysis...
2024-02-15 14:32:25 Analysis complete, generating tags...
2024-02-15 14:32:32 OpenAI inference still running...
2024-02-15 14:32:47 ERROR Task timed out after 29.00 seconds
API Gateway 有 29 秒的超时限制——而不是 Lambda。如果你通过 API Gateway 暴露函数(这很可能),就会在 29 秒处碰壁。
当此超时发生时:
- 客户端收到 504 Gateway Timeout
- Lambda 仍在运行并消耗费用
- OpenAI/Bedrock 调用完成但结果丢失
- 用户看到请求失败
- 你会为完整的 Lambda 执行时间付费
我们有 30 % 的复杂代理请求因超时而失败。用户以为我们的 AI 出了问题。其实不是——只是太慢了。
流式传输?Lambda 不行
我们的用户希望获得实时聊天响应,类似 ChatGPT 的流式界面。我们尝试实现流式传输:
export const handler: APIGatewayProxyHandler = async (event) => {
const stream = await openai.chat.completions.create({
model: 'gpt-4',
messages: [/* ... */],
stream: true
});
// This is where it breaks
for await (const chunk of stream) {
// How do you stream through API Gateway?
// You can't.
}
};
API Gateway 会在将整个 Lambda 响应发送给客户端之前先进行缓冲。无法流式传输部分数据;客户端在 Lambda 完成之前什么也看不到。
变通办法: 使用 WebSockets,但这意味着:
- 单独的 WebSocket API Gateway
- 连接管理
- 消息路由
- 状态跟踪
- 更多复杂性
我们尝试过;代码体积膨胀到原来的 3 倍,仅为实现一个简单的流式响应。
地狱般的冷启动
AI SDK 体积庞大。我们导入了以下内容:
import OpenAI from 'openai'; // 2.1 MB
import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime'; // 1.8 MB
import Anthropic from '@anthropic-ai/sdk'; // 1.9 MB
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; // 1.2 MB
import PDFParse from 'pdf-parse'; // 0.9 MB
总捆绑大小: ~8 MB
冷启动时间: 8–12 秒
当 Lambda 超过 5 分钟未运行时,AWS 会创建一个新容器。容器启动 + 代码初始化 = 用户需要 10+ 秒 才能得到首次响应。
由于我们的 AI 功能 使用不频繁,冷启动几乎是常态:
- 文档分析:约 20 次 / 小时
- 图像分类:5–10 次 / 小时
- 内容生成:1–2 次 / 小时
每个函数每天都会多次进入冷状态。用户上传文档后,需要等待约 12 秒,便会误以为平台出现故障。
我们尝试了 预置并发(Provisioned Concurrency)。它确实有帮助,但仅为保持函数“热”就要花费 ≈ $50 / 月 每个函数。7 个函数合计 ≈ $350 / 月,而几乎没有处理任何请求。
无共享状态
多轮对话是不可行的。以下是我们的尝试:
// Turn 1: User asks about a document
export const chatHandler: APIGatewayProxyHandler = async (event) => {
const { message, conversationId } = JSON.parse(event.body || '{}');
// Get conversation history from DynamoDB
const history = await getConversationHistory(conversationId);
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
...history,
{ role: 'user', content: message }
]
});
// Save new messages to DynamoDB
await saveMessage(conversationId, 'user', message);
await saveMessage(conversationId, 'assistant', response.choices[0].message.content);
return {
statusCode: 200,
body: JSON.stringify({ response: response.choices[0].message.content })
};
};
每个请求都需要:
- DynamoDB 读取 以获取对话历史
- AI 推理
- 两次 DynamoDB 写入 以持久化交流
对于一次 3 轮对话,这意味着 3 次读取 + 6 次写入,导致明显的延迟和成本增加。
成本激增的痛点
Lambda 按毫秒计费,但 AI 推理的延迟不可预测:
- 简单问题: 2–3 秒
- 复杂分析: 15–25 秒
- 代码生成: 10–30 秒
- 图像分析: 5–20 秒
一个高费用月份的成本细分
Document Summarizer: 1,200 requests × 8 s avg = 2.7 h = $180
Image Classifier: 800 requests × 12 s avg = 2.7 h = $180
Content Generator: 400 requests × 18 s avg = 2.0 h = $135
Chat Agent: 2,000 requests × 15 s avg = 8.3 h = $560
Tag Suggester: 3,000 requests × 5 s avg = 4.2 h = $280
PDF Analyzer: 200 requests × 22 s avg = 1.2 h = $80
Report Builder: 100 requests × 35 s avg = 1.0 h = $65
---------------------------------------------------------------
Total: $1,480
我们为 AI “思考”时间支付了 Lambda 计算费用。一次 20 秒的 GPT‑4 调用实际上只使用了 50 毫秒的 CPU,但我们仍需为 Lambda 完整的 20 秒运行时间付费。
相比之下,使用可以在单个 AI 调用处理期间处理多个请求的长时间运行容器——成本要高效得多。
最糟糕的是?峰值使用放大了这个问题。业务时间内我们有 50+ 并发 Lambda 执行 正在等待 AI 响应。每个执行都在消耗金钱,而实际计算发生在 OpenAI 的服务器上。这就像在交通堵塞的出租车里付费——你付的是时间,而不是进度。
Source: …
多轮代理循环
最终的关键点是构建一个能够帮助用户组织资产的代理。工作流程:
- 用户: “帮我整理我的产品照片。”
- 代理: 分析可用的照片,提出澄清性问题。
- 用户: 提供筛选标准。
- 代理: 建议文件夹结构。
- 用户: 批准或请求更改。
- 代理: 执行组织操作。
每一步都是一次独立的 Lambda 调用。状态管理如下所示:
// 步骤 1:初始请求
await saveToDynamoDB(sessionId, {
step: 'analyzing',
photos: userPhotos,
status: 'in_progress'
});
// 步骤 2:代理响应
const session = await getFromDynamoDB(sessionId);
await openai.chat.completions.create(/* ... */);
await saveToDynamoDB(sessionId, {
...session,
step: 'awaiting_criteria',
analysis: result
});
// 步骤 3:用户提供标准
const session = await getFromDynamoDB(sessionId);
// ... 依此类推
到第 6 步时,我们已经有 12+ 次 DynamoDB 操作、6 次 Lambda 调用,以及一个每次加载都变得昂贵的对话上下文。
用户体验很笨拙,因为每一步都需要一次新的 HTTP 请求。没有持久连接、没有实时更新、没有流式传输——只有请求‑响应循环,与 ChatGPT 相比显得支离破碎。
“这感觉像是 2010 年的软件,”我们的产品负责人在尝试了一遍工作流后说。他并没有说错。
临界点
我们的基于 Lambda 的 AI 平台存在根本性问题:
- 29 秒超时 导致复杂工作流被中止
- 没有流式传输 使聊天体验破碎
- 冷启动 带来 10+ 秒的延迟
- 成本低效 因为为 AI 等待时间付费
- 状态管理复杂 让代理变得痛苦
- 集成散乱 跨 7 个不同的函数
我们花在对抗基础设施上的时间比构建功能的时间还多。用户抱怨响应慢,而我们的 AWS 账单却不断攀升。
Lambdas:完美的 AI 工具,糟糕的 AI 代理
Tools 是单一用途、无状态且快速的:
- 对图像进行分类
- 对文档进行摘要
- 从 PDF 中提取文本
- 生成替代文字(alt text)
Agents 是多轮对话、带状态且复杂的:
- 帮我整理照片
- 分析数据并生成报告
- 与我的文档聊天
- 基于对话构建工作流
对于工具而言,Lambda 是理想选择。对于代理,则需要持久连接、共享状态和流式传输——这些正是 Lambda 在每一步都在抵制的。
我们最终构建的方案
我们创建了一个 网关:一个可以同时处理工具和代理的单一 API 端点,具备完善的流式传输、状态管理以及供应商灵活性。
架构概览
- API 网关 → 将请求路由到轻量级 Lambda(网关逻辑)
- 网关 Lambda → 将请求代理到执行实际 AI 处理的长运行容器
这让我们兼顾了两者的优势:API 层的无服务器弹性伸缩以及 AI 工作负载的持久连接。
在下一篇文章中,我将详细介绍网关模式,并展示我们如何将七个不同的 AI Lambda 统一为一个简洁的 API,能够兼容任何模型提供商。
这是一篇关于构建生产级 AI 平台的 8 部分系列的第 2 部分。完整代码示例可在此处找到。