lzf_0626
|
9490e5043b
|
记忆系统演示文档 + 修掉两处会让演示断链的真问题
## 新增:docs/演示用/记忆系统演示文档-2026-09-14.md
按"操作 → 看到什么 → 这体现什么"写,五个场景(探针看存储 / 记住一件事 / 谁能读谁读不到 /
画像版本与投影 / 停 Worker),每个场景配可复现命令与**实测输出**,另附建议顺序与时长、
六条问答话术、演示前自检清单、受控信号词表摘录。
配套两个新工具(都已实跑):
- `tools/memory_demo_chain.py`:一键走完"客户说 → 候选 → 客户确认 → 管理员批准 →
记忆与画像自动收敛",打印演示前后对比(实测 2 秒收敛)。
- `tools/memory_recall_demo.py`:同一客户换六个身份召回,当场看出"客户只读自己 /
员工要权限码+归属 / 运营读不到 / 管理员有权限码但没归属也读不到"。
## 修掉两处会让演示当场断链的问题(都是真机复现的)
1. **候选批准后画像字段不收敛**
`CustomerProfileCandidateService._promote` 只写画像快照、**不投**
`profile.rebuild_requested`,而 `user_facts` 与画像字段是 `ProfileAssemblyService`
的重建链路写的。后果:批准后记忆变 active、快照版本 +1,但**画像字段停在旧值**
(客户说"三年以内",画像里还是"十年以上"),要等一次无关的重建才收敛 ——
而"客户说完 → 批准 → 画像变了"正是演示主线。
现补一条重建事件(走事件而不是就地重建:本方法所在事务还没提交,
另开 session 看不到刚写入的记忆)。
2. **画像快照的唯一键从来没起作用,而且埋雷**
`uk_profile_snapshot_current` 建在列 `current_customer_id` 上(不是 `is_current`),
但 `ProfileAssemblyService._write_snapshot` 旧行只置 `is_current=False`(不清该列)、
新行**不写**该列(实测 13 行该列全 NULL)。
后果:只要客户**先被重建过一次**,旧 current 行仍占着 `current_customer_id=9001`,
下一次"批准画像候选"就会撞 `Duplicate entry '9001' for key
uk_profile_snapshot_current` → **整次批准 500**(本次实测踩到)。
现按唯一键的真实语义写:清旧行的该列、新行显式写客户号。
(`ProfileGenerationService` / 候选路径本来就是这么写的,只有这一处没对齐。)
## 守卫
新增 `tests/integration/test_profile_snapshot_current_invariant_mysql.py`:
按真实顺序"先重建再批准候选",断言 ① 只有一条 current 且它占着唯一键、
② 历史版本已归还该列、③ 批准不再 500、④ 批准投出了重建事件。
修复前这条用例会在第 ② 步失败。
## 验证
- `pytest tests/unit tests/contract` → 1490 passed, 2 skipped, 0 failed
- 新增集成用例通过;真机实测演示主线:候选 → verified → active → **2 秒内**
`user_facts` 与 `fin_customer_profile.investment_horizon` 都变成新值
- `mypy tools/memory_demo_chain.py tools/memory_recall_demo.py` → 0 错;ruff 全绿
## 文档
`docs/44-演示流程.md`:配套文档清单与"记忆链路"备选场景都指向新演示文档。
|
2026-09-15 00:55:44 +08:00 |
|
wangjianlong_0626
|
57677f6554
|
merge: 合并主干 qyqy_develop(PR #7 之后)并对齐两套投影实现
共同祖先 bbf623a;主干 54 个提交、118 个文件;本线 25 个文件;9 个冲突文件。
主干这次把 **ZSY 的整条投影实现合进来了(PR #7)**,而本线此前的提交正是
移植并修正同一套代码 —— 因此冲突的本质是"同一功能两份实现并存",取舍错了会把
已修好的缺陷又带回来。逐项取舍与理由见 `docs/39-主干合并对策记录.md`。
## 取舍(9 个冲突)
取本线:
- `app/infrastructure/milvus_profile_projection.py` —— 主干是 ZSY 原版,含两处必炸点:
① `customer_id` 要求 int 而本仓所有生产者都写 `str` ⇒ 每个事件必然失败;
② 不可投影的 `memory_key` 直接 raise ⇒ 一条 `constraint:` 记忆毒死整客户整批。
本线版已放宽为「接受纯数字字符串」与「跳过并留痕」。
- `memory_sync_outbox_worker.py` / `conversation_privacy.py` / `risk_questionnaire.py`
—— 代码逐行一致,仅注释与说明文字详略不同(`risk_questionnaire.py` 两边**独立做了
完全相同的修复**,都改成 re-export `app.model.profile`)。
- 两个投影测试文件 —— 本线是他那份的**超集**(4→10、4→5 例,包含他全部用例)。
两边合并:
- `app/worker/runtime.py`:`__init__` 两边各加一个参数,都要。
- `app/service/agent/implementations/customer_service.py`:import 取并集;
`COMPANY` 取主干的「奶龙基金责任有限公司」("奶龙"是本项目实际品牌名,主干多处出现),
`HOTLINE`/`SERVICE_HOURS` **取本线的修复**(主干仍是占位符 `400-XXX-XXXX`,
本线已改为引用 `customer_service_rules` 的唯一来源 —— 这是 A1 缺陷修复,
否则同一客服给客户两个不同号码)。
- `AGENTS.md`:表数/Agent 清单取主干(90/89、7 个 Agent),本线的
`-X utf8` 与两条 outbox 易错点保留,测试基线按合并后实测重算。
## 消费端只保留一套(本次最重要的一处)
合并后曾出现**两套消费者读同一个 `memory_sync_outbox`**:`__main__.py`(PR #7)
与 `runtime.consume_profile_projections()`(本线),而**两者的 neo4j handler 不同**
—— 前者用 ZSY 的 `Neo4jProfileProjection`(按客户各建私有节点),
后者用主干 `ProfileGraphProjectionService`(共享 tag 节点、只投影已确认事实)。
同一事件被谁领到结果不定,等于"同一事实在图里有两种说法",正是**方案 A 要避免的状态**。
现只保留 runtime 那一套(带 `memory_sources` 兜底、neo4j 复用主干服务),
删除 `__main__.py` 的重复接线;装配入口职责仍在该文件(注入 `relationships` /
`projection_cleaner`),Milvus 客户端由 `bootstrap` 工厂惰性构造、缺配置时显式降级。
副作用:`app/infrastructure/neo4j_profile_projection.py` 不再被生产代码引用,成为
**死代码**(本线未删,属架构师线,其单测仍在)—— 待架构师决定删或明确分工。
## 顺带修掉的 3 个继承缺陷(主干同样存在,PR #7 后未整套复跑故未发现)
1. `tools/seed_test_rbac.py` **少建 `review_t`(9004)账号** —— 两个集成测试都依赖它
("账号存在但无权限应返回 200 空集而非 404"、`PLACEHOLDER_ACCOUNTS`)。
同时把用户↔角色绑定从 `zip(..., strict=True)` 改为**显式配对表**:原写法隐含
"USERS 与 ROLES 一一对应",一加不绑角色的账号就 ValueError、整个种子跑不完
(commit 在最后,外部表现是"什么都没发生")。
2. `CustomerProfileCandidateService._write_profile_snapshot` **漏写 `current_customer_id`**
—— 该列不是生成列而是普通可空列 + 唯一键 `uk_profile_snapshot_current`,
不写则唯一键形同虚设(多个 NULL 不冲突),且旧当前版本也没清该列、补写就会撞键。
现旧值置 None、新值显式写入(与 `ProfileGenerationService._clear_current` 一致)。
3. 集成测试前置未记录 —— 13 个登录/RBAC 用例因 401 而红,实为"测试账号不存在",
跑 `seed_test_rbac.py` + `set_user_password.py` 后转绿;已在 `AGENTS.md` 记明,
避免被误判成代码缺陷。
## 文档
- 新增 `docs/39-主干合并对策记录.md`(逐文件取舍 + 理由 + 遗留)
- `docs/37` 订正一处过时说法:曾写 `current_customer_id` 无人使用且故意不映射,
实际 `app/model/profile.py` 已映射且有人使用(详见该文档 §6.2 的订正块)
- 文档编号:主干已占 29–36,本线两份文档让号至 `docs/37`、`docs/38`
## 验证(合并后实测)
- `pytest tests`(全量)→ `2 failed, 1396 passed, 2 skipped`
- `pytest tests/integration` → `102 passed, 1 skipped`(修上述 1、2 后从 15 failed 归零)
- `mypy app` → `Success: no issues found in 245 source files`
- `tools/audit_schema.py` → 89 张业务表无缺失/意外(未改动任何表结构)
- `tools/check_authoritative_docs.py` → 52 份文档无编号冲突
- `tools/check_rbac_seed_consistency.py` → 通过
那 2 个失败是既有环境项(`test_offsite_document_recognition_adapter.py` 断言请求体
中文原文而 httpx 序列化成 `\uXXXX`),与本次合并无关。
|
2026-09-12 13:14:57 +08:00 |
|
张胜宇
|
ef701c844c
|
merge: integrate ZSY customer service and profile capabilities
|
2026-09-11 22:31:51 +08:00 |
|
张胜宇
|
5777f0f86e
|
feat: include memory sources in profile sync events
|
2026-09-11 19:37:18 +08:00 |
|
张胜宇
|
27174b9ce2
|
feat: generate approved profile snapshots
|
2026-09-11 17:58:46 +08:00 |
|
张胜宇
|
a769130658
|
feat: add profile candidate review workflow
|
2026-09-11 17:47:06 +08:00 |
|