oh-my-muse/design-docs/临时-05-market-agent物化执行plan.md
lili 2dde0a45ef docs(mvp): 八域交付盘点+1.0.0交付计划+收尾对齐
- 8域交付盘点(codex多实例+claude深盘、均亲验以代码+真测试为准)→收敛5类系统性病灶
- 新增 docs/mvp/1.0.0-交付计划.md:基准线A最小可用子集、8域对账矩阵、E0-E7 Epic分解、反假绿验证标准
- 进度总账加指针对齐(接口门completed≠端到端可闭环;可闭环严判完成度~55-65%)
- 临时-01~04归档删除(SSE/KB物化过程文档、结论已并入memory与各.agent)、大纲清理悬空引用、临时-05并入E4

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 22:09:24 -07:00

56 KiB
Raw Blame History

临时-05 · Market Agent 类资产物化(拷配置 · installed 型 agent · 改授权门)执行 Plan

  • 版本v1执行版待人类 review/拍板后执行;本稿只读出代码、不改任何代码、不 commit
  • 更新日期2026-06-26
  • 目标读者:后端 / 架构 / PR reviewer
  • 阅读时间3040 分钟
  • 文档性质:临时件(执行依据,落定后归并入正式分册或删除,不作长期 SSOT。本稿只给"怎么做",不重定义概念:方案演进与 C/B'/暂缓权衡见 临时-02-market-install下游物化方案.md§2.2 B' 智能体侧、§3.2 智能体 schema 增量为本稿 WHAT 上游KB 类资产物化的姊妹方向(已闭环、本稿照搬其物化范式与开放项结构)见 临时-04-market-KB物化-D0fork执行plan.md;术语与 owner 边界以 架构-02-核心数据结构与双轨模型.md 为准§5 智能体运行权限、§6 市场资产、§11.2 绑定安装授权、授权快照"引用对象必须保存快照 id");表结构归 后端-04 §7 AI/Agent授权快照字符串承载归 ADR-020Source 传播事件驱动归 ADR-017BC 边界规则归 .agents/rules/bc-boundaries.md
  • 配套人读图:临时-05-market-agent物化执行plan.html

一句话:市场安装一个智能体后,槽位能绑进去(闸 A 已放宽),但 AI 运行时 requireVisibleAgent 仍把 market 来源的 agent 一律拒掉——这是 agent 物化的唯一真断点。本 plan 拍板已定的方向是 ①不 fork、拷配置agent 不像 KB 有"上架后还能往库里加私有文档"的泄露向量:版本一经 active 即不可变、运行时不回读发布者私有 prompt、config 本身就是被授权出售的商品本体),②把市场 agent 物化成安装者租户内一个独立的 installed 型 muse_agent + active muse_agent_versionconfig 拷自发布者)+ 改运行时授权门放行。物化落点严格在绑定路径(与 KB 同范式)、不在 install 侧。本 plan 把它拆成 A-source / A-materialize / A-runtime / A-naming / A-verify 五个可独立验证的实现单元

与 KB D0-fork 的根本差异(一句话)KB 的难点是"发布者私有内容不能泄露",逼出了发布侧 fork 一份只含公开快照的独立 dataset重活agent 没有这层泄露向量,所以不 fork、不碰任何外部资源、无发布侧异步工程——物化退化为"安装侧同步把发布者的 agent 配置拷成一份本地独立 agent",唯一新增的硬约束是改运行时授权门trust boundary必须做负路测


0. 背景与拍板(读过临时-02/临时-04 的人只需看这一节就能对齐)

临时-02 把市场安装的两个物化断点摆开诊断:知识库检索的"数据集门"永远命不中(断点①),智能体在运行期被授权门直接拒(断点②)。临时-04 已经把断点①用 D0-fork 完整闭环(上架 fork 公开副本 + 安装侧物化 installed_ref KB + 检索打通 + 真验私有不泄露commit c12eb01→8b3258e 已真验收)。本 plan 处理剩下的另一半——断点②,即智能体类资产的物化。

拍板已定(三条,本 plan 不再重开权衡)

  1. 不 fork、拷配置。KB 之所以要 fork是因为 KB 上架后生命周期不冻结、发布者还能继续往自己库里加私有文档,安装者若共享发布者活 dataset 就会读到上架时根本不存在的私有内容。agent 没有这条链:一个 agent 版本一旦 active 就不可变(改动只能开新版本行,见 §1 事实 4运行时也不回读发布者的私有 prompt 正文、只用版本里冻结的 config,而 config 本身就是发布者亲自写好、上架出售的商品本体name/description/promptTemplate/slotBindings/toolGrantIds。安装者拿到这份 config = 拿到他买的东西,这不是泄露、正是交付。因此 agent 物化不需要 fork 任何东西,直接把发布者那一份 active 版本的 config 进安装者租户内一份新建的 agent 版本即可。

  2. 物化成 installed 型独立 agent + 改授权门。对齐临时-04 的 installed_ref 范式:在安装者租户里建一行独立的 muse_agent(一个区别于 system/user 的 installed 型,source_market_asset_id 回填溯源)+ 一行 active muse_agent_versionconfig 拷自发布者)。光建实体还不够——运行时授权门当前对非 system/非本人 user 一律拒§1 事实 1必须把它放行"本人所有的 installed 型 + 授权有效"。这道改动是 trust boundary是本 plan 最需要严防的一点§4 R1 + A-verify 负路测)。

  3. 物化落点在绑定路径、不在 install 侧。与临时-02 §1.2 / 临时-04 完全一致install 维持"只记账、targetFactsWritten=false"§1 事实印证物化是目标域ai绑定路径在消费 handoff token、写槽位绑定的同一事务里多做的一步。


1. 执行前必读:把工作量钉死的硬事实(基于真实代码,行号为只读核验所得)

事实 1 — 运行时授权门 requireVisibleAgent 对 market 来源 agent 一律拒,这是 agent 物化的唯一真断点。 MuseAiTaskServiceImpl.requireVisibleAgent:321-342 的可见性判定在 :329-331agentType 不是 system、且不是(userownerUserId==agent.ownerUserId)就抛 AI_AGENT_SCOPE_FORBIDDEN。market 出身的 agent 既非 system、也非"本人所有的 user"必拒。三个入口全部经它::220agentTest 试运行)、:308agentOverrideRef 临时覆盖)、:315agentSlotKey→查 slot binding→拿 agentId。门内还有两道:326-327agent.status=active:333-336 要存在 active 的 muse_agent_version(按 versionNo 或最新)。结论:物化只要在安装者租户造出"本人所有的 installed 型 active agent + active version",再把 :329-331 放行 installed 型,三入口同时通——这是本 plan 的命门A-materialize 造实体、A-runtime 放门。

事实 2 — 槽位闸 A requireVisibleActiveSourceAgent 已对 market 放宽,槽位已能绑 market agent 进去。 MuseAgentSlotServiceImpl.requireVisibleActiveSourceAgent:292-314:304-308:当 marketHandoff=truetoken 已被 Market verify 通过)时放宽 agentType 可见性、接纳 market 型 agent;非 handoff 路径维持原校验。其 precheck:93/bindAgentSlot:158/consumeMarketHandoff:350)链路已闭环,绑定持久化是稀疏 override 的 upsert:25002D 首次创建死锁已修commit 9115d4f + V29 唯一约束)。结论:闸 A 不是断点、本 plan 不动它;断点纯在运行时闸 B事实 1。槽位里存的目前是一个指向 market 出身 agentId 的裸引用,物化后要让槽位指向安装者本地新建的 installed agentIdA-materialize 的去裸引用,类比 KB 的 kb_id 去污染)。

事实 3 — MuseAgentDO 未映射 source_market_asset_id,但该列 V27 已就位、休眠。 muse_agent 表的列由 V4__init_ai_schema.sql:71-91 定义(agent_key/name/description/agent_type DEFAULT 'system'/owner_user_id/prompt_key/status DEFAULT 'active'/current_version_id/slot_bindings JSONB/category/tags JSONB唯一键 uk_muse_agent_key = (tenant_id, agent_key)V27__extend_ai_agent_handoff_source.sql:11ALTER TABLE muse_agent ADD COLUMN source_market_asset_id BIGINT,注释自证"install 自动物化为独立后续主线,本列先就位、对齐 knowledge muse_knowledge_base.source_market_asset_id"——列在、但休眠MuseAgentDO.java 当前只映射到 tags 为止,没有 sourceMarketAssetId 字段结论A-materialize 要给 MuseAgentDO 补映射这一列(@TableField物化时回填承载溯源 + 幂等键之一。这是唯一的 schema 触动,且只补 DO 映射、不需新 Flyway 迁移(列已存在)。

事实 4 — agent 实体 = muse_agent 元数据 + muse_agent_version.config JSONB 商品本体version active 即不可变runtime 从 config 取 modelKey/promptTemplateVersion。 muse_agent_versionV4:96-110)的 config 是 JSONB承载发布者亲写的 agent 配置;唯一键 uk_muse_agent_version_agent_ver = (tenant_id, agent_id, version)status DEFAULT 'draft'。运行时 requireVisibleAgent:338-341configmodelKey(缺省 DEFAULT_MODEL_KEY+ promptTemplateVersion(缺省 DEFAULT_PROMPT_TEMPLATE_VERSION)组装 AgentRuntimeRef运行时只读这份冻结的 config、不回读发布者任何活数据。版本一经 active 即不可变(改动开新版本行,由 (agent_id, version) 唯一键保证),故"拷一份 active 版本的 config"= 拷一个不会再变的快照。结论:这正是"不 fork、拷配置"成立的根据——拷 config = 完整拷下商品本体,无外部资源(无 RAGFlow dataset 那类需要重建索引的东西无后续漂移。A-materialize 拷 config 时须整体拷贝、保完整性§4 R3不能只拷部分键导致运行时取不到 modelKey/promptTemplate。

事实 5 — config 拷贝的来源读取是本 plan 的核心决策点market 当前不回 config且 market 对 ai 零依赖、不能回读发布者 muse_agent_version MuseMarketAssetDOsource_id BIGINT 指发布者 muse_agent.idasset_typepublisher_idtags JSONB只存来源指针 + 元数据,不承载 config 本体。临时-04 已落的跨 BC 读端口 MarketAssetSourceApi.getAssetSource(assetId) 当前回的是 KB-fork 形状MarketAssetSourceRespDTO{exists, assetType, sourceId, publisherUserId, assetName, publicForkDatasetId, forkStatus}MarketAssetSourceApiImpl:37-54muse_market_asset + tags JSONB 里 U-fork 写回的副本状态)——没有 agent config 这一项publicForkDatasetId/forkStatus 是 KB 专属、对 agent 无意义。而 market 对 ai 零依赖(不能直读 ai 的 .dal、ArchUnit BcBoundaryArchTest 拦着),所以 market 自己也回读不到发布者的 muse_agent_version.config结论agent 物化要拿到"发布者 active 版本的 config",必须新增一条读 config 的途径——这是本 plan 最大的决策点§3 D1MarketAssetSourceApi 让 market 反向调 ai 读端口带回 config vs ai 侧自有读端口 ai 自己读发布者 agent)。这是 agent 相对 KB 物化最不一样的地方KB 的副本 dataset id 是 market 自己 fork 时写回 tags 的、market 读自有数据即可带回agent 的 config 在 ai 域、market 域天生够不着。

事实 6 — agent 无私有泄露向量(已实证),故不需要 KB 那套 fork。 三条实证:①版本不可变(事实 4(agent_id, version) 唯一键 + active 不可变);②运行时不回读发布者私有 prompt 正文,只用版本冻结 config事实 4requireVisibleAgent:338-341);③config 是被授权出售的商品本体(发布者亲写的 promptTemplate/slotBindings/toolGrantIds上架即为供人安装使用。对照 KB 的泄露链(市场对 knowledge 零依赖 → 上架不 fork → 安装者共享发布者活 dataset → 发布者上架后还能往库里加私有文档 → 安装者整库检索读到私有 chunk见临时-04 §0/事实 1agent 这条链的每一环都不成立:没有"上架后还能改"的活资源,没有"整库返回"的运行时读放大config 本就是公开交付物。结论agent 物化不需要 fork 公开副本、不需要发布侧异步重活、不需要上架侧任何改动——物化全部落在安装侧同步完成(拷 config 是一次本地 DB 写 + 一次跨 BC 读,无外部调用、无长任务)。这是 agent 比 KB 轻得多的根本原因。

事实 7 — 可复用与不需要的 KB 已落资产,钉死本 plan 的真实增量。 可复用(照搬/扩展):①跨 BC 读端口 MarketAssetSourceApi(临时-04 已落,本 plan 它支持 assetType=agent 放行 + 带 config而非新建第二个端口②handoff token 链路 MarketHandoffTokenApiP2 已闭环agent 槽位 handoff 的 expectedTargetOwner=agent 已支持,HandoffVerifyRespDTO.targetOwner/targetAction 已具备③KB 的 materializeInstalledRefKb 物化形态(MuseKnowledgeBindingService:377-417:写本地实体 + 幂等键 (source_market_asset_id, owner_user_id, deleted=false) + 与绑定/投影同 @Transactional——agent 照搬这个形态,把"建 installed_ref KB + dataset binding"换成"建 installed agent + active version + 槽位指本地 agentId"。不需要agent 没有对应物):①MarketKbListedEvent 孪生事件agent 无 fork、无上架侧异步物化全在安装侧同步不引入任何上架事件/消费者);②MarketAssetForkApi 写回端口(那是 fork 状态投影回 market 用的agent 不 forkKnowledgeMarketForkService/消费者/兜底 worker 那一整套发布侧异步管线agent 全免)。结论agent 的真实增量 = MarketAssetSourceApi 带 configA-source+ 安装侧建 installed agent 拷 configA-materialize+ 改运行时授权门A-runtime+ 命名收口A-naming+ 验证A-verify,没有任何 fork/异步/外部资源工程。

事实 8 — 命名漂移须收口O3 开放项)。 后端-04/临时-02 §3.2 文档里写的是 market_installedP2 落地的槽位绑定来源标记是 marketMuseAgentSlotServiceImplBINDING_SOURCE_MARKET / SOURCE_TYPE_MARKET_AGENTKB 侧用的是 installed_ref;本 plan 拟为 agent 新增的 agent_type 值待定(候选 installed / market_installed / installed_ref)。结论A-naming 必须在落地前把"安装物化 agent 的 agent_type 取值"统一拍定一个值,并同步 后端-04 文档、消除文档与实现漂移(事实 3 的 V27 注释已埋"对齐 knowledge source_market_asset_id"的伏笔,命名也应与 knowledge 风格一致或显式说明差异)。

这八条把 agent 物化的真实成本钉死:核心增量 = config 跨 BC 读取途径(事实 5决策点 D1+ 安装侧建 installed agent 拷 config事实 4/7+ 改运行时授权门(事实 1trust boundary+ 命名收口(事实 8KB 那套 fork 公开副本 / 发布侧异步管线 / 上架事件 / 副本资源回收(临时-04 的核心增量),在 agent 下全部不需要(事实 6/7


2. 总览:实现单元、数据流与授权门改造对比

2.1 实现单元依赖图

flowchart TD
    ASRC["A-source扩跨 BC 读端口带 config核心新读路径<br/>MarketAssetSourceApi 放行 assetType=agent<br/>+ 带回发布者 active 版本 config决策点 D1经 market 反调 ai vs ai 自有读)"]
    AMAT["A-materialize安装侧建 installed agent 拷 config<br/>建本地 muse_agent(installed 型)+ active muse_agent_version(config 拷自发布者)<br/>+ DO 映射 source_market_asset_id + 幂等 + 槽位指本地 agentId去裸引用"]
    ART["A-runtime改运行时授权门trust boundary<br/>requireVisibleAgent 放行 installed 型 + 本人所有 + 授权快照<br/>三入口(220/308/315)同时通"]
    ANAME["A-naming命名收口<br/>installed agent 的 agent_type 取值统一<br/>+ 同步 后端-04 文档"]
    AVER["A-verify验证<br/>真 PG ITinstall→物化→bind→runtime 跑 AI 任务命中物化 agent<br/>+ studio e2e + 授权门负路(他人 installed agent 必拒)"]

    ASRC --> AMAT
    AMAT --> ART
    ANAME -.贯穿命名.-> AMAT
    ANAME -.贯穿命名.-> ART
    ART --> AVER

    DEC{"前置决策门人类拍板§3<br/>D1 config 读取途径market 反调 ai vs ai 自有读)<br/>D2 拷 vs 引用 prompt 库<br/>D3 agent_type 命名最终值<br/>D4 installed agent 是否计配额/列表可见"}
    DEC --> ASRC

    style ASRC fill:#fde2e2,stroke:#c0392b,stroke-width:3px
    style ART fill:#fde2e2,stroke:#c0392b,stroke-width:3px
    style AMAT fill:#e2f7e8,stroke:#27ae60
    style ANAME fill:#e2f7e8,stroke:#27ae60
    style AVER fill:#fff6d6,stroke:#b8860b
    style DEC fill:#eef3fb,stroke:#3b6fb6,stroke-width:2px

2.2 拷配置 vs KB fork为什么 agent 不需要 fork

flowchart LR
    subgraph PUB["发布者域owner = 发布者)"]
        PAGENT["发布者 muse_agent + active muse_agent_version<br/>configpromptTemplate/slotBindings/toolGrantIds<br/>🔒 active 即不可变、不会再变"]
    end
    subgraph INS["安装者域owner = 安装者)"]
        IAGENT["installed 型 muse_agent本人所有<br/>source_market_asset_id=assetId"]
        IVER["active muse_agent_version<br/>config = 整体拷自发布者快照"]
    end
    PAGENT ==>|安装侧同步拷 config<br/>无 fork·无外部资源·无异步| IVER
    IAGENT --> IVER
    IVER --> RT["运行时 requireVisibleAgent<br/>放行 installed 型 + 本人所有 + 授权有效<br/>→ 从本地 config 取 modelKey/promptTemplate<br/>🟢 命中物化 agentAI 任务可跑"]

    NOTE["对照 KBKB 必须 fork 一份只含公开快照的独立 dataset<br/>(因发布者活 dataset 上架后还能加私有文档→整库检索泄露)<br/>agent 无此向量→config 即商品本体、拷下即交付"]

    style PAGENT fill:#fde2e2,stroke:#c0392b
    style IVER fill:#e2f7e8,stroke:#27ae60,stroke-width:2px
    style RT fill:#e2f7e8,stroke:#27ae60
    style NOTE fill:#eef3fb,stroke:#3b6fb6

2.3 安装侧物化 + 运行时数据流agent 物化后)

flowchart LR
    A["用户跳转→ai 槽位绑定路径<br/>precheckAgentSlot / bindAgentSlot"]
    A --> B["消费 handoff token原有闸 A 已放宽 market 型,不动)"]
    B --> C["A-source: MarketAssetSourceApi.getAssetSource(assetId)<br/>assetType=agent 放行 + 带回发布者 active 版本 configD1"]
    C --> D{资产存在且=agent?}
    D -->|否| E["fail-closed: 拒绝绑定0 写"]
    D -->|是| F["A-materialize: 建本地 muse_agent(installed 型)<br/>source_market_asset_id=assetId, owner=安装者, status=active"]
    F --> G["建 active muse_agent_version<br/>config=整体拷自发布者快照(保完整性)"]
    F --> H["槽位 binding.agentId=本地 installed agentId去裸引用不再指发布者 agentId"]
    G --> I["A-runtime: 运行时 requireVisibleAgent(本地 agentId)<br/>放行 installed 型 + 本人所有 + 授权快照<br/>→ 取本地 config → 🟢 命中物化 agent"]
    H --> I

    style C fill:#fff6d6,stroke:#b8860b
    style F fill:#e2f7e8,stroke:#27ae60
    style G fill:#e2f7e8,stroke:#27ae60
    style I fill:#e2f7e8,stroke:#27ae60
    style E fill:#fde2e2,stroke:#c0392b

2.4 授权门改造前后对比A-runtime · trust boundary

flowchart TD
    subgraph BEFORE["改造前 requireVisibleAgent:329-331断点②"]
        B1["agentType=system"] -->|否| B2["agentType=user 且 owner==本人?"]
        B2 -->|否| BX["🔴 AI_AGENT_SCOPE_FORBIDDEN<br/>market/installed 一律拒"]
        B1 -->|是| BOK["放行"]
        B2 -->|是| BOK
    end
    subgraph AFTER["改造后A-runtime 放行 installed"]
        A1["agentType=system"] -->|否| A2["agentType=user 且 owner==本人?"]
        A2 -->|否| A3["agentType=installed 且 owner==本人<br/>且授权快照有效?"]
        A3 -->|否| AX["🔴 SCOPE_FORBIDDEN<br/>(他人 installed / 无授权 仍拒——负路测命门)"]
        A3 -->|是| AOK["🟢 放行 installed"]
        A1 -->|是| AOK
        A2 -->|是| AOK
    end

    style BX fill:#fde2e2,stroke:#c0392b
    style AX fill:#fde2e2,stroke:#c0392b
    style AOK fill:#e2f7e8,stroke:#27ae60,stroke-width:2px

3. 实现单元

文件路径均为 repo 相对路径。每个单元含 Goal / Files / Approach / Test scenarios输入+预期)/ Verification。测试场景"输入"指具体调用入参或数据状态,"预期"指可断言的可观测结果。

A-source · 扩跨 BC 读端口带 config + 放行 assetType=agent核心新读路径

Goal:让安装侧能跨 BC 拿到"市场 agent 资产的来源 + 发布者 active 版本的 config 本体",作为拷配置的输入。复用临时-04 已落的 MarketAssetSourceApi它支持 assetType=agent(不再只服务 KB并解决"market 域够不着 ai 域 config"这一核心矛盾(决策点 D1

Files

  • 扩跨 BC 读端口(被 ai 依赖):muse-cloud/muse-module-market/muse-module-market-api/src/main/java/cn/iocoder/muse/module/market/api/asset/MarketAssetSourceApi.java(已存在,扩语义 + 可能新增带 config 的方法或扩 DTO+ .../api/asset/dto/MarketAssetSourceRespDTO.java已存在KB-fork 形状,需为 agent 增 config 字段或拆 agent 专用 DTO见 Approach
  • market 侧实现:muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/api/asset/MarketAssetSourceApiImpl.java(已存在,读 MuseMarketAssetDO 的 source_id/asset_type/publisher_idagent config 的来源见 D1
  • D1 选 Amarket 反调 ai 读端口)时新增ai 侧对外读端口 muse-cloud/muse-module-ai/muse-module-ai-api/src/main/java/cn/iocoder/muse/module/ai/api/agent/AgentConfigSourceApi.java(新)+ DTO{exists, agentType, status, activeVersion, config, ownerUserId}market 注入它读发布者 active 版本 config范式同 MarketHandoffTokenApi(本地 Bean、不挂 Feign
  • D1 选 Bai 自有读)时:不扩 market改在 ai 绑定路径内由 ai 自己读发布者 muse_agent_version(同域读自有 .dal 合规),但需 ai 凭 assetId→发布者 agentId 的映射(仍要 market 回 source_id故仍需 MarketAssetSourceApi 回 sourceId只是 config 由 ai 自取)。
  • 复用范本:MarketAssetSourceApiImplKB 已落的跨 BC 读 + tags 解析写法)、MarketHandoffTokenApi(本地进程内 Bean 端口范式)。

Approach

① DTO 形状(与 KB 共存的取舍):现有 MarketAssetSourceRespDTO 是 KB-fork 形状(带 publicForkDatasetId/forkStatus,对 agent 无意义)。两种处置:(a) 给现有 DTO 增 agent 专属字段agentConfig/agentActiveVersionKB 字段对 agent 留 null、反之亦然——一个 DTO 承两类资产,简单但字段半数恒 null(b) 按 assetType 拆 agent 专用 DTO/方法getAgentAssetSource(assetId)→MarketAgentSourceRespDTO{exists, sourceId, publisherUserId, agentName, agentConfig, activeVersion}),干净但端口多一个方法。倾向 (b)接口隔离KB/agent 各取所需,与临时-04 注释里"读写分离、避免互相依赖用不到的方法"的设计取向一致);最终由实现时定夺并回填。

② assetType=agent 放行:现有 getAssetSource 对所有 assetType 一视同仁返回 KB 形状;扩为按 asset.getAssetType() 分流——agent 类资产走 agent 路径(带 configknowledge_base 维持现状(带副本)。安装侧据 assetType fail-closedagent 绑定路径只接纳 assetType=agentKB 绑定路径只接纳 knowledge_base(防止把 KB 当 agent 物化或反之)。

③ config 读取途径(决策点 D1核心给推荐 + 理由)——倾向"market 反调 ai 读端口带回 config选 A"

  • 选项 A推荐market 反调 ai 读端口。新增 ai 侧 AgentConfigSourceApiai 域读自有 muse_agent_version 合规market 的 MarketAssetSourceApiImpl 注入它,凭 source_id(发布者 agentId读发布者 active 版本 config组装进返回 DTO 一并带给安装侧。
    • 优点:①安装侧只跟一个端口打交道MarketAssetSourceApi),与 KB 物化的调用形态完全对称(安装侧"一次跨 BC 读拿全物化所需信息"②config 读取的 BC 合规性由"ai 读自有 + market→ai 经 -api 端口"双重保证③market 作为资产来源的权威出口,"按 assetId 给来源全貌"语义自洽。
    • 代价:引入 market→ai 的新依赖方向(当前 ai→market 已有 MarketHandoffTokenApi/MarketAssetSourceApi 依赖market→ai 是新方向,需 ArchUnit 确认不成环——market 与 ai 同进程聚合、经 -api 端口不成循环依赖,但需 review
  • 选项 Bai 绑定路径自己读发布者 agentMarketAssetSourceApi 仍只回 source_id发布者 agentIdai 绑定路径凭它读发布者 muse_agent_version(同域读自有)。
    • 优点:不引入 market→ai 新依赖。
    • 代价:①跨 owner 读——ai 绑定路径以安装者身份读发布者租户的 agent 版本,要绕过/显式处理租户拦截器(类似临时-03 被 D0-fork 消解掉的"跨 owner 读发布者 dataset"难题,在 agent 这里若选 B 会重新出现②config 读取逻辑散在绑定路径、不如端口聚合清晰。
  • 权衡结论:选 A 让安装侧调用对称、避免跨 owner 读难题B 的真痛点代价market→ai 新方向)经 -api 端口 + ArchUnit 可控。推荐 A;若 review 认为 market→ai 依赖方向不可接受,退 B 并显式处理跨 owner 读A-materialize ⑤ 标注)。

④ 凭据/审计:跨 BC 读 config 不涉及外部服务、无 key但 config 可能含 promptTemplate 正文,端口日志不打印 config 内容(只记 assetId/agentType/版本号),避免商品本体进日志。

Test scenarios输入+预期)

  • agent 资产来源 + config 带回输入assetId=Xasset_type=agentsource_id=发布者 agentId=GG 有 active version v1 config={name,promptTemplate,modelKey,...}),调 getAgentAssetSource(X)|预期:返回 {exists=true, assetType=agent, sourceId=G, publisherUserId, agentConfig=v1 的完整 config, activeVersion=v1}config 各键齐全(含 modelKey/promptTemplate
  • 非 agent 资产输入assetId 指向 knowledge_base 类型预期agent 读路径 fail-closedexists=false 或显式 assetType≠agent安装侧据此拒绝按 agent 物化)。
  • 资产不存在|输入:错 assetId/跨租户|预期:notFound()exists=false安装侧 fail-closed。
  • 发布者无 active 版本输入G 存在但无 active muse_agent_version预期带回 config=null / activeVersion=null安装侧 fail-closed不物化出一个无 config 的空 agent
  • BC 边界输入ArchUnit BcBoundaryArchTest|预期:选 A 时 market→ai 经 -api 端口、不直读 ai .dal且不成循环依赖test 绿。

Verification

  • 模块单测:cd muse-cloud && mvn -pl muse-module-market/muse-module-market-server -am test -Dtest='MarketAssetSourceApiImplTest'mock ai 读端口,覆盖 agent 分流 + config 带回 + 非 agent/不存在 fail-closed
  • 改了 market-api / ai-api 模块务必先 mvn -pl muse-module-market/muse-module-market-api -am install(及 ai-api 再跑依赖模块测试,否则 stale jar 假红memory muse-market-install-isacquired-enrich 教训)。
  • ArchUnitmvn -pl ... test -Dtest=BcBoundaryArchTest(确认 market→ai 不成环)。

A-materialize · 安装侧建 installed agent 拷 config照搬 KB materializeInstalledRefKb 形态)

Goal:在 ai 目标域绑定路径里,把"槽位写裸引用"升级为"建出可用的本地 installed 型 agent + active versionconfig 拷自发布者)+ 槽位指向本地 agentId"。做到 MuseAgentDO 补映射 source_market_asset_id + 幂等 + config 整体拷贝 + 去裸引用。与 KB U-materialize 同范式、更轻(无 dataset、无 fork、无外部调用

Files

  • DO 补映射(事实 3muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/dataobject/muse/MuseAgentDO.java(加 private Long sourceMarketAssetId; + @TableField列 V27 已在、不需新迁移)
  • 主改:muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAgentSlotServiceImpl.javaprecheckAgentSlot:93bindAgentSlot:158consumeMarketHandoff:350、绑定持久化 upsert :250
  • installed agent 写入 DAL.../ai/dal/mysql/muse/MuseAgentMapper.java(复用 insert + 按 source_market_asset_id 幂等查)、MuseAgentVersionMapper.java(复用 insert active version
  • 参照范本(照搬物化形态):muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeBindingService.javamaterializeInstalledRefKb:377-417:幂等键 (source_market_asset_id, owner_user_id, deleted=false) + 建本地实体 + 与绑定同 @Transactional§128-139 precheck 固化物化所需事实、bind 阶段读回建实体的两段式)

Approach

  1. DO 补映射MuseAgentDOsourceMarketAssetId@TableField 普通列V27 已建 source_market_asset_id BIGINT)。这是物化溯源 + 幂等键的载体,唯一的 schema 触动且不需新迁移(事实 3
  2. precheck 固化 + assetType 校验(照搬 KB 两段式)precheckAgentSlot 对 market 来源(source_owner=market,事实 2调 A-source 的 getAgentAssetSource(assetId) 校验"资产存在且 assetType=agent 且发布者有 active 版本 config"fail-closed不存在/非 agent/无 config 拒),并把"发布者 agentId + 待拷 config + 授权快照"固化进 precheck 快照供 bind 读回(避免 bind 时再跨 BC 读)。precheck 不建任何 agent 实体(避免 precheck 失败留孤儿 agent 行,与 KB materializeInstalledRefKb 注释同理)。
  3. bind 阶段建 installed agent落点bindAgentSlot 同一 @Transactional
    • insert muse_agentagent_type=<installed 取值A-naming 定>source_market_asset_id=assetIdowner_user_id=安装者status=activename/description/prompt_key/category 取自拷来的 config、agent_key=<安装者维度唯一>注意 uk_muse_agent_key=(tenant_id, agent_key),事实 3installed agent 的 agent_key 须保证安装者租户内唯一,建议 installed-{assetId}-{ownerUserId} 或带 uuid避免与发布者/其他 installed agent 撞键);拿到本地新建 agentId
    • insert muse_agent_versionagent_id=本地agentIdversion=1(或拷发布者版本号)、config=整体拷自发布者 active 版本 configstatus=activeconfig 整体拷贝保完整性R3拿到 versionId 回写 muse_agent.current_version_id
    • 去裸引用:槽位 binding 的 agentId本地新建 agentId(不再是发布者 agentIdagentVersion 落本地版本号;建实体放 bindAgentSlot(用户确认绑定的写事实点)。
  4. 幂等(照搬 KB:建 installed agent 前先 SELECT ... WHERE source_market_asset_id=assetId AND owner_user_id=安装者 AND agent_type=<installed> AND deleted=false,命中则复用既有 installed agentId连同其 active version不重复建并发由 uk_muse_agent_key 唯一键 + DataIntegrityViolationException→回读模式兜(仿 KB persistDatasetBinding 的回读幂等)。这保证同一安装者对同一资产多次绑定只有一行 installed agent。
  5. 授权快照承载installed agent 的授权溯源沿用 ADR-020 字符串 envelope槽位 binding 的 authorization_snapshot_id 已 V31 VARCHAR 直透传(事实见 MuseAgentSlotServiceImpl:281-282),不回退 BIGINT/parseLong。物化 agent 实体若需携带授权快照,沿用 VARCHARD4 决策)。
  6. 事务原子性:物化(建 agent + version与"消费 handoff token + 写槽位绑定"在同一 @Transactional(现状 bindAgentSlot:157/consumeMarketHandoff:348 已沿用调用方事务),失败整体回滚。
  7. 多安装者各建各的:不同安装者各建自己的 installed agent各自 owner + 各自 agent_keyconfig 各拷一份agent config 是轻量 JSONB不像 KB dataset 那样有共享必要;agent 无"副本仅 1 份"概念——这是与 KB 的又一差异KB 副本 dataset 重、N 安装者共享只读一份agent config 轻、各拷各的最简单且无泄露顾虑)。

Test scenarios输入+预期)

  • 首次安装绑定输入T_A 安装 assetId=Xasset_type=agent发布者 active config 完整),bindAgentSlot(合法 handoff token + precheckId + slotKey预期新建 muse_agent(agent_type=installed, source_market_asset_id=X, owner_user_id=A, status=active) 记 id=G2新建 muse_agent_version(agent_id=G2, status=active, config=X 的完整 config)muse_agent.current_version_id 指向该 version槽位 binding.agentId=G2(非发布者 agentId
  • config 完整性|输入:发布者 config 含 {name, description, promptKey, promptTemplate, slotBindings, toolGrantIds, modelKey, promptTemplateVersion}|预期:拷出的本地 version config 逐键齐全(运行时取 modelKey/promptTemplate 不落缺省、不丢 toolGrantIds
  • 多安装者各建各的输入A、B 各装 assetId=X预期建两行 installed agentG2、G3各自 owner + 各自唯一 agent_key各有自己的 active versionconfig 各一份),不违反 uk_muse_agent_key
  • 幂等-重复绑定|输入:同一 (A, X) 再次 bind预期不新建第二行 installed agent复用 G2不抛 500。
  • 资产不存在/非 agent输入assetId 指向不存在 / knowledge_base 类型预期fail-closed 拒绝0 写 agent/version/binding。
  • agent_key 唯一键不撞输入A 装 X、又装另一 agent 资产 Y预期两行 installed agent 的 agent_key 互不相同(按 assetId 维度),不触发 uk_muse_agent_key 冲突。
  • BC 边界输入ArchUnit BcBoundaryArchTest预期ai 经 MarketAssetSourceApi 读、不直读 market .daltest 绿。

Verification

  • 模块单测:cd muse-cloud && mvn -pl muse-module-ai/muse-module-ai-server -am test -Dtest='MuseAgentSlotServiceTest'mock MarketAssetSourceApi,覆盖建 installed agent + config 完整拷贝 + 幂等 + 多安装者 + fail-closed
  • 改了 market-api/ai-api 先 mvn -pl ...-api -am install 再跑 server 模块测试stale jar 假红教训)。
  • ArchUnitmvn -pl ... test -Dtest=BcBoundaryArchTest

A-runtime · 改运行时授权门放行 installed 型trust boundary本 plan 最需严防处)

Goal:把运行时授权门 requireVisibleAgent 从"只放行 system / 本人 user"扩展为"+ 本人所有的 installed 型 + 授权快照有效"使三入口agentTest/agentOverrideRef/agentSlotKey对物化出的 installed agent 同时放行;同时严守边界——他人的 installed agent、无授权的 installed agent 仍必须拒(这是 trust boundary负路测是验收命门

Files

  • 主改(唯一断点):muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiTaskServiceImpl.javarequireVisibleAgent:321-342,可见性判定 :329-331;三入口 :220/:308/:315 不改、自动随门放行)
  • 不改:闸 A MuseAgentSlotServiceImpl.requireVisibleActiveSourceAgent(事实 2已放宽、与运行时是两套门运行时是本单元唯一战场

Approach

  1. 放行条件扩展(精确改 :329-331:原判定"非 system 且 非(user 且本人)→拒"扩为"非 system 且 非(user 且本人) 且 非(installed 且本人 且授权有效)→拒"。即新增一条放行支:agent_type=<installed 取值> 且 ownerUserId==agent.ownerUserId 且 授权快照有效三个收紧点缺一不可:①必须是 installed 型(不放开 market 裸引用——物化后槽位指的是本地 installed agent不存在裸 market 引用进运行时);②必须本人所有(ownerUserId 比对,防越权用他人 installed agent③授权快照有效防授权已撤销/过期仍可跑)。
  2. 授权有效性判定installed agent 行的 source_market_asset_id + 授权快照ADR-020 VARCHAR envelope钉死"凭哪份授权可用"。运行时校验授权快照有效(非 blocked: 前缀等,参照闸 A rejectBlockedSource:326-330 的 envelope 语义),授权失效则拒(与召回/下架回滚开放项联动§5
  3. 三入口自动覆盖:220/:308/:315 都调 requireVisibleAgent,改门体即三入口同时生效,无需逐入口改(与 KB 检索"改数据正确即第四门自然通"异曲同工——这里是"改门一处即三入口通")。
  4. status/version 两道维持:326-327agent.status=active+ :333-336active version 存在不放松installed agent 物化时已写 activeA-materialize自然满足。

Test scenarios输入+预期)

  • 本人 installed agent 放行正路输入A 物化的 installed agent G2active + active version + 授权有效A 发起 AI 任务命中 G2经 slotKey/override/agentTest 任一入口)|预期:requireVisibleAgent 放行,返回 AgentRuntimeRef(从 G2 active version config 取 modelKey/promptTemplate不抛 SCOPE_FORBIDDEN。
  • 他人 installed agent 必拒负路trust boundary 命门)输入A 物化的 installed agent G2B 试图用 G2伪造 agentOverrideRef 或越权 slotKey预期AI_AGENT_SCOPE_FORBIDDENownerUserId 不符0 放行——这是本 plan 安全验收的核心断言
  • 授权失效的 installed agent 必拒负路输入A 的 installed agent G2 但授权快照已 blocked/撤销|预期:拒(授权无效支),不放行。
  • market 裸引用仍拒(负路,防回归)|输入:直接用一个未物化的 market 出身 agentId非 installed 型、非本人 user预期仍抛 SCOPE_FORBIDDEN只放行 installed 型本地实体,不放开 market 型本身)。
  • system/本人 user 不回归输入system agent / 本人 user agent预期照旧放行原有行为不变
  • 三入口一致|输入:同一 installed agent 分别经 agentTest(:220)/override(:308)/slotKey(:315)|预期:三入口行为一致(都放行本人 installed、都拒他人 installed

Verification

  • 单测:mvn -pl muse-module-ai/muse-module-ai-server -am test -Dtest='MuseAiTaskServiceTest'(覆盖正路放行 + 他人 installed 必拒 + 授权失效必拒 + market 裸引用必拒 + system/user 不回归 + 三入口一致)。负路测是本单元验收门,缺负路不算通过
  • 活体并入 A-verify 端到端(真跑 AI 任务命中物化 agent + 真验他人不可用)。

A-naming · 命名收口(贯穿 A-materialize / A-runtime

Goal:把"安装物化 agent 的 agent_type 取值"统一拍定一个值,消除文档(market_installed/ P2 落地(market/ KB 风格(installed_ref/ 本 plan 拟新增(installed)的四处漂移(事实 8并同步 后端-04

Files

  • 文档同步:design-docs/后端-04-统一数据库Schema-v1.md§7 AI/Agentmuse_agent.agent_type 取值定义;当前写 system/user/market_installed,按拍定值订正)
  • 落地一致性A-materialize 写入的 agent_type 值 + A-runtime 放行判定的 agent_type 值必须用同一常量(建议 ai 模块内定义常量,避免散字符串漂移)

Approach

  1. 取值候选与倾向installed(简洁、与 KB installed_ref 风格呼应但更短)/ market_installed(与 后端-04 现有文档一致、语义最明确)/ installed_ref(完全对齐 KB倾向 market_installed(与 后端-04 既有文档零漂移、语义自明"市场安装来的",且 V27 注释已写"对齐 knowledge source_market_asset_id"暗示与 knowledge 风格协调即可、不强求同名最终由人类拍D3
  2. 单一来源:拍定后 A-materialize/A-runtime 共用一个常量,后端-04 同步订正,避免再次漂移。
  3. BINDING_SOURCE_MARKET 区分:注意 agent_typeagent 实体类型)与槽位 binding 的 binding_source=market(绑定来源标记,事实 2是两个维度不要混淆——物化后 binding_source 仍可标 market来源是市场但 binding 指向的 agent 其 agent_type 是 installed实体是本地物化的。A-naming 须在文档里点明这一区分,防后续 agent 误把两者当一回事。

Test scenarios输入+预期)

  • 取值一致性输入grep agent_type 写入点与放行判定点|预期:用同一常量、无散字符串。
  • 文档零漂移|输入:后端-04 §7 agent_type 取值 vs 代码常量|预期:一致。

Verification:人工核对 后端-04 与代码常量一致A-materialize/A-runtime 单测断言用同一常量值。


A-verify · 验证(真 PG 端到端 + studio e2e + 授权门负路)

Goal:用真实证据证明"安装 agent→物化 installed agent→槽位绑定→运行时跑 AI 任务命中物化 agent"端到端可真用;且授权门负路成立(他人 installed agent / 无授权必拒——trust boundary 的真验收)。

Files

  • 真 PG ITmuse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketAgentMaterializationIT.java(参照 KB 的 P1rMarketKbForkMaterializationIT + 既有 P1r*IT live 范式)
  • studio e2emuse-studio/e2e/market-install-agent-runtime.spec.ts(参照 agent-slot.spec.ts + market-install.spec.ts,及 KB 的 market-install-kb-retrieval.spec.ts 范式)
  • 单测(已在各单元):MuseAiTaskServiceTest(授权门负路)/ MuseAgentSlotServiceTest(物化)/ MarketAssetSourceApiImplTest(带 config

Approach / Test scenarios输入+预期)

  1. install→物化→bind→runtime 端到端(真 PG|输入:发布者建 agentactive versionconfig 完整)→ admin 审核上架成 market agent 资产 → 用户安装该资产 → 槽位绑定(物化 installed agent + version + 槽位指本地 agentId→ 发起 AI 任务命中该槽位|预期:运行时 requireVisibleAgent 放行物化 installed agent → 真 New-API 出候选AI 外部依赖在 mini-infra 100.64.0.8 在线memory muse-ai-external-deps-online);对照现状(必 AI_AGENT_SCOPE_FORBIDDEN)。
  2. 授权门负路trust boundary 核心验证)输入A 物化 installed agent G2B 试图用 G2 跑 AI 任务(伪造 agentOverrideRef预期AI_AGENT_SCOPE_FORBIDDENB 拿不到 G2——这是 agent plan 安全验收的命门,缺此不算通过
  3. config 完整性真验|输入:发布者 config 含特定 promptTemplate/modelKey/toolGrantIds预期物化 agent 真跑时用的是拷来的 config候选反映发布者 prompt 行为、modelKey 生效),不落缺省、不丢 toolGrantIds。
  4. 幂等|输入:重复 install + 重复 bind预期DB 中该安装者该资产仅一行 installed agent + 一行 active version。
  5. 多安装者各跑各的输入A、B 各装同一 agent 资产|预期:各自 installed agentconfig 各一份),各自跑 AI 任务互不影响、互不可见对方实体。
  6. 召回/下架回滚(开放项,照搬 KB 的诚实标注)|输入:发布者 agent 资产被 admin 召回/下架|预期:已物化 installed agent 被阻断新使用授权失效→A-runtime 授权支拒)。开放项market 召回当前对目标 owner 传播是 fail-closed blocked 记录式(与 KB 同源缺口,临时-04 事实 7ai 侧消费"召回→停用 installed agent"的入口本期缺——A-verify 须如实验证现状传播是否触达 installed agent若不触达标开放项与 KB 召回回滚同一未触达面§5不假装闭环。
  7. 机械门禁输入ArchUnit BcBoundaryArchTest + 覆盖 JSON 门|预期:物化经 ai facade + MarketAssetSourceApimarket 不直写 ai .dalmarket→ai若 D1 选 A不成环test 绿。

Verification

  • 真 PG ITmuse-cloud/ 跑,按 memory muse-p1r-it-run-recipep1r.flyway.* argLine + 密码经 env~/.config/muse-repo/infra.envset -a && . it && set +aonlineNew-API 在 mini-infra 在线)。_test 库铁律IT 的 flyway 目标库绝不指 muse_slice_live。
  • studio e2e起全栈 appPG 宿主 100.64.0.8 在线时),npx playwright test market-install-agent-runtime.spec.ts
  • 凭据红线:所有脚本/日志过滤 password|secret|token|sk-;不打印 config promptTemplate 正文;不 rm dump.rdb/lefthook

4. 决策点(待人类拍板,按优先级)

# 决策点 选项 倾向 阻塞谁
D1 config 跨 BC 读取途径(核心) MarketAssetSourceApi 让 market 反调 ai 读端口带回 config / ai 绑定路径自己读发布者 agent market 反调 ai选 A:安装侧调用对称 + 避免跨 owner 读难题代价market→ai 新方向)经 -api 端口+ArchUnit 可控。若 review 否决 market→ai 方向则退 B 并显式处理跨 owner 读 A-source 全部
D2 拷 vs 引用 prompt 库 config 整体拷贝(含 promptTemplate 正文快照)/ 引用发布者 prompt 库promptKey 指回) 整体拷贝agent 无泄露向量、config 即商品本体、版本不可变→拷下即冻结快照,与"不 fork、拷配置"拍板一致;引用会重新引入"发布者改 prompt 库影响安装者"的漂移) A-materialize config 写法
D3 installed agent 的 agent_type 命名最终值 market_installed / installed / installed_ref market_installed(与 后端-04 既有文档零漂移、语义自明;与 knowledge 风格协调即可不强求同名) A-naming / A-materialize / A-runtime
D4 installed agent 授权快照承载 数值主键 / 字符串 envelopeVARCHAR 字符串 envelope(沿用 ADR-020槽位 binding 已 V31 VARCHAR物化实体一致 A-materialize / A-runtime
D5 installed agent 是否计用户配额 / 列表可见性 计入用户 agent 配额并在"我的智能体"列表可见 / 不计配额、列表单独分区或隐藏 倾向列表可见但标 installed 来源、是否计配额随产品定(不阻断本 plan 核心链路,可后置;但需 A-materialize 明确 installed agent 的列表查询是否纳入,避免"装了看不到"或"污染自建列表" 列表/配额读模型(非核心链路)
D6 DTO 形状 现有 MarketAssetSourceRespDTO 增 agent 字段 / 拆 agent 专用 DTO+方法 拆 agent 专用接口隔离KB/agent 各取所需;与临时-04 读写分离取向一致) A-source DTO
D7 召回/下架回滚自动化 复用现有 market→目标 owner 传播 / 本期标开放项 与 KB 同一未触达面(临时-04 事实 7/D9倾向本期标开放项,不假装闭环 A-verify

5. 横切开放项(与 KB 同未触达,如实标,不假装闭环)

  • 召回回滚未触达 installed agent与 KB installed_ref 同一开放面)。market 召回/下架资产时对目标 owner 的传播当前是 fail-closed blocked 记录式(临时-04 事实 7writeSourceStatusBlocked errorCode=TARGET_OWNER_UNAVAILABLEai 侧没有"召回→停用 installed agent"的消费入口。本期 agent 物化落地后,这条回滚链与 KB 的 installed_ref 回滚是同一个未补的传播消费面——倾向两者一起补一个 market→knowledge+ai的召回传播消费者统一处置 installed 实体停用),但本期标开放项A-runtime 的"授权快照失效即拒"是兜底(授权撤销路径若与召回联动则部分覆盖,否则纯开放)。
  • e2e fixture vs 真物化。A-verify 的 IT 覆盖后端链路studio e2e 的 global-setup 物化 fixture 是否走真物化(真建 installed agent还是种子数据需与 KB e2e 同步对齐KB 侧 e2e fixture 也是开放项,临时-04 §诚实遗留)——避免 fixture 巧合假绿(临时-02 §1.3.1 注脚那种"种子已存在实体绕过断点"的假绿教训)。
  • installed agent 列表/配额读模型D5。非核心链路,但物化出的 installed agent 在"我的智能体"列表的可见性 + 是否计配额是产品决策,本期标横切待定,不阻断运行时打通。

6. 风险

  • R1最高改授权门 = trust boundary必负路测。A-runtime 放行 installed 型是对运行时权限边界的修改,最大风险是放过头——把"本人 installed"误放成"任意 installed / market 裸引用 / 他人 installed"。缓解放行三收紧点缺一不可installed 型 + 本人所有 + 授权有效§A-runtime ①A-verify 场景 2 + A-runtime 负路测把"他人 installed 必拒 / 授权失效必拒 / market 裸引用必拒"作为验收命门,缺负路不算通过。这是 CLAUDE.md"trust boundary 服务端强制 + 改授权必负路"红线的直接落点。
  • R2 config 跨 BC 读取的依赖方向D1。选 A 引入 market→ai 新依赖方向,需 ArchUnit 确认不成环market 与 ai 同进程聚合、经 -api 端口不构成循环依赖,但要 review 确认)。缓解A-source ArchUnit 门;若 review 否决则退 D1 选 Bai 自读)并显式处理跨 owner 读。
  • R3 config 拷贝完整性。拷 config 若只拷部分键(漏 modelKey/promptTemplate/toolGrantIds运行时取值落缺省或丢工具授权 → 物化 agent 行为与发布者不一致(用户买到"残品")。缓解A-materialize ③ 整体拷贝D2 整体拷)+ A-verify 场景 3 真验 config 完整性(候选反映发布者 prompt 行为 + modelKey 生效 + toolGrantIds 不丢)。
  • R4 命名漂移收口(事实 8agent_type 四处漂移若不收口A-materialize 写入值与 A-runtime 放行值不一致 → 物化了但运行时仍拒(自造断点)。缓解A-naming 拍定单一常量 + 同步 后端-04A-materialize/A-runtime 共用常量。
  • R5 agent_key 唯一键冲突uk_muse_agent_key=(tenant_id, agent_key)(事实 3installed agent 的 agent_key 若按发布者 agent_key 直拷,多安装者/与发布者会撞键 500。缓解A-materialize ③ agent_key 按安装者维度生成(installed-{assetId}-{ownerUserId} 或带 uuid保安装者租户内唯一幂等查走 source_market_asset_id(非 agent_key
  • R6 召回回滚未自动化(开放项,与 KB 同面)。临时-04 事实 7本期标开放§5 / D7不假装闭环A-runtime 授权失效兜底部分覆盖。

7. 回滚(本 plan 执行后如何撤回)

  • 代码回滚A-source 扩端口 + A-materializeMuseAgentSlotServiceImpl 物化 + MuseAgentDO 补映射)+ A-runtimerequireVisibleAgent 放行支)+ A-naming文档。revert 这些 commit 即回到"槽位写裸引用 + 运行时拒 market agent"现状(断点②,功能不可用但与现状一致、不报新错)。
  • 数据回滚:已物化的 installed agent 行 + active version 经软删停用(deleted=true无外部资源需清理agent 不像 KB 有 fork 出的 dataset这是 agent 回滚比 KB 简单处——无 RAGFlow 删 dataset 步骤)。
  • 迁移回滚:本 plan 不引入新 Flyway 迁移source_market_asset_id 列 V27 已在、仅补 DO 映射;授权快照 D4 沿用 V31 VARCHAR无迁移需回滚。

8. Scope 边界(明确不做什么)

  • agent 限定:本 plan 只做智能体agent类资产的物化KB 物化已由临时-04 D0-fork 闭环,不重做。
  • 不 fork、不碰外部资源agent 无泄露向量(事实 6物化 = 安装侧同步拷 config不引入任何 fork / 发布侧异步管线 / 上架事件 / 外部服务调用(与 KB D0-fork 的核心区别)。
  • install 解耦不动install 维持"只记账、targetFactsWritten=false"现状,不在 install 侧物化——物化落点严格在 ai 槽位绑定路径。
  • 闸 A 不动:槽位授权门 requireVisibleActiveSourceAgent 已放宽 market事实 2本 plan 不碰;断点纯在运行时闸 B。
  • handoff 非物化桥handoff token 只负责跳转 + 被目标域消费(MarketHandoffTokenApi.verify/consume 不动)。
  • 副本资源回收不适用agent 无副本 dataset无 KB 那条"下架/无安装清理副本"开放项。
  • 召回/下架回滚自动化不做本期标开放项R6/D7/§5与 KB 同面,不假装闭环。
  • installed agent 列表/配额读模型D5 标横切待定,非核心链路,本期不强制实现。
  • market 两列 BIGINT 议题不纳入muse_market_installation.authorization_snapshot_id/source_snapshot_id 仍 BIGINT 是独立契约议题,本 plan 不处理。

9. 与 KB D0-fork 的异同摘要(一表看清)

维度 KB 物化(临时-04 D0-fork已闭环 Agent 物化(本 plan 临时-05
泄露向量 :发布者活 dataset 上架后还能加私有文档→整库检索泄露 version 不可变 + 运行时不回读私有 prompt + config 即商品本体
物化手段 fork 公开副本(建副本 dataset + 逐文档复制 + 重索引,异步重活) 拷配置(安装侧同步拷 active version config无外部资源、无异步
发布侧工程 有:上架事件 + knowledge fork 消费者 + 兜底 worker + MarketAssetForkApi 写回 :物化全在安装侧同步、上架侧零改动
跨 BC 读端口 MarketAssetSourceApi(带回副本 dataset id + forkStatusmarket 读自有 tags 即可) MarketAssetSourceApi 带 configmarket 域够不着 ai config→需反调 ai 或 ai 自读,决策点 D1
安装侧物化形态 materializeInstalledRefKb:建 installed_ref KB + dataset binding 指副本(副本仅 1 份共享 照搬形态:建 installed 型 agent + active versionconfig 各拷各的、无共享
去污染/去裸引用 kb_id 去污染assetId→本地 kbId 槽位 agentId 去裸引用(发布者 agentId→本地 installed agentId
唯一断点 检索第四门 selectActiveDatasetByKbId(数据正确即通,代码几乎不改) 运行时授权门 requireVisibleAgent:329-331必须改门放行 installedtrust boundary
改授权门 不改(检索门靠数据命中) A-runtime本 plan 最需严防、负路测命门)
schema 触动 无(落 V5/V14 已有列) 仅补 MuseAgentDO 映射 V27 已有列(不需新迁移)
外部资源回滚 有:删 fork 出的副本 dataset :软删本地 agent 行即可
共同开放项 召回回滚未触达 installed_refe2e fixture 待补 召回回滚未触达 installed agent同面e2e fixture 待对齐

10. 关联阅读