네 가지 실험을 통해 '결정론적 에이전트 루프(deterministic agent loop)' 주장을 테스트해 보았습니다. 제가 직접 만든
요약
프로덕션급 AI 에이전트 구축 시 제안되는 '결정론적 에이전트 루프' 메커니즘의 실효성을 네 가지 실험을 통해 검증했습니다. 어휘 중첩 임계값, Temperature-0 평가자, 페이즈 게이트 등의 방식이 의미론적 계층에서 실패함을 입증하며 엔지니어링 가드레일의 한계를 지적합니다.
핵심 포인트
- 에이전트 루프에 엔지니어링 가드레일이 필요하다는 방향성은 옳음
- 어휘 중첩 임계값 방식은 의미가 아닌 문자를 읽어 50%의 오분류 발생
- Temperature-0 평가자와 페이즈 게이트 역시 결정론적 해결책으로서 한계 노출
- 단순한 코드 제약만으로는 LLM의 의미론적 불확실성을 완전히 통제하기 어려움
특정 유형의 "프로덕션급 AI 에이전트(production-grade AI agent)" 관련 글들이 유행하고 있습니다. 어떤 형태인지 아실 겁니다. ReAct 루프가 프로덕션 환경에서 깨지기 때문에, LLM의 불확실성 위에 결정론적(deterministic) 제약 조건을 쌓아야 한다고 주장합니다. 예를 들어 pre-AL 게이트, temperature 0에서의 LLM-as-Judge, 페이즈 게이트(phase gate), 결정 상태 머신(decision state machine) 같은 것들 말이죠. 제가 염두에 두고 있는 글은 프로덕션용 Rust 코드가 7,000줄 이상이라고 주장합니다.
방향은 맞습니다. 에이전트 루프에는 엔지니어링 가드레일(guardrails)이 필요합니다. LLM이 스스로 승리를 선언하게 내버려 둘 수는 없습니다. 학술적 환상에서 벗어나 "자기 완결형 에이전트(self-contained agents)"를 엔지니어링 현실로 끌어오는 것은 가치 있는 움직임입니다.
문제는 신뢰를 조작하기 위해 결정론적(deterministic), 객관적 사실(objective fact), _코드가 LLM을 거부한다(code vetoes the LLM)_와 같은 단어들을 반복적으로 사용한다는 점입니다. 과연 그 주장들이 실제로 유효할까요?
저는 논쟁하지 않았습니다. 네 가지 실험을 수행했습니다. 결론은 다음과 같습니다: "결정론"을 확립하기 위해 사용되는 세 가지 핵심 메커니즘 각각은 형식적으로만 결정론적일 뿐, 모두 의미론적 계층(semantic layer)에서 실패합니다. 그리고 이를 해결하기 위해 제가 준비한 "업그레이드"마저도 실패했습니다.
데이터는 다음과 같습니다.
먼저 공정한 평가를 하자면
이런 종류의 글에서 가장 가치 있는 점은 문제 인식입니다. 순수 ReAct 루프의 세 가지 실제 결함은 다음과 같습니다: 종료 조건(termination condition) 부재, 인터럽트 처리(interrupt handling) 부재, 유휴 루프(idle-loop) 보호 부재. LLM의 불확실성을 결정론적 제약 조건으로 감싸는 제안된 방향은 옳습니다.
문제는 방향이 아니라 착륙 방식입니다. 이러한 글들은 세 가지 특정 메커니즘을 해결된 정답처럼 취급하지만, 실제 동작은 측정 과정에서 살아남지 못합니다.
저는 정확히 이 세 가지를 테스트했습니다:
- 어휘 중첩 임계값(Lexical-overlap thresholds) — 사용자의 개입이 새로운 작업인지 아니면 부연 설명인지 결정
- Temperature-0 평가자(Temperature-0 evaluators) — 에이전트가 완료되었는지 결정
- 페이즈 게이트(Phase gates) — 작업 완료가 "객관적 사실(objective fact)"인지 결정
세 가지 실험 모두 해당 글들이 설명하는 방법과 매개변수를 그대로 사용했으며, 그 결과 해당 글들의 주장 자체가 거짓임을 입증했습니다.
환상 1: 어휘 중첩 = 의미론(semantics)?
5번째 턴의 루프 중간(Mid-loop)에 사용자가 개입합니다: "사실, X로 바꿔주세요." 이것은 이전 작업에 대한 부가 사항(addendum)인가요, 아니면 완전히 새로운 작업인가요?
제안된 해결책: 두 개의 고정된 임계값(threshold)을 사용하여 "어휘 중첩 (lexical overlap)" 점수를 계산합니다. ≥0.24는 동일 작업, ≤0.08은 새로운 작업을 의미하며, 그 사이의 값은 LLM으로 보냅니다. 주장은 "80%가 코드로 즉시 결정된다"는 것입니다.
공학적인 수준의 해결책처럼 들립니다. 하지만 어휘 중첩은 의미가 아닌 문자를 읽습니다. 저는 30개의 라벨링된 쌍(labeled pairs)을 구축하고, 해당 임계값을 적용한 뒤, 세 가지 토크나이저(tokenizer)를 실행했습니다.
결과: 50%의 심각한 오분류 (hard misclassification).
최악의 사례들:
현재 작업: "loop-engine 기사 작성을 계속하세요"
사용자 개입: "loop-engine 기사를 삭제하세요"
중첩도 0.615 → 동일 작업으로 판단
사용자는 삭제를 말했지만, 엔진은 "작성과 동일함"이라고 판단하여 계속해서 글을 씁니다. 반대되는 작업이 연속된 작업으로 취급됩니다. 이는 사고(incident) 수준의 문제입니다.
현재 작업: "결제 버그를 수정하세요"
사용자 개입: "결제 페이지에서 오류가 발생하고 있어요, 확인해 줄래요?"
중첩도 0.000 → 새로운 작업으로 판단
어떤 인간이라도 이를 하나의 작업으로 봅니다. 자카드(Jaccard) 지수는 0을 줍니다. 의역(Paraphrase)은 완전히 실패했습니다 — 6개 중 6개 모두 틀렸습니다. 교차 언어(Cross-lingual) 상황은 더 심각합니다. 동일 작업인 영어/중국어(EN/ZH) 쌍 6개 모두 점수가 0.000이며, 모두 새로운 작업으로 판단되었습니다. 어떤 이중 언어 환경에서도 이 메커니즘은 접촉 즉시 붕괴합니다.
옹호론자는 이렇게 말할지도 모릅니다: "코드가 90%의 사례에서 호출을 수행하니, 우리가 약속한 80%를 상회합니다."
그것은 미끼를 던지고 바꿔치기하는 것(bait-and-switch)입니다. "80%가 코드로 결정된다"는 암묵적인 약속은 "80%가 정확하게 결정된다"는 뜻입니다. 현실은 이렇습니다: 코드가 27번의 판결을 내렸고 그중 12번만 맞았습니다 — 정확도 44%.
"결정된다"를 "정확하게 결정된다"로 취급하는 것은 전체 설계에서 가장 위험한 수사적 기법입니다.
임계값은 쉬운 샘플(중첩도가 높은 동일 작업, 중첩도가 낮은 새로운 작업)에서만 작동합니다: 12/12 정답. 하지만 세 가지 "흔하지만 어려운" 카테고리 — 의역(paraphrase), 교차 언어(cross-lingual), 반의어(antonym) — 는 0/16입니다. 이는 임계값이 쉬운 데이터셋에 맞춰 튜닝되었음을 강력하게 시사합니다. 사소하지 않은 샘플 분포가 나타나면 즉시 무너집니다.
환상 2: temperature 0 = 결정론(determinism)?
이 기사는 "동일한 입력에 대해 평가가 가능한 한 일관되어야 하기" 때문에 평가자의 temperature(온도)를 0.0으로 설정하여 "출력이 거의 전적으로 결정되도록" 설정합니다.
이는 한 문장으로 테스트 가능합니다: 동일한 프롬프트, temperature 0, 20번 실행하여 일관성을 확인합니다.
저는 GLM-5.2에서 세 가지 프롬프트 카테고리에 대해 각각 20번씩 실행했습니다.
결과: 개방형 출력(open-ended output)의 일관성은 70%에 불과하며, 30%는 발산합니다.
| 프롬프트 유형 | 완전 일치율 (Exact-match rate) | 고유 버전 수 (Distinct versions) |
|---|---|---|
| 수학 (가장 안정적) | 100% | 1 |
| ... |
개방형(open-ended) 행이 결정적인 문제입니다. 동일한 프롬프트, temperature 0, 20번 실행 시 5개의 서로 다른 버전이 나타났으며, 가장 낮은 쌍별 유사도(pairwise similarity)는 0.198이었습니다:
"매일 마시는 특별한 커피를 위해 항상 북쪽으로 향하세요."
"앞으로의 여정을 위한 프리미엄 커피."
공유되는 문자가 거의 없습니다. 그리고 LLM-as-Judge(판단자로서의 LLM) 평가자는 정확히 이런 종류의 개방형 텍스트를 출력합니다 — done / phase_done / reason / evidence.
기사에서는 "평가자는 창의적 글쓰기를 하는 것이 아니라 판단을 하는 것이므로 temperature는 반드시 0이어야 한다"라고 말합니다. 하지만 평가자의 reason(이유) 및 evidence(근거) 필드는 본질적으로 개방적입니다. 측정된 발산 정도는 창의적 프롬프트와 동일한 수준입니다.
"구조화된 목록화(structured listing)"조차 불안정합니다. 다섯 개의 형용사가 다른 순서로 나타납니다. 만약 evidence가 목록이고 그 순서가 바뀐다면, 다운스트림(downstream) JSON이 변경되고 결정(decision)도 변경됩니다.
유일하게 100% 결정론적인 사례는 "17×23=391"입니다. 이는 다음 규칙을 증명합니다: temperature-0 결정론은 정답 공간(answer space)이 극도로 좁을 때만 유지됩니다. 출력이 조금이라도 개방성을 띠는 순간 결정론은 깨집니다. 좁은 특수 사례를 보편적인 속성으로 취급하는 것은 과잉 일반화입니다.
평가자의 재현성(reproducibility)은 전체 루프 엔진의 토대입니다. 불안정한 평가 → 단계 게이트(phase gate)로 전달되는 불안정한 done 신호 → 불안정한 결정 상태 머신(decision state machine). 토대가 흔들리면, 그 위에 쌓인 10개 층의 "결정론적 제약(deterministic constraints)"은 흔들리는 기반 위에 서 있는 것과 같습니다.
(단 하나의 제공자, GLM-5.2만 테스트했습니다. 하지만 이 글의 주장은 보편적이므로, 단일 제공자에 대한 반증만으로도 충분합니다. OpenAI의 temp-0 비결정성(non-determinism)은 이미 문서화되어 있고 독립적으로 확인되었습니다. 더 많은 제공자를 테스트한다면 이 주장은 더욱 강화될 것입니다.)
환상 3: 단계 게이트(phase gate) = 작업 완료?
이 분야에서 가장 자신만만하게 나오는 문구는 다음과 같습니다: "LLM의 자기 주장(self-claim)에서 검증 가능한 객관적 사실로 변환된 작업 완료(task completion)."
단계 게이트(phase gate)는 네 가지를 확인합니다: 스크립트가 exit 0으로 종료되었는지, 파일이 존재하는지, 파일 개수가 충족되었는지, 사용자 확인 기록이 있는지. 이 모든 것은 코드 내에서 이루어지며, 모두 "객관적 사실"을 확인합니다.
문제는 — 이러한 확인 절차는 작업이 발생했음을 검증할 뿐, 결과가 올바른지를 검증하는 것이 아니라는 점입니다.
저는 해당 기사의 설명에 따라 단계 게이트를 구현하였고 8가지 시나리오를 구축했습니다: 4개는 올바른 콘텐츠를 포함하고, 나머지 4개는 게이트를 통과할 수 있는 쓰레기(garbage) 콘텐츠를 포함합니다.
결과: 게이트 통과율 100%, 콘텐츠 정확도 50%, 위양성(false-positive) 비율 50%.
네 가지 위양성 사례는 다음과 같습니다 (원문의 표현을 빌리자면):
| 작업 | 실제 출력 | 게이트 판정 |
|---|---|---|
| 연구 요약 작성 | "나는 작은 오리입니다, 꽥꽥." | ✅ 통과 → "완료" |
| ... | ... | ... |
오리, 마침표, TODO, 테스트 케이스 0개 — 단계 게이트는 이 모든 것을 통과시킵니다. 콘텐츠의 정확성에 대해서는 전혀 식별 능력이 없습니다.
이것은 구현상의 버그가 아닙니다. 기사에서 설명하는 네 가지 확인 절차는 구조적으로 콘텐츠를 읽지 않기 때문입니다. 어떤 충실한 구현이라도 동일한 사각지대를 가질 수밖에 없습니다. exit 0은 프로세스가 충돌하지 않았음을 의미할 뿐, 결과가 맞다는 것을 의미하지 않습니다. 파일 존재 여부는 경로가 존재함을 의미할 뿐, 콘텐츠가 요구 사항을 충족함을 의미하지 않습니다.
"파일 존재 / 스크립트 실행"을 "작업 완료"로 포장하는 것은 주장의 과잉 확장입니다. 진실은 이렇습니다: 단계 게이트는 **"어떤 동작이 발생했다"**는 것을 객관적 사실로 바꿉니다. 하지만 **"작업이 완료되었다"**는 것을 객관적 사실로 바꾸지는 못합니다. 이 두 가지 사이에는 게이트가 넘을 수 없는 의미론적 간극(semantic gap)이 존재합니다.
그 간극이 바로 **콘텐츠 품질(content quality)**이며, 이것이 바로 실제 사용자들이 가장 중요하게 생각하는 부분입니다.
세 가지 기둥, 모두 균열이 생겼습니다
이 장르의 핵심 명제는 다음과 같습니다: "LLM의 불확실성 (uncertainty) 위에 결정론적 제약 조건 (deterministic constraints)을 쌓아 올린다."
이제 측정(measurement)을 통해 세 가지 "결정론 (determinisms)"이 모두 뚫렸음을 확인했습니다:
| 기둥 (Pillar) | 기사 주장 | 측정 결과 | 상태 |
|---|---|---|---|
| 어휘적 중첩 (Lexical overlap) = 의미론 (semantics) | "코드에 의해 80% 결정됨" | 50% 오분류, 44% 정확도 | ❌ |
| ... |
세 가지 기초 계층이 모두 누수되고 있습니다. 그 위의 10개 제약 계층은 누수되는 기반 위에 서 있는 셈입니다.
7,000줄의 Rust 코드는 아마 실제일 것입니다. 하지만 그것들은 기호 계층 (symbolic layer) — 문자열 매칭 (string matching), 파일 경로 (file paths), 종료 코드 (exit codes) — 를 보호할 뿐입니다. 의미론적 계층 (semantic layer; 의도, 콘텐츠, 품질)은 여전히 무방비 상태로 실행되고 있습니다.
왜 이 장르가 바이럴되는가
이것은 데모는 만들어 보았지만 실제 운영 환경(production)에는 적용해 보지 못한 독자들의 불안감을 정확히 파고듭니다. LLM 시스템을 실제 운영 환경에서 실행해 보지 않은 사람에게는, 이러한 메커니즘의 더미가 매우 무겁고 권위 있게 느껴집니다. 그들은 이러한 관행들을 본 적이 없으며, 이것들이 의미론적 계층 (semantic layer)에서 실패한다는 사실을 알지 못하기 때문입니다.
반면, 실제로 운영 환경을 경험해 본 사람이라면 이를 읽고 "내용보다 이름이 더 그럴싸하다"라고 생각할 것입니다. Pre-AL 게이트는 프롬프트 주입된 상태 (prompt-injected state)이고, temperature-0 LLM-as-Judge는 평가자 위생 (evaluator hygiene)이며, "결정론 우선 (determinism-first)"은 try/catch와 문자열 매칭의 조합이고, 페이즈 게이트 (phase gate)는 검증 로직 (validation logic)이며, 10개의 우선순위 레벨은 if-else 체인에 불과합니다. 모든 메커니즘은 올바르며 수행할 가치가 있습니다. 하지만 "독창적인 프레임워크"라는 인상을 제조하기 위해 각 요소에 고유한 용어를 붙이는 것은 **혁신이 아니라 리브랜딩 (rebranding)**입니다.
더 뼈아픈 지점은 이렇습니다. 이 기사들은 "의사 코드 (pseudocode)도 아니고, 개념도 (concept diagram)도 아니다"라고 서두를 떼지만, 실제 코드 한 줄도 제공하지 않습니다. 오직 함수 이름, 상수, 매개변수 값만 나열할 뿐입니다. 그것들은 식별자 (identifiers)이지 코드가 아닙니다. 약속은 지켜지지 않았습니다.
그리고 "운영 환경 수준 (production-grade)"의 증거로 반복해서 인용되는 "7,000줄 이상"이라는 표현은 세 번이나 등장합니다. 코드 라인 수는 품질을 나타내는 최악의 대리 지표 (proxy)입니다. 실제로 운영 환경에서 실행되는 시스템이라면 SLO 데이터, 사후 분석 (postmortems), 부하 테스트 곡선 (load-test curves)을 제시해야지, 라인 수를 제시해서는 안 됩니다.
네 번째 절단: 저도 거짓말을 했습니다
첫 세 가지 실험은 이 장르의 '결정론(determinism)'이라는 세 가지 기둥을 겨냥합니다. 데이터가 말해주는데, 모두 실패했습니다.
하지만 여기서 솔직해져야 합니다. 저는 이 세 가지 절단 뒤에 '건설적인 업그레이드'를 준비하고 있었습니다 — 어휘 중복도를 높이기 위한 임베딩(embedding), 온도 0으로 패치하기 위한 다중 투표(multi-vote), 그리고 단계 게이트(phase gate)를 보강할 두 번째 LLM이었습니다. 저는 이것이 이 글을 '비판'에서 '구축'의 영역으로 끌어올릴 것이라고 생각했습니다.
저는 틀렸습니다. 그 제안은 비판하는 기사들과 같은 질병을 가지고 있습니다: 복잡한 공학 기술을 사용해 의미론적 해결책을 위장합니다.
스스로를 설득하기 위해 실험을 진행했습니다. 대상이 아닌, 저의 제안에 대한 것이었습니다. 저는 Qwen3-embedding:0.6b (실제 신경 임베딩 모델, 1024차원)를 사용하여 정확히 같은 동의어 대 반의어 분리 테스트를 했습니다.
결과:
| 카테고리 | 평균 | 최소 | 최대 |
|---|---|---|---|
| 동의어 (높아야 함) | 0.766 | 0.490 | 0.977 |
| ... |
동의어(0.766)와 반의어(0.739)는 0.026으로 차이가 나 — 분리하기에는 너무 가깝습니다.
'optimize code performance' 대 'don't optimize code performance' — 코사인 값 0.881로, 12개의 동의어 쌍 중 10개보다 높습니다.
'build a login-registration feature' 대 'add the account-auth piece' (이것들은 동의어입니다) — 코사인 값 0.490으로, 거의 모든 반의어 쌍보다 낮습니다.
신경 임베딩이 할 수 있는 유일한 분리는 '관련 있음 대 관련 없음'이며 — 동의어/반의어 모두 0.75 근처에 위치하고, 관련 없는 것은 0.326으로 떨어집니다. 하지만 주제가 같고 방향만 반대인 순간, 임베딩은 Jaccard와 똑같이 실패합니다.
따라서 전체 분리 체인 — 문자에서 통계로, 신경 벡터까지 — 은 측정상으로 실패합니다:
- Jaccard (실험 1): 50% 오분류. 분리 불가.
- TF-IDF char 2-gram: 동의어 0.072, 반의어 0.222 — 방향이 역전됨. 실패.
- Qwen3-embedding (실험 4): 동의어 0.766, 반의어 0.739, 차이 0.026. 실패.
저의 '임베딩 업그레이드'는 이 데이터 속에서 살아남지 못했습니다. 저는 그것을 삭제하고 정직한 버전으로 대체할 것입니다.
솔직한 결론: 현재의 스택(stack) 하에서는 이 문제에 대한 공학적 해결책이 없습니다
이 장르의 세 가지 "결정론(determinism)" 기둥이 모두 무너졌습니다. 임베딩(embedding), 다중 투표(multi-vote), 그리고 두 번째 LLM을 통해 이를 보완하려던 저의 시도 또한 실패했습니다:
- 임베딩(Embedding)은 유의어(synonymy)와 반의어(antonymy)를 구분할 수 없습니다 — 동일한 주제라도 반대 방향의 의미를 가지면 거의 동일한 벡터(vector)를 생성합니다.
- 두 번째 LLM은 첫 번째 LLM의 신뢰할 수 없는 문제를 해결하지 못합니다 — 검사자(inspector) 자체가 환각(hallucination)을 일으키며, 이는 단지 문제를 한 단계 위 계층으로 옮길 뿐입니다.
결론: 사용자가 현재 주제에 방향성이 모호한 내용(새로운 작업인가 아니면 부연 설명인가? 동일한 방향인가 아니면 반대 방향인가?)을 개입시킬 때, 공학적 설계는 알고리즘이 일방적으로 결정하게 두어서는 안 됩니다. 주제의 중첩(topic overlap)을 감지한 다음, 인간에게 물어보십시오. 자동으로 판결(auto-adjudicate)하지 마십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기