긴 문서에서 마지막 값을 놓치는 이유: StateBench로 따라가는 상태 변화
긴 문서의 최신 값 찾기와 참조 추적은 왜 다른가. StateBench 공식 그림의 변수 교환을 따라가며, 같은 정답을 가진 평가 문제와 누락이 전달되는 과정을 설명한다.

긴 문서에서 이름이나 숫자를 찾는 것과, 값이 여러 차례 바뀐 뒤의 최종 상태를 구하는 것은 다른 문제다. 마지막 문장이 “b에 d의 값을 넣는다”라면 b가 언급된 문장만 찾아서는 답을 알 수 없다. 그 시점의 d가 어디서 왔는지까지 따라가야 한다. 컨텍스트에 모든 문장이 들어 있어도 이 연결을 놓치면 최종 값은 틀린다.
Ai2가 공개한 StateBench 소개 (새 창)는 상태 갱신의 종류를 나눠 이러한 차이를 평가한다. 이 글에서는 모델 순위보다 평가 문제를 만드는 방법에 집중한다. 공식 그림에서 값이 이동하는 과정을 손으로 추적하고, 정답은 같지만 더 쉬운 문제로 바꾸는 코드를 읽어 본다. 모델을 직접 실행한 성능 실험은 아니다.
b와 d를 바꾸려면 이전 값을 먼저 보관한다
논문 Figure 2 (새 창)의 왼쪽은 a·b·c·d와 임시 변수 TMP의 값을 시간 순서대로 보여 준다. 가로선은 값이 유지되는 구간이고, 다른 행으로 이어지는 곡선은 값의 복사를 뜻한다. t2에서 a에 들어온 37은 t3의 TMP를 거쳐 t5에서 c로 옮겨 간다. 마지막 숫자만 보는 대신 이 선을 따라가면 최종 결과가 c=37이 되는 근거를 찾을 수 있다.
마지막 교환 직전의 상태는 b=10, d=58이다. t6에서 TMP에 d의 값 58을 복사하고, t7에서 d에 b의 값 10을 복사한다. 이어서 t8에서 b에 TMP의 58을 넣으면 b=58, d=10으로 교환이 끝난다. TMP가 원래 d의 값을 보관하고 있으므로 중간에 d가 바뀌어도 58을 잃지 않는다.
여기서 복사는 그 순간의 값을 가져오는 연산일 뿐, TMP가 이후에도 d를 계속 따라가는 별칭은 아니다. 만약 TMP를 생략하고 d=b 다음에 b=d를 수행하면 두 변수 모두 10이 된다. 마지막 문장 하나의 참조를 해결하기 위해 앞선 갱신을 정확한 순서대로 적용해야 하는 이유가 여기에 있다. 오른쪽 정답 δ는 a=9, b=58, c=37, d=10, TMP=58이다.

정답은 그대로 두고 읽어야 할 과거를 바꾼다
공개 생성 코드의 derive_variants (새 창)는 원래 연산을 순서대로 실행하면서 더 단순한 문제를 만든다. 참조가 가리키는 현재 값과 쓰기 대상의 이전 값을 읽은 뒤, 같은 값을 쓰는 상수 대입이나 자기 값에 대한 덧셈으로 변환한다. 문제를 푸는 모델이 아니라, 정답 상태를 알고 있는 데이터 생성기가 문제를 바꾸는 방식이다.
위 교환의 마지막 연산인 b=TMP는 그 순간 b=10, TMP=58이므로 b=58로 바꿀 수 있다. 이것이 aperiodic 변형이다. 이 문제에서는 이전의 TMP를 추적할 필요가 없으며, b를 마지막으로 갱신한 상수 58만 찾으면 된다. 원래 문제와 정답은 같지만 정답에 도달하는 과정은 달라진다.
solvable 변형은 이를 자기 값에 대한 갱신으로 바꾼다. 값의 범위를 0부터 99까지로 설정한 설명용 예제라면 두 값의 차이는 (58−10) mod 100=48이다. 따라서 b=(b+48) mod 100으로 쓸 수 있다. 다른 변수인 TMP를 읽지는 않지만, 현재 b=10이라는 사실을 알아야 58을 얻을 수 있다. 여기서 100은 변환 과정을 설명하기 위해 고른 가상 범위일 뿐, 그림의 별도 측정 조건은 아니다.
코드는 참조·상수 대입·자기 갱신의 세 변형에서 각 단계의 상태와 최종 정답을 동일하게 유지한다. 반면 r-trivial 변형은 매번 해당 변수의 최초 값을 다시 쓰기 때문에 최종 상태가 달라질 수 있다. 즉, 네 가지 변형의 답이 모두 같다고 생각하면 안 된다. 이처럼 연산 위치와 변수 수를 맞추는 설계를 통해, 문제가 문서가 길어서 어려운 것인지 아니면 값의 의존 관계 때문에 어려운 것인지를 비교할 수 있다.
한 줄을 놓쳤을 때 정답이 바뀌는가
교환 예제에서 t6을 놓치면 TMP에는 이전 값인 37이 그대로 남는다. t7까지는 d=10으로 맞아 보이지만, t8에서 b는 정답 58 대신 37이 된다. 앞선 누락이 이후의 복사에 그대로 전달되는 셈이다. 반면 상수 대입으로 바꾼 마지막 연산 b=58을 정확히 읽는다면, 그 이전의 TMP에 오류가 있더라도 b의 최종 답에는 영향을 주지 않는다. 이 비교는 그림을 바탕으로 오류를 수작업으로 추적한 것이며 모델의 실제 오답 기록은 아니다.
논문 (새 창)은 중간 값을 바꾸었을 때 최종 출력이 달라질 수 있는 단계의 비중을 통해 민감도를 분석한다. 단순히 토큰 수를 늘리는 것과 정답에 영향을 주는 갱신을 늘리는 것을 구분하는 관점이다. 긴 입력에 잘 답한다는 평가 결과라도 변하지 않는 사실만 찾도록 구성했다면, 이러한 연쇄 갱신 능력을 충분히 검사한 것은 아니다.
연구진은 과제에 직접 학습한 모델과 이미 사전학습된 모델을 나누어 평가했다. 직접 학습할 때 길이에 대한 일반화가 좋았던 구조라도 사전학습 모델에서 같은 성과를 그대로 보장하지는 않았다. 따라서 특정 구조를 쓰면 긴 대화의 상태 추적 문제가 해결된다고 결론 내릴 수 없다. 이 글은 해당 실험을 재현하거나 서비스의 오류율로 환산하지 않는다.
로그 평가에서는 길이와 갱신 방식을 따로 바꾼다
긴 작업 로그를 읽는 기능을 평가한다면, 같은 사건을 두 가지 형태로 준비해 볼 것이다. 하나는 “담당자가 민수로 변경됐다”처럼 최종 값을 직접 기록하고, 다른 하나는 “현재 대기열의 담당자에게 넘겼다”처럼 다른 상태를 참조하게 만든다. 사건 수와 질문을 동일하게 맞춘 뒤 성능 차이를 살펴보면, 가장 최근 문장을 못 찾는 문제와 참조 관계를 놓치는 문제를 구별하는 데 도움이 된다. 이는 StateBench의 변형 방식을 응용한 평가 제안이며 검증된 운영 효과는 아니다.
저장소 설명 (새 창)을 보면 같은 원본에서 파생된 항목을 source_example_id와 source_instance_id로 연결한다. 서로 다른 문제를 임의로 묶기보다 이 대응 관계를 이용해 비교할 수 있다. 또한 코드 형식과 문장 형식의 차이도 함께 확인할 필요가 있다. 같은 갱신 내용이라도 표기 방식이 달라지면 모델이 받는 입력도 달라진다.
대안으로 애플리케이션이 갱신 내역을 구조화해 상태표를 유지하고, 모델에는 현재 값만 건네줄 수도 있다. 이 경우 위 교환 예제에서는 프로그램이 b=58, d=10까지 미리 계산하므로 모델이 과거의 복사 경로를 다시 풀 필요가 줄어든다. 대신 자연어로 된 사건을 올바른 연산으로 바꾸고 누락된 사건을 찾아내는 책임이 애플리케이션으로 넘어간다. 자유로운 문서를 읽는 기능과 이미 구조화된 로그를 처리하는 기능에서 설계 선택이 달라지는 이유다.
StateBench는 변수 수가 고정된 합성 과제이므로, 대화 도중 새로운 사람이나 대상이 계속 등장하는 환경 전체를 설명하지는 않는다. 이 한계를 감안하더라도 평가에 가져올 질문은 분명하다. 문서 길이가 같은 두 문제에서 한쪽은 마지막 값만 찾으면 되고 다른 쪽은 TMP에 저장한 과거까지 추적해야 한다면, 두 문제의 성공률을 하나의 긴 문맥 성능으로 합치기 전에 각각 나누어 확인할 수 있다.
