hee_!J commited on
Commit ·
f606871
1
Parent(s): 10f82ba
feat: PHM 2016 CMP 통합 + 3개 알람 실데이터 동작
Browse files- .gitignore +1 -0
- agents/cause.py +3 -2
- agents/detection.py +70 -18
- agents/rag/knowledge/FLOW-CMP-DOWN-001.md +26 -0
- agents/rag/knowledge/FMEA-CMP-003.md +31 -0
- agents/rag/knowledge/FMEA-ET-004.md +30 -0
- agents/rag/knowledge/INC-CMP-2025-0142.md +31 -0
- agents/rag/knowledge/INC-ET-2024-0301.md +30 -0
- agents/rag/knowledge/SOP-CMP-SLURRY-001.md +29 -0
- core/pipeline.py +1 -1
- data/demo.py +4 -3
- data/phm2016/README.md +53 -0
- data/phm2016/__init__.py +0 -0
- data/phm2016/loader.py +74 -0
- data/wip.py +8 -0
.gitignore
CHANGED
|
@@ -11,3 +11,4 @@ CLAUDE.md
|
|
| 11 |
.claude/
|
| 12 |
.mcp.json
|
| 13 |
data/secom/raw/*.data
|
|
|
|
|
|
| 11 |
.claude/
|
| 12 |
.mcp.json
|
| 13 |
data/secom/raw/*.data
|
| 14 |
+
data/phm2016/raw/
|
agents/cause.py
CHANGED
|
@@ -35,7 +35,7 @@ TIER2_SCHEMA = {
|
|
| 35 |
"additionalProperties": False,
|
| 36 |
}
|
| 37 |
|
| 38 |
-
SYSTEM_PROMPT = """당신은 반도체
|
| 39 |
주어진 이상 알람과 탐지 결과, 사내 지식 문서를 근거로 가장 가능성 높은 원인을
|
| 40 |
2~3개 추정합니다. 각 원인은 기여도(pct, %)를 가지며 합이 100에 가깝도록 합니다.
|
| 41 |
근거(evidence)는 제공된 문서 내용에 기반해 구체적으로 작성하고, citations에는
|
|
@@ -45,7 +45,8 @@ SYSTEM_PROMPT = """당신은 반도체 Photo 공정의 원인 분석 전문가
|
|
| 45 |
|
| 46 |
def _build_query(alarm: dict, tier1: Tier1) -> str:
|
| 47 |
feature = alarm.get("feature") or ""
|
| 48 |
-
|
|
|
|
| 49 |
|
| 50 |
|
| 51 |
def run_cause(alarm: dict, tier1: Tier1) -> Tier2:
|
|
|
|
| 35 |
"additionalProperties": False,
|
| 36 |
}
|
| 37 |
|
| 38 |
+
SYSTEM_PROMPT = """당신은 반도체 공정 원인 분석 전문가입니다.
|
| 39 |
주어진 이상 알람과 탐지 결과, 사내 지식 문서를 근거로 가장 가능성 높은 원인을
|
| 40 |
2~3개 추정합니다. 각 원인은 기여도(pct, %)를 가지며 합이 100에 가깝도록 합니다.
|
| 41 |
근거(evidence)는 제공된 문서 내용에 기반해 구체적으로 작성하고, citations에는
|
|
|
|
| 45 |
|
| 46 |
def _build_query(alarm: dict, tier1: Tier1) -> str:
|
| 47 |
feature = alarm.get("feature") or ""
|
| 48 |
+
sensors = " ".join(f["name"] for f in tier1["features"])
|
| 49 |
+
return f"{alarm['title']} {feature} {sensors} 원인 분석 공정 이상"
|
| 50 |
|
| 51 |
|
| 52 |
def run_cause(alarm: dict, tier1: Tier1) -> Tier2:
|
agents/detection.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
| 1 |
"""Tier 1 이상 탐지 에이전트
|
| 2 |
|
| 3 |
-
알람을
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
from functools import lru_cache
|
| 9 |
|
|
@@ -11,22 +14,25 @@ import numpy as np
|
|
| 11 |
from sklearn.ensemble import IsolationForest
|
| 12 |
|
| 13 |
from core.schema import Tier1
|
|
|
|
| 14 |
from data.secom.loader import load_secom
|
| 15 |
from data.secom.preprocess import SecomPreprocessor
|
| 16 |
|
| 17 |
RANDOM_STATE = 42
|
| 18 |
TOP_N_FEATURES = 3
|
| 19 |
|
| 20 |
-
#
|
| 21 |
-
#
|
| 22 |
ALARM_WAFER = {
|
| 23 |
-
"A1": {"
|
|
|
|
|
|
|
| 24 |
}
|
| 25 |
|
| 26 |
|
| 27 |
@lru_cache(maxsize=1)
|
| 28 |
-
def
|
| 29 |
-
"""SECOM 전체로 전처리기와 IsolationForest
|
| 30 |
X, _ = load_secom()
|
| 31 |
pre = SecomPreprocessor().fit(X)
|
| 32 |
Xz = pre.transform(X)
|
|
@@ -36,28 +42,74 @@ def _fit_model():
|
|
| 36 |
return X, pre, model, train_scores
|
| 37 |
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
|
|
|
| 46 |
|
| 47 |
raw_score = float(-model.score_samples(Xz)[0])
|
| 48 |
-
# 학습 분포 대비 백분위, 높을수록 이상
|
| 49 |
score = float((train_scores < raw_score).mean())
|
| 50 |
|
| 51 |
-
# 표준화 값의 절대크기 = 정상 분포에서 벗어난 정도, Top-N
|
| 52 |
deviations = np.abs(Xz[0])
|
| 53 |
top_idx = np.argsort(deviations)[::-1][:TOP_N_FEATURES]
|
| 54 |
features = [
|
| 55 |
{"name": pre.keep_cols[i], "value": round(float(deviations[i]), 2)}
|
| 56 |
for i in top_idx
|
| 57 |
]
|
| 58 |
-
|
| 59 |
return {
|
| 60 |
"score": round(score, 2),
|
| 61 |
"features": features,
|
| 62 |
"lot": {"id": alarm["lot_id"], "wafers": mapping["wafers"]},
|
| 63 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""Tier 1 이상 탐지 에이전트
|
| 2 |
|
| 3 |
+
알람을 데이터셋의 특정 wafer에 매핑해 이상 점수와 기여 피처를 계산
|
| 4 |
+
- A1, A2: SECOM 공개 데이터셋 (다른 fail row 매핑)
|
| 5 |
+
- A3: PHM 2016 CMP 공개 데이터셋 (CMP step 실측, 25개 명명 센서)
|
| 6 |
+
|
| 7 |
+
모델: IsolationForest (벤치마크에서 PR-AUC 우수)
|
| 8 |
+
이상 점수: 학습 분포 대비 백분위로 0~1 정규화
|
| 9 |
+
기여 피처: 표준화 값의 절대크기 Top-N (정상 분포에서 가장 벗어난 센서)
|
| 10 |
"""
|
| 11 |
from functools import lru_cache
|
| 12 |
|
|
|
|
| 14 |
from sklearn.ensemble import IsolationForest
|
| 15 |
|
| 16 |
from core.schema import Tier1
|
| 17 |
+
from data.phm2016.loader import load_phm_cmp
|
| 18 |
from data.secom.loader import load_secom
|
| 19 |
from data.secom.preprocess import SecomPreprocessor
|
| 20 |
|
| 21 |
RANDOM_STATE = 42
|
| 22 |
TOP_N_FEATURES = 3
|
| 23 |
|
| 24 |
+
# 알람 -> 데이터 소스 매핑
|
| 25 |
+
# wafers: 데모 컨텍스트상 영향 웨이퍼 수
|
| 26 |
ALARM_WAFER = {
|
| 27 |
+
"A1": {"source": "secom", "row": 2, "wafers": 25},
|
| 28 |
+
"A2": {"source": "secom", "row": 10, "wafers": 18},
|
| 29 |
+
"A3": {"source": "phm_cmp", "wafer_id": 2058207580, "stage": "A", "wafers": 30},
|
| 30 |
}
|
| 31 |
|
| 32 |
|
| 33 |
@lru_cache(maxsize=1)
|
| 34 |
+
def _fit_secom():
|
| 35 |
+
"""SECOM 전체로 전처리기와 IsolationForest 학습, 첫 호출 시 1회"""
|
| 36 |
X, _ = load_secom()
|
| 37 |
pre = SecomPreprocessor().fit(X)
|
| 38 |
Xz = pre.transform(X)
|
|
|
|
| 42 |
return X, pre, model, train_scores
|
| 43 |
|
| 44 |
|
| 45 |
+
@lru_cache(maxsize=1)
|
| 46 |
+
def _fit_phm_cmp():
|
| 47 |
+
"""PHM CMP 전체로 표준화 + IsolationForest 학습, 첫 호출 시 1회"""
|
| 48 |
+
from sklearn.preprocessing import StandardScaler
|
| 49 |
+
|
| 50 |
+
features, _ = load_phm_cmp()
|
| 51 |
+
scaler = StandardScaler().fit(features.values)
|
| 52 |
+
Xz = scaler.transform(features.values)
|
| 53 |
+
model = IsolationForest(n_estimators=200, random_state=RANDOM_STATE)
|
| 54 |
+
model.fit(Xz)
|
| 55 |
+
train_scores = -model.score_samples(Xz)
|
| 56 |
+
return features, scaler, model, train_scores
|
| 57 |
+
|
| 58 |
|
| 59 |
+
def _detect_secom(alarm: dict, mapping: dict) -> Tier1:
|
| 60 |
+
X, pre, model, train_scores = _fit_secom()
|
| 61 |
+
Xz = pre.transform(X.iloc[[mapping["row"]]])
|
| 62 |
|
| 63 |
raw_score = float(-model.score_samples(Xz)[0])
|
|
|
|
| 64 |
score = float((train_scores < raw_score).mean())
|
| 65 |
|
|
|
|
| 66 |
deviations = np.abs(Xz[0])
|
| 67 |
top_idx = np.argsort(deviations)[::-1][:TOP_N_FEATURES]
|
| 68 |
features = [
|
| 69 |
{"name": pre.keep_cols[i], "value": round(float(deviations[i]), 2)}
|
| 70 |
for i in top_idx
|
| 71 |
]
|
|
|
|
| 72 |
return {
|
| 73 |
"score": round(score, 2),
|
| 74 |
"features": features,
|
| 75 |
"lot": {"id": alarm["lot_id"], "wafers": mapping["wafers"]},
|
| 76 |
}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _detect_phm_cmp(alarm: dict, mapping: dict) -> Tier1:
|
| 80 |
+
features, scaler, model, train_scores = _fit_phm_cmp()
|
| 81 |
+
key = (mapping["wafer_id"], mapping["stage"])
|
| 82 |
+
if key not in features.index:
|
| 83 |
+
raise KeyError(f"PHM CMP에 없는 wafer/stage: {key}")
|
| 84 |
+
|
| 85 |
+
row = features.loc[[key]].values
|
| 86 |
+
Xz = scaler.transform(row)
|
| 87 |
+
|
| 88 |
+
raw_score = float(-model.score_samples(Xz)[0])
|
| 89 |
+
score = float((train_scores < raw_score).mean())
|
| 90 |
+
|
| 91 |
+
deviations = np.abs(Xz[0])
|
| 92 |
+
top_idx = np.argsort(deviations)[::-1][:TOP_N_FEATURES]
|
| 93 |
+
col_names = list(features.columns)
|
| 94 |
+
out_features = [
|
| 95 |
+
{"name": col_names[i], "value": round(float(deviations[i]), 2)}
|
| 96 |
+
for i in top_idx
|
| 97 |
+
]
|
| 98 |
+
return {
|
| 99 |
+
"score": round(score, 2),
|
| 100 |
+
"features": out_features,
|
| 101 |
+
"lot": {"id": alarm["lot_id"], "wafers": mapping["wafers"]},
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def run_detection(alarm: dict) -> Tier1:
|
| 106 |
+
mapping = ALARM_WAFER.get(alarm["id"])
|
| 107 |
+
if mapping is None:
|
| 108 |
+
raise ValueError(f"매핑 없는 알람: {alarm['id']}")
|
| 109 |
+
|
| 110 |
+
source = mapping["source"]
|
| 111 |
+
if source == "secom":
|
| 112 |
+
return _detect_secom(alarm, mapping)
|
| 113 |
+
if source == "phm_cmp":
|
| 114 |
+
return _detect_phm_cmp(alarm, mapping)
|
| 115 |
+
raise ValueError(f"알 수 없는 데이터 소스: {source}")
|
agents/rag/knowledge/FLOW-CMP-DOWN-001.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FLOW-CMP-DOWN-001 — CMP 하류 공정 의존성과 수율 영향
|
| 2 |
+
|
| 3 |
+
## 공정 흐름
|
| 4 |
+
|
| 5 |
+
CMP (현재) → Diffusion → Implant → Metal Deposition
|
| 6 |
+
|
| 7 |
+
## CMP 이상이 하류에 미치는 영향
|
| 8 |
+
|
| 9 |
+
CMP의 재료 제거율(MRR) 이상은 wafer 두께 편차로 직결되며 후공정 전반에 영향을
|
| 10 |
+
미칩니다. 일반적으로:
|
| 11 |
+
|
| 12 |
+
- **Diffusion**: 두께 편차에 따라 도핑 깊이 균일도 저하, 약 10~15% 변동
|
| 13 |
+
- **Implant**: 표면 위치 오차로 도핑 농도 편차 발생, 약 5~10% 변동
|
| 14 |
+
- **Metal Deposition**: 표면 평탄도 손상 시 단차 피복성 저하
|
| 15 |
+
|
| 16 |
+
## 수율 영향
|
| 17 |
+
|
| 18 |
+
CMP MRR 이탈이 1σ 이상 발생할 때 수율 손실은 통상 1.5~3.0 %p로 보고됩니다.
|
| 19 |
+
특히 동일 chamber 처리 로트 전체에 영향이 미치므로 후공정 진입 보류 후 두께
|
| 20 |
+
재측정이 우선 조치입니다.
|
| 21 |
+
|
| 22 |
+
## 의존성 분류 기준
|
| 23 |
+
|
| 24 |
+
- current: 이상이 발생한 현재 공정
|
| 25 |
+
- impacted: 직접 영향을 받는 후공정 (delta 두 자리수 이상)
|
| 26 |
+
- minor: 영향이 경미한 후공정 (delta 한 자리수)
|
agents/rag/knowledge/FMEA-CMP-003.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FMEA-CMP-003 — CMP 공정 실패 모드 분석
|
| 2 |
+
|
| 3 |
+
## 대상
|
| 4 |
+
|
| 5 |
+
CMP(Chemical Mechanical Planarization) 공정의 주요 실패 모드와 원인, 영향, 검출
|
| 6 |
+
방법을 정리합니다.
|
| 7 |
+
|
| 8 |
+
## 실패 모드
|
| 9 |
+
|
| 10 |
+
### 1. 재료 제거율(MRR) 폭주 또는 부족
|
| 11 |
+
|
| 12 |
+
- 잠재 원인: 슬러리 유량 이상, 패드 마모, 압력 설정 오류
|
| 13 |
+
- 영향: 두께 편차 → 후공정 패턴 결함, 수율 손실
|
| 14 |
+
- 검출: AVG_REMOVAL_RATE 모니터링, SLURRY_FLOW_LINE 유량 SPC
|
| 15 |
+
|
| 16 |
+
### 2. 표면 균일도 저하
|
| 17 |
+
|
| 18 |
+
- 잠재 원인: RETAINER_RING_PRESSURE 편차, MAIN_OUTER_AIR_BAG_PRESSURE 불균형
|
| 19 |
+
- 영향: 후공정 Photo 단계의 포커스 편차 유발
|
| 20 |
+
- 검출: 두께 매핑, 압력 센서 SPC
|
| 21 |
+
|
| 22 |
+
### 3. 패드 컨디션 악화
|
| 23 |
+
|
| 24 |
+
- 잠재 원인: 드레서 사용량(USAGE_OF_DRESSER) 한계 초과, DRESSING_WATER_STATUS 비정상
|
| 25 |
+
- 영향: MRR 변동성 증가, 스크래치 발생
|
| 26 |
+
- 검출: 드레서 사용량 추적, 시각 검사
|
| 27 |
+
|
| 28 |
+
## 우선순위
|
| 29 |
+
|
| 30 |
+
MRR 폭주는 즉시 후공정 영향이 크고 회수가 어려워 최우선 관리 대상입니다. 원인
|
| 31 |
+
중 슬러리 라인 이상은 펌프 제어와 직접 연관되어 자동 인터록 적용이 효과적입니다.
|
agents/rag/knowledge/FMEA-ET-004.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FMEA-ET-004 — Etch 공정 실패 모드 분석
|
| 2 |
+
|
| 3 |
+
## 대상
|
| 4 |
+
|
| 5 |
+
Etch(식각) 공정의 주요 실패 모드와 원인, 영향, 검출 방법을 정리합니다.
|
| 6 |
+
|
| 7 |
+
## 실패 모드
|
| 8 |
+
|
| 9 |
+
### 1. 트렌치 깊이 부족 또는 과식각
|
| 10 |
+
|
| 11 |
+
- 잠재 원인: 식각 가스 유량 이상, 플라즈마 밀도 저하, 전극 오염
|
| 12 |
+
- 영향: 후공정 CMP·Metal Deposition 단계의 단차 균일도 악화, 수율 손실
|
| 13 |
+
- 검출: 두께 SEM 계측, 가스 유량 SPC
|
| 14 |
+
|
| 15 |
+
### 2. 측벽 프로파일 이상
|
| 16 |
+
|
| 17 |
+
- 잠재 원인: 챔버 압력 편차, 가스 비율 오류
|
| 18 |
+
- 영향: 패턴 정합 불량, CD 산포 증가
|
| 19 |
+
- 검출: 단면 SEM, 챔버 압력 SPC
|
| 20 |
+
|
| 21 |
+
### 3. 표면 결함(미세 입자 부착)
|
| 22 |
+
|
| 23 |
+
- 잠재 원인: 챔버 클리닝 주기 초과, 전극 마모
|
| 24 |
+
- 영향: 후공정 결함 전파
|
| 25 |
+
- 검출: 결함 검사 장비, 챔버 클리닝 이력 추적
|
| 26 |
+
|
| 27 |
+
## 우선순위
|
| 28 |
+
|
| 29 |
+
트렌치 깊이 부족은 후공정 영향 범위가 가장 커 최우선 관리 대상입니다. 가스 유량
|
| 30 |
+
이상은 자동 알람과 챔버 인터록을 통해 조기 차단이 가능합니다.
|
agents/rag/knowledge/INC-CMP-2025-0142.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# INC-CMP-2025-0142 — CMP 슬러리 유량 이상에 의한 재료 제거율 폭주
|
| 2 |
+
|
| 3 |
+
## 분류
|
| 4 |
+
|
| 5 |
+
- 유형: 공정 인시던트
|
| 6 |
+
- 공정: CMP (Chemical Mechanical Planarization)
|
| 7 |
+
- 사업부: 메모리 1동
|
| 8 |
+
- 발생일: 2025-01-14
|
| 9 |
+
|
| 10 |
+
## 증상
|
| 11 |
+
|
| 12 |
+
CMP 공정에서 평균 재료 제거율(AVG_REMOVAL_RATE)이 정상 범위(70~90 nm/min)를 크게
|
| 13 |
+
벗어나 4,000 nm/min 이상으로 측정되었습니다. 동일 chamber에서 처리된 연속 로트
|
| 14 |
+
대부분이 영향을 받았습니다.
|
| 15 |
+
|
| 16 |
+
## 원인
|
| 17 |
+
|
| 18 |
+
슬러리 공급 라인(SLURRY_FLOW_LINE_A/B/C) 유량이 정상치 대비 비정상적으로 높게
|
| 19 |
+
유지되었음이 확인되었습니다. 정확히는 슬러리 공급 펌프 제어 신호 오류로 인해
|
| 20 |
+
세 라인 모두 동시에 과공급된 사례입니다.
|
| 21 |
+
|
| 22 |
+
## 조치
|
| 23 |
+
|
| 24 |
+
- 슬러리 공급 펌프 즉시 정지 및 교체
|
| 25 |
+
- 영향 로트 후공정 진입 보류 및 두께 계측으로 재작업 여부 판단
|
| 26 |
+
- 펌프 제어 로직 펌웨어 업데이트
|
| 27 |
+
|
| 28 |
+
## 재발 방지
|
| 29 |
+
|
| 30 |
+
슬러리 유량 임계값을 신설하고, 임계 초과 시 자동 알람 + 챔버 인터록이 작동하도록
|
| 31 |
+
모니터링 체계를 강화하였습니다.
|
agents/rag/knowledge/INC-ET-2024-0301.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# INC-ET-2024-0301 — Etch 트렌치 깊이 부족에 의한 결함 발생
|
| 2 |
+
|
| 3 |
+
## 분류
|
| 4 |
+
|
| 5 |
+
- 유형: 공정 인시던트
|
| 6 |
+
- 공정: Etch (식각)
|
| 7 |
+
- 사업부: 메모리 2동
|
| 8 |
+
- 발생일: 2024-08-12
|
| 9 |
+
|
| 10 |
+
## 증상
|
| 11 |
+
|
| 12 |
+
Etch 공정에서 Trench Depth가 목표치 대비 약 12% 부족한 상태로 측정되었습니다.
|
| 13 |
+
동일 chamber에서 처리된 로트에서 일관되게 관측되어 chamber 단위 이상으로
|
| 14 |
+
판단되었습니다.
|
| 15 |
+
|
| 16 |
+
## 원인
|
| 17 |
+
|
| 18 |
+
식각 가스(SF6/CHF3) 유량 제어 밸브의 부분 고장으로 가스 공급량이 정상 대비 약
|
| 19 |
+
8% 감소했음이 확인되었습니다. 추가로 전극 표면 부착물 누적으로 플라즈마 밀도가
|
| 20 |
+
저하된 것이 복합 원인이었습니다.
|
| 21 |
+
|
| 22 |
+
## 조치
|
| 23 |
+
|
| 24 |
+
- chamber 즉시 정지 및 가스 공급 밸브 교체
|
| 25 |
+
- 전극 표면 세정(약 4시간 소요)
|
| 26 |
+
- 영향 로트 후공정 진입 보류 후 두께 재측정으로 재작업 여부 판단
|
| 27 |
+
|
| 28 |
+
## 재발 방지
|
| 29 |
+
|
| 30 |
+
가스 유량 SPC를 강화하고, 전극 세정 주기를 30일에서 21일로 단축 적용하였습니다.
|
agents/rag/knowledge/SOP-CMP-SLURRY-001.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SOP-CMP-SLURRY-001 — CMP 슬러리 관리 표준 절차
|
| 2 |
+
|
| 3 |
+
## 목적
|
| 4 |
+
|
| 5 |
+
CMP 공정의 슬러리 공급 시스템 점검과 이상 발생 시 대응 절차를 표준화합니다.
|
| 6 |
+
|
| 7 |
+
## 적용 범위
|
| 8 |
+
|
| 9 |
+
전 사업부 CMP 설비의 SLURRY_FLOW_LINE_A/B/C 공급 시스템.
|
| 10 |
+
|
| 11 |
+
## 정기 점검 주기
|
| 12 |
+
|
| 13 |
+
- 슬러리 유량 캘리브레이션: 7일
|
| 14 |
+
- 펌프 동작 점검: 14일
|
| 15 |
+
- 슬러리 농도 측정: 매 로트
|
| 16 |
+
|
| 17 |
+
## 이상 발생 시 대응 절차
|
| 18 |
+
|
| 19 |
+
1. 챔버 정지 및 후공정 진입 차단
|
| 20 |
+
2. SLURRY_FLOW_LINE_A/B/C 측정값 확인 후 정상 범위 이탈 여부 판단
|
| 21 |
+
3. 펌프 제어 신호 점검 및 펌웨어 버전 확인
|
| 22 |
+
4. 필요 시 펌프 교체 (예상 3시간)
|
| 23 |
+
5. 시험 wafer로 MRR 정상 범위 재현 확인
|
| 24 |
+
6. 챔버 재가동 및 로트 진입 재개
|
| 25 |
+
|
| 26 |
+
## 기록
|
| 27 |
+
|
| 28 |
+
이상 사례는 인시던트 DB에 기록하며, 슬러리 유량 추세는 일일 SPC 리포트에
|
| 29 |
+
포함합니다.
|
core/pipeline.py
CHANGED
|
@@ -10,7 +10,7 @@ from core.schema import TierData
|
|
| 10 |
from data import demo
|
| 11 |
|
| 12 |
# LLM 멀티에이전트로 처리할 알람
|
| 13 |
-
REAL_AGENT_ALARMS = {"A1"}
|
| 14 |
|
| 15 |
|
| 16 |
def get_tier_data(alarm_id: str) -> TierData | None:
|
|
|
|
| 10 |
from data import demo
|
| 11 |
|
| 12 |
# LLM 멀티에이전트로 처리할 알람
|
| 13 |
+
REAL_AGENT_ALARMS = {"A1", "A2", "A3"}
|
| 14 |
|
| 15 |
|
| 16 |
def get_tier_data(alarm_id: str) -> TierData | None:
|
data/demo.py
CHANGED
|
@@ -27,11 +27,12 @@ DEFAULT_ALARMS = [
|
|
| 27 |
},
|
| 28 |
{
|
| 29 |
"id": "A3",
|
| 30 |
-
"status": "
|
| 31 |
"title": "CMP Step 이상",
|
| 32 |
"lot_id": "L20240510-N-08",
|
| 33 |
-
"feature":
|
| 34 |
-
"
|
|
|
|
| 35 |
},
|
| 36 |
]
|
| 37 |
|
|
|
|
| 27 |
},
|
| 28 |
{
|
| 29 |
"id": "A3",
|
| 30 |
+
"status": "warn",
|
| 31 |
"title": "CMP Step 이상",
|
| 32 |
"lot_id": "L20240510-N-08",
|
| 33 |
+
"feature": "재료 제거율(MRR)",
|
| 34 |
+
"feature_arrow": "↑",
|
| 35 |
+
"time": "1시간 전",
|
| 36 |
},
|
| 37 |
]
|
| 38 |
|
data/phm2016/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PHM 2016 CMP Dataset
|
| 2 |
+
|
| 3 |
+
CMP(Chemical Mechanical Planarization, 화학 기계적 연마) 공정 실측 데이터셋입니다.
|
| 4 |
+
A3 알람(CMP Step 이상)의 Tier 1 이상 탐지에서 사용합니다.
|
| 5 |
+
|
| 6 |
+
## 개요
|
| 7 |
+
|
| 8 |
+
- 출처: PHM Society Data Challenge 2016
|
| 9 |
+
- 1,981 wafer × 2 stage(A/B), 25개 센서 실측값 + 평균 재료 제거율(MRR) 라벨
|
| 10 |
+
- 센서 이름이 모두 공개되어 있어 도메인 해석이 가능합니다 (SECOM의 익명성과 대조)
|
| 11 |
+
|
| 12 |
+
## 주요 센서
|
| 13 |
+
|
| 14 |
+
- 압력: `PRESSURIZED_CHAMBER_PRESSURE`, `RETAINER_RING_PRESSURE`, `MAIN_OUTER_AIR_BAG_PRESSURE` 등
|
| 15 |
+
- 회전: `WAFER_ROTATION`, `STAGE_ROTATION`, `HEAD_ROTATION`
|
| 16 |
+
- 슬러리 유량: `SLURRY_FLOW_LINE_A/B/C`
|
| 17 |
+
- 사용량: `USAGE_OF_BACKING_FILM`, `USAGE_OF_DRESSER`, `USAGE_OF_POLISHING_TABLE` 등
|
| 18 |
+
|
| 19 |
+
## 준비 방법
|
| 20 |
+
|
| 21 |
+
원본 ZIP을 `data/phm2016/raw/`에 풀어두세요. 압축 해제 후 다음 구조가 되어야 합니다.
|
| 22 |
+
|
| 23 |
+
```
|
| 24 |
+
data/phm2016/raw/
|
| 25 |
+
├── CMP-training-removalrate.csv # 학습용 정답 라벨
|
| 26 |
+
├── CMP-test-removalrate.csv
|
| 27 |
+
└── CMP-data/
|
| 28 |
+
├── training/CMP-training-NNN.csv # trajectory 시계열 (185개)
|
| 29 |
+
└── test/CMP-test-NNN.csv
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
raw 데이터는 약 160MB로 git에 포함되지 않습니다(`.gitignore`).
|
| 33 |
+
|
| 34 |
+
## 로더 사용
|
| 35 |
+
|
| 36 |
+
```python
|
| 37 |
+
from data.phm2016.loader import load_phm_cmp
|
| 38 |
+
|
| 39 |
+
features, labels = load_phm_cmp()
|
| 40 |
+
# features: (N, 19) — wafer-stage 단위 센서 평균
|
| 41 |
+
# labels: (N,) — AVG_REMOVAL_RATE
|
| 42 |
+
# index: MultiIndex (WAFER_ID, STAGE)
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
trajectory 전체를 (WAFER_ID, STAGE)별로 평균 집계하여 wafer-stage 단위 feature
|
| 46 |
+
vector를 만듭니다. 첫 호출 시 약 10~30초가 소요되며 이후는 `lru_cache`로 즉시
|
| 47 |
+
응답합니다.
|
| 48 |
+
|
| 49 |
+
## 알람 매핑
|
| 50 |
+
|
| 51 |
+
A3(CMP Step 이상) 알람이 이 데이터의 특정 wafer에 매핑됩니다 (`agents/detection.py`의
|
| 52 |
+
`ALARM_WAFER`). MRR이 평균에서 크게 벗어난 wafer를 선택하여 이상 시나리오로
|
| 53 |
+
사용합니다.
|
data/phm2016/__init__.py
ADDED
|
File without changes
|
data/phm2016/loader.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""PHM 2016 Data Challenge CMP 데이터셋 로더
|
| 2 |
+
|
| 3 |
+
CMP(Chemical Mechanical Planarization) 공정 센서 데이터, 25개 실 센서 이름 공개
|
| 4 |
+
- 1981 wafer × 2 stage(A/B)
|
| 5 |
+
- target: 평균 재료 제거율(AVG_REMOVAL_RATE)
|
| 6 |
+
출처: https://phmsociety.org PHM Data Challenge 2016
|
| 7 |
+
|
| 8 |
+
raw/CMP-data/training/CMP-training-NNN.csv: trajectory 시계열 (wafer 다수)
|
| 9 |
+
raw/CMP-training-removalrate.csv: (WAFER_ID, STAGE, AVG_REMOVAL_RATE) 라벨
|
| 10 |
+
|
| 11 |
+
per-wafer feature vector를 만들기 위해 trajectory를 평균으로 집계
|
| 12 |
+
A3 알람(CMP step 이상)이 이 데이터로 Tier 1 이상 탐지 수행
|
| 13 |
+
"""
|
| 14 |
+
from functools import lru_cache
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
|
| 17 |
+
import pandas as pd
|
| 18 |
+
|
| 19 |
+
RAW_DIR = Path(__file__).parent / "raw"
|
| 20 |
+
TRAIN_TRAJ_DIR = RAW_DIR / "CMP-data" / "training"
|
| 21 |
+
TRAIN_LABEL = RAW_DIR / "CMP-training-removalrate.csv"
|
| 22 |
+
|
| 23 |
+
# 집계 대상 센서 컬럼, 진짜 의미 있는 이름들
|
| 24 |
+
SENSOR_COLS = [
|
| 25 |
+
"USAGE_OF_BACKING_FILM",
|
| 26 |
+
"USAGE_OF_DRESSER",
|
| 27 |
+
"USAGE_OF_POLISHING_TABLE",
|
| 28 |
+
"USAGE_OF_DRESSER_TABLE",
|
| 29 |
+
"PRESSURIZED_CHAMBER_PRESSURE",
|
| 30 |
+
"MAIN_OUTER_AIR_BAG_PRESSURE",
|
| 31 |
+
"CENTER_AIR_BAG_PRESSURE",
|
| 32 |
+
"RETAINER_RING_PRESSURE",
|
| 33 |
+
"RIPPLE_AIR_BAG_PRESSURE",
|
| 34 |
+
"USAGE_OF_MEMBRANE",
|
| 35 |
+
"USAGE_OF_PRESSURIZED_SHEET",
|
| 36 |
+
"SLURRY_FLOW_LINE_A",
|
| 37 |
+
"SLURRY_FLOW_LINE_B",
|
| 38 |
+
"SLURRY_FLOW_LINE_C",
|
| 39 |
+
"WAFER_ROTATION",
|
| 40 |
+
"STAGE_ROTATION",
|
| 41 |
+
"HEAD_ROTATION",
|
| 42 |
+
"DRESSING_WATER_STATUS",
|
| 43 |
+
"EDGE_AIR_BAG_PRESSURE",
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@lru_cache(maxsize=1)
|
| 48 |
+
def load_phm_cmp() -> tuple[pd.DataFrame, pd.Series]:
|
| 49 |
+
"""trajectory 전체를 (WAFER_ID, STAGE)별로 평균 집계해 wafer-stage 단위 feature 반환
|
| 50 |
+
|
| 51 |
+
features: (N, 19) - 센서 평균값
|
| 52 |
+
labels: (N,) - AVG_REMOVAL_RATE
|
| 53 |
+
index: MultiIndex (WAFER_ID, STAGE)
|
| 54 |
+
"""
|
| 55 |
+
if not TRAIN_TRAJ_DIR.exists() or not TRAIN_LABEL.exists():
|
| 56 |
+
raise FileNotFoundError(
|
| 57 |
+
f"PHM 2016 CMP 데이터가 없음, {RAW_DIR}에 데이터셋을 두세요 "
|
| 58 |
+
"(data/phm2016/README.md 참고)"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
# trajectory 파일 전체 로드 후 (WAFER_ID, STAGE)별 평균
|
| 62 |
+
frames = []
|
| 63 |
+
for path in sorted(TRAIN_TRAJ_DIR.glob("CMP-training-*.csv")):
|
| 64 |
+
df = pd.read_csv(path, usecols=["WAFER_ID", "STAGE"] + SENSOR_COLS)
|
| 65 |
+
frames.append(df)
|
| 66 |
+
all_traj = pd.concat(frames, ignore_index=True)
|
| 67 |
+
features = all_traj.groupby(["WAFER_ID", "STAGE"])[SENSOR_COLS].mean()
|
| 68 |
+
|
| 69 |
+
labels_df = pd.read_csv(TRAIN_LABEL)
|
| 70 |
+
labels_df = labels_df.set_index(["WAFER_ID", "STAGE"])["AVG_REMOVAL_RATE"]
|
| 71 |
+
|
| 72 |
+
# feature와 label 인덱스 정합
|
| 73 |
+
common = features.index.intersection(labels_df.index)
|
| 74 |
+
return features.loc[common], labels_df.loc[common]
|
data/wip.py
CHANGED
|
@@ -11,6 +11,14 @@ WIP_BY_ALARM: dict[str, list[ImpactLot]] = {
|
|
| 11 |
{"label": "가공 중", "lots": 3, "wafers": 75},
|
| 12 |
{"label": "대기 중", "lots": 5, "wafers": 125},
|
| 13 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
| 15 |
|
| 16 |
|
|
|
|
| 11 |
{"label": "가공 중", "lots": 3, "wafers": 75},
|
| 12 |
{"label": "대기 중", "lots": 5, "wafers": 125},
|
| 13 |
],
|
| 14 |
+
"A2": [
|
| 15 |
+
{"label": "가공 중", "lots": 2, "wafers": 50},
|
| 16 |
+
{"label": "대기 중", "lots": 4, "wafers": 100},
|
| 17 |
+
],
|
| 18 |
+
"A3": [
|
| 19 |
+
{"label": "가공 중", "lots": 4, "wafers": 100},
|
| 20 |
+
{"label": "대기 중", "lots": 6, "wafers": 150},
|
| 21 |
+
],
|
| 22 |
}
|
| 23 |
|
| 24 |
|