ChatGPT 网页端维护本地 Obsidian 的架构
解释 ChatGPT Workspace Agent、Skill、MCP App、Secure MCP Tunnel 与本地知识库服务如何协作完成受控检索和写入。
#type / synthesis
#status / growing
#tech / ai
#discipline / obsidian
#resource / chatgpt
#resource / mcp
#resource / obsidian
[!abstract] 架构结论 ChatGPT 不直接挂载 D 盘,也不把整个文件系统交给模型。它只调用一组语义明确的知识库工具;本地 MCP Server 负责路径、提案、并发和审计边界,Secure MCP Tunnel 只负责让这些工具从 ChatGPT 可达。
[!info] related notes
- 所属 MOC: ChatGPT MOC
- 基础概念: MCP, 本地 MCP Server
- 扩展关系: ChatGPT Apps、Skills 与 Workspace Agents
- 安全模型: 二阶段提案合并的安全边界
- 实施起点: 构建 Thought Forest 本地知识库 MCP Server
ChatGPT 网页端维护本地 Obsidian 的架构
范围
本文解释 Thought Forest 已实施方案的组件、数据流、控制流与设计理由。它不重复逐项安装命令,也不记录真实密钥、组织 ID 或 Tunnel ID。
目标是同时满足:
- 在 ChatGPT 网页端使用 Workspace Agent 的推理和对话体验;
- 实时查询本地 Obsidian 知识库;
- 允许创建和修改笔记;
- 写入必须可预览、可拒绝、可审计和可恢复;
- 不暴露整个 D 盘或公开本地服务端口。
组件图
flowchart TD
U[用户] --> A[Workspace Agent]
A --> S[Skill<br/>维护流程]
A --> P[Thought Forest KB App<br/>12 个 MCP 工具]
P --> O[OpenAI Secure MCP Tunnel]
O --> T[tunnel-client]
T --> M[kb-mcp stdio Server]
M --> I[知识索引]
M --> R[z/ 与 docs/ 只读查询]
M --> Q[inbox/ 提案]
M --> Z[z/ 精确合并]
M --> L[归档与审计日志]
每一层只承担一种主要职责
Workspace Agent:固定运行入口
它绑定 App、Skill、模型和审批策略,并保持 Draft/Preview/Publish 生命周期。它不直接拥有文件系统权限。
Skill:工作流控制
Skill 规定先读概况、查重、读取规范、提出方案并停止。第一次批准后只能暂存提案;展示 proposal ID 和 diff 后再次停止;第二次批准后才调用合并。
Skill 是软控制。如果模型遗漏指令,底层服务仍必须拒绝越权输入。
App:工具能力和 ChatGPT 权限元数据
App 保存 MCP 工具清单、读写/破坏性标注和连接方式。Workspace Agent 选择哪些工具可用,并为写操作配置用户确认。
Secure MCP Tunnel:可达性
Tunnel 使 ChatGPT 能访问私有本地 MCP,但不开放公网服务。它不理解知识库结构,也不决定哪些路径能写。
kb-mcp:最终安全边界
本地服务实现:
- 索引概况、标题/别名/标签搜索和全文搜索;
- 受限路径读取、规范枚举和链接图;
- 新建与修改提案;
- 精确 proposal ID 合并;
- 提案与目标基线哈希校验;
- 仅允许 `z/*.md` 的目标;
- symlink 逃逸和路径穿越防护;
- 归档、JSONL 审计和索引重建。
12 个工具按副作用分层
| 类别 | 工具 | 策略 |
|---|---|---|
| 只读概况 | `get_vault_overview` | 可直接调用 |
| 搜索读取 | `search_knowledge`、`search_note_content`、`read_note` | 可直接调用 |
| 规范与关系 | `list_standards`、`list_tags`、`get_related_notes`、`list_recent_notes` | 可直接调用 |
| 暂存提案 | `propose_new_note`、`propose_note_patch` | 第一次用户批准;写入 inbox |
| 检查提案 | `list_proposals` | 只读 |
| 最终合并 | `apply_proposal` | 独立的第二次批准;写 z、归档、审计、重建索引 |
一次新建笔记的完整调用链
- 用户提出知识主题。
- Agent 调用概况和搜索工具查重。
- Agent 读取标签与结构规范。
- Agent 给出明确方案并停止。
- 用户第一次确认。
- Agent 调用 `propose_new_note`,服务端返回 `proposal_id`、目标与预览。
- Agent 明确说明 `z/` 未改变,再次停止。
- 用户第二次确认。
- ChatGPT 展示写操作确认卡。
- 用户批准 `apply_proposal`。
- 服务端校验 proposal hash、目标基线和路径。
- 服务端写入目标,归档提案,追加审计日志并重建索引。
- Agent 只有收到成功结果后才报告已经合并。
为什么不直接写 D 盘根目录
把输出直接写到 `D:\` 看似简单,却会绕过:
- Obsidian Vault 的图谱和入口;
- 仓库的 frontmatter、标签和命名规范;
- 查重和 MOC 更新;
- Git diff 与可恢复历史;
- 路径白名单;
- 索引重建;
- 提案审阅与审计。
正确目标是受控 Vault 中的 `z/`,不是磁盘根目录。
失败隔离
| 失败层 | 结果 | 不应发生的连锁反应 |
|---|---|---|
| ChatGPT/Agent 不可用 | 无法发起新任务 | 本地笔记不受影响 |
| Tunnel 断开 | 工具调用失败 | 不应删除或修改提案 |
| Skill 未触发 | 流程可能偏离 | 服务端仍拒绝任意路径 |
| App 工具快照过期 | 新工具不可见 | 旧工具不应获得新增权限 |
| 合并校验失败 | 保留待审提案 | 不覆盖并发修改的目标 |
| 索引重建失败 | 已写目标但检索可能暂时陈旧 | 不应重复消费已归档提案 |
可恢复性
- 未合并提案保留在 `inbox/new/` 或 `inbox/patch/`;
- 已合并提案归档到按日期分组的目录;
- `inbox/_applied.jsonl` 记录目标、归档和时间;
- Git 保存知识库文件历史;
- CLI 提供 list、dry-run 和精确 apply 兜底;
- 索引可从 `z/` 重新生成。