mcpField
C
customer-success

customer-success

github.com/travisjneuman其他106133 次安装今天更新
代码仓库

关于 customer-success

面向客户支持与成功运营的工程实践技能,帮助团队设计支持系统、知识库、聊天机器人和自动化流程。内容涵盖 SLA、客户健康评分及反馈路由。

主要功能

  1. 01支持工单集成与分流
  2. 02知识库与站内帮助设计
  3. 03SLA 管理和升级自动化
  4. 04客户健康评分模型
  5. 05反馈收集与产品趋势分析

适用场景

  1. 01构建或整合客户支持系统
  2. 02设计聊天机器人和自助门户
  3. 03实施 SLA 跟踪及升级提醒
  4. 04识别有流失风险的客户

安装

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

$npx skills add https://github.com/travisjneuman/.claude --skill customer-success

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

详细介绍

客户成功工程

概述

这项技能涵盖构建支撑客户支持与成功运营的技术系统,包括支持工单系统集成(Zendesk、Intercom、Freshdesk)、知识库架构、对话式 AI 聊天机器人设计、客户反馈收集与分流、SLA 管理与执行、升级工作流自动化、自助服务门户实施,以及客户健康评分模型。

在构建或集成支持系统、设计聊天机器人流程、创建自助服务文档门户、实施 SLA 跟踪、构建客户健康仪表板或自动化支持工作流时,请使用这项技能。


核心原则

  1. 优先自助服务——最好的支持互动,就是根本不需要发生的互动。在扩大人工支持规模之前,应投资建设可搜索的知识库、应用内帮助和上下文引导。
  2. 自动化分诊,而非解决问题——AI 可以有效地对工单进行分类、确定优先级并分流。复杂问题则交由人工处理。过度自动化问题解决流程会让客户感到沮丧。
  3. 衡量问题解决时间,而非工单数量——快速关闭工单毫无意义,如果客户的问题并未解决。应跟踪首次联系解决率、客户付出度评分和重新开启率。
  4. 工单应携带完整上下文——每次交接(机器人转人工、L1 转 L2)都必须包含完整的对话历史、用户账户数据和已尝试的解决方案。重复提供信息是客户投诉中最常见的问题。
  5. 反馈是产品信号——支持工单是非结构化的产品反馈。应添加标签、分类并向产品团队展示趋势。最常见的支持话题应成为下一项产品改进。

关键模式

模式 1:知识库架构

适用场景: 构建可搜索的文档,既服务客户(自助服务),也供支持人员参考(内部参考)。

实现:

// Knowledge base article schema
interface Article {
  id: string;
  slug: string;
  title: string;
  content: string;          // Markdown
  excerpt: string;          // For search results
  category: string;
  subcategory: string;
  tags: string[];
  audience: "customer" | "internal" | "both";
  visibility: "public" | "authenticated" | "internal";
  relatedArticles: string[];
  metadata: {
    createdAt: Date;
    updatedAt: Date;
    author: string;
    reviewedAt: Date | null;
    helpfulVotes: number;
    notHelpfulVotes: number;
    viewCount: number;
  };
}

// Search implementation with vector + full-text hybrid
async function searchKnowledgeBase(
  query: string,
  options?: { category?: string; audience?: string; limit?: number }
): Promise<SearchResult[]> {
  const limit = options?.limit ?? 10;

  // 1. Semantic search (catches paraphrased queries)
  const embedding = await getEmbedding(query);
  const semanticResults = await vectorDb.search({
    vector: embedding,
    filter: {
      audience: options?.audience ?? "customer",
      ...(options?.category && { category: options.category }),
    },
    limit,
  });

  // 2. Full-text search (catches exact terminology)
  const textResults = await db.$queryRaw`
    SELECT id, title, excerpt,
           ts_rank(search_vector, plainto_tsquery('english', ${query})) AS rank
    FROM articles
    WHERE search_vector @@ plainto_tsquery('english', ${query})
      AND audience IN ('customer', 'both')
      ${options?.category ? Prisma.sql`AND category = ${options.category}` : Prisma.empty}
    ORDER BY rank DESC
    LIMIT ${limit}
  `;

  // 3. Merge and deduplicate results
  const merged = mergeSearchResults(semanticResults, textResults);

  // 4. Track search for analytics
  await trackSearch(query, merged.length);

  return merged;
}

// Feedback loop - track article helpfulness
async function rateArticle(
  articleId: string,
  helpful: boolean,
  feedback?: string
): Promise<void> {
  await db.articleFeedback.create({
    data: {
      articleId,
      helpful,
      feedback,
      createdAt: new Date(),
    },
  });

  // Update aggregate counts
  await db.article.update({
    where: { id: articleId },
    data: helpful
      ? { helpfulVotes: { increment: 1 } }
      : { notHelpfulVotes: { increment: 1 } },
  });

  // Flag articles with low helpfulness for review
  const article = await db.article.findUnique({ where: { id: articleId } });
  if (article) {
    const total = article.helpfulVotes + article.notHelpfulVotes;
    const helpfulRate = total > 10 ? article.helpfulVotes / total : 1;

    if (helpfulRate < 0.5 && total > 10) {
      await createReviewTask(articleId, "Low helpfulness score");
    }
  }
}
// In-app contextual help widget
function HelpWidget({ context }: { context: string }) {
  const [articles, setArticles] = useState<Article[]>([]);
  const [isOpen, setIsOpen] = useState(false);

  useEffect(() => {
    // Fetch relevant articles based on current page/feature context
    if (isOpen) {
      searchKnowledgeBase("", { category: context, limit: 5 })
        .then(setArticles);
    }
  }, [isOpen, context]);

  return (
    <div className="help-widget">
      <button
        onClick={() => setIsOpen(!isOpen)}
        aria-label="Help"
        aria-expanded={isOpen}
      >
        ?
      </button>
      {isOpen && (
        <div role="dialog" aria-label="Help articles">
          <SearchInput onSearch={(q) => searchKnowledgeBase(q, { category: context }).then(setArticles)} />
          <ArticleList articles={articles} />
          <a href="/support">Contact support</a>
        </div>
      )}
    </div>
  );
}

原因: 混合搜索(语义搜索 + 全文搜索)既能处理概念性查询(“如何分享我的项目?”),也能处理精确术语查询(“SSO SAML 配置”)。反馈闭环可确保标记低质量文章以便改进。上下文帮助会自动呈现相关文章,无需用户主动搜索。


模式 2:支持工单集成

适用场景: 将应用连接到工单系统,以实现结构化支持工作流。

实现:

// Unified support ticket interface (abstracts provider)
interface SupportTicket {
  id: string;
  externalId: string;       // Provider's ticket ID
  subject: string;
  description: string;
  status: "open" | "pending" | "in_progress" | "resolved" | "closed";
  priority: "low" | "normal" | "high" | "urgent";
  category: string;
  tags: string[];
  requester: {
    id: string;
    email: string;
    name: string;
  };
  assignee?: {
    id: string;
    name: string;
    group: string;
  };
  metadata: {
    plan: string;
    accountAge: number;
    mrr: number;            // Monthly recurring revenue
    previousTickets: number;
  };
  createdAt: Date;
  updatedAt: Date;
  firstResponseAt?: Date;
  resolvedAt?: Date;
}

// Auto-triage new tickets with AI classification
async function triageTicket(ticket: SupportTicket): Promise<TriageResult> {
  const classification = await classifyTicket(ticket.subject, ticket.description);

  // Priority escalation rules
  let adjustedPriority = classification.priority;

  // Enterprise customers get elevated priority
  if (ticket.metadata.mrr >= 5000) {
    adjustedPriority = elevate(adjustedPriority);
  }

  // Revenue at risk detection
  if (containsCancellationIntent(ticket.description)) {
    adjustedPriority = "urgent";
    classification.tags.push("churn-risk");
  }

  // Route to appropriate team
  const team = determineTeam(classification.category, adjustedPriority);

  return {
    category: classification.category,
    priority: adjustedPriority,
    team,
    tags: classification.tags,
    suggestedArticles: await findRelatedArticles(ticket.subject),
    autoResponse: classification.confidence > 0.9
      ? generateAutoResponse(classification, ticket)
      : null,
  };
}

// SLA enforcement
interface SLAPolicy {
  priority: string;
  firstResponseMinutes: number;
  resolutionMinutes: number;
}

const SLA_POLICIES: SLAPolicy[] = [
  { priority: "urgent", firstResponseMinutes: 30, resolutionMinutes: 240 },
  { priority: "high", firstResponseMinutes: 120, resolutionMinutes: 480 },
  { priority: "normal", firstResponseMinutes: 480, resolutionMinutes: 1440 },
  { priority: "low", firstResponseMinutes: 1440, resolutionMinutes: 4320 },
];

async function checkSLABreaches(): Promise<SLABreach[]> {
  const openTickets = await getOpenTickets();
  const breaches: SLABreach[] = [];

  for (const ticket of openTickets) {
    const policy = SLA_POLICIES.find((p) => p.priority === ticket.priority);
    if (!policy) continue;

    const now = new Date();
    const age = (now.getTime() - ticket.createdAt.getTime()) / 60000;

    // First response SLA
    if (!ticket.firstResponseAt && age > policy.firstResponseMinutes) {
      breaches.push({
        ticketId: ticket.id,
        type: "first_response",
        minutesOverdue: Math.round(age - policy.firstResponseMinutes),
      });
    }

    // Resolution SLA
    if (!ticket.resolvedAt && age > policy.resolutionMinutes) {
      breaches.push({
        ticketId: ticket.id,
        type: "resolution",
        minutesOverdue: Math.round(age - policy.resolutionMinutes),
      });
    }
  }

  return breaches;
}

原因: AI 驱动的分诊能将工单更快地分配给合适的团队,从而缩短首次响应时间。基于收入的优先级升级机制可确保高价值客户得到适当关注。SLA 监控有助于明确责任并发现系统性问题(如果某个类别的工单经常违反 SLA,就说明产品或文档存在缺口)。


模式 3:客户健康评分

适用场景: 在客户流失之前主动识别有流失风险的客户。

实现:

// Health score calculation
interface HealthSignal {
  name: string;
  weight: number;
  score: (customer: Customer) => number; // 0-100
}

const healthSignals: HealthSignal[] = [
  {
    name: "product_usage",
    weight: 0.3,
    score: (c) => {
      const weeklyActive = c.loginDaysLast30 / 30 * 100;
      return Math.min(100, weeklyActive * 1.5);
    },
  },
  {
    name: "feature_adoption",
    weight: 0.2,
    score: (c) => {
      const total = CORE_FEATURES.length;
      const adopted = c.featuresUsed.filter((f) => CORE_FEATURES.includes(f)).length;
      return (adopted / total) * 100;
    },
  },
  {
    name: "support_sentiment",
    weight: 0.15,
    score: (c) => {
      if (c.ticketsLast90Days === 0) return 70; // Neutral
      const resolved = c.resolvedTicketsLast90Days / c.ticketsLast90Days;
      return resolved * 100;
    },
  },
  {
    name: "expansion_signals",
    weight: 0.15,
    score: (c) => {
      const usageGrowth = c.currentMonthUsage / (c.previousMonthUsage || 1);
      if (usageGrowth > 1.2) return 100;
      if (usageGrowth > 1.0) return 70;
      if (usageGrowth > 0.8) return 40;
      return 10;
    },
  },
  {
    name: "payment_health",
    weight: 0.2,
    score: (c) => {
      if (c.failedPaymentsLast90Days > 2) return 20;
      if (c.failedPaymentsLast90Days > 0) return 60;
      return 100;
    },
  },
];

function calculateHealthScore(customer: Customer): {
  score: number;
  status: "healthy" | "at-risk" | "critical";
  signals: Array<{ name: string; score: number; weight: number }>;
} {
  const signals = healthSignals.map((signal) => ({
    name: signal.name,
    score: signal.score(customer),
    weight: signal.weight,
  }));

  const weightedScore = signals.reduce(
    (total, s) => total + s.score * s.weight,
    0
  );

  return {
    score: Math.round(weightedScore),
    status: weightedScore >= 70 ? "healthy" : weightedScore >= 40 ? "at-risk" : "critical",
    signals,
  };
}

// Scheduled job: calculate health scores and alert CSMs
async function updateHealthScores(): Promise<void> {
  const customers = await db.customer.findMany({
    where: { subscriptionStatus: "active" },
    include: { usage: true, tickets: true, payments: true },
  });

  for (const customer of customers) {
    const health = calculateHealthScore(customer);

    await db.customerHealth.upsert({
      where: { customerId: customer.id },
      create: { customerId: customer.id, ...health },
      update: health,
    });

    // Alert CSM for critical customers
    if (health.status === "critical" && customer.mrr >= 1000) {
      await notifyCSM(customer.csmId, {
        customer: customer.name,
        score: health.score,
        signals: health.signals.filter((s) => s.score < 40),
      });
    }
  }
}

原因: 健康评分将被动支持(等待客户流失)转变为主动的客户成功管理(在客户流失前介入)。综合使用情况、支持情绪、功能采用情况和付款健康状况的评分,能够提供全面视图。针对关键账户的自动提醒可确保高价值客户及时获得 CSM 关注。


反模式

反模式为什么不好更好的做法
仅由机器人提供支持,且无法转接人工客户感到沮丧并流失机器人负责分诊和提供知识,人工负责解决问题
转接时未传递上下文客户需要重复说明所有情况传递完整的对话历史和账户数据
以已关闭工单数衡量成功会促使过早关闭工单衡量首次联系解决率和客户付出的精力
知识库没有反馈闭环过时且无用的文章会一直存在跟踪有用性投票,标记表现不佳的文章
所有客户采用相同的 SLA企业客户与免费用户获得相同的优先级根据套餐和收入制定分级 SLA
支持与产品反馈相互独立错过可避免工单的规律将工单标记为产品反馈,并展示趋势

检查清单

  • 知识库可通过混合搜索(语义搜索 + 全文搜索)检索
  • 在关键页面提供应用内情境化帮助组件
  • 使用 AI 分类自动对支持工单进行分诊
  • 按优先级定义 SLA 政策
  • 监控 SLA 违约情况,并在需要升级时发出提醒
  • 按计划计算客户健康度评分
  • 高价值且有流失风险的客户会触发 CSM 提醒
  • 每次升级时都提供完整上下文(对话 + 账户数据)
  • 通过反馈闭环跟踪文章的有用性
  • 每月向产品团队展示支持趋势

相关资源

  • 技能: llm-app-development(聊天机器人 RAG)、product-analytics(支持漏斗分析)
  • 技能: email-systems(支持通知邮件)、accessibility-a11y(无障碍帮助组件)
  • 规则: docs/reference/stacks/react-typescript.md(帮助组件)

评论

支持 Markdown