Morphic用户行为跟踪:事件分析与转化漏斗设计
一、用户行为跟踪体系概述
Morphic作为AI驱动的问答引擎(Answer Engine),其核心价值在于理解用户查询意图并提供精准反馈。用户行为跟踪系统通过捕获关键交互事件,构建从"问题输入"到"满意输出"的完整转化链路,为产品迭代提供数据支撑。本方案基于Morphic现有架构,设计轻量级事件分析框架与可视化转化漏斗,无需侵入核心业务逻辑即可实现用户行为洞察。
1.1 事件跟踪范围界定
| 事件类型 | 跟踪目标 | 业务价值 | 实现难度 |
|---|---|---|---|
| 核心交互事件 | 搜索提交/问题发送/结果点击 | 评估核心功能使用率 | ★☆☆☆☆ |
| 界面操作事件 | 侧边栏切换/主题切换/模型选择 | 优化UI/UX设计 | ★★☆☆☆ |
| 用户旅程事件 | 登录状态/会话时长/历史记录访问 | 分析用户留存与活跃度 | ★★★☆☆ |
| 系统性能事件 | 响应耗时/错误率/工具调用成功率 | 保障服务稳定性 | ★★★★☆ |
1.2 技术实现约束
在Morphic现有技术栈(Next.js+TypeScript+Supabase)中实施跟踪系统需满足:
- 无侵入性:通过事件委托模式绑定跟踪逻辑,避免修改业务组件
- 数据隐私:符合GDPR规范,仅收集匿名交互数据(不含用户识别信息)
- 性能优先:客户端事件采集延迟需控制在50ms内,不阻塞主线程
二、事件分析模型设计
2.1 事件数据结构定义
基于TypeScript接口定义标准化事件格式,确保前后端数据一致性:
// lib/types/analytics.ts (建议新增文件)
export interface TrackedEvent {
eventId: string; // UUIDv4唯一标识
eventType: EventType; // 事件类型枚举
eventName: string; // 事件名称(如"search_submit")
timestamp: number; // 事件发生时间戳(ms)
metadata: {
query?: string; // 搜索查询文本
model?: string; // 使用的AI模型
duration?: number; // 操作耗时(ms)
elementId?: string; // 触发元素ID
success?: boolean; // 操作是否成功
sessionId: string; // 会话ID
};
userId?: string; // 匿名用户ID(可选)
}
// 核心事件类型枚举
export enum EventType {
SEARCH = "search", // 搜索相关事件
INTERFACE = "interface", // 界面交互事件
AUTH = "auth", // 认证相关事件
ERROR = "error" // 错误事件
}
2.2 事件触发机制
采用装饰器模式封装事件跟踪逻辑,实现业务代码与跟踪代码解耦:
// lib/utils/tracker.ts (建议新增文件)
import { v4 as uuidv4 } from 'uuid';
// 事件跟踪装饰器
export function trackEvent(eventName: string, eventType: EventType) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
// 前置处理:生成事件基本信息
const startTime = performance.now();
const eventId = uuidv4();
try {
// 执行原方法
const result = await originalMethod.apply(this, args);
// 后置处理:发送成功事件
sendEvent({
eventId,
eventType,
eventName,
timestamp: Date.now(),
metadata: {
duration: performance.now() - startTime,
success: true,
sessionId: getSessionId()
}
});
return result;
} catch (error) {
// 异常处理:发送失败事件
sendEvent({
eventId,
eventType: EventType.ERROR,
eventName: `${eventName}_failed`,
timestamp: Date.now(),
metadata: {
duration: performance.now() - startTime,
success: false,
sessionId: getSessionId()
}
});
throw error;
}
};
return descriptor;
};
}
// 会话ID管理
function getSessionId(): string {
if (!localStorage.getItem('morphic_session_id')) {
localStorage.setItem('morphic_session_id', uuidv4());
}
return localStorage.getItem('morphic_session_id')!;
}
// 事件发送实现
async function sendEvent(event: TrackedEvent) {
try {
await fetch('/api/track', { // 建议新增事件跟踪API端点
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event),
keepalive: true // 确保页面卸载时事件仍能发送
});
} catch (error) {
console.error('Event tracking failed:', error);
}
}
三、转化漏斗设计与实现
3.1 核心转化漏斗模型
基于Morphic用户旅程设计的5阶段转化漏斗:
漏斗阶段定义
| 阶段 | 关键事件指标 | 数据采集点 | 转化障碍分析 |
|---|---|---|---|
| 访问页面 | 页面加载完成事件 | app/layout.tsx中的onMount | 首屏加载速度、网络稳定性 |
| 输入查询 | 查询框聚焦事件+输入字符数>5 | components/chat-panel.tsx | 输入框UI设计、初始引导提示 |
| 查看结果 | 搜索提交事件+结果渲染完成 | lib/actions/chat.ts | AI响应速度、结果相关性 |
| 完成任务 | 结果点击/复制/展开详情 | components/message.tsx | 结果展示清晰度、交互便捷性 |
| 二次访问 | 24小时内重复会话 | lib/utils/tracker.ts中的sessionId | 结果保存功能、用户价值感知 |
3.2 漏斗分析SQL实现
在Supabase中创建转化漏斗分析视图:
-- 在Supabase SQL编辑器中执行
CREATE VIEW conversion_funnel AS
WITH session_events AS (
SELECT
session_id,
MIN(CASE WHEN event_name = 'page_view' THEN timestamp END) AS visit_time,
MIN(CASE WHEN event_name = 'query_input' THEN timestamp END) AS input_time,
MIN(CASE WHEN event_name = 'search_submit' THEN timestamp END) AS search_time,
MIN(CASE WHEN event_name = 'result_interaction' THEN timestamp END) AS interaction_time,
MIN(CASE WHEN event_name = 'result_save' THEN timestamp END) AS save_time
FROM tracked_events
WHERE timestamp > NOW() - INTERVAL '7 days'
GROUP BY session_id
)
SELECT
'访问页面' AS stage,
COUNT(session_id) AS users,
100.0 AS conversion_rate
FROM session_events
UNION ALL
SELECT
'输入查询',
COUNT(input_time),
ROUND(COUNT(input_time)*100.0/COUNT(session_id),1)
FROM session_events
UNION ALL
SELECT
'查看结果',
COUNT(search_time),
ROUND(COUNT(search_time)*100.0/COUNT(input_time),1)
FROM session_events
UNION ALL
SELECT
'完成任务',
COUNT(interaction_time),
ROUND(COUNT(interaction_time)*100.0/COUNT(search_time),1)
FROM session_events
UNION ALL
SELECT
'二次访问',
COUNT(save_time),
ROUND(COUNT(save_time)*100.0/COUNT(interaction_time),1)
FROM session_events;
3.3 漏斗优化策略矩阵
| 低转化阶段 | 数据表现特征 | 优化方案 | 实施优先级 |
|---|---|---|---|
| 输入查询 | 访问-输入转化率<60% | 1. 添加热门问题推荐 2. 优化移动端输入体验 | P0 |
| 查看结果 | 输入-搜索转化率<70% | 1. 减少搜索按钮点击距离 2. 添加语音输入 | P1 |
| 完成任务 | 搜索-交互转化率<40% | 1. 优化结果排版 2. 添加一键复制功能 | P0 |
| 二次访问 | 交互-留存转化率<20% | 1. 实现查询历史云同步 2. 添加结果收藏夹 | P2 |
四、前端事件埋点实践
4.1 关键组件埋点示例
搜索交互埋点(聊天面板组件)
// components/chat-panel.tsx (修改)
import { useState } from 'react';
import { trackEvent } from '@/lib/utils/tracker';
import { Button } from './ui/button';
import { Input } from './ui/input';
export function ChatPanel() {
const [query, setQuery] = useState('');
// 使用装饰器跟踪搜索提交事件
const handleSubmit = trackEvent('search_submit', 'SEARCH')(
async (e: React.FormEvent) => {
e.preventDefault();
if (!query.trim()) return;
// 原有搜索逻辑...
await submitSearch(query);
}
);
// 跟踪查询输入事件
const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setQuery(value);
// 输入超过5个字符且2秒未输入时触发
if (value.length > 5) {
clearTimeout(window.inputTimeout);
window.inputTimeout = setTimeout(() => {
trackEvent('query_input', 'SEARCH')(() => {})(null);
}, 2000);
}
};
return (
<form onSubmit={handleSubmit} className="flex gap-2 p-4">
<Input
value={query}
onChange={handleInput}
onFocus={() => {
// 跟踪输入框聚焦事件
trackEvent('query_focus', 'INTERFACE')(() => {})(null);
}}
placeholder="输入您的问题..."
className="flex-1"
/>
<Button type="submit">搜索</Button>
</form>
);
}
结果交互埋点(消息组件)
// components/message.tsx (修改)
import { trackEvent } from '@/lib/utils/tracker';
import { Codeblock } from './ui/codeblock';
import { TooltipButton } from './ui/tooltip-button';
export function Message({ content, isUser }) {
// 跟踪结果复制事件
const handleCopy = trackEvent('result_copy', 'SEARCH')(
async (text: string) => {
await navigator.clipboard.writeText(text);
}
);
// 跟踪结果展开事件
const handleExpand = trackEvent('result_expand', 'SEARCH')(
() => {
setExpanded(!expanded);
}
);
return (
<div className="message">
{/* 原有消息内容渲染 */}
<div className="content">{content}</div>
{/* 交互按钮 */}
<div className="actions">
<TooltipButton
onClick={() => handleCopy(content)}
icon="copy"
/>
{content.length > 200 && (
<TooltipButton
onClick={handleExpand}
icon={expanded ? "collapse" : "expand"}
/>
)}
</div>
</div>
);
}
4.2 页面级事件跟踪
// app/layout.tsx (修改)
import { useEffect } from 'react';
import { trackEvent } from '@/lib/utils/tracker';
export default function RootLayout({ children }) {
useEffect(() => {
// 页面加载完成事件
trackEvent('page_view', 'INTERFACE')(() => {})(null);
// 页面离开事件
const handleBeforeUnload = () => {
trackEvent('page_unload', 'INTERFACE')(() => {})(null);
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, []);
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
五、数据分析与可视化实现
5.1 数据查询API设计
// app/api/analytics/route.ts (新增)
import { NextResponse } from 'next/server';
import { createServerClient } from '@/lib/supabase/server';
import { revalidatePath } from 'next/cache';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const period = searchParams.get('period') || '7d';
const stage = searchParams.get('stage');
const supabase = createServerClient();
const { data, error } = await supabase
.from('conversion_funnel')
.select('stage, users, conversion_rate')
.eq('stage', stage)
.single();
if (error) return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json(data);
}
5.2 管理后台仪表盘组件
// components/analytics/dashboard.tsx (新增)
'use client';
import { useEffect, useState } from 'react';
import { Card } from '@/components/ui/card';
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
export function AnalyticsDashboard() {
const [funnelData, setFunnelData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchFunnelData() {
setLoading(true);
const res = await fetch('/api/analytics?period=7d');
const data = await res.json();
setFunnelData(data);
setLoading(false);
}
fetchFunnelData();
// 每5分钟刷新一次数据
const interval = setInterval(fetchFunnelData, 5 * 60 * 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 p-6">
<Card className="col-span-1">
<h3 className="text-lg font-semibold p-4">转化漏斗趋势</h3>
{loading ? (
<div className="h-80 flex items-center justify-center">加载中...</div>
) : (
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={funnelData}>
<defs>
<linearGradient id="colorUsers" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.8}/>
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0}/>
</linearGradient>
</defs>
<XAxis dataKey="date" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Area
type="monotone"
dataKey="users"
stroke="#3b82f6"
fillOpacity={1}
fill="url(#colorUsers)"
/>
</AreaChart>
</ResponsiveContainer>
)}
</Card>
<Card className="col-span-1">
<h3 className="text-lg font-semibold p-4">漏斗转化率</h3>
{loading ? (
<div className="h-80 flex items-center justify-center">加载中...</div>
) : (
<div className="p-4">
{funnelData.map((stage, index) => (
<div key={index} className="mb-4">
<div className="flex justify-between mb-1">
<span>{stage.stage}</span>
<span>{stage.conversion_rate}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-gradient-to-r from-orange-500 to-red-500 h-2 rounded-full"
style={{ width: `${stage.conversion_rate}%` }}
></div>
</div>
</div>
))}
</div>
)}
</Card>
</div>
);
}
六、实施路线图与最佳实践
6.1 分阶段实施计划
timeline
title Morphic用户行为跟踪系统实施路线图
section 第一阶段<br/><small>(1-2周)</small>
事件数据结构设计 :done, des1, 2025-09-01, 3d
核心事件跟踪实现 :done,
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



