캡스톤: 배치 하나, 처음부터 끝까지
📍 현재 위치: 마지막 실습 장 — 우리가 구축한 모든 계층(layer)을 통해 완전한 시뮬레이션 유가식(fed-batch) 배치 하나를 실제로 가동하고, 검토 가능하며 서명된 배치 기록(batch record) 하나로 마무리합니다.
책 한 권 내내 공장의 배관을 깔아 왔다고 상상해 보세요. 파이프(센서), 탱크(히스토리언historian), 모든 밸브에 붙은 라벨(네임스페이스namespace), 지울 수 없는 작업 일지(감사 추적audit trail), 그리고 대시보드로 가득한 관제실 말입니다. 캡스톤(capstone)은 마침내 물을 틀고, 완전한 배치 하나가 입구에서부터 저 끝의 밀봉되고 서명된 병까지 흘러가는 것을 지켜보는 날입니다. 새로 짓는 것은 없습니다. 우리는 그저 단계마다 버튼을 하나씩 누르면서, 배관이 실제로 연결되어 있다는 것을, 그리고 반대편 끝에서 나오는 것이 품질 검토자가 서명할 수 있는 무언가라는 것을 입증할 뿐입니다.
이 장에서 다루는 내용
이것은 플랫폼이 서비스 더미이기를 그치고 하나의 시스템이 되는 순간입니다. 우리는 결정론적인 14일짜리 시뮬레이션 유가식 CHO + Protein A 단일클론항체(monoclonal antibody, mAb) 런 하나를 가져다, 장(章)마다 차례로 조립해 온 스택 전체를 통과시킵니다. 시뮬레이터 → 히스토리언 → ISA-88/95 배치 모델 → 맥락화(contextualization) → 실험실 결과 → ALCOA+ 감사 체인(audit chain) → 라만(Raman)→역가(titer) 소프트 센서(soft-sensor) → 검토 가능하고 FAIR에 부합하는 데이터셋으로 이어지는 흐름입니다. 우리는 동반 저장소(companion repo)의 실제 명령어 시퀀스를 그대로 실행하고, 결합을 수행하는 실제 SQL과 Python을 들여다보며, 모든 독자가 기다려 온 정직한 질문으로 끝맺습니다. 이것은 규제 당국이 받아들일 만한 배치 기록인가? (짧은 답: 데이터는 그렇습니다. 그것을 둘러싼 검증된 시스템은 하이브리드의 마지막 한 걸음입니다 — 오픈 소스 도구가 멈추는 지점, 그리고 규제 당국을 만족시키기 위해 여전히 상용의, 형식적으로 검증된 시스템과 서명된 절차가 필요한 잔여 영역입니다. 우리는 이 표현을 책 전체에서 사용하며, 다음 장은 그 선이 정확히 어디에 떨어지는지를 점수 매깁니다.)
가동되는 사례는 고전적인 것입니다. 우리는 업계의 정전(正典)이 된 표준 예제(worked example) — Protein A 포획 단계와, 핵심 품질 속성을 공정 매개변수에 연결하는 설계 기반 품질(Quality-by-Design) 제어 전략을 갖춘, 유가식 CHO 배양으로 생산되는 IgG1 mAb — 를 사용합니다 [8]. 아래의 모든 것은 단 하나의 배치, BATCH-2026-001입니다.
유가식 CHO 배양, Protein A 포획 단계, 그리고 IgG1 mAb 자체가 새로운 용어라면, 그것들은 1권 바이오의약품 제조에서 설명한 물리적 공정입니다 — 생산 바이오리액터와 Protein A 포획입니다. 여기서 우리는 이것이 데이터를 조립할 그 런이라는 점만 알면 됩니다.
하나의 명령 표면: 오케스트레이터로서의 Make
우리는 맞춤형 파이프라인 엔진을 발명한 적이 없습니다. 이 책 전체의 "모든 주장은 실행 가능하다"라는 약속은 독자가 책이 인쇄한 정확한 명령어를 입력하는 데 달려 있으므로, 오케스트레이터(orchestrator)는 우리가 가진 가장 오래되고, 가장 따분하며, 가장 믿을 만한 도구입니다. 바로 GNU Make입니다. Make는 의존성 기반 빌드 도구입니다. 타깃(target)과 그것이 필요로 하는 것을 선언하면, Make가 그것들을 순서대로 실행합니다 [9]. 노트북 규모의, 재현 가능하고, 처음부터 끝까지 이어지는 런에는 그것으로 정확히 충분합니다. 더 크고 분기하는 과학 파이프라인의 경우, 동일한 아이디어가 Snakemake 같은 Python 기반의 의존성 인식 워크플로 엔진으로 확장되어, 재현성 이야기를 워크스테이션에서 클러스터로 이어 줍니다 [10]. 우리는 Make에 머무릅니다. 독자가 이미 그것을 가지고 있고, 명령 표면을 정직하게 유지해 주기 때문입니다.
다음은 examples/Makefile에 있는 실제 최상위 Makefile입니다. 모든 타깃이 자신이 필요로 하는 Docker Compose 프로필(profile)을 문서화하고 있다는 점, 그리고 help가 각 타깃의 ## 주석으로부터 타깃 목록을 자동으로 나열한다는 점에 주목하세요. 명령 표면이 곧 문서입니다.
# examples/Makefile
COMPOSE := docker compose -f platform/compose/compose.yaml
PY := sim/.venv/bin/python
export DATABASE_URL ?= postgresql://bioproc:bioproc@localhost:5432/bioproc
venv: ## create the Python env and install the simulator (uv)
cd sim && uv venv --python 3.12 .venv && uv pip install --python .venv -e . "psycopg[binary]" "asyncua==2.0" scikit-learn
up: ## bring up the core stack (postgres+timescale, mosquitto, grafana)
$(COMPOSE) --profile core up -d
@echo "waiting for postgres..." && sleep 3
@until docker exec bioprocess-data-stack-postgres-1 pg_isready -U bioproc >/dev/null 2>&1; do sleep 2; done
@echo "core stack up."
seed: ## load the ISA-88/95 reference CHO line into postgres
docker exec -i -e PGPASSWORD=bioproc bioprocess-data-stack-postgres-1 \
psql -U bioproc -d bioproc -q < platform/db/seed/seed_cho_line.sql
data: ## (re)generate every dataset deterministically + MANIFEST.sha256
$(PY) -m bioproc_sim.generate --all
load: ## load the datasets into the running stack (historian + lab + genealogy)
$(PY) tools/load_datasets.py
처음부터 끝까지 이어지는 런은 단지 그 타깃들을 순서대로 실행하고, 이어서 맥락화·감사·분석 타깃을 실행하는 것뿐입니다. 다음은 저장소의 README.md에서 그대로 복사한, 문자 그대로의 캡스톤 시퀀스입니다.
make venv # Python env + the simulator (uv)
make data # generate every dataset deterministically + MANIFEST.sha256
make up # bring up the core stack (postgres+timescale, mosquitto, grafana)
make seed # load the ISA-88/95 reference CHO line
make load # load the datasets into the historian + lab tables
make contextualize # join time-series to batch phases (Ch 17)
make alcoa # verify the ALCOA+ audit hash chain (0 = intact)
make soft-sensor # train the Raman -> titer PLS soft-sensor (Ch 29)
make test # the whole suite (determinism + db + analytics)
각 줄은 책에서 나온 하나의 계층입니다. 데이터가 움직이는 것을 지켜보세요.
1단계 — 배치를 결정론적으로 생성하기 (make data)
make data는 마스터 시드(master seed) SIM_SEED=2026으로 python -m bioproc_sim.generate --all을 실행하므로, 14일짜리 트레이스(trace)는 여러분의 노트북에서나 지속적 통합(continuous integration, CI) 러너 — 변경마다 모든 점검을 다시 실행하는 자동화된 빌드 서버 — 에서나 바이트 단위로 동일합니다. 이것은 들리는 것보다 더 중요합니다. 재현성(reproducibility)은 "한 번 작동한 데모"와 필요할 때 다시 도출할 수 있는 산출물 사이의 차이이며, FAIR(찾을 수 있는Findable·접근 가능한Accessible·상호 운용 가능한Interoperable·재사용 가능한Reusable) 데이터의 찾을 수 있고·접근 가능하며·재사용 가능한(Reusable) 척추입니다 [11]. 이 장은 그 찾을 수 있고·접근 가능하며·재사용 가능한 척추를 세웁니다. 상호 운용 가능한(Interoperable) 다리 — 이 행들을 공유 어휘 RDF 지식 그래프로 바꾸는 것 — 는 별도로 전달되며, 동일한 배치가 그래프로 적재되는 곳은 의미론과 디지털 스레드입니다. 생성기는 MANIFEST.sha256을 쓰고, 나중에 make test가 모든 파일이 그것과 일치하는지 단언합니다. 시뮬레이터가 부동소수점 하나라도 어긋나면 CI는 요란하게 실패합니다.
이 단일한 결정론적 출처는 또한 배치 전체가 자기 자신과 일치하는 이유이기도 합니다. 인라인(in-line) 역가(세포가 만들어 낸 항체 농도, g/L 단위), 오프라인(offline) 분석 역가, 그리고 라만 스펙트럼은 모두 동일한 기저 동역학 상태에서 끌어내어집니다. 그 내부 일관성은 ALCOA+의 "일관성(Consistent)"입니다. 행복한 우연이 아니라 설계의 속성입니다 [3].
결정론은 바람이 아니라 속성이다
"재현 가능하다"고 말하기는 쉽지만 지키기는 어렵습니다. 캡스톤은 그것을 두 계층에서 점검되는 속성으로 만듭니다. 첫째, 모든 생성기는 단일 마스터 시드에서 난수를 끌어냅니다. bioproc_sim.generate는 무작위성을 SIM_SEED(기본값 2026)에서 도출하므로, 두 머신에서의 두 번의 런이 동일한 배열을 만들어 냅니다. 시뮬레이터 자체의 테스트가 정확히 그것을 — 두 번의 독립적인 생성에 대해 np.array_equal(a, b) 를 — 단언합니다. 둘째, 생성기는 자신이 쓰는 모든 파일을 hashlib.sha256(p.read_bytes()).hexdigest()로 해싱하여 MANIFEST.sha256에 담고, make test는 데이터셋을 다시 도출하여 그 매니페스트에 대조합니다. 어느 데이터셋에서든 부동소수점 하나가 어긋나면 다이제스트가 바뀌고, 비교는 실패하며, CI는 빨갛게 변합니다.
이것은 장식이 아니라 의미 있는 설계 선택입니다. 검토 가능한 기록이 단지 어떤 데이터셋이 아니라 그 데이터셋이라는 뜻입니다. 누구든 make data를 다시 실행하면 바이트 단위로 동일한 BATCH-2026-001을 얻으며, 그것이 작동하게 만든 FAIR의 재사용 가능한(Reusable) 척추입니다 [11]. 그것은 또한 정직한 선을 긋습니다. 시뮬레이터가 결정론적인 것은 그것이 시뮬레이터이기 때문입니다. 실제 계측기는 바이트 단위로 재현되지 않습니다. 실제 공장으로 전이되는 것은 물리의 문자 그대로의 반복성이 아니라 규율 — 고정된 입력, 해싱된 출력, 자동화된 점검 — 입니다.
2단계 — 진실의 기록처(record-of-truth)를 세우고 적재하기 (make up, make seed, make load)
make up은 코어 스택(core stack) — PostgreSQL + TimescaleDB, Mosquitto, Grafana — 을 부팅하고, Postgres가 pg_isready로 응답할 때까지 멈춰 기다립니다. PostgreSQL은 맥락화되고 서명된 기록과 감사 추적을 담는 관계형 저장소이며, 검토 가능한 배치 기록이 조립되는 기록 시스템(system of record)입니다 [12]. make seed는 ISA-88/95 참조 CHO 라인을 적용합니다. 기업→사이트→영역→유닛(unit) 계층, 레시피(recipe)와 그 단계(phase)들, 그리고 BR101 생산 바이오리액터(bioreactor)입니다.
make load는 examples/tools/load_datasets.py의 데이터셋 로더를 실행합니다. 이것은 7–16장이 조각조각 쌓아 올린 것을 단 한 번의 패스로 수행하는 바로 그 스크립트입니다. 고속 센서 판독값은 대량 COPY로 히스토리언에 스트리밍되고, 오프라인 실험실 결과는 일반 INSERT 경로를 통해 들어와 각 건마다 감사 트리거(trigger)가 실제로 발화하도록 합니다.
# examples/tools/load_datasets.py
def load_timeseries(conn) -> int:
df = pd.read_parquet(DATA / "fedbatch_timeseries.parquet")
buf = io.StringIO()
df[["ts", "tag", "value", "unit", "quality", "batch_id"]].to_csv(buf, index=False, header=False)
buf.seek(0)
with conn.cursor() as cur:
cur.execute("TRUNCATE ts.sensor_reading")
with cur.copy("COPY ts.sensor_reading (ts, tag, value, unit, quality, batch_id) "
"FROM STDIN WITH (FORMAT csv)") as copy:
copy.write(buf.read())
return len(df)
오프라인 로더는 쓰기 전에 일부러 애플리케이션 사용자를 설정합니다. 그래야 감사 추적이 각 결과를 익명의 데이터베이스 연결이 아니라 책임 있는 행위자에게 귀속시킬 수 있기 때문입니다. ALCOA+의 귀속 가능성(Attributable)입니다 [3]:
# examples/tools/load_datasets.py
def load_offline(conn) -> int:
df = pd.read_csv(DATA / "offline_assays.csv", parse_dates=["sample_time"])
n = 0
with conn.cursor() as cur:
cur.execute("SELECT set_config('app.user', 'loader', false)")
for _, r in df.iterrows():
cur.execute(
"INSERT INTO lab.sample (sample_id, batch_id, sample_time, sample_point, sample_type) "
"VALUES (%s,%s,%s,%s,'in_process') ON CONFLICT (sample_id) DO NOTHING",
(r.sample_id, r.batch_id, r.sample_time.to_pydatetime(), r.sample_point))
for col, (tid, unit) in OFFLINE_TESTS.items():
cur.execute(
"INSERT INTO lab.result (sample_id, test_id, value, unit, analyst, status) "
"VALUES (%s,%s,%s,%s,'auto','verified') ON CONFLICT DO NOTHING",
(r.sample_id, tid, float(r[col]), unit))
n += 1
return n
작업이 끝나면 한 줄짜리 영수증을 출력합니다. 로더는 캠페인 CSV 전체 — 실험실·출시·계보 합계에 대해서는 여섯 개 배치 모두 — 를 읽지만, 고속 센서 트레이스는 골든 배치(BATCH-2026-001) 하나뿐입니다.
loaded: 322560 sensor readings, 1344 offline results, 66 release results, 30 genealogy edges
이것은 골든 배치 BATCH-2026-001에 대한 322,560건의 고속 센서 행, 1,344건의 오프라인 결과(168건의 공정 중 샘플 × OFFLINE_TESTS의 8개 분석), hplc_results.csv에서 나온 66건의 출시 결과, 그리고 lot_genealogy.csv에서 나온 30개의 계보(genealogy) 엣지이며 — 뒤의 세 가지는 여섯 개 캠페인 배치 모두에 걸쳐 있습니다. 아래의 로트 계보 발췌는 BATCH-2026-001 하나에 대한 다섯 개의 엣지를 보여줍니다. 로더는 캠페인의 모든 배치에 대해 동등한 체인을 씁니다.
로더는 또한 로트 계보(lot genealogy)도 씁니다. 종균 배양(seed train)에서 바이오리액터로, Protein A 포획 풀(capture pool)로, 원료 의약품(drug substance)으로, 완제 의약품(drug product)으로 이어 가는 방향성 엣지입니다. 그 체인이야말로, 21 CFR 211 — 완제 의약품에 대한 미국 FDA의 cGMP 규정 — 이 완성된 배치를 그 구성 로트까지 추적할 것을 요구할 때 기대하는 바로 그것입니다.
batch_id,child,child_type,parent,parent_type
BATCH-2026-001,SEED-001,seed_train,WCB-CHO-001,wcb
BATCH-2026-001,BATCH-2026-001,bioreactor,SEED-001,seed_train
BATCH-2026-001,PApool-001,capture_pool,BATCH-2026-001,bioreactor
BATCH-2026-001,DS-001,drug_substance,PApool-001,capture_pool
BATCH-2026-001,DP-001,drug_product,DS-001,drug_substance
그 체인에서 짚어 둘 정직한 바이오공정 단순화가 하나 있습니다. 단 하나의 capture_pool → drug_substance 홉이 1권이 단계마다 짚어 가는 하류 정제 트레인 전체를 뭉뚱그립니다. 충실한 계보라면 각 단위 공정을 저마다의 자재 노드로 엮습니다. Protein A 풀은 잔류 응집체와 숙주 세포 DNA를 제거하는 두 번째의, 직교(orthogonal)하는 컬럼에서 연마(polishing)되고(연마 크로마토그래피), 이어서 두 개의 독립적인 바이러스 안전성 단계 — 저(低)pH 또는 세제 바이러스 불활성화와 20나노미터 바이러스 여과 — 를 거친 뒤, UF/DF가 그것을 농축하고 완충액을 교환하여 원료 의약품으로 만듭니다. 각 단계는 derivedFrom 엣지를 하나씩 더하고, 출시 관련 증거를 자신의 노드에 매답니다. 바이러스 여과 단계의 로그 감소값, UF/DF 사이클의 최종 농도와 다이아볼륨(diavolume) 수 같은 것들로, 여기서 SEC 출시 분석이 로트에 매달리는 것과 정확히 같은 방식입니다. 체인은 길어질 뿐, 구조적으로 달라지지 않습니다. 그것이 핵심입니다. 동일한 역추적이 다섯 홉에서도 열두 홉에서도 똑같이 작동합니다.
3단계 — 헐벗은 태그를 지식으로 바꾸기 (make contextualize)
히스토리언의 한 행 — ('2026-01-12T03:00:05Z', 'BR101.DO.PV', 41.7, '%sat', 192, 'BATCH-2026-001') — 은 그 자체로는 거의 무의미합니다. 이 여섯 필드는 정확히 히스토리언 열 (ts, tag, value, unit, quality, batch_id)입니다. 타임스탬프, 태그 BR101.DO.PV(장비 BR101, 용존 산소 센서 DO, 현재값present value PV), %sat 단위의 값 41.7, 양호한 읽기를 뜻하는 quality 플래그 192(히스토리언이 쓰는 OPC DA 관례: 192 양호, 64 불확실, 0 불량), 그리고 그것이 속한 배치입니다. 그러나 어느 배치인지, 어느 장비에서인지, 어느 레시피 단계 동안인지는 여전히 암묵적입니다. 플랫폼의 요점 전체는 그 결합에 답하는 것입니다. 그 결합은 examples/platform/db/60-views.sql에 살아 있으며, 끝에서 검사로 품질을 끼워 넣는 것이 아니라 런에 대한 공정 중 이해 위에 품질을 쌓아 올림으로써 PAT 비전을 현실로 만드는 동작입니다 [6].
-- examples/platform/db/60-views.sql
-- A reading with its full batch + phase context.
CREATE OR REPLACE VIEW s88.v_batch_sensor AS
SELECT r.ts, r.tag, r.value, r.unit, r.quality, r.batch_id,
b.product_id, b.recipe_id, b.unit_id,
bp.phase_id, ph.name AS phase_name
FROM ts.sensor_reading r
JOIN s88.batch b ON b.batch_id = r.batch_id
LEFT JOIN s88.batch_phase bp ON bp.batch_id = r.batch_id
AND r.ts >= bp.start_ts AND (bp.end_ts IS NULL OR r.ts < bp.end_ts)
LEFT JOIN s88.phase ph ON ph.phase_id = bp.phase_id;
bp.start_ts/bp.end_ts에 대한 시간적 결합(temporal join)이 영리한 부분입니다. 각 판독값은 그 순간에 활성이던 그 어떤 ISA-88 단계(ISA-88은 결합된 ISA-88/95 모델의 배치 제어 절반으로, 레시피와 단계를 정의하며, ISA-95는 장비 계층을 공급합니다)에든 대응됩니다. 두 번째 뷰인 s88.v_phase_summary는 그것을 단계별·태그별 통계로 롤업(roll up)하여 "골든 배치(golden batch)" 참조가 되게 합니다. make contextualize는 단순히 첫 번째 뷰를 태그 하나에 대해 질의하고 단계로 그룹화합니다.
select phase_name, count(*) n, round(avg(value)::numeric,1) avg_DO
from s88.v_batch_sensor where batch_id='BATCH-2026-001' and tag='BR101.DO.PV'
group by phase_name order by min(ts);
phase_name | n | avg_do
------------+------+--------
Inoculate | 720 | 39.8
Growth | 9360 | 38.9
Production | 8640 | 35.1
Harvest | 1440 | 34.4
네 개의 단계는 정확히 시드가 정의하는 ISA-88 단계입니다 — Inoculate, Growth, Production, Harvest — 그리고 용존 산소는 배양물이 자라고 세포가 더 많은 산소를 끌어쓰면서 단계를 거쳐 부드럽게 아래로 흘러내립니다(39.8 → 38.9 → 35.1 → 34.4 %sat). 그동안 컨트롤러는 내내 그것을 설정점 가까이에 붙들어 둡니다. 이것은 극적인 붕괴가 아니라 완만하고 대략 평탄하다가 하강하는 추세입니다. 그리고 그것이야말로 잘 제어된 유가식 배치의 핵심입니다. 중요한 것은, 이제 그 추세가 322,560개의 익명 행에 파묻혀 있지 않고 단계별로 읽을 수 있게 되었다는 점입니다. 이것이 예외 검토(review-by-exception) 워크플로가 읽어 들이는 맥락화된 기록입니다.
DO는 일부러 이 런에서 가장 극적이지 않은 창입니다 — 빡빡하게 제어되는 핵심 공정 매개변수(critical process parameter, CPP)이지요. BATCH-2026-001의 오프라인 분석은 그 행들 뒤의 더 큰 이야기를 들려줍니다. 생존 세포 밀도(viable cell density, VCD — 밀리리터당 살아 있는 세포 수, 곧 얼마만큼의 바이오매스가 일하고 있는지에 대한 척도)는 ~0.34에서 22.07e6 cells/mL로 올라가고(e6은 ×10⁶을 뜻하는 과학적 약식 표기이므로 22.07e6은 밀리리터당 2,207만 개 세포입니다), 생존율(그 세포들 가운데 아직 살아 있는 비율)은 노화하는 배양물이 일부러 제품 쪽으로 밀어붙여지면서 수확 무렵까지 건강한 96.6%에서 68.0%로 떨어지며, 역가는 5.877 g/L까지 누적됩니다 — 그리고 그것이 바로, 자라나는 바이오매스가 더 많은 산소를 끌어쓰면서 DO가 부드럽게 아래로 흘러내리는 이유입니다.
시간적 결합: 판독값을 그것의 활성 단계에 맞추기
LEFT JOIN s88.batch_phase 절을 가까이 들여다보세요. 맥락화 전체가 그것에 기대고 있기 때문입니다. r.ts >= bp.start_ts AND (bp.end_ts IS NULL OR r.ts < bp.end_ts). 이것은 판독값의 타임스탬프와 s88.batch_phase의 창(window) 사이의 반열린 구간(half-open interval) — [start, end) — 매칭입니다. 시드는 골든 배치에 네 개의 창을 씁니다. 2026-01-05의 Inoculate 00:00–12:00, 2026-01-12까지의 Growth, 2026-01-18까지의 Production, 2026-01-19까지의 Harvest입니다. 판독값은 그 자신의 순간에 활성이던 그 어떤 단계로든 도장이 찍히므로, 동일한 BR101.DO.PV 태그가 한 타임스탬프에서는 "Growth"에, 다른 타임스탬프에서는 "Production"에 속합니다. 고속 행 위에 단계 열(column)을 결코 따로 써넣을 필요가 없습니다.
세 가지 세부가 이를 견고하게 만듭니다. 반열린 구간(시작은 >=, 끝은 <)은 단계 경계 위의 판독값이 정확히 하나의 단계에 떨어지게 합니다. 둘도 아니고 영도 아닙니다. end_ts IS NULL 분기는 현재 진행 중인 단계를 그 끝이 알려지기 전에도 매칭 가능하게 유지합니다. 가동 중인 배치에 중요합니다. 그리고 그것은 LEFT JOIN이므로, 매칭되는 창이 없는 판독값도 사라지지 않고 NULL phase_name으로 뷰에 살아남습니다. 데이터베이스 테스트는 네 개의 서로 다른 비-null 단계를 합격 조건으로 삼습니다(count(distinct phase_name) >= 4). 결합을 틀리게 하면 — 닫힌 구간, 내부 결합(inner join) — 경계 판독값을 조용히 이중 계산하거나 어느 창에도 들지 않는 것들을 떨궈서, 단계별 평균이 조용히 거짓말을 하게 됩니다.
두 번째 뷰인 s88.v_phase_summary는 첫 번째 위에 쌓입니다. phase_name IS NOT NULL로 필터링하고, 판독값을 배치별·단계별·태그별 count/avg/min/max로 롤업합니다. 그 요약이 운영자가 새 런을 겹쳐 보는 "골든 배치" 참조입니다. 그리고 그것은 시간적 결합이 먼저 모든 판독값을 제자리 단계에 놓았기 때문에 비로소 존재합니다.
4단계 — 기록이 변조 증명적(tamper-evident)임을 입증하기 (make alcoa)
배치 기록은 그것이 조용히 변경되지 않았다고 신뢰할 수 있을 때에만 서명할 가치가 있습니다. Annex 11은 전산화 시스템이 안전하고 시간 도장이 찍힌 감사 추적을 생성하고, 처분(disposition) 결정 전에 그 추적을 검토 가능하게 만들 것을 요구합니다 [2]. examples/platform/db/50-alcoa.sql에 있는 우리의 감사 계층은 규제 대상 테이블에 대한 모든 변경을 해시 체인(hash-chain)으로 묶어, 이후의 어떤 편집이든 체인을 깨뜨리게 합니다.
-- examples/platform/db/50-alcoa.sql
-- chain hash = H(prev_hash || payload)
v_hash := encode(digest(
coalesce(v_prev, '') || TG_TABLE_NAME || TG_OP ||
coalesce(v_old::text, '') || coalesce(v_new::text, '') ||
coalesce(v_app, '') || clock_timestamp()::text, 'sha256'), 'hex');
make alcoa는 검증기를 실행하고 0개의 끊긴 연결을 기대합니다.
select count(*) as broken_links from audit.verify_chain();
broken_links
--------------
0
이 책이 고집하는 정직함이 스키마 주석 자체에 적혀 있습니다. 트리거를 비활성화할 수 있는 슈퍼유저는 여전히 이를 우회할 수 있다. 해시 체이닝은 변조를 명백하게 만드는 것이지, 불가능하게 만드는 것이 아니다. 그 어떤 오픈 소스 데이터베이스도 21 CFR Part 11 — 전자 기록과 전자 서명에 대한 미국 FDA 규칙으로, 위에서 인용한 21 CFR 211 cGMP 배치 기록 요건과는 별개의 규칙 — 을 즉시 갖춰서 건네주지 않습니다. 이 계층이 여러분에게 주는 것은 탐지 가능성(detectability)입니다. 동시대 기록이 온전하다는 것에 대한, 독립적이고 재계산 가능한 점검입니다 [3]. 검증된 시스템의 부담 — 변경 관리, 지명된 한 사람을 승인 또는 검토 같은 특정 의미에 법적으로 묶는 전자 서명, SOP(표준 작업 절차), GAMP 5 생애주기 — 은 운영자의 몫이며, 그것이 바로 다음 장이 점수 매기는 하이브리드의 마지막 한 걸음입니다.
감사 체인 행(audit-chain row)의 해부
체인은 그것이 추가하는 행만큼만 신뢰할 수 있습니다. 그러니 그 행 하나를 해부해 볼 가치가 있습니다. 오프라인 로더가 첫 INSERT INTO lab.result를 실행하는 순간 audit.log_change() 트리거가 쓰는 바로 그 행입니다. 아래의 모든 필드는 50-alcoa.sql의 CREATE TABLE audit.change_log 정의와 트리거 본문에서 곧장 나온 것이며, 어느 것도 장식이 아닙니다.
audit.change_log의 한 행: 누가 무엇을 언제 어떤 이전·이후 값으로 바꾸었는지를, 그 앞의 행에 해시 체인으로 묶어서 기록합니다. 그리고 verify_chain()이 실제로 점검하는 것의 정직한 한계도 함께 담습니다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
행을 위에서 아래로 읽으면 ALCOA+ 속성이 하나씩 떨어져 나옵니다. seq는 bigint GENERATED ALWAYS AS IDENTITY이므로 행은 오직 순서대로만 추가될 수 있습니다. ts는 clock_timestamp()를 기본값으로 갖습니다. 둘러싼 트랜잭션의 시작 시각이 아니라 쓰기가 일어난 실제 벽시계 순간이며, 그것이 항목을 동시대적으로 만듭니다. 트리거는 db_user(current_user, 여기서는 bioproc 연결)와 app_user를 함께 포착합니다. app_user는 로더가 쓰기 전에 SELECT set_config('app.user', 'loader', false)로 설정하는 값으로, 익명의 연결이 아니라 책임 있는 행위자를 지목하는 ALCOA+의 귀속 가능성(Attributable)입니다. table_name과 action은 무엇이 바뀌었고 어떻게 바뀌었는지를 기록하며(lab.result / INSERT), row_key는 batch_id 다음 sample_id의 coalesce로, 기록으로 되돌아가는 결합 키입니다. old_row와 new_row는 완전한 jsonb 스냅숏입니다. 삽입 시에는 NULL과 to_jsonb(NEW)이므로, 원본(Original) 값과 그 변경이 둘 다 온전히 보존됩니다.
마지막 필드인 row_hash가 체인 그 자체입니다. 트리거는 그것을 SHA-256(prev_hash || table || action || old || new || app_user || ts)로 계산하는데, 여기서 prev_hash는 바로 앞 행의 row_hash입니다. 더 앞쪽의 어떤 행을 편집하면 그 뒤의 모든 해시가 재현되기를 멈춥니다. 변조가 명백해지는 것입니다. make alcoa는 audit.verify_chain()을 실행하고 0개의 끊긴 연결을 기대합니다.
다만 그림이 보라색으로 그려 둔 한계에 주목하세요. 스키마 주석에서 그대로 옮긴 것입니다. verify_chain()은 연결 일관성만 점검합니다. 각 저장된 prev_hash를 앞 행의 저장된 row_hash와 비교할 뿐, 페이로드로부터 row_hash를 다시 계산하지는 않습니다. 그래서 기존 행의 old_row·new_row·app_user에 대한 조용한 편집은 이 함수로 잡히지 않으며, 트리거를 비활성화할 수 있는 슈퍼유저는 그것을 통째로 우회합니다. 다시 그 정직한 한 줄입니다. 이 계층은 Part 11이 아니라 탐지 가능성을 제공합니다. 그리고 그 한계를 마케팅 문구가 아니라 스키마에 적어 두는 것이 이 책 전체의 자세입니다.
5단계 — 공정 데이터로부터 출시 관련 품질을 예측하기 (make soft-sensor)
make soft-sensor는 examples/analytics/soft_sensor.py에 있는 라만→역가 부분 최소제곱(Partial Least Squares, PLS) 모델을 학습시킵니다. PLS는 회귀(regression) 방법으로, 서로 상관된 많은 입력(여기서는 수백 개의 라만 스펙트럼 강도)으로부터 수치 예측(여기서는 역가)을 학습합니다. 인라인 라만 스펙트럼으로부터 역가를 학습하고 모델이 한 번도 학습한 적 없는 떼어 둔(held-out) 조각에서 검증하며, 단단한 하한선을 통과해야 합니다. 그러지 못하면 CI는 실패합니다. 두 가지 표준 점수가 적합도를 요약합니다. R2(결정 계수coefficient of determination로, 1.0이 완벽한 적합이며 R2 > 0.85 관문은 모델이 역가 변동의 최소 85%를 설명할 것을 요구합니다)와 RMSE(역가와 동일한 g/L 단위로 표현되는 전형적인 예측 오차)입니다.
# examples/analytics/soft_sensor.py
if __name__ == "__main__":
m = train()
print(f"PLS soft-sensor (titer from Raman): R2={m['r2']} RMSE={m['rmse_g_L']} g/L "
f"({m['n_components']} comps, {m['n_wavenumbers']} wavenumbers, "
f"{m['n_train']} train / {m['n_test']} test)")
assert m["r2"] > 0.85, f"soft-sensor R2 too low ({m['r2']}): dataset not predictive"
print("ASSERT ok: R2 > 0.85 — the Raman dataset is genuinely predictive of titer.")
PLS soft-sensor (titer from Raman): R2=0.9923 RMSE=0.1498 g/L (6 comps, 701 wavenumbers, 235 train / 101 test)
이것이 실시간 출시 시험(real-time release testing, RTRT)의 개념적 핵심입니다. 최종 제품 시험만이 아니라 공정 중 측정값과 공정 데이터로부터 제품 품질을 평가하는 것, 즉 측정된 스펙트럼이 역가 분석을 대신할 수 있게 하는 모델입니다 [4]. 여기서 우리는 검증된 소프트 센서를 주장하는 것이 아닙니다(라만 모델 하나가 재학습 없이 규모나 세포주를 가로질러 전이되는 일은 드뭅니다). 그리고 여기의 R2=0.9923은 단일 배치에서 나온 배치 내 무작위 떼어 두기(hold-out)이므로 실제 성능을 과대평가합니다 — 진정한 보정(calibration)에는 여러 배치에 걸친 leave-one-batch-out 검증이 필요합니다. 우리는 RTRT가 필요로 하는 데이터 경로 — 스펙트럼이 들어가고, 방어 가능한 예측이 나오며, 기록되고 재현 가능한 — 가, 기록의 나머지가 묘사하는 동일한 배치 위에서 처음부터 끝까지 가동된다는 것을 보여 줍니다. 모델링 그 자체 — PLS란 무엇인지, 교차 검증과 적용 가능 영역(applicability domain)이 소프트 센서를 어떻게 정직하게 유지하는지 — 는 5권, 바이오 제조를 위한 머신러닝과 AI에서 전개됩니다.
0.99가 교육용 숫자인 이유, ML의 언어로
그 R2가 왜 과대평가인지 정확히 짚어 둘 가치가 있습니다. 그 이유야말로 바이오공정 모델이 스스로에게 거짓말을 하는 가장 흔한 방식이며, 우리가 방금 적재한 계보가 그 해법이기 때문입니다. 행 단위 무작위 떼어 두기는 한 배치의 336개 스펙트럼을 학습과 테스트로 쪼개지만, 한 배치 안의 스펙트럼은 독립적이지 않습니다. 몇 분 간격의 연속된 인라인 판독값은 동일한, 천천히 변해 가는 배양 상태에서 끌어내어진 사실상 중복본입니다. 그래서 테스트 집합은 사실상 학습 집합의 복사본이고, 모델은 자신이 거의 다 본 데이터로 채점됩니다. 이것이 데이터 누수(data leakage)이며, 점수를 환상 쪽으로 부풀립니다. 정직한 분할은 그룹별, leave-one-batch-out 교차 검증입니다. 한 배치의 모든 스펙트럼이 학습이나 테스트 어느 한쪽으로 통째로 가므로, 모델은 진정으로 본 적 없는 런에서 채점됩니다. 그리고 그 그룹화 키가 바로 이 기록이 지니는 derivedFrom 계보 — 어느 행이 형제(sibling)인지를 알려 주는, 공유 WCB-CHO-001로 거슬러 가는 그 순회 — 입니다. 모델과 검증 장은 그것을 GroupKFold와 중첩 교차 검증으로 만들고, 데이터 장은 묶이는 제약이 행의 수가 아니라 독립적인 배치의 수 — 매번 수 주의 비용으로 하나씩 늘어나는, 336개 행이 아니라 여섯 개 배치 — 라는 콜드 스타트 현실을 틀 짓습니다.
실제 RTRT 방법이 필요로 하는, 이 교육용 런에는 없는 두 가지 규율이 같은 기록에 매답니다. 배포된 모델은 적용 가능 영역(applicability domain) — 그것이 적격성을 인정받은 입력 영역(세포주, 규모, 원자재 로트, 운전 범위)으로, 그 바깥에서는 예측이 정의상 외삽(extrapolation)이며 신뢰되지 않는 — 을 선언해야 합니다. 그래야 소프트 센서가 자신의 학습 집합과 닮지 않은 배치에 대해서는 추측하기를 거부할 수 있습니다. 그리고 일단 가동에 들어가면 모델 드리프트(model drift, 학습한 관계가 옮겨 가면서 예측기가 낡아 가는 것)와 공정 드리프트(process drift, 살아 있는 세포가 런마다 진짜로 떠도는 것으로, 디지털 스레드가 보존해야 할 실제 제조 신호)를 구별해야 합니다. 둘을 뭉뚱그리면 모니터가 헛경보를 울리거나 진짜 변화를 놓치기 때문입니다. MLOps 장은 그 둘을 가려내는 선행(라벨 없는 입력 분포) 탐지기와 후행(잔차 관리도) 탐지기를 구축합니다. 끝으로, 검증된 소프트 센서는 그 자체로, 그 계보(lineage)가 배치의 계보와 같은 그래프에 속하는 거버넌스 대상입니다. 그것이 어느 데이터셋 해시로 학습했는지, 어느 버전이 가동에 고정되어 있는지, 어느 CQA를 채점했는지가 — 다른 어떤 것과도 같은 트리플이므로 — 감사가 출시된 로트에서 그것을 건드린 정확히 그 동결된 모델까지 걸어갈 수 있습니다. 그것이 5권 MLOps 장이 기본값으로 삼는 검증된 대상으로서의 모델 규율이며, 여기서 make soft-sensor가 출시 방법이 아니라 데이터 경로 시연인 이유입니다.
배치 기록이 담아야 하는 것
우리는 다섯 계층을 가동했습니다. 이제 그 조립된 산출물이 규제 당국에게 실제로 무엇을 빚지고 있는지 묻습니다. 21 CFR 211.188은 구체적입니다. 배치 생산 및 제어 기록은 마스터 생산 기록을 재현해야 하고, 날짜 및 관련된 개인의 신원과 함께 각 중요한 단계가 수행되었음을 문서화해야 합니다 [1]. 그것은 요구되는 내용의 체크리스트이며, 우리가 조립한 BATCH-2026-001을 그것에 대조하여 필드 영역 하나하나 들여다볼 가치가 있습니다.
검토 가능한 배치 기록의 해부
검토 가능한 기록은 테이블 하나가 아닙니다. 그것은 우리가 구축한 계층들에서 꿰맨 여섯 개의 영역이며, 각각이 서로 다른 요구를 충족하고 서로 다른 테이블 또는 뷰에서 나옵니다. 책 앞부분에서 히스토리언 판독값 하나를 해부했던 방식으로 그것을 해부하면 그 대응이 명시적으로 드러납니다.
조립된
BATCH-2026-001 기록을 여섯 개의 필드 영역으로 해부한 것 — 각 영역은 하나의 21 CFR 211.188 요소와, 그것을 공급하는 출처 테이블 또는 뷰에 대응됩니다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
영역 하나하나, 각 조각이 어디에서 나오고 어떤 요구에 답하는지는 다음과 같습니다.
- 레시피 + 장비 맥락 —
s88.recipe(CHO-MAB-001v1)와BR101까지 내려가는s88.enterprise→site→area→unit계층. 이것이 규정이 첫머리에 두는 마스터 기록의 재현입니다. 어느 레시피, 어느 장비, 어느 버전인가. - 단계별 인라인 트레이스 —
s88.v_batch_sensor와 그v_phase_summary롤업. 이것이 각 중요한 단계가 수행되었다는 동시대적 증거이며, 익명 행의 벽이 아니라 단계에 날짜와 귀속이 붙어 있습니다. - 규격 대비 결과 —
lab.result를lab.test에 결합한 것으로,lab.test가spec_low/spec_high를 담으므로 각 값이 맥락 속에서 PASS 또는 FAIL로 렌더됩니다. 이것이 공정 중 시험과 완제품 시험을 모두 포괄합니다. - 로트 계보 —
s88.genealogy, 이 배치에 대한 다섯 개의 방향성child → parent엣지. 이것이 211.188(그리고 211.184)이 기대하는 구성 로트로의 역추적입니다. - 감사 추적 —
verify_chain() = 0인audit.change_log, 위에서 해부한 변조 증명적이고 검토 가능한 추적. - 뒷받침 분석 — 라만→역가 PLS 모델(R-제곱 0.9923), 처분 결정을 뒷받침하는 데이터.
그림의 초록색 블록이 단언하는 바에 주목하세요. 여섯 개 영역이 모두 존재하고 서명되어 있으면, 품질 부서는 322,560개의 행을 읽지 않습니다. 시스템이 일탈만을 표면화하는 가운데, 예외로 검토합니다. 그리고 그것이 함께 담는 정직한 단서에도 주목하세요. 데이터는 진정으로 ALCOA+이지만, 그것을 둘러싼 검증된 시스템과 절차는 여전히 하이브리드의 마지막 한 걸음입니다.
동일한 기록을, 트리플로, 셰이프로, 역량 질문으로
여섯 개 영역은 여기서 SQL로 꿰매어집니다. 진실의 관계형 기록에는 그것이 정확히 옳습니다. 그러나 동일한 산출물에는, 이 책의 의미론과 디지털 스레드 장이 구축하고 4권이 형식화하는, 두 번째의 상보적 표현이 있습니다. 계보 엣지와 출시 CQA는 또한 RDF 트리플(triple) — 주어-술어-목적어의 사실 — 이기도 하므로, 여러분이 방금 다섯 개의 CSV 행으로 적재한 계보는 동등하게, 지식 그래프 안의 derivedFrom 엣지 체인입니다. 원료 의약품 로트와 그 단량체 결과는 bp:DS-001 bp:derivedFrom bp:PApool-001과 bp:DS-001 bp:monomerPct 98.611이 되고, 211.188이 손으로 추적하는 계보는 단 한 번의 SPARQL 속성 경로(property-path) 순회 — 체인을 셀 뱅크까지 거슬러 따라가는 bp:DS-001 (bp:derivedFrom)+ ?step — 가 됩니다 [12]. 그 순회가 역량 질문(competency question)입니다. 어휘가 반드시 답해야 하는 질문을 그 기대 결과와 짝지어, 모델이 기계적인 합격/불합격으로 채점되게 하는 것입니다. 그것은 4권의 역량 질문을 질의로 모음에 CQ-01로 거의 그대로 등장하며, 거기서 derivedFrom 전이 관계가 그 역추적을 공짜로 만들어 줍니다.
출시 점검도 같은 이중생활을 합니다. 우리가 lab.test 규격 한계에 대한 SQL 결합으로 렌더하는 합격/불합격 패널은, RDF에서는 닫힌 세계(closed-world) 관문 — SHACL(Shapes Constraint Language) 노드 셰이프(node shape) — 입니다. SQL이 부재를 침묵으로 두는 곳에서, SHACL bp:ReleaseShape는 모든 출시된 로트가 요구되는 각 CQA에 대해 정확히 하나의 규격 내 값과 서명을 지녀야 한다고 단언합니다. 그래서 누락된 무균 시험 결과나 서명되지 않은 출시는 빈 칸이 아니라 위반(violation)입니다. 4권의 출시 관문과 SHACL 장이 도는 바로 그 구분입니다. BATCH-2026-001의 패널에 대한 그 셰이프는 그것이 부호화하는 규격처럼 읽힙니다.
# Illustrative — the release panel as a SHACL shape (see /ontology/the-release-gate-and-shacl).
bp:ReleaseShape a sh:NodeShape ;
sh:targetClass bp:DrugSubstance , bp:DrugProduct ;
sh:property [ sh:path bp:monomerPct ; sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ; sh:minInclusive 95.0 ] ;
sh:property [ sh:path bp:hcpPpm ; sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ; sh:maxInclusive 100.0 ] ;
sh:property [ sh:path bp:approvedBy ; sh:minCount 1 ] .
요점은 캡스톤을 RDF로 다시 짓자는 것이 아닙니다. 관계형 저장소가 곧 진실의 기록이며, 그래프는 변경 관리 하에 다시 적재되지 않으면 어긋나는 파생 뷰입니다. 요점은, 우리가 조립한 맥락화된 기록, app_user 귀속, 그리고 해시 체인의 PROV-O 식 "누가 무엇을 바꾸었는가"마저도, 두 번째 시스템이 같은 것을 의미해야 하는 순간 공유된 기계 판독 가능 어휘로 대응된다는 점입니다. 이 장이 일부러 의미론 장에 넘기는 FAIR의 상호 운용 가능한(Interoperable) 다리가 바로 그것입니다. SHACL의 완전성은, 우리의 make alcoa 관문처럼, 기록이 잘 형성되어 있고 존재함을 입증하는 것이지 그것이 참임을 입증하는 것이 아닙니다. 그럴듯한 규격 내 거짓은 둘 다 통과합니다. 그 정직한 한계 — 정확성이 아니라 완전성 — 는 ALCOA+ 절이 그은 바로 그것이며, 이제 온톨로지 자신의 용어로 진술된 것입니다.
이 조립된 기록은 삼부작이 처음부터 따라온 하나의 대상에 대한 세 번째 시선입니다. 1권은 배치 전체를 제조의 s88 척추로 틀 지었습니다. BATCH-2026-001을 실제로 만들어 내는 물리적 유가식 CHO 런입니다. 2권은 그 런을 질의 가능한 배치 계보, 곧 디지털 스레드(digital thread)로 바꾸었습니다. 로트 대 로트 추적과 그것을 온전히 유지하는 미해결 과제입니다. 이 장은 그 계보가 개념이기를 그치고, 여러분이 방금 실행한 s88.genealogy 엣지와 s88.v_batch_sensor 결합이 되는 곳입니다. 물리적 단계와 그 데이터 포인트를 구현하는 코드와 SQL입니다.
감사 추적 검토가 실제로 중요한 이유 — 현장 실패의 증거
변조 증명과 감사 추적 검토가 데이터 백본에 과잉 설계가 아닌지 물어볼 만합니다. 점검 기록은 그렇지 않다고 말합니다. 2010–2020년에 걸쳐 제약 제조사에 발부된 FDA 경고장(Warning Letter)의 후향적 분석은, 문서화 및 데이터 무결성 결함이 지배적인 cGMP 인용 범주였음을 — 경고장 다섯 건 중 약 한 건이 공정 검증 및 품질 관리 결함과 나란히 문서화를 주요 결함으로 지목한 정도로 — 발견했습니다 [13]. 반복되는 구체적 사항들은 정확히 이 계층이 겨냥하는 것들입니다. 흔적 없이 변경될 수 있었던 기록, 처분 전에 결코 검토되지 않은 감사 추적, 사람에게 귀속되거나 그 출처에 묶이지 못한 결과입니다.
그래서 감사 체인 행은 값만이 아니라 app_user, old_row/new_row, 그리고 prev_hash 연결을 지니며, make alcoa가 뒤늦은 생각이 아니라 관문(gate)인 것입니다. 동반 저장소는 그 실패에 인접한 경로를 직접 연습하기까지 합니다. test_audit_captures_update 테스트는 app.user와 app.reason을 설정하고, lab.result 행에 대해 UPDATE를 수행하며, 그 동작이 이전·이후 값과 함께 UPDATE로 기록되었음 그리고 verify_chain()이 여전히 0을 반환함을 — 즉 정당하고 귀속된 정정이 온전히 포착되고 체인을 그대로 둠을 — 단언합니다. 점검관이 가장 자주 빠져 있다고 발견하는 바로 그것을, 이 테스트는 존재한다고 입증합니다.
맥락 속의 출시 결과
여섯 개 영역을 하나로 꿰매면, 품질 부서가 실제로 행동의 근거로 삼을 수 있는 무언가가 생깁니다. 출시 결과는 규격 한계를 담은 lab.test 행에 대응하여 lab.result에 자리하므로, 검토자는 합격/불합격을 맥락 속에서 봅니다. 각 시험명은 항체가 충족해야 하는 품질 속성으로 해독됩니다. SEC_monomer_pct와 SEC_HMW_pct는 크기 배제 크로마토그래피(size-exclusion chromatography, SEC)에서 나오며, 온전한 단량체(monomer) 대 원치 않는 고분자량(high-molecular-weight, HMW) 응집체의 백분율을 보고합니다. 양이온 교환 크로마토그래피(cation-exchange chromatography, CEX)에서 나온 CEX_main_pct는 주 전하 변이체(charge-variant) 분율을 보고합니다. 그리고 HCP_ng_per_mg는 잔류 숙주 세포 단백질(host-cell protein, HCP) 불순물을 제품 밀리그램당 나노그램 단위로 측정합니다.
batch_id | test | value | unit | spec_low | spec_high | result
----------------+-----------------+--------+-------+----------+-----------+--------
BATCH-2026-001 | SEC_monomer_pct | 98.611 | % | 95.0 | 100.0 | PASS
BATCH-2026-001 | SEC_HMW_pct | 1.287 | % | 0.0 | 3.0 | PASS
BATCH-2026-001 | CEX_main_pct | 70.686 | % | 60.0 | 80.0 | PASS
BATCH-2026-001 | HCP_ng_per_mg | 28.203 | ng/mg | 0.0 | 100.0 | PASS
모든 계층이 맥락화되고 서명되어 있으므로, 그 예외 검토 패스는 표면화할 구체적인 무언가를 갖습니다. 시스템은 일탈만을 — 7일째 온도 일탈, 추세를 벗어난 대사물(예: 후반부 암모니아가 ~10 mM를 넘어서는 것), 검증되지 않는 감사 연결 — 표시하고, 사람의 주의는 필요한 곳으로 갑니다. 이 전자 생산 기록·감사 추적 검토 모델은 정확히 GAMP 5(2판)가 준수 전산화 시스템에 대해 묘사하는 바입니다 [7]. 그리고 그 전체 산출물은, 제어 상태에 머무르며 생애주기 전반에 걸쳐 출시를 뒷받침하도록 만들어진 제약 품질 시스템 안의 제품 실현(product-realization) 출력입니다 [5].
전체 그림
독자가 구축한 모든 계층이 하나의 런으로 배선됩니다. make data는 결정론적 배치를 생성하고, make load는 그것을 TimescaleDB 히스토리언과 실험실 테이블로 부채꼴처럼 펼쳐 넣으며, make contextualize는 태그를 ISA-88 단계에 결합하고, make alcoa는 기록이 변조 증명적임을 입증하며, make soft-sensor는 스펙트럼을 출시 관련 예측으로 바꿉니다. 그리고 검토 가능하고 FAIR에 부합하는 데이터셋 하나로 끝맺습니다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
데이터가 서비스들 사이에서 실제로 움직이는 방식대로, 동일한 흐름을 시퀀스로 나타내면 다음과 같습니다.
왜 중요한가
이 책의 대부분에서 각 계층은 홀로 서 있었습니다. 여기에 수집기 하나, 저기에 스키마 하나, 또 어딘가에 대시보드 하나 식이었습니다. 계층 하나를 데모하기는 쉽습니다. 어려운 것은, 센서가 내보낸 단 하나의 값이 검토자 앞에 도착할 때까지도 여전히 귀속 가능하고, 맥락화되어 있으며, 검증 가능하도록 그것들을 상호 연결하는 일입니다. 캡스톤은 그것들이 그렇게 한다는 증명입니다. 그것은 "센서에서 제출까지(sensor to submission)"를 구호에서 노트북으로 몇 분 만에 실행할 수 있는 명령어 시퀀스로 응축하며, make test는 깨끗한 CI 러너 위에서 그 전체를 다시 실행합니다. 빌드가 열망이 아니라 진정으로 구현 가능하다는, 이 책의 적대적(adversarial) 증거입니다.
그것은 또한 이 삼부작의 논증이 안착하도록 만듭니다. 첫 번째 책은 공정을 설명했고, 두 번째 책은 거버넌스를 설명했으며, 이 책은 그것을 구축합니다. 그리고 오픈 소스가, 결정적 경로(critical path)에 단 하나의 독점 구성 요소도 없이, 유가식 CHO + Protein A 런을 바이오리액터에서부터 검토 가능한 배치 기록까지 끝까지 실어 나를 수 있음을 보여 줍니다.
실제 현장에서는
실제 출시는 make test보다 무겁습니다. 여기의 맥락화된 데이터셋은 그 데이터 속성에서 진정으로 ALCOA+이지만, 규제 당국이 허가하는 것은 데이터셋이 아니라 검증된 시스템과 절차입니다. 해시 체인은 변조 증명을 입증하는 것이지 Part 11 전자 서명 준수를 입증하는 것이 아닙니다. 소프트 센서는 교육용 모델이지 검증된 RTRT 방법이 아닙니다. 오프라인 결과는 시뮬레이션된 것이지 분석가가 입회한 것이 아닙니다. 한 현장에서의 실제 예외 검토는 정확히 이런 종류의 데이터 백본(backbone) 위에 MES 전자 배치 기록, 검증된 LIMS, 그리고 품질 관리 시스템을 겹겹이 쌓아 올립니다.
여기서 파일럿 규모 현행 우수 제조 관리 기준(current Good Manufacturing Practice, cGMP) 제조 시설이 중요해집니다. 그것은 개방형 데이터 백본이 규제 대상 제품을 만드는 검증되고 물리적인 현실과 만나는 그런 종류의 장소입니다. 이 책이 구축하는 데이터 아키텍처는 그런 시설이 그 위에서 가동되는 기질(substrate)이며, 정직한 하이브리드 경계 — 약 80%는 오픈 소스, GxP의 마지막 한 걸음은 상용·검증 시스템 — 가 정확히 실제 파일럿 라인이 협상하는 경계입니다. 다음 장은 그 경계를 도구 하나하나 점수 매깁니다.
실제 출시가 더하는 것: 적격성 평가와 기술 이전
시뮬레이션된 런이 보여 줄 수 없는 두 가지 제조 현실을 짚어 둘 가치가 있습니다. 그것들이야말로 검증된 부담이 실제로 떨어지는 곳이기 때문입니다. 첫째는 적격성 평가(qualification)입니다. BATCH-2026-001의 단 한 바이트가 출시에 계산되기 전에, 그것을 만들어 낸 시스템들은 크로마토그래피 스키드가 거치는 것과 동일한 IQ/OQ/PQ 사다리를 통과합니다. 설치 적격성 평가(IQ)는 히스토리언, 로더, 데이터베이스가 명시된 버전으로 배포되었음을 입증하고, 운전 적격성 평가(OQ)는 맥락화 뷰, 감사 트리거, 소프트 센서가 자신의 범위에 걸쳐 규격대로 작동함을 입증하며, 성능 적격성 평가(PQ)는 처음부터 끝까지 이어지는 스레드가 알려진 배치의 계보와 출시 판정을 실제 데이터 위에서 재현함을 입증합니다. FDA가 규범적 CSV(전산화 시스템 검증, 시스템이 마땅히 해야 할 일을 한다는 문서화된 증명)에서 위험 기반 CSA(컴퓨터 소프트웨어 보증, Computer Software Assurance)로 옮겨 가면서, 그 시험의 깊이는 위험에 따라 조절됩니다. 컷 포인트(cut-point) 로직과 출시 관련 소프트 센서는 엄격한 스크립트 증명을 받고, 읽기 전용 추세 대시보드는 더 가벼운 비(非)스크립트 점검을 받습니다. 2권의 CSV에서 CSA로 장이 구축하고 이 책의 검증 장이 CI가 다시 실행하는 pytest 증거로 만들어 내는 구분입니다. 감사 트리거와 make alcoa 관문은 정확히 Part 11 / Annex 11 점검이 찾는 귀속 가능하고 감사 추적되는 통제이지만, 관문이 녹색인 것은 필요조건이지 충분조건이 아닙니다. 규제 당국은 데이터를 둘러싼 검증된 시스템을 허가하는 것이지, 데이터만을 허가하는 것이 아닙니다.
둘째는 스케일업과 기술 이전(tech transfer)입니다. 이 런은 한 규모에서의 고정된 14일 유가식 배치이지만, 실제 분자는 개발 바이오리액터에서 2000리터 생산 트레인으로 옮겨 가고, BATCH-2026-001을 길러 낸 공정은 규모마다 다시 적격성 평가를 받습니다. 혼합, 산소 전달, 전단력(shear)이 부피에 선형으로 비례하지 않기 때문입니다. 데이터 백본은 그 이동을 정직하게 실어 나르는 것입니다. ISA-88/95 모델, s88.v_batch_sensor 맥락화, 계보 엣지는 바뀌지 않고 그대로 옮겨 가므로, 받는 현장은 어휘를 다시 발명하는 것이 아니라 적재와 장비를 자신의 시스템에 대해 다시 적격성 평가합니다. 동일한 태그가 파일럿 규모의 BR101에서나 생산 용기에서나 같은 것을 의미하며, 그것이야말로 "골든 배치" 비교를 이전 너머에서도 사과 대 오렌지 식 겹쳐 보기가 아니라 유효한 비교로 만드는 바로 그 속성입니다.
핵심 용어
- 캡스톤 런(Capstone run) —
make명령어 시퀀스를 통해 스택의 모든 계층으로 배치 하나를 끝까지 몰아가는, 처음부터 끝까지 이어지는 단일 실습. - 로트 계보(lot genealogy) — 완성된 배치를 그 구성 요소까지 거슬러 추적하게 해 주는, 자재 로트의 방향성 체인(워킹 셀 뱅크 → 종균 배양 → 바이오리액터 → 포획 풀 → 원료 의약품 → 완제 의약품).
- 맥락화(Contextualization) —
s88.v_batch_sensor를 통해, 가공되지 않은 히스토리언 판독값을 그것의 배치, 장비, 활성 ISA-88 단계에 결합하는 것. - 예외 검토(review by exception) — 모든 가공되지 않은 값이 아니라, 맥락화된 전자 기록에서 표시된 일탈만을 검토하는 것.
- 실시간 출시 시험(real-time release testing, RTRT) — 최종 제품 시험만이 아니라 공정 데이터와 측정된 속성으로부터 공정 중 및 최종 품질을 평가하는 것.
- ALCOA+ — 기록이 충족해야 하는 데이터 무결성 속성(귀속 가능Attributable·판독 가능Legible·동시대Contemporaneous·원본Original·정확Accurate, 더하여 완전Complete·일관Consistent·영속Enduring·이용 가능Available).
- FAIR — 찾을 수 있는(Findable)·접근 가능한(Accessible)·상호 운용 가능한(Interoperable)·재사용 가능한(Reusable). 캡스톤 출력을 일회성 보고서가 아니라 재사용 가능한 데이터셋으로 만드는 설계 목표.
- 결정론적 생성(Deterministic generation) — 고정된 시드(
SIM_SEED=2026)로부터 동일한 데이터셋을 바이트 단위로 생산하는 것으로,MANIFEST.sha256에 대조하여 점검된다. - 해시 체인(변조 증명, tamper-evidence) — 각 감사 행을, 앞 행의 해시와 변경 내용에 대한
row_hash로 추가하여, 이후의 어떤 편집이든 체인을 깨뜨리게 하는 것.verify_chain()이 연결을 점검하고make alcoa가 0개의 끊긴 연결을 기대한다. 변조를 탐지 가능하게 만드는 것이지 불가능하게 만드는 것이 아니다. - 시간적 결합(temporal join) — 각 히스토리언 판독값을 그 자신의 타임스탬프에 활성이던 ISA-88 단계에 맞추는 것으로,
s88.v_batch_sensor의 반열린 구간r.ts >= start_ts AND r.ts < end_ts를 통해 이루어진다. - RDF 트리플 / SPARQL 역량 질문(competency question) — 동일한 계보·출시 사실을 주어-술어-목적어 트리플로 표현하여
(bp:derivedFrom)+속성 경로로 질의하는 것. 역량 질문은 그 질의를 기대 답과 짝지어 모델을 기계적인 합격/불합격으로 채점한다(4권의CQ-01). - SHACL 출시 셰이프(release shape) — 출시 패널의 닫힌 세계 표현. 요구되는 각 CQA에 대해 정확히 하나의 규격 내 값과 서명을 요구하는
bp:ReleaseShape로, 누락된 결과가 조용한 빈 칸이 아니라 위반이 되게 한다. 정확성이 아니라 완전성이다. - 그룹별(leave-one-batch-out) 교차 검증 — 배치 내 거의 중복인 스펙트럼이 학습/테스트 선을 가로질러 새지 못하도록, 모델을 통째의, 본 적 없는 배치에서만 채점하는 것. 공유 셀 뱅크로 가는
derivedFrom계보가 그 그룹화 키다. - 적용 가능 영역(applicability domain) — 모델이 적격성을 인정받은 입력 영역(세포주, 규모, 원자재 로트, 운전 범위)으로, 그 바깥에서는 예측이 외삽이며 신뢰되지 않는다.
- 모델 드리프트 대 공정 드리프트 — 예측기가 낡아 가는 것(탐지해야 할 결함) 대 살아 있는 배양물이 런마다 진짜로 떠도는 것(스레드가 보존해야 할 실제 신호). 둘을 뭉뚱그리면 모니터가 망가진다.
- 적격성 평가(IQ/OQ/PQ)와 CSA — 시스템이 사용에 적합함을 입증하는 설치·운전·성능 사다리로, 그 시험 깊이가 일률적으로 스크립트되는 것이 아니라 컴퓨터 소프트웨어 보증(CSA) 하에 위험에 따라 조절된다.
- 기술 이전(tech transfer) — 검증된 공정을 새 규모나 현장으로 옮기는 것. ISA-88/95 모델, 맥락화, 계보가 바뀌지 않고 옮겨 가므로 받는 현장은 어휘가 아니라 적재와 장비를 다시 적격성 평가한다.
다음 이야기
우리는 오픈 소스 스택이 실제 배치를 끝까지 실어 나를 수 있음을 입증했고, 각 단계에서 그것이 홀로는 충분하지 못한 지점이 어디인지에 대해 정직했습니다. 마지막 장인 정직한 평결: 오픈 소스 대 상용은 그 셈을 결산합니다. 순수 오픈 소스가 여러분에게 주는 것, GxP의 마지막 한 걸음이 요구하는 것, 그리고 규제 대상 mAb 시설을 위해 하이브리드 경계선이 정확히 어디에 떨어져야 하는지를, 계층 하나하나 점수 매긴 비교입니다.