Files
CyberGril/docs/r2/HEL-827-HANDOFF.md
T

13 KiB
Raw Blame History

HEL-827 后端交付与接线契约

基准 v1.10 / 59 项。读取提交 2d97050c7e86f909590ffdec3e8bc1aa5627cade(已审 A1/A2/A3)。施工前 fetch 后四个引用与派卡一致:main 3a986726d8593e0ed2274f79db6cc5d807d2ebf0;产品目标 origin/agent/agent/de8874a3803b / db571ba204c8372989f96e1ed9b5cd00d3a306c7;hel-825-review 为读取提交;hel-826-continuation 71660155cb4b17705a6b7400820ecfb35bb11895 仅作材料来源。本分支保留全部已审前端,不合并 PR,不替换产品线。

实现与原意核对

  • memory/store.py、memory/r2.sql:只增表/列/索引;迁移的列检查在 SQLite 写锁内执行,保留有内容的 R1 life_event 与原始身份、八字、聊天、记忆。唯一 run_id 对应一条事件。
  • life/runtime.py:服务端 lifespan 内单一循环,退出取消并 join;关网页不影响它。四个本地窗口,从 character.yaml 的 day_start_hour 开始每隔4小时一个,每窗口一小时。每窗口最多一次,正常最多4条/日;失败或停机会减少,绝不补足指标。IANA 时区可配置,隔离默认 Asia/Shanghai,未替用户选城市。
  • life/model.py:复用获准 provider、scene 路由和温度;人格走 dialogue_followup(standard),生活走 life_event(low),判定走 output_guard(low),成长走 fact_extract(low)。JSON 严格校验,错误/截断/超时无模板。背景调用先保守预留用量,持久化预算;0.7警告,预计达到0.95则拒绝新背景调用。主对话保留原预算降档语义。预算日期沿用已有 BudgetGuard 的服务器日期,与城市选择无关。
  • life/sharing.py:完整成功轮次、final 的随机凭证与全文摘要确认、后置实际讲述判定三者齐全才发布;telling_log 与 told 同事务。一次判定可有4个事件,以 (event_id,message_id) 去重,消息级判定另存;包含判定原文证据、模型及时间。客户端不能指定事件集合。
  • life/public.py:统一守卫与字段白名单;life WHERE told=1 后分页,详情未知/未分享/未知状态/伪造 ID 同形404;消息只返回允许的 lifeIds,凭证与候选永不进历史响应。保留原消息正文语义:她已说出的对话文字不作为结构化生活发布证明。成长只从公开生活或已确认、判定完成且全部候选已公开的成功对话提炼;未知来源拒绝。
  • life/persona.py:bazi_core 原始事实每轮独立注入,先预留 system 空间,再缩减近轮历史;不再把六段静态解释充作人设。persona_interp 是稳定的自主理解,persona_seed 是3–5条初始习惯,growth_event 另存真实来源与实际生成时刻。诠释与习惯同事务;并发、失败重试不会造多份。PATCH关系不触及这些层。

CG-013 原话「系统级的,不能修改」及 CG-014「模型会有自己的思考和理解」同时保留:事实只读,但没有固定每次回答的标准。CG-015 的“同意推荐”对应带少量自主初始特点、后续另存成长;不能把它当作允许改八字。AM-001 的「等她讲过才显示」按服务器守卫实施,未把附件中 yield final 当作已收到,也未照抄 UNIQUE(message_id) 的多事件缺陷。UX-004 的深度整改不以六段解释或浅标签代替。本轮没有需求变更,也没有宣称完整 R2 或产品验收。

开关、运行与恢复

默认不开自动生成。只有后续获授权的隔离服务设置 CYBERGRIL_R2_ENABLED=1 才运行循环与新建档案人格生成;CYBERGRIL_LIFE_TIMEZONE 配置 IANA 名称。不改 models.yaml、认证或运行时绑定。仍为已有单进程单 worker 服务,不启用 reload/多 worker;台账抢占额外防止并发执行者重复生成,不能据此宣称多 worker 的 epoch/chat 协调已实现。

生成抢占使用 INSERT OR IGNORE 的实际插入结果,不根据 pending 判断谁能执行。owner 随机令牌和40秒 lease 配合,模型调用30秒上限。未过期 pending 不偷取;崩溃过期 pending 变 skipped/lease_expired,同窗口永不重生成。晚到旧 owner 不能写事件。正常取消将本轮 slot 置 skipped。last_scan 持久化,停机错过的窗口只记 skipped/outside_window;首次启用只记当日先前窗口,不虚构启用前的历史。恢复时只执行当前窗口,不补生成错过的几天。

台账键为 (tz,day_key,slot);时区是命名空间,切换不会撞旧键,不改旧事件日期。事件 ts 是生成完成的实际墙钟,事件 day_key 按这个时间及当时的 tz 计算;不能拿重启时间或新城市时区重写旧事件。跨区切换的作息策略及城市 UI 留 R6;重复 DST 小时按同一本地日期/slot 仅生成一次。

连续3次真实生成错误将持久暂停;仅重启不会清除暂停。预算不足不补造事件。公开 health 的 life_runtime 只给状态、时区、预算布尔值和最近 slot 的通用原因,不带内容、关键词、提示词或错误正文。人工质量判断与解除暂停由后续隔离审核负责,本卡不留后台程序长跑。过期判定 claim 终止为 failed,不猜结果;事件可在未来另一完整轮次真正讲述并确认后再发布。

给视觉总监的后端契约(本卡未改 web)

接口 字段和语义
GET /api/life?limit=50&before=<id> {events, next_before, epoch}。limit 1–100;before 1–9223372036854775806;只在已公开集合上降序分页
GET /api/life/{id} {event,epoch};未知/未分享/非法/不存在统一404 {"detail":"这一条现在打不开。"}
life event id:number,ts:unix-ms,kind,content,place,salience,source,day_key,tz,told:true,told_at,messageIds:number[]。无候选或未分享信息
GET /api/messages 原字段不变,增加裁剪后的 lifeIds:number[];从不返回确认凭证或候选 ID
GET /api/persona {state,interpretation,seeds,growth};字段形状见下;不存在伴侣404
POST /api/persona/retry 仅重试本版本新建档案的已建任务,旧档案409;失败不返回虚假模板
GET /api/health 原 capabilities.life 保持 false(本卡未接前后端);backend:{life,persona,receipt,web_connected:false} 与 life_runtime 提供后端状态,不能冒充完整接通

persona.state 为 ready/loading/failed/disabled/not_initialized。新建时请求在有界人格生成后返回;并行 GET 可看到 loading。失败后档案仍保留,可显式 retry;旧档案 never auto backfill,显示 not_initialized。interpretation 为 null 或 {id,text,pillars_hash,model,created_at,provenance};seeds 为 {id,text,created_at,provenance,interp_id}[];growth 为 {id,ts,source,ref_id,text,model,created_at}[]。公开字段作为一组裁剪;不要把旧六段解释移入“她怎么理解自己”冒充模型生成。

已有档案补建仅提供 scripts/r2_backfill_persona.py --db <单独授权数据库> --authorize-core-sha256 <核对后的指纹>。本卡仅对合成旧档案验证,不对8310/8320运行该脚本;脚本不是远端管理入口。补建写 backfilled-R2 和实际生成时间,前端需注明「后来补上的初始印象」。关系修改仍只改关系,不重生人格。

客户端完整接收确认

SSE 原 chat.delta/final/error、turn_id、epoch 语义保留。成功 final 增加可空 receipt;旧客户端忽略它时不发布任何生活事件。新客户端须先经过原 TurnGate,只有完整解析 final、全文已接收且未 abort,才确认。不要对 delta、error、取消轮次或仅历史列表发确认。

{
  "t":"chat.final", "epoch":7, "turn_id":"<UUID>",
  "utterance":{"text":"完整回复","speech_tags":[]},
  "receipt": {
    "message_id":42, "turn_id":"<同轮UUID>", "epoch":7,
    "token":"<43字符随机凭证>",
    "text_hash":"<完整回复UTF-8的SHA256>", "expires_at":1792886400000
  }
}

客户端从完整 utterance.text 自行计算 SHA256,核对 receipt.text_hash 后 POST /api/chat/received:

{"message_id":42,"turn_id":"<同轮UUID>","epoch":7,"token":"<凭证>","text_hash":"<客户端算出的SHA256>"}

只允许这五个字段;禁止 event_id/event_ids。成功 {accepted:true} 仅表示确认被接受,可能还在等判定,不等于事件已公开。判定与确认任意顺序都可,最后一个条件完成时事务提交;后台每轮最多处理一条待判定消息。格式错误422,绑定不符/伪造/过期/不存在统一404。24小时有效,重复确认幂等。凭证只在完整 final 中发送,不从 health、messages 或 persona 获取。

重放成功轮次不会重调主模型或重判:它返回原全文和原 receipt,外层 epoch 是当前服务 epoch,receipt.epoch 保留成功消息的 epoch。客户端同步新 epoch 后可重放同 turn+同用户文本,重新核验完整 final,再用 receipt 内原 epoch 合法重试确认。服务重启前已经接收的合法凭证也可在有效期内重试;这不是放宽当前 chat 请求的 epoch 检查。尚未开始的判定可重启后继续;判定中崩溃并过 lease 的消息终止判定,宁可不发布,不能拿客户端重试冒充成功判定。

这证明持有完整 final 的接收方明确确认了全文;不声称能通过网络协议证明人类实际阅读,也没有新增 LAN 鉴权体系。

隐私夹具与 R3 接缝

tests/test_r2.py 中 SECRET / add_life 可执行地植入独特私密关键词;覆盖绕过前端直查 life 列表/详情、非法ID、messages 引用、persona/growth 和 health。可导入 PublicView.allows/references/growth/chat_source_allowed 复用;服务端必须先确定允许集合,再计算分页、摘要、搜索及回忆。未知分享状态和未知来源拒绝,不能只在前端调用 publicView。

本卡未创建 /api/memories、搜索、摘要、FTS/R3 回忆接口。这些路径没有被测为通过。未来实现者须在实际端点植入同一关键词、按真实请求断言不返回它;关联回忆包含任一未公开 lifeId 时整条拒绝,不能剥引用后保留其派生正文。正文/摘要/关键词/关联字段应一起受守卫约束。

非破坏迁移与回滚

迁移可重复,未 drop/recreate 任何旧表;测试保留实际有行的 R1 schema、名字/生日/聊天/底色指纹。只有新增层与运维台账写入,PATCH关系前后全部人格层指纹不变。诠释/种子和 told/log 的写入分别注入磁盘异常,验证事务回滚无半份。

回滚兼容 R1:旧代码不读取新增表/列;可停止新版本、回到原 release,仍使用同一 DB。不得为了回滚删库或删新增资料。未来再启用 R2 时扫描台账,pending 按上述恢复规则处理,不重造已完成 slot。真实部署前备份/切 release/服务操作归总工;本卡没有执行这些操作,也没有对8310/8320迁移或补建。

真实模型核验与质量边界

model-smoke-HEL-827.json 保存一次四调用有界核验:standard 人格+习惯、low 生活、low 正向实际讲述判定、low 同词/非经历排除,共33.77秒,保守计账8491。输入全为本地合成人物,临时 DB 随脚本结束清理,未创建服务。接收确认未伪造成浏览器测试,生活样例始终 told=0。报告中 life 的 synthetic-smoke 是当次隔离调度标签;最终实现已改为事件 day_key 按完成墙钟日期计算,并用压缩时钟测试检查时区/日期。

预检曾收到网关400 No user query found in messages:修复 structured 的请求契约为 system指令+user数据后执行上述一次核验;没有改模型、温度、认证或开启调参。额外预检只用极短JSON测试定位400。此证据仅证明结构契约可用,不证明人格长期质量或生活内容验收。样例出现第三人称“它”和办公楼茶水间情境,须交总工核对世界观/叙述质量;本轮到此停止真实调用,不把四个结构检查的 true 当成内容验收。

未完成承接

视觉总监下一卡:life/persona真实字段绑定、receipt确认接线与真实浏览器重放/取消验收;继续保留已审场景/阅读效果。总工:独立审核、隔离部署、内容质量抽检,前端接线后安排真实连续3天每天1–4条、关网页仍生成的隔离长跑;压缩时钟仅证明机制。本卡无长跑服务留存。

R3跨日记忆/FTS,R4主动联系/返回同步,R5连续语音,R6城市天气/素材/完整备份,R7授权观察,以及真实视频首尾衔接/口型仍由主卡承接。现有近20轮不叫长期记忆。没有浏览器接线、Safari/Firefox/真机新验收;没有部署、合并PR、访问4080或读用户聊天/档案。