Files
group_fqcd_jr/docs/NL_develop交付说明-评审意见.md
lzf_0626 927a6fecc0 评审 NL_develop 交付说明:三个环境前提必须先对齐
产出 docs/NL_develop交付说明-评审意见.md(可直接转给组员),以及一个只读探查工具
tools/probe_knowledge_collections.py(Milvus 集合 schema 与行数)。

核查中发现的硬矛盾,都有可复核的证据:

1. 配置库不是同一个。对方说 active=216、合并前生效版本是 186;我这边实测 active=201,
   最近 5 条 id 就是 201/198/197/196/195 —— release.id 自增,同库不可能一边 216 一边 201。
   所以他以为已发布的 customer_service:faq=[search_knowledge, query_customer_profile]
   在这边并不存在,而他的画像出口复用的正是这个 key ⇒ 合并后被 ToolExecutor 失败关闭。
   同理他说的 fund_query_demo:fund_quote 缺失,我这边是存在的。

2. Milvus 也不是同一个。对方说现库字段是 knowledge_id/snippet、无 visibility、行数
   106/177/73;我这边实测是 doc_id/content/chapter/section/.../visibility 共 15 个字段、
   行数 125/297/214,且与 knowledge_search_service.py 的 OUTPUT_FIELDS 逐字一致。
   他这次的字段映射改动(doc_id→knowledge_id)在这边会直接报 field knowledge_id not
   exist,把客服知识检索整条打挂 —— 比他自述的"关闭 visibility 隔离"严重得多。
   建议不是 A/B/C 三选一,而是第四种:运行时探测字段名,两套 schema 都能跑。

3. 解释器不同。他用 .venv(项目里不存在,那是他机器上的 gitignore 目录),约定是
   D:\conda\envs\jr_py313。所以"mypy 151→181"跑不到本基线 —— 这边是 138 文件 0 错。

另指出:docs/26-JWT密钥管理与轮换.md 会与已存在的 docs/21-JWT密钥管理与轮换.md 重复,
建议并入 21;驳回删除 docs/04/06/10/13/99。

四项待裁决的答复:同意 agent_type 方案(要求补审计);字段映射改为运行时探测;
驳回删除编号文档;26 并入 21。
2026-09-11 16:51:52 +08:00

11 KiB
Raw Permalink Blame History

评审意见 · NL_develop 交付说明(给架构师的回复)

评审人:qyqy_develop 维护者 被评审:交付说明-NL_develop-给架构师.md,来源分支 NL_develop @ 96a6e01(远端当前 e342670) 基线核对时间:2026-09-11 结论:代码质量认可,但合并前有三个环境前提必须先对齐,否则合并即故障。


0. 先说结论

这份交付说明写得很好:每项改动都给了理由、真机证据、以及对"动了别人什么"的逐文件说明, 还主动标注了代价(_HAS_VISIBILITY=False 会让内部资料隔离失效)。§4.1 的"确认式白名单 + 空串不注入"、§4.2 的"只改读取侧不改检索逻辑"、§4.5 的"话术只由治理层注入",这几个判断我 都同意,理由见 §3。

但是:说明里有三处前提与我这边的实际环境不符,而且它们不是细节,是"合并会当场打挂"级别的:

# 问题 后果
1 你说的生效版本 216 在我这里不存在(我这边 active 是 201) 你以为已发布的修复,在我这边没发布 → 画像出口会 AGENT_PERMISSION_DENIED
2 你说的 Milvus schema(knowledge_id/snippet/无 visibility)与我这边完全不同 你这次改的字段映射,在我这边会把检索打挂
3 你用 .venv,项目约定是 D:\conda\envs\jr_py313 "mypy 151→181" 这类结论与我这边不可比(我这边是 0 错)

下面逐条给证据和修法。


1. 环境不一致(最要紧,先对齐这个)

1.1 配置发布:你说的 216 不在这个库里

我这边实测(python tools/probe_release_state.py,证据 docs/evidence/release-state.json):

active release = 201  (cs-prompt-d918ca507fc6, "客服闲聊提示词", 2026-09-11 05:25 activated)
最近 5 条 id:201 / 198 / 197 / 196 / 195      ← 不存在 216
active 配置项:agent_tools 9 条 + prompt 1 条

release.id 是自增主键,同一个库不可能一边看到 216、一边最高只有 201。 所以"config_release 216 已在共享库生效"这句,对这台环境不成立。

同样地,你说的"合并前生效版本是 186、白名单是 query_knowledge"、"fund_query_demo:fund_quote 已经不存在",在我这边也不成立 —— 我这边 201 的内容是:

config_key allowed_tools(我这边 201 实测)
customer_service:faq ["search_knowledge"]
customer_service:policy_explain ["search_knowledge"]
customer_service:product_inquiry ["search_knowledge"]
customer_service:suitability_check ["search_knowledge", "check_suitability"]
fund_query_demo:fund_quote ["query_fund_quote"] ← 存在
risk:general []
risk:risk_evidence ["get_alert_evidence"]
risk:risk_overview ["get_risk_overview"]
risk:risk_search ["search_risk_alerts"]

1.2 ⚠️ 合并后画像出口会立刻断链

你在 §4.3 写"PROFILE_WHITELIST_INTENT = INTENT_FAQ:画像工具调用复用已发布的 faq 意图 key" —— 那是基于你的 216(faq = [search_knowledge, query_customer_profile])。

我这边 201 的 faq 只有 ["search_knowledge"],没有 query_customer_profile。 ToolExecutor 取交集、失败关闭 ⇒ 画像出口一调用就被拒。

修法(合并时必须同时做):补发一个版本,把 customer_service:faq 改成 ["search_knowledge", "query_customer_profile"],其余 8 条原样继承(config_release 是整版本 替换,漏带会清空别人的白名单)。这一步我这边可以出脚本。

1.3 ⚠️ Milvus 也不是同一个,字段映射改动会打挂检索

我这边实测三个集合(python tools/probe_knowledge_collections.py,证据 docs/evidence/knowledge-collections.json):

集合 行数 字段
fin_faq_collection 125 doc_id(主键) title content chapter section tags doc_no version effective_date expire_date source_url reviewer source_file visibility embedding
fin_policy_collection 297 同上
fin_product_collection 214 同上

你说的是 knowledge_id | title | snippet | tags | version | intent | embedding、无 visibility、 行数 106/177/73 —— 字段名、visibility 有无、行数三处全不一样。

而我这边 app/service/knowledge_search_service.py:23-25 的

OUTPUT_FIELDS = ("doc_id", "title", "content", "chapter", "section",
                 "tags", "doc_no", "version", "source_file", "visibility")

与我的集合逐字一致,:152 的 visibility == "public" 过滤也在正常工作。

所以 §4.2 不是"A/B/C 三选一"的问题。 你的 A 方案在这边会:

  1. doc_id → knowledge_id ⇒ Milvus 报 field knowledge_id not exist,整条检索失败;
  2. content → snippet ⇒ 同样失败;
  3. _HAS_VISIBILITY = False ⇒ 把本来正常工作的内部资料隔离关掉。

我要的修法不是 A/B/C,而是第四种:把字段映射改成运行时探测。

# 启动时 describe_collection 拿真实字段名,建立 逻辑名 → 实际字段名 的映射;
# visibility 有就过滤、没有就跳过;缺哪个逻辑字段就明确报出来,不要硬编码。

这样两套 schema 都能跑,不需要迁移、不需要重灌数据,也不需要改检索逻辑 —— 你在 §4.2 里 "只改读取侧、不动 KnowledgeHit 对外形状"的克制我完全同意,只是落点应该从"硬编码另一套字段名" 换成"探测"。这也顺带解决你担心的"改回去很麻烦":探测之后没有需要改回去的东西。

顺带:你那边行数 106/177/73 vs 我这边 125/297/214,说明演示数据也不是同一份。演示前要统一。

1.4 解释器不统一,数字不可比

你用 .venv\Scripts\python.exe —— 这个目录在项目里不存在(.venv 被 gitignore,那是你机器上的)。 项目约定与文档用的是 D:\conda\envs\jr_py313\python.exe。

结果就是 §5 那张"mypy 181 个错"的表跑不到我的基线上:我这边 mypy app 是 138 source files, no issues found,0 个错。你列的"app/model/fund.py 105 个错"在我这里一个都没有。

请先统一解释器再讨论 mypy 数字,否则每次合并都要重新吵一遍。


2. 文档编号:docs/26 会和已有的 docs/21 重复

你说"docs/26-JWT密钥管理与轮换.md 是新文件,原 21→25→26 两次让号"。

但我这边 docs/21-JWT密钥管理与轮换.md 已经存在。 也就是说合并后会出现两份同主题文档 (21 与 26),这比编号冲突更糟 —— 读者不知道该信哪一份。

建议:不要新增 26,把内容合并进已有的 docs/21。如果你手上那份是旧 21 的修订版, 请在 PR 里说明"以 26 的内容覆盖 21"并删除 21,二选一,别并存。


3. §6 四项待裁决的答复

3.1 AgentGovernance.review() 新增 agent_type —— ✅ 同意

风控 Agent 的输出是给风控专员看的字段化摘要(预警编号、级别、建议动作),给它追加一句面向 投资者的免责声明本身就是错的 —— 你实测 tests/contract/test_risk_agent_contract.py 因此 失败,那是真实症状,不是测试太严。

判定口径选"确认式白名单"(只有 customer_service / fund_query_demo 注入,空串不注入) 比"默认注入"安全,理由(测试替身不连库,默认注入会污染每个替身测试)我认同。 "带默认值所以旧调用点不会 TypeError"也处理得对。

补充一个要求:请确认 agent_type 同时写进了治理环节的审计记录。治理层改写了对外输出, 事后必须能追溯到是哪个 Agent 触发的。

3.2 字段映射 —— ⚠️ 不是 A/B/C,见 §1.3(改为运行时探测)

3.3 删除 docs/04/06/10/13/99 —— ❌ 驳回,请保留

我这五个文件全都在(docs/04-开发文档评审报告.md、docs/06-底座代码测试报告.md、 docs/10-业务域接入评估.md、docs/13-基金行情工具业务接入清单.md、 docs/99-已废弃-公共Agent平台接口规范.md)。

删除它们收益为零(你说"无人引用",但保留的成本同样为零),代价是:PR diff 里出现 5 个删除、 还要同步改 AGENTS.md 里"已删除"的说明、并且丢掉了 docs/10 这份业务域接入评估。 请把这一项从 PR 里去掉。

3.4 docs/26 编号 —— ⚠️ 见 §2,建议并入 docs/21


4. 关于你 §7.1 的提醒(memory_sync_outbox)

你提醒"你最近那批提交里有'记忆→画像→图全自动触发',memory_sync_outbox 没有消费者, 这两处可能重叠,请先确认再动手"。

我 grep 过了:MemorySyncOutbox 在全仓只有模型定义,既没有生产者、也没有消费者。 所以你说的"事件写入了、没人消费"在我这边不成立(根本没写进去)。不重叠。

但你的观察方向是对的,而且比你想的更严重:我另外发现 app/worker/graph_projection_worker.py 的 GraphProjectionWorker 没有任何实例化点。这两件事大概率是同一个问题 —— 组件写好了、 线没接。这条链路整体未接线,麻烦你那边也确认下你的分支上是什么状态。


5. 合并顺序建议

  1. 先对齐环境:确认你我连的是不是同一个 MySQL 与同一个 Milvus。所有"配置已发布""数据是 某某 schema"的结论都建立在这上面,不对齐的话下面每一步都要返工;
  2. 统一解释器(D:\conda\envs\jr_py313\python.exe);
  3. §1.3 的字段映射改成运行时探测,§3.3 的删除项撤回,§2 的编号并入 21;
  4. 合代码;
  5. 合并后立刻补发配置(customer_service:faq 加 query_customer_profile,§1.2),否则画像 出口不可用;
  6. 统一演示数据(行数差异见 §1.3)。

6. 我这边的状态(供你参考)

  • 分支 qyqy_develop_1 已包含 origin/qyqy_develop 的 d2cdbba,并且多 8 个提交(含组员 RM2 线并入的 4 个),尚未推送;
  • 门禁:ruff 干净 / mypy app 138 文件 0 错 / 703 unit+contract / 33 integration;
  • 我这一轮改的公共部分(供你判断冲突面):ApiTransactionService.execute_in(幂等,复用调用方 事务)、app/api/views/envelope.py(§3.3 信封,风控+会话+知识引用三处共用)、 app/api/dependencies/negotiation.py(SSE Accept 协商)、risk_cursor.py 的游标绑定、 OutboxHandlerError(worker 失败原因可诊断且不泄露异常消息);
  • 你 §4.2 说的"公共 SSE 协商""公共鉴权基座"这类问题,确实归我这边修,已经在做了 —— 分工没有空档。

有异议随时提,尤其是 §1.3:如果你那边确认过现库字段就是 knowledge_id/snippet, 那说明我们连的不是同一个 Milvus,那就先把这个对齐了再谈代码。