Skip to content
learnspace
Go back

omp 内部 URL 全解:14 个 scheme 让 agent 统一寻址所有资源

这是「omp 编程工具使用探讨」专栏的又一篇追加文章(系列自第 9 篇「完结」后,陆续补了魔法关键词、Skill 蒸馏等源码级深挖)。前几篇里我一直在用 skill://memory://local:// 这些地址却没解释它们——本篇把背后的内部 URL scheme 一次讲透。

TL;DR:omp 定义了一批 scheme:// 形式的内部 URL,它们是 read/write 工具的 path 参数值——不是文件系统路径,用户终端也 cat 不了。主要消费者是 agent(模型)自己。xd:// 是其中特殊的一个:它把低频工具挂载成”虚拟设备”,用 read xd://<name> 读文档、write xd://<name> 带 JSON 执行,把工具 schema 的成本从每次请求摊到真正使用时。

一句话讲清楚

想象 agent 需要访问这些东西:内置文档、技能包、子代理产物、远程服务器文件、GitHub issue、MCP 资源、挂载的工具……如果每种资源一套寻址方式,agent 的脑子里得装几十种协议。omp 的答案是:统一成 URL,全部塞进 read/writepath 参数。

你在 TUI 输入框 agent 的动作
───────────────────── ─────────────────────────────
"读一下 python 模式技能" → read skill://python-patterns
"看看 bash 工具怎么用" → read omp://tools/bash.md
"打开 example.com 抓流量" → write xd://mcp__chrome_devtools_new_page {json}

这里有个关键认知:URL 的消费方是 agent,不是你。你在 TUI 里输入的是自然语言,agent 把它翻译成一次 read/write 调用。这也是后面”常见坑”一节里”用户终端不认这些 scheme”的根源。

为什么需要一套”寻址词汇表”

先把问题摊开。一个 omp session 里,agent 可能要被要求访问:

资源没有 scheme 时怎么做
内置工具文档(120 余篇,docs/记文件名 + 路径,逐层翻目录
技能包(skill://thinking-in-uml.omp/skills/ 里找 SKILL.md
子代理产物 / 工具输出溢出不知道去哪找,ID 记不住
远程服务器文件手动 ssh + scp 拼路径
GitHub issue / PRcurl API + 记 JSON 结构
MCP 资源记 server 名 + 资源 URI
工具调用记每个工具的完整参数 schema

每一种都是独立的”寻址方言”。omp 的做法是把它们全部折叠进一个寻址语法——scheme://,agent 只需要会一个动作(read/write + 一个字符串),就能访问所有资源。这跟浏览器用 http:// 统一访问网页是同一个思路,只是这里的”网页”是 agent 视角的资源。

14 个内置 scheme 全清单

InternalUrlRouterinternal-urls/router.ts 的 constructor)注册了 14 个 scheme,每个 scheme 一个 handler。按用途分四类:

学知识 / 查文档

SchemeURL 形态用途
omp://omp://omp://<file>.mdharness 自带文档,omp:// 列目录
skill://skill://<名>skill://<名>/路径技能包 SKILL.md 与包内文件
rule://rule://<名>规则详情

skill:// 是本专栏的老朋友——Skill 蒸馏那篇里的 thinking-in-uml 就是通过它加载的。

看产物 / 会话

SchemeURL 形态用途
agent://agent://<id>agent://Parent/Child子代理输出;/路径?q= 做 JSON 提取
artifact://artifact://<id>工具输出溢出转存的产物内容
history://history://history://<agentId>代理会话 transcript(只读 markdown)
local://local://<name>.md会话本地沙箱文件(可写,~/.omp/agent/sessions/<id>/local/

local:// 是最常被低估的一个——它是可写的,是 agent 跨会话/跨子代理传大 payload 的通道(比如把一段超长指令 write local://plan.md 落盘,再在 task 里引用 local:// 路径)。

桥接外部

SchemeURL 形态用途
mcp://mcp://资源URIMCP 服务器暴露的资源(支持任意自定义 scheme、opaque URI)
ssh://ssh://host/路径远程文件(UTF-8 ≤1MiB),复用 ControlMaster 连接
vault://vault://vault://search?q=Obsidian 笔记库操作
issue://issue://123issue://owner/repo/123?state=open读 issue,走 SQLite 缓存
pr://pr://123pr://owner/repo?comments=0 去掉评论读 PR

ssh:// 是部署运维场景的入口——本博客的部署链路(scripts/deploy.sh 用 rsync 同步 dist/ 到服务器)就是从 read ssh:// 读服务器状态、改远程配置开始的。

记忆 / 工具

SchemeURL 形态用途
memory://memory://…记忆后端(默认 memory_summary.md,namespace root,另有 mnemopi 后端)
xd://xd://xd://<tool>挂载的工具设备:read 列/读文档,write 带 JSON 执行

memory://记忆系统那篇讲过——这里补充一点源码事实:memory-protocol.tsDEFAULT_MEMORY_FILE = "memory_summary.md"MEMORY_NAMESPACE = "root",这就是为什么你总看到 memory://root/…

不在 router 里的一个

上面四类加起来正好是 router 注册的 14 个 handler。除此之外还有一个工具层扩展不在 router 里——它由 read/write 工具本地处理,write.ts 源码原话是 “conflict:// has no router handler”:

Scheme状态说明
conflict://read/write 工具本地处理git 合并冲突解析:conflict://<N> 读块、write conflict://<N> 解决、conflict://* 批量

xd://:把工具挂载成”虚拟设备”

xd 是这份清单里最特殊的一个——它不是”读某类资源”,而是把工具本身变成可寻址的地址。三个操作:

read xd:// → 列出全部挂载设备(发现)
read xd://<tool> → 返回工具文档 + JSON 参数 schema
write xd://<tool> → 执行:content 就是 JSON 参数对象

write xd://<name> 的 content 传空、?help 时返回文档而不执行(xdev.ts 里的 HELP_CONTENT_RE)。

为什么 MCP 工具默认挂载

打开一个 session,你会发现 chrome devtools 之类的 MCP 工具全在 xd:// 下面,而不是顶层。判定逻辑(xdev.tsisMountableUnderXdev):

// xdev.ts:69-79(简化)
export function isMountableUnderXdev(tool: { name: string; loadMode?: ToolLoadMode }): boolean {
if (tool.name in XDEV_TRANSPORT_TOOLS || tool.name in XDEV_KEEP_TOP_LEVEL) return false;
return tool.loadMode === "discoverable";
}

两个排除名单(源码注释写了各自的原因):

MCP / 扩展工具经 adapter 注册,loadMode 默认 "discoverable"默认挂载。总开关是 tools.xdevsettings-schema.ts 默认 true),关掉则所有启用工具顶层暴露。

为什么用 xdev 而非顶层直接调用

配置里的原文说得很直白:“Mount rarely-used (discoverable) tools under xd:// … instead of shipping their schemas on every request.”

核心动机是系统提示的 token 预算。 顶层工具每次请求都带完整的 name + description + schema。MCP 服务器可能暴露几十上百个工具(chrome devtools 一个 server 就挂几十个),全内联到系统提示里不可承受。xdev 的方案是:

代价是每次调用多一次 read 往返——所以只有低频工具走 xdev,高频工具(read/write/bash/edit/web_search)强制顶层。频率分层,各得其所。

预算常量(xdev.ts):

export const XDEV_DOCS_TOTAL_BUDGET = 48_000; // 系统提示里整个 xd 文档区总量
export const XDEV_DOCS_PER_DEVICE_CAP = 10_000; // 单个设备文档上限
export const XDEV_EXTERNAL_DESCRIPTION_CAP = 200; // 外部工具一行摘要的字符上限

注:我最初按文档理解写的是”系统提示里每个挂载设备只留一行目录”。对着源码核对后发现实际更精细——tools.xdevDocs三种模式settings-schema.ts 默认 "builtins"):

模式行为
inline所有挂载设备全文内联
builtins(默认)内置挂载工具文档内联,外部工具只给一行摘要
catalog全部只给一行摘要,文档全按需 read

判定在 shouldInlineXdevToolxdev.ts):mode !== "catalog" && (mode === "inline" || builtInNames.has(name) || 匹配 xdevInlineDevices glob)。也就是说默认情况下,omp 自带的挂载工具(lsp、debug、browser 这些)文档是直接内联的,agent 不用先 read 就知道怎么用;只有 MCP 外部工具走”摘要 + 按需全文”。

动态挂载

MCP 服务器可以中途连接/断开。xd:// 目录是动态的——通过 xdev-mount-notice 注入 added/removed 设备通知,天然适配”运行时工具集会变”的场景。

同一条管线:参数 schema 的五层

说完了 xd:// 怎么把工具变成地址,再看它背后复用的参数 schema 管线——这五层决定了 write xd://<tool> 里的 JSON 长什么样,也解释了为什么工具作者只写一次 schema。xd:// 不是一套新 schema,它复用 toolWireSchema + jsonSchemaToTypeScript 这条现成管线,只是把”传参 + 文档”改走 read/write 通道。

普通读者可以跳过本节的实现细节,直接看「TUI 实操与常见坑」。只记住一个钩子就够:参数校验失败时返回的错误自带 schema,模型读到错误信息就能自我纠正,不用多一轮往返

这套体系分五层:

schema为什么存在
作者层Zod(canonical)/ ArkType / TypeBox·plain JSON Schema三种写法由 toolWireSchema 统一转换为 wire JSON Schema(ArkType 走原生 toJsonSchema
互操作层JSON Schema(toolWireSchema所有 LLM API 的通用语言;空 schema 归一化为 true(issue #1179)
厂商层provider sanitizer ×8各家 strict / boolean subschema / additionalProperties 约束不同,不归一化会 400
模型可读层jsonSchemaToTypeScript给模型看的精简 TS 类型,比 JSON Schema 省 token
运行时层.assert 校验(validateToolArguments参数进 execute 前把关,报错带 schema 供模型修复

这个”错误即教程”的设计,跟魔法关键词那篇讲的”隐藏 notice 改行为”是同一个哲学:把修错的成本从人工挪给模型

TUI 实操与常见坑

先说怎么上手。你不需要记任何 URL——在 TUI 里用自然语言让 agent 处理,它替你完成寻址:

你在输入框敲 agent 的动作
────────────────────── ─────────────────────────────
"把写作规范读一下" → read skill://blog-series → 会话里出现一条 tool 调用并返回内容
"看下服务器当前状态" → read ssh://…(或 bash 里执行部署脚本)

想亲眼确认这些 scheme 在跑,盯着 agent 的 tool 调用记录就行——每一条 read <scheme>://… 都是一次寻址。

三种使用场景与正确姿势

场景正确做法错误做法
在 TUI 里读资源对话让 agent 处理(read local://demo-plan.md自己终端跑 cat local://...
在自己终端用这些文件手动用真实路径(local://~/.omp/agent/sessions/<id>/local/敲 URL 字面量
写脚本 / 自定义工具InternalUrlRouter API 或 expandInternalUrls 解析硬编码 URL 当路径

关键坑:bash 展开只对 agent 的 bash 工具

bash-skill-urls.tsexpandInternalUrls() 只在 agent 调用 bash 工具时执行,把命令里的 URL 重写成真实路径。用户自己的终端是普通 shell,不认这些 scheme——会报 No such file or directory

且展开只支持 7 个 scheme(源码 SUPPORTED_INTERNAL_SCHEMES):

const SUPPORTED_INTERNAL_SCHEMES = ["skill", "agent", "artifact", "plan", "memory", "rule", "local"] as const;

名单里有个特例:plan:// 出现在展开名单里,却没有对应的 router handler——plan 文件实际走 local://PLAN.mdacp-agent.tsDEFAULT_PLAN_FILE_URL)。其余 scheme(xd://omp://issue://ssh://vault://pr://mcp://history://conflict://)都不在展开名单里——即使 agent 的 bash 工具也不展开。想在自己的 shell 里用这些地址,唯一的办法是走真实路径。

read 的 selector

read 本身还支持局部读取,跟 scheme 叠加使用:

谁直接接触这些 scheme

角色是否直接用方式
omp 用户对话让 agent 处理
agent(模型)read/write 的 path 参数
扩展开发者InternalUrlRouter 解析、local:// 传参(task payload)、MCP 自定义资源 scheme

扩展开发者的真实场景有四个:custom tool 参数收 URL 值、子代理传大 payload(write local:// 再在 task 指令里引用)、MCP server 定义自定义资源 scheme(read 自动可读)、/tools 查看挂载状态。

本文本身就是个活例

写这篇的时候,我正在亲身体验这套体系:

也就是说,这篇文章的每一处”源码引用”都是 agent 用 read + 真实路径完成的,而它读文档的方式(skill://memory://)正是本文讲的机制。寻址体系不仅服务 agent 的执行,也服务 agent 的自我学习——这是这套设计最优雅的地方:用同一套寻址语法,把”工具怎么用”这类元知识也变成可寻址资源。

总结

一句话:

omp 用 URL scheme 把”文档、技能、产物、远程文件、GitHub、MCP 资源、工具调用”统一成可 read/write 的地址,让 agent 用一套寻址方式访问所有资源;xd:// 用”目录 + 按需 read + write”把低频工具的 schema 成本从每次请求摊到真正使用时。

几个值得带走的点:

  1. scheme 是给模型用的词汇表,不是给用户的文件系统——理解这一点,所有”为什么我的终端 cat 不了”的困惑都消失了
  2. xd:// 的本质是 token 预算工程——用一次 read 往返换掉几十个工具 schema 的常驻成本,频率分层是关键
  3. schema 管线五层一体——作者用 Zod/ArkType/TypeBox 写一次,wire/厂商/模型/运行时各取所需,xd:// 只是换了传输通道
  4. 动态是设计的一部分——MCP 中途连接、挂载设备列表变化,系统提示通过 notice 跟随

下一篇想写**local:// 作为子代理 payload 通道的实操**——怎么用 write local://plan.md + task 指令引用,把超长上下文传给子代理而不撑爆对话。这也是整理本文的那场 omp session 留下的待办之一。

本文是「omp 编程工具使用探讨」专栏的追加篇。文中源码引用来自 oh-my-pi 当前版本(packages/coding-agent/src/internal-urls/packages/coding-agent/src/tools/xdev.tspackages/coding-agent/src/config/settings-schema.ts),具体实现可能随版本变化,建议以实际源码为准。


Share this post:

Previous Post
用 omp 将《大象:Thinking in UML》蒸馏成 Skill