AI-Native 시리즈 · Agent Engineering
AI가 내 레포를 리뷰하더니 더 나쁘게 만들라고 했다
그것은 나에게 10점 만점에 8.6점을 매기고 열두 가지 업그레이드를 제안했다. 그중 절반은 진짜였다. 하나는 코드베이스에서 가장 잘한 결정을 조용히 무너뜨렸을 것이다 — 그리고 그 차이를 알아낼 유일한 방법은 리뷰 읽기를 멈추고 코드를 직접 읽으러 가는 것뿐이었다.
1분
내 오픈소스 루프 엔진에 대한 AI 리뷰는 사려 깊고 체계적이었으며, 거의 전적으로 내 README를 출처로 삼고 있었다. 열두 개 제안 중 여섯 개는 진짜 공백이었고, 다섯 개는 내가 절반쯤 만들어놓고 문서화가 덜 된 것이었으며, 하나는 이미 만들어져 있는 것이었는데, 그 제안은 그것 — 열린 플러그인 레지스트리 — 을 닫힌 목록으로 대체하는 것이었다. 그중 가장 큰 진짜 문제는 한 번의 실행을 기술하는 계약 파일이었다. 나는 그것을 만들었는데, 어려웠던 부분은 스키마가 아니었다. 어려웠던 부분은 그 파일이 나를 거부할 수 있게 만드는 것이었다. 세 가지 규칙이 그것을 해냈고, 이 규칙들은 당신이 지금까지 작성한 어떤 거버넌스 파일에도 일반화된다.
리뷰
누군가 프론티어 모델을 loop-engineering-anything — 생성된 도구를 독립적인 심사자 앞에서 F등급에서 A등급까지 끌어올리는 내 엔진 — 에 돌려서 결과를 보내왔다. 결과는 훌륭했다. 내가 받아본 대부분의 인간 코드 리뷰보다 나았다: 체계적이고, 구체적이었으며, 내가 실제로 애쓴 부분에서는 칭찬도 아끼지 않았다. 열두 가지 업그레이드를 제안했고, 그다음 스물세 개짜리 백로그가 더 있었다.
헤드라인도 하나 달려 있었다: "your architecture is too CLI-shaped."(번역: 당신의 아키텍처는 지나치게 CLI 형태에 갇혀 있다.) 마지막 권고는 형식적인 아티팩트 타입을 도입하라는 것이었다 — 엔진이 수렴할 수 있는 종류를 고정된 목록으로 만들어서, 레포가 내세우는 "anything"이라는 약속을 기술적으로 참이 되게 하라는 얘기였다:
"Keep CLI as the first proven lane, but abstract the engine around 'artifact convergence,' not 'CLI convergence.'"(번역: CLI는 이미 검증된 첫 번째 경로로 남겨두되, 엔진 자체는 'CLI 수렴'이 아니라 '아티팩트 수렴'을 중심으로 추상화하라.)
여기서 핵심은 이거다. 리뷰가 내 코드에 대해 주장한 모든 내용은 출처가 정확히 하나였다: 내 README.
이건 트집을 잡으려는 것이 아니다. 이 글 전체에서 가장 유용한 문장이 바로 이것이다. 로비만 볼 수 있는 리뷰어는 로비를 리뷰할 수밖에 없고, 로비에 "이건 CLI를 만든다"고 써 있으면 리뷰어는 이게 CLI를 만든다고 옳게 결론 내린다. 그 발견 자체는 진짜였다. 다만 내 아키텍처에 대한 발견이 아니었을 뿐이다. 그건 아키텍처의 옷을 입은, 내 문서에 대한 발견이었다.
그래서 나는 지루한 일을 했다. 코드를 한 줄도 건드리기 전에 한 시간 동안 내 소스를 직접 읽었다.
코드가 되받아친 말
제안은 중앙 스키마 파일에 이런 걸 추가하라는 것이었다:
artifact_type: Literal["cli", "mcp_server", "agent_skill", "workflow", "spec"]
닫힌 목록이다. 엔진이 개선할 수 있는 새로운 종류가 생길 때마다 그 파일 하나를 고쳐야 하고, 그 파일을 소유한 사람이 검토해야 한다.
하지만 엔진에는 8주 전에 추가된 일반화 이음매(seam)가 이미 있었고, 그것은 정반대 방식으로 작동했다. domain은 세 가지 질문에 답하는 작은 플러그인이다 — 이 타겟이 내 것인가, 첫 버전을 어떻게 빌드하는가, 누가 이를 심사하는가 — 그리고 시작할 때 레지스트리에 자신을 알린다. 새로운 종류의 타겟을 추가하는 일은 등록 하나로 끝난다. 중앙의 것은 아무것도 바뀌지 않는다. 당시 내가 쓴 모듈 독스트링에는 이렇게 적혀 있다: "새로운 domain은 등록으로서 도착할 뿐, 결코 router.py 나 컨트롤러에 대한 수정으로 오지 않는다…"
리뷰어의 제안은 열린 시스템을 가져다 닫아버리는 것이었다. 악의도, 어리석음도 아니었다 — 그저 이음매를 볼 수 없었을 뿐이다. 이음매는 README에 없기 때문이다.
열두 개 제안 중 여섯 개는 진짜 공백이었다. 다섯 개는 내가 반쯤 만들어두고 문서화를 제대로 안 한 것들이었다. 나머지 하나가 바로 이것이었다. 나는 판정 내용을 항목별로 정리해 코드 옆에 커밋해 두었다[5], 왜냐하면 혼자 마음속으로만 반박한 리뷰는 여섯 달 뒤에 다시 논쟁하게 될 리뷰이기 때문이다.
진짜 공백, 그리고 그 안의 함정
가장 큰 진짜 구멍은 이름을 붙이고 보니 부끄러울 정도였다. 내 엔진에게 지시하는 모든 것 — 무엇을 개선할지, 얼마나 좋아야 충분한지, 몇 번 시도할지, 얼마를 쓸지, 언제 멈출지 — 이 모든 게 오직 커맨드라인 플래그로만 존재했다. 터미널에 타이핑된 것. 창을 닫으면 사라지는 것.
그러니 요청, 즉 ask. 나는 어떤 실행이 무엇을 달성했는지 아름다운 증거로 보여줄 수는 있어도, 애초에 무엇을 요청받았는지는 보여줄 수 없었다. 해결책은 뻔하다. 요청 사항을 파일에 담아 커밋하는 것이다.
그런데 함정은 바로 여기에 있다. 이 뻔한 해결책이야말로 소프트웨어가 거짓말하는 가장 흔한 방식이기 때문이다.
빠진 글자 하나, 서버 천 대
Kubernetes는 전 세계 서버 함대의 상당 부분을 운영하는 시스템이다. 원하는 상태를 YAML 파일에 적어 전달하는 식이다. 2023년 4월, Google의 두 엔지니어가 Kubernetes 블로그에서 검증 기능을 발표하며, 그 파일을 살짝 잘못 적었을 때 예전에는 어떤 일이 벌어졌는지를 이렇게 설명했다 [1]:
"Before Kubernetes v1.8 (!), typos, mis-indentations or minor errors in YAMLs could have catastrophic consequences (e.g. a typo like forgetting the trailing s in replica: 1000 could cause an outage, because the value would be ignored and missing, forcing a reset of replicas back to 1)." (번역: Kubernetes v1.8 이전에는(!) YAML의 오타, 들여쓰기 실수, 사소한 오류가 치명적인 결과를 낳을 수 있었다. 예를 들어 replica: 1000이라고 써야 할 것을 마지막 's'를 빠뜨리면, 그 값이 인식되지 않고 무시되어 replica 수가 1로 리셋되면서 장애가 발생할 수 있었다.)
천천히 다시 읽어보라. 당신은 천 대를 요청했다. 당신이 쓴 것은 replica이었는데, 원래 써야 했던 것은 replicas였다. 시스템은 따지지 않았다. 파일을 그대로 받아들이고, 알아듣지 못한 단어는 버린 다음, 서버를 몇 대 운영해야 하는지에 대한 지시를 찾지 못하자 친절하게도 기본값인 1로 되돌렸다.
당신의 파일은 수용되었다. 당신의 함대는 아니었다.
인용문 속 느낌표는 엔지니어들 자신이 붙인 것이다. "v1.8 이전"이라는 말을 굳이 넣은 이유는, 오탈자를 기본값에 대한 무언의 찬성표로 취급하는 시스템을 그렇게 오랫동안 출시해왔다는 사실이 놀랍기 때문이다.
이것은 Kubernetes만의 결함이 아니다. 지금까지 작성된 거의 모든 설정 파서의 기본 동작이며, "받아들이는 것에는 관대하라"는 그 자체로는 선의에서 나온 오랜 엔지니어링 전통에서 비롯된 것이다. 2023년 Internet Architecture Board는 그 조언을 철회하는 문서를 발표하며, 관용이 "모든 상황에서 더 이상 모범 사례로 간주되지 않는다"고 밝혔다. Martin Thomson과 David Schinazi는 이렇게 썼다 [2]:
"Time and experience show that negative consequences to interoperability accumulate over time if implementations silently accept faulty input." (번역: 시간과 경험에 비추어 볼 때, 구현체가 잘못된 입력을 조용히 수용할 경우 상호운용성에 대한 부정적 영향은 시간이 지남에 따라 누적된다.)
이들의 논리야말로 훔쳐올 만한 대목이다. 관용은 공짜가 아니고, 국소적이지도 않다. 어떤 구현체 하나가 잘못된 형식의 무언가를 조용히 수용하는 순간, 그 이후에 만들어지는 모든 것은 명세가 아니라 그 조용한 수용을 기준으로 지어진다. 그들은 이를 연쇄반응이라 부른다 — "Overapplication of the robustness principle therefore encourages a chain reaction that can create interoperability problems over time" (번역: 따라서 견고성 원칙의 과도한 적용은 시간이 지남에 따라 상호운용성 문제를 일으킬 수 있는 연쇄반응을 조장한다) — 그리고 이 피해가 시스템 초기에 가장 크다는 점도 짚는다. 오늘의 사소한 결함이 내일의 호환성 요건이 되어버리기 때문이다.
젊은 거버넌스 파일은 정확히 젊은 프로토콜과 같다.
허가서
내가 사용한 모델은 이렇다. 당신이 가진 어떤 설정 파일에도 적용해볼 수 있다.
학교 현장학습에는 허가서가 필요하다. 종이 한 장이 진짜 통제 장치가 되려면 세 가지가 필요하다.
- 학교가 실제로 하는 일만 요청할 수 있어야 한다. 버스를 요청할 수는 있다. 허가서에 "헬리콥터도"라고 적는다고 헬리콥터가 나타나지는 않는다.
- 사무실이 알아보지 못하는 내용을 적으면, 되돌려줘야 한다. 헬리콥터가 올 것이라 믿게 놔둔 채 버스에 태워서는 안 된다. 최악의 결과는 반려된 허가서가 아니라, 수용된 허가서와 활주로에 남겨진 아이다.
- 본인이 직접 서명할 수는 없다. 부모는 "땅콩 금지"를 추가할 수 있다. 둘 중 누구도 "서명 필요"라는 항목을 지울 수는 없다.
그 세 문장이 내가 실제로 구현한 계약의 설계 전부다. 코드로 옮기면 다음과 같다.
규칙 1 — 컴파일은 되지만 확장은 안 된다
무언가를 설정하는 모든 키는 루프가 이미 준수하고 있는 설정값으로 변환된다. 파일은 새로운 노브를 만들어낼 수 없고, 이미 존재하는 노브를 채울 수만 있다. 최상위 레벨에 여덟 개, budget 블록 안에 열 개, gate 블록과 evidence 블록 안에 각각 하나씩, 그 외에는 아무것도 없다. 플래그 기반 경로와 파일 기반 경로는 결국 동일한 객체를 엔진에 넘긴다. 코드 경로는 하나뿐이지만, 그 경로로 들어가는 문은 둘이다.
규칙 2 — 모르는 단어는 오류이지, 어깨 한번 들썩이고 넘길 일이 아니다
이것이 바로 강제된 쿠버네티스의 교훈이다:
$ loop-anything contract check loop.yaml
Error: loop.yaml.budget: unknown key(s) ['max_iteratons']; accepted:
['compression_interval', 'max_iterations', 'max_tool_retries', 'max_wall_seconds',
'min_score_gain', 'plateau_patience', 'plateau_pivots', 'target_grade',
'target_score', 'token_budget']. A contract may only declare what the engine enforces.
파싱은 실패한다. 실행은 시작되지 않는다. 그리고 에러 메시지는 할 일 목록이다 — 수용되는 키를 하나하나 이름으로 짚어주므로, 오타를 잡는 데 디버깅 시간이 아니라 몇 초면 충분하다. (실제 출력은 터미널 한 줄이며, 여기서는 페이지에 맞추기 위해 줄바꿈했다.)
이 규칙의 후반부가 실은 판단력이 진짜로 필요했던 부분이다. 리뷰어가 제안한 스키마에는 이런 블록이 들어 있었다.
safety:
forbidden:
- delete_production_data
- expose_credentials
겉보기엔 훌륭하다. 어떤 컴플라이언스 심사도 무난히 통과할 것이다. 그런데 내 엔진에서는 이 블록을 읽는 코드가 그야말로 하나도 없다 — 실제 강제는 다른 곳에, 셸 메타문자를 거부하고 워크스페이스 밖 쓰기를 가로막는 샌드박스에 있다. 저 블록을 수용했다면, 오직 슬라이드 위에서 인용되는 것 말고는 아무 쓸모도 없는 장식용 안전 시스템을 하나 더 만들어낸 셈이 됐을 것이다.
아무도 강제하지 않는 규칙은 화재감지기 없는 건물에 걸린 금연 표지판이다. 불을 막지 못한다. 그저 그 불이 이제는 규정 위반이라는 사실만 남을 뿐이다.
그래서 파서는 그것을 이름으로 짚어, 이유와 함께 거부한다. domain: 키도 마찬가지다(실행 경로가 아직 도메인을 강제로 지정할 수 없으니, 이 키를 수용해봤자 아무 일도 일어나지 않는다). 차원별 채점 가중치도 마찬가지다(심사자는 자신만의 채점 기준을 소유하고 있으며, 평가받는 대상이 작성한 파일이 채점을 조종해서는 절대 안 된다). 각각은 무언가가 그것을 실제로 강제하는 순간 비로소 유효해진다. 에러 메시지그 자체가 로드맵이다.
규칙 3 — 게이트는 오직 더 엄격해질 수만 있다
내 엔진에는 이 모든 것보다 앞서 존재하는 규칙이 있다. 실행이 성공을 보고했다는 것은 사람이 확인하기 전까지는 하나의 주장일 뿐, 출하된 사실이 아니다. 사람이 지켜보는 실행에서 사람의 승인을 제공하는 플래그는 존재한다. 휴먼 게이트를 무력화하는 플래그는 의도적으로 존재하지 않는다 — 그리고 사람 없이 예약 실행되는 무인 실행이 자기 작업을 스스로 사전 승인하려 하면, 그것은 단호히 거부된다.
리뷰어가 제안한 계약(contract) 설계에는 불리언 값이 들어간 휴먼 게이트 블록이 포함되어 있었다: require_confirm_before_ship: true. 즉 이렇게도 쓸 수 있다는 뜻이다 false. 그리고 이 계약서는 실행 결과를 내보내고 싶어 하는 바로 그 사람이 직접 작성한다.
그래서 내 쪽에서는 이 필드를 생략하거나 이렇게 설정할 수 있다 true. 이걸 이렇게 설정하면 false 설명이 붙은 파싱 에러가 발생한다. 계약서는 심사자를 추가할 수는 있어도, 서명을 빼는 것은 결코 허용되지 않는다. 허가서에서 부모의 서명 요건을 지울 수는 없는 법이다.
정밀함을 논하는 글이니 이 부분도 정확히 짚어두자면, 이 필드는 런타임에서 아무것도 아닌 것으로 컴파일된다. 게이트는 기본적으로 이미 켜져 있고, 어떤 코드 경로도 이 키를 읽지 않는다. 이 필드의 유일한 임무는 false 를 수정 불가능하게 만드는 것이다 — 계약서 전체에서 파서가 단순히 어떤 설정값으로 통하는 문이 아니라, 그 자체로 준수(compliance)의 강제 장치가 되는 유일한 지점이다. 내가 세운 규칙 2에 따르면 이것은 읽는 주체가 없는 키에 해당하지만, 이 키가 막아주는 대상은 시끄럽게 막을 만한 가치가 있기에 남겨둔다. (이 게이트에는 우회 방법이 정확히 하나 있는데, 호출자가 쓸 수 있는 것은 아니다: 빌드 플랫폼이 소유한 환경 변수이며, 예약된 실행에서는 절대 허용되지 않는다.)
증거: 이것을 약속이 아니게 만드는 부분
파일의 마지막 절에는 실행이 반드시 증명해야 할 것들 — 전후 등급, 항목별 차이, 수정이 승인되었을 때 기록된 회귀 테스트 — 이 나열되어 있다. 이런 항목은 일곱 개 존재하며, 여기에 쓰는 이름은 반드시 그 일곱 개 중 하나여야 한다. 실제 실행이 만들어낼 수 있는 것이 그것뿐이기 때문이다.
그런 다음 명령 하나가 이 파일을 실제로 기록된 실행 결과와 대조해 검사한다:
$ loop-anything contract evidence loop.yaml --run 12
present grade_trajectory
MISSING regression_tests
Error: run #12 is missing declared evidence: regression_tests
종료 코드 1. 내 증명 묶음(proof pack)은 근거가 없는 필드를 조작하는 대신 생략하므로, 항목이 빠져 있다는 것은 약속된 것과 증명된 것 사이에 실제 간극이 있다는 뜻이다 — 앞의 예시로 말하면, 버그가 재발하는지 잡아낼 테스트를 한 번도 기록하지 않은 채 수렴한 실행이었다.
이것이 거버넌스 아티팩트와 거버넌스 겉치레(aesthetic)의 차이다. 둘 중 하나만이 당신의 이름이 박힌 실패를 출력할 수 있다.
리뷰어가 중요한 방식으로 틀렸던 이유
내가 그 백로그를 그대로 실행하지 않은 두 번째 이유가 있는데, 이는 이번 리뷰 자체에 관한 것은 아니다.
이 엔진의 핵심 원칙은 만드는 주체가 곧 채점하는 주체여서는 안 된다는 것이다. Arjun Panickssery, Samuel Bowman, Shi Feng은 2024년에 이 원칙에 측정치를 부여했다. 이들은 이렇게 쓴다: [3]:
"One such bias is self-preference, where an LLM evaluator scores its own outputs higher than others' while human annotators consider them of equal quality." (번역: 그러한 편향 중 하나가 자기선호(self-preference)인데, LLM 평가자는 인간 평가자가 동등한 품질로 판단하는 결과물에 대해서도 자기 자신의 결과물에 더 높은 점수를 준다.)
이들은 한 걸음 더 나가는데, 이 부분이 핵심이다: 자기 텍스트를 알아보는 능력을 높이도록 모델을 파인튜닝하면 다음과 같은 결과가 나온다는 것이다: "a linear correlation between self-recognition capability and the strength of self-preference bias." (번역: 자기인식 능력과 자기선호 편향의 강도 사이에는 선형적 상관관계가 있다.) 모델이 자신의 목소리를 알아보는 데 능숙해질수록, 그것을 더 편애하게 된다. 자기평가는 무작위로 실패하는 것이 아니라, 방향성을 가지고.
실패한다. 당신의 레포에 대한 AI 리뷰는 AI가 자기 결과물을 채점하는 것과 같지 않다 — 이 리뷰어는 내 코드에 아무 이해관계가 없었다. 하지만 근본적인 작동 방식은 동일하며, 바로 이것을 거부하기 위해 이 엔진 전체가 존재한다. 즉, 그 상대의 근거를 직접 확인하지도 않은 채 판정을 그대로 받아들이는 것이다: 그 상대의 근거를 직접 확인하지도 않은 채 판정을 그대로 받아들이는 것이다. 그 리뷰의 근거는 내 README였다. 원본과 대조해 보니 여덟 개 항목 중 다섯 개의 점수가 내 재채점에서 바뀌었다 — 세 개는 내렸고 두 개는 올렸다. 리뷰가 가장 낮게 준 항목인 범용성은 내가 가장 많이 올린 항목이었고, 내가 내린 항목은 안전성, 관찰 가능성, 엔터프라이즈 준비도였는데, 리뷰는 코드가 받을 자격이 없을 만큼 이 항목들에 너그러웠다.
패턴과 안티패턴
패턴. 파서는 실패 시 닫히도록(fail closed) 만들고, 실패 메시지에는 준수 가능한 옵션을 나열하라. 정책 파일은 오직 기본값을 강화할 수만 있게 하고, 절대로 완화하지는 못하게 하라. 각 필드를 수용하기 전에 그것을 읽어들일 대상을 먼저 명시하라. 선언된 근거와 실제로 산출된 근거를 종료 코드로 대조 검증하라. 리뷰에 대한 판정 내역을 코드 옆에 함께 커밋해서, 그 논증이 버전 관리되게 하라.
안티패턴. 아무도 읽지 않는 설정 키. 어떤 안전 코드도 열어보지 않는 파일 속 안전 블록. 작성자가 자신에 대한 검토를 스스로 면제할 수 있게 하는 불리언 값. 시스템을 확장하려면 반드시 수정해야 하는, 닫힌 목록으로 된 타입 스키마. 번호가 매겨지고 형식이 잘 갖춰졌다는 이유만으로 백로그를 그대로 실행하는 것. 그리고 가장 오래된 안티패턴: "파싱되었다"를 "이해되었다"로 취급하는 것.
메커니즘, 한 문장으로
이 메커니즘에는 이름이 있다: 수용-준수 격차(enforcement gap) — 설정 파일이 무엇을 수용한다고 선언하는지와, 실제로 어떤 코드 한 줄이 그것을 준수하며 읽어들이는지 사이의 거리다. 조용히 숨어드는 모든 설정 버그와 장식용 컴플라이언스 산물은 모두 이 격차 속에 산다. 그리고 이 격차는 구조적으로 눈에 보이지 않는다 — 무시되는 파일은 준수되는 파일과 완전히 똑같아 보이기 때문이다.
기록상 가장 큰 사례는 설정 파일이 전혀 아니다. Do Not Track은 여러분이 켜두면 브라우저가 "추적당하고 싶지 않다"는 뜻으로 보내는 헤더였다. 브라우저들은 몇 년 동안 이 헤더를 충실히 보냈다. 그러나 어떤 서버도 이를 준수할 의무는 없었다. 이 헤더를 다루던 워킹 그룹은 2019년 1월 17일에 문을 닫았고, 관련 명세들은 폐기되었다[4] — 완벽하게 수용되었으나, 그 누구도 준수할 필요가 없었던 선호 설정의 10년이었던 셈이다.
그래서 첫 번째 원칙은 이렇다: 실패할 수 없는 선언은 통제가 아니라 코스튬이다 — 그러므로 시스템에 거버넌스를 더하는 유일하게 정직한 방법은, 거부 기능을 동시에 더하는 것이다.
이 글이 주장하지 않는 것
- 벤치마크는 없다. 이 계약 파일이 실제 현장에서의 설정 오류를 줄이는지는 측정하지 않았다. 이것은 하나의 구현을 곁들인 설계 논증일 뿐, 연구가 아니다.
- 그 리뷰가 나빴다는 뜻은 아니다. 그 절반은 맞는 말이었고, 그중 최고의 아이디어 — 더 강한 판정자라면 동의하지 않을 상황에서 내 루프가 성공을 보고하는 빈도를 측정하는 것 — 는 내가 아직 만들지 못한, 그리고 가장 필요로 하는 것이다.
- 엔진 하나, 저자 한 명. 이 세 가지 규칙은 모두 내 코드베이스에서 나온 것이다. Kubernetes와 IAB 사례는 이 원칙 자체를 뒷받침할 뿐, 내 특정 구현을 뒷받침하는 것은 아니다.
- 테스트 개수는 품질이 아니다. 테스트 스위트는 543개에서 600개 통과로 늘었다. 이는 새 코드에 대한 커버리지를 측정할 뿐, 설계가 옳다는 것을 측정하지는 않는다.
결론
업그레이드는 하루 만에 배포됐다. 그런데 교훈을 얻는 데도 꼬박 하루가 걸렸고, 그 교훈은 YAML에 관한 것이 아니었다.
아주 유창한 시스템이 당신 작업의 문제점을 아주 잘 정리된 목록으로 건넬 때, 그 목록의 유창함은 근거가 얼마나 탄탄한지에 대해 아무것도 말해주지 않는다. 그것이 무엇을 읽었는지 물어보라. 그런 다음 그것이 읽을 수 없었던 것을 직접 읽어보라.
그리고 마침내 거버넌스 파일을 — 계약서든, 정책이든, 표준이든, 체크리스트든 — 작성할 때는 거부 조항부터 써야 한다. 당신 이름이 박힌 에러를 출력하게 만들 입력을 설명할 수 없다면, 통제 장치를 만든 것이 아니다. 표지판을 인쇄한 것에 지나지 않는다.
참고 문헌
- Jeffrey Ying and Antoine Pelisse (Google), Kubernetes 1.27: Server Side Field Validation and OpenAPI V3 move to GA, Kubernetes Blog, 2023년 4월 24일. kubernetes.io/blog/2023/04/24/openapi-v3-field-validation-ga. 원문 그대로 인용했으며, 괄호 안 느낌표는 원저자가 직접 붙인 것이다.
- M. Thomson and D. Schinazi, Maintaining Robust Protocols, RFC 9413, Internet Architecture Board(IAB) 스트림, 2023년 6월. rfc-editor.org/rfc/rfc9413. 두 인용문 모두 RFC 원문(2절 및 4.1절)과 대조해 검증했다.
- Arjun Panickssery, Samuel R. Bowman and Shi Feng, LLM Evaluators Recognize and Favor Their Own Generations, arXiv:2404.13076, 2024년 4월 15일. arxiv.org/abs/2404.13076. 초록에서 인용.
- W3C Tracking Protection Working Group. 해당 워킹그룹 페이지에는 이렇게 적혀 있다: "The working group is currently closed. It closed on 17 January 2019," 그리고 Tracking Preference Expression(DNT) 문서들을 폐기된 명세(retired specifications) 항목에 올려두었다. w3.org/2011/tracking-protection.
- 그 리뷰를 항목별로 판정한 기록이며, 논쟁의 대상인 코드 바로 옆에 커밋되어 있다: docs/solutions/external-eval-2026-08-12.md. 계약 파일 자체는
src/loopeng/contracts/run_contract.py, 그 설계 노트는docs/solutions/run-contract.md, 그리고 주석이 달린 예시는docs/examples/loop.yaml이며, 위치는 github.com/wjlgatech/loop-engineering-anything.
2026년 8월 12일 작성. 이 글을 촉발한 리뷰는 한 독자의 요청으로 프런티어 언어 모델이 작성한 것이다. 나는 그 논지는 남겨두었지만 그 정체는 밝히지 않았는데, 중요한 것은 출처이지 모델이 아니기 때문이다.