QC와 출하 관문 모델링: 규격을 SHACL로
📍 현재 위치: 제5부 · 충전·완성과 출하, 모델링하기 — 제18장. 의약품은 밀봉된 단위로 존재합니다. 이 장은 그것을 출하해도 되는지를 결정하는 관문을 모델링합니다 — 디자인 스페이스 이래로 품질 가닥 전체가 향해 온 종착점 말입니다.
이 책이 모델링한 모든 품질 속성 — 단량체 순도, 변이체 수준, 바이러스 제거, 용기-마개 완전성 — 은 하나의 질문에 답하기 위해 존재합니다. 이 로트를 환자에게 출하해도 되는가? 품질관리(quality control, QC)는 출하 시험을 수행하고, 출하(release)는 규격(specification)에 비추어 로트가 모든 허용 기준을 충족하고 기록이 완전하며 서명되었다는 공식적 결정입니다. 바로 이 지점에서 그래프는 기술(description)이기를 멈추고 통제(control)가 됩니다. SHACL 셰이프(shape)로 모델링된 출하 규격은, 로트의 데이터가 출하를 주장하기 전에 반드시 통과해야 하는 관문이 됩니다 — 오픈소스 지식 그래프 장이 코드로 실행하는 바로 그 메커니즘입니다.
비행기가 이륙하기 전에 체크리스트는 반드시 완전해야 합니다 — 모든 항목이 존재하고, 점검되고, 서명되어야 합니다. 비행기가 아무리 멀쩡해 보여도, 빈칸 하나나 누락된 서명 하나가 비행을 멈춰 세웁니다. 출하는 배치(batch)에 대한 그 체크리스트입니다. 모든 필수 시험 결과가 존재하고, 한계 안에 있으며, 한 번 기록되고, 서명되어 승인되는 것이죠. 규격을 기계가 읽을 수 있는 체크리스트 — SHACL 셰이프 — 로 모델링한다는 것은, 체크리스트가 진정으로 완전해질 때까지 그래프 스스로가 로트를 출하되었다고 부르기를 거부할 수 있다는 뜻입니다. 이 장은 그 관문을 구축하며, 동시에 완전한 체크리스트가 좋은 비행과 같은 것은 아님을 정직하게 인정합니다.
이 장에서 다루는 내용
우리는 규격을 SHACL 셰이프의 집합으로, 출하 결정을 그 셰이프들이 강제하는 관문으로, 그리고 분석성적서와 서명을 출하를 귀속 가능하게 만드는 기록으로 모델링합니다. 출하 관문과 그 검증 보고서를 해부하고, 날카롭고 중요한 한계로 마무리합니다. SHACL은 기록이 완전하고 범위 안에 있음을 보장할 뿐, 그것이 참임을 보장하지는 못합니다 — 관문을 통과한 배치와 실제로 좋은 배치 사이의 차이 말입니다.
규격은 셰이프이고, 출하는 적합성이다
출하 규격은 원료의약품과 완제의약품에 대해 필수 시험과 그 허용 기준 — 한계 안의 단량체 순도, 임계값 이하의 응집체, 무균성, 그리고 나머지 — 을 ICH Q6B 같은 지침에 따라 구조화하여 나열합니다 [2]. 제1부에서 우리는 OWL은 추론하고 SHACL은 검증한다는 것을 배웠습니다. OWL의 열린 세계(open world)는 누락된 결과를 "알 수 없음"으로 취급하는데, 이는 누락된 필수 시험이 곧 실패인 출하에서는 정확히 잘못된 처리입니다. 그래서 규격은 자연스럽게 SHACL 셰이프가 됩니다 — 유효하게 출하된 로트가 반드시 충족해야 하는, 닫힌 세계(closed-world) 규칙입니다 [1]. 이것이 실행 예제가 검증하는 실제 bp:ReleaseShape이며, 원료의약품과 완제의약품을 모두 대상으로 삼습니다.
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix bp: <https://example.org/bioproc#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
bp:ReleaseShape a sh:NodeShape ;
sh:targetClass bp:DrugSubstance , bp:DrugProduct ;
# Exactly one monomer result, a float, at or above the spec floor.
sh:property [
sh:path bp:monomerPct ;
sh:name "SEC %monomer" ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ;
sh:minInclusive 95.0 ;
sh:message "Monomer purity is missing, duplicated, or below the 95.0 % release limit." ] ;
# HMW aggregate at or below its upper limit — the criterion DS-004/DP-004 trip.
sh:property [
sh:path bp:hmwPct ;
sh:name "SEC %HMW aggregate" ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ;
sh:maxInclusive 2.0 ;
sh:message "HMW aggregate is missing or above the 2.0 % release limit." ] ;
# CEX main charge-variant peak within its window.
sh:property [
sh:path bp:cexMainPct ;
sh:name "CEX %main (charge variant)" ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ;
sh:minInclusive 60.0 ; sh:maxInclusive 80.0 ;
sh:message "CEX main peak is missing or outside the 60.0-80.0 % window." ] ;
# Host-cell protein at or below its upper limit.
sh:property [
sh:path bp:hcpPpm ;
sh:name "host-cell protein (ppm)" ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ;
sh:maxInclusive 100.0 ;
sh:message "HCP is missing or above the 100 ppm release limit." ] ;
# Protein concentration within the formulation window.
sh:property [
sh:path bp:proteinConcMgPerMl ;
sh:name "protein concentration (mg/mL)" ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:float ;
sh:minInclusive 45.0 ; sh:maxInclusive 55.0 ;
sh:message "Protein concentration is missing or outside the 45-55 mg/mL window." ] ;
# Release status drawn from a controlled set.
sh:property [
sh:path bp:releaseStatus ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:in ( "PASS" "OOS" "PENDING" ) ] ;
# An attributable signature (21 CFR Part 11 / Annex 11).
sh:property [
sh:path bp:approvedBy ;
sh:minCount 1 ;
sh:message "Release record is unsigned." ] .
이를 실행 가능해진 체크리스트로 읽어 보세요. 로트는 정확히 하나의 단량체 결과를 지녀야 하고(minCount/maxCount — 반복 측정 중에서 골라잡기 금지), 그 값은 규격 하한 이상이어야 하며(minInclusive 95.0), HMW 응집체 결과를 지니되 그 값은 상한 이하여야 하고(maxInclusive 2.0), releaseStatus는 통제된 집합에서 나온 값이어야 하며, 로트는 서명을 지녀야 합니다(approvedBy). 검증기를 돌리면 적합한 로트는 sh:conforms true를 보고하고, 시험이 누락되었거나 범위를 벗어난 값을 지녔거나 서명되지 않은 로트는 sh:conforms false를 보고합니다. 출하 관문은 사람이 적용하기를 잊지 말아야 하는 SOP 속의 산문이 아닙니다 — 그래프가 모든 로트에 대해 자동으로, 동일하게 강제하는 규칙입니다.
검증 보고서는 예/아니오가 아니라 구조화된 판정이다
SHACL은 단순히 합격이나 불합격만 내놓지 않습니다. 그 자체가 RDF 그래프인 검증 보고서(validation report)를 반환하므로, 실패는 다른 어떤 사실과 마찬가지로 질의할 수 있습니다 [1]. OOS 로트들이 실패하면, 보고서는 sh:focusNode에 문제의 각 로트를, sh:resultPath에 실패한 시험을, 발동된 규칙을 sh:sourceConstraintComponent에, sh:Violation 심각도를, 그리고 사람이 읽을 수 있는 메시지를 담습니다. 실행 예제의 정직한 세부 사항은 이것이 어떤 종류의 실패인가입니다. 원료의약품 로트 DS-004와 그것으로 충전된 완제품 DP-004는 둘 다 단량체 순도가 98.687 %로 95.0 % 하한을 넉넉히 웃돌아 monomerPct 셰이프를 통과하며, 다른 모든 패널 값(CEX 주피크, HCP, 단백질 농도)도 규격 안에 있습니다. 관문에 걸리는 것은 2.0 % 한계를 넘은 두 로트의 HMW 응집체 2.41 % — sh:resultPath bp:hmwPct에서 일어난 sh:MaxInclusiveConstraintComponent 위반입니다. 이것이 현실적인 규격 이탈 양상입니다. 로트가 한 기준으로는 순수하면서도 응집체에서는 여전히 실패할 수 있으며, 그 실패는 정말로 범위를 벗어난 바로 그 하나의 경로에 정확히 국한되어 머무는 것이죠. 다음은 전체 그래프 검증에 대한 실제 pySHACL 보고서입니다 — OOS 로트마다 하나씩, 모두 같은 경로 위의 두 결과입니다.
Validation Report
Conforms: False
Results (2):
Constraint Violation in MaxInclusiveConstraintComponent (http://www.w3.org/ns/shacl#MaxInclusiveConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:datatype xsd:float ; sh:maxCount Literal("1", datatype=xsd:integer) ; sh:maxInclusive Literal("2.0", datatype=xsd:decimal) ; sh:message Literal("HMW aggregate is missing or above the 2.0 % release limit.") ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:name Literal("SEC %HMW aggregate") ; sh:path bp:hmwPct ]
Focus Node: bp:DP-004
Value Node: Literal("2.41", datatype=xsd:float)
Result Path: bp:hmwPct
Message: HMW aggregate is missing or above the 2.0 % release limit.
Constraint Violation in MaxInclusiveConstraintComponent (http://www.w3.org/ns/shacl#MaxInclusiveConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:datatype xsd:float ; sh:maxCount Literal("1", datatype=xsd:integer) ; sh:maxInclusive Literal("2.0", datatype=xsd:decimal) ; sh:message Literal("HMW aggregate is missing or above the 2.0 % release limit.") ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:name Literal("SEC %HMW aggregate") ; sh:path bp:hmwPct ]
Focus Node: bp:DS-004
Value Node: Literal("2.41", datatype=xsd:float)
Result Path: bp:hmwPct
Message: HMW aggregate is missing or above the 2.0 % release limit.
모든 부분이 트리플(triple)이기에, 보고서는 다시 같은 그래프 기계장치 속으로 떨어집니다. 스택 추적(stack trace)을 읽는 대신 "캠페인 전체에서 어떤 로트가 어떤 기준에 불합격했는가"를 질의할 수 있는 것이죠. 이것이 제약 언어(constraint language)와 단순한 단정(assertion)의 차이입니다 — 실패가 조사를 안내하기에 충분한 구조화된 맥락을 지니며, 어떤 형제 로트들이 같은 문제를 공유하는지 범위를 정하는 디지털 스레드로 곧장 연결됩니다. 보고서가 곧 RDF이기에, "어떤 로트가, 어떤 경로에서, 어떤 값으로 실패했는가"라는 질문 자체가 보고서 그래프에 대한 SPARQL 질의가 됩니다.
PREFIX sh: <http://www.w3.org/ns/shacl#>
PREFIX bp: <https://example.org/bioproc#>
SELECT ?focus ?path ?value ?component WHERE {
?r a sh:ValidationResult ;
sh:focusNode ?focus ;
sh:resultPath ?path ;
sh:value ?value ;
sh:sourceConstraintComponent ?component .
}
# -> bp:DS-004 bp:hmwPct 2.41 sh:MaxInclusiveConstraintComponent
# -> bp:DP-004 bp:hmwPct 2.41 sh:MaxInclusiveConstraintComponent
관문으로서의 출하: 로트의 CQA 결과와 서명을 SHACL 셰이프로 표현된 규격에 비추어 검사한다. 적합하면 출하 상태가 되고, 위반이 있으면 조사와 영향 질의를 안내하는 구조화된 보고서를 내보낸다 — 품질 가닥의 종착점을 강제 가능하게 만든 것이다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
서명과 성적서가 출하를 귀속 가능하게 만든다
출하는 규제된 행위이므로, 관문은 과학뿐 아니라 귀속성(attribution)까지 검사해야 합니다. 전자 기록 규정 — 미국의 21 CFR Part 11과 EU의 Annex 11 — 하에서, 출하된 기록은 서명되고, 귀속 가능하며, 감사 추적이 남아 있어야 합니다 [3]. 모델은 서명을 SHACL 관문이 요구하는 일급 엔티티(누가, 언제, 어떤 역할로 승인했는지)로 다루고, 결과를 규격에 비추어 요약한 문서인 분석성적서(certificate of analysis, CofA)를 로트의 결과로부터 derivedFrom인 정보 산출물(information artifact)로 다룹니다. 이것을 모델링한다는 것은 "출하됨"이 누구나 설정할 수 있는 단순한 상태 플래그가 아니라는 뜻입니다 — 그것을 뒷받침하는 서명되고, 완전하며, 규격 안에 든 증거가 그래프에 존재하지 않는 한 단정할 수 없는 상태인 것이죠. 시리즈 전체의 데이터 무결성 규율이, 여기서는 관문이 강제하는 전제 조건이 됩니다.
관문을 풀어헤치다: 모든 필수 결과에 대한 셰이프와 귀속 가능한 서명, 그리고 파생된 성적서가 합쳐져 적합성 판정을 내고, 실패 시 구조화된 RDF 보고서를 내놓는다 — 그래서 "출하됨"은 누군가가 세우는 플래그가 아니라 증거가 얻어내야 하는 상태다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
미해결 과제: SHACL은 완전성을 검사할 뿐 정확성은 검사하지 못한다
여기 가장 중요한 한계가 있으며, 이는 일관적이라고 정확한 것은 아니다라는 공리 장의 경고를 일반화합니다. SHACL 관문은 로트의 기록이 완전하고, 잘 형성되었으며, 범위 안에 있고, 서명되었음을 검증합니다. 그러나 그 기록이 참임을 검증할 수는 없습니다. 자신 있게 잘못 라벨링된 결과 — 엉뚱한 시험에 입력된 올바른 숫자, 시료 뒤바뀜, 우연히 범위 안에 떨어진 전사 오류 — 는 모든 구조적 규칙을 충족하므로 관문을 말끔히 통과합니다. SHACL은 흔한 실패들(누락된 시험, 범위를 벗어난 값, 서명되지 않은 기록, 중복된 결과)에 대한 강력한 방어 수단이며, 그것들이 실패의 대부분이긴 합니다. 하지만 그럴듯하고 범위 안에 든 거짓에는 눈이 멀어 있습니다. 관문은 체크리스트가 채워졌음을 증명할 뿐, 그 점검들이 정직했음을 증명하지는 못합니다.
이것이 바로 현실에서 출하가 결코 관문만은 아닌 이유입니다. SHACL 통과는 자동화가 지치지 않고 동일하게 강제할 수 있는 필요한 전제 조건이며 — 사람이 종이 체크리스트를 다시 점검하는 것에 비하면 진정한 진보입니다 — 그러나 출하 결정은 여전히 QC의 판단, 일탈 검토, 그리고 관문이 기록하되 대체하지는 못하는 검정자의 승인 서명에 달려 있습니다. "SHACL 관문을 통과했다"를 "좋은 배치다"와 뒤섞는 그래프는, 이 책이 매 단계에서 경고하는 바로 그 과신을 저지르는 것입니다. 정직한 기준은 이렇습니다. 완전성과 범위가 기계적으로 보장되고 조사를 안내할 수 있도록 규격을 SHACL로 모델링하되, 관문은 불완전성과 범위 이탈 오류를 막아 줄 뿐, 정확성 — 각 결과가 정말로 이 로트를 기술하는지 — 은 상류의 데이터 무결성과, 모델이 대체하기보다 뒷받침하는 인간의 판단에 달려 있음을 분명히 하는 것입니다.
왜 중요한가
출하는 공정 전체와 그 데이터가 봉사하기 위해 존재하는 결정이며, 규격을 SHACL로 모델링한다는 것은 기억해 두고 수동으로 적용하는 체크리스트를 강제되고 감사 가능한 관문으로 바꾸는 일입니다. 책이 모델링한 모든 CQA — 어떤 속성이 핵심인지를 정의한 디자인 스페이스로까지 거슬러 올라가 — 가 로트가 충족해야 할 셰이프로 이곳에 수렴하며, 실패는 피해 범위를 정하는 영향 분석으로 곧장 이어집니다. 바로 이 지점에서 품질 가닥은 기록이 아니라 통제가 됩니다. 그리고 관문의 정직한 한계 — 정확성이 아니라 완전성 — 는 온톨로지(ontology)가 무엇을 위한 것인지에 대한 가장 명료한 진술입니다. 온톨로지는 신뢰의 구조를 강제 가능하게 만들지만, 신뢰의 실체는 여전히 그 안으로 들어오는 것의 무결성에 달려 있습니다.
통신선에서 그래프까지
이 장이 구축한 출하 관문은 bp:DS-001을 내부 계보 노드로서 승인합니다 — 자신의 풀로부터 derivedFrom로 도달할 수 있는 원료의약품 로트로, 자신의 CQA 결과와 서명, 그리고 분석성적서를 지니고 있습니다. 그러나 그 로트가 규제 제출에 들어서는 순간, 규제기관은 여러분의 내부 IRI에는 관심이 없습니다. 규제기관이 원하는 것은 그 물질의 공식적 정체성, 즉 자신의 마스터 데이터 등록부에 비추어 다시 점검할 수 있는 정체성입니다. 그래서 실행 예제는 DS-001을 별도의 평행 체계로 분기시키지 않으면서 두 번째의, 규제용 이름을 부여합니다. examples/platform/ontology/instances.ttl에서, bp:ReleaseShape 관문이 이미 검증한 바로 그 그래프 노드가 ISO 11238 / IDMP 물질 식별자(substance identifier) — FDA UNII(GSRS) 코드와 IDMP 의약품 식별자(Medicinal Product Identifier) — 를 직접 부착받습니다.
# ISO IDMP substance identity: the regulator's name for the substance the graph tracks as DS-001
bp:DS-001 bp:hasSubstanceIdentifier bp:IDMP-DS-001 .
bp:IDMP-DS-001 a bp:SubstanceIdentifier ; rdfs:label "IDMP substance identity for DS-001" ;
bp:isAbout bp:DS-001 ;
bp:uniiCode "ILLUSTRATIVE-UNII-0001" ; # an FDA UNII / GSRS code (value illustrative)
bp:mpid "ILLUSTRATIVE-MPID-mAb-A" . # an IDMP Medicinal Product Identifier (value illustrative)
IDMP-DS-001이 바로 그 동일한 bp:DS-001을 — 그것의 복사본이 아니라 — isAbout하기 때문에, 출하된 로트는 단절된 신고 체계 안에서 처음부터 다시 기술되는 대신, 규제기관이 다시 점검하는 정체성을 지니고 PQ-CMC / eCTD 모듈 3 / SPL 제출로 건너갑니다. 여기의 UNII와 MPID 문자열은 예시용 자리표시자이지 부여된 코드가 아닙니다. 적재 가능한 산출물은 규제용 정체성이 관문이 서명한 바로 그 노드에 매달려 있다는 구조적 사실입니다. 그리고 파일럿 단계의 현장 표준들과 달리, IDMP/SPOR, UNII, SPL은 의무화된, 상용 등급의 규제 시맨틱입니다 — 다음 장 이미 의무화된 규제 시맨틱에서 살펴봅니다.
실제 현장에서는
Part 11과 Annex 11에 따른 서명된 기록과 함께 규격에 비추어 로트를 출하하는 일은 GMP 제조의 법적 구속력을 가진 현실이며, 생명공학 규격의 구조는 오래전부터 성문화되어 있습니다 [2][3]. SHACL은 확립된 W3C 표준이고, 오픈소스 책은 바로 이런 종류의 BatchShape 관문 — 포커스 노드와 제약을 명시한 검증 결과와 함께 sh:conforms false를 내놓는 — 을 시험된 코드로 실행하여, 이 메커니즘이 가설이 아님을 증명합니다 [1]. 기술적으로 해결된 것이 아니라 여전히 인간과 조직의 현실로 남아 있는 것은, 관문이 볼 수 없는 모든 것입니다. 일탈 조사, 데이터 무결성 문화, 그리고 그래프가 구조적으로 검증할 수는 있어도 보증하지는 못하는 출하 뒤에 서 있는 검정자의 판단 말입니다. 이 관문에는 실제 규제 쪽 사촌이 있습니다. 규제 시맨틱 장은 FDA의 KASA 플랫폼이 CMC 데이터에 규칙 기반의 구조화된 점검을 적용하는 모습과, IDMP가 의무화한 구조화 마스터 데이터가 제출 경계에서 점검 가능한 의미를 실어 나르는 모습을 보여 줍니다 — GxP 검증 체제가 살아 있는 추론기를 현장에서 밀어내는 와중에도 말입니다.
핵심 용어
- 출하 규격(release specification) — 로트가 충족해야 하는 필수 시험과 허용 기준으로, 로트의 데이터가 충족해야 하는 SHACL 셰이프로 모델링된다.
- SHACL 출하 관문(SHACL release gate) — 로트가 출하를 주장하기 전에 모든 필수 CQA가 존재하고, 단일하며, 타입이 지정되고, 범위 안에 있으며, 서명되었는지를 검사하는 닫힌 세계 검증.
- 검증 보고서(validation report) — 실패 시 SHACL이 반환하는 질의 가능한 RDF 그래프(포커스 노드, 결과 경로, 제약, 심각도, 메시지)로, 조사를 안내하는 데 쓰인다.
- 분석성적서(certificate of analysis, CofA) — 결과를 규격에 비추어 요약한 정보 산출물로, 로트의 결과로부터 파생된 것으로 모델링된다.
- 귀속성(서명)(attribution, signature) — 출하가 서명되고 감사 추적이 남아야 한다는 Part 11 / Annex 11 요구사항으로, 관문이 요구하는 일급 엔티티로 모델링된다.
- 완전성 대 정확성(completeness versus correctness) — SHACL은 기록이 완전하고, 잘 형성되었으며, 범위 안에 있음을 보장할 뿐 그것이 참임을 보장하지는 않는다. 그럴듯하고 범위 안에 든 거짓은 통과하므로, 관문은 인간의 출하 판단을 뒷받침하되 대체하지는 않는다.
다음 이야기
로트는 출하되었습니다 — 완전하고, 규격 안에 있으며, 서명되었음이 입증되었습니다. 다음 장 포장과 일련화 모델링: GS1과 단위 정체성은 그것을 따라 포장으로 들어갑니다. 거기서 로트는 마침내 개별적으로 식별되는 단위 — 각 바이알이 GS1 표준 하의 고유한 일련번호 항목 — 가 되며, 모델은 자신의 derivedFrom 계보를, 공급망 전체에서 의약품을 추적하기 위해 만들어진 두 번째 전 지구적 정체성 체계와 조화시켜야 합니다.