Doctor Core 保持开源,但具体 Plugin 的 Service Catalog、固定查询和排障知识可能属于企业内部资产。 Plugin 通过版本化、自包含、可离线交付的归档分发这些业务扩展,同一份交付物同时贡献:
- PluginDefinition:向确定性诊断提供 Service Catalog 和插件级 capability,并在运行时携带同版本
已解析的
PluginSkill; - Skill 资源:采用标准
SKILL.md目录,供本地doctor chat的 agent loop 渐进加载业务知识和脚本。
Plugin 是 Service 与 Skill 的打包和分发单位。PluginDefinition 是唯一运行时入口,id@version
构成其精确身份,其中
Service Catalog 可包含组成同一应用的多个 Service,每个 Service 各自声明 capability 及所需 access;
同一 Plugin 也可携带多个 Skill。Plugin manifest 只定位代码和 Skill,loader 再把已解析的 Skill runtime view 附到
PluginDefinition.skills,不重复声明 store、log、data、model 等能力。
例如业务 ID 到规范 trace_id 的转换由 Service 的 traceId capability 声明,trace/log 只消费其
约定结果,不通过通用 data 查询或 span tag 猜测业务关系。
monorepo 中的源码按依赖方向分为三层:
cli/src/plugin/ Plugin 安装、选择与加载的宿主边界
packages/plugin/ doctor-plugin 协议与可选共享 SDK
plugins/<plugin>/ 可独立构建、归档和分发的具体 Plugin 实现
领域依赖保持 cli/collect -> packages/plugin <- plugins/<plugin>。CLI core 只接收注入的
PluginDefinition,不引用具体 Plugin;根目录发行构建和 Host loader 分别负责在编译期、运行期
取得具体实现,collect 不感知来源。
Doctor 与 Plugin 的运行边界围绕 capability 调用展开:Plugin 声明自己能提供哪些 capability;Doctor 在每次调用时 告知当前 profile 选择的 Target 和 Target-scoped access;Plugin 返回对应 capability 约定的结果,使 Doctor 能够统一串联、诊断和展示。Core 负责通用 Host/Target 访问和 Doctor-owned operation,Plugin 负责业务目标与数据语义;业务 HTTP/数据库协议仍属于 Plugin,但 Kubernetes 传输和 port-forward 生命周期不由 Plugin 重建。 当操作依赖原始凭据或厂商私有配置时,Plugin 可以返回“规范化身份 + 操作方法”的临时 handle; Core 只持有并调用 handle,不要求 Plugin 把敏感配置翻译成公共字段再传出。
capability 是 Core 发现、准备和调用 Plugin 能力的中心。下面四类契约只服务于 capability,不各自形成 Plugin 扩展点或生命周期:
| 协议面 | 方向 | 所有权 |
|---|---|---|
| access | Plugin capability → Core | Plugin 声明最小需求,Core 合并、预检并执行策略 |
| data | Core ↔ Plugin capability | 公共包定义类型化输入输出;私有 schema 留在 Plugin 内 |
| infra | Core → Plugin context | 当前 Target 的 Kubernetes access、取消与资源生命周期等运行便利 |
| config | profile/Core → Plugin context | Core 不透明保存和透传,schema、校验和解释归 Plugin |
这四类不能互相代替:infra access 不代表 capability 已获授权;config 不承载 kubeconfig 等 Core-owned 连接状态;data 返回值也不用于把 Plugin 私有配置整包泄露给 Core。
Trace Capability 把采集定位和纯分析明确分开:trace.source.store 引用 Service Catalog 中的静态 Store,
供 Core 在运行时解析实际 OpenSearch target;trace.analysis 直接采用 trace-harness 的
TraceContributions,只对已标准化的 Trace IR/Facts 做确定性 feature、detect 与 render 扩展。它不读取
profile config,不持有 infra,也不访问外部资源;诊断流程和 TraceHarness 实例生命周期仍由 Core 拥有。
Model Capability 是 Plugin 对模型供给的聚合声明,它把 tenant directory、model catalog 与 inference Service 组成一项可消费能力。Chat 用它选择并调用 LLM,Model Collect 在同一能力上增加 validation、 performance 和 Evidence;诊断是 Core command 行为,不是 Plugin capability 的身份。Core 在调用前 检查 Kubernetes access 并提供 port-forward,Plugin 持有业务路由与凭据;inference factory 只有在 所需连通性准备完成后才返回 handle,因此 Chat 不会在首轮请求时才发现连接尚未建立。
Case Capability 是 Service 的稳定请求资产与触发协议。它暴露一个或多个 CaseSet,以及并发安全的
单次 Case runner;Case 的 canonical schema 归 spec-case,Doctor Plugin SDK 只消费运行所需的
id/input/facets 投影。环境地址、身份和凭据由 runner 从 Plugin context 取得,不写入 Case。
Perf Capability 是其上的场景预设,只选择 CaseSet 中的一个或多个 Case、声明本次权重、业务关联键
优先级和 Metric/Log Service。权重属于本次 Experiment,不属于 Case。并发模型、dispatch、
Stage/Window、请求预算、熔断、Outcome IR、by_case 统计和报告编排由 Core 与共享 Perf Harness 拥有。
Core 在每个调度点调用一次 runner,Plugin 决定这次调用如何变成真实 HTTP/SSE 请求,可以持有 Trial 级
setup/deactivate/cleanup,但不得另起不可记账的发压循环。这样 runner 还能被单 Case 调试等入口复用。
持久化模型只包含两个事实:
- Doctor Host 已安装哪些
plugin@version; - Doctor Host 当前加载哪个精确插件版本。
Plugin 安装/加载是 Host 级生命周期,profile 只选择诊断环境并提供该环境下的 Plugin config。两者正交, 不增加 Package / Instance / Binding 或常驻插件进程。
Plugin archive ──install──> ~/.doctor/plugins/<plugin>/<version>/
│
active.json ─┘
├── PluginDefinition ──> ServiceCatalog ──> collect
└── Skills ─────────────────────────────> local doctor chat
Profile ──> target / access / Plugin-owned config
一个 Plugin 可同时携带多个 Service 和多个 Skill。切换 profile 只改变它们面对的环境、权限和配置, 不会加载或卸载 Plugin。当前加载一个业务 Plugin,不为多个业务 Plugin 的并行组合设计额外生命周期。
Plugin archive 使用 tar/tar.gz;所有归档来源统一落到同一安装目录:
~/.doctor/plugins/
├── active.json
└── sample/
└── 1.2.0/
├── plugin.json
├── plugin.mjs
├── .doctor-install.json
└── skills/
├── service-ops/
│ └── SKILL.md
└── trace-ops/
└── SKILL.md
归档文件名不参与身份判断,真实 id 和 version 只取自根目录的 plugin.json。manifest 的最小形态:
{
"manifestVersion": 1,
"pluginApiVersion": 1,
"id": "sample",
"version": "1.2.0",
"requiresDoctor": ">=0.1.0",
"contentDigest": "sha256:<64-hex>",
"main": "./plugin.mjs",
"skills": ["./skills/service-ops", "./skills/trace-ops"]
}Plugin 入口是可直接执行的 Node-compatible ESM,默认导出一个 PluginDefinition。manifest id 与导出
对象 id 必须一致;pluginApiVersion 必须与当前 Doctor 支持的 Plugin API 精确匹配。归档必须自包含运行依赖,不在客户现场执行 npm install、
install script 或编译 TypeScript;Skill 目录可携带其 references/、scripts/ 等标准资源。
Service Catalog 还可声明 Toolchain,表达源码语言与稳定构建方式,供 Core 选择通用诊断采集器。它是 Plugin 知识而非 Target Fact:当前 runtime、镜像和实际依赖必须在 collect 阶段重新观察;Plugin 不通过 Toolchain 下发任意命令。
cd plugins/example
make build
# dist/example-<version>.doctor-plugin.tar.gzPlugin 在自身 dist/ 中产出 <id>-<version>.doctor-plugin.tar.gz。归档只包含 manifest、已 bundle 的
ESM 入口和 Skills,不包含 TypeScript 源码或 node_modules。plugins/example/Makefile 是可复制的
最短构建入口。
doctor plugin install ./sample-1.2.0.doctor-plugin.tar.gz
doctor plugin uninstall sample@1.2.0install 是面向用户的一步式“安装并加载”操作:
- 在临时目录解包,读取并校验 manifest、Doctor 版本兼容性和所有资源路径;
- 按
contentDigest校验实际 ESM/Skill payload,加载代码入口并校验PluginDefinition的身份、 Service/capability 结构和跨 Service 引用,扫描 Skill 的基础元数据并附加 runtime view; - 原子移动到
~/.doctor/plugins/<id>/<version>/;目标版本已存在时不原地覆盖; - 在安装完全成功后把精确
id@version原子写入~/.doctor/plugins/active.json; - 已加载同一 Plugin 的旧版本时替换 Host 级引用,但保留旧版本目录。
安装时生成 Host-owned .doctor-install.json,封存归档、manifest 与实际 payload 的摘要;后续每次加载
都在 import Plugin 代码前重新校验。安装目录中的版本内容不可变。失败发生在 active state 更新前,不改变当前可用版本;旧版本由
uninstall 显式清理,不隐含在 install 中。卸载当前版本时同时清除 Host 级 active state。
profile 可提供随环境变化的 Plugin config,但不保存 Plugin 身份:
profiles:
sample:
plugin:
config:
region: exampleconfig 由 Core 原样保存并只放进已加载 Plugin 的调用上下文。Core 不根据其中字段推导 Target 或权限;
Plugin 通过 validateConfig 在命令准备阶段校验自己的 schema,校验完成前不会开始 Target 访问。
CLI composition root 从 Doctor Host 的 active state 加载精确 Plugin 版本:
- 校验 active ref 的版本仍存在,并加载 Plugin 代码与 Skill;
- Skill name 冲突时直接报错,不按加载顺序静默覆盖;
- 需要业务语义的 collect 命令取得 Host 已加载的
PluginDefinition,进入通用 collect 链路; - Plugin command 始终可见,缺少 required capability 时提示具体缺口;不依赖 Plugin 的 Core/离线命令 保持零配置可用;
doctor chat使用 Host 已加载 Plugin 所携带的 Skills,并把解析结果交给本地 Agent。
启动本地 Agent 前,Doctor 以 profile name 作为 env 标识,并把 env、namespace、readonly 组成
SkillExecutionTarget。Plugin 可用 prepareSkillContext 补充 OpenSearch、DB 等业务访问事实;Core
直接把 kubeconfig 等 profile-owned target 字段写入脚本的 TARGET_* 环境,不经 Plugin 转交。profile
确定的 target 字段始终覆盖 Plugin 返回值,避免 Plugin 在无感知情况下把会话重定向到另一环境。凭据
只能进入执行环境,不能写进会被模型看到的 contextPrompt。
Plugin 是 Doctor Host 的本地状态。CLI 不向远端执行环境隐式上传本地 Skill 或 Plugin;会话级上传属于 独立协议和授权能力,不隐含在 Plugin install 中。
升级不需要独立状态机:install 新版本成功后,将 Host 的精确版本引用从旧版切到新版。旧版本仍在时, 重新 install 对应归档即可回退。进程启动后不监听目录变化;正在运行的命令或 chat 继续使用启动时解析的 版本,新版本在下一次进程启动时生效。
Doctor Host 负责 Plugin 的安装、加载和版本身份;profile 负责目标环境、凭据和该环境下的 Plugin config。 切换 profile 不改变代码与 Skills,安装或卸载 Plugin 也不改写任何 profile。
PluginDefinition 的 capability 是确定性诊断代码,PluginSkill 是 agent 使用的知识与工作流。
两者运行接口独立,但由同一个 runtime definition 汇合,并共同跟随 Plugin 安装、选择、信任和升级;
Skill 没有平行的全局生命周期。
同一 plugin@version 的代码与 Skill 内容不可变。Plugin workspace 对 src/ 和 skills/ 统一计算内容锁;
构建和测试只接受与当前 version 匹配的锁,任一目录变化都必须 bump Plugin version 后重新封存。
Skill 资源本身保持宿主中立:同一份多环境台账和脚本原样分发。环境选择与基础设施连接属于宿主和 Plugin 的准备边界,不通过裁剪 Skill、修改 Skill 文案或维护宿主专属副本表达。
doctor-plugin 同时承担稳定协议和可选 SDK,但两者职责不同。协议定义 Plugin/Service/capability 的
声明、调用输入输出,以及所有 Service 共用的 PluginContext;上下文提供 namespace、当前 Service、
取消信号和 Target-scoped Kubernetes access。Plugin 用 access 读取 Service、定位 Pod 或访问其它资源,
Core 不接收再回传 Plugin-owned 的 selector 等实现细节。profile 切换后,Doctor 在下一次调用中注入新的
上下文,Plugin 不持有 kubeconfig 或旧环境选择。
网络 endpoint 跟随实际消费它的 capability 声明,不放在 Service 根上假设一个全局端口。同一 Service 可以分别为 tenant directory、model catalog、inference、MCP 或 metrics 提供不同 endpoint;命令只为 本轮选中的 capability 建立对应连接。
Kubernetes 传输以及 port-forward 的本地端口分配、取消和回收具有明确的 Doctor 调用生命周期,因此由
PluginContext 按需提供。HTTP、数据库客户端由 Plugin 实现;SDK helper 只承载稳定且跨 Plugin 同义
重复的代码。协议不注入 Doctor 的 HttpTransport、Database 等具体实现。Service 定位规则、API、
SQL、表结构及诊断知识始终属于具体 Plugin。
access 跟随实际被调用的 capability,而不是汇总成 Plugin 的最大权限。Doctor 先根据命令和用户选择确定 本轮参与的 Service,再把 Core command 自身需求与这些 capability 的声明合成阶段性的 access plan; 因此同一 Plugin 中未参与本次命令的 Service 不会扩大预检权限。
Plugin 的贡献深度随 command 类型变化:业务型命令由 capability 执行业务访问并返回约定数据;
基础设施型命令只需要 Plugin 贡献目标或连接信息,标准诊断算法仍由 Core 持有;混合型命令先由 Plugin
完成业务 ID、私有配置等投影,再交给 Core 的标准采集阶段。分类、典型命令和 Kubernetes 分工统一见
kernel.md。
解包、路径校验、版本目录和 active state 更新属于 cli/src/plugin 宿主边界;packages/plugin 只定义
Plugin 与 Service 公共语义,collect 只消费注入的 PluginDefinition,本地 agent loop 只消费解析后的
Skills。归档来源只负责取得交付物,不影响 Catalog 或诊断领域实现。
Plugin 与 Doctor CLI 同进程运行,拥有相同的文件、网络和凭据权限;Plugin 不是安全沙箱,只允许加载
来自受信任交付渠道的 Plugin。解包仍必须拒绝绝对路径、.. 穿越、符号链接逃逸和越出插件根目录的
manifest 入口,并使用临时目录加原子 rename,避免半安装状态。签名校验和私有仓库属于归档来源与信任
能力,不改变本地安装模型。
- Marketplace、在线搜索或自动升级;
- Package / Instance / Binding、独立 Runner 或插件间依赖解析;
- 同版本原地覆盖、运行中热更新或动态卸载;
- 客户现场依赖安装和 install script;
- CLI 到远端执行环境的隐式 Skill/Plugin 上传。
企业 Plugin 的 workspace package.json 可只服务开发和构建,保持 private: true,不发布 npm。
标准交付把自包含 plugin.mjs 与 Skills 打成 tar,由通用 Doctor binary 安装并在 Host 上加载;需要定制 binary 时,
分发方可从 doctor-cli/embed 导入 startDoctor,提供独立 composition entry,并通过
cli/Makefile 的 DOCTOR_ENTRY 构建。本仓根 make build/install 始终构建不带具体 Plugin 的通用 CLI。
两种形态共用 PluginDefinition 与 capability,差别只在启动时如何取得 Plugin。