跳转到内容

分析流程

本页是让智能体完成第一次分析之后的深入参考。

告诉智能体问题,不必安排分析步骤

Section titled “告诉智能体问题,不必安排分析步骤”

说明你想了解什么,并补充已经明确的指标、比较方式、范围或关注方向。智能体 会选择合适的类型化算子,在分析会话中推进调查,并记录证据。

例如:

分析上季度已完成订单收入相比去年同期下降的原因。先关注地区差异,最后说明主要结论、 支撑证据和限制。

你不需要选择算子、要求智能体调用 show(),也不需要提前设计检查点。

只要下一步仍在已经确认的指标和问题范围内,智能体就可以自行推进。需要更换指标、调整分析 对象或比较方式,或者引入可能明显改变结论的业务假设时,智能体应该停下来确认。

用业务语言确认或纠正这个选择即可。如果缺少语义定义,或者定义本身有误,智能体应该把 受影响的类型化分支停下来。智能体可以使用显式披露临时推断口径的终端 md.raw_sql(...) 继续,但不得在分析阶段修改语义层,并且必须在收尾请求批准最小的 正式修订。

分析结束后,重点看三件事:结论是否回答了问题,重要判断是否有清楚的证据,已经说明的限制 是否影响结论用于当前决策。需要检查或集成运行时的读者,可以继续查看下面的技术参考。

每次 Marivo 分析都从受治理的语义输入开始,并按写-运行-读循环推进: 智能体写出一个意图(一次操作调用及其参数),运行它,读取类型化结果,再决定 下一步。大多数分析从 session.observe(...) 开始,再进入指标比较、归因、发现或质量检查。 事件路径与基于重放的生命周期分析属于靠后的专项路径。

运行时由以下部分组成:

  • 会话 持有指导问题、语义目录,以及每一步的持久化结果。
  • 操作调用 是一次确定性的分析步骤,通常使用 observecompareattributediscover.<objective>;需要专项路径时再使用 events.match 等 命名空间操作。调用参数就是分析规格。
  • 产物 是操作的类型化结果。产物是步骤之间的边界:每个操作消费 特定产物家族,并产出另一个。
import marivo
import marivo.analysis as mv
import marivo.semantic as ms

分析需要新建或刚修改的对象时,先完成该变更的编写收尾: md.inspect(...) → 显式范围 → 一次 inspection.sample(...) → 快照 投影 → 一个 Python 声明 → 当前条目 → catalog.verify(entry)catalog.preview(entry, using=snapshot) → 零查询 catalog.readiness(refs=[entry])。 等价的精确引用形式仍然有效。日常分析复用未变化的对象时,不重复这条编写 证据流程。非常见格式和语义判断仍由智能体负责。

session = mv.session.get_or_create(name="revenue-investigation", question="Why did Q4 drop?")
catalog = session.catalog
revenue = catalog.metrics.get("sales.revenue")
region = catalog.dimensions.get("sales.orders.region")
revenue.details().show() # 读取定义、候选轴和 measure lineage
region.details().show()

get_or_create 是幂等的:它会附着到同名的已有会话,或创建一个新会话,并设为当前 会话。 会话的精简 API 为 get_or_createcurrent()recent()inspect(name)delete(name)

复用同名会话但传入不同的非空 question 会触发 SessionQuestionMismatchWarning, 并保留原 question——请传入新名称来开始新的分析,或省略 question 以恢复现有会话。

repr(session) 会引导调用 session.show();该方法打印包含问题、读写状态、报告时区、 时间戳、已注册直接操作、命名空间以及目录/job/frame 检查入口的有界 状态卡片。session.render() 返回同一文本,但不写 stdout。

catalog.domains.show() 和限定范围的 navigation 发现条目,然后对本次分析会使用的每个 指标、维度和时间维度调用 .details().show()details 输出会展示已编写 的 ai_context;指标详情还会展示组合、有效实体、候选轴和度量 血缘。候选轴只用于静态发现,关系与扩散的有效性仍以 observe 为准。 .contract() 只表示验证/预览/就绪检查等机械下一步;.render() 返回的有界卡片 与 .show() 打印的内容相同。如果所需对象缺失或定义存在争议,首次 observe 前先回到 语义编写处理。

MetricFrame.show() 会展示这些持久化执行事实:观测窗口、粒度、轴、 切片,聚合、可加性、重新聚合状态,以及折叠策略、状态轴、 样本区间或快照身份键。采样折叠会显示预期槽位覆盖率; 无采样的快照选择会明确说明预期-样本覆盖率不适用。恢复后的 frame 使用同一份元数据展示这些事实,不会重新查询。

操作接受精确的当前条目/引用、封闭表达式和若干共享值对象。多数意图都会复用这些输入:

形态含义
指标输入catalog.metrics.get("sales.revenue")精确当前 MetricEntryRef[metric],也可传 observe 使用的封闭 RuntimeMetricExpr;拒绝过期/跨目录条目、通用引用与字符串。
维度输入catalog.dimensions.get("sales.orders.region")精确当前维度/时间-维度条目或匹配的引用(见 语义引用)。用于 dimensionsslice_by 键与轴。
time_scopemv.time_scope(start="2026-10-01", end="2027-01-01")半开时间区间 —— start 含、end 不含。
grainmv.grain("day")ms.calendar_grain(...)统一的时间桶值。存在 ⇒ 时间序列或面板。
dimensions[region, country]分段轴。v1 中全部必须解析到指标所属实体。
slice_by{region: "US"}{amount: {"op": ">", "value": 100}}聚合前的行过滤(见下方运算符)。
AlignmentPolicymv.window_bucket()compare / correlate 时两个窗口如何配对。

必须按字面使用传入的 time_scope.end。Marivo 不包含该边界,智能体也不应为了 把请求解释成闭区间而擅自把结束日期向后推进。例如,end="2026-06-30" 会排除 6 月 30 日;只有当预期范围确实包含整个 6 月时,才使用 end="2026-07-01"

结果的 语义类型graindimensions 决定:scalar(两者都没有)、 time_series(仅粒度)、segmented(仅维度)或 panel(两者皆有)。

键是目录维度;值为标量(==)、列表(in),或结构化的 {"op": ..., "value": ...} 形式:

形式含义
"US"==(相等)
["US", "CA"]in(成员)
{"op": "!=", "value": "US"}不等
{"op": ">", "value": 100}>=<<= 同理)数值比较
{"op": "between", "value": ["2026-07-01", "2026-09-30"]}闭区间(恰好两个值)

任何分析都从这里开始:在某个时间范围和/或分段上物化一个指标。

参数类型必填默认含义
metric`MetricEntryRef[metric]RuntimeMetricExpr`,或非空序列
time_scopeTimeScopeNonemv.time_scope(...) 返回的范围,或精确目录周期 lookup 返回的范围。
grainGrainNonemv.grain(...)ms.calendar_grain(...) 返回的统一值。
dimensions条目/引用序列None精确当前维度/时间-维度条目或引用。
slice_by以条目/引用为键的 mappingNone全局聚合前行过滤。
time_dimension时间-维度条目/引用实体默认当实体声明多个时间轴时选择其一。
expect_shape形状None守卫;若预测形状不符,在访问后端前抛错。

当前条目也可以直接用于 Session.attribute(axes=...)、发现的 search-space/peer 轴,以及变换的 slice/rollup 轴。所有符合条件的调用都会在 规划、持久化、证据或重放前把条目归一化为规范引用; catalog.require(ref) 仍是从配置、日志或持久化状态恢复身份时使用的严格仅引用 路径。

current = session.observe(
revenue,
time_scope=mv.time_scope(start="2026-10-01", end="2027-01-01"),
grain=mv.grain("month"),
dimensions=[region],
)

当时间请求没有候选时间维度、把普通维度当作时间轴、存在多个 歧义候选、不同指标根解析出不同的隐式时间轴,或 encoding/粒度不兼容时,只要 编译后的目录事实足够,Marivo 就会在后端执行前失败。结构化修复建议会 暴露精确的当前候选项,不会猜测时间轴;只有替换项机械唯一时才提供 retry。

传入序列即可在一个 frame 中观测多个相同-范围指标。时间根必须解析到同一个 精确时间-维度引用;Marivo 会把同一数据源的指标合并为一次查询,并把 兼容的跨数据源指标按该共享时间轴 outer-连接。用 frame.metric(id) 投影出 arity-1 frame,对单个指标做 drill-down。

单指标 MetricFrame 的所有公开读取路径都使用指标短名作为值列名,包括 show()columnscontract()、索引和 to_pandas();经过变换后列名 仍保持不变。若短名与轴列冲突,Marivo 会改用限定后的指标 id;若该名称仍 被占用,则追加确定性的 #N 后缀。多指标 frame 仍保持每个指标一列, 因此不同 arity 的 frame 可以直接 merge,无需单独 rename。用 frame.value_columns 可读取准确的公开值列名。

每个产物卡片还会直接打印 output_columns,其顺序和名称与 .columnsto_pandas() 完全一致。机械契约通过 contract.output_columns 暴露同一 元组,并直接从 contract.artifact_schema.columns 推导,因此不会发生漂移。对于指标、 事件和生命周期产物,contract.semantic_inputs 会记录保留的语义角色 与路径、绑定的输出列、精确的 session.catalog.<collection>.get("<path>") 重新获取调用及对应专项目录 帮助目标。卡片也会渲染相同的获取调用,智能体无需再从结果列名猜测目录 集合。

类型化变换内部仍使用规范 value 列。例如 frame.transform.filter(predicate=lambda data: data["value"] > 0) 仍然有效; 之后可通过 filtered[filtered.value_columns[0]]filtered.to_pandas() 读取结果。

report = session.observe(
[revenue, catalog.metrics.get("sales.total_orders")],
time_scope=mv.time_scope(start="2026-10-01", end="2027-01-01"),
grain=mv.grain("month"),
)
revenue_only = report.metric("sales.revenue")

当下游能力要求单指标时,多指标 frame.contract() 会为每个 carried 根 提供一条可执行的完整 ID frame.metric(...) 投影,但不会代替智能体选择。

当目录没有需要的临时分析口径时,可构造封闭表达式,并且仍只通过 session.observe(...) 物化:

failed = mv.runtime_metric.aggregate(
failed_requests_measure,
agg="sum",
slice_by={state: "failed"},
label="Runtime failed requests",
)
weighted_latency = mv.runtime_metric.weighted_mean(
latency_measure,
request_count_measure,
label="Runtime weighted latency",
)
failure_rate = mv.runtime_metric.ratio(
failed,
requests,
zero_division="null",
label="Runtime failure rate",
)
frame = session.observe(failure_rate, time_scope=window, grain=mv.grain("day"))

运行时表达式可以递归包含运行时表达式与目录 Ref[metric]runtime_metric.slice(..., by=...) 是分支-本地,observe(..., slice_by=...) 是 global。目录/运行时根都会 lower 到同一规范图(最大深度 10, 提交的 pre-CSE 发生项最多 256)以及一个模型/来源领域。label 只影响 展示,不会变成值身份或目录权威来源。每个 runtime_metric 构造器都 必须设置非空 label,包括嵌套表达式;当表达式作为已观测根物化时, 该 label 成为稳定的公开值列 handle。下游操作根据持久化 frame 状态准入,不根据 目录/运行时来源准入。

runtime_metric.weighted_mean(value, weight) 会自动完成行级乘权与成对聚合。 两个度量必须属于同一实体和物理行粒度,权重必须为可加;无需预先 定义 SUM(value * weight) 度量。

每个已观测根与 mixed forest 都会持久化递归组件图。可通过 frame.components() 检查 node 角色、evaluator 契约、质量、覆盖率引用 与受治理的 leaf 血缘;只有同时支持数值分解的根才会另外带有 component_ref

对于 single、派生、累计和多指标 frame,观测时得到的可加性、 聚合与状态-时间语义都会参与产物身份。升级后重新执行 observe 时,不会复用缺少当前语义门的旧缓存 frame。

量化两个 observe 结果之间的变化(当前减 baseline)。两个 frame 必须共享语义 类型与持久化 comparable 值语义;目录/运行时身份可以不同。

参数类型必填默认含义
currentMetricFrame当前期 frame。
baselineMetricFrame基线期 frame。
alignmentAlignmentPolicywindow_bucket桶/分段如何配对。
baseline = session.observe(
revenue,
time_scope=mv.time_scope(start="2025-10-01", end="2026-01-01"),
grain=mv.grain("month"),
dimensions=[region],
)
delta = session.compare(current, baseline)

compare 默认用 window_bucket 配对桶。传入 alignment= 时只能选择一个 封闭辅助函数:mv.window_bucket()mv.day_of_week()mv.period_progress()mv.period_correspondence()mv.occurrence_progress()mv.working_day_progress(schedule=...)。后五者要求源框架携带完全匹配的已认证 权威来源(前四者是周期或 TemporalSet 权威来源,working_day_progress 使用一个精确 WorkSchedule 快照); 不再使用分析本地 holiday-calendar 参数。

其中 mv.occurrence_progress(anchor="start" | "end") 只接受由精确 TemporalSet 发生项范围选择的 day-粒度时间-series 或面板 frame,按发生项 有效本地日从首日正向或从 exclusive end 反向配对,不会推断 recurrence 或名称对应关系。 裸 built-in day 粒度仍使用各自会话的报告时区;要通过 occurrence_progress admission,必须与发生项边界时区一致。

working_day_progress 只接受 day-粒度时间-series 或面板 frame,排除认证快照中的非工作日, 按从选定范围起算的零基工作日 ordinal 配对,并在对齐证据中记录排除和 unmatched ordinal。

只有两侧源框架的可加性、聚合与状态-时间三项语义完全一致, 且可加性已知时,差值才会携带这些语义。任一项缺失或不一致时,语义门保持 未知,使后续归因失败即停止,而不是继承单侧语义。

把差值的变动归因到显式轴上。这是“为什么变了?”分析的默认公共入口。 组件-感知的比率和加权平均差值会使用混合归因。普通 分段非线性 point estimate 仍不能直接按轴求和。对于图-owned 的 count_distinctmedianpercentile(q) 根,只有已持久化归因 basis 明确 admission 时,才使用各自的 distribution-感知方法。

DeltaFrame 会持久化归因门所需的语义;执行归因时,Marivo 不会回查一个可能已经 变化的目录。若请求的轴尚未物化,Marivo 也会先校验原始差值,再重放 observe 来补齐轴。DeltaFrame.show() 会直接显示归因是支持还是 blocked; DeltaFrame.contract().attribute_admission 是这一机械边界的唯一类型化来源。 未知与普通不可加差值标记为失败;半可加差值 要求轴排除状态时间维度;已持久化的比率/加权平均组件 路径仍保持可用。

已持久化的指标语义轴归因
additive支持 sum 与 hierarchy 归因。
semi_additive支持非时间轴;在其 status_time_dimension 上拒绝。
组件-感知 ratio / weighted_mean支持比率/加权混合归因。
基于度量的 Tier-1 meanobserve 时自动展开为 sum(measure) / count_non_null(measure),并支持加权混合归因。
图-owned count_distinct对可重现的标量键使用 distinct membership;distinct 原始键始终留在数据源。
图-owned median / percentile(q)支持 DuckDB 精确值-frequency 或 Trino mergeable qdigest replacement;ClickHouse reservoir sampling 保持 blocked。
其他 non_additive 指标拒绝,包括 opaque/tier-2 mean、min、max、不受支持的分位数来源与不可加 linear 组合。
缺少可加性元数据拒绝;重新执行 observecompare 后再归因。

mean 展开使用度量的非空计数,而不是实体行数。对于被拒绝的指标,应按 DeltaFrame.contract().attribute_admission 的修复建议处理:重新 observe 旧版产物, 或补齐该 aggregate 明确要求的组件/distribution 证据。

参数类型必填默认含义
frameDeltaFrame要归因的差值。
axeslist[dimension]进行归因的分段轴或时间轴。
mode"joint" | "hierarchy" | "multiresolution"仅多轴必填可加/组件允许 jointhierarchy;不可加允许 joint 或独立 multiresolution。合法组合以差值 admission 为准。
attribution = session.attribute(delta, axes=[region, platform], mode="joint")
attribution.show()

标准单轴调用不传 mode

attribution = session.attribute(delta, axes=[region])

joint 的贡献行可直接求和并与总变化对齐。hierarchy 的父级行会重复 子级总量,核对总变化时只对最深层求和。组件-感知的比率和加权平均 在两种模式下都会保留 value_effectmix_effectresidual。 多轴调用没有默认 mode;单轴应省略 mode,即使传入也不会改变输出。 单轴输出会保留解析后的维度列名(例如 cluster),因此可以直接按该列与来源 DeltaFrame 连接。带命名空间的 attribution_levelattribution_axisattribution_driverattribution_path 列仅用于多轴 hierarchy 输出;可加 单轴归因的 attribution_shape"sum"。 保留的维度名不能与归因结果、值或面板桶列冲突;发生冲突时,Marivo 会失败即停止并返回结构化语义-编写修复建议,而不会生成重复列或含义 歧义的列。证据协议字段通过元数据显式映射,不会占用用户维度名。 attribution.attribution_mode 表示行布局,而 attribution.attribution_shape / meta.method 表示归因数学,因此两种布局都可能显示 method="weighted_mix"。可调用 marivo.help("analysis.AttributionMode") 查看这一专项契约。

去重计数 membership 在数据源内完成 (key, partition) 去重,并按 1 / membership_degree 分配;原始键不进入 frame、job、血缘、证据、 遥测、日志或错误。分位数会独立重放无业务维度的 endpoint,而不是汇总 segmented point estimate。DuckDB 对值-frequency 使用精确 Shapley(不超过 8 个 分区)或 128 次确定性 permutation(9–64 个);Trino 在服务端 merge qdigest。 任一中间 coalition 为空、分区超过 64、frequency 行超过 250,000,或无法复现 独立 endpoint 时,本次调用结构化阻塞且不持久化归因产物。

mode="multiresolution" 表示“独立多分辨率归因”:每个有序轴 prefix 都是独立重算、 排名和对账的 game,完整结果不能跨解析求和。消费前用精确语义-引用 prefix 选择 immutable、无查询 view:

regional = attribution.at_resolution(axes=[region])
regional.to_pandas() # 每个 comparison bucket 只求和一次

贡献 share 会显式说明分母:

  • share_of_total_delta 保留符号;当某个驱动因素抵消整体净变化时,它可以超过 100%;
  • share_of_positive_pool 只为正贡献填值;
  • share_of_negative_pool 只为负贡献填值,并返回正的池内占比。

Marivo 不会把任一符号池直接命名为“改善”或“恶化”,因为当前持久化指标契约 不包含指标好坏方向;只有明确业务方向后,智能体才能加上这层解释。new/churned 分段 会保留精确的单边组件贡献。AttributionFrame.show() 会显示对账行,包括 总计差值、贡献合计、one-sided 贡献合计、未归因合计和 residual;若任一 最深层分区无法在数值容差内对齐,归因会直接失败,不会输出表面合计为 100% 的结果。

在对齐后的桶上度量两个指标之间的关联。

参数类型必填默认含义
abMetricFrame要关联的两个 frame。
measure_ameasure_bstrframe 度量各 frame 的 value_columns 返回的公开值列名。
alignmentAlignmentPolicywindow_bucket桶配对。
method"pearson""spearman""kendall""pearson"相关性计算方法。
lag_rangerange 或有符号整数序列滞后期 0要计算的滞后期;k 表示 a[t]b[t+k]。正值表示 a 领先 b,负值表示 b 领先 a

非零滞后期要求输入为 time_seriespanel。面板滞后期只在各维度序列内偏移, 不会跨序列边界配对;空值会在偏移后按配对过滤,因此缺失桶不会压缩时间轴。 每个滞后期至少需要两个重叠且非常量的配对。结果为每个滞后期生成一行,并将绝对 相关性最强的滞后期记录为 meta.best_lag

省略 measure_a / measure_b 时使用各 frame 唯一的指标值;显式指定时, 传入 a.value_columnsb.value_columns 返回的准确公开名称。

将一个时间序列或面板向前投影。若 history 使用已认证的语义 粒度,forecast 会保留精确的 SemanticPeriodBindingV1:预测周期按认证序数 连续推进,未来键与 [start, end) 边界直接来自同一快照,不会用固定频率近似。

参数类型必填默认含义
historyMetricFrame(time_series/面板)连续历史,无 NaN。
horizonint要投影的周期数(≥ 1);语义粒度按认证周期序数计数。
model"naive" | "seasonal_naive" | "drift""seasonal_naive"预测策略。
seasonality_periodint按粒度季节序数距离。内置默认值为 day=7、week=52、month=12、quarter=4;语义粒度使用 seasonal_naive 时必须显式提供。
interval_levelfloat0.95预测区间的置信水平。
measure_columnstrframe 度量history.value_columns 返回的公开值列名。

面板中每个序列必须覆盖相同且连续的训练周期。语义粒度若出现不完整或不匹配 的周期、精确快照不可用、不支持的模型或未来覆盖率不足,forecast 会在写入 产物前抛出 ForecastShapeUnsupportedError。它不会静默补零、猜测季节性,或按 历史平均时长制造边界;内置面板的缺桶仍抛出 ForecastInputQualityError

history = session.observe(
revenue,
time_scope=mv.time_scope(start="2026-01-01", end="2026-04-01"),
grain=mv.grain("day"),
)
projection = session.forecast(history, horizon=30, measure_column=history.value_columns[0])

对一个产物运行质量检查,返回一个独立的 QualityReportartifact.quality_summary 只是 cheap、已持久化的元数据投影;它不等同于执行 session.assess_quality(artifact)。 使用 report.overall_statusreport.blocking_issue_countreport.warning_count 读取权威质量判定与计数。report.state 仍是描述 物化的 ArtifactState 元数据,不是质量状态。 对于累计 comparable-周期 DeltaFrame,报告直接读取类型化 pairing 证据,并呈现 matched-null、unpaired 和回退计数。 报告的 contract().issues 包含类型化数据质量失败。存在 blocking 的 time_coverage_incomplete 问题时,不得把该窗口作为完整周期事实报告。

参数类型必填默认含义
frame支持的分析产物要检查的 frame,包括指标 DeltaFrame。

对一个指标在两期之间均值是否变化做配对检验。

参数类型必填默认含义
abMetricFrame当前期与基线期 frame。
hypothesis"mean_changed""mean_changed"检验类型(v1)。
value_avalue_bstrframe 度量各 frame 的 value_columns 返回的公开值列名。
alignmentAlignmentPolicywindow_bucket检验的配对方式。
samplingSamplingPolicy推断配对/最小样本规则。
alphafloat0.05显著性水平。

发现 —— session.discover.*CandidateSet

Section titled “发现 —— session.discover.* → CandidateSet”

发现操作在一个产物中搜索确定性候选行,并返回 CandidateSet。有分数的 objectives 使用确定性 score order;本体 hypothesis 不打分,按确定性语义身份排序。 两种顺序都不是 Marivo 的推荐。

辅助函数源形状必填关键选项
point_anomaliesMetricFrame time_series/面板valuethreshold=3.0
period_shiftsDeltaFrame time_series/面板≥ 7 个桶valuethreshold=2.0
driver_axesDeltaFramesearch_spacevaluelimit
interesting_slicesMetricFrameDeltaFramesearch_spacevaluethreshold=2.0limit
interesting_windowstime_series/面板 framevaluethreshold=2.0
cross_sectional_outliersMetricFrame segmented/面板peer_scopevaluethreshold=3.0
semantic_hypotheses单一目录指标的 MetricFrame 或同指标 DeltaFrame就绪的可选本体limit=501..200
series = session.observe(
revenue,
time_scope=mv.time_scope(start="2026-01-01", end="2026-04-01"),
grain=mv.grain("day"),
)
candidates = session.discover.point_anomalies(series, threshold=2.0)
candidates.show()
item_id = str(candidates.to_pandas().iloc[0]["item_id"])
selection = candidates.select(item_id=item_id)
print(selection.kind, selection.window, selection.keys)

select(item_id=...) 返回与候选形状对应的封闭、不可变选择 variant, 不接受数字 rank。选择不会创建 job、产物、血缘步骤或证据记录。

当源产物的 contract() 给出可选本体后续路径时,把它作为不打分的 hypothesis 路径使用:

hypotheses = session.discover.semantic_hypotheses(series, limit=50)
hypotheses.show() # edge 含义、guardrails、exclusions 和可复制的 item_id
candidate = hypotheses.select(item_id="candidate_<full-sha256>")
driver = session.observe(candidate, analysis_purpose="test a reviewed hypothesis")

选中的 OntologyMetricCandidate 继承源观测范围,任何范围 override 都会被拒绝。 本体 edge 和候选不会生成证据项,也不构成因果证明。只有显式 observe 并运行合适的 下游操作后,才会产生统计证据。

其他 scored 发现选择不能传给 session.observe。它们表示源产物中已经计算出的 行、周期、轴、slice、窗口或 peer,而不是仍需物化的新指标。

变换在保持 frame 家族(MetricFrameMetricFrameDeltaFrameDeltaFrame)的前提下重塑 frame。每个变换是 frame 自身的方法——调用 frame.transform.<op>(...),而非会话级别的辅助函数。

变换关键参数效果
filterpredicate(callable)保留谓词为真的行。
sliceslice_by(轴 → 值/列表/区间)保留匹配精确轴值的行。
rollupdrop_axes 和/或 grain删除轴并重新聚合,或将时间轴重新分桶到更粗的 grain。累计 frame 取每个周期的最后一个桶(rollup_fold="last")。
topkbylimit按某个度量取前 N 行。
bottomkbylimit取后 N 行。
rankbymethodrank_column按某个度量排序并加一列 rank。
normalizemodebaselineindex / share / pct_change / per_unit / z_score(仅 MetricFrame)。
windowwindow限制到一个时间窗口。

对于差值 frame,bottomk(by="delta") 返回最大下降项,因为它选择最负的差值值。

累计 MetricFrame 存储累计值,其语义取决于累计锚点(all_historygrain_to_datetrailing)。

  • show()contract()transform.window(...) 在累计 frame 上正常工作。contract()show() 会呈现锚点特定的注意事项,因此在你正在阅读的 frame 上即可看到允许的路径。
  • correlatediscoverassess_qualityhypothesis_test 允许使用。尾随 frame 是独立的 窗口聚合(不是累计值),因此这些意图会产生有意义的结果。单调趋势注意事项仅适用于 all_historygrain_to_date 锚点——解读结果时需留意。
  • compare 按锚点分派:
    • all_history:当两个 frame 共享有效锚点,且业务坐标能够按精确 evaluation_end cutoff 配对时允许。结果是当前减 baseline 的观测层级差,不声明等价于区间流程;来源 revision 未验证。
    • trailing:当两个 frame 的固定时长规范化后相同时允许;例如 7 day1 week 可直接比较。
    • grain_to_date:对单周期、边界锚定的窗口允许。窗口必须从重置边界开始,至多跨越一个 重置周期,且两个 frame 必须共享重置粒度和查询粒度。
    • 两类可比期间都支持窗口序号对齐,以及星期位置、节假日位置、节假日优先再星期位置对齐。 grain_to_date 的日历策略 period 必须等于重置粒度。本版本不公开工作日位置比较模式。
  • 对派生指标,仅当所有外层组件都是累计,且完全共享同一个锚点(包括 all_history)时,才复用上述比较路径。混合锚点、累计与非累计混合、非法元数据, 以及当前/baseline 锚点不一致都会失败即停止。all-history 组件 sidecar 使用 parent 的精确配对业务坐标集合。
  • attribute 只接受由 compare 产生的当前累计 DeltaFrame。业务维度会重放相同的累计观测并 解释端点层级变化;只请求累计 over 轴时,直接 sum/count 结构使用可加基础-流程 bridge。共享同一锚点的比率与加权累计差值支持业务轴组件混合,但不支持 时间 bridge。时间与业务轴混合、累计 count_distinct 会失败即停止;请读取 delta.contract().cumulative_attribution 中的类型化 route 状态。
  • decomposeforecast 仍拒绝累计 frame。当类型化 route 支持时应对当前累计差值使用 attribute;其他分析则重新 observe 基础流量指标。
  • transform.rollup(...) 通过 rollup_fold="last" 重新聚合累计 frame:时间轴被重新分桶到目标 grain,每个周期贡献完整的最后一行(包括 evaluation_end)。既不可重新聚合、又未携带 rollup_fold 的 frame 会被拒绝——请在目标粒度重新 observe。

每个直接或结构完整的派生累计行都携带系统保留、以 UTC 序列化的 evaluation_end 坐标。对于 all-history 差值,请读取精确的 current_evaluation_endbaseline_evaluation_end 列。单侧坐标会被删除并计入 delta.meta.alignment["cumulative_pairs"];已配对的 null 仍保持配对并产生 null 差值。

尾随与粒度-to-日期差值通过 delta.meta.cumulative_alignment 暴露双方原始锚点、 身份计算使用的规范锚点,以及已配对 null、当前单侧、baseline 单侧和回退的精确计数。 最终差值只保留已配对坐标,面板 frame 也遵循同一规则。

cum_frame = session.observe(
cumulative_active_users,
time_scope=mv.time_scope(start="2026-01-01", end="2026-04-01"),
grain=mv.grain("day"),
)
cum_frame.contract().show() # 显示锚点特定的注意事项
windowed = cum_frame.transform.window(window={"start": "2026-02-01", "end": "2026-03-01"})
# 将累计 frame rollup 到更粗的粒度(周期末语义):
monthly = cum_frame.transform.rollup(grain=mv.grain("month"))
# 对于 attribute/forecast,请改为 observe base metric:
base_frame = session.observe(active_users, ...)

当受治理的度量或目录指标足以表达临时口径时,应先使用封闭的 mv.runtime_metric 表达式。只有计算仍无法通过 session.observe(...) 表达时, 才使用 md.raw_sql(...)——唯一的终端 原始 SQL 执行路径。它强制 timeout、限制返回行数、只读执行。结果是一个 RawSqlResult,无法回到类型化分析;调用 RawSqlResult.to_pandas() 获取终端 pandas 导出。它的 ordered columnsshaperow_count 只描述有界返回行: row_count == shape[0] == returned_row_count。读取行数时还应同时检查 requested_limitis_truncated。它没有 .contract() 或类型化可用操作。

任何语义缺口也可以无需事前审批使用该出口。智能体可以临时推断指标、维度、 关系或过滤口径,但必须记录全部假设,并将原始-SQL-支持 claims 与规范 产物证据分开。

import marivo.datasource as md
result = md.raw_sql(
ms.ref.datasource("warehouse"),
"SELECT region, SUM(amount) AS revenue FROM orders GROUP BY region",
reason="自定义收入拆分",
)
result.show()
df = result.to_pandas()

需要从类型化 frame 做终端本地 pandas 工作时,使用 artifact.to_pandas()md.raw_sql(...) 结果和 to_pandas() 导出都无法回到类型化分析。原始 SQL 不会修复缺失的 业务语义;收尾仍然必须请求批准最小语义修订。 当前 Marivo 操作接口不支持类型化 regression;回归任务仍是显式的终端 custom 分析,不能提升回类型化链。

智能体读取产物时按层下钻,以降低跨 loop 成本:

repr(delta) # 廉价单行提示
delta.show() # 有界当前状态读取
delta.contract().show() # 接口兼容性与类型化 issues
delta.to_pandas() # 终端导出,用于自定义本地分析

当操作产生证据时,artifact.show() 会在结果预览之前展示有界的类型化 摘要。同一个值可以从 artifact.evidence_digest 读取;通过 artifact.evidence_status 检查降级。精确证据项与 derivation 追踪使用 session.evidence

artifact.contract().affordances 查看接口兼容性。每个可用操作携带 capability_id(稳定的注册表 id)、public_entrypointhelp_target、 保留参数角色的 inputspreconditionsexpected_output_family。每个输入 记录准确参数名、接受的产物家族,以及当前产物能否绑定它。契约 不排序、不推荐、不枚举调用,也不决定下一步。artifact.contract().boundary_ports 列出类型化的终端出口端口 (例如 boundary.to_pandas),并附带 preservesdoes_not_preserve 保证。

当操作失败时,它会抛出一个类型化的 AnalysisError 子类。每个错误都携带稳定的 类型化字段——expectedreceivedlocationrepair——而不是一个通用的 详情包。repair 字段是一个类型化的 AnalysisRepair 对象,包含 kindretryinspectuser_choicesemantic_authoringenvironment)、 actionhelp_target、可选的 snippet 和从实时状态生成的可选 candidatesuser_choice 表示仍有多个机械上合法的方案,需要业务判断选择其一。 按照修复指引操作即可;它会指向失败能力的确切 marivo.help("analysis.<target>") 目标。

事件后续路径错误沿用同一契约:匹配策略和步骤选择使用 user_choice;过期产物与未知覆盖率使用 inspect;不安全的 关系路径或时间编写缺口使用 semantic_authoring

Marivo 维护一条类型化分析链。终端出口是 frame.to_pandas()md.raw_sql(...)frame.to_pandas() 返回防御性副本用于自定义本地分析;md.raw_sql(...) 返回 RawSqlResult,其 RawSqlResult.to_pandas() 是自身的终端 pandas 出口。两者都不 保留血缘或证据连续性。终端结果无法回到类型化分析。如果缺少业务语义, 只能用终端原始 SQL 提供临时结果,并在收尾保留缺口,获得批准后再编写。

类型化分析在符合条件的运行时边界接受精确当前目录条目或稳定的 语义引用,并立即把两种形式归一化为引用,绝不猜测业务含义。如果缺少或有争议的 指标、维度或时间维度,受影响的类型化分支会停止。智能体只可以在终端 md.raw_sql(...) 分支临时推断口径,并必须披露数据源、目的、全部假设、有界结果,以及规范身份、 血缘和证据 continuity 的缺失。它在收尾提出最小正式语义修订,并且只在用户 明确批准后进入 marivo-semantic。语义层拥有业务对象契约;分析层拥有基于这些对象的 类型化计算。

后续脚本需要前序结果时,读取持久化事实,而不是重跑上游查询:

summaries = session.frame_summaries(limit=20)
jobs = session.recent_jobs(limit=5)
previous = session.get_frame(ref)
# 仅在恢复任务、问题明显重复或同类错误再次出现时查看历史:
history = mv.session.recent(limit=10)
prior = mv.session.inspect(history.items[0].name) if history.items else None

frame_summaries() 返回 FrameSummaryPagehas_more 为 true 时把不透明的 next_cursor 传回同一个方法。分页采用普通 keyset 语义,不提供快照隔离。

使用 marivo.help("analysis.recovery") 查看这些真实的会话成员。recoveryartifacts 是帮助主题,并不是 session.recovery / session.artifacts 命名空间。

每个已提交操作都会把确定性的类型化证据写入会话。Marivo 不生成跨产物 判断,也不选择下一步操作。

  • session.evidence.digests(...) —— 有界、最新优先的摘要页面。
  • session.evidence.findings(...) —— 有界、最新优先的证据项页面。
  • session.evidence.digest(ref).finding(id).trace(id) —— 精确读取与证据项 derivation 审计。

完整模型见 证据链

import marivo.analysis as mv
session = mv.session.get_or_create(name="revenue-check", question="Why did Q4 drop?")
catalog = session.catalog
revenue = catalog.metrics.get("sales.revenue")
region = catalog.dimensions.get("sales.orders.region")
current = session.observe(
revenue,
time_scope=mv.time_scope(start="2026-10-01", end="2027-01-01"),
grain=mv.grain("month"),
dimensions=[region],
)
baseline = session.observe(
revenue,
time_scope=mv.time_scope(start="2025-10-01", end="2026-01-01"),
grain=mv.grain("month"),
dimensions=[region],
)
delta = session.compare(current, baseline)
attribution = session.attribute(delta, axes=[region])
attribution.show()

拿到 delta 后,可以继续分支:用 session.discover.period_shifts(delta) 找出变化发生在何时, 或用 session.forecast(current, horizon=3) 向前投影。

大多数问题可以通过指标与前面的核心算子回答。只有分析依赖业务事件顺序、重复尝试匹配、 漏斗推进、耗时或状态回放时,才使用事件与生命周期路径。

事件分析从类型化参与者角色和有序 EventPattern 开始。每个步骤 使用稳定的 snake-case 键;角色已携带事件和主体实体,而主体实体 的主键提供身份。

cart_created = catalog.events.get("commerce.cart_created")
checkout_started = catalog.events.get("commerce.checkout_started")
payment_succeeded = catalog.events.get("commerce.payment_succeeded")
cart_user = ms.participant_role(event=cart_created.ref, name="user")
checkout_user = ms.participant_role(event=checkout_started.ref, name="user")
payment_user = ms.participant_role(event=payment_succeeded.ref, name="user")
cart_step = mv.step(participant=cart_user, key="cart")
checkout_step = mv.step(participant=checkout_user, key="checkout")
payment_step = mv.step(participant=payment_user, key="payment")
pattern = mv.sequence(cart_step, checkout_step, payment_step)
journeys = session.events.match(
pattern=pattern,
cohort_window=mv.time_scope(start=start, end=end),
completion_through=followup_end,
matching=mv.first_per_subject(),
)

队列窗口是半开区间:只有 [start, end) 内的首步骤发生项能建立 路径。completion_through 是包含端点的后续项 bound,并且不得早于 end。 可选的就绪 SubjectSet 会进一步限制主体 membership;它不会替代或推导 队列窗口。

当每个首步骤发生项都应建立尝试时,必须显式选择 repeated-尝试 策略。exclusive 最多把一个 completion 分配给一个打开尝试;shared 允许一个 completion 关闭多个合格尝试:

exclusive_attempts = session.events.match(
pattern=pattern,
cohort_window=mv.time_scope(start=start, end=end),
completion_through=followup_end,
matching=mv.every_start(completion_assignment="exclusive"),
)
shared_attempts = session.events.match(
pattern=pattern,
cohort_window=mv.time_scope(start=start, end=end),
completion_through=followup_end,
matching=mv.every_start(completion_assignment="shared"),
)

EventFrame[journey] 是 dense long 表:每个 journey × pattern step 都有一行。 后续步骤缺失时,该行仍然存在,其事件、时间和 elapsed 值为空。只有全部必需 事件都已知完整覆盖到后续项 bound 时,缺失步骤才是 incomplete;否则为 coverage_censored

在确定 replay 或 matching 窗口前,可以先检查精确的事件时间范围:

lifecycle = session.catalog.state_models.get("commerce.order_lifecycle")
bounds = session.events.occurrence_bounds(lifecycle)
print(bounds.earliest_occurrence_at, bounds.latest_occurrence_at)

传入 StateModel 时,Marivo 会自动推导其 inception 和 transition 使用的去重 Event,并且只聚合符合这些 Event 谓词的 occurrence。返回的 EventOccurrenceBounds 不是 Datasource 全局最大时间,也不能证明数据完整 覆盖到最新 occurrence。它只用于选择候选窗口;完整性仍需单独核实。没有 Event trigger 的 StateModel 会返回 event_refs=(),两个时间边界均为 None

后端可以通过 marivo_event_watermark(request) 提供权威事件完整性。 Marivo 不会从最大已观测事件时间、查询时间或 SLA 推导 watermark。调用方可以 通过 session.events.watermark(event, through=...) 读取该权威 watermark,返回 provider 的 EventWatermarkReceipt——没有 provider 或该事件无权威 watermark 时 返回 None。没有权威覆盖率时,调用方可以携带精确且有界的显式假设:

coverage = mv.declared_complete_through(
inputs=(cart_created, checkout_started, payment_succeeded),
through=followup_end,
rationale="Warehouse reconciliation completed through followup_end.",
)
journeys = session.events.match(
pattern=pattern,
cohort_window=mv.time_scope(start=start, end=end),
completion_through=followup_end,
matching=mv.first_per_subject(),
completeness=(coverage,),
)

归约事件路径并组合类型化队列

Section titled “归约事件路径并组合类型化队列”

首个-per-主体路径在阶段二有三个后续路径。漏斗只归约已持久化的 发生项 assignment,不重新查询事件。指定轴时,唯一允许的数据源工作是按 首步骤发生项时刻执行受治理的主体-维度丰富化:

funnel = session.events.funnel(
journeys,
axes=[acquisition_channel],
analysis_purpose="Measure checkout conversion by entry channel.",
)
elapsed = session.events.time_to_event(
journeys,
start_step=checkout_step,
end_step=payment_step,
analysis_purpose="Measure checkout-to-payment elapsed time.",
)
dropouts = session.select_subjects(
journeys,
selection=mv.dropped_before(step=payment_step),
)

EventFrame[funnel] 保留可加 counts、censoring-感知 rates 和 grouped 对账证据。EventFrame[time_to_event] 精确保留来源的开始/end assignment,绝不重新匹配另一个 completion。SubjectSet 只暴露受治理的 subject_identity 元组;原始身份不会进入卡片、job、证据、structured 错误或 consumer 元数据。

只有就绪 SubjectSet 能限定 observe 或后续 events.match

dropout_revenue = session.observe(revenue, cohort=dropouts)
return_journeys = session.events.match(
pattern=return_pattern,
cohort=dropouts,
cohort_window=mv.time_scope(start=start, end=end),
completion_through=followup_end,
matching=mv.first_per_subject(),
)

loss 仍为 coverage_censored 的主体会被排除,并计入 SubjectSet 元数据。 该 SubjectSet 仍可读取和恢复,但用作队列时会抛出 event_coverage_unknown。漏斗与 dropped_before 要求 first_per_subject;事件耗时也接受 repeated-尝试路径,因为它为每个 已分配尝试保留一行。

已观测 watermark 优先于声明。产物及其质量/证据记录 会保留每个输入的 observed_watermark | declared_complete | unknown basis,以及 整体 observed_watermark | declared_complete | mixed | unknown basis。

两个兼容的 EventFrame[funnel] 也使用同一个 compare 入口,但不接受 alignment= 参数。Marivo 按持久化的精确 PatternStep 身份与完整轴元组 对齐;仅单侧存在的元组会把可加 counts 补零,而缺失侧的 rate 保持 null。

funnel_delta = session.compare(current_funnel, baseline_funnel)
payment_loss = mv.funnel_loss_rate(step=payment_step)
drivers = session.attribute(
funnel_delta,
axes=[acquisition_channel],
target=payment_loss,
)

归因输入必须是未分组的漏斗差值。它在持久化路径 assignment 上按受治理的 队列进入维度重新聚合,绝不会重新匹配事件。每个驱动因素输出可加的 lossdenominator_mix 组件;两者之和会在 1e-9 内精确对账到目标 loss-rate 差值。多轴时显式选择 mode="joint"mode="hierarchy"。 hierarchy 的 pool share 在每个可见层级内分别归一化;产物元数据保留用于 对账的最深 joint 分区 pool。这些贡献只描述观测变化的算术分解, 不代表因果关系。

DeltaFrame[funnel] 是封闭产物家族:只暴露自身漏斗字段,绝不投影 指标差值的 facet。指标专属后续路径(components()transform.*) 会以结构化错误失败即停止,而不是伪装出并不存在的组件图或指标 契约。

生命周期分析会把一个精确的当前 StateModel 应用于其已建模事件 stream。 模型负责状态和合法转换;分析调用负责窗口、显式 seed、可选队列和 完整性声明。首次使用前应读取 marivo.help("analysis.lifecycle.replay"),不要复制缓存中的函数签名。

order_model = catalog.state_models.get("sales.order_lifecycle")
history = session.lifecycle.replay(
order_model,
window=mv.time_scope(start=start, end=end),
seed=mv.from_inception(),
analysis_purpose="Reconstruct governed order state history.",
)

重放窗口是时区-感知半开区间。mv.from_inception() 是必需的显式选择: 每个主体从第一个符合条件的已建模起始开始重放,计算 window.end 之前的全部已建模发生项,但只输出与请求窗口重叠的区间。 没有起始的 StateModel 可以为未来的投影 seed 加载,但不能使用这条 重放路径。

每个不同的触发事件最多查询一次。事件 predicate、参与者-角色 解析、身份验证、deterministic ordering 与完整性都复用 事件路径的同一套核心。完整性声明只能覆盖所选 StateModel 实际消费的事件,且权威 provider watermark 优先。模型外事件既不会被查询, 也不会被标记为违规。

合法触发会改变状态。当前状态下不合法的已建模触发不改变状态,但会进入 重放的私有违规追踪。同一时刻的跨事件发生项只有在顺序会改变最终 状态或违规 outcome 时才会失败。

LifecycleFrame[history] 保存有序且不重叠的状态区间。区间状态为 completedright_censoredcoverage_censored。用 history.show() 读取有界当前状态,用 history.contract() 查看机械合法的后续路径。

归约生命周期 history 并选择状态队列

Section titled “归约生命周期 history 并选择状态队列”

生命周期 reducer 只消费已提交 history,不重新查询事件来源,也不重新重放 StateModel:

weekly_state = session.lifecycle.distribution(
history,
at=(week_1_end, week_2_end),
axes=[account_region],
)
transition_counts = session.lifecycle.transitions(history)
dwell = session.lifecycle.dwell(history)
violations = session.lifecycle.violations(history)

distribution 对每个请求 instant 和每个声明状态生成 dense 输出。受治理的主体 轴在各 instant 上解析,分组计数必须与未分组状态 population 对账。transitions 包含每个不同的已建模状态 pair,即使 count 为零。dwell 包含每个声明状态; duration statistic 只使用已完成且按窗口裁剪的区间,censored 区间 则保留为显式计数。violations 是 committed 追踪的类型化 copy,不会被重命名为 策略 breach、质量失败或 causal fact。

通过精确的类型化模型-状态 handle 选择主体,不能传裸状态字符串:

paid_state = ms.model_state(model=order_model.ref, name="paid")
paid_at_end = session.select_subjects(
history,
selection=mv.in_state(paid_state, as_of=end),
)

在该 instant 状态未知或覆盖率-censored 的主体会被排除,并保留在 censoring 元数据中。结果 SubjectSet 始终可读,但只有就绪 set 才能限定后续 observeevents.matchlifecycle.replay。当前精确 admission rules 以 marivo.help("analysis.in_state") 和来源产物契约为准。

Frame由谁产出
MetricFrameobserve
DeltaFramecompare
AttributionFrameattribute
AssociationResultcorrelate
ForecastFrameforecast
QualityReportassess_quality
HypothesisTestResulthypothesis_test
CandidateSetdiscover.*