mcpField
M
marimo-pair

marimo-pair

github.com/marimo-team数据与数据库43212.9k 次安装6 周前更新
代码仓库

关于 marimo-pair

marimo-pair 是一项用于操作实时 marimo 笔记本的技能,通过内核和 code-mode API 检查状态并持久化修改。它强调遵循响应式数据流,避免直接改写活动笔记本文件。

主要功能

  1. 01检查实时内核与单元格状态
  2. 02通过 code mode 创建和编辑单元格
  3. 03运行单元格及其响应式依赖
  4. 04管理笔记本依赖包与控件
  5. 05发现并选择笔记本会话

适用场景

  1. 01检查活动笔记本中的变量
  2. 02添加或修改持久化单元格
  3. 03通过 code mode 更改控件值

MCP 相关

该技能说明可通过 marimo MCP 执行代码和发现笔记本会话,也可使用随附脚本。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marimo-team/marimo-pair --skill marimo-pair

安装后在 Claude Code 中输入/marimo-pair确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

reference/
execution-context.md
finding-marimo.md
gotchas.md
notebook-improvements.md
rich-representations.md
scripts/
discover-servers.sh
execute-code.sh

详细介绍

marimo 是一个响应式 Python 运行时,用于构建可复现的 Python 程序(marimo 笔记本)。单元格通过其定义和引用的变量相互连接。运行一个单元格时,会按数据流顺序重新执行依赖项。活动运行时保存内核命名空间、单元格状态和数据流图。笔记本(.py 文件)是内核在会话运行期间根据该状态写入的产物。

用户通过包含单元格、输出和小组件的笔记本 UI 与同一个运行时交互。

警告。活动运行时才是事实来源。 会话期间,SHOULD NOT 直接修改关联的 .py 文件。文件编辑不会传到活动内核或用户,并且内核可能会在保存时覆盖这些修改。请使用 marimo._code_mode(cm)更改笔记本。读取磁盘文件没有问题,但要获取当前单元格代码,优先使用 ctx.cells[...].code。

运行环境会报告此 SKILL.md 的绝对路径。即使当前工作目录是笔记本工作区,也请从该文件的父目录解析随附的 scripts/... 和 reference/... 路径。在命令示例中,将 /absolute/path/to/marimo-pair 替换为该目录。

必需的首次内核命令

每个代码模式会话都要以此专用命令开始:

bash /absolute/path/to/marimo-pair/scripts/execute-code.sh \
  --url http://localhost:2718 \
  -c "import marimo._code_mode as cm; help(cm)"

对每个内核都按以下顺序操作,包括只读任务:

  1. 运行一次检查命令。
  2. 等待 help(cm) 成功输出。
  3. 然后在后续调用中使用 cm.get_context() 或其他 cm API。

检查命令成功之前,不要运行特定任务的 cm 代码。

连接到笔记本

使用随附的 execute-code.sh(位于报告的技能目录中)或 MCP(execute_code(...)),在正在运行的 marimo 内核中执行 Python。

execute-code.sh 始终需要 --url。如果用户提供了笔记本 URL,请直接针对该 URL 运行必需的检查:

bash /absolute/path/to/marimo-pair/scripts/execute-code.sh \
  --url http://localhost:2718 \
  -c "import marimo._code_mode as cm; help(cm)"

该命令成功后,通过 -c CODE、-(表示标准输入)或文件路径传入任务代码:

bash /absolute/path/to/marimo-pair/scripts/execute-code.sh \
  --url http://localhost:2718 - <<'PY'
import marimo._code_mode as cm

async with cm.get_context() as ctx:
    cid = ctx.create_cell("x = df.head()")
    ctx.run_cell(cid)
PY

如果用户没有提供 URL,请查找或启动笔记本。使用 bash /absolute/path/to/marimo-pair/scripts/discover-servers.sh、MCP list_sessions() 或本地进程上下文查找正在运行的服务,并将其报告的 url 传给 --url。打开一个笔记本时,脚本会自动定位到它;打开多个笔记本时,请通过 --file 指定笔记本的文件键。

如果没有正在运行的服务且用户需要笔记本,请使用 --no-token 启动 marimo(不要使用 --headless),以便自动注册供发现。必须打开笔记本 UI,execute-code 才能定位到它。正确的调用方式取决于上下文(项目工具、全局安装、沙盒模式)。如果笔记本文件包含 PEP 723 # /// script 标头,则 MUST 使用 --sandbox 打开,否则 marimo 会忽略内联依赖项。完整的决策树请参阅 finding-marimo.md,有关选择器解析、脚本、MCP 和 shell 引号,请参阅 execution-context.md。

临时环境的作用范围

execute-code 会在 marimo 的临时环境中执行 Python:这是一个临时命名空间,其中包含内核全局变量的浅拷贝。笔记本变量可以通过名称访问,但每次调用结束后,新的顶层绑定和重新绑定都会被丢弃。对笔记本拥有的对象进行原地修改可能会保留,因为这些名称仍然引用实时对象。

每次调用都会报告临时环境中的标准输出和标准错误,以及由其触发运行的笔记本单元格(包括响应式后代)的控制台输出。

普通 Python

使用临时环境中的普通 Python 检查变量、采样数据、测试转换、探查 API、检查导入项以及读取小组件状态。

print(df.head())

x = 10
print(x)

这里的 df 来自笔记本全局变量,而 x 是临时环境中的局部绑定。x 仅在本次调用期间存在,不会添加到笔记本全局变量中。

使用 cm 持久化

顶层暂存区赋值和重新绑定都是临时的。要持久化工作,包括新变量,你 MUST 通过 marimo._code_mode (cm)提交更改。

marimo._code_mode 是一个 PRIVATE、UNSTABLE 的 Agent API(注意开头的 下划线)。它的存在是为了让此类技能等工具能够从暂存区驱动实时内核。不要从笔记本单元格、库代码或用户会运行的任何内容中导入它——其方法可能会随 marimo 版本和内核而变化或消失。请将每个 import marimo._code_mode as cm 都视为仅用于暂存区。

打开代码模式上下文,以排队提交笔记本更改。

import marimo._code_mode as cm

async with cm.get_context() as ctx:
    cid = ctx.create_cell("x = df.head()")
    ctx.run_cell(cid)

暂存区支持顶层异步代码。直接使用 async with; 无需将其包装在 asyncio.run(...) 中,而且这样做可能会与 内核的事件循环冲突。

此代码块退出且新单元格运行后,x 就成为笔记本状态的一部分。后续 暂存区调用可以按名称读取 x。同一次暂存区调用中后续的代码 应读取 ctx.globals["x"],因为暂存区命名空间是在单元格运行之前复制的。

在上下文中,排队的变更方法是同步的。直接调用它们;不要使用 await。每次调用都会将一个操作排入队列,供 marimo 在上下文正常退出时应用。如果代码块引发异常,队列中的操作会被丢弃。

正常退出时,marimo 会应用包、验证并应用单元格结构变更、运行排队的单元格,然后可能运行依赖单元格。验证仅检查结构,因为排队单元格的运行仍可能出错。create_cell 和 edit_cell 只会更改单元格结构。执行请使用 run_cell。

create_cell 当前默认使用 hide_code=True,这会在 UI 中折叠代码编辑器。如果用户希望创建的单元格无需手动展开即可显示,请传入 hide_code=False。

marimo 规则

marimo 对笔记本代码施加了一套简明约定,以便将笔记本保持为有向无环图(DAG):

  • 无循环——单元格之间不能形成循环依赖。
  • 单元格之间不得重新定义公开名称——每个名称只能由一个单元格持有。
  • 禁止通配符导入——import * 会妨碍对定义进行静态分析。

这些规则可以确保内核、UI 和保存的产物保持一致。

当 cm 提交单元格正文时,marimo 会解析其顶层定义和引用。公开名称会进入图中。以下划线 _ 开头的名称仅属于其所在单元格,其他单元格无法使用。如果 cm 的编辑违反了约定,marimo 会拒绝结构变更并返回验证错误。

笔记本的结构

笔记本是按顺序排列的单元格集合。ctx.cells 是文档视图, ctx.graph 是数据流视图。

for cell in ctx.cells:
    cell  # .id, .code, .name, .config, .status, .errors

ctx.cells["setup"]         # by name
ctx.cells[0]               # by position
list(ctx.cells.keys())     # all IDs, in notebook order

单元格 ID 是不透明字符串,可以从笔记本中查询,也可以从 cm 的返回值中获取:

cid = ctx.create_cell("df = pd.read_csv('data.csv')")
print(cid)   # e.g. 'Hbol'

也可以按 name 为单元格指定名称并引用。可以利用图来了解其在数据流中的作用。

for cid, impl in ctx.graph.cells.items():
    impl  # .defs, .refs   (sets of public names)

ctx.graph.descendants(cid)   # cells that re-run when this one changes
ctx.graph.ancestors(cid)     # cells this one depends on

在 marimo 中,删除操作具有破坏性,因此删除前可以查询后代单元格,以了解删除的影响。

编写笔记本更改

图约定确保 marimo 能够运行和保存笔记本。仅通过这些检查并不能保证产物实用。提交的单元格仍应易读、可重新运行且可编辑。

进行持久化编辑时,应沿用笔记本现有的名称、导入、依赖项和 UI 模型。不要偷懒。避免采用只能通过 cm 验证、却会让笔记本变得脆弱的一次性变通方案。

单元格内容

提交属于该单元格的代码。

  • 提交单元格内容——create_cell 和 edit_cell 接收的是单元格内容, 而不是保存文件中的 @app.cell 包装器。
  • 替换前先读取——目前,其他编辑者可能会在暂存区调用之间更改单元格。 调用 edit_cell 前,先从 ctx.cells[...] 读取当前内容,并提交完整的替换内容。
  • 复用笔记本中的导入——如果 np 已存在,请使用它或编辑所属的导入单元格。不要仅为绕过依赖图而添加 import numpy as _np。
  • 每个公开名称只定义一次——一个公开名称只能有一个所属单元格。 在另一个单元格中重新赋值会导致 Multiply-defined names 错误;请编辑所属单元格,或为结果使用新名称。参见 gotchas.md。
  • 有意运行单元格——create_cell 和 edit_cell 只会更改结构。需要执行单元格时,加入 ctx.run_cell(...) 队列。
单元格边界

单元格也是重新运行的边界。将开销较大或可复用的计算放在展示内容的上游,这样修改 UI 时成本更低。简单的、特定于展示的工作,则可与视图放在一起,以便阅读。

仅当组合属于 UI 时才使用 mo.vstack 和 mo.hstack。 叙述内容通常放在相邻的 Markdown 单元格中更易阅读。

优先使用 cm 管理更改

有 cm API 时请使用。避免直接编辑文件、使用 shell 执行包管理命令,以及仅存在于暂存区的状态,因为这些更改需要持久化。

  • 不要编辑 .py 文件——在实时会话中,切勿对笔记本文件使用 Edit、Write 或 NotebookEdit。即使是小改动,也请使用 ctx.edit_cell(...)。
  • 通过 cm 管理包——请使用 ctx.packages.add() 或 ctx.packages.remove(),而不是直接使用 uv 或 pip;对于不明显的依赖项变更,请先确认。
  • 避免临时路径——除非工作本来就是临时的,否则持久化的单元格不应依赖 /tmp/...。
  • 有意删除——删除单元格会移除它定义的全局变量。方便时可复用空单元格,并删除编辑后留下的空单元格。
UI 和小组件

修改对象前先检查对象。不同的 UI 对象需要通过不同的途径更新。

  • 通过 cm 设置 mo.ui.*——在 cm.get_context() 内使用 ctx.set_ui_value(element, value)。
  • 直接设置 anywidget traitlets——同步的 traitlets 是 Python 属性,例如 widget.value = 5。

有关自定义可视化或交互式输出的设计,请参阅 rich-representations.md。

参考资料

评论

支持 Markdown