OpenSpace 实战:让 AI Agent 用 Skills、MCP 与谱系持续复用经验
一句话结论: OpenSpace 不是再造一个聊天 Agent,而是在现有 Agent 之上增加一层“技能管理系统”:任务执行后沉淀技能,用 SQLite 记录版本与谱系,在相似任务中复用,再通过 MCP 接入 Claude Code、Codex 等宿主。
>
适合读者: 已会使用 Python 和大模型 API,希望验证“任务执行 → 技能沉淀 → 相似任务复用 → 外部 Agent 调用”闭环的开发者。
>
来源: Sana Hassan,MarkTechPost,2026-07-25,*Building Self-Evolving AI Agents with OpenSpace Using Skills, MCP, Lineage, and Low-Cost Reuse*
>
项目: HKUDS/OpenSpace(MIT;官方 README 要求 Python 3.12+)
1. 先理解目标:这里的“自我演进”到底是什么
普通 Agent 每次都从提示词重新开始;OpenSpace 把执行中形成的可复用做法保存成 Skill,并记录它从哪里来、如何变化、是否被再次使用。
本文把闭环拆成四层:
- 执行层:通过 OpenSpace Python API 完成任务。
- 技能层:任务后分析结果,生成或更新
SKILL.md。 - 谱系层:在 SQLite 中记录技能版本、来源和演进关系。
- 接入层:通过 MCP 把技能管理能力暴露给 Claude Code、Codex 等宿主 Agent。
原文把技能来源概括为:
- FIX:从失败或问题修复中沉淀出的技能;
- DERIVED:从已有技能派生出的变体;
- CAPTURED:从一次成功任务中捕获的可复用方法。
要注意:“存下了技能”不等于“系统真的变强了”。至少还要观察相似任务是否命中旧技能、结果质量是否稳定、Token 或执行成本是否下降,以及错误经验能否被淘汰。
2. 环境与安全边界
本教程沿用原文的 Google Colab 路线,便于快速验证,不直接改生产 Agent。
2.1 前置条件
- Python 3.12+
- 一个 Anthropic 或 OpenAI API Key
- 可访问 GitHub
- 可选:OpenSpace Cloud Key(仅用于云端技能上传/下载)
2.2 不要把 Key 写进 Notebook
原文示例把 Key 留作空字符串。实际使用时,建议通过 Colab Secrets 或环境变量注入。
实践扩展:安全读取环境变量
# Colab:先在左侧 Secrets 中配置 ANTHROPIC_API_KEY 或 OPENAI_API_KEY
import os
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
OPENSPACE_CLOUD_KEY = os.getenv("OPENSPACE_API_KEY", "")
OPENSPACE_MODEL = os.getenv(
"OPENSPACE_MODEL",
"anthropic/claude-sonnet-4-5",
)
if not (ANTHROPIC_API_KEY or OPENAI_API_KEY):
print("未检测到 LLM Key:可以完成安装和数据库检查,但跳过真实任务。")验收标准: 输出中不得出现完整 Key;分享 Notebook 前检查所有单元格输出。
3. 第一步:安装 OpenSpace 并建立隔离工作区
下面的单元格完成 Python 版本检查、稀疏克隆、可编辑安装和目录初始化。
import os
import pathlib
import shutil
import subprocess
import sys
assert sys.version_info >= (3, 12), (
f"OpenSpace 需要 Python 3.12+,当前为 {sys.version.split()[0]}"
)
REPO_DIR = "/content/OpenSpace"
WORKSPACE = "/content/openspace_workspace"
SKILLS_DIR = "/content/my_agent_skills"
if not os.path.exists(REPO_DIR):
subprocess.run(
[
"git", "clone", "--filter=blob:none", "--sparse",
"https://github.com/HKUDS/OpenSpace.git", REPO_DIR,
],
check=True,
)
subprocess.run(
["git", "sparse-checkout", "set", "--no-cone", "/*", "!/assets/"],
cwd=REPO_DIR,
check=True,
)
subprocess.run(
[sys.executable, "-m", "pip", "install", "-q", "-e", REPO_DIR],
check=True,
)
subprocess.run(
[sys.executable, "-m", "pip", "install", "-q", "nest_asyncio"],
check=True,
)
os.makedirs(WORKSPACE, exist_ok=True)
os.makedirs(SKILLS_DIR, exist_ok=True)
for cli in ["openspace-mcp", "openspace-dashboard"]:
print(cli, "=>", shutil.which(cli) or "未找到")将非敏感配置写入 .env;Key 仍从环境变量读取:
config = {
"OPENSPACE_MODEL": OPENSPACE_MODEL,
"OPENSPACE_WORKSPACE": WORKSPACE,
"OPENSPACE_HOST_SKILL_DIRS": SKILLS_DIR,
}
for key, value in config.items():
os.environ[key] = value
safe_env = "\n".join(f"{k}={v}" for k, v in config.items()) + "\n"
pathlib.Path(REPO_DIR, "openspace", ".env").write_text(safe_env)
print("配置完成:", config)预期输出形态:
openspace-mcp => /usr/local/bin/openspace-mcp
openspace-dashboard => /usr/local/bin/openspace-dashboard
配置完成: {'OPENSPACE_MODEL': '...', 'OPENSPACE_WORKSPACE': '...', ...}失败处理:
- Python 版本过低:在 Colab 更换运行时;不要绕过版本断言。
- CLI 未找到:重新执行安装单元格,再运行
pip show openspace检查安装位置。 - 仓库结构或 CLI 名称变化:以 OpenSpace 官方 README 为准,文章代码可能随版本演进。
4. 实战一:执行首个任务并检查技能是否产生
4.1 调用异步 Python API
import asyncio
import nest_asyncio
nest_asyncio.apply()
async def run_task(query: str):
from openspace import OpenSpace
async with OpenSpace() as agent:
result = await agent.execute(query)
print("── RESPONSE ──")
print(result["response"][:3000])
evolved = result.get("evolved_skills", [])
for skill in evolved:
print(f"Evolved: {skill['name']} (origin={skill['origin']})")
return result
result_1 = asyncio.run(run_task(
"Write a Python function that parses a CSV of employee hours and "
"computes weekly payroll with overtime (1.5x beyond 40h). "
"Test it on a small synthetic example and show the output."
))4.2 你应该检查什么
不要只看自然语言答案;至少检查:
response是否包含可运行代码和样例结果;evolved_skills是否出现新技能;- 每个技能是否带有
name与origin; - 工作区是否生成
.openspace/openspace.db。
预期输出形态(名称会随任务变化):
── RESPONSE ──
...工资计算代码与测试输出...
Evolved: <skill-name> (origin=CAPTURED)如果 evolved_skills 为空,不要强行断言演进失败:可能是任务没有达到捕获阈值、后处理未完成,或当前版本配置不同。先查看日志和数据库。
5. 实战二:用相似任务验证“热复用”
第二个任务故意继承第一个任务的工资计算上下文,但新增税率 CSV 和净工资要求:
result_2 = asyncio.run(run_task(
"Extend the payroll logic: add a second CSV of tax withholding rates "
"per employee and produce net pay. Reuse any prior payroll skill."
))5.1 查看 SQLite 技能库
import os
import sqlite3
runtime_db = os.path.join(WORKSPACE, ".openspace", "openspace.db")
repo_db = os.path.join(REPO_DIR, ".openspace", "openspace.db")
db_path = runtime_db if os.path.exists(runtime_db) else repo_db
if not os.path.exists(db_path):
raise FileNotFoundError(f"没有找到 OpenSpace 数据库:{db_path}")
with sqlite3.connect(db_path) as conn:
tables = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
).fetchall()
print("tables:", [row[0] for row in tables])
for (table,) in tables:
columns = conn.execute(f"PRAGMA table_info({table})").fetchall()
print(table, "=>", [column[1] for column in columns])这里只执行元数据查询。不要把来自用户输入的表名直接拼接进 SQL;上例的表名来自数据库自身的
sqlite_master。
5.2 热复用的验收标准
一次演示不能证明“成本持续下降”。建议把结果记录成对照表:
| 指标 | 首次任务 | 相似任务 | 判定 |
|---|---|---|---|
| 是否检索到已有技能 | 否/未知 | 是/否 | 相似任务应可命中 |
| 新技能来源 | CAPTURED/FIX/其他 | DERIVED/其他 | 是否形成谱系 |
| 输出是否通过测试 | 是/否 | 是/否 | 质量不能因复用下降 |
| Token/费用 | 实测值 | 实测值 | 多轮后再判断趋势 |
| 总耗时 | 实测值 | 实测值 | 排除网络抖动后比较 |
本文提炼: “低成本复用”应视为待测假设,而不是看到数据库里有 Skill 就自动成立。最小可信证据是:同类任务命中旧技能、结果通过同一测试,并在多次重复中出现稳定的 Token 或耗时改善。
6. 实战三:手写一个 SKILL.md 并让 Agent 自动发现
6.1 创建 CSV 报告技能
import pathlib
import textwrap
skill_dir = pathlib.Path(SKILLS_DIR) / "colab-csv-report"
skill_dir.mkdir(parents=True, exist_ok=True)
(skill_dir / "SKILL.md").write_text(textwrap.dedent("""\
---
name: colab-csv-report
description: Turn any CSV into a short markdown report with summary stats, null counts, dtypes, and 3 key observations. Use pandas; never plot.
---
# colab-csv-report
1. Load the CSV with pandas (`on_bad_lines="skip"` fallback).
2. Emit: shape, dtypes table, describe(), null counts.
3. Write 3 bullet observations in plain Markdown.
4. If parsing fails, retry with `sep=None, engine="python"`.
"""))
print(skill_dir / "SKILL.md")6.2 准备样本输入并调用技能
sample_csv = pathlib.Path("/content/demo.csv")
sample_csv.write_text(
"name,dept,hours,rate\n"
"Ada,Eng,45,90\n"
"Grace,Eng,38,95\n"
"Alan,Math,50,80\n"
)
csv_result = asyncio.run(run_task(
f"Using the colab-csv-report skill, produce a markdown report for {sample_csv}"
))预期输出至少包含: 数据形状、字段类型、描述统计、空值计数、3 条观察。
6.3 反向边界案例:空文件
empty_csv = pathlib.Path("/content/empty.csv")
empty_csv.write_text("")
empty_result = asyncio.run(run_task(
f"Using the colab-csv-report skill, inspect {empty_csv}. "
"Do not invent statistics; report that the input is empty."
))验收标准: Agent 明确报告空输入或解析失败,不生成虚假的行数、均值或观察结论。
7. 通过 MCP 接入外部 Agent
原文使用 streamable HTTP 启动 MCP 服务:
import subprocess
import time
import urllib.error
import urllib.request
mcp_proc = subprocess.Popen(
[
"openspace-mcp",
"--transport", "streamable-http",
"--host", "127.0.0.1",
"--port", "8081",
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
env={**os.environ},
)
time.sleep(8)
try:
request = urllib.request.Request("http://127.0.0.1:8081/mcp", method="GET")
urllib.request.urlopen(request, timeout=5)
print("MCP endpoint is reachable")
except urllib.error.HTTPError as error:
print(f"MCP server is alive; bare GET returned HTTP {error.code}")
finally:
mcp_proc.terminate()裸 GET 返回非 2xx 不一定表示服务失效,因为 MCP 端点可能要求协议化请求。更可靠的验收是:进程存活、端口可达,并由真实 MCP 客户端完成一次工具发现。
宿主配置示意:
{
"mcpServers": {
"openspace": {
"command": "openspace-mcp",
"toolTimeout": 600,
"env": {
"OPENSPACE_HOST_SKILL_DIRS": "/content/my_agent_skills",
"OPENSPACE_WORKSPACE": "/content/openspace_workspace"
}
}
}
}安全边界:
- 本地验证只绑定
127.0.0.1,不要直接暴露到公网; - 不在配置文件中写真实 API Key;
- 接入生产 Agent 前,先限制技能目录和工作区权限;
- 技能上传云端前检查是否包含内部路径、客户数据、密钥或专有流程。
8. 如何阅读谱系,而不是只看技能数量
原文还检查了仓库 showcase/.openspace/openspace.db,并按 origin 统计技能。你可以使用同样的只读方法:
showcase_db = os.path.join(REPO_DIR, "showcase", ".openspace", "openspace.db")
if os.path.exists(showcase_db):
with sqlite3.connect(showcase_db) as conn:
for (table,) in conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
):
columns = [
row[1].lower()
for row in conn.execute(f"PRAGMA table_info({table})")
]
if "origin" in columns:
rows = conn.execute(
f"SELECT origin, COUNT(*) FROM {table} "
"GROUP BY origin ORDER BY COUNT(*) DESC"
).fetchall()
print(table, rows)分析时重点问四个问题:
- 来源是否清楚? 能否区分 CAPTURED、DERIVED、FIX?
- 父子关系是否可追踪? 派生技能能否找到父技能和版本?
- 质量是否有证据? 是否只有“生成记录”,没有任务结果或测试?
- 错误能否退出? 低质量技能是否可以停用、回滚或被更高质量版本替代?
技能数量增长不是目标;可审计的质量增长才是目标。
9. 一套更可靠的落地顺序
这是基于原文流程整理出的实践扩展,不是原文声称的官方实施规范:
阶段 A:只读观察
- 选择 3~5 个低风险、重复性高的任务;
- 保存任务输入、输出、测试结果、Token 和耗时;
- 只检查技能生成与谱系,不自动发布技能。
阶段 B:受控复用
- 只让相似任务检索已审核技能;
- 用同一组测试比较“冷启动”和“热复用”;
- 发现退化时停止复用并保留失败证据。
阶段 C:宿主接入
- 先通过本机 MCP 接入一个测试 Agent;
- 工具权限保持只读或人工确认;
- 验证技能发现、调用、错误返回和超时。
阶段 D:团队共享
- 建立技能评审、版本、所有者和退役规则;
- 上传前执行敏感信息扫描;
- 用真实任务结果决定晋升,不以模型自评作为唯一依据。
10. 常见失败与排查
没有生成技能
检查任务是否完成、后处理是否运行、日志是否报错,以及当前版本是否改变了演进阈值或字段。不要通过反复改提示词制造“看起来像演进”的输出。
相似任务没有复用旧技能
检查 OPENSPACE_HOST_SKILL_DIRS 与 OPENSPACE_WORKSPACE 是否在同一进程中生效;确认技能目录可读;再检查检索日志和技能描述是否足以匹配任务。
MCP 端口可达但客户端找不到工具
裸 HTTP 探针只能证明服务进程响应。继续检查客户端配置、传输类型、环境变量、工作目录和 MCP 初始化日志。
Token 没有下降
单次对比没有统计意义。固定模型、输入、工具和测试,至少进行多轮冷/热对照;同时确认复用没有以牺牲质量为代价。
技能越积越多,效果反而变差
这是技能库的典型维护风险。需要去重、质量门槛、停用机制和谱系回滚,而不是继续增加技能数量。
11. 原文方法覆盖清单
| 原文方法 | 是否纳入 | 落点 | 若遗漏原因 |
|---|---|---|---|
| Python 3.12+、稀疏克隆、可编辑安装 | 是 | 第 2~3 节 | — |
| 配置模型、工作区与技能目录 | 是 | 第 2~3 节 | Key 改为环境变量注入 |
| 异步 Python API 执行任务 | 是 | 第 4 节 | — |
| 检查 evolved skills 与 SQLite | 是 | 第 4~5 节 | — |
| 相似工资任务验证复用 | 是 | 第 5 节 | — |
创建自定义 SKILL.md |
是 | 第 6 节 | — |
安装 delegate-task、skill-discovery 宿主技能 |
否 | — | 它依赖当前仓库目录结构,建议按官方 host integration 文档执行,避免复制过时路径 |
| 启动 streamable HTTP MCP | 是 | 第 7 节 | 增加 loopback 与协议探针边界 |
| 云端技能上传/下载 | 部分 | 第 7、9 节 | 未执行外部上传;只保留安全检查与阶段建议 |
| 检查 showcase DB 和 FIX/DERIVED/CAPTURED | 是 | 第 8 节 | — |
| Dashboard | 否 | — | 不是验证技能演进闭环的必要步骤,且还需 Node 20+ 与前端安装 |
12. 最终验收清单
完成教程后,你应该能回答“是”或给出明确失败原因:
- OpenSpace 在 Python 3.12+ 环境安装成功;
- 首个任务通过 Python API 返回结果;
- 工作区存在 SQLite 数据库;
- 能看到技能来源或解释为什么没有生成技能;
- 相似任务完成一次冷/热对照;
- 自定义
SKILL.md能被发现并处理正常 CSV; - 空 CSV 不会产生虚构统计;
- MCP 只绑定 loopback,并由客户端完成一次工具发现;
- 所有 Key 均来自环境变量或 Secret Store;
- 没有把“技能数量增加”误当成“能力和成本已经改善”。
结语
OpenSpace 最值得验证的不是“Agent 会自动写 Skill”,而是它能否把任务经验变成可检索、可测试、可追踪、可回滚的资产。先在低风险任务上建立冷/热对照,再接 MCP、团队共享和自动演进;否则,系统可能只是把临时提示词变成了一个不断膨胀的技能仓库。
证据与验证状态
原文事实:OpenSpace 的安装、Python API、技能目录、SQLite 谱系、MCP、云端命令及 FIX/DERIVED/CAPTURED 流程来自 MarkTechPost 原文。本文提炼:四层架构、冷/热对照指标和“可审计质量增长”判断由本文根据原文步骤整理。实践扩展:环境变量密钥注入、空文件反向案例、分阶段上线和生产安全边界由本文补充。static_publish_ok:是;Markdown/HTML 已生成,目录、复制代码按钮、链接、远端非空与 SHA-256 一致性检查均通过。mock_code_run_ok:不适用;教程直接面向 OpenSpace 实际运行环境,没有伪造 Mock 成功证据。real_backend_contract_ok:未验证;代码按原文 API 组织,实际接口以当前 OpenSpace 官方仓库为准。real_backend_smoke_ok:未执行;未调用付费模型 API,也未上传云端技能。