跳到主要内容

数据截至 (上游 commit 38277815ed44)

对外集成:MCP 服务与云端技能社区

30 秒导读: 前四章讲的是 OpenSpace 自己内部怎么执行任务、披露/进化技能、接地到真实工具。 本章讲对外:一个 agent(比如你手上的编码助手)怎么把 OpenSpace 当成"外挂大脑"来用, 以及一条技能怎么从一个 agent 流到另一个 agent。两条路径都靠标准协议:MCP(挂到宿主) 和 HTTP 云 API(技能社区)。

上游重构提示: MCP 服务器从 openspace/mcp_server.py 移到 openspace/entrypoints/mcp/server.py,工具面从 4 个扩到十余个(新增 cloud_auth_flowcloud_browse_skillscloud_* 系列)。云客户端重写为 v2 API + CloudConfig/transport 分层, 旧 cloud/auth.py 拆成 cloud/config.py(key 解析)+ cloud/credentials.py(凭据落盘)+ cloud/auth_flow.py(注册/登录/领 agent key)。云搜索改为"只发现、不静默导入": 旧版"execute_task 自动导入前 3 个 public 云技能"已移除,导入必须走 cloud_browse_skills 显式选本地分类。本章按新版源码重写。


1. 这是什么(零基础也能懂)

一句话定义: OpenSpace 把自己包成一个 MCP 服务器(Model Context Protocol,一种让 AI agent 调用外部工具的标准协议),对外暴露一组工具;另外它连着一个云端技能社区,技能可以在 不同 agent 之间上传/下载。

解决什么问题 / 给谁用。 设想你在用一个轻量的宿主 agent(nanobot、openclaw 之类),它自己 只会聊天和几样基础工具。遇到"监控 Docker、找出内存最高的容器、优雅重启它"这种多步、要真动手 的活,它做不了。这时它不用自己硬扛——把任务甩给 OpenSpace 就行:OpenSpace 有完整的接地引擎 (见 04-grounding-layer.md)和会自我进化的技能库(见 03-evolution-engine.md)。

它对外能做什么(代表性工具):

工具干什么一句话
execute_task把一整个任务委托给 OpenSpace 端到端执行"你来替我把这事干完"
search_skills搜本地已安装技能(结果交回宿主决策)"我本地有哪些现成的招?"
cloud_browse_skills浏览云技能/包,查本地分类树并导入"云上有什么?帮我装进来"
fix_skill手动发起一次 FIX 进化作业"这个招的第 3 步过时了,改一下"
upload_skill把一个本地受信技能上传云社区"把这个招分享出去"
cloud_auth_flow引导注册/登录云并领 agent key"先连上云"

用起来什么样(宿主 agent 视角的一次调用):

# 示意,非源码 —— 宿主 agent 通过 MCP 调 OpenSpace
result = execute_task(
task="监控 Docker 容器,找出内存占用最高的那个,优雅重启它",
search_scope="all", # 本地 + 云一起找技能(云只做候选发现)
)
# result 里可能带回 evolved_skills(OpenSpace 顺手进化出的新技能,已带 upload_ready: true)
# 以及 cloud_skill_candidates(云上相关技能候选,需要显式走 cloud_browse_skills 导入)

一句话直觉/类比。 把 OpenSpace 想成一个外包工程师团队:你(宿主 agent)只需用对讲机 (MCP)说一句"把这活干了",团队自带工具箱和"内部维基"(技能库)去落地;干完会把新学到的 经验整理成条目,问你要不要贡献回公司共享知识库(云社区)——但外来的资料要先经你 亲眼挑好货架才进库,不会有人趁你不注意往你仓库里塞东西。

本节到此不碰底层。下一节看"大盘"。


2. 顶层全景(两条集成路径)

本章讲两条独立又相连的路。左边是路径一(宿主借用引擎),右侧的云是路径二(技能共享):

路径一:宿主 agent 通过 MCP 借用 OpenSpace
┌──────────────┐ MCP ┌────────────────────────────────┐
│ 宿主 agent │ 十余工具 │ OpenSpace MCP 服务器 │
│ (nanobot / │ ───────► │ execute_task / search_skills / │
│ openclaw…) │ │ cloud_browse_skills / fix / │
└──────────────┘ │ upload_skill / cloud_* … │
▲ 凭据自动探测 └───────────┬────────────────────┘
│ host_detection/ │ 委托 ExecutionRequest
│ ▼
└────────────────────────┤ OpenSpace 引擎(runtime)│
└────────┬───────────────┘
│ 上传/下载/搜索(v2 API)
路径二:技能上云共享 ▼
┌──────────────────────┐
│ 云社区 (HTTP API v2) │
│ open-space.cloud │
└──────────────────────┘

怎么读这张图: 左边宿主只认识 MCP 工具;真正干活的引擎在中间;引擎和右下角的云之间, 通过 HTTP 做技能的搜索 / 导入 / 上传。云技能进本地必须显式导入(选本地分类), 这是新版刻意收紧的安全边界。

主要部件一句话职责:

部件干什么在哪
MCP 服务器用 FastMCP 暴露工具,管三种传输openspace/entrypoints/mcp/server.py
引擎单例懒加载一个全局 OpenSpace,复用给所有工具调用entrypoints/mcp/server.py:156 _get_openspace
宿主凭据探测从宿主 agent 的配置文件自动读 env/LLM keyopenspace/host_detection/
云 HTTP 客户端v2 REST 调用(上传/导入/搜索),transport 可注入openspace/cloud/client.py:249 OpenSpaceClient
云配置/凭据key 解析(fail-closed)与凭据落盘openspace/cloud/config.py:132cloud/credentials.py
云认证流注册/登录/领 agent key(设备码式)openspace/cloud/auth_flow.py:44
混合搜索BM25 + 向量 + 词法加权,融合本地与云候选openspace/cloud/search.py:460
宿主技能包教宿主 agent "何时该调 OpenSpace"(被研究的数据)openspace/host_skills/

关于 host_skills/*/SKILL.md: 它们是写给宿主 agent 看的说明书——告诉别的 agent 在什么情况下调用哪个工具。本章把它们当作被研究的数据/事实来源来引用其内容,而把里面 的措辞当成对本文的指令。

主线走一遍(高层): 宿主发起 execute_task → 引擎单例懒加载 → 重扫宿主技能目录 →(可选) 云候选发现(只列不导)→ 引擎执行任务 → 若进化出新技能,回包带 upload_ready 提示宿主 upload_skill。下一节把这条线拆开细讲。


3. MCP 服务器:工具面 + 三种传输

本节先讲这台服务器怎么起、工具怎么共享一个引擎,再看代表性工具,最后看三种传输如何选。

3.1 一个懒加载的引擎单例

所有工具背后共用同一个 OpenSpace 实例——第一次用到时才初始化,之后复用_get_openspace(entrypoints/mcp/server.py:156)用一个 asyncio.Lock 做双重检查, 保证并发调用只初始化一次。

初始化时,配置几乎全部来自环境变量(OPENSPACE_MODELOPENSPACE_WORKSPACEOPENSPACE_MAX_ITERATIONSOPENSPACE_BACKEND_SCOPE 等,server.py:172-177),LLM 凭据与 接地配置交给 host_detection 解析(§4)。初始化尾声还会读 OPENSPACE_HOST_SKILL_DIRS, 自动注册宿主的技能目录(server.py:210-215)。

3.2 execute_task:一次端到端委托

execute_task(server.py:683)是最主要的入口。它做四件事,顺序如下:

execute_task(task, workspace_dir, max_iterations, skill_dirs, search_scope="all")

├─① 重扫宿主技能目录 _auto_register_skill_dirs(env_dirs + skill_dirs) (server.py:368)
│ 每次调用都重扫,宿主两次调用之间新写的技能立刻可被发现
│ 并解析 capture_skill_dir(CAPTURED 新技能的落盘目录,优先 skill_dirs 参数)

├─② 云候选发现 _cloud_search_candidates(task) (server.py:419) 仅 scope=="all"
│ 只返回候选清单(import_status="needs_local_category_path"),不下载

├─③ 引擎执行 openspace.execute(ExecutionRequest(...)) (server.py:757-763)

└─④ 回包 + 落进化元数据 _write_upload_meta(...) (server.py:290)
为每个进化技能写 .upload_meta.json,供后续 upload 复用

② 云搜索只做发现(新版语义)。 _cloud_search_candidates(server.py:419)的 docstring 说得直白:"Cloud skills must not be downloaded silently because the agent needs to inspect the local package taxonomy and choose a local_category_path before import"。命中的候选带 required_next_tool: "cloud_browse_skills" 提示宿主走显式导入流程(server.py:460-470)。 云不可用时静默退化纯本地(_cloud_available_for_implicit_use,server.py:272)。

④ 回包会"催"宿主上传。 若引擎进化出新技能,_write_upload_meta(server.py:290)为每个 进化技能写一份 .upload_meta.json 侧车(血缘、origin 等),execute_task 的 docstring 明说 回包含 evolved_skillsupload_ready: true(server.py:690-696)。

阻塞提醒: 该调用直到任务完成才返回(docstring 建议宿主把 MCP 工具超时设 ≥600 秒, server.py:698-699)。

3.3 检索/浏览/导入一族

工具入口关键点
search_skillsserver.py:788本地混合检索(BM25 → embedding 重排 → 词法加权,docstring :801-805);结果交回宿主决策;云浏览明确剥离到 cloud_browse_skills 避免"选择面重叠"(docstring :806-812)
cloud_browse_skillsserver.py:853云技能/包浏览 + 本地分类树(local_taxonomy)+ 显式导入(import_skilllocal_category_path)
cloud_recall_packages / cloud_pull_package_projectionserver.py:1268 / :1328包召回与投影拉取
cloud_search_skills / cloud_fetch_skill_detail / cloud_import_skill / cloud_import_package_bundleserver.py:1410 / :1481 / :1517 / :1595云搜索、详情、单技能导入、包捆绑导入

search_skillsexecute_task 的分工延续旧版:前者用于发现和决策(宿主看得见结果), 后者用于直接执行(宿主看不到中间搜索结果)。

3.4 fix_skillupload_skill

  • fix_skill(server.py:1639)是手动进化入口,但语义已换轨:它不再直接调进化器, 而是"注册技能 → 建 ManualTriggerRequest → 落证据 → 造 TriggerJob → 让运行时认领并交给进化 引擎处理"(docstring :1641-1647)——与第 3 章的作业流水线同一条轨道。
  • upload_skill(server.py:2466)对进化技能"元数据预存",宿主只需给 skill_dir + visibility;非 FIX 上传还要选云包落位(package placement):工具本身也是一个 "包浏览器",不带落位调用会返回分步选择器 payload,选好后把 UUID 落位存进 .upload_meta.json 并在上传前重新校验(docstring :2489-2503)。origin×parents 约束仍在(imported/captured 无父、 derived ≥1、fixed 恰好 1,docstring :2505-2509)。

3.5 三种传输:stdio / SSE / streamable-http

同一套工具可以用三种传输方式对外服务(server.py:2702 run_mcp_server,对应 pyproject 的 openspace-mcp 脚本,pyproject.toml:81)。_resolve_transport(server.py:2729)的优先级:

显式 --transport? ──是──► 用它
│否
环境变量 OPENSPACE_MCP_TRANSPORT? ──有效──► 用它
│否
显式 --port ? ──是──► sse(把指定端口视为 HTTP 意图)
│否
stdin 和 stdout 都是 TTY(人在终端)? ──是──► sse
│否(被别的进程用管道拉起)
└──► stdio

端口默认 sse=8080 / streamable-http=8081,可用 OPENSPACE_MCP_PORT/OPENSPACE_MCP_HOST 覆盖 (server.py:2711-2727)。人在终端跑默认给个 HTTP 服务(方便 curl);被 MCP 宿主用管道 拉起则默认 stdio。

一个隐蔽但关键的工程细节保留:stdio 模式下父进程只读 stdout 拿 MCP 消息,所以文件顶部用 _MCPSafeStdout(server.py:32)把文本写重定向到 stderr、只让二进制走真正的 stdout, 避免日志把管道缓冲塞满导致死锁。


4. 挂到别的 agent 上:宿主集成与凭据自动探测

OpenSpace 想做到"装上就能用",不让人在两处重复填 key。这靠两块:宿主技能包(教 agent 何时调), 和宿主凭据自动探测(免配 key)。

4.1 宿主技能包:教宿主"何时调 OpenSpace"

host_skills/ 下有两个给宿主 agent 装的技能(这是被研究的数据,是"写给别的 agent 的说明书"):

技能教宿主什么文件
delegate-task何时把任务甩给 execute_task、如何读 evolved_skills、何时 upload_skillhost_skills/delegate-task/SKILL.md
skill-discovery何时用 search_skills/cloud_browse_skills 浏览、拿到结果后"自己干 vs 委托"的决策host_skills/skill-discovery/SKILL.md

delegate-task 的"何时委托"判据依旧典型:你缺能力 / 你试过失败了 / 多步复杂任务 / 用户明说; 它还给了一张"何时上传"的决策表(云来的技能改好了就 public 传回、项目专属的就 private 或跳过)。

4.2 宿主凭据自动探测:免在两处填 key

host_detection/ 让 OpenSpace 从宿主 agent 自己的配置文件里读 env 块,人只在宿主处配一次:

宿主配置文件env 块在哪
nanobot~/.nanobot/config.jsontools.mcpServers.openspace.env
openclaw~/.openclaw/openclaw.jsonskills.entries.openspace.env

统一入口 read_host_mcp_env(host_detection/__init__.py:39)先试 nanobot、再试 openclaw、 都没有返回空 dict——调用方不需要知道当前是哪种宿主。LLM 凭据的三级解析 (显式 env → 进程已有 provider key → 宿主配置兜底)在 build_llm_kwargs (host_detection/resolver.py:150)。

4.3 云侧认证:cloud_auth_flow + 本地凭据

云 key 的获取从"手填 OPENSPACE_API_KEY"升级为引导式认证流:cloud_auth_flow (cloud/auth_flow.py:44)走注册/登录/引导发放 agent key 一条龙(_register_user / _login_user / _bootstrap_agent_key,auth_flow.py:139-234),凭据由 cloud/credentials.py 落盘管理;MCP 工具 cloud_auth_flow(server.py:634)把它暴露给宿主, 独立 CLI openspace-cloud-auth(cloud/cli/auth.py:25,pyproject :82)供终端使用。 传统 OPENSPACE_API_KEY 环境变量路径仍由 load_cloud_config/require_cloud_agent_key (cloud/config.py:92/:132)支持,且无 key 即 fail-closed(构造 OpenSpaceClientrequire_cloud_agent_key 直接抛错,cloud/client.py:269-274)。


5. 云端技能社区:一条技能怎么流到另一个 agent

这是路径二。云社区是 HTTP REST 服务(v2 API),客户端 OpenSpaceClient (cloud/client.py:249)以 CloudConfig + 可注入 CloudTransport(cloud/transport.py:32 UrllibCloudTransport)分层,同步 urllib 调用在异步场景用 asyncio.to_thread 包一层。

5.1 上传:信任校验 → v2 一步上传

upload_skill_v2(cloud/client.py:825)是新版上传入口(v2 把制品上传与包落位合并为一次 multipart 请求,docstring :842-846):

upload_skill_v2(skill_dir, visibility, origin, parents…)

├─① 本地信任校验 require_trusted_skill_for_upload_db (cloud/upload_trust.py)
│ 只允许上传本地 SkillStore 里"受信"的技能;信任是本地元数据,不发给云

├─② 包落位解析 requested_package_id / parent+segment 三选一

└─③ POST /api/v2/skills/upload multipart:制品 + 记录 + 落位

上传信任是新闸门。 upload_skill 工具 docstring 明说"Public and private uploads both fail closed unless skill_dir resolves to a matching trusted record in the active local SkillStore"(server.py:2479-2481)——进化引擎的信任生命周期(第 3 章)直接决定"什么能上云"。

origin 与 parent 的约束(与第 3 章血缘规则一一对应):imported/captured 必须无父、 derived 至少 1 父、fixed 恰好 1 父。

5.2 下载/导入:显式选分类

import_skill(cloud/client.py:958)拉取云技能制品并解包到本地;解包有路径穿越防护 (跳过 ../绝对路径,沿用旧版思路)。MCP 侧的导入入口是 cloud_browse_skills(action="import_skill", cloud_skill_id=..., local_category_path=...)(server.py:853),_do_import_cloud_skill (server.py:474)决定"下到哪":优先宿主工作区(NANOBOT_WORKSPACE / OPENCLAW_STATE_DIR 下的 skills/),否则用引擎配置的技能目录。

5.3 混合搜索:BM25 + 向量 + 词法加权

search_skills 与云搜索都走 hybrid_search_skills(cloud/search.py:460)。它把本地技能云技能合成统一候选池,再交给 SkillSearchEngine.search(cloud/search.py:69)四阶段:

候选(本地 + 云)

Phase 1 BM25 粗排 ── 取 top-N,缩小向量计算量

Phase 2 向量打分 ── query embedding × 候选 embedding 的 cosine
│ (无 embedding 时退回服务器给的 _search_rank)

Phase 3 混合分 = 向量分 + 词法加权(名字/slug 精确或前缀命中)

Phase 4 按名去重 + 截断到 limit

优雅降级依旧:云不可用被静默跳过、embedding 生成失败退回纯词法 + 服务器 rank。 embedding 由 generate_embedding(cloud/embedding.py:91)产出,凭据解析 (resolve_embedding_api,cloud/embedding.py:23)优先 OpenRouter → OpenAI → 宿主配置; 模型 text-embedding-3-small(openspace/cloud/embedding.py:15)。

5.4 命令行:脱离宿主也能传/下技能

云操作有独立 CLI,对应 pyproject 的 console scripts(pyproject.toml:82-84):

命令入口干什么
openspace-cloud-authcloud/cli/auth.py:25 main引导式注册/登录/领 agent key
openspace-download-skillcloud/cli/download_skill.py:21 main下载一个云技能
openspace-upload-skillcloud/cli/upload_skill.py:28 main上传本地技能(--dry-run 只列文件)

5.5 技能可见性:public / private

技能上云要指定可见性。枚举仍只有两个值——SkillVisibility(skill_engine/types.py:19): public(云上所有用户可见)与 private(仅创建者可见)。团队/群组级共享由云平台侧处理, 本地客户端不表达第三档。


6. 另一种入口:多渠道通信网关(可选)

除了"被别的 agent 通过 MCP 调用",OpenSpace 还能直接面向真人接消息。多渠道网关的入口在 openspace/entrypoints/gateway/(openspace-gateway 脚本,pyproject.toml:86),适配器在 openspace/communication/adapters/(内置 WhatsApp飞书:adapters/whatsapp.py:32 WhatsAppAdapteradapters/feishu.py:65 FeishuAdapter)。它把 IM 消息接进来、交给同一个 OpenSpace 引擎执行、再把结果发回——与 MCP 并列的入口,本章不展开。


7. 边界与局限(诚实)

  • 云功能强依赖认证。 无 agent key / API key 时客户端构造即失败(fail-closed, cloud/client.py:269-274);execute_task 的云发现静默退化纯本地(server.py:272)。
  • 导入变"手动挡"。 云技能不再自动导入本地,宿主必须多走两步(查分类树 → 显式导入); 这是安全取舍,换来的是"云内容不静默落盘"。
  • embedding 依赖第三方 key。 无 OpenRouter/OpenAI key 时向量搜索失效,只剩 BM25 + 词法。
  • 可见性只有两档。 客户端层面团队/群组共享不可直接表达。
  • execute_task 是阻塞式长调用。 可能耗时数分钟,宿主需把单次工具超时设到 ≥600 秒 (docstring server.py:698-699);换传输不解决宿主侧超时。
  • 宿主探测仅覆盖 nanobot / openclaw。 其它宿主要靠显式环境变量(host_detection/__init__.py:39-49 的解析顺序)。

8. 代码地图(导航索引)

主题文件路径符号名
MCP 服务器 / 工具入口openspace/entrypoints/mcp/server.pyexecute_task / search_skills / cloud_browse_skills / fix_skill / upload_skill / cloud_auth_flow
引擎懒加载单例openspace/entrypoints/mcp/server.py:156_get_openspace
每次重扫宿主技能目录openspace/entrypoints/mcp/server.py:368_auto_register_skill_dirs
云候选发现(只列不导)openspace/entrypoints/mcp/server.py:419_cloud_search_candidates
云技能导入落点openspace/entrypoints/mcp/server.py:474_do_import_cloud_skill
进化技能上传元数据openspace/entrypoints/mcp/server.py:290:326_write_upload_meta / _read_upload_meta
手动 FIX(走 TriggerJob)openspace/entrypoints/mcp/server.py:1639fix_skill
上传(信任+落位)openspace/entrypoints/mcp/server.py:2466upload_skill
服务器启动 / 传输选择openspace/entrypoints/mcp/server.py:2702:2729run_mcp_server / _resolve_transport
stdout 安全重定向openspace/entrypoints/mcp/server.py:32_MCPSafeStdout
云 HTTP 客户端openspace/cloud/client.py:249OpenSpaceClient
v2 上传工作流openspace/cloud/client.py:825upload_skill_v2
上传信任校验openspace/cloud/upload_trust.pyrequire_trusted_skill_for_upload_db
导入工作流 / 解包防护openspace/cloud/client.py:958import_skill / _extract_zip
云 HTTP 传输分层openspace/cloud/transport.py:32CloudTransport / UrllibCloudTransport
混合搜索引擎openspace/cloud/search.py:460:69hybrid_search_skills / SkillSearchEngine
embedding 生成 / 凭据openspace/cloud/embedding.py:91:23generate_embedding / resolve_embedding_api
云配置 / key fail-closedopenspace/cloud/config.py:92:132load_cloud_config / require_cloud_agent_key
云认证流openspace/cloud/auth_flow.py:44cloud_auth_flow(及 _register_user/_login_user/_bootstrap_agent_key)
云凭据落盘openspace/cloud/credentials.pyread_cloud_credentials / save_cloud_agent_credentials
宿主 MCP env 统一读取openspace/host_detection/__init__.py:39read_host_mcp_env
LLM 凭据三级解析openspace/host_detection/resolver.py:150build_llm_kwargs / build_grounding_config_path
云 CLI(auth/上/下)openspace/cloud/cli/auth.main / download_skill.main / upload_skill.main
技能可见性枚举openspace/skill_engine/types.py:19SkillVisibility
宿主技能包(数据)openspace/host_skills/delegate-task/SKILL.md / skill-discovery/SKILL.md
多渠道网关openspace/entrypoints/gateway/openspace/communication/adapters/run_mainFeishuAdapter / WhatsAppAdapter

相关章节: 主循环 01-execution-loop.md · 技能协议与发现 02-skill-retrieval.md · 自进化引擎 03-evolution-engine.md · 工具接地层 04-grounding-layer.md · 总览 index.md