04 · 技术架构与当前代码基线
基线时间:2026-10-27 00:45 | 只读核实,未改任何代码
金额/配额口径:Cloudflare Workers 免费版 KV = 每天 1000 次 list、1000 次写、10 万次读(账户级,北京时间 08:00 重置)
1. 仓库与工作区
| 项 |
值 |
证据 |
| 本地目录 |
/workspace/chat |
【实测】 |
| 远程 |
github.com/beibbloveme-crypto/yuai |
git remote -v【代码】 |
| 分支 |
main(当前)、fix/p0-memory-consistency(历史分支) |
git branch -a【实测】 |
| HEAD |
5666a72(2026-10-25 20:36)fix(kv): 修复每日 list 配额烧穿导致聊天 500 |
git log【实测】 |
| 工作区 |
干净(无已修改文件);未跟踪:handoff-public.md、小手机-完整交付文档.md、2 张 PNG |
git status【实测】 |
| 提交总数 |
60(10-05 起) |
git rev-list --count HEAD【实测】 |
| 远端同步 |
本地 main 已包含 10-25 的提交;远端实时状态未验证(沙箱访问 GitHub 受限) |
【未验证】 |
2. 文件与职责
| 文件 |
行数 / 大小 |
角色 |
纪律 |
index.html |
12,509 行 / 230,966 B |
前端唯一真源 |
改完必须 node build.js |
worker.js |
2,801 行 / 152,446 B |
后端唯一真源 |
同上;serveFrontend 由构建期注入,源码里未定义 |
build.js |
58 行 |
构建脚本 |
worker.js + base64(index.html) + serveFrontend() → 产物 |
cat-chat-all-in-one.js |
2,822 行 / 460,954 B |
部署产物 |
禁止手工编辑 |
wrangler.toml |
— |
v2 配置(zhai-v2 + KV 0a4c…) |
含真实账号/KV ID,勿外传 |
wrangler.v1.toml |
— |
v1 配置(zhai + KV ec1a…) |
同上 |
deploy-web/server.js |
484 行 |
国内版平台垫片 + 快照管理 |
只做适配,不做业务 |
deploy-web/kv-sqlite.js |
155 行 |
env.CHAT 的 SQLite 实现 |
对齐 KV 六方法 |
deploy-web/worker.mjs |
2,822 行 / 460,954 B |
后端部署副本 |
与 bundle 逐字节相同(手工同步) |
deploy-web/start-domestic.sh |
— |
看门狗(退出 3 秒重启 + pidfile) |
— |
tests/*.mjs |
10 个文件 |
契约测试 |
全部 Mock,非 E2E |
tools/*.mjs |
3 个 |
搬迁/审计脚本 |
一次性工具 |
3. 构建链
index.html ─┐
├─ node build.js ─► cat-chat-all-in-one.js ─┐
worker.js ──┘ ├─ wrangler deploy (v2) → zhai-v2
└─ wrangler deploy --config wrangler.v1.toml → zhai
index.html ──► deploy-web/server.js 运行时读取(注入令牌/WEB_MODE/sharecode 垫片)
worker.js ──► cp 成 deploy-web/worker.mjs ──► Node 动态 import
已知脆弱点:
- wrangler deploy --config wrangler.v1.toml 不会自动构建 → 忘记 node build.js 就部署旧产物;
- deploy-web/worker.mjs 没有自动同步步骤(当前一致,靠人工);
- worker.js 单独 import 会因 serveFrontend 未定义在 GET / 时报错。
4. 运行时拓扑
┌──────────────── 浏览器(手机) ────────────────┐
│ index.html(单页,内联 CSS/JS) │
│ localStorage:设置/外观/模型 Key/worker_url │
│ IndexedDB(cat_chat_db/kv):聊天记录、设备快照 │
└───────┬─────────────────────────────┬──────────┘
│ 聊天:CONFIG.WORKER_URL │ 记忆/档案/删除:相对路径(同源)
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ Cloudflare Worker │ │ Node 垫片(server.js) │
│ v1: zhai │ │ + SQLite(/tmp/...db) │
│ v2: zhai-v2 │ │ 复用同一 worker.js │
└────────┬──────────┘ └──────────┬───────────┘
▼ ▼
Cloudflare KV SQLite(WAL)
v1 ec1a / v2 0a4c 652 键
│
▼
大模型 API(OpenAI 兼容,Key 由前端提供 → Worker 代理)
5. 路由表(worker.js,三端共用)
| 路径 |
方法 |
鉴权 |
说明 |
/、未匹配 GET |
GET |
否 |
返回页面(注入令牌) |
| 任意路径 |
POST |
是 |
聊天主接口(handleChat) |
/manifest.webmanifest、/icon-192.png、/icon-512.png、/apple-touch-icon.png |
GET |
否 |
PWA 资源(base64 内嵌) |
/health |
GET |
否 |
{ok, service, version:"2.0"}(版本号写死) |
/init |
POST |
是 |
空实现(兼容保留) |
/backup |
GET |
是 |
旧版导出(只含 mem) |
/archive |
GET |
是 |
档案导出;支持 keyword/from/to/speaker/limit/offset |
/archive |
POST |
是 |
档案幂等导入(按 mid) |
/export/full |
GET |
是 |
全量导出(msg/hl/exp/rel/mem) |
/import/full |
POST |
是 |
全量导入(dry_run、幂等、逐条 errors/warnings、墓碑跳过) |
/highlights |
GET/POST/DELETE |
是 |
高光;写入强制引用真实 msg: |
/experiences |
GET/POST/DELETE |
是 |
经历;source_mids 必须存在 |
/relations |
GET/POST/DELETE |
是 |
关系;两端必须存在 |
/proactive |
POST |
是 |
主动消息(3 次/天、间隔 ≥3h、按 profile) |
/messages/delete |
POST |
是 |
mode=soft\|hard、force;有引用 → 409 |
/snapshots、/snapshot/now、/snapshot/rollback、/sysinfo |
GET/POST |
令牌 |
仅国内版(server.js 自处理) |
6. KV / SQLite 键位图
| 键模式 |
内容 |
说明 |
mem:<profile>:<ts>_<rand> |
长期记忆(role/content/ts/day_key/hour/period) |
聊天检索的数据源 |
mem_<ts>_<rand> |
旧格式(全局) |
仅 owner 只读兼容 |
msg:<profile>:<mid> |
结构化档案(含 deleted_at) |
导出/恢复/删除的对象 |
hl:<profile>:<hid> |
高光原话(配 mid、content_hash) |
记忆库 UI |
exp:<profile>:<eid> |
经历(active/candidate、source_mids、确定性) |
记忆库 UI |
rel:<profile>:<rid> |
关系(5 种类型) |
记忆库 UI;不进提示词 |
tomb:<profile>:<id> |
墓碑(400 天 TTL,只存编号) |
硬删防复活 |
meta_lastTs:<profile> / meta_lastTs |
最后聊天时间 |
后者为旧键 |
proactive:<profile>:<day> / proactive_<day> |
主动消息每日计数 |
26h TTL |
rl_<hash> / rl_global |
限流计数 |
2 天 TTL |
<profile> 默认 owner,校验 ^[a-z0-9_]{1,32}$。
7. 三端配置现状(值已脱敏)
| 项 |
v1 zhai |
v2 zhai-v2 |
国内版 |
| KV 绑定 |
CHAT = ec1a…7accc |
CHAT = 0a4c…ce8ad |
CHAT = SqliteKV 实例 |
| Secret |
DEEPSEEK_API_KEY(代码不读) |
AUTH_TOKEN |
无(AUTH_TOKEN 由 makeEnv 注入) |
| 令牌 |
代码默认值(源码内) |
secret(页面注入) |
代码内 cat-web-token |
| vars |
API_URL/ALLOWED_API_HOSTS/ALLOW_CUSTOM_API=0/ALLOWED_ORIGINS |
同上 + MODEL |
ALLOW_CUSTOM_API=1、STRICT_MID=1、MEM_READ_LEGACY=1 |
| 部署版本 |
a3ef2a33(10-25 22:54) |
8c6fe98a(10-25 20:38) |
进程 22:26 启动 |
| 代码一致性 |
线上 = 本地(含修复)【线上核验】 |
同左(etag 相同) |
worker.mjs = bundle 逐字节【实测】 |
8. 数据面(截至 2026-10-27 00:45)
8.1 国内版 SQLite(/tmp/cat-chat-data/chat.db,652 键)
| 家族 |
数量 |
备注 |
msg: |
320 |
307 条迁移导入 + 13 条 10-25 新聊 |
mem: |
312 |
迁移带入 |
hl: |
2 |
— |
exp: |
1 |
— |
rel: |
0 |
— |
tomb: |
0 |
硬删功能尚未在此库使用过 |
meta |
2 |
meta_lastTs 与 meta_lastTs:owner |
proactive |
1 |
— |
rl |
14 |
限流计数残留 |
被删状态:320 条中 279 条带 deleted_at(10-23 22 点 68 条、10-24 13 点 211 条),有效 41 条。
迁移来源:307 条的 mid 前缀 = local_ 217 / c_ 44 / a_ 39 / u_ 7 → 大多数来自本机历史的「云端同步」上传,不是服务端实时聊天记录。
时间跨度:2026-10-14 02:40 → 2026-10-25 22:37。
8.2 Cloudflare KV
| 库 |
内容 |
证据 |
v1 ec1a… |
抽样 4 个 msg:owner:*(local_/c_ 各 2)全部 200 命中;meta_lastTs:owner 读出 10-25 22:31 的时间戳 |
【实测】 |
v2 0a4c… |
同 4 个键全部 404;meta_lastTs 停在 10-25 18:56 |
【实测】 |
精确键数无法枚举:免费版 list 配额已耗尽(code 10048),REST keys 同样被拒。以上为抽样结论,不是全量统计。
8.3 浏览器
每一台设备各有一份 IndexedDB(cat_chat_db/kv,键 messages),含图片 base64;云端不存图。
9. 测试覆盖
| 文件 |
断言数 |
覆盖 |
test_p0_memory_isolation.mjs |
15 |
profile 隔离(含图片/主动/关键词重叠/旧数据) |
test_p0_mid_refs.mjs |
21 |
mid 回写、重试幂等、409、引用校验 |
test_p0_delete_sync.mjs |
36 |
软/硬删、409、级联、墓碑、防复活 |
test_p0_export_import.mjs |
27 |
全量导出/导入、dry_run、幂等、错误报告 |
test_p0_kv_quota.mjs |
17 |
list 缓存、降级、503、写去重 |
test_p1_memory_context.mjs |
13 |
高光/经历进 prompt、候选分离 |
test_p1_archive_search.mjs |
14 |
搜索/时间/说话方/分页/软删 |
test_phase4_v2.mjs |
32 |
阶段 4 回归(源码 + 产物各 16) |
test_kv_sqlite.mjs |
23 |
SqliteKV 六方法 |
test_ui_delete_scope.mjs |
~8 |
删除范围 UI 契约(静态断言) |
| 合计 |
约 206 |
全部为 Mock / 静态测试,非生产 E2E |
未覆盖:CORS 跨源、令牌注入、WORKER_URL 覆盖、三端联调、真实上游、浏览器 E2E、配额恢复后的行为。
10. 依赖与成本
| 项 |
说明 |
| 运行时依赖 |
无 npm 依赖(Workers 原生 + Node 内置 node:sqlite) |
| 外部服务 |
Cloudflare Workers/KV;国内托管平台(WorkBuddy);大模型 API(用户自选) |
| 成本结构 |
KV 免费额度(list/写各 1000/天);模型费用由用户 Key 承担 |
| 实测约束 |
一次聊天约 5–6 次 KV 写、约 300 次读、1–2 次 list(热缓存 0–1 次) |
11. 与原设计的偏差
| 设计 |
现状 |
偏差原因 |
| D1 或 KV |
选 KV |
用户选型 |
| 单一 Worker |
v1 + v2 + 国内版三端 |
重建 + 国内可用性需求 |
| 「外置记忆库,原文入库」 |
mem: 与 msg: 双写 |
分阶段演进叠加,未合并 |
| 「云端完整档案」 |
图片只存占位 |
未接 R2 |
| 「管理后台 + 强认证」 |
无 |
阶段 7 未开始 |
| 「人设版本管理」 |
一段文本 |
阶段 5 未开始 |