# 资源分类(taxonomy) > 本文件是**决策记录**:记下 dsh-stash 把外部资源分成哪几类、为什么这么分、刻意不收哪些, > 以及企业场景各自落在哪一类。**术语的唯一权威在这里**——面板、工具描述、README、经验库条目 > 引用的分类名,以本文件为准。与 `lib/registry.js` 的字段定义冲突时,以那边的代码为准并回改本文件。 > > 首版 2026-09-30。起因:面板里"钥匙"与"文库"两个名字撞车(`sources.mjs` 自称"文库注册表", > 而"文库"又被当成层名),且没有任何一处能回答"stash 一共能登记多少类资源"。 --- ## 一、这份分类要解决的三件事 1. **一名一物。** 同一个集合此前有两个名字("已登记的库" / "文库"),面板与工具各自叫各的。 2. **统计轴。** 面板顶部要能回答"各类库的整体情况",就得先有公认的"类"。 3. **往哪放。** 以后每加一条库,都能立刻回答"它属于哪一类、按什么形状登记"。 --- ## 二、两个属性:能不能**登记**,能不能经 stash **取数** 这两件事此前被混为一谈,导致 MCP 服务被整个划出分类。它们必须分开: | 形态 | 能登记 | 能经 stash 取数 | 说明 | |---|---|---|---| | A HTTP(S) 端点 | ✅ | ✅ | `kind: remote` + `handler` | | F 本地语料 | ✅ | ✅(检索 + `read`) | `kind: files` | | D MCP 服务 | ✅ | ❌ 走 DSH 直连 | 登记边界 / 钥匙 / 经验 | | B 数据库(限命名查询) | ⬜ 扩展位 | ⬜ 扩展位 | 需新 handler | | C 对象存储 / 文件传输 | ⬜ 扩展位 | ⬜ 扩展位 | 需新 handler | | E 消息 / 流 | ❌ | ❌ | 非一次取数 | | G 本地引擎 | ❌ | ❌ | 属 harness 工具能力 | | H 人工导入 | ➖ 是 F 的 `access` 变体 | ➖ | 不是独立形态 | **登记** = 在注册表里有条目、在 `stash_catalog` 与面板里可见、有边界声明、有经验挂靠点。 **取数** = 能由 `stash_fetch` 调一次并留下 `ledgerId` / `contentHash`。 --- ## 三、大类:程序触达外部世界只有四条通道 穷尽性不靠行业举例(那是无穷的),靠**通道枚举**:程序触达外部世界,只有四条物理通路。 | 大类 | 展开 | |---|---| | **网络套接字** | 按对端协议再分(A–E) | | **文件系统** | 读写磁盘上的字节(F) | | **子进程** | 拉起本地程序、拿它的输出(G) | | **人的输入** | 没有程序化入口,只能由人把东西搬进来(H) | ### 两条前提 1. **只收"能被确定性调用一次"的资源。** 流式订阅、人机会话被排除(E 由此出局)——否则 "通道"里还得加上"长连接"和"对话",边界会散。 2. **把"人的输入"当作一条通道。** 否则需登录 / 合规受限的那批资源(H)无处安放, 而它们恰恰是 `access: export-import` 存在的理由。 ### 检验法(用来反驳这套划分) 拿一个具体资源,问"它走哪条通道": - 答得出、落在四条里 → 分类成立; - 答"哪条都不走" → **缺一个大类**,补上并写明理由; - 答"走网络,但不是 HTTP / 数据库 / 对象存储 / 工具协议" → 是在 A–D 里**加一条形态**, 不是加一个大类。 ### 与面板那三类的映射(两个视角,不是一个分类) 四条通道是**给程序看的**(handler 走哪条路取数);面板上的三类是**给人看的**(东西从哪来)。 两套名字必须能互相推出来,否则界面和文档就会各说各话: | 面板三类 | 覆盖的通道与形态 | 判据(`library-view.bucketOf`) | |---|---|---| | **远端接口** | 网络套接字:A 声明式 HTTP、B 数据库的 HTTP 门面、C 对象存储、D 的 http 承载 | `kind: remote` 且(非回环地址 或 非 http handler) | | **本机服务** | 网络套接字的**回环**部分 + 子进程:本机端口、D 的 stdio 承载 | 地址是 `127.0.0.1` / `localhost` / `::1`;或 `kind: mcp` + `transport: stdio` | | **本机文件** | 文件系统 + 人的输入:F 本地语料(含 `export-import` 的人工导入件)、B 的 SQLite | `kind: files`;或 `handler: db` | 三点必须记住: 1. **人话在前,技术名只在登记时出现。** 首页与卡片上只有三类与形态标签;`http` / `sqlite` / `s3` 这类词出现在新建菜单、`sources.example.mjs` 与 `stash_catalog` 里,不出现在分类位上。 2. **归类是派生的,不是人填的。** 使用者选的是形态,三类由上面的判据算出来。 3. **判据可以被覆盖。** 唯一已知反例:本地反向代理把远端服务伪装成 `127.0.0.1`——那时自动判定 必然错,所以注册表留了 `bucket` 字段(只认这三个值),登记表单里可改。 ### 一处例外,不假装整齐 **D 跨承载**:MCP 走 `streamable-http` / `sse` 时是套接字,走 `stdio` 时实际是子进程管道。 所以 D 按"对端协议"归类,通道只是它的承载——这是本分类唯一不能一一对应的地方。 --- ## 四、形态表 | 字母 | 形态 | 判定 | 理由 / 落地 | |---|---|---|---| | **A** | HTTP(S) 端点 | ✅ 现役 | 四条判据全过。`kind: remote`,`handler` 三选一:`http`(声明式,写 json 即可加库)、`trade_stats` / `policy_alerts`(专用固化,把已知坑写进代码)。本机端口(如本地聚合后端)、GraphQL、带配额的接口都是它的用法变体 | | **B** | 数据库线协议 | ⬜ 扩展位 | 与"一条源 = 一个端点形状"冲突:任意 SQL 没有形状。**只有限定成命名查询才成立**。现状用 A 的门面解决(例如给只读视图套一层 PostgREST 之类的 HTTP 门面) | | **C** | 对象存储 / 文件传输 | ⬜ 扩展位(低优先) | 读为主时预签名 URL 用 A 就够;只有列目录 / 签名上传才值得专门 handler。SMB/NFS 挂载成盘符后按 F 处理 | | **D** | 工具协议(MCP / A2A) | ✅ 现役(只登记) | 取数归 DSH 直连,不重复建设;登记是为了边界、钥匙、经验与"本机有什么"。见第六节行为约束 | | **E** | 消息 / 流 | ❌ 不收 | 非一次取数:没有边界可声明,也没有 `contentHash` 可留 | | **F** | 本地语料 | ✅ 现役 | `kind: files`:登记路径 → (列目录 / 子串检索)→ `read`。检索能力是短板,见第五节 | | **G** | 本地引擎(duckdb / jq / pandoc) | ❌ 不收 | 判据"源 vs 工具"不过:那是本机计算能力,harness 的 shell 工具已经能做;把"能跑程序"放进注册表等于把数据源注册表变成代码执行面 | | **H** | 人工导入 | ➖ 不单列 | 代码里没有独立字段:它实际是 **F 的 `access: export-import` + 一条流程**(人工导出 → `corpus/` → 登记为 files)。是用法,不是形态 | | — | 复合编排 | ➖ 横切 | 不是新通道,是 A–H 的组合(先搜后取、翻页聚合)。现状"一条源 = 一个端点形状",编排交给模型 / 工作流;`trade_stats` 那种专用 handler 是把编排固化进代码的例外 | --- ## 五、企业场景落位 企业"大量内部库 / 文件资料"落在这套分类里是**位置明确的**,但要分清四件事: | 企业里的东西 | 落哪一类 | 怎么登记 | |---|---|---| | 内部数据库 / 数仓 / BI 平台 | **B**(若允许直连,且限定命名查询);有 REST / JDBC-over-HTTP 门面时走 **A** | 边界写"只读哪些表/视图";连接串属凭据,只留引用名 | | 内部文件资料:共享盘、NAS、档案目录 | **F**(挂载成盘符即是本地路径);SMB/NFS 直连协议属 **C** | `kind: files` + `paths`;按业务分目录、逐目录一条库 | | 内部知识库 / 文档系统(SharePoint、Confluence、OA) | 有 API 时走 **A**;导出后处理走 **H**(= F + `access: export-import`) | 有 API 就能取数留痕;无 API 就人工导出 | | 合规上不许程序化访问的内部库 | **H** | `access: export-import`:`stash_fetch` 拒绝,只能人工导出 → `corpus/` | ### 三个企业特有的短板(现状,别当已解决) 1. **规模检索。** F 的能力是"列目录 + 子串检索 + `read`"。几万份文件时不够用。 现有出路:企业内部搜索 API 走 A;或由 harness 自己的检索能力处理。 **本分类不为此新增形态**——Q:是否要一个"本地索引"形态?(未决,见变更记录) 2. **合规外发。** `boundary` 与 `access` 能表达"不许外发 / 只能人工导出",但**审计靠台账**: MCP(D)与人工路径(H)都进不了取数台账,只能看会话日志与文件系统。这一条要写在 面板卡片上,不能让人误以为"有台账就等于全覆盖"。 3. **证书 / SSO 凭据。** `inject` 已声明六种落点形式:`env:` / `header:` / `query:` / `file:` / `arg:` / `env-file:`。但插件只为 `env:` 做镜像搬运(写 `$DSH_HOME/.env`),`header:` / `query:` 走宿主凭据服务,`file:` 等落点**只记录不搬运**。企业常见的 PEM 证书、Kerberos keytab、 交互式 SSO **目前没有通道**——SSO 自动化按硬边界明确不做(见第八节)。 --- ## 六、D(MCP 服务)的登记形状与行为约束 ``` { id: 'my_mcp_service', name: '某个 MCP 服务', kind: 'mcp', server: 'my-service', // DSH profile 里那条 MCP 行的 serverName —— 与 harness 的对接键 transport: 'stdio', // stdio | streamable-http | sse,与坑直接相关 tools: 'mcp__my-service__*', // 给模型指路:该服务暴露的工具前缀 credentials: ['MY_SERVICE_KEY'], // 只写引用名;落点由钥匙台账字段的 inject 决定 access: 'official-api', // 四值同样适用 notes: [...], boundary: '...', } ``` 三条行为约束(写进代码,不只是文档): 1. `stash_fetch source=` → **明确拒绝**,hint 说明"用 `mcp____` 直连; 本条目只登记边界、钥匙与经验"。拒绝本身是边界表达,不是错误。 2. `stash_catalog` 与面板 → 正常列出,标「引用型(不经 stash 取数)」。 3. `stash_doctor` → 校验 `server` 非空、`transport` 取值;若某字段 `inject` 是 `env:`, 提示"MCP 行读 `process.env`,值须写 `$DSH_HOME/.env`,粘在「设置 → 钥匙」面板对本消费者无效"。 **已知缺口(明写)**:MCP 调用不经过 stash,因此**没有 `ledgerId` / `contentHash`**,溯源只能看会话日志。 D 的 `ready` 判定因此不是"能不能取数",而是"边界声明了吗 / 钥匙配了吗 / 坑记了吗"。 --- ## 七、术语表(一名一物) | 词 | 只用来说 | |---|---| | **库 / 资源** | 登记进来的外部资源(统称)。**"文库"不再作为层名或独立名词**,它等于"库" | | **大类** | 四条物理通道:网络套接字 / 文件系统 / 子进程 / 人的输入 | | **形态** | A–H 之一(HTTP 端点、本地语料、MCP 服务…) | | **变体** | 同一形态下的用法差别:免凭据 / 要凭据 / 带配额 / 本机端口 / 离线导出…(是标签,不是类) | | **钥匙 / 账号** | 凭据条目,`category` 六值:网站账号 / API 密钥 / 数据库 / 令牌 / MCP 智能体服务 / 其他 | | **边界** | `access` 四值:`public-api` / `official-api` / `export-import` / `unsupported` | | **记录** | 取数台账、经验库——不是登记对象 | 面板呈现:**一级按大类,二级按形态,变体作为卡片上的标签**;一级只显示有登记的类。 --- ## 八、不进的与理由 | 不收 | 理由 | |---|---| | E 消息 / 流 | 非一次取数,无边界无指纹 | | G 本地引擎 | "源 vs 工具":本机计算属 harness 能力 | | 通用网页抓取 | 一次性检索归 `web_search` / `web_fetch`;需要长期盯的页面本质是语料,走 F | | 需登录数据库的 SSO 自动化 | 不模拟登录、不调加密接口、不解密页面函数——会从"调用公开接口"越界到绕过访问控制;正当路径是 H | | 值的存储与回读 | 凭据服务本身没有回值方法,这是框架保证,不是代码自律 | | 重复包装 DSH 已直调的通道 | MCP 工具的取数不重复建设,只补边界与经验(D 由此从"不收"改为"只登记") | --- ## 九、与代码的对应 | 分类概念 | 代码 | |---|---| | 可登记形态 | `kind`:`remote`(A/B/C)、`files`(F)、`mcp`(D,本次新增) | | 取数实现方式 | `handler`:`http` / `trade_stats` / `policy_alerts`(均在 A 下) | | 边界 | `access` + `boundary` | | 钥匙 | `credentials`(引用名)+ 钥匙台账字段的 `inject`(六种落点形式) | | 记录 | 取数台账(仅 A/B/C/F)、经验库(全部形态)、边界声明(全部形态) | --- ## 十、变更记录 - **2026-09-30 首版。** 大类四条通道、形态 A–H;D 由"不收"改为"只登记"(依据:登记与取数是两个属性); B / C 定为扩展位(纳入分类,不预先实现 handler);E / G / H 定为不进分类。 - **2026-09-30 面板落位。** 界面收敛为**三类**(远端接口 / 本机服务 / 本机文件),四条通道退为文档与代码里的 派生依据;给定映射与覆盖字段 `bucket`(见第三节末)。B / C 两类 handler 已实现(`db` 只支持 SQLite、`objstore` 支持 S3 与 WebDAV)。 - **未决:** 是否为"企业内部大批量语料的检索"新增一个"本地索引"形态。当前不新增, 先靠企业内部搜索 API(A)与 harness 自身检索能力覆盖。