数据科学家日常最耗时间的不是建模,而是数据清洗、探索和反复试错。Jupyter Notebook 虽然普及,但单元格之间的隐式依赖和不可复现性一直是痛点。marimo 作为新一代 reactive notebook,已经在解决这些问题上走出了坚实的一步——而现在,它又往前迈了一层:marimo pair 把 coding agent 直接嵌入到 notebook 工作流里,让 agent 成为你的数据科学搭档,而不是一个需要反复 copy-paste prompt 的外部工具。
marimo 的 reactive 基础:为什么它比 Jupyter 更适合接 agent
Jupyter 的核心问题是:改了上面一个单元格,下面的单元格不会自动更新,运行顺序决定了结果,notebook 的"状态"藏在内核里,不可追踪。marimo 的做法完全不同——每个单元格是一个纯函数,变量之间的依赖关系被自动追踪,改了任何一处,所有下游单元格立刻重新执行。这意味着 notebook 的状态永远是确定的、可复现的。
这种确定性对 agent 尤其重要。agent 需要在一个可预测的执行环境里操作:它改了一段代码,必须能确认下游效果,而不是面对一个"不知道当前内核状态是什么"的黑箱。marimo 的 reactive 语义天然提供了这个保证。
marimo pair:agent 不再是聊天窗口里的旁观者
传统方式下,你和 AI 的协作是这样的:在另一个窗口写 prompt,拿到代码片段,手动粘贴到 notebook 里,运行,看结果,再回去写下一个 prompt。这个循环的摩擦很大——上下文在两个环境之间断裂,agent 看不到你的 notebook 状态,你也看不到 agent 的推理过程。
marimo pair 的思路是:agent 直接在 notebook 里工作。它能看到你的单元格、数据、输出,也能直接生成或修改单元格。你不再是"向 agent 提问",而是"和 agent 一起写 notebook"。具体来说:
- 数据清洗搭档:你描述清洗意图,agent 生成对应的 pandas/polars 操作单元格,你确认后它立刻成为 notebook 的一部分,下游自动更新。
- 探索性分析搭档:你指向一列数据,agent 生成分布图、统计摘要、异常检测的单元格,你选择保留或调整。
- 研究搭档:agent 帮你查找文档、解释 API 参数、生成调用示例,直接嵌入 notebook,而不是丢在聊天记录里。
关键区别在于:agent 的产出不是一段需要你手动搬运的文本,而是一个活的、会随数据变化自动更新的单元格。
实践:从安装到和 agent 协作写一个数据清洗 notebook
下面用一个完整的小例子展示 marimo pair 的实际用法。假设我们有一份脏数据需要清洗和初步探索。
安装和启动
# 安装 marimo(pair 功能在 0.9+ 版本可用)
pip install "marimo>=0.9"
# 启动 marimo notebook
marimo edit data_cleaning.py
注意 marimo 的 notebook 保存为 .py 文件,不是 JSON——这意味着它们可以直接被 Git 追踪、被 CI 运行、被 Python 导入。
手动写第一个单元格:加载脏数据
在 marimo 编辑器里新建一个单元格,输入:
import marimo as mo
import polars as pl
# 加载示例脏数据(这里用内联数据演示,实际可替换为 read_csv 等)
raw_df = pl.DataFrame({
"name": ["Alice", "bob", "CAROL", None, "Dave"],
"age": [28, -1, 35, 999, 22],
"email": ["alice@example.com", "invalid-email", "carol@test.org", None, "dave@demo.net"],
"signup_date": ["2024-01-15", "2024-02-30", "2024-03-10", None, "2024-04-01"],
})
raw_df
运行后你会看到表格输出。因为 marimo 是 reactive 的,这个变量 raw_df 自动对下游可见。
用 marimo pair 让 agent 生成清洗逻辑
在 marimo 编辑器中,你可以通过内置的 AI 助手(pair 功能)来生成下一个单元格。在对话区域描述意图,例如:
"raw_df 里有几个问题:name 列大小写不一致且有缺失值,age 列有 -1 和 999 这种异常值,email 列有格式错误,signup_date 有无效日期。帮我生成一个清洗单元格。"
agent 会生成类似下面的代码作为新单元格:
import re
from datetime import date
cleaned_df = raw_df.with_columns([
# name: 标准化大小写,填充缺失值
pl.col("name").str.to_uppercase().fill_null("UNKNOWN").alias("name"),
# age: 将 -1 和 999 替换为 null,后续可决定填充策略
pl.when(pl.col("age").is_in([-1, 999]))
.then(None)
.otherwise(pl.col("age"))
.alias("age"),
# email: 简单正则校验,不合法的标为 null
pl.when(pl.col("email").str.contains(r"^[\w.+-]+@[\w-]+\.[\w.-]+$"))
.then(pl.col("email"))
.otherwise(None)
.alias("email"),
# signup_date: 尝试解析,无效日期标为 null
pl.col("signup_date").str.to_date("%Y-%m-%d", strict=False).alias("signup_date"),
])
cleaned_df
由于 marimo 的 reactive 特性,这个单元格自动依赖 raw_df——如果你回头修改了 raw_df 的数据源,cleaned_df 会立刻重新计算。
继续让 agent 帮你做探索性分析
再向 pair 描述:
"对 cleaned_df 做一个快速概览:各列的缺失值计数、age 的分布统计。"
agent 可能生成:
# 缺失值统计
null_counts = cleaned_df.select([
pl.col(c).null_count().alias(c) for c in cleaned_df.columns
])
null_counts
# age 列分布统计(排除 null)
age_stats = cleaned_df.filter(pl.col("age").is_not_null()).select([
pl.col("age").min().alias("min"),
pl.col("age").max().alias("max"),
pl.col("age").mean().alias("mean"),
pl.col("age").median().alias("median"),
])
age_stats
每一段都是独立的单元格,输出自动渲染在 notebook 里。你可以保留、修改、删除,全都在同一个可复现的 .py 文件中。
保存和导出
# marimo notebook 自动保存为 Python 文件
# 可以直接作为脚本运行(跳过 UI)
python data_cleaning.py
# 也可以导出为 HTML 报告分享
marimo export html data_cleaning.py --output report.html
采纳建议与需要注意的边界
什么时候值得用 marimo pair:
- 你的数据工作以探索和清洗为主,需要频繁试错——agent 的即时单元格生成比手动写 prompt-复制-运行循环快得多。
- 你需要 notebook 可复现、可版本控制——marimo 的
.py格式和 reactive 语义解决了 Jupyter 的两个顽疾。 - 团队协作中需要"可运行的文档"而不是"只能看的报告"。
什么时候要谨慎:
- agent 生成的清洗逻辑需要你逐行审查。它可能选择和你意图不同的策略(比如用均值填充 vs 标记为 null),不要盲目接受。
- marimo pair 的 agent 能力依赖底层 LLM 服务,涉及数据隐私的场景要注意 API 调用是否把数据发送到外部。
- 复杂的、需要长时间运行的 pipeline(比如训练大模型)不适合在 notebook 里做——notebook 的优势是交互式探索,不是批量生产。
快速检查清单:
- ✅ 安装
marimo>=0.9,确认 pair 功能可用 - ✅ 用
marimo edit xxx.py启动,习惯.py文件即 notebook 的心智模型 - ✅ 先手动写数据加载单元格,再让 agent 生成下游逻辑——你掌控数据源,agent 辅助处理
- ✅ 审查 agent 生成的每一行代码,特别是填充策略和正则逻辑
- ✅ 利用 reactive 特性:改上游数据后检查下游是否如预期更新
- ✅ 完成探索后用
marimo export html生成可分享的报告
marimo pair 不是让 agent 替你做数据科学,而是让 agent 在一个确定性环境里和你同步工作——你定方向,它写细节,reactive 语义保证每一步的效果都是可见的、可追踪的。这种协作模式比在聊天窗口里来回搬运代码要高效得多。