Call Input Smoke

0. 这份 smoke 要回答什么

Task Accuracy Baseline v1 证明的是:AI 能不能把 contact / call / message / lead / task context 转成正确的 taskDecisions[]

Call Input Smoke 在它前面检查一层:

单次 call analysis 写进 calls 表的 structured facts 是否够准、够完整、够可解释。

如果 task 错了,先不要马上怪 taskDecisions[] prompt。可能是上游 call facts 已经歪了:

  • Call analysis 把 member_package 误导成别的业务意图。
  • customerType 写错,导致后续 contact lifecycle 判断跑偏。
  • followUpNeeded / followUpReasons 没写,导致 unresolved objective 被当成已解决。
  • direction / fromPhoneNumber / toPhoneNumber / storeId 归因不清,导致 AI 用错门店上下文。

这不是完整的 Call Accuracy Foundation。它是 demo 前的 lightweight upstream sanity check:确认 Task Accuracy 依赖的 call facts 没有明显错。

1. 它在 pipeline 里的位置

Raw call transcript + call metadata
        |
        v
ai-analysis-processor
        |
        v
Neon calls
  direction
  startTime
  duration
  primaryCategory
  primarySubcategory
  primaryOutcomeResult
  creditCardCaptured
  followUpNeeded
  followUpReasons
  customerType
  executiveSummary
        |
        v
contacts-analyzer RECENT CALLS context
        |
        v
contact profile + taskDecisions[]

这份 smoke 覆盖的是:

transcript / call metadata
   -> ai-analysis-processor output
   -> calls row
   -> contacts-analyzer RECENT CALLS line

它不测试 raw audio transcription 质量,也不测试 Vapi。demo 前可以直接用已转写 transcript 或已落库 call row 来验证。

2. 当前 production context 已经喂了什么

contacts-analyzer 看到的 RECENT CALLS 是半结构化文本:

- [startTime] direction duration |
  primaryCategory/primarySubcategory |
  out=<primaryOutcomeResult> cc=<creditCardCaptured> fu=yes(reason) cust=<customerType> |
  executiveSummary

这些字段来自 ai-analysis-processor 的 call analysis output,再被 contacts-analyzer 当成 Layer 1 evidence 使用。

所以要分开验证:

  • Call analysis 是否把 transcript 理解成正确 taxonomy。
  • calls 表是否持久化了正确字段。
  • contacts-analyzer prompt 是否真的拿到了这些字段。
  • Prompt 里的 enum values 是否来自 @retaintive/common/taxonomy/call-analysis,不要手写漂移。

3. Smoke 的 review 原则

不要做所有全排列。这里按 call decision point 覆盖:

  • Source identity:这是 inbound call、outbound call、voicemail call,还是系统/非客户流量?
  • Recency:startTime 是否格式正确,是否能和 contact 的 current time / last activity 对上。
  • Direction:Inbound / Outbound 是否影响 attribution 和 follow-up 判断。
  • Taxonomy:primaryCategoryprimarySubcategoryoutcomecustomerTypefollowUpReasons 是否符合 common taxonomy。
  • Outcome:客户目标是否已解决,还是 attempted / pending follow-up。
  • Safety:wrong number、vendor、corporate inquiry、DNC signal 是否保留。

这个 smoke 不是看模型“会不会编一个合理故事”,而是看 production call facts 是否足够支撑后续 contact / task 判断。

4. 第一批 smoke scenario set

每个 scenario 至少检查三件事:

  • DB factscalls row 里的字段是否写对。
  • Prompt contextcontacts-analyzerRECENT CALLS line 是否带到了这些字段。
  • Expected implication:这些 call facts 是否足够支持后续 contact / task decision。

C-01 Additional class pack / add-on purchase

业务状态

现有会员打电话,说自己本月课包用完,想买一次额外 class 或 package。

必须看到的 call facts

  • calls.primaryCategory = revenue_impacting
  • calls.primarySubcategory = member_package
  • calls.customerType = existing_member
  • calls.primaryOutcomeResult = attemptedsuccess
  • RECENT CALLS 里出现 revenue_impacting/member_package
  • executiveSummary 说明是 package / add-on / extra class,不是 membership renewal

Expected implication

Task taxonomy 上更接近 upgrade,不是 renewal。短期 UI label 可以显示成 Upgrade / Add-on;长期再把 typeCategory 抽象成更通用的 objectiveType

C-02 Billing / failed payment recovery

业务状态

会员的付款方式失败、卡过期,或需要补付款来恢复服务连续性。

必须看到的 call facts

  • calls.primarySubcategory = billing_issue
  • calls.customerType = existing_member
  • calls.followUpNeeded = yes when unresolved
  • followUpReasons 只能来自 common taxonomy,例如 needs_manager_approvalcomplaint_feedback
  • executiveSummary 说明是 payment / billing continuity,不只是普通 service note

Expected implication

这类更接近 renewal / continuity recovery。AI 不应该把它当作普通 service note 忽略。

C-03 Downgrade / freeze / cancel risk

业务状态

会员表达想 downgrade、freeze 或 cancel,但还没最终确认。

必须看到的 call facts

  • primarySubcategorymembership_downgrademembership_freezemembership_cancel
  • cancellation 语义要进 topic_type = cancellation when customer explicitly wants to cancel
  • outcome.result = pending_follow_up when unresolved
  • executiveSummary 说明客户的真实意图和 unresolved next step

Expected implication

如果 objective 未解决,后续 task 应该偏向 cancellation_riskretention,而不是 renewal

C-04 Existing lead books intro

业务状态

Lead 接通电话并确认预约 intro / first class。

必须看到的 call facts

  • Call direction 和 summary 能证明是 booking,不只是 generic inquiry。
  • primarySubcategory = intro_booking
  • customerType = prospective_client
  • primaryOutcomeResult = success when booking is confirmed
  • executiveSummary 说明已经 booked,不只是 interested

Expected implication

已有 lead task 应该 close as booked。不要因为已经 booked 再创建 intake、waiver、arrival logistics task。

C-05 Outbound no-answer

业务状态

员工对一个 open objective 打电话,没人接。

必须看到的 call facts

  • calls.direction = Outbound
  • callState = no_answer 或 summary 能证明 no answer
  • 没有客户新表达的 objection / DNC / close outcome
  • 如果没有 transcript 或只有 no-answer metadata,summary 不应该伪造客户意图

Expected implication

通常只是给 existing task record_progress,不是 create new task,也不是随便 close。

C-06 Wrong number / non-customer business inquiry

业务状态

来电是 wrong number、vendor、corporate sponsorship、partner、marketing pitch,和健身 customer lifecycle 无关。

必须看到的 call facts

  • customerType = other,或历史 row 可能仍是 legacy wrong_number
  • category/subcategory 能体现 corporate_inquiryother 或 wrong-number 语义
  • executiveSummary 保存有用事实,但不要伪造成 lead/member

Expected implication

后续 contacts-analyzer 应该把普通 fitness task no-op,或 close invalid existing task。历史 cust=wrong_number 也必须被当作 hard stop。

C-07 Referral / bring-a-friend opportunity

业务状态

现有会员主动说要带朋友、问 guest pass、推荐家人,或留下 referral opportunity。

必须看到的 call facts

  • primarySubcategory = referral_program 或 summary 明确提 referral / guest / friend
  • customerType = existing_member 或有明确 customer identity
  • executiveSummary 说明 referral opportunity 是否需要 staff follow-up

Expected implication

这可以支持后续 referral task。若同一个 contact 还有另一个 open task,Contact / Task UI 要能让 staff 看见 related open tasks。

C-08 Churned member asks to rejoin

业务状态

前会员主动来电,说想重新加入或回来上课。

必须看到的 call facts

  • customerType = former_member
  • inbound customer-initiated re-engagement evidence
  • primarySubcategory = reactivation_purchase
  • 如果 interaction 当场完成 rejoin,outcome 要能显示 success;否则 follow-up remains open

Expected implication

后续 task 应该偏向 win_back。这不是 cold outbound win-back;是 customer-initiated reactivation。

5. 当前已发现的 call prompt contract 风险

5.1 Call taxonomy prompt drift

ai-analysis-processor 的 Zod schema 已经大量引用 @retaintive/common/taxonomy/call-analysis。但 classify base prompt 曾经还有手写 enum 文案,例如 customer type、category、outcome、follow-up reason、secondary topic forbidden list。

这类手写列表的风险不是 DB 不认,而是 prompt contract 和 common taxonomy 漂移:模型读到旧定义,Zod 却按新 schema 校验。

处理原则:

  • enum values / allowed values / forbidden list 必须从 common taxonomy 生成。
  • 业务判断规则和例子可以留在 prompt,但值本身不要手写成第二份 source of truth。
  • Prompt snapshot 必须让 assembled system prompt diff 可 review。

5.2 Legacy call rows 要保留解释

历史 call rows 可能还带旧的 customerType value,例如 existingprospectnon_memberwrong_number。在完成数据迁移或 renderer normalization 前,prompt 必须告诉模型这些 legacy aliases 怎么理解。

最重要的是:cust=wrong_number 必须还是 hard-stop non-customer,不能因为新 taxonomy 只写 other 就失去保护。

5.3 Call Accuracy Foundation v1 的长期形态

#1132 里讨论的 subcategory -> primary category 方向长期是对的,但它不应该做成 prompt 里又一份手写 if-else。更稳的形态是:

Transcript + call metadata

LLM 只提出 topic_candidates
  - subcategory
  - outcome
  - evidence
  - confidence / reasoning

Deterministic canonicalizer
  - subcategory 是否在 common taxonomy
  - primaryCategory 从 taxonomy 反推
  - topic_type 是否需要特殊处理,例如 membership_cancel -> cancellation
  - outcome 是否允许
  - primary / secondary 用业务优先级决定

旧 shape 输出
  - primaryCategory
  - primarySubcategory
  - secondaryTopics
  - primaryOutcomeResult

后续 contacts-analyzer / DB / UI 不需要大改

这和 AI Agent Foundation 的关系是:

  • Facts:transcript、direction、from/to phone、storeId、duration、recording metadata。
  • Tenant / Vertical Taxonomy Registry:当前是 gym taxonomy;未来 fraud、医疗、BPO 可以换自己的 taxonomy pack。
  • AI Decision:LLM 提出候选 topic,不直接拥有最终 canonical category。
  • Deterministic Canonicalizer:用 @retaintive/common/taxonomy/call-analysis 做 source of truth,避免 prompt 和 schema 漂移。
  • Eval / Replay:Call Accuracy smoke / golden eval 验证 candidate extraction 和 canonical output 是否稳定。

6. Demo 前怎么用这份 smoke

每个 demo candidate 至少检查这一条链:

transcript / call metadata
  -> ai-analysis-processor output
  -> calls row
  -> contacts-analyzer RECENT CALLS context
  -> contact / task decision
  -> Task UI / timeline

建议最小操作:

  1. 从 8-12 个 demo candidate 里选 3 个真实端到端 case。
  2. 对每个 case 保存一份 prompt context 或 OpenRouter log。
  3. 在 DB 里确认 calls 的关键 structured fields。
  4. 如果错了,先归因:call transcript 错、call taxonomy 错、prompt contract 错,还是 downstream contact / task decision 错。