架构一览

Markdown 仓库(私有)
│ git push
▼
GitHub Actions ── 抽取/切分/嵌入/判定 ──▶ Cloudflare
├─ D1(文档、切片、三元组、判定)
├─ Workers KV(索引产物)
├─ Vectorize(语义向量)
└─ Worker(查询 API)
▲
Pages + Access(只给自己看)

选这套的原因很朴素:没有一个组件需要常驻服务器。
Neo4j、OpenSPG 这类图数据库确实更强,但要一台机器一直开着,就不符合”零成本”。

四个撞过的硬限制

1. 嵌入请求有 6 万 token 的总上限

第一次跑索引,第一批就 400:

AiError: Max context reached 99392 tokens but model supports only 60000

原因是按固定 64 条一批,而中文分片每片接近 900 token,64 × 900 直接顶破。
改成按估算 token 预算动态分批,并对单条做字符上限保护;仍失败就二分重试。

2. KV 的单键上限是 25 MiB

索引产物里有一份 embeddings.jsonl,88 MB,PUT 直接 413:

content size of 87905171 bytes exceeds maximum allowed size of 27MiB

后来发现这个键运行时根本没人读——向量在 Vectorize 里,Worker 只读
docs.json 和 meta.json。删掉即可。先查清楚谁在读,再决定怎么存。

3. D1 单条语句的绑定参数有上限

想在一条 SQL 里从 300 个节点取边:

WHERE e.src IN (?,?,...) OR e.dst IN (?,?,...)   -- 600 个参数

直接 500。官方文档没写这个数,实测大约 100。改成拆两条 SQL、每批 50 个 id,
再用 id 去重合并。

4. 免费神经元额度是 10,000/天

全量重建一次约 3000 neurons(4290 片 × 约 700 token),
所以一天最多重建三次。这直接决定了:增量索引不是”优化”,是”必需”。

一个反直觉的设计:判定放在离线

一开始我想把规则引擎放进 Worker,请求时现算。算了一下:1187 篇 × 13 条规则 ≈ 1.5 万条判定,
而 Workers 免费档单请求 CPU 只有 10 ms——跑不完,也写不完。

于是把判定挪到管线里离线跑(一次约 3 秒),Worker 只做”读”。
副产品是:判定结果成了可查看的历史快照,而不是每次请求都变的临时值。

代价与收益

项 结果
月成本 0 元
全量索引耗时 约 4 分钟(Actions)
查询延迟 毫秒级(D1 / KV 命中)
规模上限 Vectorize 5M 维度,当前已用 87.8%

最后一行是唯一的隐患:知识库再涨约 14%,语义检索就会超限。
到那天的退路已经想好了——降级成纯关键词检索,功能不崩,只是没那么”聪明”。

小结

免费额度的真正价值不是”省钱”,而是逼你把架构想清楚:
不能堆组件,就得想清楚每一层到底在解决什么问题。
这套系统里,真正不可替代的只有两件事——分片策略和规则。
其余的,都是可以替换的实现细节。