소개
2-1. 이 편의 위치
편
층
하는 일
실패했을 때
1~3
판단
파싱·라우팅·검색을 판단한다
탈출구로 신호를 올 린다
4
조립
호출로 엮고, 실패를 되돌린다
OWNER로 신호를 보낸다
5
규율
나가도 되는지 검사한다
막는다. 그리고 막은 사실을 남긴다
앞의 네 편은 무언가를 만드는 층이고, 5편은 만들어진 것을 거부하는 층입니다.
2-2. 규율은 왜 프롬프트에 두면 안 되는가
프롬프트에 "반드시 slide 번호를 인용하세요"라고 쓰는 것은 부탁입니다. 100번 중 97번은 지켜집니다. 문제는 지켜지지 않은 3번이 지켜진 97번과 똑같이 그럴듯해 보인다는 점입니다. 근거 없는 문장이 근거 있는 문장보다 오히려 더 매끄럽습니다.
규율층의 정의: 부탁하지 않고, 코드가 기계적으로 검사해서 통과·차단을 결정하는 층.
여 기서 "기계적"이 핵심입니다. 5편 게이트에는 문맥 판단이 한 줄도 없습니다. LLM을 호출하지 않고, 같은 입력이면 언제나 같은 판정을 냅니다. 문맥 판정은 6편의 몫이고, 5편은 그 자리만 비워 둡니다.
2-3. 이미 하고 있던 것 — 반성문의 필수 3종 검증
문서 작업에서는 이 원칙을 이미 쓰고 있었습니다.
문서 작업 게이트
성격
통변 파이프라인 대응
글자 수 대조
결정적
근거 건수 ≥ 1
XML 구조 비교
결정적
(source, slide_number) 복합키 존재
이미지·리소스 유지
결정적
스텁(본문 공백) 배제
파일 크기 -10% 이상 감소 = 이상 신호
임계값
RS 하한, 축약본 생성 하한
마지막 줄이 곧 fail-closed입니다 — "압축·최적화라고 자가정당화하지 말고 확인받는다". 5편은 문서 작업에서 검증된 이 원칙을 통변 파이프라인으로 옮기는 작업입니다.
진행 방법
3-1. 사용 도구
도구
용도
확정 여부
Python dataclass(frozen)
게이트 결과 불변 객체
확정
hashlib.sha256
입력 해시 — 재현성 증명
확정
PostgreSQL hermes.proof_artifact
증적 적재·집계
스키마 미확인
JSONL (append-only)
증적 원본
경로 미결정
pg_dump
백업 기준선
관행 있음
pytest
게이트 자체 회귀 검사
미작성
LLM이 목록에 없는 것이 요점입니다. 규율층에서 API 키가 필요하면 설계가 잘못된 것입니다.
3-2. 게이트 규칙표 — 규칙에 ID를 붙인다
rule_id
검사
등급
차단 사유
G-EV-001
근거 건수 ≥ 1
block
근거 0건
G-EV-002
모든 근거에 (source, slide_number) 쌍
block
근거 출처 불완전
G-EV-003
근거 본문 비어 있지 않음
block
스텁 근거 혼입
G-LN-001
한 단계 안에 2계통 혼재 없음
block
계통 혼용
G-VR-001
skill_versions 3종 기록됨
block
버전 미기록
G-DS-001
면책 고지 포함
block
면책 누락
G-TB-001
(6편 전용 자리) 처방형 단정
block
6편 판정 결과
G-RS-001
RS ≥ 0.5
warn
축약본 미생성
G-WN-001
N1 warnings 리포트 표기
warn
경계일 경고 미표기
등급 기준은 하나입니다 — "이대로 상담 자리에 나가면 사람에게 잘못 전달되는가."
G-EV-002가 신설 규칙입니다. slide_number는 전역 고유값이 아니어서(정진반 上·제트엔진 上이 7001~7098 공유), "slide" in e만 보면 어느 강의의 7001인지 알 수 없는 근거가 통과합니다.
3-3. 게이트 구현 — 결정적 검사만
python
# code_as_gate.py (layer: 규율)
# 원칙 1: LLM 호출 금지. 2: 같은 입력 → 같은 판정. 3: 예외 = 차단.
from dataclasses import dataclass
@dataclass(frozen=True)
class GateResult:
rule_id: str
passed: bool
reason: str
severity: str # 'block' | 'warn'
def g_ev_001(state):
ev = state.get("evidence") or []
ok = len(ev) >= 1
return GateResult("G-EV-001", ok, "" if ok else "근거 0건", "block")
def g_ev_002(state):
ev = state.get("evidence") or []
bad = [i for i, e in enumerate(ev)
if not e.get("source") or e.get("slide_number") in (None, "")]
return GateResult("G-EV-002", not bad,
"" if not bad else f"출처 불완전 {len(bad)}건 (idx={bad})", "block")
def g_ev_003(state):
ev = state.get("evidence") or []
stub = [f'{e.get("source")}#{e.get("slide_number")}'
for e in ev if not (e.get("text") or "").strip()]
return GateResult("G-EV-003", not stub,
"" if not stub else f"스텁 혼입: {stub}", "block")
def g_vr_001(state):
v = state.get("skill_versions") or {}
missing = [n for n in ("N1", "N2", "N3") if n not in v]
return GateResult("G-VR-001", not missing,
"" if not missing else f"버전 미기록: {missing}", "block")
GATE_CHAIN = [g_ev_001, g_ev_002, g_ev_003, g_vr_001]
def run_gate(state) -> list[GateResult]:
"""전 규칙을 끝까지 돌린다 — 첫 실패에서 멈추지 않는다."""
out = []
for rule in GATE_CHAIN:
try:
out.append(rule(state))
except Exception as e: # 예외 = 차단
out.append(GateResult(rule.__name__, False, f"검사 자체 실패: {e!r}", "block"))
return out
def verdict_of(results) -> str:
return "BLOCK" if any((not r.passed) and r.severity == "block" for r in results) else "PASS"첫 실패에서 멈추지 않는 이유 — 차단 사유가 매번 하나씩만 나오면 고치고 돌리기를 반복해야 합니다. 한 번에 전부 보여 주는 편이 수정 횟수를 줄입니다.
예외를 통과로 처리하지 않는 이유 — 검사가 죽었다는 것은 "문제 없음"이 아니라 "문제가 있는지 모른다"입니다.
3-4. Proof Artifact — 판정을 다시 하지 않는다
v1.0 초안의 가장 큰 결함이 여기 있었습니다. 증적을 쓰면서 게이트를 한 번 더 호출한 것입니다.
python
# 초안 (문제)
return {..., "gate_result": gate_evidence(state["evidence"])} # ← 판정 재실행판정이 두 번 일어나면 진실이 둘이 됩니다. 게이트가 통과시킨 순간과 증적을 쓰는 순간 사이에 상태가 달라지면, 증적이 실제와 다른 것을 기록합니다. 증적이 거짓말을 하는 것보다 증적이 없는 편이 낫습니다.
python
# proof_artifact.py (layer: 규율)
import hashlib, json, uuid
from datetime import datetime, timezone, timedelta
KST = timezone(timedelta(hours=9))
def now_kst(): return datetime.now(KST).strftime("%Y-%m-%d %H:%M:%S")
def input_hash(state) -> str:
payload = {"question": state.get("question", ""), "saju": state.get("saju", {}),
"lineage_plan": state.get("lineage_plan", {}),
"skill_versions": state.get("skill_versions", {})}
blob = json.dumps(payload, ensure_ascii=False, sort_keys=True)
return hashlib.sha256(blob.encode("utf-8")).hexdigest()
def write_proof_artifact(state) -> dict:
"""증적을 남긴다. 판정을 다시 하지 않는다 — 상태의 결과를 옮겨 적을 뿐이다."""
results = state["gate_results"] # ← run_gate가 이미 넣어 둔 값
return {
"run_id": str(uuid.uuid4()), "timestamp_kst": now_kst(),
"profile_id": state.get("profile_id"), # 생년월일시 원문 미기록
"input_hash": input_hash(state),
"lineage_plan": state.get("lineage_plan", {}),
"used_evidence": [{"source": e["source"], "slide_number": e["slide_number"],
"ts": e.get("ts")} for e in state.get("evidence", [])],
"missing_ts": state.get("missing_ts", []), # ★ 못 찾은 것
"rejected": state.get("rejected_evidence", []), # ★ 버린 것과 사유
"retry_history": state.get("retry_history", []),
"reroute_history": state.get("reroute_history", []),# ★ v2.0 — 4편 환류 이력
"owner_signal": state.get("owner_signal", {}), # ★ v2.0
"halt_reason": state.get("halt_reason", ""),
"gate_results": [r.__dict__ for r in results],
"verdict": state.get("verdict"), "rtype": state.get("rtype"),
"skill_versions": state.get("skill_versions", {}),
"call_trace": state.get("call_trace", []),
"human_gate": state.get("human_gate", {"required": False}), # ★ v2.0
"readback": state.get("readback", {"status": "not_checked"}),# ★ v2.0
}missing_ts·rejected가 v1.0 신설분, reroute_history·owner_signal·human_gate·readback이 v2.0 신설분입니다.
증적의 가치는
used가 아니라missing에 있습니다. 쓴 것은 리포트에도 남습니다. 증적에만 남는 것은 버린 것과 못 찾은 것입니다. 없는 것을 없다고 기록하는 구조가 이 프로젝트의 지향입니다.
3-5. 완료 주장의 5단계 — CROSS와 HUMAN GATE 신설
장표는 완료 주장이 지나야 할 관문을 이렇게 놓습니다.
text
EXECUTE → PROOF → CROSS → CODE GATE → HUMAN GATE
실행 증거 독립경로 기계 사람
확인 불변조건 미감·의미·승인v1.0은 PROOF와 CODE GATE 둘만 있었습니다.
관문
v1.0
v2.0
EXECUTE
✅
4편 그래프
PROOF
✅
§3-4
CROSS
❌
자리 신설 — §4-5 (아직 미구현, 정직하게 표기)
CODE GATE
✅
§3-3
HUMAN GATE
❌
§3-6 신설
3-6. Human Gate — 기계가 판정할 수 없는 것
기계 게이트가 판정할 수 없는 종류가 있습니다. 그것을 자동화하려는 시도가 오히려 위험합니다.
승인 사항
왜 기계가 못 하나
승인자
프로덕션 GuardrailAgent 정책 전환
계통 해석의 문제
교수님
계통별 차단 정책 변경
스승 계통의 태도 차이 판단
교수님
허용어 목록 추가
차단력 손실과 맞바꾸는 결정
교수님
gwanbeop_ppt 자료 삭제·수정
되돌릴 수 없음
교수님 (반성문 원칙)
축약본을 상담 자리에 사용
사람에게 전달되는 최종 판단
교수님
UNESCO 서류에 증적 인용
대외 문서
교수님
python
HUMAN_GATE_REQUIRED = {
"guardrail_policy_change", "lineage_policy_change", "allowlist_add",
"data_delete", "brief_report_release", "external_citation",
}
def needs_human_gate(action: str) -> bool:
return action in HUMAN_GATE_REQUIREDHuman Gate가 없으면 Code Gate가 대신 판정해 버립니다. 그리고 기계는 "이 정도면 됐다"를 늘 통과로 판정합니다. 사람 판단이 필요한 자리를 비워 두면 자동으로 기계가 채우는 것이 규율층의 기본 실패 형태입니다.
3-7. 실패 예시 · 금지사항 · 승인 지점
확장과제 4번 산출물입니다. v1.0에 통째로 없었습니다.
실패 예시 (게이트가 잡아야 할 것)
#
실패
잡는 규칙
1
근거 0건인데 문장이 완결되어 나감
G-EV-001
2
slide 7001 인용, source 없음
G-EV-002
3
스텁(제목만 있고 본문 공백) 근거 인용
G-EV-003
4
한 단계 답변에 ①·②계통 혼재
G-LN-001
5
버전 미기록 상태로 증적 생성
G-VR-001
6
게이트 예외 발생 → 조용히 통과
run_gate except
MUST NOT (규율층이 하지 않는 일)
하위 실패를 기본값으로 메우지 않는다
차단 사유 문구를 조립층·규율층이 새로 만들지 않는다 (하위 문장 그대로 전파)
증적을 수정하지 않는다 (append-only)
통과시키려고 기준값을 고치지 않는다
게이트를 통과시키려 근거를 추가 검색하지 않는다 (그것은 N3의 일)
APPROVAL (승인 전까지 수행하지 않는 것)
프로덕션 DB 스키마 변경 (
hermes스키마 생성 포함)기존
GuardrailAgent정책 교체증적의 외부 공개·인용
축약본 생성 및 상담 사용
3-8. BACKUP · READBACK — 응답과 실제 상태를 분리한다
v2.0의 핵심 추가분입니다.
python
def readback_verify(conn, run_id: str, expected: dict) -> dict:
"""INSERT 응답을 믿지 않는다. 다시 읽어 대조한다."""
row = conn.execute(
"SELECT input_hash, verdict, jsonb_array_length(used_evidence) AS n "
"FROM hermes.proof_artifact WHERE run_id = %s", (run_id,)
).fetchone()
if row is None:
return {"status": "fail", "reason": "INSERT 응답은 성공이나 행이 없음"}
mismatch = {k: (getattr(row, k), expected[k])
for k in ("input_hash", "verdict") if getattr(row, k) != expected[k]}
if mismatch:
return {"status": "fail", "reason": f"응답과 실제 상태 불일치: {mismatch}"}
return {"status": "pass", "row_found": True, "evidence_count": row.n}"INSERT 성공"은 결과가 아니라 응답입니다. 트랜잭션이 롤백됐을 수도, 다른 스키마에 들어갔을 수도, 트리거가 값을 바꿨을 수도 있습니다. 다시 읽어서 확인한 것만 pass이고, 나머지는 not_checked입니다.
이것은 제 반성문에서 가장 자주 반복된 실수와 정확히 같은 구조입니다 — 작업 결과를 검증 없이 성공으로 보고한 것. 문서 작업에서는 글자 수를 다시 세어 확인하기로 했으면서, DB 쓰기에서는 응답만 보고 있었습니다.
BACKUP 기준
대상
기준선
시점
hanbadook_production 전체
pg_dump
스키마 변 경 전
gwanbeop_ppt
source별 건수 스냅샷
INSERT 전후
증적 JSONL
파일 자체가 append-only 원본
상시
백업 없이 스키마를 만들지 않습니다. 되돌릴 원본이 없으면 롤백은 말뿐입니다.
3-9. ROLLBACK 판단 기준
장표는 재판정이 다시 실패할 때 구조가 틀렸거나 복구 비용이 크면 별도 rollback 판단을 하라고 정리합니다. 그 기준을 미리 적어 둡니다.
조건
조치
같은 규칙이 3회 연속 차단 + 원인 동일
수리 중단, 구조 재검토
증적 테이블에 잘못된 스키마로 행이 쌓임
테이블 드롭 후 재생성 (교수님 승인 필요)
게이트 도입 후 기존 11노드 파이프라인 영향 감지
즉시 롤백 — 사이드카 원칙 위반
증적 파일이 손상·부분 기록
해당 구간 격리, 삭제하지 않음
마지막 줄이 중요합니다. 손상된 증적도 지우지 않습니다. 지우는 순간 "무슨 일이 있었는지 모른다"가 "아무 일도 없었다"로 바뀝니다.
3-10. Repair / Re-eval 루프
text
Eval Contract → Execute → Proof → Gate ──PASS──▶ 종료
│
FAIL
▼
Repair (가장 작은 실패 단계만)
▼
New Proof (새 증적)
▼
Gate 재판정 ──FAIL──▶ ↺ (또는 §3-9 rollback 판단)수정은 완료가 아니라 새 Proof를 만드는 다음 시도입니다. 그러므로 수리 후에는 기존 증적을 고치는 것이 아니라 새 run_id로 새 증적을 남깁니다. 두 증적을 나란히 두면 무엇을 고쳐서 무엇이 달라졌는지가 그 자체로 기록됩니다.
3-11. Proof Artifact 예시 (JSON) — 차단 케이스
규율층에서 진짜로 보여야 할 것은 통과가 아니라 차단입니다.
json
{
"run_id": "9f2c7a10-3b44-4d2e-9c81-6a5f0b1d77e2",
"timestamp_kst": "2026-07-03 14:20:11",
"profile_id": 1042,
"input_hash": "3a7f…d91c",
"lineage_plan": {"TS1": "①", "TS2": "①", "TS5": "②"},
"used_evidence": [
{"source": "박청화_정진반_下", "slide_number": 9636, "ts": 5},
{"source": "박청화_정진반_下", "slide_number": 9640, "ts": 5}
],
"missing_ts": [1, 2],
"rejected": [
{"source": "박청화_무엇이든물어보세요", "slide_number": 4108,
"reason": "본문 공백 — 스텁"}
],
"retry_history": [{"attempt": 0, "hits": 0}, {"attempt": 1, "hits": 3}],
"reroute_history": [{"from": {"TS1": "②"}, "to": {"TS1": "①"}, "cause": "근거 0건"}],
"owner_signal": {"stage": "N3", "reason": "", "repairable": true},
"gate_results": [
{"rule_id": "G-EV-001", "passed": true, "reason": "", "severity": "block"},
{"rule_id": "G-EV-002", "passed": true, "reason": "", "severity": "block"},
{"rule_id": "G-EV-003", "passed": true, "reason": "", "severity": "block"},
{"rule_id": "G-VR-001", "passed": false, "reason": "버전 미기록: ['N3']",
"severity": "block"}
],
"verdict": "BLOCK",
"rtype": "BLOCKED",
"skill_versions": {"N1": "0.2.0", "N2": "0.3.0"},
"human_gate": {"required": false},
"readback": {"status": "not_checked", "reason": "DB 미적재"},
"cross_verification": {"status": "not_checked", "reason": "독립 경로 미구현"}
}readback과 cross_verification이 not_checked로 남아 있는 것이 정직한 상태입니다. 확인하지 않은 것을 확인했다고 적지 않습니다.
3-12. 증적은 모든 종료 경로에서 남는다
text
[v1.0 초안]
END(성공) ──▶ 증적 ✓
HALT(실패) ─▶ ✗ ← 추적이 가장 필요한 쪽이 비어 있다
[v2.0]
END ──┐
OWNER ┼─▶ GATE ─▶ PROOF ─▶ READBACK ─▶ 종료
BLOCK ┘OWNER → GATE 엣지가 이 편 의 핵심 한 줄입니다. 막힌 것도 기록으로 남깁니다.
3-13. 저장 구조 — 파일과 DB 두 벌
sql
CREATE SCHEMA IF NOT EXISTS hermes;
CREATE TABLE hermes.proof_artifact (
id bigserial PRIMARY KEY,
run_id uuid NOT NULL UNIQUE,
ts_kst timestamptz NOT NULL DEFAULT now(),
profile_id bigint,
input_hash text NOT NULL,
lineage_plan jsonb NOT NULL,
used_evidence jsonb NOT NULL,
missing_ts smallint[] NOT NULL DEFAULT '{}',
rejected jsonb NOT NULL DEFAULT '[]',
retry_history jsonb NOT NULL DEFAULT '[]',
reroute_history jsonb NOT NULL DEFAULT '[]',
gate_results jsonb NOT NULL,
verdict text NOT NULL CHECK (verdict IN ('PASS','BLOCK')),
rtype text,
halt_reason text NOT NULL DEFAULT '',
skill_versions jsonb NOT NULL,
call_trace jsonb NOT NULL DEFAULT '[]',
human_gate jsonb NOT NULL DEFAULT '{"required": false}',
readback jsonb NOT NULL DEFAULT '{"status":"not_checked"}',
elapsed_ms integer
);
CREATE INDEX ON hermes.proof_artifact (ts_kst DESC);
CREATE INDEX ON hermes.proof_artifact (verdict) WHERE verdict = 'BLOCK';
CREATE INDEX ON hermes.proof_artifact (input_hash);
CREATE INDEX ON hermes.proof_artifact USING gin (used_evidence);
-- append-only 강제: 증적은 고쳐 쓸 수 없어야 한다
REVOKE UPDATE, DELETE ON hermes.proof_artifact FROM PUBLIC;JSONL 파일 (append-only)
PostgreSQL 테이블
역할
원본 증적
집계·조회 인덱스
강점
위변조 검출 쉬움, DB 장애와 무관
규칙별 통계 즉시 조회
약점
조회 불편
UPDATE 가능 → 사후 편집 위험
등재 관점
원본 증적
재현성 지표 산출
둘 중 하나만 고른다면 파일입니다. 심사가 요구하는 것은 조회 편의가 아니라 "고쳐지지 않았음"이기 때문입니다.
3-14. 집계 쿼리
sql
-- 최근 7일, 규칙별 차단 건수
SELECT r->>'rule_id' AS rule_id,
COUNT(*) FILTER (WHERE (r->>'passed')::bool IS FALSE) AS blocked
FROM hermes.proof_artifact a, jsonb_array_elements(a.gate_results) r
WHERE a.ts_kst >= now() - interval '7 days'
GROUP BY 1 ORDER BY blocked DESC;
-- 재현성 검사: 같은 입력이 다른 근거를 냈는가
SELECT input_hash, COUNT(DISTINCT used_evidence::text) AS variants
FROM hermes.proof_artifact
GROUP BY 1 HAVING COUNT(DISTINCT used_evidence::text) > 1;
-- 리드백 미확인 비율 — 이 값이 0이 아니면 증적을 신뢰할 수 없다
SELECT readback->>'status' AS status, COUNT(*)
FROM hermes.proof_artifact GROUP BY 1;두 번째 쿼리가 이 프로젝트의 핵심 지표입니다. 같은 입력에 다른 근거가 나왔다면 재현성이 깨진 것이고, 그것이 등재 서류가 요구하는 바로 그 항목입니다.
(이미지 자리 — 5관문 흐름도 + 차단 증적 캡처)
결과와 배운 점
4-1. 결함 종합
#
결함
발견
심각도
①
증적 작성이 게이트를 재호출
v1.0
치명
②
차단 케이스에 증적이 안 남음
v1.0
치명
③
slide만 검사 — 복합키 누락
v1.0
높음
④
쓴 것만 남기고 못 찾은 것 미기록
v1.0
높음
⑤
gate_result 튜플 — 집계 불가
v1.0
중
⑥
재현성 증명 수단 없음
v1.0
높음
⑦
Human Gate 부재
v2.0
높음
⑧
금지사항·승인 지점 목록 없음
v2.0
중
⑨
백업·리드백·롤백 전부 없음
v2.0
치명
⑩
독립 경로 교차검증 자리 없음
v2.0
중
⑪
판정 어휘 partial은 공식 어휘 아님
v2.0
중
⑨가 치명인 이유는 앞의 여덟 개와 성격이 다르기 때문입니다. ①~⑧은 "게이트가 제대로 막지 못한다"는 문제이지만, ⑨는 "증적 자체를 믿을 수 없다"는 문제입니다. 증적이 틀렸으면 게이트가 아무리 정교해도 남는 것이 없습니다.
4-2. 규율층 안티패턴 4종
#
안티패턴
증상
이번 편에서
①
부탁하기 — 프롬프트에 "반드시 인용하세요"
3%가 조용히 샌다
게이트 함수로 이전
②
fail-open — 검사 예외를 통과로
검사가 죽은 날 전부 통과
except → block
③
증적 사후 편집
회귀 은폐
REVOKE UPDATE, DELETE
④
자기보고 신뢰 — 응답을 결과로
없었던 일이 있었던 일로 기록됨
READBACK
④는 반성문 원칙의 판박이입니다. 골든셋이 실패하면 로직을 의심해야지 기준값을 고쳐 통과시키면 안 된다 — 그 원칙을 증적에 적용하면 UPDATE 권한 회수가 되고, DB 쓰기에 적용하면 리드백이 됩니다. 원칙을 문장이 아니라 권한과 절차로 새기는 것이 규율층의 방식입니다.
4-3. 게이트와 가드레일의 경계선
5편 Code as Gate
6편 GuardrailAgent
판정 방식
결정적 (if)
문맥 기반
LLM 사용
금지
사용 (완화 전용)
대상
근거 구조
문장 내용
재현성
100% 보장
골든셋으로 관리
"용신" in text로 막으면 박청화 선생의 격국·용신 재정립 강의 인용까지 통째로 막힙니다. 그 판정은 문맥이 필요하고, 문맥이 필요한 순간 그것은 게이트가 아닙니다. 5편은 G-TB-001 자리만 비워 두고 6편으로 넘깁니다.
4-4. 성능
단계
목표
게이트 9규칙
< 50ms
증적 직렬화 + 파일 append
< 30ms
DB INSERT
< 20ms
READBACK SELECT
< 20ms
규율층 합계
< 120ms (전체 20s의 0.6%)
게이트가 느리면 반드시 꺼집니다. 규율층이 살아남는 조건은 엄격함이 아니라 가벼움입니다. 리드백을 추가해도 20ms이므로, "느려서 못 한다"는 변명이 성립하지 않습니다.
4-5. 독립 경로 교차검증 — 자리만 만들고 비워 둡니다
장표는 독립 경로 확인이 같은 판정자에게 같 은 질문을 반복하는 것과 다르다고 명시합니다. 채점 사다리도 사람 → 내 봇(같은 경로 편향) → 다른 에이전트(독립성 보강) → 반복실험 순으로 놓고, 일치가 곧 진실은 아니라고 못 박습니다.
제 상황을 정직하게 적으면, 현재 헤르메스에는 독립 경로가 없습니다. 같은 Claude가 생성하고 같은 Claude가 검증하면 그것은 교차검증이 아니라 자기 확인입니다. 증적에 cross_verification: not_checked로 남기고, 구현은 이후로 미룹니다. 없는 것을 있는 것처럼 필드만 채우지 않습니다.
4-6. 확장과제 2번(Eval Contract)의 처리
Eval Contract는 6편에서 다룹니다. 5편에서 계약을 쓰면 판정 기준을 게이트가 스스로 정하는 셈이 되어, 규율층이 자기 시험 문제를 출제하는 형태가 됩니다. 기준은 판정 대상 바깥에 있어야 하므로 6편에 둡니다.
4-7. 꿀팁 · 시행착오 · 도움 필요
꿀팁 1 — 규율은 문장이 아니라 권한에 새긴다. "증적을 고치지 맙시다"는 지켜지지 않고, REVOKE UPDATE는 지켜집니다.
꿀팁 2 — 게이트 예시는 통과가 아니라 차단으로 쓴다. 통과 예시만 있으면 차단 경로가 실제로 도는지 아무도 확인하지 않습니다.
꿀팁 3 — 응답과 상태를 분리한다. "성공했습니다"는 결과가 아니라 주장입니다.
시행착오 1. 증적 없이 운영하다 오답 원인 추적이 불가했습니다 → 상시화.
시행착오 2. 게이트를 프롬 프트 마지막 줄로 넣고 대부분 지켜져서 문제가 없다고 착각했습니다.
시행착오 3. 증적에 생년월일시 원문을 넣으려다 멈췄습니다. 영구·수정불가 저장소에 개인정보를 쌓는 것은 위험합니다 → profile_id + input_hash만.
시행착오 4 (v2.0). 문서 작업에서는 글자 수를 다시 세면서, DB 쓰기에서는 응답만 보고 있었습니다. 같은 원칙을 한쪽에만 적용하고 있었던 것을 장표 대조로 알았습니다.
도움 필요 1. 증적 무결성 — JSONL에 체인 해시(직전 레코드 해시를 다음에 포함)까지 넣는 것이 과한지, 등재 서류 관점에서는 필요한지 판단이 서지 않습니다.
도움 필요 2. 독립 경로 교차검증을 실제로 어떻게 구성하시는지. 다른 모델을 쓰는 것으로 충분한지, 아니면 검색 인덱스까지 달라야 독립인지.
4-8. 첫 실행 로그
검증 상태 어휘 (공식 4종) — v1.0의 partial 폐기.
상태
뜻