回复 ZSY 的底座扩展确认 v2:逐行核验两句确认;指出 docs/05 端点编号撞车、三个新权限未随代码合并;补权限种子脚本
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
# 回复 ZSY:底座扩展确认(v2)
|
||||
|
||||
> **致**:ZSY
|
||||
> **被回复**:`致qyqy_底座扩展确认沟通文案_v2.md`
|
||||
> **我方基线**:`qyqy_develop`(本地,领先 `origin/qyqy_develop` 6 个提交)
|
||||
> **被评审的分支**:`origin/ZSY_develop` = `9aaacc2`(PR **#7**)
|
||||
> **结论**:**你要补的两句确认,我独立核验后全部成立 —— 可以照批**;
|
||||
> 但合并前有 **3 项必须处理**(§2),其中 2 项是**功能会直接不可用**,不是风格问题。
|
||||
|
||||
---
|
||||
|
||||
## 0. 先说核验方法与结论
|
||||
|
||||
我把 `origin/ZSY_develop`(`9aaacc2`)取到本地逐行读了,不是照抄你的描述:
|
||||
|
||||
| 你的主张 | 我实测到的 | 结论 |
|
||||
|---|---|---|
|
||||
| `security.py:87-91` 是那段 `sub` 校验 | 第 87 行 `subject = claims["sub"]`,88–91 行正是 `isascii`/`isdecimal`/`len ≤ 20`/`≤ 2⁶⁴−1` | **一致** |
|
||||
| 访客 `sub` 是纯数字,`[1, 9×10¹⁸]` | 第 43 行 `str(uuid4().int % 9_000_000_000_000_000_000 + 1)`,19 位上限 | **一致** |
|
||||
| 访客分支复用同一套 `decode` 与校验 | 访客判断在第 92 行,**在校验之后**;`auth.py` 只跳过 `IdentityService.resolve()`(第 52 行),没另写入口 | **一致** |
|
||||
| `contracts.py:88` 默认 `True` | 第 88 行 `recalls_customer_memory: bool = True` | **一致** |
|
||||
| `base.py:138` 短路 | 第 138 行 `if not self.definition.recalls_customer_memory or "visitor" in context.roles:` | **一致** |
|
||||
| `customer_service.py:197` 显式 `False` | 第 197 行 `recalls_customer_memory=False` | **一致** |
|
||||
|
||||
**`docs/33` §1.2 提的两个确认,答复如下:**
|
||||
|
||||
1. **`sub` 是纯数字 —— 满足,这一条关掉了。**
|
||||
2. **`visitor` 分支复用了同一个 `jwt.decode` 与同一段 `sub` 校验 —— 满足,这一条也关掉了。**
|
||||
|
||||
顺带把 72 处 `int(context.user_id)` 的结论说明白:**入口校验保证所有通过鉴权的 `sub` 都是纯十进制数**,
|
||||
所以那 69 处未防御的 `int()` 不会因访客而抛 `ValueError`,**不需要**改成统一转换函数,
|
||||
那轮跨模块收敛**不必做**。
|
||||
|
||||
**你主动提的两个「要不要改」,我的答复都是「不用改」**:
|
||||
|
||||
- **访客 `sub` 不必换号段**。数值撞上真实用户 ID 时,`auth.py:52` 已按 `visitor` 角色跳过
|
||||
`IdentityService`,拿不到任何 RBAC;`data_scope="public"` 的 26 个消费点我已逐点核过,
|
||||
对未知值一致 fail closed。而 `visitor: true` 需要私钥签名,普通用户令牌伪造不出来 —— 碰撞无实际后果。
|
||||
- **「访客不召回」保留在 `base.py` 的角色判断里,不要下放给声明位**。角色判断是**底座级安全兜底**:
|
||||
声明位漏写一个 Agent 就会静默召回访客记忆,而角色判断不会。加上 `recall_memory` 里那道
|
||||
`memory.customer_id != context.user_id` 的越界检查,是三层,比一层好。
|
||||
|
||||
---
|
||||
|
||||
## 1. 照批的部分
|
||||
|
||||
| 项 | 依据 |
|
||||
|---|---|
|
||||
| §5 删掉的两个模块 | 我按你在 PR 描述里的口径实测:`git grep -n "knowledge_tool_service"`、`git grep -n "milvus_knowledge_adapter"`(限 `app/ tests/ tools/`)**输出均为空**。**照批** |
|
||||
| `9aaacc2` 的提交拆分 | 实测 9 文件 +189/−483,与 `docs/33` §6 记录一致。**做法请保持** |
|
||||
| C3 画像 ORM 清理 | 与我方独立核实结论一致(`EXTRA=''`、`GENERATION_EXPRESSION=''`)。`docs/00` 那处偏差已按 `docs/33` §3 登记进 `docs/08` 的「已知文档偏差」,**你不用再管** |
|
||||
| `docs/05` §8.5 | **对**:没挤掉 §8.1–§8.4,`AGENTS.md`/`docs/09`/`docs/14` 的既有引用继续成立 |
|
||||
| `docs/05` §9.7 | **不撞号**:我方 §9 只到 §9.6 |
|
||||
| 元数据不可外部注入 | 比你自己注释的更强:`build_outbox_metadata()` 用 `model_copy(update=...)` **服务端覆盖**客户端提交的 `chitchat_streak`/`clarification_round`/`session_context`,不是「不接收」而是「不采信」。加分项 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 合并前必须处理的三项
|
||||
|
||||
### 2.1 ⚠️ `docs/05` §19 端点编号撞车:`A034`、`A035` 各被占用两次
|
||||
|
||||
`e85989b` 你合进了含我方登录 + RBAC 只读接口的 `qyqy_develop`,那批在 §19 已占:
|
||||
|
||||
```
|
||||
A034 | POST /api/v1/auth/tokens
|
||||
A035 | GET /api/v1/admin/roles
|
||||
A036 | GET /api/v1/admin/roles/{role_code}
|
||||
A037 | GET /api/v1/admin/roles/{role_code}/permissions
|
||||
A038 | GET /api/v1/admin/users/{user_id}/roles
|
||||
```
|
||||
|
||||
你在 `9aaacc2` 里又插了两行同名编号:
|
||||
|
||||
```
|
||||
A034 | GET /api/v1/admin/customer-profile-candidates ← 与上面重复
|
||||
A035 | POST /api/v1/admin/customer-profile-candidates/{id}/reviews ← 与上面重复
|
||||
```
|
||||
|
||||
**实测**:`9aaacc2:docs/05` 里 `A034` 出现 2 次、`A035` 出现 2 次。
|
||||
|
||||
**请改成 `A039`/`A040`**(`M003`/`M004` 不冲突,保留)。这与你 §8.5 的处置逻辑是同一件事 ——
|
||||
编号被复用比编号不够更麻烦,我们刚因两家同时占 `docs/21`、`docs/22` 让过两次号。
|
||||
|
||||
> ⚠️ 顺带告知一个**守卫盲区**:`tools/check_authoritative_docs.py` 只校验 **`docs/` 文件名编号唯一性**
|
||||
> 与权威性声明,**不校验 §19 的端点编号**。所以合并后文档守卫会**通过**,这处重复会静默遗留 ——
|
||||
> 这也是为什么我在评审里手工核了一遍。
|
||||
|
||||
### 2.2 ⚠️ 三个新权限不在任何环境脚本里:接口会全 403
|
||||
|
||||
你新增的接口要三个新权限,但我实测:
|
||||
|
||||
- **我方库**(`127.0.0.1:3306/jr`)`sys_permission` 共 38 条,
|
||||
`memory:candidate:confirm`、`memory:candidate:review`、`handover:read` **都不存在**
|
||||
(已有的只有 `handover:create`=9008、`memory:read:self`=9009、`memory:read:customer`=9010);
|
||||
- 你的分支里这三个字符串**只出现在业务代码、`docs/05`、和测试里**,
|
||||
`tools/` 与 `alembic/` 下**没有任何种子或迁移**建立它们。
|
||||
|
||||
**后果**(合并后立即生效):
|
||||
|
||||
| 接口 | 需要的权限 | 现状 |
|
||||
|---|---|---|
|
||||
| `GET /api/v1/users/me/memory-candidates` | `memory:read:self` | ✅ 已有,能通 |
|
||||
| `POST /api/v1/users/me/memory-candidates/{id}/decisions` | `memory:candidate:confirm` | ❌ **403** |
|
||||
| `GET/POST /api/v1/admin/customer-profile-candidates*` | `memory:candidate:review` | ❌ **403** |
|
||||
| `GET /api/v1/admin/customer-service/handover-tickets*` | `handover:read` | ❌ **403** |
|
||||
|
||||
fail closed 是对的(不会误放行),但**功能等于没上**。你的集成测试能过,说明**你的环境里已经手工插过** ——
|
||||
这正是 `AGENTS.md` §E 那条「`config_release` / `sys_role` / `sys_permission` / `sys_user` 是环境数据、
|
||||
不随代码合并」的**第 4 次踩坑**(前面已有三条线各踩一次)。
|
||||
|
||||
**分工建议**:这一份由**我方**出 `tools/grant_customer_service_phase2_permissions.py`
|
||||
(id 从 **9036** 起,避开我方 9001–9010 与投顾 9020–9035),**两边各跑一次**。
|
||||
你若已在自己环境手工插过,请把实际 id 报给我,我按你的号段对齐,避免两边不一致。
|
||||
|
||||
### 2.3 `milvus-lite` 放进了主 `dependencies`,与确认口径相反
|
||||
|
||||
`docs/33` §4 的口径是:
|
||||
|
||||
> 依赖新增 `milvus-lite` → **请放进 `pyproject.toml` 的 `optional-dependencies`**,
|
||||
> **不要进主 `dependencies`**。
|
||||
|
||||
实测 `9aaacc2` 的实际改动是:
|
||||
|
||||
```
|
||||
pyproject.toml → dependencies 段新增 "milvus-lite>=3.2,<4"
|
||||
requirements.txt → 新增 "milvus-lite>=3.2,<4"
|
||||
```
|
||||
|
||||
而且**你自己的接入说明**(`docs/客服Agent接入底座扩展说明_v1.md` §四 B 类表格)写的也是
|
||||
「(**可选**本地开发依赖)」—— 所以这是**放错了段**,不是理念分歧:挪到
|
||||
`[project.optional-dependencies]` 即可(`requirements.txt` 同理只留在本地开发那份里)。
|
||||
|
||||
理由不变:它只是本地开发用,进主依赖会让生产环境多背一个包,而 `MILVUS_LOCAL_URI` 那个坑正是它引入的。
|
||||
|
||||
---
|
||||
|
||||
## 3. 品牌名:新增一个事实,请连同这条一起上报
|
||||
|
||||
`docs/33` §2.4 我的口径是「需项目方拍板」。**现在给你一个补充事实,它对你有利**:
|
||||
|
||||
`app/core/customer_service_rules.py` 第 116、129 行**早就是「奶龙基金」**:
|
||||
|
||||
```
|
||||
116: "奶龙基金不会通过电话、短信或聊天索要您的验证码、密码,也不会要求您把钱转到指定账户。"
|
||||
129: "这件事需要人工为您办理。奶龙基金智能助手不能代办交易、修改资料、销户或受理投诉赔偿。"
|
||||
```
|
||||
|
||||
也就是说,改之前我方底座的现状是**自相矛盾**的:转人工/防诈骗话术说「奶龙基金」,
|
||||
而 `COMPANY` 是「南方科技」。**你这一改实际是消除了一处内部不一致**,不只是换品牌。
|
||||
|
||||
所以请在上报时把这层理由写进去(比单说「改个常量」更容易通过)。
|
||||
|
||||
两个附带提醒:
|
||||
|
||||
- **建议把品牌名单独一个提交**,便于项目方拍板后单独保留或回滚,不要混在功能提交里;
|
||||
- 我方 `tools/chat_console.py` 的标题栏现在也还是「南方科技」(第 48、81 行)。
|
||||
若最终定「奶龙」,**我方自己也要改**,这条算在我账上,不占你时间。
|
||||
|
||||
---
|
||||
|
||||
## 4. 合并预演结果:干净,可以合
|
||||
|
||||
我按 `docs/32-平台侧交接与联调准备.md` §5 的口径做了预演:
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| `git merge-tree --write-tree qyqy_develop 9aaacc2` | **无冲突**(返回单一 tree,无 conflict 段) |
|
||||
| 与你这 89 个文件的交集 | **我方领先的 6 个提交与 89 个文件零重叠**(`Compare-Object` 结果为空) |
|
||||
| PR #7 元数据(Gitea API 实测) | `state=open`、`mergeable=True`、`base=qyqy_develop`、`head=ZSY_develop`、**89 files / +7635 / −73** |
|
||||
| 迁移脚本 | **无新增**;`app/model` 的改动经我逐行核对只是别名与映射整理(`FinKnowledgeMeta` 别名、`ProfileSnapshot` 转出、`current_customer_id` 普通列、去重复声明),**未改任何表结构** → 不需要新迁移 |
|
||||
| PR 标题 | 「客服 Agent 接入底座:访客身份、画像候选、转人工工单(含底座合规清理)」—— **范围写得准**,评审时以它为准 |
|
||||
|
||||
**合并顺序建议**:我方本地 `qyqy_develop` 领先远端 6 个提交、且与你的改动零重叠,
|
||||
所以 **先推我这 6 个、再合 PR #7**(两个方向都无冲突,但这样 PR 的 diff 更干净)。
|
||||
|
||||
**合并后我方会跑**(`docs/32` §5):`alembic upgrade head` → `python tools/audit_schema.py`
|
||||
→ `python tools/check_authoritative_docs.py` → `ruff` / `mypy` / `pytest` 四项门禁 → 真机联调;
|
||||
外加 §2.2 的权限种子脚本。
|
||||
|
||||
---
|
||||
|
||||
## 5. 结论
|
||||
|
||||
- **你要补的两句确认:核验通过**,§1.2 与 §2.2 两条全部关掉,**不用再改代码**;
|
||||
- **你主动提的两个「要不要改」:都不用改**(访客号段、角色兜底);
|
||||
- **合并前请改两处**:§2.1 的 `A034`/`A035` → `A039`/`A040`;§2.3 的 `milvus-lite` 挪到
|
||||
`optional-dependencies`;
|
||||
- **由我方补一件**:§2.2 的权限种子脚本(等你报 id 号段后我对齐);
|
||||
- **品牌名**:按你原计划上报项目方,请带上 §3 的补充事实,并单独一个提交。
|
||||
|
||||
这四项处理完,PR #7 我这边没有别的反对意见,可以合。
|
||||
|
||||
辛苦了。
|
||||
|
||||
---
|
||||
|
||||
## 6. 附:可直接复制发送的微信版
|
||||
|
||||
> 给不读长文档的场景用;内容与上文一致,逐条对应。
|
||||
|
||||
```
|
||||
ZSY 你好,你的 v2 我逐行核过了(把 origin/ZSY_develop 取到本地读的,不是照抄描述)。
|
||||
|
||||
你补的两句确认:都成立,可以关掉。
|
||||
1)sub 是纯数字 —— security.py:87-91 那段校验在访客分支之前,你第 43 行的生成方式
|
||||
落在 [1, 9e18],19 位,全部满足;
|
||||
2)默认值 True —— contracts.py:88 实测就是 True,base.py:138 的短路也读到了。
|
||||
另外你主动提的两个"要不要改",我的答复都是不用改:访客 sub 不用换号段(撞上真实 ID 也
|
||||
拿不到权限,auth.py:52 已跳过身份解析);"访客不召回"也别下放到声明位,放 base.py 的
|
||||
角色判断是底座级兜底,比声明位可靠。
|
||||
|
||||
顺带一个好消息:入口校验保证了所有 sub 都是纯数字,所以我上次担心的 69 处没防御的
|
||||
int(context.user_id) 不会被访客打炸,那轮跨模块收敛不用做了。
|
||||
|
||||
但合并前有三件事要处理,两件是功能会直接不可用:
|
||||
|
||||
1)docs/05 §19 端点编号撞车:A034/A035 各被占了两次(我方登录 + RBAC 只读已占 A034-A038,
|
||||
你又插了画像候选那两行)。麻烦改成 A039/A040,M003/M004 不冲突、保留。
|
||||
提醒一下:tools/check_authoritative_docs.py 只查文档文件名编号,不查 §19 端点号,
|
||||
所以这个重复合并后守卫会放过去,我是手工核出来的。
|
||||
|
||||
2)三个新权限不在任何脚本里:memory:candidate:confirm、memory:candidate:review、
|
||||
handover:read。我实测我方库 sys_permission 38 条里都没有,你分支的 tools/ 和 alembic/
|
||||
里也没有种子或迁移。这样合过来以后,候选确认、候选审核、转人工工单这 5 个接口会全 403
|
||||
(fail closed 是对的,但等于没上)。你的集成测试能过,说明你环境里已经手工插过了 ——
|
||||
这正是"环境数据不随代码合并"的第 4 次踩坑。
|
||||
这份我出了:tools/grant_customer_service_phase2_permissions.py,id 从 9036 起
|
||||
(避开种子 9001-9019 和投顾 9020-9035),customer 角色给 confirm、admin 给 review +
|
||||
handover:read。你如果已经插过,把你用的 id 号段报我,我按你的对齐。
|
||||
注意它只增不删,跑过 seed_test_rbac.py(DELETE 9001-9099)之后要重跑。
|
||||
|
||||
3)milvus-lite 加到主 dependencies 了(pyproject.toml 和 requirements.txt 都是)。
|
||||
按确认口径应该放 optional-dependencies,你自己的接入说明里也写的"(可选本地开发依赖)",
|
||||
所以是放错段了,挪一下就行。
|
||||
|
||||
还有品牌名那条,给你一个对你有利的补充事实:app/core/customer_service_rules.py 第 116、
|
||||
129 行早就是"奶龙基金"了(转人工和防诈骗话术),而 COMPANY 是"南方科技" —— 所以改之前
|
||||
底座自己就是矛盾的,你这一改实际是消除内部不一致,不只是换品牌。上报时把这层理由写进去
|
||||
更容易过。建议单独一个提交,便于项目方拍板后单独回滚。我方 tools/chat_console.py 也还是
|
||||
南方科技,定了奶龙我们自己改。
|
||||
|
||||
合并预演我做了:merge-tree 无冲突,你这 89 个文件与我方本地领先的 6 个提交零重叠,
|
||||
没有新迁移、app/model 的改动只是别名和映射整理、表结构没变。
|
||||
建议顺序是先推我这 6 个、再合 PR #7(都无冲突,但这样 PR 的 diff 更干净)。
|
||||
|
||||
那三件处理完,PR #7 我这边没有别的反对意见。
|
||||
|
||||
辛苦了。
|
||||
```
|
||||
Reference in New Issue
Block a user