Files
zyt/server/tests/TcmClinicalRiskReferencePromptTest.php
2026-10-27 14:03:35 +08:00

48 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
// Input-only tests deliberately do not load the concurrently edited Guard.
require dirname(__DIR__) . '/app/adminapi/service/TcmClinicalEvidenceIndex.php';
use app\adminapi\service\TcmClinicalEvidenceIndex as Index;
$checks = 0;
function riskPromptExpect(bool $ok, string $why): void { ++$GLOBALS['checks']; if (!$ok) throw new RuntimeException($why); }
$guidance = '第二至六节复核已采集的过敏、既有用药或其他风险阴性基线时,优先只回指真实sN,不再次改述或截短重引阴性';
$example = '例式“既有风险记录见sN;拟用暴露另行核实”,sN须替换为实际匹配编号';
$cases = [
['s5', 'allergy_history', 'current', '2026-10-23', '本次询问无已知药物过敏史', 'negative'],
['s41', 'past_history', 'historical', '2025-04-12', '随访问诊:无明确皮疹或喘息;停药后变化未记录。', 'negative'],
['s72', 'allergy_history', 'current', '', '过敏详情未记录', 'unknown'],
];
foreach ($cases as [$id, $field, $scope, $date, $quote, $state]) {
$ledger = ['sources' => [$id => ['id' => $id, 'field' => $field, 'path' => $scope . '.' . $field,
'date' => $date, 'scope' => $scope, 'quote' => $quote]],
'assertions' => [['source_id' => $id, 'state' => $state, 'quote' => $quote, 'date' => $date, 'scope' => $scope]]];
$original = serialize($ledger);
$index = Index::build($ledger); $indexBefore = serialize($index);
$prompt = Index::prompt($index);
riskPromptExpect(serialize($ledger) === $original && serialize($index) === $indexBefore,
'Building the supplementary prompt cannot rewrite any original source or typed state.');
preg_match('~<SOURCE_EXAM_ROLE_INDEX>\s*(.*?)\s*</SOURCE_EXAM_ROLE_INDEX>~su', $prompt, $match);
$rows = json_decode($match[1], true, 64, JSON_THROW_ON_ERROR);
riskPromptExpect(count($rows) === 1 && $rows[0]['id'] === $id && $rows[0]['date'] === $date
&& $rows[0]['scope'] === $scope && $rows[0]['field'] === $field
&& $rows[0]['path'] === $ledger['sources'][$id]['path'] && $rows[0]['spans'] === $index['sources'][$id]['spans'],
'Each full date/role/source payload remains lossless; historical and absent dates are not relabelled.');
riskPromptExpect($rows[0]['spans'][0]['quote'] === $quote
&& $index['sources'][$id]['record_state'] === ($state === 'unknown' ? 'unknown' : 'recorded'),
'Temporal/collection/known/degree qualifiers remain exact and an unknown never becomes a denial.');
$controls = substr($prompt, 0, strpos($prompt, '<SOURCE_EXAM_ROLE_INDEX>'));
riskPromptExpect(str_contains($controls, $guidance) && str_contains($controls, $example)
&& !str_contains($controls, '风险记录见' . $id),
'The same source-only risk-reference grammar is generic rather than a fixed case ID or answer prefill.');
riskPromptExpect(str_contains($controls, '完整句或分号分隔的完整片段')
&& str_contains($controls, '保留时间、询问或记录语境、已知及程度等限定词,不能只摘否认核心')
&& str_contains($controls, '不能据此认定本次用药已安全'),
'Source-ID reuse does not weaken strict canonical quotation or turn a prior risk record into medication approval.');
riskPromptExpect(str_contains($controls, '候选方也须有关键方证依据') && str_contains($controls, '关键条件或优先理由不足则暂不列方名、药味')
&& str_contains($controls, '用药待核事项每项独立写成中性问句') && str_contains($controls, '不证明证名或方药成立'),
'Critical formula support, safe prospective inquiry and the index medical-validation limit remain unchanged.');
}
echo "TCM risk-reference input prompt: {$checks} assertions passed (pure Index input; shared integration deferred).\n";