'asr_synthetic', 'start_ms' => 0, 'end_ms' => 120000, 'text' => $text]]; $item = ['kind' => 'blood', 'values' => ['systolic_pressure' => 120], 'record_date' => $date, 'record_time' => '07:45', 'date_text' => $dateText, 'time_text' => '七点四十五分', 'time_period' => null, 'time_estimated' => false, 'needs_review' => false]; if ($v1) { $item['evidence'] = [['segment_id' => $segments[0]['id'], 'text' => $text]]; } else { $item['evidence_ids'] = [Prompt::citations($segments)[0]['id']]; } $raw = ['schema_version' => $v1 ? 'followup-audio-transcript-v1' : 'followup-audio-transcript-v2', 'summary' => '合成日期安全测试', 'uncertainties' => [], 'items' => [$item]]; $actual = Pipeline::validateAnswer(json_encode($raw, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), $text, $segments, '2026-03-01 10:00:00', ['blood' => [['key' => 'systolic_pressure', 'type' => 'number']]]); $observed = array_intersect_key($actual['items'][0], array_flip(['record_date', 'date_text', 'needs_review', 'time_estimated'])); $expected = ['record_date' => $expectedDate, 'date_text' => $expectedDateText, 'needs_review' => $review, 'time_estimated' => false]; $again = Policy::normalizeExtraction($actual, '2026-03-01 10:00:00'); $pass = Policy::canonical($observed) === Policy::canonical($expected) && $again === $actual; $rows[] = ['id' => $id, 'input' => $raw, 'synthetic_source' => $text, 'expected' => $expected, 'actual' => $observed, 'renormalization_identical' => $again === $actual, 'passed' => $pass]; if (!$pass) { $failed[] = $id; } }; $wide = '今天准备联系您。七点四十五分收缩压一百二十。'; $run('wide_window_other_sentence_does_not_fill_abstention', $wide, null, '', null, '', true); $run('explicit_calendar_without_date_text_is_unbound', $wide, '2026-03-01', '', null, '', true); $run('whitespace_date_text_is_unbound', $wide, '2026-03-01', ' ', null, '', true); $run('explicit_null_even_with_label_stays_abstained', $wide, null, '今天', null, '今天', true); $run('single_sentence_missing_date_still_preserves_model_abstention', '今天七点四十五分收缩压一百二十。', null, '', null, '', true); $run('complete_explicit_date_still_checked', '今天七点四十五分收缩压一百二十。', '2026-03-01', '今天', '2026-03-01', '今天', false); $run('conflicting_explicit_date_still_cleared', '昨天七点四十五分收缩压一百二十。', '2026-03-01', '昨天', null, '昨天', true); $run('v1_response_in_strict_pipeline_also_preserves_abstention', $wide, null, '', null, '', true, true); $run('unrelated_yesterday_sentence_not_assigned', '昨天已经联系完成。七点四十五分收缩压一百二十。', null, '', null, '', true); echo json_encode(['suite' => 'followup-audio-citation-date-safety', 'synthetic_only' => true, 'database_calls' => 0, 'transport_calls' => 0, 'cases' => $rows, 'passed' => count($rows) - count($failed), 'total' => count($rows), 'failures' => $failed], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR) . PHP_EOL; exit($failed === [] ? 0 : 1);