mcpField
S
signals

signals

github.com/posthog数据与数据库95259 次安装今天更新
代码仓库

关于 signals

signals 是用于通过 HogQL 查询 PostHog 原始信号数据的技能,适合需要超出整理后报告进行语义检索或临时分析的场景。它涵盖去重、相似度搜索,以及按报告、来源或关键词筛选信号。

主要功能

  1. 01按自然语言描述进行语义搜索
  2. 02提取指定报告的关联信号
  3. 03列出可用信号类型
  4. 04按产品来源查询近期信号
  5. 05通过关键词进行全文搜索

适用场景

  1. 01搜索与产品问题相关的近期观察
  2. 02查看某个信号报告包含的记录
  3. 03了解信号的来源与类型分布
  4. 04按错误文本或产品词查找信号

MCP 相关

技能通过 PostHog MCP 的 `posthog:execute-sql` 工具使用 HogQL 查询 `document_embeddings` 表中的信号数据。

安装

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

$npx skills add https://github.com/posthog/ai-plugin --skill signals

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

详细介绍

查询 Signals

什么是 Signals?

Signals 是 PostHog 通过监控客户在多个来源中的产品数据自动生成的观测结果,包括错误跟踪、Web 分析、实验、会话回放等。每条 signal 都是对值得关注事项的简短自然语言描述(例如:“/checkout 的错误率飙升了 3 倍”)。

Signals 按 Signal Reports 分组。当报告积累了足够的权重后,系统会对其进行摘要并评估其可操作性。Signal report 代表一组相关观测结果,这些结果共同描述了一个有意义的问题或趋势。

Signals 及其嵌入向量存储在 ClickHouse 的 document_embeddings 表中,可通过 posthog:execute-sql MCP 工具使用 HogQL 查询。它们可能有助于以语义方式查询用户产品中最近发生的事件。

何时使用此技能,而不是 inbox-exploration

这两项技能覆盖了同一产品的不同层面:

  • inbox-exploration — 通过专用 MCP 工具(inbox-reports-list、inbox-reports-retrieve、inbox-source-configs-list、inbox-source-configs-retrieve)访问精选报告层。适用于“我的收件箱里有什么?”“哪些事项可采取行动?”按状态 / 来源 / 建议审核人筛选报告,或按 ID 或 URL 查找特定报告。
  • 此技能(signals) — 通过 document_embeddings 上的 HogQL 访问原始 signal 层。当精选报告层无法满足需求时使用:对 signal 文本进行语义搜索,获取对特定报告有贡献的所有 signal,查看有哪些类型的 signal,或进行报告工具未提供的临时分析。

典型做法是先使用 inbox-exploration,获取 report_id 或了解用户关注的领域;然后,当用户想查看原始观测结果时,再使用此技能。

表和列参考

HogQL 表别名为 document_embeddings。HogQL 会自动将查询限制在当前团队范围内,因此无需按 team_id 筛选。Signals 的关键列如下:

列类型说明
productString产品类别 — signals 的值始终为 'signals'
document_typeString文档类型 — signals 的值始终为 'signal'
model_nameString嵌入模型 — 始终为 'text-embedding-3-small-1536'
document_idString唯一的 signal ID(UUID)
timestampDateTime64(3)signal 的创建时间
inserted_atDateTime64(3)此行版本的插入时间(用于去重和软删除)
contentStringsignal 的描述文本
metadataString包含 report_id、来源信息、权重、删除标记等内容的 JSON 字符串
embeddingArray(Float64)1536 维嵌入向量

必需筛选条件

每个 signals 查询都 MUST 包含以下四个筛选条件。缺少其中任何一个,都可能导致查询因模型无效而失败、返回错误数据,或触发不必要的高成本扫描:

WHERE model_name = 'text-embedding-3-small-1536'
  AND product = 'signals'
  AND document_type = 'signal'
  AND timestamp >= now() - INTERVAL 30 DAY

model_name 筛选条件尤其关键——HogQL 引擎会使用它路由到正确的底层 ClickHouse 表。如果缺少 WHERE model_name = ... 等值筛选条件,或使用了未知模型,查询将失败并报错“Invalid model name”(此处不能使用 IN 或其他表达式)。

product 和 document_type 筛选条件同样重要——同一模型包含来自多个产品的数据(例如错误跟踪、AI memory)。如果没有这些筛选条件,结果中会混入无关数据。

出于性能考虑,必须添加 timestamp 筛选条件——该表按周分区,TTL 为 3 个月。始终使用 now() - INTERVAL N DAY(或 WEEK、MONTH 等)设置时间范围。除非有理由查询更早的数据,否则默认查询最近 30 天。通常,较新的数据更可能相关,除非是在调查长期存在的问题。

去重模式

底层表可能包含同一信号的多个版本(例如软删除后重新发出)。读取数据时 MUST 始终使用子查询包装,并通过按 document_id 分组的 argMax(..., inserted_at) 进行去重。

注意: HogQL 支持对原始 metadata JSON 列使用 metadata.field_name 点号访问,但该列经过 argMax() 等聚合函数后会丢失类型信息。MUST 在内部去重子查询中提取各个 metadata 字段——不要将整个 metadata 数据块传入 argMax,再在外层查询中使用点号访问,否则会因类型错误而失败。

HogQL 的 JSON 点号访问始终会将值提取为 Nullable(String),无论底层 JSON 类型是什么。这意味着 metadata.deleted 是字符串 'true'、'false' 或 null,而不是 Bool。请使用 deleted != 'true'——不要使用 NOT deleted。

SELECT ... FROM (
    SELECT
        document_id,
        argMax(content, inserted_at) as content,
        argMax(metadata.report_id, inserted_at) as report_id,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_type, inserted_at) as source_type,
        argMax(metadata.deleted, inserted_at) as deleted,
        argMax(embedding, inserted_at) as embedding,
        argMax(timestamp, inserted_at) as signal_ts
    FROM document_embeddings
    WHERE model_name = 'text-embedding-3-small-1536'
      AND product = 'signals'
      AND document_type = 'signal'
      AND timestamp >= now() - INTERVAL 1 MONTH
    GROUP BY document_id
)
WHERE deleted != 'true'

只有在确实需要进行相似度搜索时,才在内部子查询中选择 embedding 列——它是一个包含 1536 个元素的浮点数组,否则实例化该列的成本很高。

embedText() 函数

embedText() 是一个 HogQL 函数,可在查询编译时将文本字符串转换为 embedding 向量。它会调用 embedding API,并在执行查询前将生成的向量内联为常量。这意味着你无需任何外部 embedding 步骤,就能通过单个查询执行语义搜索。

签名: embedText(text, model_name)

  • text — 要进行 embedding 的字符串。必须是字符串字面量,不能是列引用。
  • model_name — 要使用的 embedding 模型。对于 signals,始终使用 'text-embedding-3-small-1536'。

两个参数都必须是字面量字符串。不能传入列值或表达式——该函数在编译时解析,而不是逐行解析。

使用 cosineDistance() 进行相似度搜索

使用 cosineDistance(embedding, ...) 按语义相似度对信号排序。值越低 = 越相似。始终使用 ORDER BY distance ASC,并添加 LIMIT。

cosineDistance(embedding, embedText('your search text', 'text-embedding-3-small-1536')) as distance

embedding 模型(text-embedding-3-small-1536)采用 matryoshka 表示学习,因此 embedding 维度按重要性排序。这意味着即使在高维情况下,相似度搜索也能很好地工作——维度灾难在这里并非重大问题。

元数据 JSON 字段

metadata 列是一个 JSON 字符串。HogQL 仅支持对原始表列使用 metadata.field_name 点号访问。聚合后(例如使用 argMax),JSON 类型会丢失,点号访问将失败。请始终在去重子查询中提取所需字段。

字段内层查询访问方式描述
report_idmetadata.report_id父级 Signal Report 的 UUID(未分配时为空)
source_productmetadata.source_product来源产品(使用示例 3 查找可用值)
source_typemetadata.source_typeSignal 类型(使用示例 3 查找可用值)
source_idmetadata.source_id来源实体的 ID
weightmetadata.weightSignal 权重(影响报告提升阈值)
deletedmetadata.deleted软删除标记(提取为 String — 使用 != 'true' 进行比较)
extrametadata.extra来源产品提供的任意 JSON 数据块
match_metadatametadata.match_metadata分组期间存储的大模型匹配推理

示例 1:对 Signals 进行语义搜索

查找与自然语言查询最相似的 Signals。这是了解客户产品中正在发生什么最有用的查询:

SELECT
    document_id,
    content,
    report_id,
    source_product,
    source_type,
    cosineDistance(embedding, embedText('users seeing errors on checkout page', 'text-embedding-3-small-1536')) as distance
FROM (
    SELECT
        document_id,
        argMax(content, inserted_at) as content,
        argMax(metadata.report_id, inserted_at) as report_id,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_type, inserted_at) as source_type,
        argMax(metadata.deleted, inserted_at) as deleted,
        argMax(embedding, inserted_at) as embedding,
        argMax(timestamp, inserted_at) as signal_ts
    FROM document_embeddings
    WHERE model_name = 'text-embedding-3-small-1536'
      AND product = 'signals'
      AND document_type = 'signal'
      AND timestamp >= now() - INTERVAL 1 MONTH
    GROUP BY document_id
)
WHERE deleted != 'true'
ORDER BY distance ASC
LIMIT 10

将 embedText 的第一个参数调整为你要查找的内容。使用自然语言描述你想查找的问题或观察结果。

若要限制为已归入报告的 Signals,请在外层 WHERE 中添加 AND report_id != ''。

示例 2:获取特定报告的所有 Signals

获取 report_id 后(来自语义搜索或 Signal Reports API),即可提取该报告中的所有 Signals:

SELECT
    document_id,
    content,
    report_id,
    source_product,
    source_type,
    signal_ts
FROM (
    SELECT
        document_id,
        argMax(content, inserted_at) as content,
        argMax(metadata.report_id, inserted_at) as report_id,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_type, inserted_at) as source_type,
        argMax(metadata.deleted, inserted_at) as deleted,
        argMax(timestamp, inserted_at) as signal_ts
    FROM document_embeddings
    WHERE model_name = 'text-embedding-3-small-1536'
      AND product = 'signals'
      AND document_type = 'signal'
      AND timestamp >= now() - INTERVAL 3 MONTH
    GROUP BY document_id
)
WHERE report_id = '<report-uuid-here>'
  AND deleted != 'true'
ORDER BY signal_ts ASC
LIMIT 100

示例 3:列出 Signal 类型

查看此客户有哪些类型的 Signals — 返回上个月每个唯一 (source_product, source_type) 组合的一个示例:

SELECT
    source_product,
    source_type,
    count() as cnt,
    max(signal_ts) as latest_timestamp
FROM (
    SELECT
        document_id,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_type, inserted_at) as source_type,
        argMax(metadata.deleted, inserted_at) as deleted,
        argMax(timestamp, inserted_at) as signal_ts
    FROM document_embeddings
    WHERE model_name = 'text-embedding-3-small-1536'
      AND product = 'signals'
      AND document_type = 'signal'
      AND timestamp >= now() - INTERVAL 1 MONTH
    GROUP BY document_id
)
WHERE deleted != 'true'
GROUP BY source_product, source_type
ORDER BY latest_timestamp DESC
LIMIT 100

示例 4:获取特定来源的近期 Signals

查找特定产品来源的最新 Signals(例如所有错误跟踪 Signals):

SELECT
    document_id,
    content,
    source_type,
    report_id,
    signal_ts
FROM (
    SELECT
        document_id,
        argMax(content, inserted_at) as content,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_type, inserted_at) as source_type,
        argMax(metadata.report_id, inserted_at) as report_id,
        argMax(metadata.deleted, inserted_at) as deleted,
        argMax(timestamp, inserted_at) as signal_ts
    FROM document_embeddings
    WHERE model_name = 'text-embedding-3-small-1536'
      AND product = 'signals'
      AND document_type = 'signal'
      AND timestamp >= now() - INTERVAL 1 WEEK
    GROUP BY document_id
)
WHERE source_product = 'error_tracking'
  AND deleted != 'true'
ORDER BY signal_ts DESC
LIMIT 100

将 'error_tracking' 替换为任意来源产品,例如:'web_analytics'、'experiments'、'session_replay' 等。使用示例 3 查找现有的来源产品和类型。

示例 5:对 Signals 进行全文搜索

如果你知道要搜索的特定关键词或短语(例如产品名称、错误消息或 URL),使用 ILIKE 进行全文搜索比语义搜索更快、更准确:

SELECT
    document_id,
    content,
    source_product,
    source_type,
    signal_ts
FROM (
    SELECT
        document_id,
        argMax(content, inserted_at) as content,
        argMax(metadata.source_product, inserted_at) as source_product,
        argMax(metadata.source_type, inserted_at) as source_type,
        argMax(metadata.deleted, inserted_at) as deleted,
        argMax(timestamp, inserted_at) as signal_ts
    FROM document_embeddings
    WHERE model_name = 'text-embedding-3-small-1536'
      AND product = 'signals'
      AND document_type = 'signal'
      AND timestamp >= now() - INTERVAL 1 MONTH
    GROUP BY document_id
)
WHERE deleted != 'true'
  AND content ILIKE '%feature flag%'
ORDER BY signal_ts DESC
LIMIT 10

将 '%feature flag%' 替换为你要查找的词语。使用 ILIKE 进行不区分大小写的子字符串匹配。若要精确匹配词元,请改用 hasTokenCaseInsensitive(content, 'token')。

注意事项

  1. 始终使用 text-embedding-3-small-1536 作为模型名称。 这是唯一用于信号的模型。
  2. embedText() 的参数必须是字符串字面量。 不能传入列引用或表达式——此函数在编译时解析,而非逐行解析。
  3. 始终为查询设置时间范围。 该表的 TTL 为 3 个月,但无时间范围的扫描成本很高。请使用 timestamp >= now() - INTERVAL 1 MONTH 或更短的时间范围。为获得最佳性能,请将时间筛选条件放在内部子查询的 WHERE 子句中(针对原始 timestamp 列)。
  4. 始终去重。 如果没有 argMax(..., inserted_at) GROUP BY document_id 子查询,就会看到过时和重复的行。
  5. 仅在需要时选择 embedding。 它是一个包含 1536 个元素的浮点数数组——不进行相似度搜索时,请在内部子查询中省略该字段。
  6. 查询末尾不要加分号。 HogQL 不使用分号。
  7. 每个查询都要添加 LIMIT。 最多允许 500 行。通常只应选择约 10 个信号,并使用语义搜索或全文搜索为其排序。
  8. 在去重子查询中提取元数据字段。 HogQL 的 metadata.field 点访问仅适用于原始表列。经过 argMax() 聚合后,JSON 类型会丢失,点访问将导致类型错误。请始终在内部查询中使用 argMax(metadata.field_name, inserted_at) as field_name。
  9. 所有 JSON 点访问值都是 Nullable(String)。 HogQL 会将每个 JSON 字段都提取为字符串,包括布尔值和数字。对于 metadata.deleted,请使用 deleted != 'true'——不要使用 NOT deleted。
  10. 如果同一个内部查询也会筛选原始 timestamp 列,请勿将 argMax(timestamp, inserted_at) 的别名设为 timestamp。 HogQL 会优先解析别名,导致出现“WHERE 中存在聚合”错误。请改用 signal_ts 这样的不同别名,或将时间筛选条件移到外层查询。

评论

支持 Markdown