Cloudflare Worker
承载一切的运行时。全球分布式,驱动 tick 循环,提供 API 服务。
Worker 在这里的职责
单个 Worker 负责:
- 🌍 HTTP 请求 — 所有玩家 API 调用(REST + SSE)
- ⏰ Cron 触发 — 每分钟运行一次 tick
- 💾 D1 访问 — 读写世界状态
- 🧠 Vectorize 访问 — 嵌入和查询记忆
- 📡 Durable Objects — 管理 SSE 事件扇出
- 🤖 Workers AI 绑定 — 可选的本地嵌入
wrangler.toml
toml
name = "ai-world-sim"
main = "src/index.ts"
compatibility_date = "2025-01-01"
[vars]
ENVIRONMENT = "production"
TICK_INTERVAL_MINUTES = "1"
DIARY_HOUR = "22"
[[d1_databases]]
binding = "DB"
database_name = "ai-world-sim"
database_id = "<your-db-id>"
[[vectorize]]
binding = "VECTORIZE"
index_name = "ai-world-sim-memory"
[[durable_objects.bindings]]
name = "TICK_DO"
class_name = "TickBroadcaster"
[[migrations]]
tag = "v1"
new_classes = ["TickBroadcaster"]
[triggers]
crons = ["* * * * *"] # 每分钟一次入口
ts
// src/index.ts
import { app } from './router';
export { TickBroadcaster } from './tick/broadcaster';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return app.fetch(request, env, ctx);
},
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
const { runTick } = await import('./tick/engine');
ctx.waitUntil(runTick(env));
},
};Cron 触发
Worker 配置为每分钟运行一次(Cloudflare 的最小间隔)。每次调用时:
- 从 D1 读取当前世界时间
- 如果距上次 tick 已过去 1 个游戏内小时,执行新的 tick
- 否则,什么都不做
这将现实时间与游戏内时间解耦。
绑定
D1 数据库
ts
const npc = await env.DB.prepare(
'SELECT * FROM npc WHERE id = ?'
).bind('margaret').first();Vectorize 索引
ts
const results = await env.VECTORIZE.query(
npcEmbedding,
{ topK: 5, filter: { npcId: 'margaret' } }
);Durable Object
ts
const id = env.TICK_DO.idFromName('tick-broadcaster');
const stub = env.TICK_DO.get(id);
await stub.fetch('https://do/notify', {
method: 'POST',
body: JSON.stringify({ type: 'tick.completed', ... }),
});Workers AI(可选)
用于本地嵌入(免费但质量较低):
ts
const { data } = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: ['some memory text'],
});环境变量
| 变量 | 用途 |
|---|---|
ENVIRONMENT | development 或 production |
TICK_INTERVAL_MINUTES | 每个游戏内小时对应多少现实分钟(默认:1) |
DIARY_HOUR | 游戏内写日记的小时(默认:22) |
LLM_PROVIDER | openai、anthropic 或 local |
LLM_API_KEY | 通过 secret 绑定,非环境变量 |
通过以下命令设置 secret:
bash
wrangler secret put LLM_API_KEY需要了解的限制
| 限制 | 免费版 | 付费版 |
|---|---|---|
| 每次调用 Worker CPU 时间 | 30 秒 | 5 分钟 |
| Worker 内存 | 128MB | 128MB |
| D1 数据库大小 | 500MB | 10GB |
| D1 读取吞吐量 | ~500 万行/天 | 更高 |
| Vectorize 索引大小 | 3000 万向量 | 更高 |
| Cron 精度 | 1 分钟 | 1 分钟 |
Tick 必须在 < 30 秒内完成。30 个 NPC 加上有选择的 LLM 调用,通常在 5~15 秒内完成。
本地开发
bash
npm install -g wrangler
wrangler dev
wrangler d1 migrations apply ai-world-sim --local部署
bash
wrangler deploy
wrangler d1 migrations apply ai-world-sim --remote部署完成后,Cron 即刻生效,API 在 https://ai-world-sim.<your-subdomain>.workers.dev 上线。
可观测性
生产环境使用 Workers Logs(内置)+ Logpush:
ts
console.log(JSON.stringify({
tick: worldTime,
duration_ms: Date.now() - start,
npcs_processed: npcs.length,
llm_calls: llmCallCount,
}));常见模式
使用 ctx.waitUntil 处理后台任务
ts
app.post('/api/suggestions', async (c) => {
const body = await c.req.json();
await saveSuggestion(c.env.DB, body);
c.executionCtx.waitUntil(
embedAndIndex(c.env.VECTORIZE, body.text)
);
return c.json({ ok: true });
});幂等性
Tick 运行是幂等的 — 对同一个游戏内小时重复运行 tick,会产生相同的 D1 状态(除 RNG 种子外)。