Files
pj0235-eai_agentplatform/docs/09_Research/RS04_对象封装与分层完善性考察.md
eaiadmin 90031b75f3 docs: 重构仓库文档目录并迁移训练素材
按当前架构重组 docs 目录,统一中文命名与目录分层,并将训练原材料迁移到独立目录以保持架构文档边界清晰。
2026-09-22 23:23:16 +08:00

30 KiB
Raw Permalink Blame History

对象封装与分层完善性考察

落盘日期:2026-09-19 状态:考察结论(第 1、5 节为判断;第 2–4 节为实测证据;第 6 节为推论,未实施) 目的:从对象封装与层次两个正交视角,考察本项目当前的完善程度,定位结构性缺口的确切位置 关联文档:

  • docs/09_Research/RS03_本体论与语义层专题.md(第 8 节的切口判断;本稿是其代码侧取证,两稿结论互相印证)
  • docs/09_Research/RS02_外部智能体平台架构对照.md(六层的外部对照)
  • docs/02_Architecture/对象标准化与解耦总则.md(本稿第 2 节的判据来源)
  • docs/01_System_Overall/SY22_角色技能应用统一任务架构.md(六层定义,本稿第 3 节的判据来源)
  • docs/02_Architecture/AR09_对象命名规范.md(对象命名规范)

目录

  1. 本稿性质与判据来源
  2. 结论摘要
  3. 思路一:对象封装
  4. 思路二:层次
  5. 两个思路的交点
  6. 完善性总评
  7. 对本项目的推论(未实施)
  8. 未考察 / 存疑项
  9. 证据索引

0. 本稿性质与判据来源

与 RS02 / RS03 的关键区别:本稿不含任何外部检索。

RS02(外部平台对照)与 RS03(本体论专题)的结论依赖公开文档,需要用「高/中/低」可信度分档、需要标注「未查到」。本稿的全部结论来自对本仓库的直接读取与 grep 计数,证据类型不同,因此不适用那套分档 —— 本稿每一处断言都在第 8 节有可复现的命令或代码位置。

唯一的分档区分是「实测」与「判断」:

标记 含义
【实测】 直接 grep / 读文件得到,第 8 节有索引,可复现
【判断】 基于实测的推论,可能被推翻

两个思路的判据来自项目自己的文档,不是外部理论:

  • 封装的判据来自 docs/02_Architecture/对象标准化与解耦总则.md:§2 定义对象为 对象定义 + 绑定关系 + 运行态数据;§4 给出五条解耦原则(原则 1 后端唯一目录源 / 原则 2 页面不能成为数据源 / 原则 3 配置文件不能充当生产目录 / 原则 4 对象定义与用户态分离 / 原则 5 编号系统化);§6 指定绑定关系应显式化为四张关系表。
  • 层次的判据来自 SY22 §4.2–4.6 的六层定义:外部连接层 / 本体与语义上下文层 / 总线与编排层 / 能力层 / 对象层 / 工作台与运行治理层。

本稿的方法论立场:用项目自己写下的标准去量项目自己。如果量出来不合格,那说明标准和实现之间有一段落差 —— 不预设是哪一方的错。


1. 结论摘要

这个项目把「对象是什么」做得很好,把「对象之间怎么动」还没开始做。

1.1 两条思路各自的发现

思路 发现 标记
封装 定义层做到了(表 / 编号 / state / 后端唯一目录源) 【实测】
封装 关系层不是「没建」,是「建成了字符串且没人读」 —— 三种表达形式,读取方分别是 0 / 1(仅UI) / 1(仅UI) 【实测】
封装 正确做法已在同一仓库里(xapp_definition 用真列 + 索引),只是没推广 【实测】
封装 buildActionRecords 前后端各有一个实现;同一对象 3 个生产者 【实测】
封装 运行态层是伪造的(缺省时生成一段看起来像发生过的记录) 【实测】
层次 六层在代码里没有对应物,因此没有任何机制能阻止跨层调用 【实测】
层次 第 1 层越位(持有对象-动作目录)、第 2 层缺位(无动作无权限)、第 4 层空转(治理字段零读取方)、第 6 层越位(前端自推风险与审批) 【实测】
层次 action 没有归属层 —— 五个碎片分散在五层,无一有完整责任 【实测】+【判断】

1.2 两条思路的交点

封装思路说「专员没封装住动作」;层次思路说「动作没有归属层」。这是同一件事的两种说法,且互为因果形成闭环。

详见第 4 节。

1.3 一句话判断

做得好 还没开始
对象定义、编号、状态、目录源唯一 对象之间的关系(真关系而非字符串)
系统目录与用户态分离 运行态的真实记录(而非伪造)
命名与迁移收口(判据干净) 层与层之间的边界
可删除封装有明确目标 动作的责任主体

证据表明产品自己知道下一步是「动」:三个 output 连接器已声明 insert_* / update_*、Mode: "write"、Capabilities: ["write","upsert"],全部是 blueprint(见 §3.2 与 RS03 §8.5)。


2. 思路一:对象封装

2.1 判据

docs/02_Architecture/对象标准化与解耦总则.md §2:一个对象 = 对象定义 + 绑定关系 + 运行态数据。三者必须分层,不能混。

补充判据(来自§4 五条原则):

  • 原则 1:后端是唯一目录源 —— 前端只请求、归一化、渲染
  • 原则 2:页面不能成为数据源
  • 原则 3:配置文件只能承担 fallback / 样式增强 / 本地 demo
  • 原则 4:系统目录 != 用户偏好 != 运行态挂载
  • 原则 5:编号是正式对象属性,不是前端算出的 UI 字符串

2.2 逐对象打分

对象 定义表 编号 关系怎么表达 运行态 判定
应用 xapp_definition ✓ 真列 + 索引(specialist_key / skill_key) user_xapp_center 分离 ✓ 最完整
专员 specialist ✓ 9 个 type:text 字段,技能是分隔符字符串 4 个 *RecordsJSON,缺省时伪造 定义扎实,关系未封
技能 skill_definition ✓ 3 个 JSON 字符串(ActionRefs / PolicyRefs / OntologyBinding) — 关系三个全空转
动作 action_definition ✓ OntologyBindingJSON(字符串)+ ConnectorRef — 有表无运行时
连接器 registry 字面量 — Definition.Actions / Objects — 不是对象,是代码

2.2.1 定义层:做到了

【实测】每个对象都有后端表、有编号(display_code / eailogic_code)、有 state / sort_order、前端只消费。

原则 1(后端唯一目录源)成立,原则 4(系统目录与用户态分离)成立 —— xapp_definition(系统目录)与 user_xapp_center(用户收藏 / 最近 / 自定义)是两张表。

原则 5(编号系统化)成立 —— 专 / EAI-S-、能 / EAI-K-、应 / EAI-A-、连 / EAI-C- 落地为 display_code + eailogic_code 两个真列。

这是本项目最扎实的部分,不应被下文的批评抹掉。

2.3 关系层的真实形态

docs/02_Architecture/对象标准化与解耦总则.md §6 说:

后面不再继续把绑定关系藏在自由文本里。……应该逐步补成显式关系:specialist_skill_binding / xapp_specialist_binding / xapp_skill_binding / specialist_connector_binding

【实测】这四张表全仓各 0 处。但**「四张表没建」不是本节的重点** —— §9 明确说了「先强对象,再补关系」,推迟建表是有意的决策。

重点是:关系不是「没建」,而是已经用另外三种形式建了,而它们都没有读取方。

2.3.1 三种表达形式

形式 实例 字段数 运行时读取方
分隔符文本 specialist.BaseSkills = "事项处理,合同审查",靠 splitText() 运行时切 9 个 有(但只作字符串切分,不作关系)
JSON 字符串(指向别层) ActionRefsJSON / PolicyRefsJSON / OntologyBindingJSON 3 个 0 / 0 / 0
JSON 字符串(自描述记录) ActionRecordsJSON / PermissionRecordsJSON / ResultRecordsJSON / InteractionCardJSON 4 个 有(前端读,见 2.3.3)

2.3.2 指向别层的三个字段,逐个核实

【实测】排除 model / seed / api / validation / admin_handlers 之后的真正运行时读取方:

字段 后端运行时读者 前端读者 实际用途
PolicyRefsJSON 无 无 全仓零读取方
ActionRefsJSON 无 skillCatalog.js:57 把动作 key 渲染成**「步骤 1」「步骤 2」**
OntologyBindingJSON 无 skillCatalog.js:72 把 object_types / action_types 渲染成标签

结论【实测】+【判断】:关系只被用来显示,从不参与执行。

ActionRefsJSON 的渲染目标尤其能说明问题 —— 一个动作引用列表,在 UI 上的最终形态是「步骤 1 / 步骤 2 / 步骤 3」,detail 是动作 key 字符串本身,expectedOutput 为空。

2.3.3 自描述记录的读者是前端

【实测】ActionRecordsJSON(15 处) / ResultRecordsJSON(13 处) / PermissionRecordsJSON(8 处) / InputsRecordsJSON(8 处) 在前端有真实消费。这不是缺陷 —— 后端算好视图模型给前端渲染,是合理做法。

但要注意它们的来源:见 §2.6,这些记录在缺省时是后端生成的。

2.3.4 与前一轮发现的一致性

RS03 §8.5 发现「连接器层持有全平台唯一真实的对象-动作目录」。本节发现「技能/专员层持有三个指向别层的字符串」。

同一个现象的两种尺寸:真实的关系结构长在第 1 层(连接器),而第 2 层该有的关系只剩字符串壳子。

2.4 正确做法已在仓库里

【实测】xapp_definition 绑专员 / 技能用的是真列 + 索引:

SpecialistKey string `gorm:"column:specialist_key;size:64;default:'';index" json:"specialist_key"`
SkillKey      string `gorm:"column:skill_key;size:64;default:'';index" json:"skill_key"`

而 specialist 绑技能用的是:

BaseSkills string `gorm:"type:text" json:"base_skills"`

对比:

xapp_definition → 专员/技能 specialist → 技能
形式 真列 分隔符字符串
可索引 ✓ ✗
可 join ✓ ✗
可加外键约束 ✓ ✗
查询 WHERE specialist_key = ? 全表扫 + 应用层切分

这不是「项目不会做」,是「同一个仓库里两种做法并存,正确的那种没被当成标准」。

该判断与 RS03 §8.5 完全同构 —— 那里是「对象-动作模式写在了连接器契约里」,这里是「真关系写在了应用定义里」。两处都是:正确模式已存在,只是不在它该在的层,也没被推广。

2.5 封装破裂:一个概念三个生产者

「封装」的定义是一个概念只有一处定义、一个权威。以下实测显示恰好相反。

概念 后端实现 前端实现 生产者数
buildActionRecords specialists/runtime/records.go:131 specialists/runtime/specialistFlow.js:165 2
inferRiskLevel records.go:230 StudioPage.vue:1418、MarketPage.vue:1176 3
inferActionType records.go:242 specialistFlow.js:65(getActionType)、MarketPage.vue:1169 3
审批判定 records.go:216(approvalState) MarketPage.vue:1182(shouldRequireApproval) 2

动作记录这一种对象,有 3 个生产者:后端 records.go、前端 specialistFlow.js、前端 MarketPage.vue。

这不是有人偷懒 —— 是因为没有权威的那一处可抄。

【实测】这与 RS03 §8.4.1 找到的「同一概念 5 套词表」是同一件事的不同侧面:词表分叉是封装破裂的症状,封装破裂是词表分叉的原因。

2.6 运行态层是伪造的

docs/02_Architecture/对象标准化与解耦总则.md §2.3 定义运行态为「这次执行过程中发生了什么」。

【实测】records.go 的实际写法是:

if strings.TrimSpace(item.ActionRecordsJSON) == "" {
    item.ActionRecordsJSON = jsonutil.MustJSON(buildActionRecords(item))
}

缺省时生成一段。 生成方式是:

skills := splitText(item.BaseSkills)          // 切分隔符字符串
...
"risk_level": inferRiskLevel(value),          // 从中文名子串猜
"action_type": inferActionType(value),        // 从中文名子串猜
"approval_state": approvalState(item.SpecialistMode, value),

所以这层不是「记录发生了什么」,是「编一段看起来像发生了什么的话」。

【实测】其中 PermissionRecordsJSON 由 buildPermissionRecords 生成,内容为硬编码的四条(资源范围 / 访问边界 / 审批要求 / 禁止动作),与本次执行无关。

一处必须说清的区分:这不等于「这些字段没用」。它们作为初始展示数据是有效的 —— 一个刚创建、还没跑过的专员,UI 需要显示点什么。问题在于它们与「真实运行态」共用同一组字段名,且没有任何标记区分二者。 消费方无法知道读到的是记录还是占位。

2.7 小结

三件 状态
对象定义 做到了
绑定关系 建成了字符串,无读取方(正确做法已在应用对象上示范)
运行态数据 缺省时伪造,且与真实记录共用字段名

3. 思路二:层次

3.1 六层在代码里没有对应物

【实测】internal/ 顶层目录:

ai  api  auth  config  connectors  jsonutil  middleware
model  repository  skills  specialists  store  web  xapps

按技术职责与对象类型划分,不按层划分。 六层的名字(外部连接 / 本体与语义 / 总线与编排 / 能力 / 对象 / 工作台治理)在目录结构中无对应物。

【实测】grep ontology|semantic|bus|orchestrat 在 internal/ 的命中,全部落在 skills/packages/*/manifest.go 与 skill_definition.go —— 没有任何一个是「本体层」或「总线层」的代码模块。

3.1.1 后果(【判断】,但很重要)

分层是逻辑概念,不必一一对应目录 —— 这一点必须先承认。但后果需要说清:

没有任何一层有代码边界,因此没有任何机制能阻止跨层调用。没有边界,就没有「越界」这回事。

这解释了为什么第 2 节与第 3 节找到的所有问题都以同一种形状出现 —— 不是五种独立的疏漏,是同一个原因的五次显形。

3.2 逐层:空位与越位

层 应该管什么(SY22) 实际 判定
1 外部连接 协议适配 持有全平台唯一真实的对象-动作目录;是全仓唯一运行时读对象定义处(task_runtime.go:269) 越位(干了第 2 层的活)
2 本体与语义上下文 业务对象、工作对象与关系 对象有表;关系是字符串;无动作、无权限(SY22 §4.2 原文即不含) 缺位
3 总线与编排 任务总线 / 事件总线 / 能力编排 specialists/runtime 存在,但策略是「AI 还是单连接器」硬编码二选一(ChooseTaskStrategy) 弱
4 能力层 skill / action / policy action_definition 表有、5 个治理字段有、GetByKey 写好 —— 全部零调用方 空转
5 对象层 任务对象、产物 task_record / task_run / task_artifact 相对健康
6 工作台与运行治理 呈现 + 治理 前端自己推动作类型、风险等级、审批判定,自己拼动作记录 越位(干了第 4 层的活)

3.2.1 最锋利的一对

【实测】第 4 层:action_definition 的 RiskLevel / ApprovalMode / AuditLevel / InputSchemaJSON / ExposedToUser 零运行时读取方,ActionDefinitionRepo.GetByKey 写好了且有唯一索引但无调用方。

【实测】第 6 层:前端 MarketPage.vue:1176 / StudioPage.vue:1418 自己实现 inferRiskLevel,MarketPage.vue:1182 自己实现 shouldRequireApproval。

第 4 层不是没实现 —— 是实现完了没有消费方,而消费方在另一层重新实现了。

这比「没实现」难查得多,因为两边看起来都「有」。

3.2.2 第 4 层的运行时风险实际来自哪里

【实测】records.go:230:

func inferRiskLevel(value string) string {
    switch {
    case strings.Contains(value, "发布"), strings.Contains(value, "审批"),
         strings.Contains(value, "同步"), strings.Contains(value, "回写"):
        return "high"
    case strings.Contains(value, "分析"), strings.Contains(value, "生成"),
         strings.Contains(value, "汇总"):
        return "medium"
    default:
        return "low"
    }
}

风险等级由技能的中文名子串决定,而非由第 4 层声明。且动作条目本身也不是从 action_definition 读的,而是从 specialist.BaseSkills 这个分隔符文本字段切出来的(records.go:132)。

3.3 action 没有归属层

这是层次思路的核心发现。

「动作」的碎片 在哪层 有无读取方
声明 第 4 层 action_definition ✗
关系 指向第 2 层 OntologyBindingJSON ✗(仅前端渲染成标签)
治理 第 4 层 RiskLevel / ApprovalMode / AuditLevel ✗
执行实现 第 1 层 Definition.Actions ✗(全仓零次)
展示 第 6 层前端 ✓ 自己重算

五层各持一块碎片,没有任何一层对「动作」负完整责任。

这正是 RS03 §8 那个判断的具体机制:切口切错不是抽象的层次问题 —— 它的表现是 一个对象被五层瓜分,每层只拿到一个没有读取方的碎片。


4. 两个思路的交点

思路 说法
封装 专员没有封装住「动作」
层次 「动作」没有归属层

同一件事的两种说法 —— 而且它们互为因果,形成闭环。

没有归属层
  → 每层各拿一块
  → 没有唯一权威
  → 消费方就地重算(§2.5)
  → 对象永远封不住
  → 没有「这个对象归这层」的边界
  → 层只能是文档上的概念(§3.1)
  → 更没有归属层

修任何一边都会带动另一边。 这一点对实施顺序有直接含义(见 §6)。

4.1 一个反直觉的推论

通常的做法是「先定好分层,再按层实现」。但本项目的证据指向相反的顺序:

分层之所以没落地,不是因为没画清楚(SY22 画得很清楚),而是因为没有任何一个对象真正需要一个层。当对象本身还只是自描述的字面量时,「这一层管什么」是个空问题 —— 什么都可以放任何地方。

【判断】让某一个对象真正横跨完整的生命周期(声明→治理→执行→审计),是让分层变得有意义的必要前提,而不是它的结果。


5. 完善性总评

5.1 做到了的(【实测】)

项 状态
对象定义层:表 / 编号 / state / sort_order / 后端唯一目录源 原则 1 成立
系统目录与用户态分离(xapp_definition vs user_xapp_center) 原则 4 成立
编号系统化(display_code + eailogic_code 两个真列) 原则 5 成立
应用对象的关系用真列 + 索引 做法正确
命名与迁移收口 判据干净(见 更名收尾说明.md:「改名字的地方保留旧名,用名字的地方改新名」)
可删除封装有明确目标(AR10 / AR11「删目录 + 删注册 + 跑卸载」) 目标明确
对象命名有规范性文件(AR09)且与 TOP_CODING_RULES.md G03 配套 规范存在

5.2 没做到的(【实测】)

项 状态
关系层 三种字符串形式,指向别层的三个字段读取方为 0
运行态层 缺省时伪造,且与真实记录共用字段名
层次 无代码边界,无机制阻止跨层调用
动作 五个碎片分散五层,无责任主体
前端兜底目录 前端有静态目录,且在部分路径上优先于后端数据(skillCatalog.js 的 fallback 分支先返回)

5.3 一句话

这个项目把「对象是什么」做得很好,把「对象之间怎么动」还没开始做。

而 §1.3 已述:产品自己已经声明了「动」的那一半(三个 output 连接器、insert_* / update_*、Capabilities: ["write","upsert"]),全部停在 blueprint。


6. 对本项目的推论(未实施)

本节是推论,不是结论。未实施,未验证。

由 §4.1 的反直觉推论出发:

不先修分层,也不先补关系表。先让「一个动作」横跨完整生命周期。

理由(三条,均可从本稿实测推出):

  1. 它同时给动作一个归属层、给对象一个唯一实现 —— §4 的闭环从任一端切开都可以,而横切一刀同时切两端。
  2. 它让第 4 层空转的治理字段第一次有意义 —— 写动作必须回答「谁批准、写哪个对象、记什么审计」,RiskLevel / ApprovalMode / AuditLevel / ConnectorRef 从装饰变成必需。
  3. 它是产品已经画好的路线 —— 三个 output 连接器连写入对象都定义好了(ObjectDefinition.Mode: "write"、FilterHint: "按业务键 upsert"),不是新方向。

候选切口(按风险从低到高):

候选 风险 说明
dingtalk_table_output / feishu_bitable_output / wecom_sheet_output 三选一 低 写自己拥有的外部表格,不碰 ERP 主数据
kingdee(唯一有真实代码包的连接器) 高 写 ERP 主数据;且当前只读,需先确认 K3Cloud 写接口与授权

验收判据(可证伪):

  • Definition.Actions 出现第一个读取方
  • action_definition 的某个治理字段被运行时读取并改变行为
  • 「同一概念 N 套词表」(RS03 §8.4.1)减到 1 套
  • 一个动作的完整链路只有一个实现,不分叉到前端

若上述判据无法达成 —— 那说明本项目实际要做的是只读语义层(RS03 §8.6 的路线 B),此时应改的是命名与文档,而非继续加对象类型。这个判断留给实测结果,不在此预设。


7. 未考察 / 存疑项

7.1 未考察(本轮范围外,不得据此推断)

  1. ai / auth / middleware / web 目录 —— 未审计,本稿结论不覆盖
  2. tasks / 项目 / 知识库相关模型 —— 第 5 层「相对健康」是基于模型命名与结构的印象,未逐字段核实
  3. user_xapp_center 内的 FavoriteKeys / RecentKeys / CustomXApps 三个分隔符字段 —— 已见于模型定义,未核实其读取方与一致性(原则 4 说用户态要分离,已分离;但内部仍是字符串)
  4. AR10 / AR11 的可删除封装 —— 有规范文件,未核实实现是否满足「删目录 + 删注册 + 跑卸载」
  5. 前端静态目录的优先级影响面 —— 已知 skillCatalog.js 的 fallback 分支先于远端返回,未穷举其他 store 是否有同样写法
  6. specialist 的 9 个 type:text 字段中,哪些实际存 JSON 而非分隔符文本 —— splitText() 只用于 BaseSkills / GeneratedSkills / InfoSources,其余 6 个未逐一核实

7.2 存疑(【判断】,可能被推翻)

项 存疑点
§2.6「运行态是伪造的」 伪造的记录作为初始展示数据是否合理,取决于产品意图;本稿只断言「与真实记录共用字段名、无区分标记」,未断言这些字段无用
§3.2 第 3 层评为「弱」 仅基于 ChooseTaskStrategy 的硬编码二选一,未系统审计 specialists/runtime 全部逻辑
§4.1「先横切一个对象,再修分层」 这是推论;存在相反可能 —— 分层先立、对象自然归位。本稿倾向前者,理由见 §4.1,但未验证
§5.2「前端兜底目录优先于后端」 已核实 skillCatalog.js 的一个分支,未确认在生产数据下是否真会走到该分支

7.3 与 RS03 的关系

RS03 本稿
视角 外部标准(Palantir / Fabric / 工业标准)对照 项目自身标准(总则 / SY22)度量
结论 第 2 层与第 4 层切口切错 动作无归属层;关系是字符串;运行态伪造
关系 互相印证 本稿是 RS03 §8 的代码侧取证

RS03 §8.4.1 记载的一处自我更正(初稿误写「没有权限裁决、没有审计」)在本稿 §3.2.1 得到系统性表述:字段齐全,零读取方 —— 比「没有」更麻烦。


8. 证据索引

所有路径相对于仓库根。命令均可在 eai_agentplatform/backend-go 或 eai_agentplatform/frontend/src 下复现。

8.1 对象定义表

位置 事实
backend-go/internal/specialists/model/specialist.go:24-32 9 个 type:text 关系字段:ConnectorScope / PermissionScope / ResourceBindings / InfoSources / BaseSkills / AIAssistance / GeneratedSkills / RuleFileMarkdown / AllowedSkills
backend-go/internal/specialists/model/specialist.go:33-36 4 个 *RecordsJSON:InputsRecordsJSON / PermissionRecordsJSON / ActionRecordsJSON / ResultRecordsJSON
backend-go/internal/skills/model/skill_definition.go:26-28 ActionRefsJSON / PolicyRefsJSON / OntologyBindingJSON
backend-go/internal/xapps/model/model.go:25-26 SpecialistKey / SkillKey —— 真列 + index
backend-go/internal/xapps/model/user_center.go:9-11 FavoriteKeys / RecentKeys / CustomXApps(用户态,分隔符字符串)

8.2 关系字段的读取方

字段 后端运行时 前端 结论
PolicyRefsJSON 0 0 全仓零读取方
ActionRefsJSON 0 skills/store/skillCatalog.js:57 仅渲染「步骤 N」
OntologyBindingJSON 0 skills/store/skillCatalog.js:72 仅渲染标签

复现:grep -rn '<字段名>' --include='*.go' . | grep -v '_test.go' | grep -v '/model/' | grep -v 'seed.go'

8.3 四张提议关系表

specialist_skill_binding / xapp_specialist_binding / xapp_skill_binding / specialist_connector_binding —— 各 0 处(grep -rn '<表名>' --include='*.go' . | wc -l)

8.4 封装破裂:重复实现

概念 后端 前端
buildActionRecords internal/specialists/runtime/records.go:131 frontend/src/specialists/runtime/specialistFlow.js:165
inferRiskLevel records.go:230 frontend/src/views/workbench/StudioPage.vue:1418、MarketPage.vue:1176
inferActionType records.go:242 specialistFlow.js:65(getActionType)、MarketPage.vue:1169
审批判定 records.go:216(approvalState) MarketPage.vue:1182(shouldRequireApproval)

8.5 第 4 层空转

位置 事实
backend-go/internal/model/action_definition.go:15-18 RiskLevel / ApprovalMode / AuditLevel / ExposedToUser 齐全
backend-go/internal/repository/action_definition.go:36-42 GetByKey 写好,key 有唯一索引,无调用方
internal/api/action_definition.go ActionDefinitionRepo 的唯一引用方(后台 CRUD)

8.6 Definition.Actions 零读取方

grep -rn '\.Actions' --include='*.go' . → 无匹配。

连接器 registry 共 33 个连接器定义、上百个动作声明,无一有读取方。

8.7 写连接器全部 blueprint

Key Direction Capabilities 动作 Mode / Status
dingtalk_table_output output ["write","upsert","record"] insert_table_record / update_table_record / append_result_report blueprint / blueprint
feishu_bitable_output output ["write","upsert","record"] insert_bitable_record / update_bitable_record / sync_result_view blueprint / blueprint
wecom_sheet_output output ["write","upsert","record"] insert_sheet_record / update_sheet_record / notify_sheet_owner blueprint / blueprint

三者 ObjectDefinition.Mode 均为 "write",FilterHint 均描述 upsert 语义。

8.8 连接器层只有读动词

internal/connectors/registry/registry.go 导出:ListDefinitions / GetDefinition / Query —— 唯一数据动词是 Query。

internal/connectors/packages/ 下只有 kingdee 一个真实代码包,其 Capabilities: ["read","query","schema_hint"],5 个动作全为 fetch_* / query_*。

注:kingdee.go:299 的 postJSON(http.MethodPost)是 K3Cloud WebAPI 的查询传输方式,非写操作。

8.9 六层在代码中无对应物

ls internal/ → ai api auth config connectors jsonutil middleware model repository skills specialists store web xapps

grep -rln 'ontology|semantic|bus|orchestrat' --include='*.go' internal/ → 命中全部落在 skills/packages/*/manifest.go 与 skill_definition.go

8.10 运行态伪造

backend-go/internal/specialists/runtime/records.go:16-34 —— 四个 *RecordsJSON 的 if empty { generate } 写法:

if strings.TrimSpace(item.InputsRecordsJSON) == "" {
    item.InputsRecordsJSON = jsonutil.MustJSON(buildInputsRecords(item))
}

records.go:82-127 —— buildPermissionRecords 返回硬编码四条,与本次执行无关。

8.11 与 RS03 相关的既有取证

位置 事实
internal/model/action_definition.go:19、internal/skills/model/skill_definition.go:28 OntologyBindingJSON 字段定义
全仓 OntologyBindingJSON 共 22 处,全部落在 model / validation / seed / admin handler 运行时读取方 0
internal/specialists/runtime/task_runtime.go:269,279 全仓唯一运行时读对象目录处(definition.Objects[0],UseDemo: true)
frontend/src/specialists/runtime/specialistFlow.js:58,186 判断 approval_state === 'approval_required';后端只产出 ready / pending —— 该分支永不命中