Commit Graph
11 Commits
Author SHA1 Message Date
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
张胜宇 cbcf7c7444 feat: add milvus profile projection 2026-09-11 20:45:50 +08:00
张胜宇 5e848f5690 feat: wire neo4j projection into worker 2026-09-11 20:19:34 +08:00
yuancong_0626 3f7c5ca74f merge: 同步 origin/qyqy_develop(风控扫描、知识检索、客服 Agent、协商等)
冲突处理:均为双方各自新增,按并集保留——
- .gitignore:本地 data/logs 忽略项 + 远端 .dsh-drop/
- app/core/config.py:offsite/promotion 与 risk_scan 配置项并存
- app/service/agent/bootstrap.py:场外/推介/风控/NL2SQL/知识检索 工具与 Agent 全部注册
- app/worker/__main__.py:场外邮件 Worker 接线 + runtime 关系服务/投影清理注入并存

收尾:新增 20260911_merge_risk_heads 收敛迁移双 head;按 docs/21 生成 config/jwt/dev 开发密钥。
2026-09-11 17:32:47 +08:00
yuancong_0626 fd9598efdf 袁聪的第二次提交,项目已完整 2026-09-11 16:57:47 +08:00
lzf_0626 d4836ed4df feat: 装配投影删除客户端(记忆失效/销户清理图与向量),并修复画像字段残留
一、装配 projection_cleaner
新增 app/service/projection_cleanup_service.py,并在**组装层**(app/worker/__main__.py)
注入。此前该客户端一直未装配,清理链路只记录降级(skipped_no_client),实际后果是
**销户后图里仍留着偏好关系**——投顾仍能通过关系网络看到这个人。

清理策略是"以画像为准"而不是按标识直接删边:先删掉该记忆对应的长期事实,再重建画像,
最后用对账修复让图跟着画像收敛。这样即使一条记忆影响多条派生边也能删干净,不依赖
"记得它当初投影成了什么"。Milvus 侧按 memory_uuid 删除;集合不存在(语义召回未启用)
时视为无需清理,客户端不可用则如实报告未清理,绝不伪造成功。

放在组装层而不是 runtime 内部兜底:组件内部给默认实现会把"尚未装配"这一事实悄悄盖住,
而"未注入即显式降级并留痕"是 runtime 刻意保留的语义。最初的改法写成内部兜底,被 5 个
既有单测拦下——那些测试是对的,因此改为在入口处注入。

二、顺带修复:事实消失后画像字段残留
实测:让 preference:horizon 记忆失效并清理后,user_facts 与图边都正确清除,但
fin_customer_profile.investment_horizon 仍是"长期(5年以上)"。根因是 rebuild_profile
只写"本轮有新值"的字段;事实被删后该字段没有新值,旧值就留在画像里——于是记忆已经作废,
投顾还能看到一个客户从未授权继续生效的投资期限。

改为:本轮没有对应事实时**清空**该字段;investor_type 例外——问卷是唯一权威,本轮没有
问卷记录时保持原值(该列 NOT NULL,且清空会在重测空档抹掉开户时的等级)。

三、实测
· 让 preference:horizon 失效后清理:cleaned=True,
  detail=profile_rebuilt; graph_cleaned; vector_collection_absent;
· user_facts 2→1;图边由 ['HAS_GOAL','PREFERS'] 变为 ['PREFERS'];
· 画像 investment_horizon 由"长期(5年以上)"变为 None;investor_type 保持 C2、
  risk_tags 不受影响(与 horizon 无关);
· 恢复记忆状态后重建,画像与图边均正确复原;
· ruff 通过、mypy 113 文件无错、unit+contract 447 passed、integration 29 passed。
2026-09-10 21:58:42 +08:00
张胜宇 8e36a9941d merge: preserve existing business features on new foundation 2026-09-10 17:26:52 +08:00
lzf_0626 6516ccb385 feat: 第二版——接口契约对齐 docs/05,修复静默故障与数据库基线
相对第一版 46fc976 的完整变更。组员迁移对照表见 docs/20。

一、对外契约对齐 docs/05(破坏性,共 4 处,组员需按 docs/20 调整)
1) 配置发布端点改为文档规定的复数资源名:submit→validations、
   approve→reviews(需 body decision)、activate→activations、
   rollback→rollbacks;第一版这 4 个动词式路径 docs/05 从未定义过。
2) 错误码由 8 个笼统码改为 15 个具体语义码(FORBIDDEN→AGENT_PERMISSION_DENIED、
   UNAUTHORIZED→AUTHENTICATION_REQUIRED、CONFLICT→RESOURCE_VERSION_CONFLICT、
   RESOURCE_NOT_FOUND→RUN_NOT_FOUND/SESSION_NOT_FOUND 等),
   输入类错误状态码 400→422。
3) POST /api/v1/agent-runs 与 GET /api/v1/agent-runs/{run_id} 统一为
   {data, meta} 信封(data 内字段名与语义未变)。
4) 错误响应体统一为 {error:{code,message,retryable,field_errors}, meta:{trace_id}},
   不再返回 FastAPI 默认的 {"detail": ...}。

二、数据库基线与约束
新增 39 张表的基线迁移(链根)与联合唯一键纠偏(4 张表、删 8 增 4,幂等收敛);
撤下 config_release 的双人复核 CHECK(应用层已允许自审,审核节点保留,
自审如实写入 reviewer_id);记忆 active key 生成列与唯一键;
activate 开始记录 supersedes_release_id 使版本链可追溯。
docs/00 基线未修改,未重命名或删除任何表与字段。

三、修复会静默出错或无报错的缺陷
- 跑完集成测试后平台会静默失去生效配置:清理只删自己创建的版本,却没有恢复被它
  顶成 superseded 的原生效版本,且审计一并删除因而完全无痕,表现为所有工具被拒
  但没有任何报错。已修清理逻辑并加恢复。
- Worker 单轮异常导致进程退出;记忆抽取调用方的“事务已开始”异常;
  召回缓存丢失 degraded 标记;连接时区未生效导致 created_at/updated_at 差 8 小时;
  .env 与 os.getenv 密钥来源分裂导致“没有可用的已批准模型端点”。
- 记忆信号识别漏判与跨键误命中;SSE 未带 Accept 的协商行为。

四、功能补齐
记忆链路 P1/P2/P3(抽取、受控词表、召回与缓存、生命周期级联及投影事件)、
fin_* 场内交易只读 ORM 层、agent_intent_config 状态流转并在运行期真正生效、
限流(Redis 固定窗口、故障一律放行)、游标校验、trace_id 中间件、
示例业务 Agent fund_query_demo 与一键端到端验证脚本,以及审计/指纹/迁移状态工具。

五、文档与验证
新增 docs/19(业务 Agent 接入实操)、docs/20(第一版迁移指南)与 docs/evidence 证据;
docs/01/02/06/08/09/17 同步实现现状。

验证结果:ruff 通过、mypy 103 文件无错、unit+contract 447 passed、
integration 29 passed、acceptance_check --production 7 PASS、
demo_agent_e2e 9/9 PASS(含失败关闭反证)。
2026-09-10 15:55:54 +08:00
yuancong_0626 5907fcd6d2 袁聪的第一次提交,包含nl2sql,行情数据,场外申购 2026-09-10 09:23:22 +08:00
Codex b1497fd2c6 chore: initialize project repository 2026-09-09 21:55:37 +08:00