Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ make check
기준 위치는 [FOWOCO Hugging Face](https://huggingface.co/fowoco)이며, 운영 서빙 코드의
장기 소유권은 `fowoco/ai`에 둡니다.

모델 파일의 SHA·학습 데이터 계보·비공개 HF 배포 기준은
[`MODEL_ARTIFACT_POLICY.md`](fowoco-knowledge/docs/MODEL_ARTIFACT_POLICY.md)와
[`artifact-manifest.yaml`](fowoco-knowledge/hr-intent-service/models/artifact-manifest.yaml)에
고정했습니다. 운영 배포는 HF `main`이 아니라 인증 후 확인한 commit SHA를 사용합니다.

## 안전 원칙과 현재 한계

- 모델 출력만으로 법률·체류·계약·급여·신고 결론을 확정하지 않습니다.
Expand All @@ -184,3 +189,4 @@ make check
- [공식 데이터 파이프라인](fowoco-knowledge/docs/OFFICIAL_DATA_PIPELINE.md)
- [E-9 신고·연장 Workflow](fowoco-knowledge/docs/E9_REPORTING_WORKFLOWS.md)
- [모델 서빙 실행 방법](fowoco-knowledge/hr-intent-service/README.md)
- [모델 산출물 보관·배포 정책](fowoco-knowledge/docs/MODEL_ARTIFACT_POLICY.md)
2 changes: 2 additions & 0 deletions fowoco-knowledge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Slot·공식 출처·Guardrail을 조회하고, 데이터 계약과 교차참조

모델 가중치의 배포 기준 위치는 [FOWOCO Hugging Face](https://huggingface.co/fowoco)입니다.
`hr-intent-service/`는 프로젝트 결과 재현과 AI 저장소 인계를 위한 스냅샷입니다.
파일 SHA·학습 데이터 계보·Secret 관리 기준은
[`docs/MODEL_ARTIFACT_POLICY.md`](docs/MODEL_ARTIFACT_POLICY.md)를 따릅니다.

## 데이터 기준

Expand Down
63 changes: 63 additions & 0 deletions fowoco-knowledge/docs/MODEL_ARTIFACT_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Intent 모델 산출물 보관·배포 정책

## 한 줄 기준

학습 데이터와 검증 기준은 `fowoco/knowledge`, 배포 가중치는 비공개 Hugging Face,
운영 추론 코드는 `fowoco/ai`가 소유한다. 배포는 모델 이름의 `latest`가 아니라
Hugging Face commit SHA를 고정해서 수행한다.

## 저장 위치와 책임

| 대상 | 기준 위치 | 책임 |
| --- | --- | --- |
| Intent 라벨·학습/검증 데이터 | `fowoco/knowledge` | 데이터 버전·SHA·검수 기준 |
| 제출·재현용 가중치 스냅샷 | Knowledge Git LFS | 파일 무결성과 인계 |
| 배포용 가중치 | `fowoco` Hugging Face private repo | 접근 제어·배포 revision |
| 운영 추론 코드 | `fowoco/ai` | 모델 로딩·fallback·응답 계약 |
| `HF_TOKEN` | 로컬 `.env` 또는 배포 Secret | 배포 운영자 |

Knowledge Git LFS 파일은 HF 장애나 권한 인계 시 결과를 재현하기 위한 스냅샷이다.
운영 서비스가 GitHub의 대용량 파일을 직접 내려받아 실행하는 경로로 사용하지 않는다.

## 배포 전 확인 순서

1. `python -m fowoco_knowledge validate`로 데이터와 로컬 스냅샷의 SHA·크기를 확인한다.
2. 권한이 있는 계정으로 HF repo의 commit SHA를 조회한다.
3. AI 배포 환경의 모델 경로를 `repo@commit_sha`로 고정한다.
4. `HF_TOKEN`을 저장소가 아닌 Secret으로 주입한다.
5. 같은 문장으로 BERT 기본 경로와 A.X fallback 경로를 각각 Smoke Test한다.
6. 응답의 `modelVersion`, Knowledge 데이터 version·SHA, AI commit을 실행 기록에 남긴다.

비공개 저장소이므로 인증하지 않은 HF API가 `401`을 반환하는 것은 정상이다. 이때
revision을 추측하거나 `main`으로 대체하지 않고, 배포 담당자가 인증 후 SHA를 확인한다.

## 재현 기준

- 모델 파일 목록·SHA-256·크기: `hr-intent-service/models/artifact-manifest.yaml`
- 모델 목적·평가·한계: `hr-intent-service/models/MODEL_CARD.md`
- 학습 데이터 기준: `data/intent/manifest.yaml`의 `known_model_training_snapshot`
- 라이브러리 범위: `hr-intent-service/requirements.txt`

현재 가중치는 Intent 데이터 `1.2.0` snapshot으로 학습됐다. 현재 데이터 `1.2.1`과
일치하지 않으므로, `1.2.1` 성능으로 표현하거나 재학습된 가중치처럼 배포하면 안 된다.

## Token 관리

- 개인 Read token 또는 배포 전용 최소 권한 token만 사용한다.
- `.env`, GitHub Actions Secret, Kubernetes Secret 이외의 위치에 기록하지 않는다.
- README, Issue, PR, 로그, Docker image layer에 실제 token을 넣지 않는다.
- 유출이 의심되면 즉시 폐기·재발급하고 실행 환경의 Secret을 교체한다.

## 최소 Smoke Test

AI 저장소에서 실제 모델을 켠 뒤 다음을 확인한다.

```text
입력: 응웬반안 체류연장 준비해줘
기대 Intent: EXPIRY_RENEWAL
기대 Workflow: WF-STY-001
금지: modelProvider/modelVersion이 stub으로 표시되는 응답
```

정확도 수치는 내부 Validation 268건의 개발 결과다. 독립 Gold Test 또는 운영 성능으로
표현하지 않으며 법률 판단, Workflow 실행, 외부기관 제출은 모델의 책임이 아니다.
5 changes: 5 additions & 0 deletions fowoco-knowledge/hr-intent-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ HF_TOKEN=<fowoco 조직 접근 권한이 있는 개인 토큰 : intent 모델

`HF_TOKEN`은 절대 코드나 `Dockerfile`에 하드코딩하지 않는다. `.env`(`.gitignore`로 제외됨) 또는 배포 시 Secret으로 주입한다.

HF 저장소는 비공개이므로 배포 전에 인증된 계정으로 commit SHA를 조회하고 모델 경로를
해당 revision에 고정한다. 로컬 스냅샷의 파일 목록·SHA·크기는
[`models/artifact-manifest.yaml`](models/artifact-manifest.yaml), 전체 정책은
[`docs/MODEL_ARTIFACT_POLICY.md`](../docs/MODEL_ARTIFACT_POLICY.md)를 기준으로 한다.


## 로컬 실행 - 가상환경

Expand Down
50 changes: 50 additions & 0 deletions fowoco-knowledge/hr-intent-service/models/MODEL_CARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# FOWOCO HR Intent models

## 목적

HR 담당자의 한국어 발화에서 FOWOCO의 지원 Intent를 분류한다. KLUE-RoBERTa를 빠른
기본 분류기로 사용하고, 경계 규칙 또는 낮은 margin에 해당하는 요청만 A.X QLoRA
adapter로 보낸다.

## 산출물

| ID | 배포 저장소 | 역할 |
| --- | --- | --- |
| `KLUE_ROBERTA_INTENT` | `fowoco/klue-roberta-base-intent-classifier` | 기본 분류 |
| `AX_INTENT_QLORA` | `fowoco/ax-intent-qlora` | 복잡·경계 요청 보조 |

두 저장소는 비공개다. 파일 무결성은 같은 디렉터리의 `artifact-manifest.yaml`을 기준으로
검증하고, 운영 배포에서는 HF commit SHA를 별도로 고정한다.

## 데이터 계보

- 학습·내부 Validation snapshot: Intent 데이터 `1.2.0`
- snapshot SHA-256: `447174a992e31bd44ec8abe658dc4082d3197bf284e0cc08b7ac5e3e6341a237`
- 현재 Knowledge 데이터: `1.2.1`
- 현재 가중치의 `1.2.1` 재학습 여부: 아니오
- 개인정보 원문 포함 여부: 아니오

## 내부 Validation 결과

| 경로 | 내부 Validation 268건 |
| --- | ---: |
| KLUE-RoBERTa | 95.5% |
| A.X-4.0-Light QLoRA | 92.2% |
| 조건부 Cascade | 93.2% |

동일 Validation으로 모델 선택과 routing 규칙을 조정했으므로 위 수치는 독립 Test 또는
운영 성능이 아니다.

## 사용 범위와 제한

- 허용: Intent 후보와 evidence 생성, 위험 요청을 보조 모델로 routing
- 금지: 법률·체류·계약 결론, Workflow 자동 승인, 근로자 안내 자동 발송, 외부기관 제출
- BERT가 evidence를 제공하지 못할 수 있으므로 evidence는 nullable 계약을 따른다.
- A.X가 분류 확률을 제공하지 않으면 confidence를 만들지 않고 `UNAVAILABLE`로 표현한다.
- 모델 실패 시 fallback 사용 여부와 실제 모델 버전을 응답 메타데이터에 명시한다.

## 재현

환경 구성과 실행은 상위 `README.md`, 보관·배포 절차는
`../../docs/MODEL_ARTIFACT_POLICY.md`를 따른다. `HF_TOKEN`은 환경변수 또는 Secret으로만
주입한다.
55 changes: 55 additions & 0 deletions fowoco-knowledge/hr-intent-service/models/artifact-manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
manifest_version: 1.0.0
canonical_distribution: HUGGING_FACE_PRIVATE
training_dataset:
version: 1.2.0
sha256: 447174a992e31bd44ec8abe658dc4082d3197bf284e0cc08b7ac5e3e6341a237
current_dataset_version: 1.2.1
matches_current_dataset: false
models:
- id: KLUE_ROBERTA_INTENT
role: PRIMARY_CLASSIFIER
base_model: klue/roberta-base
hugging_face_repo: fowoco/klue-roberta-base-intent-classifier
access: PRIVATE
revision_policy: PIN_COMMIT_SHA_AT_DEPLOYMENT
runtime_owner: fowoco/ai
snapshot_files:
- path: hr-intent-service/models/klue-roberta-base-intent-classifier/config.json
sha256: e061cd96d6dd2fea2cade4cca60cb578cf8a7045f0173bbd0b3c7825848ea537
bytes: 1242
- path: hr-intent-service/models/klue-roberta-base-intent-classifier/model.safetensors
sha256: abf0d7d3bdb89a5ac34abc8cc41a77396bf024f02de6939591b3c555446bd48b
bytes: 442518124
- path: hr-intent-service/models/klue-roberta-base-intent-classifier/tokenizer.json
sha256: 5eebbce20f3196cbe82aa65376cd211151a0635614999a9191b0cb8f1b04f86b
bytes: 752096
- path: hr-intent-service/models/klue-roberta-base-intent-classifier/tokenizer_config.json
sha256: 6e7bb997371020d3c04d4c08798aad63113a59406957a90bc51bb908309ca0ae
bytes: 641
- id: AX_INTENT_QLORA
role: FALLBACK_ADAPTER
base_model: skt/A.X-4.0-Light
hugging_face_repo: fowoco/ax-intent-qlora
access: PRIVATE
revision_policy: PIN_COMMIT_SHA_AT_DEPLOYMENT
runtime_owner: fowoco/ai
snapshot_files:
- path: hr-intent-service/models/ax-4.0-light-qlora-classifier/adapter_config.json
sha256: 3b92a54bc112f250e041d828726b610bc2062f8b87cfb0b1759b6ba7a609c307
bytes: 1096
- path: hr-intent-service/models/ax-4.0-light-qlora-classifier/adapter_model.safetensors
sha256: 8bbf957ae0939de40b5b34fd88c34a5f7196c4f8f53e054024e17d755883bac3
bytes: 80792880
- path: hr-intent-service/models/ax-4.0-light-qlora-classifier/chat_template.jinja
sha256: 15f2d4a602c42c90a1f03e13fe353510d745422a5c61b9abdefe47d5b77b5113
bytes: 4003
- path: hr-intent-service/models/ax-4.0-light-qlora-classifier/tokenizer.json
sha256: 4050a8c9791364e3a81b1b813a32a6423e09ba7608e2eada83cc73e28a92c0eb
bytes: 8495509
- path: hr-intent-service/models/ax-4.0-light-qlora-classifier/tokenizer_config.json
sha256: 4f878003210156bd1e60a9eadfe20b0dd936ddd542736a4d5db865cd0303e437
bytes: 1319
secret_policy:
environment_variable: HF_TOKEN
repository_storage_forbidden: true
owner: deployment-operator
16 changes: 8 additions & 8 deletions fowoco-knowledge/hr-intent-service/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
fastapi>=0.115.0
uvicorn[standard]>=0.30.0
pydantic-settings>=2.5.0
transformers>=4.46.0
accelerate
bitsandbytes
peft
torch
fastapi>=0.115.0,<1
uvicorn[standard]>=0.30.0,<1
pydantic-settings>=2.5.0,<3
transformers>=4.46.0,<5
accelerate>=1,<2
bitsandbytes>=0.44,<1
peft>=0.13,<1
torch>=2.4,<3
3 changes: 3 additions & 0 deletions fowoco-knowledge/knowledge/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ datasets:
catalog_e2e_manifest: data/evaluation/e2e_catalog_manifest.yaml
catalog_e2e_gold_candidates: data/evaluation/e2e_catalog_cases.jsonl
demo_expiry_renewal: data/demo/expiry_renewal_golden.yaml
artifacts:
intent_models: hr-intent-service/models/artifact-manifest.yaml
intent_model_schema: schemas/model-artifact-manifest.schema.json
review_policy:
minimum_reviewers_for_official_knowledge: 2
all_outbound_messages_require_hr_approval: true
Expand Down
85 changes: 85 additions & 0 deletions fowoco-knowledge/schemas/model-artifact-manifest.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://fowoco.dev/schemas/model-artifact-manifest.schema.json",
"title": "FOWOCO intent model artifact manifest",
"type": "object",
"additionalProperties": false,
"required": [
"manifest_version",
"canonical_distribution",
"training_dataset",
"models",
"secret_policy"
],
"properties": {
"manifest_version": {"type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
"canonical_distribution": {"const": "HUGGING_FACE_PRIVATE"},
"training_dataset": {
"type": "object",
"additionalProperties": false,
"required": ["version", "sha256", "current_dataset_version", "matches_current_dataset"],
"properties": {
"version": {"type": "string"},
"sha256": {"$ref": "#/$defs/sha256"},
"current_dataset_version": {"type": "string"},
"matches_current_dataset": {"type": "boolean"}
}
},
"models": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"role",
"hugging_face_repo",
"access",
"revision_policy",
"runtime_owner",
"snapshot_files"
],
"properties": {
"id": {"type": "string", "pattern": "^[A-Z0-9_]+$"},
"role": {"enum": ["PRIMARY_CLASSIFIER", "FALLBACK_ADAPTER"]},
"base_model": {"type": "string", "minLength": 1},
"hugging_face_repo": {"type": "string", "pattern": "^fowoco/[A-Za-z0-9._-]+$"},
"access": {"const": "PRIVATE"},
"revision_policy": {"const": "PIN_COMMIT_SHA_AT_DEPLOYMENT"},
"runtime_owner": {"const": "fowoco/ai"},
"snapshot_files": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["path", "sha256", "bytes"],
"properties": {
"path": {
"type": "string",
"pattern": "^hr-intent-service/models/[A-Za-z0-9._/-]+$"
},
"sha256": {"$ref": "#/$defs/sha256"},
"bytes": {"type": "integer", "minimum": 1}
}
}
}
}
}
},
"secret_policy": {
"type": "object",
"additionalProperties": false,
"required": ["environment_variable", "repository_storage_forbidden", "owner"],
"properties": {
"environment_variable": {"const": "HF_TOKEN"},
"repository_storage_forbidden": {"const": true},
"owner": {"const": "deployment-operator"}
}
}
},
"$defs": {
"sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"}
}
}
Loading