跳到主内容
精选80PostHog 博客(RSS)产品与增长

PostHog 语义层构建实录:让 AI 与人类对指标说同一种话

Building a semantic layer: What it is and how we did it at PostHog

原文
推荐理由

给做数据产品、AI 功能或内部工具的人:一套可落地的语义层设计——如何用 SQL 表承载定义、如何用审批与漂移标记保证 AI 输出可信,以及为什么放弃纯 SQL 和纯自然语言两种简化方案。

Ask Claude, Cursor, and PostHog AI the same question: "what was our MRR last month?", and you'll get three different queries and three different numbers.

向Claude、Cursor和PostHog AI提出同一个问题:“我们上个月的MRR是多少?”,你会得到三个不同的查询和三个不同的数字。

I did exactly this, half-expecting the tools to agree. They didn't; one summed a Stripe table, one found a slightly different Stripe table, and one tried to reconstruct recurring revenue from raw events and got the proration wrong. Every method and number was plausible, but there was no way to tell which was right.

我确实这么做了,半期待这些工具能给出一致答案。但它们没有;一个汇总了Stripe表,一个找到了略有不同的Stripe表,还有一个试图从原始事件中重建经常性收入,但按比例计算错了。每种方法和数字都看似合理,但无法判断哪个是正确的。

The problem is what "MRR" means at PostHog, which table holds it, and how it's calculated all lives in people's heads. So every agent session reinvents the definition from scratch, slightly differently.

问题在于“MRR”在PostHog的含义、哪个表包含它以及如何计算,这些都存在于人们的脑海中。因此,每次代理会话都会从零开始重新定义,且略有不同。

Humans have this problem too. Every new analyst needs to learns which revenue table is the real one and how Stripe is connected. Agents just amplify the issue by answering confidently and hallucinating to fill the gaps in their knowledge, and nobody thinks to double check it.

人类也有这个问题。每个新分析师都需要了解哪个收入表是真实的,以及Stripe是如何连接的。代理只是通过自信地回答和编造来填补知识空白,从而放大了问题,而且没有人会想到去复核。

You can't fix it with a smarter model. You need to give every agent a single place to read the definition from so "MRR", and every other metric you want to define, mean the same thing on every call. That place is the semantic layer, and this is the story of building it into PostHog's context warehouse.

你不能用更智能的模型来解决这个问题。你需要给每个代理一个单一的地方来读取定义,这样“MRR”以及你想定义的每个其他指标,在每次调用时都意味着相同的东西。那个地方就是语义层,这就是将其构建到PostHog上下文仓库中的故事。

What is a semantic layer?

什么是语义层?

A semantic layer is a dictionary of definitions that everyone (agent and human) reads from. You define MRR once, approve it once, and every query you run from then on returns the same number.

语义层是一个定义字典,所有代理和人类都从中读取。你定义一次MRR,批准一次,之后每次运行的查询都会返回相同的数字。

The most important thing to understand about it is what it doesn't do. It doesn't copy your data, replace your warehouse, or move a single row of data. It sits on top of the data you already have and describes it, "this is what MRR means, this is the table to trust, this is how these two sources connect." It's a map, not a second copy of the territory. That's why it's called a layer.

关于它最重要的一点是它不做什么。它不复制你的数据,不替换你的仓库,也不移动一行数据。它位于你已有数据之上并描述它,“这就是MRR的含义,这是要信任的表,这是这两个来源的连接方式。”它是一张地图,而不是领土的第二个副本。这就是为什么它被称为层。

It fixes is three kinds of knowledge that only exists as tribal memory:

它修复了三种只存在于部落记忆中的知识:

  • What our metrics are: MRR isn't just "revenue", it's a specific calculation over a specific source.
  • Which tables to trust (and which tables to avoid): A mature project imports dozens of sources and builds hundreds of data models. Plenty of them could answer "revenue." Only one is current and blessed as accurate by the finance team. Others can be useful too, but there might be tables that we should avoid, e.g. they have been deprecated.
  • How the data joins together: The Stripe customer ID maps to an organization property, but only after it's reformatted, and nothing tells you that except the analyst who figured it out last time.
  • 我们的指标是什么:MRR不仅仅是“收入”,它是基于特定来源的特定计算。
  • 哪些表值得信任(以及哪些表应避免):一个成熟的项目会导入数十个来源并构建数百个数据模型。其中很多都能回答“收入”问题。但只有一个是最新的,并被财务团队认可为准确的。其他表也可能有用,但有些表我们应该避免,例如它们已被弃用。
  • 数据如何连接在一起:Stripe 客户ID映射到一个组织属性,但只有在重新格式化之后,而且除了上次弄明白的分析师之外,没人知道这一点。

If you're a small company with a handful of tables you interact with regularly, you probably know where everything is. But, as you grow, you add more data sources and models, the meaning of those tables stop being obvious, least of all to an AI agent seeing your schema for the first time.

如果你是一家小公司,只有几张经常交互的表,你可能知道所有东西在哪里。但随着成长,你添加更多数据源和模型,这些表的含义不再显而易见,尤其是对于第一次看到你的模式的AI代理来说。

Semantic layer /sɪˈmæntɪk ˈleɪə/ – noun. A governed catalog of definitions that sits on top of your existing data and tells people and machines what it means: what each metric is, which tables to trust, and how sources connect. It describes the data; it doesn't copy or move it.

语义层 /sɪˈmæntɪk ˈleɪə/ – 名词。一个受管理的定义目录,位于现有数据之上,告诉人和机器数据的含义:每个指标是什么,哪些表可信,以及数据源如何连接。它描述数据;不复制或移动数据。

Glossary

术语表

  • Catalog: the structured, queryable inventory the semantic layer maintains.
  • Metric: a named, governed definition of a business measure, like MRR.
  • Governed: nothing becomes official until a human approves it.
  • 目录:语义层维护的结构化、可查询的清单。
  • 指标:业务度量的命名、受管理的定义,如MRR。
  • 受管理:除非有人批准,否则没有东西成为官方定义。

Where this lives: The context warehouse

位置:上下文仓库

The context warehouse is where PostHog pulls together everything an agent needs to answer questions. That includes product events, imported sources like your Stripe data, and data models. The semantic layer makes sure agents know what your data means and can interpret it correctly.

上下文仓库是PostHog汇集代理回答问题所需一切的地方。这包括产品事件、导入的数据源(如你的Stripe数据)和数据模型。语义层确保代理知道你的数据含义并能正确解释。

A catalog actually stores the semantic layer. What integrates it so fully is that it is just SQL. Every definition shows up as ordinary tables. Metrics are a table. There's no bespoke "catalog API" for an agent to learn; if it can run execute-sql, it already knows how to read the entire semantic layer. Discovery is a query, not an integration.

目录实际上存储了语义层。它之所以如此集成,是因为它只是SQL。每个定义都显示为普通表。指标是一个表。没有专门的“目录API”供代理学习;如果它能运行execute-sql,它就已经知道如何读取整个语义层。发现是一个查询,而不是集成。

The reason three tools gave three MRR numbers is that each had to invent an answer. When you have a catalog, it becomes the first thing any agent checks: is there an approved metric? If so, it runs the governed definition instead of writing its own SQL. Same question, same number, every time.

三个工具给出三个MRR数字的原因是每个都必须发明一个答案。当你有一个目录时,它成为任何代理首先检查的东西:是否有批准的指标?如果有,它运行受管理的定义,而不是编写自己的SQL。相同的问题,相同的数字,每次都一样。

AI generated, human owned

AI生成,人类拥有

Agents are genuinely good at proposing improvements to your data governance. Point one at your schema and it'll happily draft metric definitions, suggest canonical tables, and spot likely joins from column names and sample data. Letting agents do the first pass is useful, but letting it edit doesn't improve trust, it muddles it further.

代理确实擅长提出改进数据治理的建议。指向你的模式,它会乐意起草指标定义,建议规范表,并从列名和样本数据中找出可能的连接。让代理做第一遍是有用的,但让它编辑不会提高信任,反而会进一步混淆。

So everything an agent creates lands as proposed. Nothing an agent touches is ever canonical on its own. A human promotes it, approving a metric, certifying a table, accepting a join. We added two guardrails to verify any definition changes even after approval:

因此,代理创建的所有内容都作为“提议”状态落地。代理接触的任何内容本身都不是权威的。人类会进行提升,批准指标、认证表、接受连接。我们添加了两条护栏,即使在批准后也能验证任何定义变更:

  • Editing an approved metric's definition drops it straight back to proposed. Changing what a number means re-opens the question of whether it's right.
  • If a metric was built from an existing insight and someone edits that insight, the metric gets flagged as drifted. It's a signal that the definition it was born from has moved and a human should re-review.
  • 编辑已批准指标的定义会将其直接降回“提议”状态。改变数字的含义会重新开启其正确性的问题。
  • 如果指标是基于现有洞察构建的,而有人编辑了该洞察,该指标会被标记为“已漂移”。这是一个信号,表明其来源的定义已发生变化,需要人类重新审查。

These give agents one simple rule to live by: a result is canonical only when status = 'approved' and is_drifted = false. Everything else including proposed, drifted, and archived, gets labeled non-canonical, and a well-trained agent will tell you so rather than passing it off as gospel.

这些为代理提供了一条简单的生存规则:只有当 status = 'approved' 且 is_drifted = false 时,结果才是权威的。其他所有内容,包括“提议”、“已漂移”和“已归档”,都被标记为非权威,训练有素的代理会告诉你这一点,而不是将其当作金科玉律。

The design decisions we made (and the ones we didn't)

我们做出的设计决策(以及我们未做出的决策)

The interesting part of building this was the tempting-looking architecture choices we walked away from. Almost every simplification you reach for breaks in a way that shows up later as a wrong number. Here are a few of the choices we didn't make:

构建这个系统的有趣之处在于我们放弃的那些看似诱人的架构选择。几乎你想到的每一个简化方案都会以某种方式出错,最终表现为错误的数字。以下是我们未做出的几个选择:

"Why not just make every metric a SQL query?"

“为什么不干脆把每个指标都做成 SQL 查询?”

This is the first thing everyone suggests: funnels and trends all compile down to SQL eventually, so why not require every metric to be SQL and be done with it? Because the same metric written by hand as SQL gives a different number than the insight it came from.

这是每个人首先会建议的:漏斗和趋势最终都会编译成 SQL,那么为什么不要求每个指标都是 SQL 并就此了事呢?因为手工用 SQL 编写的同一指标,其数字与它来源的洞察会不同。

Take activation, which at PostHog is a funnel. To make it SQL-only, someone must rewrite that funnel by hand. PostHog's funnel engine does a lot under the hood: steps happen in order, per person, inside a conversion window, with exclusions and deduplication applied. A hand-written SQL version gets some of that wrong.

以激活为例,在 PostHog 中它是一个漏斗。要使其仅用 SQL,必须有人手工重写该漏斗。PostHog 的漏斗引擎在幕后做了很多工作:步骤按顺序发生,按人计算,在转化窗口内,并应用排除和去重。手工编写的 SQL 版本会在某些方面出错。

Storing the metric as the same funnel definition the insight uses, and running it through the same engine, makes that mismatch impossible. The metric and the dashboard execute identical queries. That guarantee is why we support insight-shaped metrics, not just SQL.

将指标存储为与洞察相同的漏斗定义,并通过相同的引擎运行,使得这种不匹配变得不可能。指标和仪表板执行完全相同的查询。这种保证是我们支持洞察形态指标而不仅仅是 SQL 的原因。

So where does that definition live? When you create a metric from an insight, PostHog snapshots the insight's query and stores it on the metric server-side. That snapshot is what runs, which is how the metric and the insight stay in lockstep.

那么该定义存储在哪里?当你从洞察创建指标时,PostHog 会快照洞察的查询并将其存储在指标的服务端。运行的就是这个快照,这就是指标和洞察保持同步的方式。

It also means the metric can notice when it falls out of step. Because the snapshot is stored but the insight keeps evolving, PostHog compares the two every time you read the metric and flags it as drifted if someone has changed the insight underneath it. Nothing runs in the background, the check happens live.

这也意味着该指标能够察觉到其偏离同步的情况。因为快照被存储,但洞察持续演变,PostHog 在每次读取指标时都会将两者进行比较,如果有人修改了其背后的洞察,便会将其标记为已漂移。后台无需运行任何任务,检查是实时进行的。

"Why not write definitions in plain English?"

"为什么不用简单的英语来定义?"

A good part of it is. Users can define how the agent should calculate a given metric using a Markdown structure. However, plain English is limited because it is less deterministic than insight-backed and SQL-backed metrics.

其中很大一部分确实如此。用户可以使用 Markdown 结构来定义代理应如何计算特定指标。然而,简单的英语有其局限性,因为它不如基于洞察和基于 SQL 的指标那样具有确定性。

"Why not just point a metric at an existing insight?"

"为什么不直接将指标指向现有的洞察?"

更进一步:量化金融体系

看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力

进入量化体系 →

相似阅读

另一事件,读法相近