首页 / 文章

使用 PostHog 自动捕获与自定义事件实现乐器教师的工作流程管理

叠加 PostHog 的自动抓取功能、结果事件、会话回放以及个人时间线,以便人工智能辅助教学产品能够衡量实际完成的任务,而不仅仅是点击次数。

发布者:REACTAPP.TOP阅读约 4 分钟1031 词

来源: **Instrumenting User Behaviour on KaushalSaathi with PostHog**

KaushalSaathi是一个面向教师的、以人工智能为核心的教育平台。它能够协助编写教案、引导多步骤的教学模式、安排课程表以及提供辅导聊天功能。由于该产品的诸多价值来自于异步生成内容而非简单的页面跳转,因此其指标策略必须将已完成的任务视为重要信号——而不仅仅是点击次数和路径变更。

在未观察实际使用情况的情况下就推出新功能是一个常见的陷阱。额外的界面并不必然带来更好的效果。在添加新功能之前,团队需要了解教育工作者实际使用哪些功能、在哪一步会遇到困难,以及有哪些界面从未被打开。行为可观测性能让产品决策从凭猜测变为基于实际使用数据的判断。这正是PostHog被引入所要解决的问题:教师们究竟是如何使用现有产品的?

自动捕获作为免费基准

自动捕获无需额外配置即可覆盖DOM层面的操作——点击事件、$pageview、$pageleave以及会话边界。无条件启用该功能,并将缺失的API密钥视为关闭开关,这样本地开发和贡献者分支就无需进行账户设置,也不用在代码中到处添加if (enabled)判断:

const POSTHOG_KEY = import.meta.env.VITE_POSTHOG_KEY ?? "phc_disabled_no_key_set";
const ENABLED = POSTHOG_KEY !== "phc_disabled_no_key_set";
<PostHogProvider
apiKey={POSTHOG_KEY}
options={{
api_host: POSTHOG_HOST,
autocapture: ENABLED,
capture_pageview: ENABLED,
session_recording: { maskAllInputs: true },
loaded: (ph) => { if (!ENABLED) ph.opt_out_capturing(); },
}}

自动捕获能记录用户导航的位置,但无法知晓他们完成了什么。DOM点击处理程序永远无法感知异步任务的完成情况——比如课程计划生成完毕、教学模式时间结束或PDF导出成功。这些操作完成时都不会产生可被捕获的DOM点击事件,因此按设计自动捕获也无法检测到它们。正是由于这一缺陷,才需要第二层解决方案。

转化为指标的自定义事件

有意义的成果会通过一个简单的三功能封装层以自定义事件的形式输出。每次调用都会检查 posthog.__loaded,因此应用程序的其他部分无需进行空值检测,也不必关心数据采集是否处于激活状态:

export function captureEvent(name: string, props?: Record<string, unknown>) {
if (!posthog.__loaded) return;
posthog.capture(name, props);
}

两种约定确保了这些事件日后可用于查询。

在成功时发送事件,而非尝试时。数据采集会在异步成功的路径上执行,而非在按钮处理函数内部,这样图表就能反映已完成的工作:

const plan = await generateLessonPlan(payload); // resolves only on success
captureEvent("lesson_plan_generated", {
grade: plan.grade, sector: plan.sector, language: plan.language,
});

附加可作为分类维度的属性。诸如 grade、language、sector 和 periodNumber 等字段会在事件发送时一同传递。在 PostHog 中,它们无需再次处理即可直接用作筛选和分类条件——一个事件就能回答“按年级”“按语言”或“按行业”的统计问题。

核心辅助函数是identifyUser,在登录成功后通过认证钩子调用一次。该函数将临时的匿名标识符与已验证的账户关联起来,并附加类似角色的用户字段,这样后续的捕获和记录就会继承相同的属性:

posthog.identify(user.id,
{ role: user.role, grade: user.grade, school: user.school });
// on logout: posthog.reset() ← must run before the redirect, or events leak to the next id

登出时的调用顺序很重要。在共享设备上于reset()之前进行重定向,可能会导致下一个会话仍带有之前的身份标识。应先执行重置操作,然后再进行跳转。

每个事件都会生成一条保存的洞察数据。常见的处理方式是按用户名统计事件的次数:

  • lesson_plan_generated 数据按小时分组显示在整个窗口中,可生成反映时间的时间序列,以及按排名排列的显示使用者详细结果的表格。通过这一视图即可识别出高频用户、闲置账号以及使用集中的时段。默认的细分行数为25条;当需要分析长尾数据时可将该数值提升至50条。
  • chat_message_sent 数据限定在昨日范围内并按小时分组,实际上只是使用了相同事件但时间范围更短——例如会在15:00左右出现与某位教师相关的使用高峰。

事件定义相同,只是日期范围和细分方式不同。新的需求只需创建新的保存视图,无需编写新的应用代码。

用于分析“原因”的会话回放功能

统计数据说明的是“什么”以及“出现的频率”,而非“原因”。由于教师会输入学生的个人身份信息及学校相关数据,因此在默认设置下记录功能处于开启状态,且设置了maskAllInputs: true。点击操作、导航行为、滚动深度以及犹豫时间均会显示,而输入内容在采集时会被遮蔽。

以下情况重放功能能带来实际价值:

  • 定位流程漏洞。当某个班级满足lesson_plan_generated的条件但未触发pdf_exported时,统计数据会标出漏洞所在,重放功能则能显示用户具体在哪个导出步骤离开。
  • 排查隐性多步故障。教学模式可能会在无声无息中出错,用户仅会停止操作。重放功能能够区分是界面状态异常、标签表述不清还是生成速度过慢导致超时等问题。

个人操作时间线

一旦身份识别将捕获数据与回放内容绑定到某个账户,PostHog的用户界面就会为该教育工作者呈现一条时间线:显示了哪些事件被触发、他们打开了哪些功能、以及存储了哪些会话。实际应用包括确认是否有新的registration_completed事件之后紧接着出现第一个lesson_plan_generated事件(用于评估用户适应情况),在为特定用户设计功能前先查看其时间线,或者通过具体的会话数据而非仅凭记忆来重现已报告的故障。

整合各层数据

Autocapture可免费提供基准信号。自定义事件和指标能将功能运行结果转化为按属性分类的排序时间序列。会话回放则可展示图表中各种形态背后的因果关系细节。个人时间线功能可从整体数据逐步聚焦到某位具体教育工作者。这些功能共同构成了足够的依据,让我们能够根据实际观察到的行为来决定开发方向,而非依赖关于产品“应如何使用”的假设。