HEL-832: R3 real-memory integration verification report, FTS probe evidence, handoff and follow-up construction cards (docs only)

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Chief Engineer
2026-10-24 12:46:53 +08:00
co-authored by multica-agent
parent c285a0fd07
commit ee4459433c
4 changed files with 300 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
# R3 接续施工卡正文(HEL-832 产出,供总管直接建卡复制)
> 三张卡按依赖排序:① 后台施工(施工员)→ ② 回忆页接线(视觉总监)→ ③ 统一审核+独立部署(总工)。
> 共同头部(每张卡须含):基准 v1.10(59 项);读取提交 `c285a0fd07e9d20e4fd4984942f3ae2fb8cbbd0c`;开工前必读仓库 `AGENTS.md`、`docs/requirements/README.md`、`current.md`、`coverage.md`、`changes.md`、`sources/integration-plan-v1.md` §3 R3、`docs/r3/REPORT-HEL-832.md`;新工作从 c285a0f 拉独立任务分支,不覆盖原作者工作;需求变更一律无。交付前跑 `python3 scripts/check_requirements.py --base-ref origin/main` 与 `--base-ref origin/agent/agent/de8874a3803b`(有交接 JSON 加 `--handoff`)。
---
## 卡① R3 记忆后台施工(施工员)
**标题建议**:【🌸·797·P50·施工】R3 真实记忆后台:成功对话写入、中文 FTS 接入对话、回忆页接口
**输入**:`docs/r3/REPORT-HEL-832.md` §4(改动边界逐条)、§5(DTO 合同)、§6(测试矩阵 T1–T8)、`docs/r3/fts-probe-HEL-832.txt`(实测证据)。关联要求:CG-006/008/013/014/015/016/018/021、AM-001、UX-006、GOV-001–005、DEL-001。
**做(严格按报告 §4.1–§4.4,重述要点)**:
1. 成功事务内同步双写 mem_episodic(user+companion raw)+ 幂等列 turn_id(guarded ALTER + 部分唯一索引);登记 mem_job(growth_job 同款租约模式)。
2. MEMORY_GIST(low 档,零配置改动)后台作业产 gist+affect;失败重试上限 3 次后 failed,gist 保持 NULL,禁模板;重启租约恢复。
3. 新增 r3.sql:`mem_fts_zh`(trigram、外部内容表、IF NOT EXISTS)+ 一次性幂等重建;add_episodic 双写索引。
4. 检索:服务端提词(剥引号/标点)→ 引号短语 → 三元组 OR 二遍 → <3 字基表 LIKE 回退;top-k≤4、accessibility≥0.05、排除近 20 轮 turn_id、噪声规则;独立 system 小节 ≤600 字计入 context_budget;命中 reinforce + JSON 日志(turn_id、命中记忆 ID、rank、mode)。
5. `GET /api/memories`、`GET /api/memories/{id}` 按 §5 DTO(title 只用已压缩 gist;lifeIds/messageIds 服务端裁剪;404 中性);`life_control` 键 `memory_search`(默认 on)与 `last_mem_decay`(每小时 decay 批)。
6. 诚实文案:系统提示「长期记忆…」句与 `/api/health` capabilities.memory 按实际生效更新。
7. 测试 T1–T8 全过(ControlledLLM、隔离库、clock 注入)。
**不做**:向量/语义/关系层、identity promotion、记忆搜索/编辑端点、生活事件入记忆、前端任何改动、模型/温度/路由/预算/运行时/认证改动、新依赖、部署、真实模型调用(含 smoke——零真实调用)、触碰 told/telling_log/bazi_core/persona_*/growth_* 及 8300/8310/8320/8340。
**验收**:报告 §6 全矩阵绿 + 基线等价断言(/api/life、/api/messages、/api/persona 响应与 c285a0f 等价;底色 fingerprint 不变);交接 JSON(模板必填键)+ `docs/r3/` 交付说明;`docs/r3/` 下附完整 pytest/build 输出文件。
**停止条件**:任何 AM-001 断言失败、底色 fingerprint 变化、pytest 基线回归、或实现需要越界(新依赖/改路由/动旧表)→ 停止并回报具体冲突,不自行减项。
---
## 卡② 回忆页接线(视觉总监)
**标题建议**:【🌸·797·P50·接线】回忆页 episodic 层接真:/api/memories 接线与真实状态呈现
**输入**:报告 §5 接口/DTO/分页/可见性合同;已认可的回忆页设计(HEL-815 页面稿在正式界面的既有接入);Memory 域模型与 real 校验模式(`web/src/adapters/real.ts:35-38` serverLife 先例)。关联要求:AM-001、UX-006、VIS 系(呈现由视觉总监定稿)、GOV-005(本轮界面代码由视觉总监亲自写)。
**做**:
1. 真实模式回忆 tab 数据源换 `GET /api/memories`(游标分页、重开重读、失败中性态——照 LifePanel 现有 real 生命周期约定);`serverMemory` 校验器丢不合格行不修补。
2. 依视觉稿呈现 DTO 字段(title/text/group/source/lifeIds/messageIds 关联跳转);「她的成长」组数据仍来自 /api/persona,合流与否由视觉总监定稿。
3. 空态/未接通态保持诚实(UX-006:记忆未生成前不伪装、演示路径零改动、不前端过滤替权限——只渲染服务端已裁剪数据)。
4. 记忆详情关联「那时候的聊天」沿用 messageIds→/api/messages 既有模式。
**不做**:后端任何改动、新端点、演示模式改动、系统提示/能力文案改动;不替后端裁剪补漏(发现疑似泄露即停,回报总管转卡①修)。
**验收**:真实实例(卡③部署后或本地 mock 按 §5 合同)分页/详情/关联/空态/失败态全流程截图;两主题电脑+手机呈现;演示模式逐字节不变;web build/lint/test 绿。
**停止条件**:DTO 与合同不符、或任何视图出现未分享 lifeId/未发送消息引用 → 停止回报。
---
## 卡③ 统一审核 + 独立部署 + 授权 smoke(总工)
**标题建议**:【🌸·797·P50·审核部署】R3 记忆后台统一复审、隔离实例部署与受限真实验证
**做**:
1. 对卡①卡②按本报告 §3(AM-001 五条)、§6 矩阵逐项复审核验(重跑测试、抽查中文 FTS 证据、基线等价断言)。
2. 独立实例部署:新端口(部署时复核空闲,建议候选 8350,避开 8300/8310/8320/8340)、独立数据目录、releases+current 结构、隔离全新档案;health 可验证。
3. 授权真实 smoke(上限,超限即停):gist 压缩 ≤8 次模型调用;真实对话 ≤6 轮(含 1 次隔天记忆取回:前日话题在排除近轮上下文后仍被检索注入并体现在回复);全量请求/响应证据留存(照 HEL-830 model-smoke 格式)。
4. 隐私实测:told=0 事件经 /api/memories、详情、旧链接、消息 lifeIds 全路径零泄漏;told/bazi/growth 不变。
5. 回滚方案就位(Git 回退 + 链接切回 + 数据保留);结果与访问地址报总管。
**不做**:并入 8310 真实档案(须白栖知另行确认)、修改 HEL-831 观测条件、对 8340 发任何聊天或模型请求、超上限 smoke、需求变更。
**停止条件**:检索噪声致人设漂移 → 关 memory_search 回报;gist 质量持续不可用或预算异常 → 停止回报;任何 AM-001 泄漏 → 立即下线该实例回报。
+150
View File
@@ -0,0 +1,150 @@
# R3 真实记忆接入核对报告(HEL-832)
> 性质:只读核对 + 施工收敛方案。基准 v1.10(59 项);读取提交 `c285a0fd07e9d20e4fd4984942f3ae2fb8cbbd0c`(分支 `agent/agent/a67b70ddbb63`;产品目标 `origin/agent/agent/de8874a3803b` = `db571ba`,是 c285a0f 的祖先,落后 12 个提交)。本轮未写产品代码、未启动服务、未发任何真实模型请求、未触碰 8300/8310/8320/8340 与任何用户数据。中文 FTS 结论来自隔离临时库实测(证据:`docs/r3/fts-probe-HEL-832.txt`)。
> 需求依据:R1–R7 顺序获批原话 `01a1629c-732f-7559-8417-6f2ea05c7151`「同意按这个顺序开工」;R3 范围 = `sources/integration-plan-v1.md` §3 R3(成功对话写 mem_episodic:raw + low 档 gist 压缩 + affect 粗估;chat 上下文加 FTS top-k;reinforce/decay 定期跑批;回忆页 episodic 层接真;不做向量/语义/关系层整合)。AM-001 原话 `01a15f42-2781-700e-b75a-3ae4b023f46c`「整套认可,1选等她讲过才显示…」。CG-013/014/015 底色/诠释/成长分离依据 `01a15772`+`01a15774`「同意你的推荐」。本报告的工程取舍均为建议,不构成用户新决定。
## 1. 现状核对(c285a0f,已有能力 vs 占位)
### 1.1 存储与 schema
| 事实 | 证据 | 判定 |
|---|---|---|
| `mem_episodic`(raw/gist/affect/salience/accessibility/recall_count/consolidated/withheld/source)、`mem_semantic`、`mem_relational` 表已建 | `src/cybergril/memory/schema.sql:92-137` | 表可用,但全库无写入方(见 1.2) |
| `Store.add_episodic`/`decay_accessibility`/`reinforce`/`search_fts` 已实现(同步 FTS 维护、指数衰减 floor=0.03、间隔效应) | `src/cybergril/memory/store.py:102-171` | API 可复用;`search_fts` 直传用户原文给 MATCH,不安全(§2.2) |
| FTS 索引 `mem_fts` 用 `tokenize='unicode61 remove_diacritics 2'`,外部内容表挂 mem_episodic | `schema.sql:176-180` | **对中文不可用**(§2.1);结构可照搬换 trigram |
| 向量表 mem_vec 仅注释 | `schema.sql:182-186` | 与「不上向量」一致,保持 |
| 聊天表 `chat_message(id,ts,role,text,turn_id,status)`,`UNIQUE(turn_id,role)`,status ∈ pending/sent/failed/cancelled | `schema.sql:193-201` | R3 幂等键可挂 turn_id(建议加列,§4.1) |
| R2 作业表模式:`growth_job(source,ref_id,state,owner,lease_until)`、`sharing_attempt`、`life_run` 租约恢复 | `src/cybergril/memory/r2.sql:2-49` | R3 记忆作业照此模式新建,不动旧表 |
**结论:真实记忆未实现。** `grep` 全 `src/`:`add_episodic`/`search_fts`/`decay_accessibility`/`reinforce` 除 `memory/store.py` 自身定义外**零调用方**;`tests/test_store.py:41-53` 只测插入,从不测中文检索。近 20 轮上下文(`api/app.py:468-471`)只是聊天记录,系统提示自己声明「长期记忆…仍未接通。近20轮不是长期记忆」(`app.py:478`)。**不能把已有表或近 20 轮上下文当成真实记忆已实现。**
### 1.2 聊天成功/取消/断连/接收确认(R3 写入的触发面,全部已验证)
| 路径 | 行为 | 证据 |
|---|---|---|
| 成功 | 同一事务:INSERT companion 'sent' + UPDATE user 'sent' + `sharing.prepare` 回执 | `api/app.py:553-565` |
| 取消(新轮次/主动取消) | `active["cancel"].set()`;generate 提前 return 不写;finally 把 user 置 cancelled | `app.py:439-443, 549-550, 583-588` |
| 断连 | `await request.is_disconnected()` 即返回,不落 sent | `app.py:532, 549` |
| 失败 | 异常 → status='failed',user 行置 failed | `app.py:566-570, 583-588` |
| 重启 | 启动时 pending→cancelled,epoch+1 持久 | `app.py:196-201` |
| 重放 | 同 turn_id 同文重发 → 直接回放已存 sent 文本,不再调模型 | `app.py:444-456` |
| 接收确认 | `POST /api/chat/received` → `Sharing.acknowledge`:status='sent' + token/text_hash 常量时间比对 + 有效期内 | `app.py:406-410`, `life/sharing.py:53-70` |
**R3 写入点即「成功事务」`app.py:553-560`:取消/失败/断连天然不触发,重放被 UNIQUE 挡住。**
### 1.3 life/told 三条件(AM-001 现行执行)
披露 = **成功轮次 AND 完整回执 AND 事后判定**(`life/sharing.py:1-4` 文档串;提交条件 `sharing.py:72-91`:m.status='sent' AND a.ack_at IS NOT NULL AND a.state='judged' 才置 told=1 并写 telling_log)。`PublicView.allows`(told=1 才放行,`life/public.py:9-12`);`/api/life` 只回 told=1(`app.py:386`);详情 404 中性文案(`app.py:394-404`);`public.message` 的 lifeIds 服务端裁剪(`public.py:26-31`);`chat_source_allowed`:回复若以任何未公开候选为条件,不得作为公开成长来源(`public.py:33-42`)。**R3 必须原样继承,不改 told、不自行确认接收(§3)。**
### 1.4 预算与路由
- `Scene.MEMORY_GIST` 已定义且路由 low(`think/scenes.py:58-59`、`config/models.yaml:58-59`),「量最大,必须低档 + 批处理」;`structured()`(`life/model.py:50-94`)走既有路由 + 预算预留(`PersistentBudget` 持久 token_usage,`model.py:13-47`),失败不落模板(LifeRuntime 同款约定 `runtime.py:180`)。**R3 的 gist 作业直接复用,无需改 models.yaml。**
- 主对话上下文预算按档位裁剪(HIGH=4000,`scenes.py:119-124`;裁剪逻辑 `app.py:507-520`)——记忆注入块必须计入该预算。
- 检索本身(SQLite)零模型调用,不占预算。
### 1.5 growth(成长层,R3 不得触碰)
`Persona.grow` 从 told=1 生活事件或 `chat_source_allowed` 聊天消息提炼 `growth_event`(`life/persona.py:136-173`;调度 `runtime.py:131-143`),出生底色经 `fingerprint` 校验且只在 `persona_interp` 层(`persona.py:72-99`)。**R3 的 mem 表与 growth_event 互不引用、互不写;底色/人格/初始习惯/关系零改写。**
### 1.6 回忆页现状
真实模式 `memories: []`(`web/src/adapters/real.ts:102`);回忆 tab 硬编码占位「暂无可核实的回忆 / 长期记忆尚未接通;文字聊天记录独立保留。」(`web/src/life/LifePanel.tsx:153-157`,real 视图 `LifePanel.tsx:32`)。域名模型 `Memory {id,title,text,group,source,lifeIds}` 已存在(`web/src/domain/model.ts:32-39`),演示模式四分组 `MEMORY_GROUPS`(`LifePanel.tsx:11`)。**接线合同见 §5,实际界面由视觉总监负责。**
## 2. 中文 FTS 实际能力(隔离实测,`docs/r3/fts-probe-HEL-832.txt`)
### 2.1 能力结论
1. **现行 unicode61 中文全 0 命中**(难过/下雨/雨声/杭州/我 → 0;仅英文命中)。表在≠中文可用。
2. **SQLite 3.37.2 自带 trigram 分词器**:≥3 字中文子串命中正常(「杭州的雨」命中;跨句 OR 三元组成功召回前日话题;BM25 rank 正常;英文默认大小写不敏感)。服务器 Python 侧 SQLite 版本需在施工卡开工时同样实测一次(本沙箱 3.37.2 已含)。
3. **trigram 表上 <3 字符查询(MATCH 和 LIKE 都)静默 0 行**——1–2 字中文词(雨声/难过/杭州)必须回退基表 `mem_episodic` LIKE(实测命中)。禁止对 trigram 表用短 LIKE。
4. **安全查询**:用户原文直传 MATCH 会抛 OperationalError(引号/连字符/NEAR 等)。唯一安全构造 = 服务端自行提词(剥离双引号/控制符/标点后按 ≥3 字片段或三元组),每个词元双引号包裹,词元间只写固定 ` OR `;任何用户原文不得出现在引号外(实测 `'…' OR b-c'` 仍报错,证明必须整表达式由服务端生成)。
### 2.2 降级与停止条件(沿用获批 R3 停止条件并具体化)
- 0 命中 → 不注入记忆段,纯近轮上下文(正常降级,不算故障)。
- 噪声判定:仅命中 1 个词元且无整短语命中视为噪声 → 同样不注入。
- 真实体验中人设漂移/答非所问持续出现 → `life_control` 键 `memory_search=off` 一键关闭检索(保留写入),日志留证并回报总管——对应获批「检索噪声导致人设漂移 → 关检索开关回报」。
## 3. AM-001 对 R3 的硬边界(重点)
1. **R3 episodic 只写聊天文本**(user/companion 双方 'sent' 消息),**绝不写 life_event.content**——未分享生活从构造上就进不了记忆层,压缩/成长层无从绕过披露门禁。生活记忆仍留待 told 门禁之后的后续批次。
2. 模型内部上下文 ≠ 对用户公开返回:检索命中的记忆注入的是模型 system 上下文(内部);她是否讲述某条记忆属输出层,已由 R2 sharing judge + 世界红线约束,R3 不新增任何披露路径。
3. `/api/memories*` 全部走 `PublicView` 同款服务端裁剪序列化器:lifeIds 用 `telling_log`+`PublicView.references` 过滤(同 `public.py:26-31`);messageIds 只回 'sent';未知/未分享不出现,404 统一中性文案(同 `app.py:396-404` 模式)。**前端过滤不是权限控制,响应前裁剪。**
4. 记忆作业**不触碰** `life_event.told`、`telling_log`、`sharing_attempt`、`bazi_core`、`persona_*`、`growth_*`;不调用任何等价「确认接收」接口;回执确认只由前端既有路径发起。
5. 旧链接/搜索:本批不新增记忆搜索端点;未来任何搜索端点必须先过同一 allows 门禁(写入本报告作为合同,防后续绕过)。
## 4. R3 后台施工范围(可直接执行;工程建议,非用户新决定)
### 4.1 写入(成功对话 → 可靠落库)
- **触发时点**:`app.py:553-560` 成功事务内,同步写两条 `mem_episodic`(role='user'/'companion',raw=原文,gist=NULL 待压缩,salience 默认 0.5 数值占位,affect=NULL 待粗估,source='chat'),并同事务登记压缩作业 `mem_job(source='chat_message', ref_id=message_id, …)`。同步写仅本地 SQLite 微秒级,不阻塞主对话;**取消/失败/断连不写**(不走该事务);**重放幂等**:新增守卫列 `turn_id TEXT`(guarded ALTER,照 `store.py:44-51` life_event 先例)+ 部分唯一索引 `UNIQUE(turn_id, role) WHERE turn_id IS NOT NULL`。
- **gist/affect 粗估作业**:后台循环(挂在现有 LifeRuntime.tick 的一个有界小节,照 growth_job「每次一条」模式 `runtime.py:131-143`)用 `structured()` + `Scene.MEMORY_GIST`(low 档)产出 gist(≤120 字要点)与 affect 粗估 {v,a,d};**模型失败→作业 state='failed' 可重试(上限 3 次后留 failed+reason),gist 保持 NULL,绝不填模板**(照 `runtime.py:180` 约定)。重启恢复照 `runtime.recover()`(`runtime.py:61-68`)租约过期模式。
- **affect/salience 数值启发式**(命中关键词词典给 v/a 符号,缺省 NULL/0.5):数值字段、非人格文本,不涉 CG-014 模板红线;真实粗估由 gist 作业一并提供。
### 4.2 检索(FTS top-k 接后续对话)
- **索引迁移(增量)**:新建 `mem_fts_zh USING fts5(raw, gist, content='mem_episodic', content_rowid='id', tokenize='trigram')`(r3.sql,`CREATE … IF NOT EXISTS`);一次性 `INSERT INTO mem_fts_zh(rowid,raw,gist) SELECT id,raw,gist FROM mem_episodic`(事务内、幂等);旧 `mem_fts` 保留不删(回滚即旧路)。`add_episodic` 改为双写两个索引直至切换验收后清理。
- **查询构造(§2 实测结论的落地)**:用户新消息 → 剥离控制符/双引号/标点取连续段;段 ≥3 字 → 整段双引号短语;全无 ≥3 字段 → 基表 LIKE 回退;短语零命中 → 第二遍按段采样三元组 OR(上限 64 词元);词元只由服务端生成,固定 ` OR ` 连接。
- **注入合同**:命中 top-k(k≤4,accessibility≥0.05,排除本轮及近 20 轮已含 turn_id;rank 过滤噪声规则 §2.2)以独立 system 小节「她确实记得的更早的事(自然使用,不逐条复述,不确定就不用)」注入,块长 ≤600 字并计入 `decision.context_budget` 裁剪(`app.py:507-520`);命中即 `reinforce(id)` 并打一行 JSON 日志 `{"turn_id","hits":[{"id","rank"}],"mode":"phrase|trigram|like|none"}`(可追溯记忆 ID 与来源)。
- **诚实文案**:`app.py:478` 系统提示改为如实反映「长期记忆已接通、主动消息/语音/视觉/天气仍未接通」;`/api/health` `capabilities.memory`(`app.py:289-294`)仅在写入+检索都真实生效后置 true。
### 4.3 衰减/强化批次
- `decay_accessibility` 每小时一批(`life_control` 键 `last_mem_decay` 记 dt,clock 参数注入可测);`reinforce` 检索命中即时调用(既有实现零改动)。
### 4.4 明确不做(本批边界)
向量/embedding(providers/embedding 闲置保留)、mem_semantic/mem_relational 整合、identity promotion、记忆搜索端点、记忆编辑/删除、生活事件入记忆、R4 主动提及记忆、任何模型/温度/路由/预算/运行时/认证改动、任何新依赖(trigram 为 SQLite 自带)。
## 5. 接口/DTO/分页/可见性合同(视觉总监接线依据)
```
GET /api/memories?limit=30&before=<id> (游标分页,同 /api/life 模式)
→ {"memories":[MemoryDTO…], "next_before": <id>|null, "epoch": n}
GET /api/memories/{id} → {"memory": MemoryDTO, "epoch": n}
未知/未分享/伪造 id 一律 404 中性文案(同 app.py:397「这一条现在打不开。」)
```
`MemoryDTO`(映射既有 `Memory` 域模型 `model.ts:32-39`):
| 字段 | 生成规则 | 可见性 |
|---|---|---|
| id | mem_episodic.id(字符串化) | — |
| title | gist(未压缩完成前不用该条)截断 ≤40 字 | 服务端生成 |
| text | gist 全文 ≤200 字 | 服务端生成 |
| group | episodic 固定「我们一起的」;「她的成长」仍来自 /api/persona(两源并存,是否合流由视觉总监定) | — |
| source | 「来自 M月D日 HH:MM 的聊天」(事件 ts + 档案时区) | — |
| lifeIds | `telling_log` × `PublicView.references` 服务端裁剪,未知/未分享不出现 | **AM-001** |
| messageIds | 仅 'sent' 消息(同 public.message 白名单思路) | **AM-001** |
| ts | 毫秒时间戳 | — |
前端规则:真实模式只渲染服务端已裁剪数据(照 LifePanel 现有 real 约定 `LifePanel.tsx:31-32` 注释);校验器照 `serverLife` 模式(`real.ts:35-38`)写 `serverMemory`,不合格行丢弃不修补;占位文案在数据到位前保持诚实(UX-006);演示路径零改动。
## 6. 隔离验收矩阵(施工卡必测;全部隔离库+注入时钟,不改系统时钟,零真实模型调用)
| # | 项 | 断言要点 |
|---|---|---|
| T1 | 中文 FTS 证据套件 | probe 全部用例转 pytest:unicode61 中文 0 命中(防回归声明)、trigram 短语/三元组命中、<3 字 LIKE 回退命中、trigram 表短 LIKE 静默 0(负例)、非法输入(引号/NEAR/OR/-/*)构造后不抛错且不越权 |
| T2 | 成功写入幂等 | 成功轮→2 行 mem_episodic(turn_id 正确);取消/失败/断连→0 行;重放→不新增;重启→不重写 |
| T3 | 作业持久与恢复 | mem_job pending 经租约恢复;模型失败→failed 可重试、gist 保持 NULL 无模板;成功→gist/affect 落库且 FTS 双索引可见 |
| T4 | 检索接线 | 旧记忆(近 20 轮窗口外+不直接复述答案)经新消息触发后出现在 ControlledLLM 收到的上下文;0 命中/噪声→无记忆段;reinforce+1 且日志含记忆 ID;记忆块计入预算裁剪 |
| T5 | 回忆页合同 | /api/memories 分页/before/epoch;DTO 逐字段;未分享 lifeId 不出现(种子 told=0 引用);详情 404 中性 |
| T6 | 隐私全路径 | /api/life、/api/messages、/api/persona 响应与 c285a0f 基线逐字节等价(除声明变更);told/telling_log/bazi_core/persona_*/growth_* 零变化(fingerprint 断言底色不变) |
| T7 | 模拟时间 | clock 注入驱动 decay 批次与租约过期(不碰系统时钟) |
| T8 | 回归 | pytest 全绿(≥3.11 环境)、web build/lint/test 全绿、前端本卡零改动 |
隔天真实体验证据(真实模型+真实档案)**不在施工卡**:由总工在独立部署卡按授权上限执行(§7),模拟时间单测与真实证据分开记录。
## 7. 部署/回滚/真实体验授权上限(交后续卡)
- 部署:独立实例(新端口、独立数据目录、全新隔离档案;不复制用户库、不动 8300/8310/8320/8340),模式照 `/opt/cybergril-*/releases/<ts> + current` 已验证路径。正式并入 8310 真实档案须白栖知另行确认。
- 回滚:代码 Git 回退 + 符号链接切回上一 release + start;schema 全增量(新列/新表/新索引),数据不删;`mem_fts` 未动,老代码回退即忽略全部 R3 表。
- 真实 smoke 上限(供总管在部署卡明确授权):gist 压缩验证 ≤8 次模型调用;真实对话验证 ≤6 轮(含 1 次隔天记忆取回);超限或触发 §2.2 停止条件即停并回报。日志留存请求/响应证据(照 HEL-830 model-smoke 记录格式)。
## 8. 关联要求与结论
关联:CG-006(同一份记忆跨设备)、CG-008(后台连续性)、CG-013/014/015(底色只读/诠释自由/成长分离——R3 零触碰)、CG-016(关系不改写过去)、CG-018(同一份记忆)、CG-021(后台任务小模型)、AM-001(§3 全文)、UX-006(诚实接线)、GOV-001–005、DEL-001(R1–R7 顺序)。需求变更:无。
**未完待续**:R3 后台施工、视觉总监回忆页接线、总工统一审核+独立部署均未开始(卡文见 `CONSTRUCTION-CARDS-HEL-832.md`,由总管建卡);R2 三天观察仍由 HEL-831 承接;R4–R7 仍主卡 HEL-797 追踪。阶段通过≠产品完成。
*HEL-832 · 总工 · 2026-10-24 · 只读核对+文档交付;未写产品代码、未部署、未真实调用模型。*
+51
View File
@@ -0,0 +1,51 @@
== current schema (unicode61), Store.search_fts ==
query '难过': 0 hits
query '下雨': 0 hits
query '雨声': 0 hits
query '杭州': 0 hits
query 'rain': 1 hits
query 'Hangzhou': 1 hits
query '我': 0 hits
== raw user input passed straight to MATCH (syntax safety) ==
query '难过"': ERROR OperationalError: unterminated string
query 'a OR b': 0 hits
query 'NEAR(a b)': 0 hits
query 'rain*': 1 hits
query '"unclosed': ERROR OperationalError: unterminated string
query 'a-b': ERROR OperationalError: no such column: b
== trigram tokenizer (SQLite 3.37.2) ==
MATCH exact substring: {'难过': '0 hits', '雨声': '0 hits', '杭州的雨': '1 hits', 'rain': '1 hits', 'hangzhou': '1 hits'}
MATCH 1-2 char substrings: {'雨': '0 hits', '下雨': '0 hits', 'ra': '0 hits'}
LIKE 1-2 char substrings: {'雨': '0 hits', '下雨': '0 hits'}
MATCH quoted phrase (safe user input): 0 hits 1 hits
case folding LIKE: ERROR OperationalError: fts5: syntax error near "%" 1 hits
== trigram + case_sensitive 0 default check with English ==
MATCH 'Rain': 1 hits
MATCH 'rain': 1 hits
MATCH 'RAIN': 1 hits
MATCH '"rain"': 1 hits
done
LIKE %雨% on trigram table: []
count LIKE %雨%: (0,)
trigram-OR expression: "我今天" OR "今天很" OR "天很难" OR "很难过" OR "难过," OR "过,因" OR ",因为" ...
hits: [('我今天很难过,因为下雨了', -8.19484240687679e-06)]
cross-sentence recall via OR-of-trigrams: [('我今天很难过,因为下雨了',)]
2-char MATCH 雨声: []
base-table LIKE %雨声%: [(1,)]
external-content trigram phrase match: [(2,)]
Traceback (most recent call last):
File "/tmp/multica-task-2302699922/opencode/fts_probe2.py", line 48, in <module>
print("unsafe input safely quoted:", c3.execute(
sqlite3.OperationalError: no such column: c
== 结论(HEL-832,隔离临时库实测,未触碰任何真实数据/服务) ==
环境:Python 3.10.12 / SQLite 3.37.2(编译含 ENABLE_FTS5)。
1. 现行 schema(unicode61)对中文检索全为 0 命中(难过/下雨/雨声/杭州/我 均 0);仅英文可命中。中文 FTS 现状=不可用。
2. 用户原文不经处理直传 MATCH 会抛 OperationalError(unterminated string / no such column)。
3. trigram 分词器(3.37.2 自带)对 ≥3 字中文子串命中正常("杭州的雨" 命中;跨句 OR 三元组召回成功;BM25 rank 正常;英文默认大小写不敏感)。
4. trigram 表上 <3 字符查询(MATCH 与 LIKE 均)静默返回 0 行——1–2 字中文词必须回退到基表 mem_episodic LIKE,绝不能走 trigram 表。
5. 外部内容表(content='mem_episodic') + trigram 可按 schema.sql:176-180 同款方式重建并正常检索。
6. 引号包裹词元后,表达式里仍出现未引号用户片段(如 ' OR b-c')依旧报错——服务端必须只输出「剥离双引号后的自提词元 + 固定 OR 连接」,绝不拼接原文。
+35
View File
@@ -0,0 +1,35 @@
{
"baseline_version": "1.10",
"read_commit": "c285a0fd07e9d20e4fd4984942f3ae2fb8cbbd0c",
"requirement_ids": ["CG-006", "CG-008", "CG-013", "CG-014", "CG-015", "CG-016", "CG-018", "CG-021", "AM-001", "UX-006", "GOV-001", "GOV-002", "GOV-003", "GOV-004", "GOV-005", "DEL-001"],
"deliverable": "R3真实记忆接入核对与施工收敛:c285a0f只读核对报告(Store/schema、聊天成功/取消/断连/回执、预算/路由、life/told三条件、growth、回忆页现状,含文件行号与『已有表≠真实记忆已实现』结论);中文FTS隔离实测(unicode61中文全0命中、trigram≥3字可用、<3字须基表LIKE回退、MATCH注入防护);AM-001五条硬边界;R3后台施工范围+接口/DTO/分页/可见性合同+隔离测试矩阵T1–T8+部署回滚与smoke上限;三张可直接建卡的接续卡正文。交付docs/r3/REPORT-HEL-832.md、CONSTRUCTION-CARDS-HEL-832.md、fts-probe-HEL-832.txt与本交接。",
"acceptance": [
"报告关键结论均有源码 文件:行号 或来源原话依据(R1–R7顺序01a1629c、AM-001 01a15f42、CG-013/014/015 01a15772+01a15774),未知与工程建议明确标注,不冒充用户决定",
"中文FTS结论来自隔离临时库实测并留存探针输出:现行unicode61对中文全0命中;trigram≥3字命中且跨句三元组召回成功;<3字查询须基表LIKE回退(trigram表短查询静默0行);用户原文不得直传MATCH,表达式只由服务端提词+引号+固定OR构成",
"真实记忆现状判定清楚:add_episodic/search_fts/decay/reinforce全库零调用方、近20轮仅聊天记录、系统提示自述长期记忆未接通、回忆页真实模式为诚实占位——不把已有表或近轮上下文记成记忆已实现",
"R3施工范围可直接派工:写入触发时点/幂等键(turn_id,role部分唯一索引)/取消失败不写/重启租约恢复/模型失败不填模板;检索注入计入context_budget并记命中ID日志;DTO合同lifelds/messageIds服务端裁剪;T1–T8隔离测试矩阵与停止条件齐备",
"AM-001边界成文:R3 episodic只写聊天文本绝不写life_event.content;模型内部上下文与对用户返回分离;新端点全走PublicView同款裁剪;不触碰told/telling_log/bazi_core/persona_*/growth_*;不新增确认接收路径",
"docs/r3/三文件+交接提交独立任务分支(仅文档变更),需求结构检查对origin/main与origin/agent/agent/de8874a3803b均通过"
],
"deferred": [
"R3后台施工、回忆页接线、统一审核+独立部署均未开始,由总管按docs/r3/CONSTRUCTION-CARDS-HEL-832.md建卡派工(施工员→视觉总监→总工)",
"真实smoke与隔天记忆体验证据未做(本卡零真实模型调用);部署卡按上限执行:gist≤8次调用、真实对话≤6轮,触发噪声漂移即关memory_search回报",
"正式并入8310真实档案须白栖知另行确认;向量/语义/关系层整合、identity promotion、记忆搜索/编辑仍不做(获批R3边界)",
"R2三天观察仍由HEL-831承接;R4主动联系、R5语音、R6城市天气/素材/完整备份、R7授权观察及真实视频/口型仍由主卡HEL-797追踪",
"服务器侧SQLite版本与trigram可用性需在施工卡开工时同款探针复核一次;本沙箱Python 3.10无法跑全量pytest(仓库要求≥3.11),全量回归在施工卡以≥3.11环境执行"
],
"requirement_changes": [],
"verification": [
"只读核对基于 c285a0fd07e9d20e4fd4984942f3ae2fb8cbbd0c(分支agent/agent/a67b70ddbb63);产品目标origin/agent/agent/de8874a3803b=db571ba为其祖先、落后12提交,已核实",
"中文FTS隔离实测:docs/r3/fts-probe-HEL-832.txt(Python3.10.12/SQLite3.37.2含FTS5;临时库,未触任何真实数据与服务)",
"代码事实核对:memory/store.py:102-171零外部调用方;api/app.py:553-565成功事务、583-588失败/取消落status、444-456重放、196-201重启、406-410回执;life/sharing.py:72-91三条件;life/public.py:9-42裁剪;think/scenes.py:58-59+models.yaml:58-59 MEMORY_GIST已路由low;web/src/adapters/real.ts:102与LifePanel.tsx:153-157回忆页占位",
"本沙箱测试:tests/test_store.py+test_router.py 28 passed(3.13秒);全量套件因沙箱Python3.10缺asyncio.timeout(仓库requires-python>=3.11)7项API测试环境性失败,非代码回归;HEL-830曾在Python3.12.14记录194 passed(handoff-HEL-830.json),本卡仅文档变更不改代码路径",
"python3 scripts/check_requirements.py --base-ref origin/main 与 --base-ref origin/agent/agent/de8874a3803b(含--handoff本文件)均通过:59项结构完整",
"未写产品代码、未启动服务、未发真实模型请求、未访问.11/.12服务、未读取真实用户正文、未复制用户库、未触碰8300/8310/8320/8340/共享语音/4080/系统时钟"
],
"product_target": "origin/agent/agent/de8874a3803b",
"product_target_commit": "db571ba204c8372989f96e1ed9b5cd00d3a306c7",
"branch": "agent/agent/6f98c609c73f",
"deployed": false,
"real_model_calls": 0
}