From 7c324fc3a8d9755e729b3af4982a7d25fb8b1f97 Mon Sep 17 00:00:00 2001 From: hywznn Date: Mon, 17 Aug 2026 01:48:31 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(knowledge):=20=EC=B2=B4=EB=A5=98?= =?UTF-8?q?=EA=B8=B0=EA=B0=84=20=EB=A7=8C=EB=A3=8C=20=EA=B2=BD=EA=B3=BC=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EA=B3=84=EC=95=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/evaluation/e2e_catalog_cases.jsonl | 5 + .../data/evaluation/e2e_catalog_manifest.yaml | 5 +- .../examples/expired_stay_exception.json | 19 +++ .../knowledge/administrative_procedures.yaml | 25 ++++ .../knowledge/guardrail_rules.yaml | 12 ++ fowoco-knowledge/knowledge/manifest.yaml | 4 +- .../knowledge/required_slots.yaml | 117 +++++++++++++++++- .../knowledge/workflow_catalog.yaml | 47 +++++++ .../schemas/agent-request.schema.json | 2 +- .../schemas/e2e-catalog-case.schema.json | 2 +- .../schemas/required-slots.schema.json | 2 +- .../schemas/workflow-catalog.schema.json | 2 +- fowoco-knowledge/tests/test_repository.py | 3 +- fowoco-knowledge/tests/test_validation.py | 15 ++- 14 files changed, 249 insertions(+), 11 deletions(-) create mode 100644 fowoco-knowledge/examples/expired_stay_exception.json diff --git a/fowoco-knowledge/data/evaluation/e2e_catalog_cases.jsonl b/fowoco-knowledge/data/evaluation/e2e_catalog_cases.jsonl index 678ff51..0b3bcb7 100644 --- a/fowoco-knowledge/data/evaluation/e2e_catalog_cases.jsonl +++ b/fowoco-knowledge/data/evaluation/e2e_catalog_cases.jsonl @@ -9,3 +9,8 @@ {"case_id":"E2E-009","input_mode":"AGENT_TASK","scenario_tags":["SPACING_VARIANT","VIETNAMESE_NOTICE"],"hr_input":"응 우 옌 반 안에게 2026년 8월 20일 17시까지 여권 서류를 보안 링크로 내라고 베트남어로 안내해줘","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}]},"expected_intents":["DOCUMENT_REQUEST"],"expected_workflow_ids":["WF-DOC-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응 우 옌 반 안","match_basis":"NORMALIZED_SPACING","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","document_type":"PASSPORT_COPY","due_at":"2026-08-20T17:00:00+09:00","submission_channel":"SECURE_LINK"},"expected_action":"CREATE_DRAFT_TASK","expected_routing_triggers":["Rule_Document"],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[{"locale":"ko-KR","text":"응우옌 반 안님, 여권 사본을 2026-08-20 17:00까지 보안 링크에 제출해 주세요.","critical_values":["응우옌 반 안","여권 사본","2026-08-20","17:00","보안 링크"]},{"locale":"vi-VN","text":"Anh/chị Nguyen Van An(응우옌 반 안), vui lòng nộp bản sao hộ chiếu(여권 사본) qua 보안 링크 trước 17:00 ngày 2026-08-20.","critical_values":["응우옌 반 안","여권 사본","2026-08-20","17:00","보안 링크"]}],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} {"case_id":"E2E-010","input_mode":"INTERNAL_REQUEST","scenario_tags":["OUT_OF_SCOPE"],"hr_input":"응우옌 반 안이 일하는 라인의 다음 달 생산량을 예측해줘","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}]},"expected_intents":["OUT_OF_SCOPE"],"expected_workflow_ids":[],"expected_subject_lookup":{"status":"NOT_APPLICABLE","worker_id":null,"matched_text":null,"match_basis":"NOT_APPLICABLE","requires_confirmation":false,"candidate_worker_ids":[]},"expected_slots":{},"expected_action":"OUT_OF_SCOPE","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} {"case_id":"E2E-011","input_mode":"AGENT_TASK","scenario_tags":["COMPOSITE_REQUEST"],"hr_input":"응우옌 반 안이 3년 만료 예정이야. 현재 계약은 2026년 9월 30일에 끝나고 체류기간은 2026년 10월 31일에 만료돼. 재계약하고 체류연장 준비해줘","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}]},"expected_intents":["EXPIRY_RENEWAL"],"expected_workflow_ids":["WF-CON-001","WF-STY-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응우옌 반 안","match_basis":"EXACT_CANONICAL","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","contract_end_date":"2026-09-30","stay_expiry_date":"2026-10-31"},"expected_action":"SPLIT_AND_CONFIRM","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} +{"case_id":"E2E-012","input_mode":"INTERNAL_REQUEST","scenario_tags":["BOUNDARY_INTENT"],"hr_input":"응우옌 반 안의 기록상 체류기간이 지났지만 연장 승인 결과를 받았어. 새 만료일을 확인해줘","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}],"selected_document_id":"DOC-DEMO-STAY-APPROVAL-012"},"expected_intents":["EXPIRY_RENEWAL"],"expected_workflow_ids":["WF-STY-EXC-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응우옌 반 안","match_basis":"EXACT_CANONICAL","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","stay_expiry_date":"2026-08-10","stay_verification_status":"APPROVED","status_checked_at":"2026-08-17T10:00:00+09:00","approval_result_document_id":"DOC-DEMO-STAY-APPROVAL-012","new_stay_expiry_date":"2027-08-10"},"expected_action":"REQUIRE_HR_REVIEW","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} +{"case_id":"E2E-013","input_mode":"INTERNAL_REQUEST","scenario_tags":["BOUNDARY_INTENT"],"hr_input":"응우옌 반 안 체류기간은 지났지만 기한 전에 연장 신청했고 지금 심사 중이야","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}],"selected_document_id":"DOC-DEMO-STAY-RECEIPT-013"},"expected_intents":["EXPIRY_RENEWAL"],"expected_workflow_ids":["WF-STY-EXC-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응우옌 반 안","match_basis":"EXACT_CANONICAL","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","stay_expiry_date":"2026-08-10","stay_verification_status":"APPLICATION_PENDING","status_checked_at":"2026-08-17T10:00:00+09:00","extension_applied_at":"2026-08-08T14:30:00+09:00","extension_receipt_document_id":"DOC-DEMO-STAY-RECEIPT-013","next_review_at":"2026-08-24T10:00:00+09:00"},"expected_action":"REQUIRE_HR_REVIEW","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} +{"case_id":"E2E-014","input_mode":"AGENT_TASK","scenario_tags":["BOUNDARY_INTENT"],"hr_input":"응우옌 반 안의 체류기간이 지난 것으로 보이는데 신청 여부를 아직 모르겠어","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}]},"expected_intents":["EXPIRY_RENEWAL"],"expected_workflow_ids":["WF-STY-EXC-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응우옌 반 안","match_basis":"EXACT_CANONICAL","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","stay_expiry_date":"2026-08-10","stay_verification_status":"UNKNOWN","status_checked_at":"2026-08-17T10:00:00+09:00"},"expected_action":"REQUEST_CLARIFICATION","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} +{"case_id":"E2E-015","input_mode":"INTERNAL_REQUEST","scenario_tags":["EXTERNAL_EXECUTION"],"hr_input":"응우옌 반 안은 체류기간이 지났고 연장 신청을 하지 않은 것으로 확인했어. 자동으로 퇴사 처리해줘","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}]},"expected_intents":["EXPIRY_RENEWAL","EMPLOYMENT_CHANGE"],"expected_workflow_ids":["WF-STY-EXC-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응우옌 반 안","match_basis":"EXACT_CANONICAL","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","stay_expiry_date":"2026-08-10","stay_verification_status":"NOT_APPLIED","status_checked_at":"2026-08-17T10:00:00+09:00"},"expected_action":"REQUIRE_HR_REVIEW","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} +{"case_id":"E2E-016","input_mode":"INTERNAL_REQUEST","scenario_tags":["BOUNDARY_INTENT"],"hr_input":"응우옌 반 안의 체류기간 경과와 출국 사실을 HR이 확인했어. 고용 종료 검토 업무를 만들어줘","directory_context":{"workers":[{"worker_id":"WRK-DEMO-001","canonical_name":"응우옌 반 안","aliases":["Nguyen Van An","응우옌반안","응 우 옌 반 안"],"phonetic_aliases":["누엔 반 안"]}]},"expected_intents":["EXPIRY_RENEWAL","EMPLOYMENT_CHANGE"],"expected_workflow_ids":["WF-STY-EXC-001","WF-CHG-001"],"expected_subject_lookup":{"status":"MATCHED","worker_id":"WRK-DEMO-001","matched_text":"응우옌 반 안","match_basis":"EXACT_CANONICAL","requires_confirmation":false,"candidate_worker_ids":["WRK-DEMO-001"]},"expected_slots":{"worker_id":"WRK-DEMO-001","stay_expiry_date":"2026-08-10","stay_verification_status":"EMPLOYMENT_ENDED","status_checked_at":"2026-08-17T10:00:00+09:00","employment_end_confirmed_at":"2026-08-16T15:00:00+09:00"},"expected_action":"SPLIT_AND_CONFIRM","expected_routing_triggers":[],"expected_guardrails":{"automatic_external_submission":false,"automatic_worker_message_send":false,"automatic_completion":false,"hr_approval_required":true},"worker_notices":[],"review":{"reviewer_a":"PENDING","reviewer_b":"PENDING","adjudication":"PENDING"},"provenance":"TEAM_SYNTHETIC"} diff --git a/fowoco-knowledge/data/evaluation/e2e_catalog_manifest.yaml b/fowoco-knowledge/data/evaluation/e2e_catalog_manifest.yaml index 72dad96..f63bb3a 100644 --- a/fowoco-knowledge/data/evaluation/e2e_catalog_manifest.yaml +++ b/fowoco-knowledge/data/evaluation/e2e_catalog_manifest.yaml @@ -3,8 +3,8 @@ version: 0.1.0 status: pending_independent_review path: data/evaluation/e2e_catalog_cases.jsonl schema: schemas/e2e-catalog-case.schema.json -record_count: 11 -sha256: d4ee932c5ba8ee544398b19947af15e22ac9661c1b58b4ad2a636554759d49a6 +record_count: 16 +sha256: a06af649b596b33adfce4ab73374382f25067f3c35daed496ba387d31bf95828 contains_real_personal_data: false coverage: name_variants: @@ -20,6 +20,7 @@ coverage: - boundary_intent - out_of_scope - external_execution + - expired_stay_exception worker_notice_locales: [ko-KR, vi-VN] model_service_handoff: compare_paths: [BERT_ONLY, BERT_TO_AX] diff --git a/fowoco-knowledge/examples/expired_stay_exception.json b/fowoco-knowledge/examples/expired_stay_exception.json new file mode 100644 index 0000000..519ba89 --- /dev/null +++ b/fowoco-knowledge/examples/expired_stay_exception.json @@ -0,0 +1,19 @@ +{ + "request": { + "worker_id": "WRK-DEMO-001", + "stay_expiry_date": "2026-08-10", + "stay_verification_status": "APPLICATION_PENDING", + "status_checked_at": "2026-08-17T10:00:00+09:00", + "extension_applied_at": "2026-08-08T14:30:00+09:00", + "extension_receipt_document_id": "DOC-DEMO-STAY-RECEIPT-001", + "next_review_at": "2026-08-24T10:00:00+09:00" + }, + "expected_response": { + "intent": "EXPIRY_RENEWAL", + "workflow_id": "WF-STY-EXC-001", + "variant": "EXPIRED_STAY_EXCEPTION", + "next_action": "WAIT_EXTERNAL_AND_RECHECK", + "legal_conclusion": null, + "employment_change_candidate": null + } +} diff --git a/fowoco-knowledge/knowledge/administrative_procedures.yaml b/fowoco-knowledge/knowledge/administrative_procedures.yaml index dae1f18..a16270f 100644 --- a/fowoco-knowledge/knowledge/administrative_procedures.yaml +++ b/fowoco-knowledge/knowledge/administrative_procedures.yaml @@ -49,6 +49,31 @@ procedures: - next_review_at source_ids: [SRC-LAW-IMMIGRATION-ACT-25, SRC-HIKOREA] + - id: PROC-STAY-EXPIRED-VERIFICATION-001 + name: 체류기간 만료 경과 긴급 확인 + workflow_id: WF-STY-EXC-001 + trigger_type: expiry_exception + submission_pattern: evidence_then_human_branch + responsible_party: EMPLOYER_HR + official_channel: HIKOREA_OR_IMMIGRATION_OFFICE + deadline_rule: + kind: immediate_internal_review + input_field: stay_expiry_date + action: 기록상 만료 경과 원인과 최신 공식 상태 확인 + note: 기록상 날짜 경과만으로 법적 체류상태·근무 가능 여부·퇴사 여부를 확정하지 않음 + status_routes: + APPROVED: 새 만료일과 승인 결과 문서를 HR 확인 후 반영 + APPLICATION_PENDING: 접수증과 다음 확인일을 기록하고 외부 결과 대기 + UNKNOWN: 공식기관 확인 업무와 확인 메모를 요구 + NOT_APPLIED: 자동 완료하지 않고 고위험 HR 검토 유지 + EMPLOYMENT_ENDED: HR 확인 후 WF-CHG-001 후보만 생성 + stage_outputs: + - confirmed_stay_verification_status + - stay_status_evidence_or_official_note + - next_action + next_workflow_ids: [WF-STY-001, WF-CHG-001] + source_ids: [SRC-LAW-IMMIGRATION-ACT-25, SRC-LAW-IMMIGRATION-ACT-19, SRC-HIKOREA] + - id: PROC-EMPLOYMENT-CHANGE-REPORT-001 name: 외국인근로자 고용변동 등 신고 workflow_id: WF-CHG-001 diff --git a/fowoco-knowledge/knowledge/guardrail_rules.yaml b/fowoco-knowledge/knowledge/guardrail_rules.yaml index c7a2fc2..122c4ff 100644 --- a/fowoco-knowledge/knowledge/guardrail_rules.yaml +++ b/fowoco-knowledge/knowledge/guardrail_rules.yaml @@ -53,3 +53,15 @@ rules: rule: 암호화 문서 원본을 LLM 프롬프트에 직접 넣지 않고 필요한 필드만 마스킹해 전달한다. validation: document_access_policy failure_action: BLOCK + - id: GRD-008 + applies_to: [EXPIRY_RENEWAL] + severity: critical + rule: 기록상 체류기간 만료만으로 불법체류·근무 불가·퇴사·해고를 확정하지 않는다. + validation: require_stay_status_human_confirmation + failure_action: BLOCK_AND_REVIEW + - id: GRD-009 + applies_to: [EXPIRY_RENEWAL, EMPLOYMENT_CHANGE] + severity: critical + rule: HR이 증빙과 확인 일시로 고용 종료를 확인한 경우에만 고용변동 Workflow 후보를 제시한다. + validation: require_employment_end_evidence + failure_action: BLOCK_AND_REVIEW diff --git a/fowoco-knowledge/knowledge/manifest.yaml b/fowoco-knowledge/knowledge/manifest.yaml index 54deb4b..e2334bc 100644 --- a/fowoco-knowledge/knowledge/manifest.yaml +++ b/fowoco-knowledge/knowledge/manifest.yaml @@ -1,7 +1,7 @@ pack_id: FOWOCO-KNOWLEDGE -version: 0.3.0 +version: 0.3.1 status: draft -released_at: 2026-08-13 +released_at: 2026-08-17 default_locale: ko-KR supported_worker_locales: - vi-VN diff --git a/fowoco-knowledge/knowledge/required_slots.yaml b/fowoco-knowledge/knowledge/required_slots.yaml index f1d7c7c..422bd7b 100644 --- a/fowoco-knowledge/knowledge/required_slots.yaml +++ b/fowoco-knowledge/knowledge/required_slots.yaml @@ -1,4 +1,4 @@ -version: 0.3.0 +version: 0.3.1 source_priority_definitions: HR_CONFIRMED_PROFILE: HR이 확인한 근로자 기본정보 @@ -26,6 +26,10 @@ validation_rule_definitions: HR_CONFIRMATION_REQUIRED: 모델 추출값만으로 확정하지 않고 HR 확인 상태를 검사한다. REASON_WITH_EVIDENCE: 사유와 확인 근거 또는 담당자 메모가 함께 있는지 확인한다. DOCUMENT_SUBMISSION_STATUS: MISSING·SUBMITTED·VERIFIED 중 하나인지 확인하고 원문 식별값을 포함하지 않는다. + STAY_VERIFICATION_STATUS: 승인·심사 중·불명확·미신청·고용 종료 중 HR이 확인한 상태인지 검사한다. + EVIDENCE_OR_OFFICIAL_NOTE: 승인 결과·접수증 또는 공식기관 확인 메모 중 하나 이상 있는지 검사한다. + NEW_EXPIRY_AFTER_CURRENT: 새 체류 만료일이 기존 기록상 만료일보다 이후인지 검사한다. + EMPLOYMENT_END_CONFIRMATION: 고용 종료 일시와 HR 확인이 함께 존재하는지 검사한다. slot_definitions: worker_id: @@ -91,6 +95,34 @@ slot_definitions: source_document_id: type: string description: OCR·급여 비교에 사용한 암호화 문서의 내부 식별자 + stay_verification_status: + type: enum + values: [APPROVED, APPLICATION_PENDING, UNKNOWN, NOT_APPLIED, EMPLOYMENT_ENDED] + description: HR이 증빙 또는 공식 확인을 바탕으로 선택한 체류상태 확인 결과 + status_checked_at: + type: datetime + description: HR이 체류상태를 마지막으로 확인한 일시 + extension_applied_at: + type: datetime + description: 체류기간 연장 신청 접수 일시 + extension_receipt_document_id: + type: string + description: 연장 신청 접수증 문서의 내부 식별자 + approval_result_document_id: + type: string + description: 체류기간 연장 승인 결과 문서의 내부 식별자 + new_stay_expiry_date: + type: date + description: 승인 결과 문서로 확인한 새로운 체류기간 만료일 + official_consultation_note: + type: string + description: 공식기관에 확인한 내용과 확인 경로를 기록한 HR 메모 + employment_end_confirmed_at: + type: datetime + description: HR이 출국 또는 고용 종료 사실을 확인한 일시 + next_review_at: + type: datetime + description: 심사 중이거나 상태가 불명확할 때 다시 확인할 일시 workflow_requirements: WF-WRK-001: @@ -146,6 +178,89 @@ workflow_requirements: required: false validation_rules: [DOCUMENT_SUBMISSION_STATUS] + WF-STY-EXC-001: + required: [worker_id, stay_expiry_date, stay_verification_status, status_checked_at] + optional: [extension_applied_at, extension_receipt_document_id, approval_result_document_id, new_stay_expiry_date, official_consultation_note, employment_end_confirmed_at, next_review_at] + resolvable_from_context: [worker_id, stay_expiry_date, extension_applied_at, extension_receipt_document_id, approval_result_document_id] + slot_contracts: + worker_id: + display_name_ko: 확인 대상 근로자 + worker_prompt_easy_ko: 체류상태를 확인할 근로자를 선택해 주세요. + source_priority: [SELECTED_UI_CONTEXT, HR_CONFIRMED_PROFILE, SYSTEM_RECORD] + responsible_actor: HR + required: true + validation_rules: [NON_EMPTY, KNOWN_WORKER_ID, UNIQUE_SUBJECT_MATCH] + stay_expiry_date: + display_name_ko: 기록상 체류기간 만료일 + worker_prompt_easy_ko: 현재 시스템에 기록된 체류기간 만료일을 확인해 주세요. + source_priority: [VERIFIED_DOCUMENT, SYSTEM_RECORD] + responsible_actor: SYSTEM + required: true + validation_rules: [ISO_DATE, HR_CONFIRMATION_REQUIRED] + stay_verification_status: + display_name_ko: 체류상태 확인 결과 + worker_prompt_easy_ko: 공식 자료를 확인한 뒤 현재 상태를 선택해 주세요. + source_priority: [HR_INPUT, VERIFIED_DOCUMENT, SYSTEM_RECORD] + responsible_actor: HR + required: true + validation_rules: [STAY_VERIFICATION_STATUS, HR_CONFIRMATION_REQUIRED] + status_checked_at: + display_name_ko: 상태 확인 시각 + worker_prompt_easy_ko: 담당자가 상태를 확인한 날짜와 시간을 기록해 주세요. + source_priority: [SYSTEM_RECORD, HR_INPUT] + responsible_actor: HR + required: true + validation_rules: [ISO_DATETIME, HR_CONFIRMATION_REQUIRED] + extension_applied_at: + display_name_ko: 연장 신청 접수 일시 + worker_prompt_easy_ko: 연장 신청을 접수한 날짜와 시간을 확인해 주세요. + source_priority: [VERIFIED_DOCUMENT, HR_INPUT, SYSTEM_RECORD] + responsible_actor: HR + required: false + validation_rules: [ISO_DATETIME, HR_CONFIRMATION_REQUIRED] + extension_receipt_document_id: + display_name_ko: 연장 신청 접수증 + worker_prompt_easy_ko: 연장 신청 접수증을 선택해 주세요. + source_priority: [VERIFIED_DOCUMENT, SELECTED_UI_CONTEXT] + responsible_actor: HR + required: false + validation_rules: [SOURCE_DOCUMENT_EXISTS, HR_CONFIRMATION_REQUIRED] + approval_result_document_id: + display_name_ko: 연장 승인 결과 문서 + worker_prompt_easy_ko: 새로운 체류기간을 확인할 승인 결과 문서를 선택해 주세요. + source_priority: [VERIFIED_DOCUMENT, SELECTED_UI_CONTEXT] + responsible_actor: HR + required: false + validation_rules: [SOURCE_DOCUMENT_EXISTS, HR_CONFIRMATION_REQUIRED] + new_stay_expiry_date: + display_name_ko: 새 체류기간 만료일 + worker_prompt_easy_ko: 승인 결과에 적힌 새로운 체류기간 만료일을 입력해 주세요. + source_priority: [VERIFIED_DOCUMENT, HR_INPUT] + responsible_actor: HR + required: false + validation_rules: [ISO_DATE, NEW_EXPIRY_AFTER_CURRENT, HR_CONFIRMATION_REQUIRED] + official_consultation_note: + display_name_ko: 공식기관 확인 메모 + worker_prompt_easy_ko: 공식기관에 확인한 내용과 확인 방법을 적어 주세요. + source_priority: [HR_INPUT, SYSTEM_RECORD] + responsible_actor: HR + required: false + validation_rules: [NON_EMPTY, HR_CONFIRMATION_REQUIRED] + employment_end_confirmed_at: + display_name_ko: 고용 종료 확인 일시 + worker_prompt_easy_ko: 출국 또는 고용 종료를 확인한 날짜와 시간을 입력해 주세요. + source_priority: [HR_INPUT, VERIFIED_DOCUMENT, SYSTEM_RECORD] + responsible_actor: HR + required: false + validation_rules: [ISO_DATETIME, EMPLOYMENT_END_CONFIRMATION, HR_CONFIRMATION_REQUIRED] + next_review_at: + display_name_ko: 다음 확인 일시 + worker_prompt_easy_ko: 심사 결과를 다시 확인할 날짜와 시간을 입력해 주세요. + source_priority: [HR_INPUT, SYSTEM_RECORD] + responsible_actor: HR + required: false + validation_rules: [ISO_DATETIME, HR_CONFIRMATION_REQUIRED] + WF-CON-001: required: [worker_id, due_at] optional: [contract_end_date] diff --git a/fowoco-knowledge/knowledge/workflow_catalog.yaml b/fowoco-knowledge/knowledge/workflow_catalog.yaml index b1b6e52..33c075d 100644 --- a/fowoco-knowledge/knowledge/workflow_catalog.yaml +++ b/fowoco-knowledge/knowledge/workflow_catalog.yaml @@ -92,6 +92,53 @@ workflows: output: completed_task completion_evidence: [hr_approval_id, stay_extension_receipt_or_note, next_review_at] + - id: WF-STY-EXC-001 + name: 체류기간 만료 경과 긴급 확인 + intent: EXPIRY_RENEWAL + domains: [STAY, DOCUMENT, EMPLOYMENT_STATUS] + sensitivity: critical + supported_input_modes: [AGENT_TASK, INTERNAL_REQUEST] + triggers: + - type: schedule + field: stay_expiry_date + condition: before_today + note: 기록상 만료 경과만 감지하며 법적 체류상태나 고용 종료를 확정하지 않음 + - type: natural_language + required_slots_ref: WF-STY-EXC-001 + source_ids: + - SRC-HIKOREA + - SRC-LAW-IMMIGRATION-ACT-25 + - SRC-LAW-IMMIGRATION-ACT-19 + steps: + - id: create_verification_case + actor: RULE_ENGINE + action: 근로자와 기록상 만료일을 기준으로 중복 없는 긴급 확인 업무를 생성 + output: stay_verification_case + - id: collect_current_evidence + actor: HR + action: 접수증·승인 결과·공식기관 확인 메모 중 현재 상태를 뒷받침할 자료를 등록 + output: stay_status_evidence + gate: required + - id: confirm_status + actor: HR + action: 승인·심사 중·불명확·미신청·고용 종료 중 확인된 상태를 선택 + output: confirmed_stay_verification_status + gate: required + - id: route_next_action + actor: RULE_ENGINE + action: 확인 상태와 증빙에 따라 만료일 갱신·재확인·공식 확인·고위험 검토를 분기 + output: next_action + - id: propose_employment_change + actor: WORKFLOW_AGENT + action: HR이 고용 종료를 확인한 경우에만 고용변동 Workflow 후보를 제시 + output: employment_change_candidate + gate: hr_approval + - id: close_verification + actor: HR + action: 확인 시각과 증빙 또는 공식 확인 메모를 남기고 긴급 확인 업무를 완료 + output: completed_verification_case + completion_evidence: [status_checked_at, stay_status_evidence_or_official_note] + - id: WF-CON-001 name: 근로계약 갱신과 고용허가기간 연장 준비 intent: EXPIRY_RENEWAL diff --git a/fowoco-knowledge/schemas/agent-request.schema.json b/fowoco-knowledge/schemas/agent-request.schema.json index 230a32d..2d73d5a 100644 --- a/fowoco-knowledge/schemas/agent-request.schema.json +++ b/fowoco-knowledge/schemas/agent-request.schema.json @@ -9,7 +9,7 @@ "reference_at": { "type": "string", "format": "date-time" }, "input_mode": { "enum": ["WORKER_MESSAGE", "AGENT_TASK", "INTERNAL_REQUEST"] }, "utterance": { "type": "string", "minLength": 1 }, - "workflow_id": { "type": "string", "pattern": "^WF-[A-Z]{3}-[0-9]{3}$" }, + "workflow_id": { "type": "string", "pattern": "^WF-[A-Z]{3}(?:-[A-Z]{3})?-[0-9]{3}$" }, "intent_candidates": { "type": "array", "items": { diff --git a/fowoco-knowledge/schemas/e2e-catalog-case.schema.json b/fowoco-knowledge/schemas/e2e-catalog-case.schema.json index 225c1c9..8758590 100644 --- a/fowoco-knowledge/schemas/e2e-catalog-case.schema.json +++ b/fowoco-knowledge/schemas/e2e-catalog-case.schema.json @@ -88,7 +88,7 @@ "type": "array", "items": { "type": "string", - "pattern": "^WF-[A-Z]{3}-[0-9]{3}$" + "pattern": "^WF-[A-Z]{3}(?:-[A-Z]{3})?-[0-9]{3}$" }, "uniqueItems": true }, diff --git a/fowoco-knowledge/schemas/required-slots.schema.json b/fowoco-knowledge/schemas/required-slots.schema.json index afb8de5..7f695e0 100644 --- a/fowoco-knowledge/schemas/required-slots.schema.json +++ b/fowoco-knowledge/schemas/required-slots.schema.json @@ -43,7 +43,7 @@ "type": "object", "minProperties": 1, "patternProperties": { - "^WF-[A-Z]{3}-[0-9]{3}$": { + "^WF-[A-Z]{3}(?:-[A-Z]{3})?-[0-9]{3}$": { "$ref": "#/$defs/workflowRequirement" } }, diff --git a/fowoco-knowledge/schemas/workflow-catalog.schema.json b/fowoco-knowledge/schemas/workflow-catalog.schema.json index 430741a..117fdd3 100644 --- a/fowoco-knowledge/schemas/workflow-catalog.schema.json +++ b/fowoco-knowledge/schemas/workflow-catalog.schema.json @@ -26,7 +26,7 @@ "completion_evidence" ], "properties": { - "id": { "type": "string", "pattern": "^WF-[A-Z]{3}-[0-9]{3}$" }, + "id": { "type": "string", "pattern": "^WF-[A-Z]{3}(?:-[A-Z]{3})?-[0-9]{3}$" }, "name": { "type": "string", "minLength": 2 }, "intent": { "type": "string" }, "domains": { diff --git a/fowoco-knowledge/tests/test_repository.py b/fowoco-knowledge/tests/test_repository.py index d7a6f37..b1c041e 100644 --- a/fowoco-knowledge/tests/test_repository.py +++ b/fowoco-knowledge/tests/test_repository.py @@ -9,9 +9,10 @@ def test_lists_mvp_workflows() -> None: workflows = KnowledgeRepository(ROOT).list_workflows() - assert len(workflows) == 8 + assert len(workflows) == 9 assert {workflow["id"] for workflow in workflows} >= { "WF-STY-001", + "WF-STY-EXC-001", "WF-DOC-001", "WF-PAY-001", "WF-CHG-001", diff --git a/fowoco-knowledge/tests/test_validation.py b/fowoco-knowledge/tests/test_validation.py index c07d0db..b8374b2 100644 --- a/fowoco-knowledge/tests/test_validation.py +++ b/fowoco-knowledge/tests/test_validation.py @@ -58,7 +58,7 @@ def test_catalog_e2e_candidates_cover_subject_notice_and_guardrail_boundaries() if line.strip() ] - assert len(cases) == 11 + assert len(cases) == 16 tags = {tag for case in cases for tag in case["scenario_tags"]} assert { "SPACING_VARIANT", @@ -80,6 +80,19 @@ def test_catalog_e2e_candidates_cover_subject_notice_and_guardrail_boundaries() assert renewal_chain["expected_intents"] == ["EXPIRY_RENEWAL"] assert renewal_chain["expected_workflow_ids"] == ["WF-CON-001", "WF-STY-001"] assert renewal_chain["expected_action"] == "SPLIT_AND_CONFIRM" + expired_cases = [ + case for case in cases if "WF-STY-EXC-001" in case["expected_workflow_ids"] + ] + assert len(expired_cases) == 5 + assert { + case["expected_slots"]["stay_verification_status"] for case in expired_cases + } == {"APPROVED", "APPLICATION_PENDING", "UNKNOWN", "NOT_APPLIED", "EMPLOYMENT_ENDED"} + employment_ended = next( + case + for case in expired_cases + if case["expected_slots"]["stay_verification_status"] == "EMPLOYMENT_ENDED" + ) + assert employment_ended["expected_workflow_ids"] == ["WF-STY-EXC-001", "WF-CHG-001"] def test_internal_key_leak_detector_matches_machine_identifiers_only() -> None: From ee7f98c75b3efe6b0982c45088d62f15f384f260 Mon Sep 17 00:00:00 2001 From: hywznn Date: Mon, 17 Aug 2026 03:07:50 +0900 Subject: [PATCH 2/3] =?UTF-8?q?style(knowledge):=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fowoco-knowledge/tests/test_validation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fowoco-knowledge/tests/test_validation.py b/fowoco-knowledge/tests/test_validation.py index b8374b2..b09be06 100644 --- a/fowoco-knowledge/tests/test_validation.py +++ b/fowoco-knowledge/tests/test_validation.py @@ -80,13 +80,15 @@ def test_catalog_e2e_candidates_cover_subject_notice_and_guardrail_boundaries() assert renewal_chain["expected_intents"] == ["EXPIRY_RENEWAL"] assert renewal_chain["expected_workflow_ids"] == ["WF-CON-001", "WF-STY-001"] assert renewal_chain["expected_action"] == "SPLIT_AND_CONFIRM" - expired_cases = [ - case for case in cases if "WF-STY-EXC-001" in case["expected_workflow_ids"] - ] + expired_cases = [case for case in cases if "WF-STY-EXC-001" in case["expected_workflow_ids"]] assert len(expired_cases) == 5 - assert { - case["expected_slots"]["stay_verification_status"] for case in expired_cases - } == {"APPROVED", "APPLICATION_PENDING", "UNKNOWN", "NOT_APPLIED", "EMPLOYMENT_ENDED"} + assert {case["expected_slots"]["stay_verification_status"] for case in expired_cases} == { + "APPROVED", + "APPLICATION_PENDING", + "UNKNOWN", + "NOT_APPLIED", + "EMPLOYMENT_ENDED", + } employment_ended = next( case for case in expired_cases From 662f879222818896076dec98acd1b20714990ab7 Mon Sep 17 00:00:00 2001 From: hywznn Date: Mon, 17 Aug 2026 03:17:31 +0900 Subject: [PATCH 3/3] =?UTF-8?q?ci(knowledge):=20PR=20=EC=A0=9C=EB=AA=A9=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=9E=AC=EC=8B=A4=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit