跳转到内容

Template — Cloudflare Workers 部署

Cloudflare Workers 完整 starter:webhook 接入 + 异步队列 + DO 持久 per-conv 状态。 基于 部署到 Cloudflare Workers 完整教程

目录结构

hashee-cf-workers/
├── src/
│ ├── worker.ts # 入口(webhook handler)
│ ├── queue.ts # 队列消费者
│ └── conversation-do.ts # Durable Object: per-conv 状态
├── wrangler.toml
├── package.json
├── tsconfig.json
├── .env.example # 仅供本地 dev 参考;生产用 wrangler secret
└── README.md

package.json

{
"name": "hashee-cf-workers",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"tail": "wrangler tail",
"secret:put": "wrangler secret put"
},
"dependencies": {
"@hasheeai/agent-sdk-ts": "^0.2.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20260317.1",
"typescript": "^5.7.0",
"wrangler": "^4.0.0"
}
}

wrangler.toml

name = "my-hashee-agent"
main = "src/worker.ts"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]
[observability]
enabled = true
# Durable Object(per-conv 状态机;可选)
[[durable_objects.bindings]]
name = "CONVO"
class_name = "ConversationState"
[[migrations]]
tag = "v1"
new_classes = ["ConversationState"]
# Queue(异步长任务;可选)
[[queues.producers]]
queue = "agent-jobs"
binding = "JOBS"
[[queues.consumers]]
queue = "agent-jobs"
max_batch_size = 5
max_batch_timeout = 30

src/worker.ts

import { createWebhookDispatcher } from "@hasheeai/agent-sdk-ts";
export { ConversationState } from "./conversation-do";
import { processJob, type Job } from "./queue";
interface Env {
HASHEE_AGENT_ID: string;
HASHEE_AGENT_TOKEN: string;
HASHEE_WEBHOOK_SECRET: string;
HASHEE_X25519_PRIVATE_BASE64: string;
HASHEE_ED25519_PRIVATE_BASE64: string;
CONVO: DurableObjectNamespace;
JOBS: Queue<Job>;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (request.method !== "POST" || new URL(request.url).pathname !== "/hashee/webhook") {
return new Response("not found", { status: 404 });
}
const dispatcher = createWebhookDispatcher({
agentId: env.HASHEE_AGENT_ID,
token: env.HASHEE_AGENT_TOKEN,
secret: env.HASHEE_WEBHOOK_SECRET,
privateKeyBase64: env.HASHEE_X25519_PRIVATE_BASE64,
signingPrivateKeyBase64: env.HASHEE_ED25519_PRIVATE_BASE64,
onMessage: async (msg) => {
if (msg.payload?.type !== "text") return;
// 短消息 → 立即 ack
if (msg.payload.text.length < 50) {
ctx.waitUntil(
dispatcher.sendReply(msg.conversation_id, {
type: "text",
text: "收到,正在处理...",
}),
);
}
// 长任务 enqueue 异步处理
await env.JOBS.send({
conversation_id: msg.conversation_id,
text: msg.payload.text,
sender_id: msg.sender_id,
});
},
onEvent: async (event) => {
if (event.type === "relation.revoked") {
// DO 清理
const id = env.CONVO.idFromName(event.payload.conversation_id);
await env.CONVO.get(id).fetch("/cleanup", { method: "POST" });
}
},
});
try {
await dispatcher.handle(
Object.fromEntries(request.headers),
await request.text(),
);
return new Response("ok", { status: 200 });
} catch (err: any) {
console.error("[hashee] dispatcher error:", err);
return new Response("error", { status: err?.statusCode ?? 500 });
}
},
async queue(batch: MessageBatch<Job>, env: Env, ctx: ExecutionContext): Promise<void> {
for (const m of batch.messages) {
try {
await processJob(m.body, env);
m.ack();
} catch (e) {
console.error("[job] failed:", e);
m.retry();
}
}
},
};

src/queue.ts

import { restRequest } from "@hasheeai/agent-sdk-ts";
export interface Job {
conversation_id: string;
text: string;
sender_id: string;
}
interface Env {
HASHEE_AGENT_ID: string;
HASHEE_AGENT_TOKEN: string;
// ... LLM API key etc
}
export async function processJob(job: Job, env: Env): Promise<void> {
// 你的 LLM 调用(这里用一个虚拟 echo)
const reply = `Echo: ${job.text}`;
await restRequest({
method: "POST",
baseUrl: "https://api.hashee.ai",
path: `/agents/${env.HASHEE_AGENT_ID}/messages`,
token: env.HASHEE_AGENT_TOKEN,
body: {
conversation_id: job.conversation_id,
payload: { type: "text", text: reply },
},
});
}

src/conversation-do.ts

interface Env {}
export class ConversationState {
constructor(private state: DurableObjectState, private env: Env) {}
async fetch(req: Request): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === "/get") {
const data = await this.state.storage.get("data");
return Response.json(data ?? null);
}
if (url.pathname === "/set" && req.method === "POST") {
const body = await req.json();
await this.state.storage.put("data", body);
return new Response("ok");
}
if (url.pathname === "/cleanup" && req.method === "POST") {
await this.state.storage.deleteAll();
return new Response("ok");
}
return new Response("not found", { status: 404 });
}
}

.env.example(仅供本地 dev)

Terminal window
# 生产用 wrangler secret put 设置,不存这里
HASHEE_AGENT_ID=
HASHEE_AGENT_TOKEN=
HASHEE_WEBHOOK_SECRET=
HASHEE_X25519_PRIVATE_BASE64=
HASHEE_ED25519_PRIVATE_BASE64=

部署步骤

Terminal window
# 1. 装 wrangler + 登录
npm install
npx wrangler login
# 2. 创建 Queue
npx wrangler queues create agent-jobs
# 3. 设 secret
npx wrangler secret put HASHEE_AGENT_ID
npx wrangler secret put HASHEE_AGENT_TOKEN
npx wrangler secret put HASHEE_WEBHOOK_SECRET
npx wrangler secret put HASHEE_X25519_PRIVATE_BASE64
npx wrangler secret put HASHEE_ED25519_PRIVATE_BASE64
# 4. 部署
npx wrangler deploy
# 输出 URL: https://my-hashee-agent.<subdomain>.workers.dev

注册 webhook

在 Hashee app 跟系统 Agent 说:

把 DemoBot 的连接模式改成 webhook
URL: https://my-hashee-agent.<subdomain>.workers.dev/hashee/webhook
secret: <你前面 wrangler secret 设的 HASHEE_WEBHOOK_SECRET 值>

监控

Terminal window
npx wrangler tail my-hashee-agent

相关