에이전트가 규칙을 자꾸 빠뜨려서 프롬프트 끝에 "스스로 다시 확인하라"를 붙였습니다. 확인은 대체로 통과했고, 그래도 규칙은 빠졌습니다. 답을 만든 모델이 그 답의 채점까지 맡고 있었기 때문입니다. 수행하는 책임과 채택하는 책임을 나눈 과정과, 나누고 나서도 보장되지 않는 것이 무엇인지 정리합니다.
스스로 확인하라는 문장에 필수 항목을 맡기고 있었습니다
컴파일한 워크플로우를 외부에서 호출할 수 있게 만든 다음, 실제로 쓰이던 다단계 업무 검토 흐름을 다시 봤습니다.
그 흐름은 여러 하위 단계로 나뉘어 있었고, 각 단계의 판정 방법과 금지 조건, 결과 형식이 전부 긴 시스템 프롬프트 안에 함께 들어 있었습니다.
같은 모델이 자료를 읽어 결과를 만들고, 바로 이어서 자기 결과가 규칙을 지켰는지 판단합니다. 구현은 간단합니다.
문제는 그 두 일이 한 컨텍스트에 있다는 것이었습니다. 생성 방법과 합격 기준이 같은 문장 안에서 경쟁합니다. 빠뜨리면 안 되는 필수 항목까지 "스스로 다시 확인하라"는 지시 하나에 매달립니다.
빠졌을 때 어느 규칙 때문에 다시 실행됐는지도 알 수 없었습니다. 모델은 "확인했습니다"라고 답하고 넘어갔습니다.
여기서 저희가 다시 본 것은 프롬프트의 구조가 아니라 누가 채택을 결정하는가였습니다. 1편에서 통과 여부를 코드가 정하게 했던 것과 같은 문제가, 이번에는 업무 규칙의 층위에서 돌아온 것입니다.
그래서 5월 말 설계를 시작해 6월 초 캔버스의 하네스 노드 실행 경로에 적용했습니다. 업무 데이터 추출과 문서 처리 도구는 생성 경로에 남기고, 반드시 지켜야 할 판정 기준과 출력 스키마는 판정 단계와 정책 검사로 옮겼습니다.
화면에 노드를 두 개 만든 것이 아닙니다. 한 하네스 노드 안에서 생성 단계와 판정 단계를 서로 다른 상태로 나눴습니다.
판정기에게 대화 전체를 보여줬더니 무엇을 채점했는지 알 수 없었습니다
처음 만든 판정 경로는 생성 대화 전체를 판정기에 그대로 넘겼습니다. 맥락이 많을수록 잘 판단할 것 같았습니다.
그 안에는 도구 호출과 중간 추론, 이전에 반려된 후보가 전부 섞여 있었습니다. 판정 결과를 받아도 어느 후보를 대상으로 한 점수인지 분명하지 않았습니다.
판정 모델을 바꾸려고 하니 문제가 더 커졌습니다. 새 판정기가 생성 대화의 형식까지 알아야 했습니다. 판정기를 갈아끼우는 일이 생성 단계를 건드리는 일이 됐습니다.
그래서 입력을 좁혔습니다. 사용자의 현재 요청, 이번에 평가할 답 후보, 평가 기준만 받습니다. 근거로 쓴 도구 결과가 필요하면 별도 필드로 주되, 생성 단계의 내부 상태를 통째로 넘기지 않습니다.
출력도 자유로운 비평문 대신 구조를 정했습니다. 기준별 점수와 사유, 전체 점수, 짧은 피드백, 그리고 통과인지 재시도인지의 판단입니다.
생성기 → 답 후보
↓
독립 판정기 → 기준별 점수·피드백
↓
상태 머신 → complete 또는 retry
입력을 좁히자 판정 결과가 등급표가 아니라 다음 전이의 입력이 됐습니다. 생성 모델은 "더 잘 써라" 대신 어떤 기준을 왜 충족하지 못했는지 받아서 새 후보를 만듭니다.
평가자에게 맥락을 많이 주는 것이 좋아 보이지만, 맥락이 넓어질수록 그 평가가 무엇에 대한 것인지 흐려집니다. 평가 대상을 한 문장으로 적을 수 없으면 그 점수는 나중에 해석할 수 없습니다.
점수 하나로는 다음 후보가 무엇을 고쳐야 할지 알 수 없었습니다
초기 판정기는 종합 점수 하나를 돌려줬습니다. 문턱을 넘으면 통과, 못 넘으면 재시도입니다.
재시도를 걸어놓고 보니 다음 후보가 나아지지 않았습니다. 0.62라는 숫자는 무엇이 부족했는지 말해주지 않고, 그 숫자를 그대로 돌려받은 생성기도 무엇을 고칠지 알 수 없었습니다.
점수가 낮다는 사실과 무엇을 고쳐야 하는지는 다른 정보였습니다.
그래서 기준마다 이름과 설명, 가중치, 필수 여부를 뒀습니다. 업무마다 좋은 답의 기준이 다르기 때문이기도 합니다. 요약 에이전트와 코드 생성 에이전트가 관련성, 완성도, 형식 준수, 근거 충실도를 같은 비율로 볼 이유가 없습니다.
캔버스에서 만든 기준은 독립 산출물의 컴파일 규격에도 포함했습니다. 제품 화면에서 정한 기준이 독립 실행에서 사라지면 같은 에이전트라고 부를 수 없습니다. 판정기 모델과 기준 버전, 통과 문턱도 실행 기록에 남겨서 점수의 의미가 바뀐 시점을 구분할 수 있게 했습니다.
필수 기준을 넣으면 지켜질 줄 알았는데 그것도 모델 판정이었습니다
기준에 필수 표시를 달면서 저희는 이제 빠뜨릴 수 없는 항목이 생겼다고 생각했습니다. 반드시 포함해야 할 필드가 없으면 문장이 아무리 자연스러워도 통과시키지 않습니다.
그런데 그 필수 판정을 하는 주체가 여전히 모델이었습니다.
필수 기준은 다른 장점으로 상쇄되지 않는다는 규칙일 뿐, 그 기준을 충족했는지 판단하는 일 자체는 여전히 흔들릴 수 있는 판정입니다. 이름을 필수로 붙였다고 보장이 생기지는 않았습니다.
그래서 문턱을 두 종류로 나눴습니다.
품질 판정기 얼마나 충족했는가 관련성 · 완성도 · 근거 충실도 · 명확성
→ LLM 판정. 정도를 비교하는 데 쓴다
정책 게이트 위반하면 실행할 수 없다 개인정보 · 금지 도구 · 사용자 승인 · 비용 한도
→ 코드로 확정. 판정 응답이 흔들려도 같이 흔들리지 않는다
JSON 필드가 있는지처럼 코드로 확정할 수 있는 조건은 규칙 기반 검사로 옮겼습니다.
정책이 차단하면 품질 점수가 높아도 실행이 멈춥니다. 반대로 정책을 통과했다고 좋은 답이라는 뜻도 아닙니다. 이 구분 덕분에 '안전하지만 부족한 답'과 '내용은 좋지만 실행할 수 없는 답'이 서로 다른 상태와 종료 이유로 남습니다.
LLM 판정기를 도입하는 조직에서 이 경계가 가장 자주 무너집니다. 판정기가 잘 동작하기 시작하면 개인정보와 비용 한도까지 거기에 넣고 싶어집니다. 판정기의 응답이 흔들리는 날 보안 정책이 같이 흔들려서는 안 됩니다. 지켜야 하는 것과 잘하면 좋은 것은 다른 기구가 맡아야 합니다.
판정 모델을 분리하면 독립성은 오르고 비용도 같이 올랐습니다
생성 모델을 판정에 그대로 쓰면 연결이 쉽고 비용도 적습니다. 대신 자기가 선호하는 표현에 관대할 수 있고, 생성 과정에서 가진 약점을 평가에서도 공유합니다.
별도 공급자나 더 강한 모델을 붙이면 독립성은 올라가고 지연 시간과 비용이 늘어납니다.
저희는 한쪽을 정답으로 고정하지 않았습니다. 결정 가능한 항목은 규칙 기반 전략으로 검사하고, 자연어 품질은 별도 판정 공급자와 모델을 연결할 수 있게 했습니다.
다만 비용 때문에 본문 모델을 재사용하는 경우에는 그 결과가 독립 평가가 아니라는 사실을 설정과 로그에 남깁니다. 같은 점수라도 어떻게 매겨졌는지를 나중에 알 수 있어야 합니다.
판정 모델을 바꾸면 같은 0.8도 같은 척도로 볼 수 없습니다. 그래서 생성 모델을 비교할 때는 판정기를 고정하고, 판정기를 바꿀 때는 기존 기준 답안들의 순서가 유지되는지 다시 확인합니다.
점수의 절댓값보다 좋은 답이 명백히 부족한 답보다 꾸준히 높게 나오는지가 실제로 쓸모 있는 기준이었습니다.
반성문을 남기는 것과 다음 회차를 바꾸는 것은 다른 일이었습니다
판정 결과를 만들어놓고 로그에만 남기면 실행은 아무것도 달라지지 않습니다. 초기에는 그랬습니다. 판정 기록이 쌓였고 결과는 그대로였습니다.
교정 경로는 기준별 미달 사유를 짧은 지시로 바꿔 생성 단계에 돌려보냅니다. 이때 원래 사용자 요청을 대체하지 않고, 이번 후보에서 고칠 내용으로 덧붙입니다.
여기서 1편의 구분이 다시 필요했습니다. 이 재시도는 같은 요청을 다시 보내는 전송 복구가 아니라 판정 피드백을 받은 새로운 생성 회차입니다. 그래서 품질 재시도 횟수와 도구를 쓰며 진행한 회차를 따로 기록했습니다.
역할도 지켰습니다. 판정기가 답을 만들지 않고, 생성기가 스스로 통과 여부를 정하지 않습니다.
판정에 실패했을 때 조용히 통과시키지 않았습니다
판정 공급자를 호출하지 못하거나 응답을 해석할 수 없는 경우가 있습니다.
여기서 임의의 점수를 만들어 넣으면 실행은 계속되지만 그 점수는 아무 의미가 없습니다. 나중에 기록을 봐도 진짜 판정과 구분되지 않습니다.
그래서 엔진은 이때 점수를 만들지 않고 판정을 건너뛴 상태와 사유를 반환합니다. 품질 검증이 필수인 제품은 이 상태를 정책으로 차단할 수 있습니다.
다만 여기에 경계가 하나 있습니다. 엔진이 이 상태를 반환한다는 것과, 그것을 받은 제품 연결 계층이 실제로 차단한다는 것은 다른 이야기입니다. 연결 계층마다 실패 정책이 다를 수 있어서 엔진 상태만으로 전체 통합이 안전하다고 말하지 않았고, 통합별 정책을 따로 확인해야 했습니다.
실패했을 때 기본값으로 통과시키는 설계는 대체로 조용합니다. 조용한 만큼 오래 발견되지 않습니다.
검증도 점수가 아니라 순서를 봤습니다
검증에서 특정 답에 정확히 같은 소수점 점수가 나오는지는 보지 않았습니다. LLM 판정에는 변동이 있습니다.
대신 세 가지를 봤습니다. 기준을 충분히 만족한 답이 명백히 어긴 답보다 높은지, 필수 조건을 어긴 답이 통과하지 않는지, 반려 피드백을 받은 다음 후보가 지적받은 항목을 실제로 바꾸는지입니다.
같은 기준을 캔버스 노드와 독립 산출물에서 각각 실행해 결과 구조와 전이가 일치하는지도 확인했습니다. 전략을 찾지 못하거나 기준 정의가 빠졌을 때 조용히 기본 판정으로 바뀌지 않아야 했습니다.
이 검증을 거치면서 판정 기준은 화면 설정이 아니라 실행 계약의 일부가 됐습니다.
나눴다고 답이 좋아진 것은 아니었습니다
생성과 판정을 나누고 나서 답이 눈에 띄게 좋아졌느냐고 하면, 그렇지 않습니다.
달라진 것은 다른 데 있습니다. 어느 후보가 어떤 기준으로 반려됐고, 그 피드백이 다음 생성에 어떻게 들어갔는지 설명할 수 있게 됐습니다. 통과한 답에 대해서도 무엇을 봐서 통과시켰는지 말할 수 있습니다.
그러니까 저희가 얻은 것은 품질이 아니라 품질에 대해 말할 수 있는 언어였습니다. 품질을 올리는 일은 그 언어가 생긴 다음에야 시작할 수 있었습니다.
그리고 나누면서 새 문제가 생겼습니다. 점수가 정확히 어느 후보에 속하는지, 회차가 넘어갈 때 이전 회차의 판정이 남아 있지는 않은지입니다. 이 최신성 문제는 10편에서 다룹니다.
다음 편에서는 그 앞에 놓인 문제를 봅니다. 도구를 쓰며 진행하는 것과 전송 오류로 다시 보내는 것과 품질 미달로 다시 만드는 것을 구분하고, 실행을 어디에서 끝낼지 정하는 일입니다.

