Files
group_fqcd_jr/docs/34-ZSY底座扩展确认-回复v2.md
T

14 KiB
Raw Blame History

回复 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 我这边没有别的反对意见。

辛苦了。