跳转到内容

部署到 AWS Lambda

AWS Lambda 是另一个常用 serverless 目标。本页讲用 Lambda Function URL (不用 API Gateway)+ SQS 做异步队列 + DynamoDB 存状态的最简部署方式。

项目脚手架(用 SAM)

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Hashee Agent on AWS Lambda
Parameters:
HasheeAgentId: { Type: String, NoEcho: true }
HasheeAgentToken: { Type: String, NoEcho: true }
HasheeWebhookSecret: { Type: String, NoEcho: true }
HasheeX25519Private: { Type: String, NoEcho: true }
HasheeEd25519Private: { Type: String, NoEcho: true }
Resources:
# 1. 接收 webhook 的 Lambda
WebhookFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs22.x
Handler: handlers/webhook.handler
Timeout: 15
MemorySize: 512
Architectures: [arm64]
FunctionUrlConfig:
AuthType: NONE
Environment:
Variables:
HASHEE_AGENT_ID: !Ref HasheeAgentId
HASHEE_AGENT_TOKEN: !Ref HasheeAgentToken
HASHEE_WEBHOOK_SECRET: !Ref HasheeWebhookSecret
HASHEE_X25519_PRIVATE_BASE64: !Ref HasheeX25519Private
HASHEE_ED25519_PRIVATE_BASE64: !Ref HasheeEd25519Private
JOBS_QUEUE_URL: !Ref JobsQueue
Policies:
- SQSSendMessagePolicy:
QueueName: !GetAtt JobsQueue.QueueName
# 2. 异步 job 处理 Lambda
JobProcessor:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs22.x
Handler: handlers/processor.handler
Timeout: 60
MemorySize: 1024
Architectures: [arm64]
Environment:
Variables:
HASHEE_AGENT_ID: !Ref HasheeAgentId
HASHEE_AGENT_TOKEN: !Ref HasheeAgentToken
Events:
QueueEvent:
Type: SQS
Properties:
Queue: !GetAtt JobsQueue.Arn
BatchSize: 5
JobsQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 60
MessageRetentionPeriod: 86400
Outputs:
WebhookUrl:
Value: !GetAtt WebhookFunctionUrl.FunctionUrl

Webhook Handler

handlers/webhook.ts
import { createWebhookDispatcher } from "@hasheeai/agent-sdk-ts";
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
const sqs = new SQSClient({});
const QUEUE_URL = process.env.JOBS_QUEUE_URL!;
const dispatcher = createWebhookDispatcher({
agentId: process.env.HASHEE_AGENT_ID!,
token: process.env.HASHEE_AGENT_TOKEN!,
secret: process.env.HASHEE_WEBHOOK_SECRET!,
privateKeyBase64: process.env.HASHEE_X25519_PRIVATE_BASE64!,
signingPrivateKeyBase64: process.env.HASHEE_ED25519_PRIVATE_BASE64!,
baseUrl: "https://api.hashee.ai",
onMessage: async (msg) => {
if (msg.payload?.type !== "text") return;
// 立即 enqueue,不在 webhook handler 里同步处理
await sqs.send(new SendMessageCommand({
QueueUrl: QUEUE_URL,
MessageBody: JSON.stringify({
conversation_id: msg.conversation_id,
text: msg.payload.text,
sender_id: msg.sender_id,
}),
}));
},
onEvent: async (event) => {
// ...
},
});
export const handler = async (event: any) => {
try {
await dispatcher.handle(event.headers, event.body ?? "");
return { statusCode: 200, body: "ok" };
} catch (err: any) {
return { statusCode: err?.statusCode ?? 500, body: "error" };
}
};

Job Processor

handlers/processor.ts
import { restRequest } from "@hasheeai/agent-sdk-ts";
interface Job {
conversation_id: string;
text: string;
sender_id: string;
}
export const handler = async (event: any) => {
for (const record of event.Records ?? []) {
const job: Job = JSON.parse(record.body);
try {
const reply = await callLLM(job.text);
await restRequest({
method: "POST",
baseUrl: "https://api.hashee.ai",
path: `/agents/${process.env.HASHEE_AGENT_ID}/messages`,
token: process.env.HASHEE_AGENT_TOKEN!,
body: {
conversation_id: job.conversation_id,
payload: { type: "text", text: reply },
},
});
} catch (e) {
console.error("[job] failed:", e);
throw e; // SQS 重试
}
}
};
async function callLLM(text: string): Promise<string> {
return `Echo: ${text}`;
}

部署

Terminal window
sam build
sam deploy --guided
# 输入 stack name, region, parameter overrides

完成后 Output 有 WebhookUrl,类似 https://abc123.lambda-url.us-east-1.on.aws/

注册到 Agent

把 DemoBot 的连接模式改成 webhook
URL: https://abc123.lambda-url.us-east-1.on.aws/
secret: <HASHEE_WEBHOOK_SECRET 值>

持久状态:DynamoDB

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, GetCommand, PutCommand } from "@aws-sdk/lib-dynamodb";
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
async function getConvState(convId: string) {
const r = await ddb.send(new GetCommand({
TableName: process.env.STATE_TABLE!,
Key: { conv_id: convId },
}));
return r.Item ?? null;
}
async function setConvState(convId: string, data: object) {
await ddb.send(new PutCommand({
TableName: process.env.STATE_TABLE!,
Item: { conv_id: convId, data, updated_at: Date.now() },
}));
}

Cold start

Lambda Node.js 22 cold start 约 200-400ms。要降到更低:

  • 用 ARM64(约 -30% 延迟)
  • 用 Lambda SnapStart(Node.js 22 不支持,等待 SDK 更新)
  • 把私钥 import 提到 module scope(避免每次 cold start 重做 import)

性能与限制

Lambda 限Hashee 限
单 invocation 超时15 min(webhook 我们设 15s)10 s
内存10 GB max
并发1000 / region 默认
请求 body6 MB(Lambda)1 MB(Hashee)
Function URL throttle10000 / s

故障排查

症状排查
webhook 不被调用检查 Function URL CORS / Auth;URL 是否带在 Hashee Agent 注册
401 invalid signaturesecret 错;CloudFormation 参数是否更新
Lambda timeoutwebhook 必须 ≤ 15s,长任务 enqueue
SQS 消息堆积增 BatchSize 或 reserve concurrency

下一步