oh-my-muse/docs/agent-specs/2026-06-23-market资产物化-review.md
lili b5a6b1b423 docs(handoff): market 资产物化架构评审版(裁决伪缺口 + O1 跨租户验证)
2 路调研 + O1 验证(反假绿 file:line)裁决"market 资产物化":
- B(内容副本)与产品-02F:135 禁止项 + Candidate Envelope 已确认决策冲突 → 否
- O1 实证:单租户运行(system_tenant 仅 id=1)+ AI 消费链路(binding→kb 正文)未建成
  (ai application 全量 grep 零命中)→ C/B' 解决的跨租户可读问题当前不触发、
  服务的消费场景不存在 → 双重假想未来建设(违 YAGNI)
- 结论:整条物化主线暂缓,前置(真多租户 or AI 检索消费链路/RAGFlow)就绪前不投入
- SSOT 内部矛盾(A 不物化 vs B 物化)待人类裁决;V27 物化注释系 P2 自写、证据弱

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 00:52:47 -07:00

105 lines
11 KiB
Markdown

# market 资产物化 — 架构评审版(裁决"物化"真伪缺口)
> 版本:v0.1 · 日期:2026-06-23 · 目标读者:架构/产品拍板人 + 实现 agent · 类型:评审版(结论 + 拍板点 + blast radius + 验收,不预写代码)
> 前置:handoff P0/P1(knowledge)/P2(agent)/P3(content) token 红线三 owner 已交付;本主线是 handoff 诚实范围反复标记的"另一维度——market 资产物化"。
## 〇、本版定位与核心结论(先行)
**本版不是"如何实现物化",而是先裁决"物化"是不是真缺口。** 2 路反假绿调研(file:line 实证)得到一个**反直觉但证据扎实**的结论:
> **"install 自动把 market 资产物化成内容副本"很可能是伪缺口,且其强版与产品规格的明确禁止项直接冲突。** SSOT 主流(流程-02B / 产品-02F / 后端-01)+ 已确认架构决策(Candidate Envelope 去掉、来源版本实时对比)都指向**引用语义、不复制内容**。"物化"主张只剩 1 个 schema 枚举值 + 1 条 P2 阶段我自己写的迁移注释支撑。
因此真正待拍的不是"怎么物化",而是**三选一的方向裁决**(详见 §四拍板点):
- **方案 C(维持引用,评审倾向)**:不建实体,`source_market_asset_id` 降级为纯溯源标记或删除。
- **方案 B'(installed_ref 引用壳)**:install 在消费者侧建 `kb_type=installed_ref` 引用记录(指向 source asset、**不复制内容**),解决跨租户可见/可绑 + 字段落地——A 主线的一种落地。
- **方案 B(物化建内容副本)**:install 复制源 kb/agent 内容成独立实体——**与产品-02F:135 禁止项冲突,评审不建议**。
### ⚠️ O1 验证后的修订结论(2026-06-23,强化为"暂缓整条主线")
O1 已实证(见 §六):当前**单租户运行**(system_tenant 仅 seed id=1、muse 表 tenant_id DEFAULT 0)+ **AI 消费链路(binding→kb 正文/检索)根本未建成**(ai application 全量 grep 零命中、knowledge-api 仅 ErrorCode、source-owner facade 只有 fail-closed 桩)。这意味着:
- C/B' 解决的"跨租户可读"问题**当前不触发**(单租户双方 tenant_id 相同);
- 它们服务的消费场景(作品用 kb 内容生成)**还不存在入口**。
**现在做 C/B' = 为「还没启用的多租户」+「还没建的 AI 检索消费链路」双重假想未来建设,违 YAGNI / CLAUDE.md「不为一次性代码建长期抽象」。** 故评审最终建议升级:**整条 market 资产物化主线暂缓**——前置(真多租户需求 **或** AI 检索消费链路/RAGFlow 落地)就绪前不投入。唯一低成本可选动作 = 补 `MuseAgentDO` 字段映射 + handoff/install 时 set `source_market_asset_id` 作**纯溯源留痕**;高 ROI 不在此主线(下一步方向见 §四 Q5 / 回 2026-06-22 盘点)。
---
## 一、现状(2 路调研实证,file:line)
### install 当前行为(无歧义事实基线)
- `MarketInstallServiceImpl.installMarketplaceAsset:50-107`:install 只校验授权 + 写 `muse_market_installation`(status=installed)+ 推进 license 投影;**显式 `targetFactsWritten=false`**(`:95-96``:198-200` 硬编码)。
- `:62-64` 注释:"作品资产不走安装流程;后续 bind/handoff 也不能把 install 当目标 owner 事实写入"。
- 全方法**无任何 kb/agent 建实体调用**。
### 模块边界(物化的硬约束)
- market-server **唯一跨模块依赖 = member-api**,对 AI/Knowledge/Content **零依赖**(`muse-module-market-server/pom.xml`;`grep MuseKnowledgeBaseDO muse-module-market` 空)→ market 模块**物理上无法**建 kb/agent 实体或回源读内容。
### asset 不承载内容(物化"内容从哪来"的核心问题)
- `MuseMarketAssetDO` / `V6__init_market_schema.sql`:asset 只存 `source_id BIGINT`(指向原始 kb/agent)+ 元数据;**不含知识条目/agent prompt 正文**。
- version snapshot(`AdminMarketReviewServiceImpl.createListedVersion:500-507`)只快照发布草稿元数据(`name/summary/sourceId/...`),**无源内容本体**。
- → 若要建内容副本,必须**回源**读发布者的 `muse_knowledge_*` / `muse_agent*`(跨发布者→消费者复制),market 模块还没这个依赖。
### 目标侧字段 = 休眠列(零读写)
- `muse_knowledge_base.source_market_asset_id`(`V5:10`,DO `MuseKnowledgeBaseDO.java:31` 已映射)+ `muse_agent.source_market_asset_id`(`V27:11`,**DO 未映射**):全仓 `grep setSourceMarketAssetId` / mapper xml 三路皆空——**字段在、零写入**。
- 现状 binding 直接 `kb_id = parseLong(sourceId)` 引用 source kb(`MuseKnowledgeBindingService:110,148`),不建新实体。
---
## 二、SSOT 内部矛盾(评审版必须裁决,原文对照)
| 主线 | 主张 | 证据(文档:行) | 证据强度 |
|---|---|---|---|
| **A 不物化/引用** | 不新增 owner、不自动写入、引用语义 | 流程-02B:363「市场知识库资产**不新增知识库 owner**,不自动写入局域知识库」;产品-02F:16「任何市场资产都不能**自动写入作品事实**」;**产品-02F:135 把「安装后自动写入局域知识库」列为禁止项**;后端-01:108「只记录授权/安装/可展示;**不写目标对象绑定事实**」 | **强**(产品+流程+领域模型,含禁止项) |
| **A 既有架构决策** | 引用 + 实时对比,非快照 | CLAUDE.md 核心决策「Candidate Envelope:去掉,简化为**来源版本实时对比**」;流程-02B:29 主流程含「来源版本实时校验」 | **强**(已确认决策) |
| **B 物化/建实体** | install 自动物化建独立实体 | 后端-04:426 `kb_type=installed_ref`(枚举值预留);后端-04:428 `source_market_asset_id`「若来自市场安装,**引用**市场资产」(注:原文"引用"其实偏 A);V27 SQL 注释「install 自动物化为独立后续主线」 | **弱**(1 枚举值 + 1 字段注释 + **V27 注释系 P2 阶段本 agent 自写**) |
| **D 复制为副本** | 许可允许时用户**主动**复制 | 产品-02E:34 / 02D:34「许可允许时**可复制为用户知识库/个人智能体**」 | 文档有、**代码零实现**(grep copyKb/cloneAgent 空);且是**用户主动**非 install 自动 |
**关键裁决张力**:B 强版(install 自动复制内容成副本)= 产品-02F:135 **明令禁止**的"安装后自动写入局域知识库"。二者不可兼得。
---
## 三、问题重新框定
把"物化"拆成两个被混淆的不同诉求:
1. **内容复制(B 强版)** — install 自动把发布者 kb/agent 内容拷成消费者独立副本。**与禁止项冲突 + 违背 Candidate Envelope 决策,评审不建议。** 真要"拥有副本"应走 D(用户主动复制),非 install 自动。
2. **引用壳 + 溯源(B')** — 真正可能的缺口在这:现状 binding 用 `kb_id=parseLong(sourceId)` **直接引用发布者 kb id**(P1 诚实范围已记:e2e 绿靠 fixture 巧合 asset1→kb_id=1)。**跨租户下,消费者作品直接绑发布者租户的 kb 实体,可见性/租户隔离存疑(未确证、列开放项 O1)。** `kb_type=installed_ref` 的设计本意很可能是:install 在消费者侧建一条**引用记录**(指向 source asset、内容仍引用源、实时对比),让消费者有自己租户下的可绑条目 + 让 `source_market_asset_id` 溯源落地。这是 **A 主线的合规落地**,不复制内容、不违反禁止项。
→ 评审主张:**否掉 B,在 C(纯引用,最小)与 B'(installed_ref 引用壳,解决跨租户+溯源)之间裁决**。
---
## 四、拍板点(请你定)
| # | 拍板点 | 选项 | 评审建议 |
|---|---|---|---|
| **Q1** | install 是否物化建**内容副本**(B 强版) | 是 / **否** | **否**——与产品-02F:135 禁止项 + Candidate Envelope 决策冲突 |
| **Q2** | 是否需要 **installed_ref 引用壳**(B',消费者侧建引用记录、不复制内容) | 需要(若跨租户绑定确有隔离问题)/ 不需要(维持直接引用 source) | **先验证 O1 跨租户可见性** → 有问题则 B'、无问题则 C |
| **Q3** | `source_market_asset_id` 休眠字段去留 | 降级纯溯源标记(install set、不建实体)/ 配合 B' 落地 / 删除 | 跟随 Q2:选 C→纯溯源标记或删;选 B'→配合落地 |
| **Q4** | 是否做 02E/02D"用户**主动**复制为副本"(D,独立功能) | 本主线做 / 列独立功能按需排期 | **列独立功能**(文档有代码无、用户主动授权、非 install 自动,与本主线解耦) |
---
## 五、blast radius(按方案)
- **方案 C(纯引用 + 溯源标记)**:最小。install 处可选 set `source_market_asset_id`(纯标记)+ 补 `MuseAgentDO` 字段映射 + 删/留休眠列决策。**不碰模块依赖、不碰内容复制。** 风险极低。
- **方案 B'(installed_ref 引用壳)**:中。需定 install 时由**谁**建引用记录(market 无 knowledge/ai 依赖 → 经各 owner 的 -api 端口反向回调,如 P1-P3 的 BC 端口模式)+ `kb_type=installed_ref` / agent `market_installed` 枚举落地 + 工作台"已安装 kb/agent"列表与绑定接线 + 跨租户可见性方案。涉及 knowledge/ai/market 三模块 BC 边界,须 ArchUnit 守。
- **方案 B(内容副本)**:大且**不建议**。market 反向依赖 knowledge/ai 回源读内容 + 多表内容复制(异步任务/幂等/补偿,CLAUDE.md 铁律)+ 与禁止项冲突需先改 SSOT。
---
## 六、开放项(实现前须验证)
- **O1(决定性,✅ 已验证 2026-06-23)**:结论 **(b)+(d)+(c') 复合**——① binding 阶段**不查 kb**(直接信前端 sourceId,`MuseKnowledgeBindingService:110,148` 无 kbBaseMapper 注入)→ 跨租户不因绑定失败;② 租户隔离基线**完整**(kb/document/entity/relation/binding 全 `extends TenantBaseDO``V5` 各表 `tenant_id NOT NULL DEFAULT 0`、无 @TenantIgnore,yudao 拦截器自动注入租户条件,`MuseKnowledgeEntityMapper:36` 注释自证)→ 设计上**跨租户读不到内容(b)**;③ **当前实际单租户**(`system_tenant` 仅 seed id=1、muse 表 tenant_id DEFAULT 0)→ 问题**当前不触发(d)**;④ **额外铁证(c')**:AI 域当前**根本没有 binding→kb 正文的读取/检索路径**(`muse-module-ai` application 全量 grep 零命中、`muse-module-knowledge-api` 仅含 ErrorCode、`MuseExternalSourceOwnerFacade` 只有 fail-closed 桩 `Unavailable...`)——"用到发布者内容"今天连入口都没有。**→ installed_ref/物化只在「多租户化 + 消费链路建成」双前置就绪后才有意义,二者当前都不存在(见 §〇 修订结论)。**
- **O2**:`muse_market_installation` 账户记录 与 假想的 `installed_ref` kb 条目职责是否重叠? 工作台"可绑定 kb 清单"现在读哪张表?
- **O3**:agent 侧 `agentType='market_installed'`(04a 注释枚举)与 P2 已落的 `agent_type='market'`(handoff 放宽用)关系——是否同一概念两套命名,需统一。
## 七、验收(方案确定后细化)
- C:字段决策落地 + DO 映射补齐 + 单测/无回归;不新增 e2e(无行为变更)或补"溯源标记可读"读端 e2e。
- B':install→引用记录→工作台可见→绑定 真后端 e2e + BC 边界 ArchUnit + 跨租户隔离用例。
---
> 下一步:先答 **O1**(跨租户可见性,决定 C vs B')与 **Q1/Q4**(否掉内容副本、D 解耦),即可定主线方向。评审强烈建议:**不做 B 内容副本**;在确认 O1 后于 C / B' 间取一。