# 工具执行环境功能逻辑设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 状态 | 已实施,验收中 | | 版本 | 0.2 | | 日期 | 2026-09-02 | | 关联 PRD | [工具执行环境 PRD](./prd.md) | | 关联场景 | [工具执行环境 User Stories](./user-stories.md) | 本文是本功能业务规则、状态转换、决策表和跨场景一致性的权威来源。它不定义组件视觉,也 不规定代码模块如何实现。 ## 1. 逻辑目标 1. 同一用户操作在不同本机 Runtime 和 stdio MCP 中得到一致的解释器选择。 2. 系统不通过隐式回退掩盖配置错误。 3. 工具可执行、配套命令可执行和具体能力依赖可用是三个可区分事实。 4. 已运行进程使用稳定环境,不被设置变化中途改写。 5. 安装、更新、取消、失败和删除都有唯一、可恢复的结果。 6. 本机环境和远程 Host 环境没有隐式传播。 ## 2. 核心不变量 ### INV-1 单一明确来源 Node.js 和 Python 各自恰好保存一个来源:`managed` 或 `custom`。不存在 `auto`、候选优先级 列表或运行时动态选择。 ### INV-2 禁止静默回退 当前来源不可用时,该工具明确不可用。即使发现另一套系统解释器,也不会自动切换。 ### INV-3 配套命令同源 - `node`、`npm`、`npx` 必须属于同一 Node 来源。 - `python`、`python3`、`pip` 必须属于同一 Python 来源。 - `pip` 等价于当前 Python 的 `-m pip`,不能解析到其他解释器。 ### INV-4 三层状态独立 1. **解释器状态**:Node 或 Python 本身能否执行。 2. **工具组合状态**:npm/npx 或 pip/venv/SSL 是否完整。 3. **能力依赖状态**:具体 SKILL/MCP 的入口和第三方依赖是否通过验证。 任一上层成功都不能推导下一层成功。 ### INV-5 进程环境不可变 每个 Runtime/MCP 进程在启动时取得一份环境快照。设置变化只影响之后启动的进程,不改写 已运行进程的 PATH 或解释器。 ### INV-6 绝对命令优先 stdio MCP 配置的绝对 command 表达用户对该 MCP 的明确覆盖,原样执行,不经过全局工具 命令替换。 ### INV-7 本机与远程隔离 本机工具选择、shim、Python 包、SKILL 包和 stdio MCP 配置不上传到远程 Host。 ### INV-8 不推断能力依赖已就绪 能力没有专属实际诊断时,状态只能是“未验证”,不能根据解释器版本或文件存在推断“已就绪”。 ### INV-9 受管工具工件来源明确且冻结 每个受管工具外部工件同时声明固定原生地址和 OSS Target。用户选择一个下载源后,任务 启动时冻结该来源;失败不回退。OSS 必须是原生工件的字节级副本。 ### INV-10 设置导航不改变业务状态 “能力与工具”的三个水平 Tab 只组织界面,不改变 Skill、MCP 或工具环境设置。切换 Tab 不得提交、丢弃或重置当前设置面板会话中的编辑状态;关闭后不持久化外层 Tab。 分阶段交付期间,未实现的“工具执行环境”不进入 Tab 状态集合。第一阶段状态集合只有 `skills | mcp`,后续实现完成后才扩展为 `skills | mcp | tool-environment`。 ### INV-11 内置 Skill 默认值不覆盖用户状态 只有状态存储中没有对应 Skill 记录时才使用产品默认值。通用文档工具 `deai-writing`、`longdoc-docx` 默认启用;产品营销类 `product-evidence`、 `product-marketing`、`product-presentation` 默认停用。用户显式保存的启停状态和 Runtime 分配优先,升级不得重置。 九种产品营销文档配方属于 `product-marketing` 包内工作流,不是独立能力状态维度, 不进入顶层 Skill 目录,也不单独注入 Runtime。 ## 3. 状态维度 ### 3.1 来源选择 ```text managed custom(executablePath) ``` 这是唯一持久状态。 候选选择还具有不持久化的界面状态: ```text closed choosing validating failed ``` - 从 `closed` 进入 `choosing` 不改变当前持久来源。 - `choosing` 中不得自动采用首个候选;只有用户明确选择候选或文件后才进入 `validating`。 - 验证成功后一次性保存 `custom(executablePath)`;验证失败回到 `choosing` 并就地显示错误。 - 文件选择取消回到 `choosing`,不显示错误且不改变当前来源。 - 没有候选不是禁用“自定义环境”的理由,用户仍可刷新或选择其他可执行文件。 ### 3.2 托管 Python 安装状态 ```text not-installed downloading verifying installing testing ready update-available error ``` `error` 同时携带“是否仍有旧版可用”。若旧版可用,实际工具状态仍基于旧版诊断。 托管 Node 随 GoodBuddy 存在,不使用下载安装状态,但仍可能因打包损坏进入诊断失败。 ### 3.3 工具诊断状态 ```text unknown testing ready partial unavailable ``` - `ready`:解释器和本功能承诺的配套命令全部通过。 - `partial`:解释器通过,至少一个配套命令失败。 - `unavailable`:解释器本身不能使用。 ### 3.4 能力依赖状态 ```text unknown checking ready missing incompatible error ``` - `unknown` 在 UI 中显示“依赖未验证”。 - `missing` 表示明确缺少包或入口。 - `incompatible` 表示解释器版本、架构或 ABI 不满足已声明要求。 - `error` 表示诊断本身没有得出有效结论。 ### 3.5 工具下载源 ```text native oss ``` 这是独立于 Node/Python 环境来源的持久设置。全新用户默认 `native`。 ## 4. 来源解析决策表 | 保存来源 | 当前事实 | 解析结果 | 是否回退 | | --- | --- | --- | --- | | 托管 Node | 完整诊断通过 | 使用 Electron Node 与锁定 npm/npx | 否 | | 托管 Node | Node 通过,npm/npx 失败 | Node 部分可用,显示失败项 | 否 | | 托管 Node | Node 失败 | Node 不可用,提示修复/重装 GoodBuddy | 否 | | 托管 Python | 未安装 | Python 不可用,提供安装 | 否 | | 托管 Python | 已安装且诊断通过 | 使用托管 Python | 否 | | 托管 Python | 更新失败但旧版健康 | 继续使用旧版并显示更新失败 | 否 | | 托管 Python | 安装损坏 | Python 不可用,提供重新安装/删除 | 否 | | 自定义 | 路径及诊断通过 | 使用该绝对路径 | 否 | | 自定义 | 路径失效 | 保持选择并显示路径不可用 | 否 | | 自定义 | 解释器通过、配套命令失败 | 部分可用并显示失败项 | 否 | | 任意 | 发现其他有效解释器 | 只作为候选展示 | 不自动使用 | ## 5. 命令解析决策表 | 调用形式 | 结果 | | --- | --- | | `node ...` | 当前 Node | | `npm ...` | 当前 Node 配套 npm | | `npx ...` | 当前 Node 配套 npx | | `python ...` | 当前 Python | | `python3 ...` | 当前 Python | | `pip ...` | 当前 Python `-m pip` | | `#!/usr/bin/env node` | 从进程工具 PATH 解析当前 Node | | `#!/usr/bin/env python3` | 从进程工具 PATH 解析当前 Python | | stdio MCP 绝对 command | 原样执行 | | 未托管裸命令,例如 `uvx` | 按原筛选 PATH 处理;找不到则明确失败 | | HTTP/SSE MCP | 不参与本机命令解析 | 绝对 command 的子进程若自行调用裸 `node` 或 `python`,仍会看到该 MCP 进程启动时的工具 PATH。这不改变绝对 command 本身。 ## 6. 首次使用和迁移 ### 6.1 全新安装 | 工具 | 默认选择 | 首次状态 | | --- | --- | --- | | Node.js | GoodBuddy 托管 | 立即真实诊断 | | Python | GoodBuddy 托管 | 未安装,展示安装入口 | 系统候选可以展示,但不会改变默认选择。 ### 6.2 旧版本升级 1. 设置中没有工具环境字段时执行一次迁移。 2. 分别按旧版本实际 PATH 解析 Node 和 Python 3。 3. 有效解释器固化为 `custom + 绝对真实路径`。 4. 无有效解释器时选择 `managed`。 5. 持久化后不再因 PATH 变化迁移。 迁移中 Node 和 Python 独立决策,不要求二者使用同一种来源。 ## 7. 托管 Python 生命周期 ### 7.1 首次安装 ```text not-installed → downloading → verifying → installing → testing → ready ``` 任一步失败: ```text → 删除本次暂存 → error(没有活动版本) → 用户可重试 ``` ### 7.2 取消 | 取消阶段 | 结果 | | --- | --- | | 下载 | 终止下载、删除暂存、回到安装前状态 | | 校验 | 终止校验、删除暂存、回到安装前状态 | | 解压/安装 | 完成本次有界清理、回到安装前状态 | | 测试 | 停止诊断、删除未发布候选、回到安装前状态 | 取消不发布候选版本,不把取消显示为安装失败。 ### 7.3 更新 1. 下载和验证候选时,旧版继续服务现有进程。 2. 应用候选前,页面说明需要停止依赖托管 Python 的本机 Runtime/MCP。 3. 用户确认后通过现有生命周期取消/停止相关进程。 4. 无法确认停止时不替换当前版本。 5. 候选完整诊断通过后才发布。 6. 失败继续使用旧版,并显示更新失败。 首期不让两个托管 Python 版本长期并存。候选暂存只服务本次更新。 ### 7.4 删除 | 条件 | 行为 | | --- | --- | | 没有使用中进程 | 确认后删除托管目录 | | 有使用中进程 | 说明影响,用户确认后停止相关进程再删除 | | 进程停止失败 | 不删除,保留当前环境 | | 当前选择为自定义 | 删除托管包不改变自定义来源 | | 当前选择为托管 | 删除后保持托管选择,状态变为未安装 | ## 8. 设置保存与进程生效 ### 8.1 保存时 - 验证待保存来源。 - 自定义路径必须在保存前通过解释器诊断;配套命令可以形成 `partial`。 - 保存成功后标记本机 Runtime 环境配置已变化。 - 不直接修改任何运行中进程。 ### 8.2 后续进程 - 没有活动请求的缓存 Runtime 在下次使用前按现有重建入口重建。 - 有活动请求的 Runtime 完成或被用户停止前保持原环境。 - 已启动 stdio MCP 保持原环境,正常关闭、重连或显式测试后使用新环境。 - 新启动的 Runtime/MCP 立即使用新环境。 每个工具调用的活动记录应归属于实际承载它的进程,不根据最新设置追改历史来源。 ### 8.3 设置导航 | 入口或操作 | 打开的外层 Tab | 数据行为 | | --- | --- | --- | | 普通“能力与工具”入口 | Skills | 不修改业务数据 | | 用户点击 Skills | Skills | 不修改业务数据 | | 用户点击 MCP | MCP | 不修改业务数据 | | 未来的定向入口 | 指定 Tab | 不修改业务数据 | | 同一设置面板会话中切换 Tab | 用户选择的 Tab | 保留三个面板的会话内状态 | | 关闭后重新打开普通入口 | Skills | 不恢复上次外层 Tab | MCP 内层页签状态属于 MCP 面板,不与外层 Tab 合并。外层 Tab 的默认值或定向入口不能改写 MCP 的连接、凭据、启用或 Runtime 分配状态。 ## 9. 能力依赖逻辑 ### 9.1 诊断输入 能力可以声明: - 需要 Node 或 Python。 - 最低/最高解释器版本。 - 可选依赖文件位置。 - 可选的无副作用诊断入口。 首期声明只用于显示、版本比较和显式诊断,不触发自动安装。 ### 9.2 状态推导 | 工具状态 | 能力诊断 | 能力状态 | | --- | --- | --- | | unavailable | 不执行 | missing 或 incompatible,附工具原因 | | partial | 能力不依赖失败配套项且实际诊断成功 | ready | | partial | 能力依赖失败配套项 | missing | | ready | 没有能力诊断 | unknown | | ready | 实际诊断成功 | ready | | ready | 明确缺包 | missing | | ready | 版本/ABI 不满足 | incompatible | | 任意 | 诊断自身异常 | error | “依赖未验证”不是错误,也不能显示为“已就绪”。 ## 10. Runtime 与 MCP | 场景 | 解释器是否可见 | 是否允许执行 | | --- | --- | --- | | 本机 Runtime | 是 | 按实际注册能力和当前账号权限 | | 内部无工具文本调用 | 可以存在于进程环境 | 不注入工具,解释器不改变调用用途 | | Main 启动的已启用 stdio MCP | 是 | 按当前 MCP 分配和资源范围 | | HTTP/SSE MCP | 不适用 | 按当前网络 MCP 规则 | | 远程 Runtime | 否 | 使用远程 Host 自身环境 | 工具环境不创建新的工具,不扩大 Runtime 当前允许的工具集合。 ## 11. 网络受限与离线 ### 11.1 托管 Node 随 GoodBuddy 提供,安装后可离线使用。 ### 11.2 托管 Python - Windows 原生 Target 固定到官方 NuGet 包;macOS/Linux 原生 Target 固定到 Astral `python-build-standalone`。 - OSS Target 是对应原生工件的字节级副本。 - 下载源不可达时保持未安装或旧版可用状态,并显示准确原因。 - 首期不提供 Python 离线包导入,避免引入未经选型的包格式和导入流程。 - 完全离线用户可以选择已经离线安装的自定义 Python。 未来若加入离线导入,必须在 PRD、User Stories、逻辑状态和技术包校验中同时定义,不能只 增加一个文件按钮。 ### 11.3 工具下载源决策 | 保存选择 | 目录事实 | 行为 | | --- | --- | --- | | 原生地址 | 原生 Target 完整 | 请求固定上游 URL | | 原生地址 | 原生 Target 缺失 | 工件不可下载,不请求 OSS | | OSS 镜像 | OSS Target 完整 | 只请求固定 OSS URL | | OSS 镜像 | OSS Target 缺失 | 工件不可下载,不请求原生地址 | | 任意 | 下载后字节数/SHA-256 不符 | 删除暂存并失败 | | 任意 | 设置在任务期间变化 | 当前任务不变,后续任务使用新选择 | 该选择只覆盖受管工具目录中的外部工件,不影响模型、应用更新、Agent、pip/npm 注册表或 用户 URL。 ## 12. 冲突与一致性检查 | 可能冲突 | 唯一规则 | | --- | --- | | 全局工具选择 vs MCP 绝对 command | 绝对 command 优先 | | 新设置 vs 运行中进程 | 运行中进程保留启动快照 | | 新托管版本 vs 健康旧版本 | 候选完整通过后才发布 | | 系统发现新版本 vs 已保存自定义路径 | 不自动切换 | | 解释器成功 vs 缺少第三方包 | 工具 ready,能力 missing/unknown | | 内部文本调用环境包含解释器 | 保持无工具构造 | | 本机设置 vs 远程项目 | 远程隔离优先 | | 镜像不可达 vs 其他来源可用 | 不在同一次操作中静默切换来源 | | 工具下载源 vs 模型/更新下载源 | 各自独立,工具执行环境设置不覆盖其他功能 | | OSS 对象 vs 原生工件 | 必须字节、大小和 SHA-256 相同 | | 合并设置入口 vs 原有 Skills/MCP 数据 | 只重组导航,不迁移或重写业务数据 | | 外层 Tab vs MCP 内层 Tab | 保持两个独立 tablist、标识和键盘焦点范围 | ## 13. 逻辑评估结论 ### 13.1 完整性评估 | 评估维度 | 结论 | 依据 | | --- | --- | --- | | 首次使用 | 通过 | 托管 Node 立即诊断,托管 Python 明确未安装 | | 正常执行 | 通过 | SKILL 与 stdio MCP 共用同一命令解析 | | 自定义环境 | 通过 | 明确路径、真实诊断、禁止静默回退 | | 部分失败 | 通过 | 解释器、工具组合、能力依赖三层状态独立 | | 安装生命周期 | 通过 | 安装、取消、失败、重试有唯一结果 | | 更新生命周期 | 通过 | 候选通过后发布,失败保留健康旧版 | | 删除生命周期 | 通过 | 使用中进程先停止,失败不删除 | | 设置生效 | 通过 | 进程启动快照不可变,新进程使用新设置 | | 升级兼容 | 通过 | 旧系统解释器一次性固化为自定义路径 | | 执行范围 | 通过 | 工具环境不扩大工具集合或权限 | | 远程边界 | 通过 | 本机配置不进入 Host | | 受限网络 | 通过 | 来源失败可见,不在同次操作静默切换 | | 完全离线 | 有明确限制 | Node 可用;Python 首期要求已有自定义环境 | | 受管工具工件来源 | 通过 | 原生/OSS 独立选择、任务冻结、禁止回退 | | 设置导航 | 通过 | 单一一级入口、分阶段水平 Tab、业务数据不变 | ### 13.2 一致性评估 - “两种来源”与自定义候选探测不冲突:候选只是自定义来源的选值,不是第三种来源。 - “禁止回退”与升级迁移不冲突:迁移只发生一次,完成后按普通保存选择处理。 - “进程环境不可变”与 Runtime 重建不冲突:只有空闲或后续启动的进程使用新环境。 - “工具环境就绪”与“能力依赖就绪”不冲突:两者属于独立状态维度。 - “绝对 MCP command 优先”与 PATH 注入不冲突:command 不替换,但其子进程继承启动快照。 - “更新保留旧版”不要求长期多版本管理:旧版只在候选发布前保持当前,候选失败即删除。 ### 13.3 当前未决项 没有未决的产品逻辑分支。以下属于实施前必须解决的技术选型,不改变业务规则: 1. 六平台 CPython 独立发行包、固定版本、许可证和摘要。 2. SKILL/MCP 运行要求声明的最小 schema 和无副作用诊断入口。 3. 各 Runtime 当前缓存进程的精确空闲判定与既有重建入口接线。 若技术选型无法满足当前逻辑,例如目标 Python 工件不包含可用 pip/venv,必须回到 PRD 和 逻辑设计调整承诺,不能在实现中静默降级。 ## 14. 需求追踪 | 逻辑范围 | PRD | User Stories | | --- | --- | --- | | 首次使用默认状态 | FR-1、FR-2、FR-6 | US-A0 | | 明确来源与禁止回退 | FR-3、FR-8 | US-C1、US-C2、US-D3 | | 托管 Node | FR-1 | US-A1、US-B2 | | 托管 Python 生命周期 | FR-2、FR-9 | US-A2、US-D2、US-D4、US-D5 | | 三层状态 | FR-6 | US-A3、US-A4 | | SKILL 命令解析 | FR-4 | US-A1、US-A3 | | MCP 命令解析 | FR-5 | US-B1、US-B2、US-B3 | | 生效时机 | FR-7 | US-C3、US-C4 | | 执行范围 | FR-10 | US-D6 | | 远程隔离 | FR-7、FR-9 | US-D1 | | 受限网络与离线 | FR-2、FR-11 | US-E1、US-E2、US-E3 | | 统一设置入口 | FR-12 | US-F1、US-F2 |