PostHog Signals 分组管线离线评估解析:从 2026-03-16 报告读懂 ARI、匹配失败模式与裁判指标
【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
导读
本文以 products/signals/eval/reports/2026-03-16.md 这份评估报告为骨架,深入解读 PostHog Signals 产品中**信号分组管线(signal grouping pipeline)**的一次离线(offline)端到端评估结果。报告中记录了 91 个合成信号、41 个 ground-truth 分组、54 份产出报告的聚类质量(ARI、同质性、完整性、纯度、组召回)、匹配失败模式分布、按来源划分的 pre-emit 可操作性检查,以及 Safety/Actionability 两个报告级裁判的准确率。读完本文,你将理解报告中每个指标的业务含义与计算口径、失败模式(尤其是 SPECIFICITY_SPLIT)背后对应的管线阶段,以及如何在本地复现这次评估并用 HogQL 查询同样的指标。
一、这份报告是什么:Signals 分组管线的离线评估
PostHog Signals 是一个将 Zendesk、GitHub、Linear 等外部工单/Issue 信号聚合为"报告(report)"的功能:多个信号如果描述同一个问题,应被聚类到同一份报告中,从而让用户一次看到问题的全貌。为了保证聚类质量,仓库在 products/signals/eval/ 下维护了一套端到端评估(end-to-end eval) ,其目标定义在 AGENTS.md 中: 将合成信号送入真实管线 (LLM 查询生成 → 嵌入检索 → LLM 匹配 → 特异性校验 → 摘要 → 安全裁判 → 可操作性裁判),与人工标注的 ground-truth 分组对比,衡量管线把信号聚成报告的效果。 关键设计是"管线真实、基础设施打桩":用内存版 ,包含聚合指标、匹配质量、裁判准确率等若干层面,供开发者在迭代分组逻辑时对比基线。 从文件命名看, 的机制,本文的 03-16 报告是其中的基线记录之一。 报告开头给出了本次评估的规模信息: 信号到达顺序并非按组排列,而是由 common.py 中的 ,模拟真实世界的乱序到达,同时保持组内顺序。这意味着管线必须在信号"不按主题排队"的情况下完成在线聚类,难度更接近生产环境。 从 AGENTS.md 与 eval_grouping_e2e.py 的编排逻辑看,本次评估中每个信号依次经过以下阶段: 所有 LLM 调用统一走内部 LLM 网关( ,定义在 common.py)。 报告的第一张核心表是聚合指标: 这些指标由 sklearn 的 等函数计算(见 eval_grouping_e2e.py 的导入),并结合报告里的定义逐项解读: 从源码角度,这些指标在 AGENTS.md 中有同样的定义清单,并被作为 实验捕获,用于在 PostHog 内做长期趋势监控。 报告的第二张核心表关注每个信号的匹配决策质量 ,本次共覆盖 104 个匹配决策: 在 common.py 中定义了三个基础匹配结果枚举: 而报告中的SPECIFICITY_SPLIT 是特异性校验阶段(specificity judge)引入的第四种状态:matcher 原本给出的匹配被特异性判断器判定为"过于宽泛"而否决/拆分,导致本应属于同一报告的信号被拆开。报告用一行专门给出了它的体量: Total undergrouping (UNDERGROUP + SPECIFICITY_SPLIT): 19/104 (18.3%). Specificity split share of undergrouping: 13/19 (68.4%). 也就是说:本次 18.3% 的欠聚合错误中,68.4%(13/19)是由特异性判断器"过度拆分"造成的 ,而不是 matcher 本身没有识别出关联。这直接点出了本次运行的一个优化方向——特异性判断器的阈值/行为需要校准。 这种"特异性判断器引入拆分"的现象并非孤例,2026-03-17.md 明确记录:特异性判断器把 14 个 overgroup 转为正确匹配,但同时引入了 5 个新的 undergroup(净 +9);2026-08-18.md 则在模型对比中发现"在 sonnet-5 上特异性判断器是净损失(84.9% → 83.7%)",因为该模型本身已很少过度聚合,判断器剩余的作用只是拆分本应合并的组。这解释了 03-16 报告中 SPECIFICITY_SPLIT 占比高、而后续迭代不断调整该阶段的原因。 虽然 03-16 报告未列出检索多样性表,但 (1 − Jaccard)两个数值指标,用于诊断"检索召回是否足够多样化"。后续报告(如 03-17 的 query_diversity 均值 0.465、candidate_diversity 均值 0.445)展示了它们的典型量级:查询中等程度多样化,不同查询召回的候选集部分重叠。 报告第三张表评估信号进入分组之前的可操作性过滤器( 实验): 解读要点: 该实验按来源拆分命名( ,与 ground-truth 的可操作性标签对比。 所有信号分组完成后,管线对每份报告运行两个裁判(对应 实验): 从实现看,报告级裁判分别对应 products/signals/backend/temporal/report_safety_judge.py( ,提示注入检测)与 actionability 判断,二者在 eval_grouping_e2e.py 中被逐一调用。 最后一张表从"单份报告"粒度给出分组质量的分布: 综合看,本次评估的结论画像可以概括为:报告内容足够"纯"(同质性/纯度都很高),但"全"不够(完整性/组召回偏低),根因主要是特异性判断器过度拆分,其次是 matcher 自身的欠聚合 。 评估入口是 eval_grouping_e2e.py,通过 pytest 运行: 需要的环境变量(配置在仓库根目录 ,自动加载)见 AGENTS.md: 支持的命令行选项: 运行过程中 stderr 会输出两条 tqdm 进度条(Matching 与 Judging),随后是聚合结果汇总表,与 03-16 报告的数据结构一一对应。 评估结果以 )落库,AGENTS.md 提供了多段可直接在 PostHog SQL 编辑器中执行的查询。例如聚合指标: 匹配失败模式分布: 按来源统计 pre-emit 可操作性(对应报告第五张表): 此外还有报告级裁判、逐报告分组质量、特异性判断器影响(对比 )以及详细失败样本等查询,完整清单见 AGENTS.md 的 "HogQL queries" 一节——这正是把 03-16 这种快照报告转化为可持续监控手段的桥梁。 将 03-16 报告与仓库中后续报告对比,可以更准确地定位这次运行的水平(以下数字均来自对应报告文件本身): 其中 03-17 报告特别注明"Safety judge 从 90.7% 提升到 100.0%(对照 03-16)",且其 。 2026-03-16 报告是 PostHog Signals 分组管线一次信息密度很高的离线评估快照,其价值体现在三个层面: 对于任何从事"LLM 驱动的在线聚类/工单归并"类系统的工程师,这份报告连同其评估框架(真实管线 + mock 基础设施 + 固定种子信号流 + 分级指标体系)都是一个值得参考的离线评估范式。 【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthogEmbeddingStore替代 ClickHouse + Kafka、用ReportStore替代 Postgres(实现见 mock.py),从而在纯 Python 测试环境中跑完整链路。2026-03-16.md正是某一次离线评估产出的结果快照reports/目录下还保存着 2026-03-17.md、2026-08-18.md、2026-09-03.md 等后续报告,说明这套评估是持续迭代、逐次对比二、本次运行的测试集与执行规模
subject/description字段、GitHub 使用title/body、error tracking 按 cymbal 格式渲染描述)。get_signals_stream()用固定随机种子(RNG_SEED = 1337)将各组信号交错打乱每个信号经历的管线阶段
text-embedding-3-small)→ 对已存信号做余弦相似度检索 → LLM 判断"并入已有报告"还是"新建报告" → 特异性校验(specificity judge)确认匹配是否过于宽泛。LLM_GATEWAY_URL),信号并发执行,但 match + persist 阶段由asyncio.Lock串行化,以保证嵌入库与报告库在做"并入/新建"决策时看到一致视图(并发上限MAX_CONCURRENT_RUNS = 70三、聚合指标(Aggregate metrics):聚类的四个"体检项"
Metric Score ARI 0.7059 Homogeneity 0.9923 Completeness 0.9065 Mean purity 0.9954 Mean group recall 0.6790 Malicious leaked rate 0.2083 (5/24) adjusted_rand_score、homogeneity_completeness_v_measure$ai_evaluation事件中的grouping-aggregate四、匹配质量:失败模式的分布与 SPECIFICITY_SPLIT 的来源
Failure mode Count % of total CORRECT 79 76.0% SPECIFICITY_SPLIT 13 12.5% UNDERGROUP 6 5.8% OVERGROUP 6 5.8% 失败模式的定义
NONE:匹配正确(对应报告中的 CORRECT);UNDERGROUP:应该并入已有报告却新建了一份(欠聚合);OVERGROUP:加入了属于其他 ground-truth 组的报告(过度聚合)。与后续报告的印证
检索多样性(供理解匹配质量辅助信息)
match-quality实验本身就包含query_diversity(查询间平均余弦距离)与candidate_diversity五、Pre-emit 可操作性检查:按来源的过滤质量
{source}-actionability-checkSource Total Correct Failure rate FP FN Zendesk 88 64 27.3% 12 0 GitHub 58 38 34.5% 10 0 Linear 36 24 33.3% 6 0 check_actionability(来自 products/signals/backend/emission/pipeline.py),属于 emission 阶段的预处理,未通过即被丢弃,不会进入后续匹配。zendesk-actionability-check、github-actionability-check、linear-actionability-check),指标为二值correct_classification六、报告级裁判:Safety 与 Actionability
report-safety-check与report-actionability-checkJudge Total Correct Accuracy FP FN Safety 54 49 90.7% 5 0 Actionability 54 35 64.8% 19 0 judge_report_safety七、逐报告分组质量(Per-report grouping quality)
Metric n Mean Min Max Purity 54 0.995 0.750 1.000 Is pure 54 53/54 (98.1%) - - Group recall 54 0.679 0.333 1.000 八、如何复现与继续观测:运行评估与查询指标
复现本次评估
# 完整运行——评估结果上报到 PostHog pytest products/signals/eval/eval_grouping_e2e.py -xvs # 快速试跑——只处理前 10 个信号,不上报 pytest products/signals/eval/eval_grouping_e2e.py -xvs --limit 10 --no-capture # 在线评估模式(结果标记为 online 而非 offline) pytest products/signals/eval/eval_grouping_e2e.py -xvs --online.envVariable Purpose OPENAI_API_KEY嵌入(text-embedding-3-small) LLM_GATEWAY_URL内部 LLM 网关地址(匹配/特异性/摘要/可操作性/安全等全部 LLM 调用) LLM_GATEWAY_PERSONAL_API_KEYLLM 网关的 Bearer token(PostHog 个人 API key) SIGNALS_EVAL_TEAM_IDLLM 成本归属头使用的 team id(默认 1) POSTHOG_PROJECT_API_KEY上报评估结果用(可用 --no-capture跳过)POSTHOG_HOSTPostHog 实例地址(默认 http://localhost:8010)Flag Effect --limit N只处理信号流中的前 N 个信号 --no-capture不向 PostHog 发送 $ai_evaluation事件--online将上报结果标记为 online 评估(默认 offline) 用 HogQL 复现报告中的指标
$ai_evaluation事件($ai_eval_source = 'signals-grouping'、$ai_evaluation_type = 'offline'SELECT properties.$ai_metric_name AS metric, properties.$ai_score AS score, properties.$ai_metric_description AS description, properties.$ai_reasoning AS reasoning, properties.$ai_input AS input, properties.$ai_output AS output, properties.$ai_expected AS expected FROM events WHERE event = '$ai_evaluation' AND properties.$ai_eval_source = 'signals-grouping' AND properties.$ai_evaluation_type = 'offline' AND properties.$ai_experiment_name = 'signals-grouping/grouping-aggregate' ORDER BY metricSELECT multiIf( properties.$ai_score = 1.0, 'CORRECT', properties.$ai_reasoning LIKE '%UNDERGROUP%', 'UNDERGROUP', properties.$ai_reasoning LIKE '%OVERGROUP%', 'OVERGROUP', 'UNKNOWN' ) AS failure_mode, count() AS cnt, round(count() * 100.0 / (SELECT count() FROM events WHERE event = '$ai_evaluation' AND properties.$ai_eval_source = 'signals-grouping' AND properties.$ai_evaluation_type = 'offline' AND properties.$ai_experiment_name = 'signals-grouping/match-quality'), 1) AS pct FROM events WHERE event = '$ai_evaluation' AND properties.$ai_eval_source = 'signals-grouping' AND properties.$ai_evaluation_type = 'offline' AND properties.$ai_experiment_name = 'signals-grouping/match-quality' AND properties.$ai_metric_name = 'correct_match' GROUP BY failure_mode ORDER BY cnt DESCSELECT replaceOne(properties.$ai_experiment_name, 'signals-grouping/', '') AS check_name, count() AS total, countIf(properties.$ai_score = 1.0) AS correct, countIf(properties.$ai_score != 1.0) AS failures, round(countIf(properties.$ai_score != 1.0) * 100.0 / count(), 1) AS failure_pct, countIf(properties.$ai_score != 1.0 AND properties.$ai_output = 'ACTIONABLE') AS false_positives, countIf(properties.$ai_score != 1.0 AND properties.$ai_output = 'NOT_ACTIONABLE') AS false_negatives FROM events WHERE event = '$ai_evaluation' AND properties.$ai_eval_source = 'signals-grouping' AND properties.$ai_evaluation_type = 'offline' AND properties.$ai_experiment_name IN ( 'signals-grouping/zendesk-actionability-check', 'signals-grouping/github-actionability-check', 'signals-grouping/linear-actionability-check' ) AND properties.$ai_metric_name = 'correct_classification' GROUP BY check_name ORDER BY check_namecorrect_match_pre_specificity与correct_match九、基线定位:本次运行在迭代脉络中的位置
维度 2026-03-16 2026-03-17 说明 ARI 0.706 0.754 +0.048 Completeness 0.907 0.932 +0.025 Mean group recall 0.679 0.775 +0.096 Malicious leaked 5/24 0/24 泄漏清零 Match accuracy 76.0% 82.1% +6.1pp Safety accuracy 90.7% 100% +9.3pp Actionability acc 64.8% 81.1% +16.3pp match-quality表中不再出现 SPECIFICITY_SPLIT 单独列(合并进了 UNDERGROUP),说明特异性判断器行为在两次运行之间发生了调整。这正好印证了 03-16 报告最大的诊断价值:它通过 SPECIFICITY_SPLIT 的 13/19 占比,把"特异性校验过度拆分"这一根因显式暴露出来,为后续迭代提供了明确靶点十、总结
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考