From 440ffb71cee09ec4a24b2e64689bf70aa0365351 Mon Sep 17 00:00:00 2001 From: let5sne Date: Tue, 14 Jul 2026 08:56:36 +0800 Subject: [PATCH] fix(gates): enforce BRD content thresholds --- docs/BRD_IMPLEMENTATION_AUDIT.md | 6 ++++++ scripts/demo-check.sh | 8 ++++++-- scripts/tests/demo-check-pilot-gates.test.sh | 21 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/BRD_IMPLEMENTATION_AUDIT.md b/docs/BRD_IMPLEMENTATION_AUDIT.md index bbfc51ca..7366ec35 100644 --- a/docs/BRD_IMPLEMENTATION_AUDIT.md +++ b/docs/BRD_IMPLEMENTATION_AUDIT.md @@ -46,6 +46,12 @@ | RAG 可用率与知识治理 | 4.7、L1-C、G3 | 搜索、引用、反馈、知识缺口和人工评审入口已有;员工反馈保存同一次搜索的 `reviewId`,SOP 评审记录和搜索响应现在保留答案生成 `promptVersion`;知识库上传、导入、向量重建、评审队列和模型配置管理接口已收紧为 `superadmin/hr_operator`;住宅核心流程覆盖率与可用率仍需正式评审数据,不能宣称完成逐问答全链路追溯 | 后续由业务确认模型/检索批次的正式版本口径,再补检索批次与模型版本;当前先用 `reviewId + promptVersion + query + fragment` 做可追溯统计,不伪造正式可用率 | | 方言语音与多模态训练验收 | 4.3.2、4.3.3、L1-C | ASR/TTS、音频 OSS 留痕和文本降级链路已有;但当前没有四川话/粤语等方言供应商配置、样本集和准确率/响应时延证据,也没有把图像/视频训练输入纳入一期验收口径 | 先确定一期方言厂商和可接受指标,用真实短音频做至少一条方言路径回归;图像/视频输入另按场景与合规边界评审,不用通用文本链路冒充完成 | +### 2026-07-14 增量:BRD 内容数量门槛 + +- `scripts/demo-check.sh` 的严格模式现在额外检查已解析的住宅 SOP 文档数量 `>=5`(`aihr_knowledge_attach.type='sop' AND status=2`)和已入库案例数量 `>=20`(`aihr_case_record.status='已入库'`)。 +- 这两个查询只证明数量,不证明 5 个核心流程覆盖、案例脱敏质量、每周每项目产出或视频学习内容;这些仍需业务内容复核和正式窗口数据。 +- `scripts/tests/demo-check-pilot-gates.test.sh` 已补充跨租户、错误状态和非 SOP 文档样本,防止严格门禁用错误数据刷过。 + ### P2/P3:明确暂缓,不应混入当前修复 - 个人 AI 助理、个人资料库、网页抓取、引用问答、PPT 生成:BRD 4.8,阶段二。 diff --git a/scripts/demo-check.sh b/scripts/demo-check.sh index 15575523..52b0871a 100755 --- a/scripts/demo-check.sh +++ b/scripts/demo-check.sh @@ -79,7 +79,7 @@ check_pilot_samples() { [[ "$(pilot_sql_scalar "SELECT DATE('$pilot_end') IS NOT NULL")" == "1" ]] || fail "invalid AIHR_PILOT_END_DATE: $pilot_end" [[ "$pilot_start" > "$pilot_end" ]] && fail "pilot start date must not be after end date" - local scenario_count completed_people pilot_people org_active org_phone_linked org_pilot_people org_pilot_projects org_completed_people audio_table_count prompt_version_schema_count + local scenario_count completed_people pilot_people org_active org_phone_linked org_pilot_people org_pilot_projects org_completed_people audio_table_count prompt_version_schema_count sop_document_count case_stored_count local calibration_stats calibration_count calibration_matched sop_total sop_usable sop_pending satisfaction_count satisfaction_average local org_identity_cte=" WITH active_org AS ( @@ -124,6 +124,8 @@ check_pilot_samples() { scenario_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_practice_scenario WHERE tenant_id = '000000' AND position = '生活顾问' AND enabled = 1")" audio_table_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'aihr_practice_audio'")" prompt_version_schema_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_sop_answer_review' AND column_name = 'prompt_version'")" + sop_document_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_knowledge_attach WHERE tenant_id = '000000' AND type = 'sop' AND status = 2")" + case_stored_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_case_record WHERE tenant_id = '000000' AND status = '已入库'")" completed_people="$(pilot_sql_scalar " SELECT COUNT(*) FROM ( @@ -265,10 +267,12 @@ check_pilot_samples() { AND s.finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ) SELECT COALESCE(ROUND(AVG(satisfaction_score), 1), 0) FROM formal_sessions WHERE satisfaction_score IS NOT NULL")" - echo "OK: pilot gates window=${pilot_start}..${pilot_end} scenarios=${scenario_count}/12 ten_sessions=${completed_people}/${pilot_people} org_login=${org_phone_linked}/${org_active} org_pilot=${org_pilot_people}/20 projects=${org_pilot_projects}/1-2 org_ten_sessions=${org_completed_people}/${org_pilot_people} calibration=${calibration_count}/20 consistency=${calibration_matched}/${calibration_count} sop_usable=${sop_usable}/${sop_total} sop_pending=${sop_pending} satisfaction=${satisfaction_average}/5 responses=${satisfaction_count} audio_schema=${audio_table_count} prompt_version_schema=${prompt_version_schema_count}" + echo "OK: pilot gates window=${pilot_start}..${pilot_end} scenarios=${scenario_count}/12 sop_docs=${sop_document_count}/5 stored_cases=${case_stored_count}/20 ten_sessions=${completed_people}/${pilot_people} org_login=${org_phone_linked}/${org_active} org_pilot=${org_pilot_people}/20 projects=${org_pilot_projects}/1-2 org_ten_sessions=${org_completed_people}/${org_pilot_people} calibration=${calibration_count}/20 consistency=${calibration_matched}/${calibration_count} sop_usable=${sop_usable}/${sop_total} sop_pending=${sop_pending} satisfaction=${satisfaction_average}/5 responses=${satisfaction_count} audio_schema=${audio_table_count} prompt_version_schema=${prompt_version_schema_count}" [[ "${AIHR_PILOT_STRICT:-false}" != "true" ]] || { (( scenario_count >= 12 )) || fail "pilot enabled scenarios ${scenario_count}/12" + (( sop_document_count >= 5 )) || fail "pilot residential SOP documents ${sop_document_count}/5" + (( case_stored_count >= 20 )) || fail "pilot stored cases ${case_stored_count}/20" (( audio_table_count == 1 )) || fail "pilot audio schema unavailable (aihr_practice_audio missing)" (( prompt_version_schema_count == 1 )) || fail "pilot RAG traceability schema unavailable (aihr_sop_answer_review.prompt_version missing)" (( org_phone_linked >= 20 )) || fail "pilot mobile-login org members ${org_phone_linked}/20 (active org ${org_active})" diff --git a/scripts/tests/demo-check-pilot-gates.test.sh b/scripts/tests/demo-check-pilot-gates.test.sh index d4a6b1ed..55a49bb2 100644 --- a/scripts/tests/demo-check-pilot-gates.test.sh +++ b/scripts/tests/demo-check-pilot-gates.test.sh @@ -56,6 +56,8 @@ docker exec -i wygj-mysql mysql -uroot -proot --default-character-set=utf8mb4 << CREATE DATABASE \`$TEST_DB\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE \`$TEST_DB\`; CREATE TABLE aihr_practice_scenario (tenant_id varchar(20), position varchar(80), enabled tinyint); +CREATE TABLE aihr_knowledge_attach (tenant_id varchar(20), type varchar(50), status tinyint); +CREATE TABLE aihr_case_record (tenant_id varchar(20), status varchar(30)); CREATE TABLE aihr_practice_session (tenant_id varchar(20), session_id varchar(100), ext_party_id varchar(100), mode varchar(30), finished_time datetime, satisfaction_score tinyint); CREATE TABLE aihr_practice_assignment (tenant_id varchar(20), ext_party_id varchar(100), create_time datetime); CREATE TABLE aihr_org_snapshot ( @@ -76,6 +78,24 @@ SELECT '000000','生活顾问',1 FROM ( SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 ) n; +INSERT INTO aihr_knowledge_attach +SELECT '000000','sop',2 FROM ( + SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 +) n; +INSERT INTO aihr_knowledge_attach VALUES + ('000000','training',2), + ('other','sop',2), + ('000000','sop',1); +INSERT INTO aihr_case_record +SELECT '000000','已入库' FROM ( + SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 + UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 + UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 + UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 +) n; +INSERT INTO aihr_case_record VALUES + ('000000','已转写'), + ('other','已入库'); INSERT INTO aihr_org_snapshot VALUES ('000000','active','A','P1','13900000001'), ('000000','active','B','P1','13900000001'), @@ -124,6 +144,7 @@ pilot_output="$(AIHR_PILOT_START_DATE=2026-07-07 AIHR_PILOT_END_DATE=2026-07-10 [[ "$pilot_output" == *'projects=1/1-2'* ]] || { echo "FAIL: safe pilot project count: $pilot_output" >&2; exit 1; } [[ "$pilot_output" == *'org_ten_sessions=1/2'* ]] || { echo "FAIL: formal 9/10 boundary: $pilot_output" >&2; exit 1; } [[ "$pilot_output" == *'calibration=1/20 consistency=1/1'* ]] || { echo "FAIL: calibration window: $pilot_output" >&2; exit 1; } +[[ "$pilot_output" == *'sop_docs=5/5 stored_cases=20/20'* ]] || { echo "FAIL: BRD content quantity gates: $pilot_output" >&2; exit 1; } [[ "$pilot_output" == *'sop_usable=1/1 sop_pending=1'* ]] || { echo "FAIL: SOP window: $pilot_output" >&2; exit 1; } [[ "$pilot_output" == *'satisfaction=5.0/5 responses=19'* ]] || { echo "FAIL: safe satisfaction identity scope: $pilot_output" >&2; exit 1; }