14 KiB
回复 ZSY:底座扩展确认(v2)
致:ZSY 被回复:
致qyqy_底座扩展确认沟通文案_v2.md我方基线:qyqy_develop(本地,领先origin/qyqy_develop6 个提交) 被评审的分支: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 提的两个确认,答复如下:
sub是纯数字 —— 满足,这一条关掉了。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 我这边没有别的反对意见。
辛苦了。