Task Accuracy Baseline v1 设计稿

Current legacy executable baseline(2026-07-15):本文用于解释和回归现有 production prompt/Zod/writer vocabulary;lead_outreach / lead_follow_up relay、global closeResulttask_progress_eventstask_objectives / task_issues 等不得作为 Target。新 domain/eval contract 见 Task System Design V3

0. 先说结论

Task Accuracy Baseline v1 不是“先做 Vapi 自动外呼”,也不是“让 AI 自由发挥生成任务”。它要先证明一个更底层的能力:

用 production prompt + production Zod schema + synthetic scenarios,证明 AI 能稳定把 contact / call / message / lead / task context 转换成正确的 taskDecisions[],并且所有高风险 mutation 都被 Policy Guard 和 DB constraint 控住。

这一步如果不准,后面接 Vapi、自动短信、AI agent execution、usage metering 都会变成“把错误更快放大”。所以 demo 前最重要的是:让 task decision 先可信、可复现、可解释、可回归测试

维度Demo 前先做Demo 后再做
AI 判断35-60 个 synthetic scenarios,跑真实模型多轮历史、更复杂行业、模型路由
写库安全8-12 个 DB replay 验证 writer / policy / constraints并发、approval queue、agent run audit
数据输入假设 transcript 已经被上游处理成 CallSummary / MessageSummary原始 transcript → call classifier → contact analyzer 全链路
对外执行不自动打电话、不自动发消息接 Vapi / SMS / Email,但先走 approval
Schema不建议 demo 前改 DB schema设计 task_objectives / task_issues 抽象层

0.1 今天改什么,不动什么

这次 Task Accuracy Baseline v1 先把“评审对象”和“测试对象”改对。不要把所有未来架构一次性塞进来。

今天改:

  • 把 scenario 写成可 review 的业务状态,而不是只写一个简陋表格。
  • 每个 scenario 必须写清楚:
    • existingState:已有 contact / task / attempt / lifecycle。
    • newEvidence:这次 call / SMS / voicemail / lead 到底发生了什么。
    • expected:正确的 task decision。
    • notExpected:明确不应该出现的 decision。
    • rationaleZh:为什么这样判。
  • contacts-analyzer golden eval 代码里维护同一份 scenario metadata。
  • attemptCount 放进 analyzer 的 OPEN TASKS context,因为 unable_to_reach / follow-up threshold 离不开它。
  • 先输出 review Markdown,让产品先确认 expected decision,再跑真实模型。

今天不动:

  • 不改 production DB schema;tasks.attempt_count / task_progress_events / dueAt 已够支撑 v1。
  • 不加 objective / desiredOutcome column;v1 先用 scenario 的 objectiveContext 表达。
  • 不接 Vapi。
  • 不做 raw audio / speech-to-text 测试。
  • 不把 message pipeline 改成主动触发 analyzer;先作为后续 issue。
  • 不让 writer 自动在 attempt threshold 时 close;v1 先由 AI decision 在 threshold 上 close,deterministic writer hardening 作为后续 issue。

最重要的 policy decision / implementation gap:

record_progress:
  writer 会写 task_progress_events
  writer 会 attempt_count + 1
  writer 会更新 dueAt
  writer 不会自动 close unable_to_reach

unable_to_reach:
  v1 policy 已确认 threshold = 2
  attemptCount = 0 + failed attempt -> record_progress
  attemptCount = 1 + failed attempt -> close unable_to_reach
  deterministic writer auto-close / progress_then_close 作为 #1142 后续 hardening

1. 这份设计根据什么来

Scenario 不是只靠 common sense 拍脑袋,也不是只机械覆盖 enum。优先级是:

优先级来源用来决定什么
1production schema / prompt / Policy GuardAI 能输出什么、哪些 mutation 必须被 reject、哪些字段必须存在
2当前健身房业务 workflow什么场景应该开任务、关任务、记进展、不要动
3AI eval good practice不做全排列;按 decision point 覆盖风险最高的路径
4未来 AI agent vision把 objective / approval / usage / feedback 的长期方向提前留口子

关键原则:

  • taskDecisions[] 是 proposal,不是 command。
  • AI 只做判断和建议;真正写库必须走 applyTaskAction()
  • 安全层是刹车:Zod schema、taskRef resolver、Policy Guard、writer guard、DB constraint 各拦一段。DNC、duplicate、store mismatch、hallucinated task ref、missing evidence 都要 fail closed。
  • Scenario 不做 enum 全排列。我们按“会影响决策的字段”覆盖。
  • Demo 只展示稳定通过的 case;不稳定但产品上正确的 case 标成 knownGap,不现场赌模型。

2. Source of Truth

这份设计以 live code 为准。主要读过的 source:

AreaSource说明
Task DB schemacallytics-common/src/db/schema/tasks.tsstatustypeCategorycloseResultexecutorType、dedup index
Progress event schemacallytics-common/src/db/schema/task-progress-events.tsrecord_progressprogressType / channel / idempotency
Suggestionscallytics-common/src/db/schema/task-suggestions.tsappend-only task_suggestions,保留 issueId 未来挂 task_issues
Playbookscallytics-common/src/db/schema/task-playbooks.tsprimaryActionrecommendedStepscloseGuidance 等执行指导
Task orchestratorcallytics-common/src/domain/task-orchestrator.ts所有 task mutation 的统一入口:applyTaskAction()
Policy Guardcallytics-common/src/domain/policy-guard.tsDNC、lifecycle allowlist、duplicate、store-match 等
AI output schemacontacts-analyzer/src/core/models.tsContactsAnalysisSchemataskDecisions[] Zod contract
Prompt decision policycontacts-analyzer/src/core/prompts/task/decision-policy.tscreate / close / update / progress / no-op 规则
Prompt input buildercontacts-analyzer/src/core/prompt-builder.tsAI 实际看到哪些 context
Writercontacts-analyzer/src/infrastructure/neon-repository.tsAI validated output 如何落到 contacts/tasks/timeline/suggestions/playbooks
Golden evalcontacts-analyzer/scripts/golden-eval/*当前真实模型 + production prompt + synthetic fixtures 的 eval 框架

3. 我们到底在测试什么

In Scope

Action目标
create_open新的 unresolved customer objective,且没有同类 open task
create_closed单次互动里 objective 出现又被解决,需要记录结果但不留 open task
closeopen task 的目标成功、失败、无效、DNC、wrong number、attempt exhausted
update同一个 open objective 有新证据,改变 next action / priority / suggestedActions
record_progress员工已经尝试联系,但目标还没完成,比如 no answer / voicemail / text sent
no-op没有 unresolved objective,或者 routine confirmation / campaign no reply
SafetyDNC、duplicate、wrong category、hallucinated task ref、missing evidence、store mismatch

Out of Scope for v1

  • 不测原始 audio。
  • 不把语音重新转 transcript。
  • 不做 Vapi 自动外呼。
  • 不自动发 SMS / Email / WhatsApp。
  • 不实现完整 approval queue。
  • 不在 demo 前做 task objective schema migration。

这些不是不重要,而是 demo 前不是最短路径。

4. 当前生产流程

现在的 contacts-analyzer 不是一个“任意 POST transcript 就生成 task”的公开接口。它是事件触发:上游 pipeline 已经把 call / message / lead 的结构化结果写进 Neon,然后 SQS 触发 analyzer 聚合 context。

Call 入口:现在最接近 Task Accuracy v1 的真实主路径

RingCentral call ended webhook
  -> transcribe-queue
  -> transcribe-processor
  -> S3 transcript / structured call summary
  -> ai-analysis-queue
  -> ai-analysis-processor
  -> Neon: calls / contacts / contact_timeline
  -> contacts-analyzer SQS: { phone, storeId, source }
  -> read recent calls/messages/leads/tasks/contact
  -> production prompt + ContactsAnalysisSchema
  -> taskDecisions[]
  -> resolve taskRef T1/T2
  -> writeAnalysisWithTasks()
  -> applyTaskAction()
  -> Policy Guard + DB constraints
  -> tasks / task_progress_events / task_suggestions / task_playbooks
Message 入口:现在主要先写事实,后续被 analyzer 读取

RingCentral SMS / voicemail webhook
  -> message-processing-queue
  -> message-processor
  -> Neon: messages / contacts / contact_timeline
  -> STOP keyword can update contact.doNotContact
  -> 当前不会主动 trigger contacts-analyzer
  -> 但下一次 contacts-analyzer 运行时会把 messages 读进 context

未来建议:
message-processor 在高价值 inbound reply / STOP / objection 场景下
  -> enqueue contacts-analyzer
Lead 入口:lead_outreach 是 deterministic system task,不是 AI 创建

lead-tracking poller
  -> Neon: leads
  -> EventBridge LeadCreated
  -> lead-processor
  -> contacts upsert
  -> deterministic create lead_outreach task
  -> contact_timeline
  -> 后续 call/SMS/AI 再 progress / update / close

关键约束:
AI 可以 create 8 个业务 category
AI 不能 create lead_outreach
lead_outreach belongs to lead-tracking / lead-processor

这三条入口对应到 Task Accuracy v1 的测试含义:

入口现在代码支持什么v1 怎么测Demo 后补什么
Callcall facts 写入后会触发 contacts-analyzersynthetic CallSummary + golden eval;关键 case 做 DB replayfull pipeline E2E:raw transcript → call classifier → analyzer
Messagemessage / voicemail 写入 Neon;STOP 可改 DNC;analyzer 后续可读synthetic MessageSummary / voicemail transcriptionmessage processor 主动 enqueue analyzer
Leadlead created 后 deterministic 创建 lead_outreachseed LeadInfo + existing lead_outreach task,验证 AI 后续 progress/close/updatelead event 到 analyzer 的更清晰触发策略

4.1 Transcript 怎么插入?

我们 demo 前不需要真的走 audio → transcription。可以直接假设 transcript 已经处理完,把关键内容放到 analyzer 真实会读的字段里:

输入类型v1 怎么表达代码里的载体
Call transcript摘要成一句或几句事实CallSummary.executiveSummary
Call classifier factsoutcome / category / customerTypeprimaryCategoryprimarySubcategoryprimaryOutcomeResultfollowUpNeeded
SMS短信正文MessageSummary.subject
Voicemail语音留言转写MessageSummary.voicemailTranscription
Lead表单或来源信息LeadInfo
Existing taskopen task contextTaskRow,prompt 展示成 taskRef: T1

三层测试深度:

Level怎么做用途
Level 1: Golden Evalgolden-eval/cases.ts 里构造 synthetic ContactData / CallSummary / MessageSummary / LeadInfo / TaskRow快速验证 prompt + Zod + real model
Level 2: DB Replayseed Neon test DB 的 processed rows,再跑 writer 或 analyzer harness验证 writer / Policy Guard / DB constraints / artifact writes
Level 3: Full Pipeline E2Eraw transcript → call classifier → calls structured fields → contacts-analyzerdemo 后做,验证上游 transcript summarization

Demo 前建议优先 Level 1 + Level 2。Level 3 很重要,但会把变量变多,不适合两天内证明 task decision 准确性。

4.2 为什么不先大改 DB schema?

短答案:demo 前不要先改 production DB schema。先把 scenario 和 eval 跑准,再让真实 scenario 反推 schema。

原因有三个:

  1. Task Accuracy v1 的核心是验证 prompt -> Zod schema -> Policy Guard -> writer 是否稳定,不依赖新表。
  2. typeCategory 现在已经深度绑定 prompt、Zod schema、writer、UI、open-task dedup index。两天内迁 schema 容易把 demo 风险放大。
  3. 我们还没用 35-60 个 scenario 证明哪些 objective / issue 真的是高频、稳定、跨行业的抽象。

所以 v1 先在 scenario 里显式写 objectiveContext,但不立刻写入 production DB。这样 review 的内容是长期正确的,同时不打断现有系统。

4.3 Objective / ContextAI Decision 的区别

你问得对:如果完全没有 AI 分析,系统怎么知道 objective?

答案是:Objective / Context 不是“AI 已经做完判断后的结论”,而是 AI 判断前的候选目标目录 + 当前上下文包。AI 再基于这个上下文做 decision。

Objective Catalog + Context Builder
  已知事实:
    contact lifecycle / lead status / doNotContact
    recent calls / messages / voicemail / lead
    existing open tasks / recently closed tasks
    tenant/store policy
    task playbook / suggestion history

  产出给 AI 的不是最终答案,而是候选问题:
    这里是否有 unresolved customer objective?
    如果有,是 existing task 的 objective,还是新的 objective?
    这次 evidence 应该 create / close / update / progress / no-op?

AI Decision Layer
  读取上面的 context
  判断 taskDecisions[]
  输出 proposal
  不能直接写库

三个例子:

情况Objective 从哪里来AI 要判断什么
已有 open tasktask 已经给出 objective,比如 lead_follow_up最新 call/SMS 是 closerecord_progressupdate 还是 no-op
没有 open task,但有新 inbound evidencesystem 只知道候选 objective,比如 lead 可能要预约AI 判断是否要 create_opencreate_closed
没有 open task,也没有 actionable evidence只有事实,没有 objectiveAI 应该 no-op,不能硬造任务

5. Task Decision 思考模型

这张图是 reviewer 判断 scenario 的心智模型:同一个通话可能是店员打出去,也可能是客人打进来;可能只是一次 attempt,也可能是 close outcome;也可能多轮往返后改变 next action。

New evidence: call / SMS / voicemail / lead
  |
  v
Is this DNC / STOP / doNotContact?
  |
  +-- yes
  |     -> set contact.doNotContact = true
  |     -> close or block open outreach tasks
  |     -> no outbound action
  |
  +-- no
        |
        v
Does a matching open task exist?
  |
  +-- yes
  |     |
  |     v
  |   Did the latest event change the objective state?
  |     |
  |     +-- outbound attempt only
  |     |     -> record_progress: no_answer / left_voicemail / text_sent
  |     |
  |     +-- customer still deciding / asks callback
  |     |     -> record_progress or update
  |     |
  |     +-- achieved / rejected / wrong number / exhausted
  |     |     -> close: booked / converted / not_interested / wrong_number / unable_to_reach
  |     |
  |     +-- new evidence changes next action
  |     |     -> update suggestedActions / priority / dueAt
  |     |
  |     +-- noise only
  |           -> no-op
  |
  +-- no
        |
        v
      Is there a new actionable unresolved objective?
        |
        +-- yes, still open
        |     -> create_open
        |
        +-- yes, arose and settled in same call
        |     -> create_closed
        |     -> requires triggering call evidence
        |        analyzer uses triggeringCallId -> sourceCallId
        |
        +-- no
              -> no-op

几个容易混的点:

  • 店员打出去没人接:通常是 record_progress,不是 close,也不是新建任务。
  • 客人打进来并预约成功:如果有 open lead_follow_up,应该 close booked;如果没有 open task 且从产生到解决都在这通电话里,可能是 create_closed booked
  • “我不感兴趣”通常是 close not_interested;只有明确 “STOP / do not contact / remove me” 才是 DNC。
  • Routine confirmation、waiver、arrival logistics 不应该变成 task。
  • dueAt 只是 staff effort 的排期,不应该阻止记录真实 outcome。

6. Scenario Schema

Scenario 的目的不是让机器看懂,而是让产品 / 业务 / 工程能一起 review:“给了这些证据,expected decision 是否正确?”

6.1 Review 格式

不要只写 “C-01 lead follow-up create”。每个 scenario 都要写成下面这种状态机格式:

Case A: 店员打给 lead,没人接

existingState:
  Contact 是 active lead
  Open task: T1 lead_outreach
  T1 attemptCount = 0

newEvidence:
  Outbound call
  callState = no_answer
  客户没有任何回复

expected:
  record_progress no_answer on T1
  task 保持 open
  后续由 dueAt 安排下一次 follow-up

notExpected:
  close unable_to_reach
  create_open lead_follow_up
  taskDecisions=[]

rationaleZh:
  单次 outbound no-answer 是一次员工尝试,不是目标完成或失败;
  已有 lead_outreach 就推进原 task,不能重复创建 lead_follow_up。

完整 review 清单由代码导出,避免文档和测试漂移:

cd ../callytics-infrastructure
npm run eval:golden -- --review-md ../docs/docs/ai/product/task-accuracy/baseline-v1-scenario-review.md

这个命令不调用模型,不需要 OPENROUTER_API_KEY。它只把 GOLDEN_CASES[].review 导出成 Markdown,给你逐个 review。

6.2 Executable Schema

代码里的每个 GoldenCase 同时包含两层:

用途
review给产品/业务/工程 review,写 existingState / newEvidence / expected / notExpected / rationaleZh
assertions给 golden eval 执行,只断言结构化字段,比如 action / taskId / typeCategory / closeResult / progressType

建议 registry 里每个 case 长这样:

interface TaskAccuracyScenario {
  id: string;
  titleZh: string;
  priority: 'P0-demo' | 'P1-baseline' | 'P2-hardening';
  reviewStatus: 'approved' | 'needs_product_decision' | 'known_gap';
  demoCandidate?: {
    id: 'P0-01' | string;
    status: 'needs_model_validation' | 'blocked_known_gap' | 'candidate';
    titleZh: string;
    rationaleZh: string;
  };
  bucket:
    | 'create_open'
    | 'create_closed'
    | 'close'
    | 'update'
    | 'record_progress'
    | 'negative_noop'
    | 'safety';
  sourceOfRule: Array<'production_prompt' | 'zod_schema' | 'policy_guard' | 'business_rule' | 'eval_practice'>;
  contact: {
    lifecycleStage: 'lead' | 'member' | 'churned' | 'unknown';
    lifecycleState: 'active' | 'paused' | 'terminal';
    doNotContact: boolean;
    leadStatus?: string | null;
    purchaseIntent?: 'high' | 'medium' | 'low' | null;
    hasOpenComplaint?: boolean | null;
  };
  existingTasks: Array<{
    taskRef: 'T1' | 'T2';
    status: 'open' | 'closed';
    typeCategory: string;
    attemptCount?: number;
    dueAtRelative?: string;
    suggestedAction?: string;
    closeResult?: string;
  }>;
  evidence: {
    source: 'call' | 'sms' | 'voicemail' | 'lead' | 'mixed';
    direction?: 'Inbound' | 'Outbound';
    hasTriggeringCallId?: boolean;
    fromPhoneRole?: 'store' | 'contact';
    toPhoneRole?: 'store' | 'contact';
    summaryZh: string;
  };
  objectiveContext?: {
    mode: 'existing_task' | 'candidate_objective' | 'no_objective';
    objectiveType?: string;
    objectiveZh?: string;
    desiredOutcomeZh?: string;
    successCriteria?: string[];
    failureModes?: string[];
    evidenceRequirement?: string[];
  };
  expected: {
    taskDecisions: Array<Record<string, unknown>>;
    contactFields?: Record<string, unknown>;
    dbReplay?: boolean;
    artifactChecks?: Array<'task_suggestions' | 'task_playbooks'>;
  };
  reviewQuestionZh: string;
  notes?: string;
}

字段解释:

字段为什么要有
priority区分 demo 必测、baseline、hardening,不把所有 case 混成一锅
bucket确保覆盖的是 decision point,不是 enum 全排列
sourceOfRule标清这个 expected 是来自 prompt、schema、Policy Guard、业务规则还是 eval practice
contactlifecycle / DNC / intent 直接影响 AI 是否可以 create
existingTasksopen task 决定 close / update / progress;recently closed 只能做 read-only context
evidencecall/SMS/voicemail/lead、方向、phone role、触发证据会改变 writer 行为
objectiveContext用中文把“这个场景的业务目标”讲清楚;v1 先用于 review/eval,不要求 production DB 已有这个字段
expected结构化 expected decision,避免只写自然语言导致测试不可执行
reviewQuestionZh给你逐个判断 scenario 是否正确时用
reviewStatus区分已认可、需要产品拍板、已知 gap,避免把模糊 case 写成硬断言
demoCandidateP0 demo 候选 metadata;最终是否进 demo 取决于真实模型稳定性和 DB replay

6.3 Scenario Registry / Golden Eval / DB Replay / Demo Pack 的区别

这几个名字容易混在一起。它们不是同一个东西,也不是四套重复系统。

Scenario Registry
  = 题库 / 真值表
  = 我们认为业务上应该发生什么

Golden Eval
  = 让真实模型做题
  = 验证 AI 判断准不准

DB Replay
  = 把关键答案拿去真实 writer path 试写
  = 验证 Policy Guard / writer / DB constraints 是否安全

Demo Pack
  = 从题库里挑稳定通过的展示路线
  = 给 investor / team 看,不是新的测试层

更具体地说:

名称输入做什么输出主要回答的问题
Scenario Registrysynthetic contact / call / message / lead / task context + expected decision保存可评审、可执行的 caseGOLDEN_CASES[]这道题应该怎么判?
Golden EvalScenario Registry + production prompt + production Zod schema + real model跑模型并检查 assertionspass / fail / xfailAI 会不会判对?
DB Replay关键 scenario + seed DB rows + proposed action走 writer / Policy Guard / DB constraintspersisted assertion verdict判对以后,写库会不会安全?
Demo PackScenario Registry 里的 demoCandidate metadata + 稳定 eval 结果选一条演示路线P0 demo case list哪些 case 可以放心现场展示?

可以把它们理解成:

Scenario Registry 是考试题
Golden Eval 是让 AI 真做题
DB Replay 是把答案拿去真实系统里试写库
Demo Pack 是挑已经稳定做对的题现场展示

6.4 DB Replay 不是新业务逻辑

DB Replay 需要写一点 test harness,但不应该写新的 task 业务逻辑。正确边界是:

DB Replay Scenario
  |
  | seed synthetic DB rows
  | contacts / calls / messages / leads / existing tasks
  v
真实 Neon test branch
  |
  | inject mock AI output
  | taskDecisions[] = expected decision
  v
现有 production code
  |
  | contacts-analyzer handler
  | -> resolveTaskRefs(T1/T2)
  | -> NeonRepository.writeAnalysisWithTasks()
  | -> common applyTaskAction()
  | -> Policy Guard / DB constraints
  v
真实数据库写入
  |
  | query persisted state
  | tasks / task_progress_events / contact_timeline / contacts / artifacts
  v
assert expected DB state

所以新增的是:

  • scenario seed data。
  • mock AI output injector。
  • persisted assertion runner。

不新增的是:

  • task writer。
  • duplicate logic。
  • DNC logic。
  • create_open / create_closed / close / update / record_progress 业务逻辑。
  • Policy Guard。

当前可执行入口:

cd ../callytics-infrastructure
npm run eval:db-replay

运行前置条件:

  • NEON_API_KEY 已设置。
  • NEON_PROJECT_ID_TEST 指向 test Neon project。
  • 该命令会创建 ephemeral Neon branch,跑完后删除。

为什么只选 8-12 个 DB replay,而不是把 57 个 scenario 全部 DB replay:

  • Golden Eval 已经负责全量判断覆盖。
  • DB Replay 更慢、更重,需要 Neon branch、seed、writer、query assertions。
  • 很多 scenario 在 writer 层走同一条路径,全部 replay 会变成重复测同一段代码。
  • 第一批先覆盖代表性 mutation 和高风险 guard:create_open / create_closed / close / record_progress / update / no-op / DNC / duplicate / forbidden create / missing evidence / hallucinated taskRef
  • 后续如果 matrix 某个 cell 在真实模型、线上或 demo 中不稳,再给那个 cell 加 targeted DB replay。

6.5 它是不是 plug-in / plug-out?

方向上是,但 Baseline v1 先不把 framework 抽象过度。

长期目标是:

厚底座 AI Foundation
├─ context builder
├─ LLM / structured output
├─ Policy Guard
├─ writer / Orchestrator
├─ eval harness
├─ replay harness
└─ feedback loop

薄 Skill: Fitness Task Skill
├─ objective / category definitions
├─ prompt fragments
├─ allowed actions
├─ scenario registry
└─ demo pack

未来薄 Skill: Fraud Detection Skill
├─ fraud objective definitions
├─ fraud policy rules
├─ fraud scenarios
└─ fraud demo pack

Task Accuracy Baseline v1 暂时还是 contacts-analyzer 专用,但设计上已经按这个方向切分:

  • scenario 是业务 skill 层的东西。
  • prompt / schema / model call 是 AI Decision 层。
  • Policy Guard / writer 是底座层。
  • demo pack 是展示路线,不是业务逻辑。

以后如果换到 fraud、billing、support,应该替换的是 skill 的 objective/scenario/playbook,不是重写整套 eval / policy / replay 基座。

6.6 什么时候会触发这些检查?

Task Accuracy Foundation 不是 production request path 里的同步检查。真实用户事件进来时,系统不会临时跑完整 Scenario Registry。

真实 production event
  -> contacts-analyzer
  -> taskDecisions[]
  -> Policy Guard / writer
  -> DB mutation

生产路径每次都会走 runtime guard,但不会每次跑 Golden Eval / DB Replay。

开发 / CI / demo 前
  -> Scenario Registry
  -> Golden Eval
  -> selected DB Replay
  -> Demo Pack / Known Gaps

建议触发方式:

时机应该跑什么
改 prompt / output schema / task prompt registryGolden Eval
applyTaskAction() / writer / Policy GuardDB Replay + related unit tests
改 scenario expected 或新增 scenarioreview Markdown generation + Golden Eval
demo 前多轮 Golden Eval + 关键 DB Replay,只展示稳定 case
nightly / weeklyGolden Eval 全量或重点 case subset

7. Decision Axes

不要做全排列。我们按“会改变决策”的轴覆盖。

7.1 Contact Axes

FieldValues为什么影响 decisionReview question
lifecycleStagelead / member / churned / unknown决定可以创建哪些 typeCategory这个人到底是 lead、member、churned,还是不属于 workflow?
lifecycleStateactive / paused / terminalterminal 默认不能再主动创建任务这个 lifecycle 还能被 staff 推进吗?
doNotContacttrue / falsehard stop;writer 会 cascade close open tasks是否有明确 STOP / do not contact?
leadStatusnew / attempted / connected / booked / showed / trialed / converted / ...区分 cold lead、engaged lead、booked、converted、not_interested是首次 outreach,还是已经有互动后的 follow-up?
purchaseIntenthigh / medium / low影响 priority,不直接等于 create是否有足够证据说明购买意图?
hasOpenComplainttrue / falseunresolved complaint 可能触发 retention投诉是否还没解决?

7.2 Task Axes

FieldValues为什么影响 decision
typeCategory9 个 DB categoryexisting open tasks 可以是 9 个,包括 lead_outreach
AI-creatable category8 个,不含 lead_outreachAI 不能创建 lead_outreach,那是 lead-tracking owned
statusopen / closed只有 open task 会有 taskRef;closed task 是 history
attemptCount0 / 1 / threshold决定 unable_to_reach 是否允许
dueAtoverdue / future不能因为未来才 due 就忽略真实 outcome

7.3 Evidence Axes

FieldValues为什么影响 decision
Input sourcecall / SMS / voicemail / lead / mixed不同输入触发不同 evidence surface
DirectionInbound / Outboundoutbound attempt 多数是 progress;inbound response 更可能 close/update/create
Phone rolestore → contact / contact → store校验方向语义和 store attribution
Triggering evidencepresent / absentcreate_closed 在 contacts-analyzer writer 里必须有 triggeringCallId,落库 payload 使用 sourceCallId;缺失时 writer skip
Channelphone / sms / voicemail / emailtask_progress_events.channel 当前支持 4 个 channel;v1 主要测 phone/SMS/voicemail,email 属于后续扩展
Multi-turnlatest event + history多轮后可能从 update 变 close,或从 no-op 变 create

8. Proposed Scenario Set

第一批目标是 35-60 个,不是把下面所有组合硬乘。P0 demo pack 是从 executable scenario metadata 里挑出来的稳定 case,不额外算一套,也不维护一份脱离代码的 wish list。

8.1 P0 Demo Pack

ID中文场景Expected decision为什么适合 demo
P0-01新 lead 主动来电问课程和价格,还没预约create_open lead_follow_up展示 AI 能从 intent 开 actionable task
P0-02open lead follow-up,客户预约 intro classclose booked展示成功 outcome 会关任务
P0-03open lead follow-up,客户明确不感兴趣close not_interested展示不会让 staff 继续追 dead task
P0-04客户 SMS 回复 STOP,且有多个 open tasksdoNotContact=true + cascade close展示安全边界
P0-05员工打 outbound call 没人接record_progress no_answer展示 attempt 不等于 close
P0-06员工 outbound 留 voicemailrecord_progress left_voicemail展示 progress events / attempt count
P0-07member 来电要 cancel,当场被挽留成功create_closed cancellation_risk cancel_saved展示 one-touch resolved outcome
P0-08member 有未解决投诉create_open retention展示 retention risk
P0-09wrong number 证据明确close wrong_number展示 invalid contact path
P0-10routine confirmation,没有 unresolved objectivetaskDecisions=[]展示不会乱建任务

8.2 Create Open Cases

覆盖 8 个 AI-creatable category。

ID中文场景Expected
C-01engaged lead 问价格 / 课程,没有预约create_open lead_follow_up
C-02lead 已完成 intro / trial,但没有购买 membershipcreate_open booked_not_converted
C-03member 表达 cancel / freeze / downgrade 风险create_open cancellation_risk
C-04member 投诉 billing / service,问题未解决create_open retention
C-05churned former member 主动问重新加入create_open win_back
C-06member 对 upgrade / add-on / personal training 感兴趣create_open upgrade
C-07freeze expiring 或 payment recovery 需要人工处理create_open renewal
C-08member 主动要带 friend / family / referral creditcreate_open referral

8.3 Negative / No-op Cases

ID中文场景Expected
N-01doNotContact=true,但又出现 sales signalno create;DNC only
N-02lifecycleState=terminal,没有新的 qualifying signalno create
N-03unknown lifecycle,且没有可靠 customer contextno create
N-04cold lead 只有 lead record,没有 inbound engagementno lead_follow_uplead_outreach belongs to lead-tracking
N-05campaign / broadcast SMS,没有客户回复no create
N-06routine appointment confirmationno create
N-07已预约 lead,只剩 waiver / arrival / intake logisticsno create
N-08问题在电话里完全解决,且没有后续 objectiveno open task
N-09vendor / corporate pitch,不属于 customer workflowno fitness task
N-10recently closed same objective,没有新证据no reopen / no duplicate create

8.4 Close Outcome Cases

Close 不和所有 category 做硬乘,而是按业务结果测。

IDOpen task中文证据Expected
CL-01lead_follow_uplead 预约 introclose booked
CL-02lead_follow_uplead 成为 paying memberclose converted
CL-03lead_follow_up客户明确拒绝,不感兴趣close not_interested
CL-04any outreach taskSTOP / do not contactDNC cascade 或 close do_not_contact
CL-05any lead/member taskwrong / unrelated numberclose wrong_number
CL-06cancellation_risk客户同意留下 / freeze / downgradeclose cancel_saved
CL-07cancellation_riskcancellation approved 或 form sentclose cancelled
CL-08retentioncomplaint resolvedclose issue_resolved
CL-09upgrademember 买了 upgrade / add-onclose upgraded
CL-10renewalcard fixed / renewal restoredclose renewed
CL-11win_backformer member re-enrollsclose win_back
CL-12referralreferral contact captured / bookedclose referral_obtained
CL-13lead_follow_upattempt threshold reached,仍无法联系close unable_to_reach
CL-14anyoutcome 真实但分类不覆盖close other with reason

8.5 Progress Cases

IDOpen task中文证据Expected
P-01lead_outreach员工 outbound call,no answerrecord_progress no_answer channel=phone
P-02lead_follow_up员工 outbound call,left voicemailrecord_progress left_voicemail channel=voicemail
P-03lead_follow_up员工发 outbound SMS,还没有回复record_progress text_sent channel=sms
P-04cancellation_risk客户接通但还在考虑record_progress customer_considering channel=phone
P-05renewal客户要求晚点回拨record_progress callback_requested
P-06retention约定 manager follow-uprecord_progress follow_up_scheduled
P-07any open task单次 no-answernot close unable_to_reach
P-08any open taskmissing evidence / no triggering callproposed decision may be skipped by writer

8.6 Update Cases

IDOpen task中文新证据Expected
U-01lead_follow_up新出现 pricing objectionupdate suggestedActions
U-02cancellation_riskmanager callback promisedupdate suggestedActions
U-03retentionbilling dispute details clarifiedupdate suggestedActions
U-04renewalpayment link sent but not confirmedupdate suggestedActions,not close
U-05booked_not_converted客户说要和 spouse 商量update suggestedActions
U-06any open task重复 generic no-answerrecord_progress,not update

8.7 Safety Cases

ID风险Expected
S-01AI 尝试 create lead_outreachAI output Zod schema 不允许 create_open lead_outreach;如果绕过 schema,Policy Guard 也应 reject
S-02AI 引用 T9,但 prompt 只展示 T1resolveTaskRefs drops / rejects
S-03AI 输出 raw UUID 作为 taskIdschema repair / reject
S-04同 contact/store/category 重复 open taskPolicy Guard / DB unique prevents duplicate
S-05create_closed missing triggering call evidencecontacts-analyzer writer skips;shared Orchestrator 也会 reject missing sourceCallId 做 defense-in-depth
S-06record_progress missing evidencewriter skips or Orchestrator rejects
S-07existing task store mismatchPolicy Guard rejects
S-08closeResult=other without note / reasonPolicy Guard rejects

9. TaskPlaybook / Suggestions 要不要测

要加,但 v1 不要过度扩大范围。

当前代码里 writeAnalysisWithTasks() 对 accepted create_open、带 suggestedActionscreate_closed/update 会写:

  • task_suggestions:append-only,每条建议一行,带 promptVersion / runId
  • task_playbooks:append-only/cache,content 包含 primaryActionrecommendedStepsoptionsYouCanOfferavoidcloseGuidance

v1 建议测两层:

层级是否放 v1验证什么
Artifact persistence是,DB replay 必测accepted mutation 后 task_suggestions.activetask_playbooks.current 是否存在
Basic content shape是,轻量测primaryActionrecommendedStepscloseGuidance 非空
Semantic playbook quality第二批playbook 是否真的是好话术、好策略、符合品牌 SOP

原因:demo 前我们要证明“AI decision + safe write + artifact created”闭环存在;playbook 质量当然重要,但它是另一个 eval 维度,不应该阻塞 Task Accuracy Baseline v1。

10. First Batch vs Second Batch

第一批:Task Accuracy Baseline v1

目标是证明 task decision 本身准。

  • 35-60 个 scenario。
  • Golden eval 跑 production prompt + production Zod schema + real model。
  • 8-12 个 DB replay 验证 writer / policy / constraints / artifact writes。
  • Demo pack 只选稳定通过 case。

第二批:Hardening + Voice-Agent Readiness

目标是让系统准备好接自动执行。

  • 多轮 back-and-forth:几天内来回通话 / SMS。
  • 同 phone 跨 store。
  • recently closed 后又出现新证据。
  • concurrent duplicate create。
  • stale proposal:AI 开始分析后 staff 已经改了 task。
  • playbook semantic quality eval。
  • approval queue。
  • Vapi agent execution log。
  • operating hours / blackout / DNC before dialing。

11. Definition of Done

Task Accuracy Baseline v1 完成时应该有:

  • 35-60 个 synthetic scenarios,每个有结构化 expected decision。
  • 覆盖 create_open / create_closed / close / update / record_progress / no-op / safety
  • 使用 production prompt + production Zod schema + real model 的 golden eval。
  • 8-12 个 DB replay case,验证 writer / Policy Guard / DB constraints;当前入口是 npm run eval:db-replay
  • task_suggestions / task_playbooks 的基础 artifact write 被验证。
  • knownGap 明确记录,不把不稳定 case 放进 demo。
  • GitHub parent issue 统筹,child issues 分开执行。
  • Scenario registry 能继续复用:以后加 industry、加 Vapi、加 prompt update,都从这里扩展。

12. 当前缺口

缺口影响Demo 前要不要做
Scenario registry 不完整没法系统 review expected decision
Golden eval case 太少覆盖不了 6 action + safety
DB replay harness 初版已加,当前本地缺 Neon env 未完成实跑不知道当前 branch 在真实 Neon 上是否全绿要,在设置 NEON_API_KEY / NEON_PROJECT_ID_TEST 后跑 npm run eval:db-replay
voicemail -> record_progress 曾是 known gapattempt count 可能不进展要修或不放 demo
firm rejection -> close not_interested 曾是 known gapstaff 会继续追 dead task要修或不放 demo
Playbook 质量 eval 没有执行建议可能质量不稳v1 只测基础 artifact,质量第二批
tasks 没有 objective layer长期跨行业不够抽象不建议 demo 前改

13. 下一步评审方式

建议接下来这样推进:

  1. 先把上面的 scenario registry 写成机器可读文件。
  2. 你逐个 review P0 + P1:expected decision 对不对。
  3. 不确定的 scenario 标 needs_product_decision,不要直接写进 hard assertion。
  4. 通过 review 的 case 才进 golden eval。
  5. 高风险 case 再进 DB replay。
  6. Demo pack 从“连续稳定通过”的 case 里挑,不从 wish list 里挑。

这会让 task eval 变成团队长期资产,而不是一次性的 demo 脚本。