zhangshy
8914b6ed99
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
2026-09-13 16:21:35 +08:00
zhangshy
7ab49c3f7f
新增风控模块需求说明书和接口文档
2026-09-13 16:20:52 +08:00
lzf_0626
f7f43742fd
chore: 移除误提交的 .agents/skills 与 skills-lock.json(AI 助手配置,与项目无关)
...
- 这 15 个文件随 e38ece3 前端提交一起进了仓库,内容与本项目无关
- 用 git rm --cached 从索引移除(本地文件保留),并加入 .gitignore
2026-09-13 16:17:00 +08:00
张胜宇
0ee3e894e7
merge: integrate latest qyqy_develop + push docs/41
2026-09-12 17:14:36 +08:00
ZSY_docs
554d125190
docs(41): 客服 Agent 前端开发约束 v1.0(团队强制规范)
...
新增 docs/41-客服Agent前端开发约束_v1.md(720 行 / 68 个标题 / 13 节 + 3 附录):
- §2 技术栈:原生 JS + CSS + ECharts;禁 React/Vue/TS/Tailwind(与底座现有 app/static/index.html 风格一致)
- §3 目录结构:portal/<role>/<page>/ 三层命名空间
- §7 接口调用:api-client.js 封装;trace_id + JWT 自动注入;4xx 不重试
- §8 权限 RBAC:9060-9065(§T 段)映射可见性;前端不发明角色
- §11 看板专属:模块归属 portal/customer/dashboard/;4 模块按 T001 字段拆;60s 缓存 + 仅手动刷新;3 状态必齐
- §13 验收清单:通用 10 项 + 看板 9 项 + 自动化 3 项
本文件与 docs/40-前端验收清单.md 互补(约束 ≠ 验收),编号 41 不撞号
2026-09-12 17:04:37 +08:00
zhangshy
4f495c6048
修正风控前端文档重命名后的引用
2026-09-12 16:58:31 +08:00
zhangshy
6039ee3d89
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
2026-09-12 16:56:46 +08:00
zhangshy
d919b2ae5c
重命名风控前端合并文档并更新引用
2026-09-12 16:56:27 +08:00
lzf_0626
280c8e5026
docs: 清单风控章节按 17- 前端合并约束重写(15 项,含实测证据与三个坑)
2026-09-12 16:31:55 +08:00
zhangshy
a03d5e33c3
更新风控邮件通知与定时扫描文档
2026-09-12 16:30:01 +08:00
lzf_0626
6e916aab03
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
2026-09-12 16:08:39 +08:00
lzf_0626
76923d7e8b
fix(portal): 补齐写操作的必填 body(风控升级/解决、场外六个写接口)
...
- 风控:escalations 要 reason、resolutions 要 resolution(字段名不同),
且真实顺序是 先确认接收 才能升级/解决(否则 409 请先确认接收预警)
- 场外:六个写接口都必填 operator_id(防伪校验,须等于当前登录用户),
confirmations 还要 decision(中文枚举)、notifications 还要 notification_type、
recalculate 要 fund_code+application_date;门户自动带当前 user_id
- 实测:recalculate 200 code=0;不传 operator_id 必得 422(证明该字段必须)
- 清单 §3/§4 更新为实测结果并列出各写接口的必填字段表
2026-09-12 16:08:38 +08:00
ZSY_merge
e25296f30a
merge: integrate origin/qyqy_develop ( 552055ee)
2026-09-12 16:05:24 +08:00
lzf_0626
552055ee2d
fix(portal): 修风控预警列表 422(limit 上限是 5,改为不传);运营邮件改用 page/page_size;审计 limit 钳制到 1-100
...
- risk/alerts 传 limit=20 会 422 query.limit<=5,导致整张表格渲染不出来、
行内确认/升级/解决按钮随之消失 —— 这正是验收清单 3-3/3-6~3-9 看不到功能的原因
- 实测列表返回 2 条预警;处置接口有状态前置条件(409 = 当前状态不允许)
- 清单 3-3/3-6 更新为实测结果并写明 limit 上限与状态机约束
2026-09-12 16:03:22 +08:00
ZSY_merge_bot
4c651ccce1
merge: integrate latest origin/qyqy_develop (frontend acceptance checklist + portal flow wizard)
2026-09-12 15:58:08 +08:00
张胜宇
ebc3fe4cbe
feat(§T): 账户看板 + 场内模拟交易 9 端点(用户自助首版)
...
新增 §T 用户自助段(docs/05 §19 新号段 7 个 = A×40/C×7/K×4/M×4/O×3/R×4/T×9):
- T001 GET /api/v1/users/me/account/dashboard — 账户/资金/持仓/盈亏汇总
- T002 POST /api/v1/users/me/orders — 委托提交(首版 market 立即全额成交)
- T003 / T004 / T005 委托列表/详情/撤单
- T006 GET /api/v1/users/me/holdings — 持仓列表(含市值/盈亏/当日盈亏)
- T007 / T008 成交记录列表/详情
- T009 GET /api/v1/users/me/cash-ledger — 资金账本
要点(与 docs/00 §6.6 一致):
- 首版市价委托立即全额成交,不实现撮合队列/部分成交;T005 撤单首版对任何在场委托返回 ORDER_NOT_CANCELLABLE (409)
- 价格来源复用 base FundQuoteService;service 层不二次封装(满足 AGENTS 第 2 条)
- 首版风控 3 条硬性:产品可交易、客户适当性、持仓比例上限(fin_market_price 缺失或过期 → 拒绝买入)
- 数据库零修改:10 张 fin_* 表全部 docs/00 既定,本批 PR 改列类型与可空性均 0;底座实际偏差(id 无 AUTO_INCREMENT、所谓'生成列'是普通 NOT NULL)由 service _next_id / 业务派生值补偿
注册 API:9 端点均注册进 app.main;user=9001(cust)'s id 写账
权限码(tools/seed_test_rbac.py 同步登记 + CUSTOMER 全量):
9047 account:read:self
9048 trade:order:create
9049 trade:order:read
9050 trade:order:cancel
9051 holding:read:self
9052 trade:txn:read
错误码(app/core/errors.py + docs/05 §3.6 + tests/unit/core/test_errors.py DOCUMENTED 三方同步):
404 ACCOUNT_NOT_FOUND / ORDER_NOT_FOUND
409 ORDER_NOT_CANCELLABLE
422 INSUFFICIENT_FUNDS / INSUFFICIENT_HOLDING / HOLDING_RATIO_EXCEEDED / SUITABILITY_MISMATCH / PRODUCT_NOT_TRADABLE
503 FUND_QUOTE_UNAVAILABLE(可重试)
新增:app/api/controllers/trading.py / app/api/schemas/trading.py / app/service/trade_service.py / tools/seed_sim_account_demo.py / tests/unit/service/test_trade_service.py(unit×8) / tests/contract/test_trading_endpoint_contract.py(contract×11)
修改:app/main.py(挂载 controller) / app/core/errors.py(10 新异常类) / tools/seed_test_rbac.py / docs/05-接口文档.md(§19 T001-T009 + §3.6 9 新码) / tests/unit/core/test_errors.py(DOCUMENTED 同步)
门禁:pytest tests/unit tests/contract 1313 passed (+19 新增) / ruff all clean / 三道守卫全过
2026-09-12 15:50:37 +08:00
lzf_0626
49ebda4cb7
docs: 新增前端验收清单(64 项,逐项给出预期结果,区分实测与按契约)
2026-09-12 15:48:31 +08:00
lzf_0626
4c00db5636
docs: docs/32 工具索引补上功能测试台与两个新守卫,修正已过期的权限号段口径
2026-09-12 14:57:56 +08:00
zhangshy
5634fdc023
完善风控登录权限和模型能力配置
2026-09-12 14:43:05 +08:00
Windows
026fec7514
Merge remote-tracking branch 'origin/qyqy_develop' into lzl_qyqy_integration
2026-09-12 14:41:13 +08:00
Windows
9c6f839a6e
feat: add advisor demo environment bootstrap
2026-09-12 14:40:53 +08:00
lzf_0626
c200a61cd5
docs: docs/32 更新到 2026-09-12 状态,补功能性测试的触发条件与外部依赖前置
2026-09-12 14:22:34 +08:00
lzf_0626
c8cdc06e80
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
2026-09-12 14:18:06 +08:00
Windows
050eedac68
Merge remote-tracking branch 'origin/qyqy_develop' into lzl_qyqy_integration
2026-09-12 14:16:27 +08:00
Windows
54fadb3a99
Merge branch 'qyqy_develop' of http://47.106.207.27:3000/AI260626/group_fqcd_jr into lzl_qyqy_integration
...
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2026-09-12 14:15:43 +08:00
wangjianlong_0626
f7b35006ff
Merge remote-tracking branch 'origin/qyqy_develop' into nl-merge-colleague
2026-09-12 14:06:18 +08:00
wangjianlong_0626
67ba1b8eee
docs: 标明 neo4j_profile_projection 当前未被生产装配(方案 A 取舍)及启用前提
...
该适配器与主干 `ProfileGraphProjectionService` 是同一件事的两套实现,对图的建模不同:
本模块按客户各建**私有** `Preference`/`goal` 节点、数据源是 `memory_unit`;
主干服务写**共享** tag 节点、数据源是 `user_facts` 且只投影已确认事实。
## 它不是"本来就没被装配"
| 提交 | 事件 |
|---|---|
| `f167390`(ZSY) | 新建该适配器 |
| `5e848f5`(ZSY) | `feat: wire neo4j projection into worker` —— 在 `__main__.py` 装配,此后一直是**活的** |
| `4d8edb4`(主干) | PR #7 合并后接线仍在,**仍是活的** |
| `57677f6`(本次合并) | 主动摘掉那段装配 ⇒ 失去生产引用 |
`__main__.py` 在本次合并中并没有冲突(git 自动取的是带接线的主干版本),
是本线解决完冲突后**主动手工删除**的。
## 但根本原因是它与方案 A 互斥
只要落实方案 A,它就必然失去引用 —— "删 `__main__.py` 接线、保留 runtime 那套"与
"保留 `__main__.py` 骨架、把它的 neo4j handler 换成主干服务"两种做法结果相同。
所以这不是方案 A 的副作用,而是"两套图投影本来就只能活一套"。
## 改动
- `app/infrastructure/neo4j_profile_projection.py` 文件头加 `.. warning::`:
写明当前未被生产装配、为什么、其单测保护的是**模块自身契约**而非"已装配",
以及**启用前提** —— 必须先决定"图的节点模型以谁为准",只加回 `__main__.py` 装配
会重新变成两套图投影并存。
- `docs/39-主干合并对策记录.md` §3.3 补完整时间线与上述论证;§6 第 1 条改为准确表述。
- **保留文件**(实现本身完整:`MERGE` 幂等、按 `profile_version` 判重不被旧版本覆盖、
写入前经 `sanitize_customer_service_message` 脱敏),去留待架构师定:
删除 / 保留为参考实现(当前取此)/ 反过来改用它(则方案 A 需重议)。
验证:`mypy app` → 245 文件 0 错;该模块 4 个单测通过;文档守卫 53 份无编号冲突。
2026-09-12 13:23:43 +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
wangjianlong_0626
3e081addf0
docs: docs/32、docs/33 让号至 37、38(主干续占 32–36);同步引用
...
主干 `qyqy_develop` 已占用 29–36(29 登录接口 / 30 投顾迁移TODO / 31 投顾灰度 /
32 平台侧交接与联调准备 / 33–35 ZSY 底座扩展确认往来 / 36 PR7 合并记录),
本线原用的 32、33 与之重号。按"以架构师为主线"继续让号。
- `docs/32-记忆投影链路实现说明.md` → `docs/37-记忆投影链路实现说明.md`
- `docs/33-架构对齐-记忆与画像投影链路.md` → `docs/38-架构对齐-记忆与画像投影链路.md`
(两次 `git mv`,保留文件历史)
- 同步更新 `AGENTS.md` 内 2 处引用、`docs/38` 内 2 处交叉引用
- 文档内部无自引用编号;全仓已无其它 `docs/32`、`docs/33` 引用(全量搜索确认)
验证:`tools/check_authoritative_docs.py` → checked 41 documents, no number collision
2026-09-12 13:00:30 +08:00
yuancong_0626
a501060e50
merge: merge yc into qyqy_develop
2026-09-12 12:50:14 +08:00
lzf_0626
4cc0ecdea9
fix: 种子改用 UPSERT 建用户且不覆盖密码,解除投顾 FK 导致的跑不通;记录 RBAC 对齐最终状态
2026-09-12 12:22:32 +08:00
yuancong_0626
91f5373cfb
袁聪的第三次提交,API接口完善
2026-09-12 12:20:01 +08:00
lzf_0626
39f7b81200
docs: 记录本机 RBAC 对齐的实际执行结果,以及 seed_test_rbac.py 被投顾 FK 挡住这一既有破坏
2026-09-12 12:16:41 +08:00
Windows
3751891574
Merge branch 'qyqy_develop' of http://47.106.207.27:3000/AI260626/group_fqcd_jr into lzl_qyqy_integration
2026-09-12 12:10:41 +08:00
Windows
f094cbeae1
fix: harden advisor market history dual-source sync
2026-09-12 12:10:10 +08:00
lzf_0626
e0468a9d9f
docs: 记录 PR #7 合并门禁结果、两处顺手修正与权限号段冲突的来龙去脉
2026-09-12 12:10:03 +08:00
lzf_0626
441364437c
merge: 合并 ZSY 的客服 Agent 接入(访客身份、画像候选、转人工工单)—— PR #7
2026-09-12 12:05:25 +08:00
lzf_0626
c7226d0b69
回复 ZSY 的处置回执 v3:核实两处修正;更正端点编号计数(62 非 55);预检合并树并定位 test_security.py 两处密钥路径漂移
2026-09-12 12:04:18 +08:00
张胜宇
f68b052a69
fix: 端点编号去重与 milvus-lite 降为可选依赖
...
按 qyqy 在 PR #7 评审中的要求处理两项:
- docs/05-接口文档.md §19:客服画像候选的两个端点由 A034/A035 改为 A039/A040。
原编号与 qyqy 侧登录 / RBAC 只读接口(A034-A038)重复;docs 守卫脚本只校验
文档文件名编号、不校验 §19 端点编号,因此该重复会静默遗留。改后全表 55 个
端点编号唯一。
- pyproject.toml / requirements.txt:milvus-lite 由主 dependencies 挪到
[project.optional-dependencies] dev;requirements.txt 只保留说明性注释,
不再作为生效依赖。理由:它仅用于本地开发(Docker Milvus 未运行时的本地
持久化向量库),进主依赖会让生产环境多背一个包。
验证:pytest tests/unit tests/contract -> 1275 passed, 2 skipped, 0 failed;
ruff check app tests tools alembic 通过;mypy app 通过(244 个源文件)。
2026-09-12 11:53:35 +08:00
lzf_0626
ade5e0c085
回复 ZSY 的底座扩展确认 v2:逐行核验两句确认;指出 docs/05 端点编号撞车、三个新权限未随代码合并;补权限种子脚本
2026-09-12 11:46:47 +08:00
lzf_0626
244f03917b
回复 ZSY 的底座扩展确认;登记 docs/00 的一处已知偏差;更新 AGENTS.md 过期口径
...
## 对 ZSY 三件事的核实与裁决(docs/33-ZSY底座扩展确认-回复.md)
1. **访客身份**:`data_scope="public"` 我扫了全平台 26 个消费点,对未知值的行为一致
fail closed(6 处 `== "all"` 判假、1 处 `!= "all"` 会要求 customer_ids 非空否则 403、
1 处显式白名单直接拒),**安全,不用改**。但有一条硬约束必须先确认:全平台有
**72 处 `int(context.user_id)`,只有 3 处做了防御**——若访客的 sub 不是纯数字,
其中"权限被拒时要写审计"的路径会把本该 403 的情况变成 500。已要求他确认
sub 为纯十进制数字、且 visitor 分支复用同一套 `jwt.decode`(含 isdecimal 校验),
而不是另写第二个鉴权入口。
2. **客服 Agent**:`allowed_roles` 扩集合与确定性安全路由可接受;`recalls_customer_memory`
是新声明字段,已要求**默认值必须是 True**(否则会静默关掉所有既有 Agent 的记忆召回,
界面看不出来、只表现为回答变差)。品牌名(南方科技 → 奶龙基金责任有限公司)属业务
口径,已上报项目方定,不由技术侧拍板。
3. **current_customer_id**:**他的判断正确**。我独立实测 information_schema:
`EXTRA=''` 且 `GENERATION_EXPRESSION=''`,配合 baseline_generated.sql 无 GENERATED
子句、seed_profile_demo.py 的注释、profile_repository.py 用原生 SQL 显式写入,
四处一致 ⇒ `docs/00` 第 783 行"生成列"的描述是错的。处置:按真实 schema 映射成
普通可空列、**不改 docs/00**(规则 1)、**不补迁移**(DDL 本身正确,为让文档成真而加
生成列会改变既有列语义,违反规则 4)、但**必须登记这处偏差**。
另:他删了两个死代码文件(含一处硬编码 Milvus 字段名,违反 AGENTS.md §E),方向认同,
但要求他在 PR 里附上两条 git grep 的实际输出以证明"全仓唯一引用是它自己的单测"。
## 落实我在回复里承诺的两件事
- `docs/08-数据库结构审计基线.md` 新增"六、已知文档偏差",逐条登记上述偏差(含四处
证据与处置口径),并注明发现方式;
- `AGENTS.md` 环境口径新增 `MILVUS_LOCAL_URI` 的坑:配了会让健康检查与部分检索指向
本地 Milvus Lite 文件,出现"健康检查正常、实际查的是另一个库";并注明 `milvus-lite`
属本地开发依赖,应放 `optional-dependencies` 而非主依赖。
## 顺带更新 AGENTS.md 的过期口径
- 表数 68 → **89**(场内 51 + 场外/推广 17 + 投顾 21),并注明投顾那 21 张的登记文档待补;
- 测试基线从"1034 passed / 1 failed"改为实测值:ruff 干净 / mypy 228 文件 0 错 /
1207 passed 0 failed / integration 99 passed,并指向 docs/32;
- mypy 那条从"本机 181 错、双方不可比"改成可操作的判据:先对版本,根因是某一侧虚拟环境
没满足 pyproject 的 sqlalchemy>=2.0,<3 / mypy>=1.14,<2;并写明**不要装 sqlalchemy2-stubs**
(那是给 1.4 的,2.0 自带 py.typed,装了反而按 1.4 API 报一批新错)。
文档守卫:42 份无编号冲突。
2026-09-12 11:27:28 +08:00
wangjianlong_0626
57f56bbcbe
fix(profile): 修 profile_snapshots 重复定义(会打挂 Worker);消费端兜底 memory_sources
...
## 1. 独立缺陷:`profile_snapshots` 被两个 ORM 类重复映射
排查 `memory_sync_outbox` 中 `target_store='neo4j'` 那行 `last_error='InvalidRequestError'`
时发现,根因不在图库,而在模型层:
- `app/model/profile.py` → `ProfileSnapshot` 映射 `profile_snapshots`
- `app/model/risk_questionnaire.py` → **另一个** `ProfileSnapshot` 也映射 `profile_snapshots`
SQLAlchemy 不允许两个类映射同一张表。实测:
| 场景 | 结果 |
|---|---|
| 单独导入 `app.main` / `app.worker.runtime` | 正常 |
| 单独导入 `profile_assembly_service` / `risk_questionnaire_service` | 正常 |
| **两者同时导入** | `InvalidRequestError: Table 'profile_snapshots' is already defined` |
Worker 在同一进程里既要处理 `profile.rebuild_requested`(走 `app.model.profile`),
又要处理投顾风险问卷(走 `risk_questionnaire.py`)——所以这是**会打挂 Worker 的缺陷**,
不是理论风险。
修法:`app/model/risk_questionnaire.py` 不再重复定义,改为从 `app.model.profile`
转出(re-export),既有 4 处 `from app.model.risk_questionnaire import ProfileSnapshot`
无需改动。原定义多映射的 `current_customer_id` 经全仓核查无人使用,故不保留
(`app.model.profile` 明确注明该列由数据库维护、故意不映射)。
## 2. 消费端兜底 `memory_sources`
`memory_sources` 是本线新增的投影入参,而投顾线两处生产者的 payload
(`{customer_id, profile_uuid, version, profile}`)没有这个键,原样会导致它们每次画像
变更都 `memory_sources is invalid` → 重试至死信。
新增 `WorkerRuntime._with_memory_sources()`:**键缺失或为 None** 时回退查询该客户
`memory_unit` 中 `status='active'` 的记忆,并记 warning(使"谁没提供"保持可见)。
语义成立:长期记忆是**客户级**而非画像版本级的,每条记忆自带 `version`,
适配器按 `memory_uuid + version` 幂等,故"用的是哪一版"仍确定。
**兜底不掩盖真错误**:键**存在但格式不对**时**不兜底**,原样交给适配器失败关闭。
实现上用键存在性判断而非 `isinstance`——后者会把"缺失"与"格式错"混为一谈,
那是初版实现里的一个真 bug,被新测试抓出后修正。
## 3. 补上此前欠缺的消费端全路径验证
此前"整合验证"是直接调适配器,跳过了 outbox 的领取→分派→状态更新。
- **失败分支**(Milvus 断开时实测):行被领取、按 target_store 分派、异常被捕获、
`status`/`retry_count`/`last_error`/`next_retry_at` 正确落库。
- **成功分支**(注入替身向量客户端):outbox 行 → `processed`、`processed_at` 已写;
不可投影的 `constraint:` 被跳过(只写 1 行);维度 1024;字符串客户号转 int;
**手机号脱敏生效**(`稳健型投资者,手机号 [手机号已隐藏] 请勿外泄`)。
- **兜底实证**:历史行 `id=5`(payload 无 `memory_sources`)经兜底后成功投递为 `processed`。
- **修复实证**:`id=6` 的 `last_error` 从 `InvalidRequestError` 变为
`RecoverableAgentError`(图库不可用)——证明重复定义缺陷确已消除,剩下的是环境问题。
## 4. 测试与验证
- 新增 `tests/unit/worker/test_runtime_profile_projection.py`(5 用例:已提供原样透传、
缺失兜底、空记忆给空列表而非删键、无客户号不兜底、格式错不兜底)
- 全量:`2 failed, 1312 passed, 2 skipped`(2 个失败为既有环境项,非本次引入)
- mypy:`Success: no issues found in 227 source files`
- 表结构审计:89 张业务表无缺失/意外(未改动任何表结构)
- 文档守卫:41 份文档无编号冲突
## 5. 文档
`docs/32-记忆投影链路实现说明.md` 增补 §6.1(兜底)、§6.2(重复定义缺陷)、
§7.1(消费端全路径验证)并更新验证表与文件清单;
`AGENTS.md` 新增"一张表只能有一个 ORM 类"易错点、校正测试基线数字。
## 未做
未改 `docs/00` 基线、未动数据库迁移、未改投顾线生产者代码、未启动常驻 Worker。
遗留:投顾线两处生产者的 payload 仍缺 `memory_sources`(已有兜底,不再死信,
但根治应由投顾线确认);Milvus/Neo4j 容器本轮不可用(Docker Desktop 崩溃),
`id=6` 停在 failed 属环境不可用、非代码缺陷。
2026-09-12 11:24:50 +08:00
lzf_0626
39f261da14
新增 docs/32-平台侧交接与联调准备.md
...
把当前状态整理成可交接的一份:分支与门禁、三条线合进来的内容、平台侧补齐的登录与
RBAC 只读接口、修掉的真缺陷、演示账号与环境口径、新增工具索引、联调清单、悬置决策项。
其中三条是这一轮实际踩出来的,值得留档:
1. 环境数据不随代码合并(config_release / sys_role / sys_permission / Milvus schema /
advisor_product_*)—— 三条组员线各自都在这上面栽过一次;
2. 合并后必须**单独**跑 alembic upgrade head 与 tools/check_authoritative_docs.py:
三家合并里两次带进文档重号、两次库与代码版本不一致(一次"版本号跑了表没建"、
一次"表没建版本号也没跑"),两者都靠 upgrade 收敛,重号只有守卫能发现;
3. 投顾演示数据里"方案"那步配不出来,缺的是**证据来源**(source_url / document_sha256),
必须来自真实的销售适当性披露或基金合同文件;不伪造这两个字段是关键红线。
另记下环境口径:mypy 与测试数必须带环境读,NL 那边 184 错与本机 0 错的差异根因是
对方的 .venv 没满足 pyproject 的 sqlalchemy>=2.0,<3 / mypy>=1.14,<2,不是代码质量问题。
文档守卫:41 份无编号冲突。
2026-09-12 11:23:46 +08:00
张胜宇
9aaacc242f
chore: 清理违反底座规则的死代码并修正接口文档编号
...
- 删除生产死代码 app/service/knowledge_tool_service.py 与
app/infrastructure/milvus_knowledge_adapter.py:后者硬编码 Milvus 字段名,
违反 AGENTS.md §E,且仅被前者引用;生产检索链路实际走
knowledge_search_tool -> KnowledgeSearchService -> knowledge_schema 运行时探测。
- 删除上述两模块的单测,以及依赖 legacy 位置参数构造的
tests/unit/service/test_knowledge_retrieval.py。
- app/service/knowledge_retrieval_service.py 整文件回退底座版本,
移除 legacy 双构造与重复检索实现。
- docs/05-接口文档.md:客服画像候选改登记为 §8.5,恢复 §8.2 解析知识引用;
既有 §8.1-§8.4 编号全部保持,修复此前出现两个 8.3 的问题。
- app/model/profile.py:current_customer_id 改为普通可空列映射,与
alembic/baseline_generated.sql 及真实库一致;原 Computed 声明会让 ORM 把该列
从 INSERT 中排除,与「必须显式写入」的实际 schema 不符。
- 新增 docs/客服Agent接入底座扩展说明_v1.md,供集成分支评审逐项确认。
验证:pytest tests/unit tests/contract -> 1275 passed, 2 skipped, 0 failed;
ruff check app tests tools alembic 通过;mypy app 通过(244 个源文件)。
2026-09-12 11:15:24 +08:00
wangjianlong_0626
cf70ac49e9
docs: docs/29 让号给架构师线(改号 33);补实施后续说明
...
架构师线 2026-09-12 推送的 `docs/29-Agent组员登录接口使用说明.md` 占用了 29,
与本线的《架构对齐 · 记忆→画像→图》重号。按"以架构师为主线"的口径,本线让号。
- `git mv docs/29-架构对齐-记忆与画像投影链路.md docs/33-架构对齐-记忆与画像投影链路.md`
(用 mv 而非增删,保留文件历史)
- 该文档开头补"⚠️ 后续(2026-09-12)"提示:结论**已落地实施**,指向
`docs/32-记忆投影链路实现说明.md`;避免接手人按原文的"本文只做核对与建议,
不改任何代码"误判为尚未实施。同时记录让号原因,避免编号来历不明
- 文档内部无自引用编号、全仓无 `docs/29` 引用(已全量搜索确认),故无引用需改
验证
- `tools/check_authoritative_docs.py` → checked 41 documents, no number collision
- docs 编号现为 30(投顾迁移TODO) / 31(投顾灰度) / 32(记忆投影链路) / 33(架构对齐)
说明:架构师分支上 `docs/21`、`docs/22` 各自有两份(投顾线带入的重号),
本线已在 c82e989 改号为 30/31,合并后该重号自动消除,无需额外处理。
2026-09-12 10:52:25 +08:00
wangjianlong_0626
8a0cbab636
fix(memory-projection): 订正 outbox 取值口径并接通画像投影链路
...
背景:memory_sync_outbox 这条链此前**完全没有消费者**,且生产端照 docs/00 §6.4.6
写成大写 MILVUS/NEO4J + 中文「待处理」,而消费端按 target_store 的**值**分派 handler、
且只领 status in {pending, failed} —— 两个条件都不满足,事件任何消费者都领不到、
永久滞留且不报错(唯一键 (event_uuid, target_store) 对大小写无约束,MySQL 也不报错)。
根因是代码与测试都硬编码字面量,所以测试跟着一起错、谁也没拦住。
订正
- profile_generation_service:取值改为全仓一致的小写(milvus/neo4j/upsert/pending)
- 测试改为引用常量并断言消费端契约,不再硬编码(硬编码是本次跑偏的直接原因)
- 新增契约回归测试:断言大写值分派不到 handler、会进死信,谁改回大写立刻红
- 新增 tools/normalize_memory_sync_outbox.py:订正历史脏行(默认 dry-run、幂等)
接通投影链路(此前零消费者)
- 新增 Milvus 集合 user_long_term_memory_v1 及建集合工具(幂等、不覆盖已有集合)
- 新增 MilvusProfileProjection / MilvusProfileVectorClient,并修掉移植带来的两处必炸点:
customer_id 由「必须 int」放宽为接受数字字符串(本仓所有生产者都写 str,
不放宽则每个事件必然失败);不可投影的 memory_key 由「整批 raise」改为跳过留痕
(否则一条 constraint: 记忆毒死该客户整批,而受控词表 13 个键里有 7 个不满足前缀)
- 新增 MemorySyncOutboxWorker(领取/指数退避/死信骨架保留原样)并接入 WorkerRuntime
- milvus → 向量投影;neo4j → 复用主干 ProfileGraphProjectionService(方案 A,
不引入第二套投影,避免同一事实在图中两种说法、违反主干既有的只投影已确认事实的不变式)
- 生产端从 memory_unit(status=active) 组装 memory_sources,随事件带上确定快照
- 前置移植 conversation_privacy:写外部存储前脱敏手机号/证件号/银行卡等
验证
- 新增 17 个单测;全量 2 failed, 1307 passed, 2 skipped
(2 个失败为既有环境项:断言请求体中文原文而 httpx 序列化成 \uXXXX,非本次引入)
- mypy app → 0 错(227 文件);audit_schema → 89 张业务表无缺失/意外,未改动表结构
- 真机:真实 embedding(1024 维) + 真实 Milvus 写入并回读通过
- 整合链路(测试记忆 → 生产端组装 → outbox → 消费端投递 → Milvus 回读)通过,
且 MySQL 已回滚、Milvus 无残留
文档
- 新增 docs/32-记忆投影链路实现说明.md:真实口径、根因、契约与验证证据(供接手)
- AGENTS.md:新增该易错点;新增 Windows 中文输出乱码的正确命令(-X utf8);
校正测试基线与 mypy 文件数
未做:未改 docs/00 基线、未动数据库迁移、未改投顾线代码、未启动常驻 Worker。
遗留:投顾线两处生产者的 payload 缺 memory_sources,会被消费至死信,待架构师确认是否投影。
2026-09-12 10:45:40 +08:00
张胜宇
e85989b344
merge: integrate latest qyqy_develop (auth login + RBAC read) into ZSY branch
...
Incremental merge on top of ef701c8 , which already integrated the earlier qyqy base bbf623a . qyqy_develop only added commits on top of bbf623a , so this merge is conflict-free. Incoming: account/password login (POST /api/v1/auth/tokens), RBAC read-only query API, rate limit dependency, login test console and user management tools. Additive changes in app/main.py, requirements.txt and pyproject.toml from both sides are all preserved. ZSY side capabilities (visitor tokens, customer service agent, knowledge retrieval, profile projection) are unchanged.
2026-09-12 10:37:53 +08:00
qyqy
c82e9890e4
docs: 修投顾线带入的文档重号(21/22 → 30/31);新增 docs/29 架构对齐
...
投顾线(bbf623a)带进两份文档,与既有的撞号:
21-投顾Agent迁移TODO.md → 30-(原 21 已被《风控业务第二版迁移清单》占用)
22-投顾Agent灰度与回滚操作手册.md → 31-(原 22 已被《四大Agent流程实现拆解》占用)
处理原则与 docs/27 一致:**让新来的那份让号**(既有那份被更多地方引用)。
同时修了 30 号文档里两处指向旧号的自引用。守卫恢复通过(40 份无重号)。
另含 docs/29-架构对齐-记忆与画像投影链路.md(合并 ZSY 分支前必须做的对齐,
结论见该文档:主干那条链**其实已经接好**,走的是 domain_event_outbox + profile.rebuild_requested;
ZSY 走的是另一条 memory_sync_outbox,两条平行不冲突,但需要先明确分工)。
2026-09-12 09:37:00 +08:00
qyqy
af6f689b3f
docs(29): 新增《架构对齐 · 记忆→画像→图 这条链》(ZSY_develop vs 主干)
...
合并 ZSY 分支前必须先做的对齐。核对结论与"线没接"的旧说法**不一致**:
**主干那条链其实已经接好了**——架构师在 09-10 21:52 的 d7f6ef7 就做完了"记忆→画像→图全自动触发",
走的是 `domain_event_outbox` + `profile.rebuild_requested`,**已注册进 runtime.py 的 handler 白名单**:
agent.run_completed → memory.extraction_requested → MemoryExtractionService(提升 user_facts)
→ profile.rebuild_requested → ProfileAssemblyService 重建画像 + ProfileGraphProjectionService 投影 Neo4j
而 ZSY 走的是**另一条 outbox**(`memory_sync_outbox` + 自建 MemorySyncOutboxWorker)。
⇒ 两条平行链做同一件事,但 outbox、画像生成方式、消费装配都不同。
顺带修正一处旧结论:`graph_projection_worker.py` 确实**零实例化**(架构师说的对),
但由此推论"整条链没接"不对——接的是 handler 字典,不是那个类;那个类是死代码。
文档内容:
- §1 主干链的完整链路与引入者、现场数据(user_facts 0 行 / memory_sync_outbox 2 行是**我的**生产者写的)
- §2 ZSY 的**真正增量**逐文件列表(只有 3 个是主干完全没有的:Milvus 画像投影适配器、
memory_sync_outbox worker、画像候选复核流程)
- §3 四个必须对齐的分歧(两条 outbox 的分工 / 直接快照 vs 候选复核 / 两套客服 Agent / ZSY 落后 144 提交)
- §4 三步收口方案(先只移植投影适配器,再定 outbox 分工,其余等裁决)
- §5 给架构师与张胜宇的四个问题
- §6 明确不做的事(不整体合并、不改已验链路)
文档守卫通过;本文只做核对,未改任何代码。
2026-09-12 09:36:27 +08:00
张胜宇
ef701c844c
merge: integrate ZSY customer service and profile capabilities
2026-09-11 22:31:51 +08:00