AI가 실패한 다음에도 실패할까: 전이 횟수와 조건부 비율 읽기

AI Agent Fast Learning · 실행 결과 읽기

“한 번 실패한 다음 실행도 실패하는가”를 보려면 전체 실패율이 아니라 실패 뒤의 전이를 따로 세어야 한다. 분모는 모든 실패가 아니라, 바로 다음 결과까지 확인된 실패다. 이 관측 비율은 조사할 구간을 찾는 데 쓸 수 있지만 다음 요청의 실패확률을 그대로 보장하지는 않는다.

앞선 글에서 실패 묶음과 런 수를 살폈다. 이번에는 “실패가 이어졌는지, 성공으로 돌아왔는지”를 이전 결과와 다음 결과를 나눈 표로 읽는다. 필요한 지식은 분수 계산뿐이다. 아래의 배열과 수치는 모두 설명을 위해 만든 합성 자료다.

가상 8회 배열 A와 B의 전체 실패율은 50%지만 실패 뒤 실패는 각각 3/4와 0/3이다. 다음 결과가 없는 마지막 관측, 미확인 결과, 세션 경계는 연결하지 않는다.
가상 배열의 전이 집계. 분모는 다음 결과까지 확인된 실패이며, 관측 비율은 미래 확률을 보장하지 않는다.

실패 네 번을 세는 것과 실패 뒤를 세는 것은 다르다

S는 검증된 성공, F는 종료 실패다. 한 실행이 끝난 뒤 다음 실행을 시작한 여덟 번의 기록이라고 하자. A는 SSFFFFSS, B는 SFSFSFSF다. 둘 다 실패는 4/8, 즉 50%다.

A에서 F 다음 결과를 차례로 읽으면 F·F·F·S다. 따라서 실패 뒤 실패는 3/4다. B에서는 F 다음에 확인된 결과가 S·S·S뿐이다. 마지막 F 뒤의 결과는 관측하지 않았으므로 0/3이다. 마지막 실패를 분모에 넣어 0/4로 쓰면 아직 보지 않은 다음 실행까지 셈에 넣는다.

가상 기록전체 실패율실패 뒤 실패성공 뒤 실패
A: SSFFFFSS4/8 = 50%3/4 = 75%1/3 ≈ 33.3%
B: SFSFSFSF4/8 = 50%0/3 = 0%4/4 = 100%
각 비율은 해당 배열에서 관측한 비율이다. 0%·100%도 미래의 확정적 결과를 뜻하지 않는다.

전이표의 행은 이전 결과, 열은 다음 결과다

연속한 두 결과를 SS, SF, FS, FF로 센다. A의 전이 횟수는 각각 2·1·1·3이다. 8개의 결과에는 이웃한 쌍이 7개 있으므로 합계도 7이다. 전체 실패율은 결과 8개를, 전이 비율은 쌍 7개를 다른 방식으로 나눈다.

A의 이전 결과다음 S다음 F행 합계
S213
F134
행 합계로 나누면 이전 결과를 조건으로 한 관측 비율이 된다.

실패 뒤 실패 비율 = FF / (FS + FF)
성공 뒤 실패 비율 = SF / (SS + SF). 각 행의 관측이 하나도 없으면 비율은 0이 아니라 계산 불가다. 예를 들어 기록이 F 하나뿐이면 실패는 있었지만 전이는 없었다.

전이 비율을 계산했다고 마르코프 모형을 검증한 것은 아니다

확률 모형에서는 P(다음 상태 = F | 현재 상태 = F)를 한 단계 전이확률로 표현한다. Prob 140 교재의 Transitions는 전이확률을 조건부 확률로 정의하고, 시간이 바뀌어도 전이확률이 같다는 조건을 별도로 둔다. 문서 확인일은 2026-10-24다. 여기서는 그 정의를 관측 횟수 표와 구분해 사용한다.

마르코프 모형으로 미래를 계산하려면 현재 상태를 알고 나서 더 오래된 이력이 추가 정보를 주지 않는다는 가정 등 모형의 타당성을 확인해야 한다. 한 구간의 표를 만들었다는 사실은 그 확인을 대신하지 못한다. 같은 F라도 앞에 몇 번 실패했는지, 어떤 작업인지, 버전이나 부하가 무엇인지에 따라 다음 결과가 달라질 수 있다.

특히 A의 75%를 근거로 자동 재시도를 막거나 “재시도하면 75% 실패한다”고 안내하면 범위를 넘는다. 이 예제의 다음 실행은 다음 요청이며 같은 요청의 재시도 실험이 아니다. 실패 뒤에 어려운 요청이 몰렸다면 표의 차이는 작업 구성 변화에서도 생길 수 있다. 인과관계와 재시도 정책은 별도로 검증한다.

미확인 결과를 지우면 없던 전이가 생긴다

F?F에서 ?는 결과를 확인하지 못한 실행이다. ?를 삭제하면 FF가 생기지만, 원래 기록에는 바로 이웃한 FF가 없다. 두 쌍 F?와 ?F를 전이 계산에서 제외하고 제외 개수를 남긴다. 이것은 결측 문제를 해결한 것이 아니다. 실패할 때 로그가 더 자주 빠진다면 남은 쌍의 비율에도 편향이 생길 수 있다.

세션 경계도 같다. 세션 하나가 SF, 다음 세션이 FS라면 각각 SF와 FS만 센다. 두 문자열을 붙여 만든 SFFS의 가운데 FF는 세션을 가로지른 인위적인 전이다. 아래 코드는 세션을 목록으로 받아 이 연결을 만들지 않는다.

복사해서 실행하는 전이 집계 코드

코드를 transitions.py로 저장하고 python transitions.py로 실행한다. 입력은 순서가 보존된 세션 문자열의 목록 또는 튜플이다. S·F·?만 허용하며, ?를 사이에 둔 양쪽 결과를 연결하지 않는다. 빈 세션은 오류로 처리한다. 파일·네트워크·모델 API를 호출하지 않는다.

from collections import Counter
from fractions import Fraction

def summarize(sessions):
    if not isinstance(sessions, (list, tuple)) or not sessions:
        raise ValueError('Use a nonempty list or tuple of session strings')
    counts = Counter({a+b: 0 for a in 'SF' for b in 'SF'})
    known = failures = skipped = 0
    for s in sessions:
        if not isinstance(s, str) or not s or set(s) - set('SF?'):
            raise ValueError('Each session must use S, F, or ?')
        known += s.count('S') + s.count('F')
        failures += s.count('F')
        for a, b in zip(s, s[1:]):
            if '?' in (a, b):
                skipped += 1
            else:
                counts[a+b] += 1
    rates = {}
    for a in 'SF':
        denominator = counts[a+'S'] + counts[a+'F']
        rates[a] = (Fraction(counts[a+'F'], denominator)
                    if denominator else None)
    return dict(counts), rates, known, failures, skipped

if __name__ == '__main__':
    for s in ['SSFFFFSS', 'SFSFSFSF']:
        print(s, summarize([s]))
    a = summarize(['SSFFFFSS'])
    b = summarize(['SFSFSFSF'])
    assert a[0] == {'SS': 2, 'SF': 1, 'FS': 1, 'FF': 3}
    assert a[1] == {'S': Fraction(1, 3), 'F': Fraction(3, 4)}
    assert b[1] == {'S': Fraction(1), 'F': Fraction(0)}
    assert a[2:4] == b[2:4] == (8, 4)
    assert sum(a[0].values()) == sum(b[0].values()) == 7
    assert summarize(['F?F'])[0]['FF'] == 0
    assert summarize(['F?F'])[4] == 2
    assert summarize(['SF', 'FS'])[0]['FF'] == 0
    assert summarize(['F'])[1]['F'] is None
    assert summarize(['???'])[1] == {'S': None, 'F': None}
    for bad in [[], 'SF', [''], ['FX'], [None], None]:
        try:
            summarize(bad)
        except ValueError:
            pass
        else:
            raise AssertionError('Invalid input accepted')
    print('16 checks passed')

반환값은 차례로 전이 횟수, 이전 S·F별 다음 F 비율, 확인된 결과 수, 확인된 실패 수, ?가 포함되어 제외한 이웃 쌍 수다. 전체 실패율이 필요하면 확인된 실패 수를 확인된 결과 수로 나누되, 확인된 결과가 0이면 계산 불가로 남긴다. ?가 있는 자료에서는 이 비율이 전체 요청의 실패율을 대표한다고 단정하지 않는다.

2026-10-24 Python 3.12.14에서 위 코드를 실제 실행해 정상 계산·경계·미확인 처리 10개와 잘못된 입력 6개, 총 16개 검사를 통과했다. 이는 합성 자료의 집계 코드 시험이며 실제 AI 성능 시험이 아니다. Python 3.12 공식 Counter 문서에서 횟수 집계와 없는 키의 0 반환 동작을 확인했다. 문서 확인일: 2026-10-24. 외부 패키지나 에이전트 SDK는 사용하지 않는다.

실제 기록을 읽는 순서

먼저 단위를 고정한다. 사용자 요청, 요청 안의 도구 호출, 재시도 횟수는 서로 다른 단위다. 한 표에 섞지 않는다. 병렬 실행에서는 시작 순서인지 종료 순서인지 미리 정하고, 그 순서가 무엇을 설명하는지 함께 적는다. 위 순차 실행 예제의 의미를 동시 실행에 그대로 옮기지 않는다.

다음으로 쌍의 수를 확인한다. 전이 횟수와 행 합계를 비율 옆에 남긴다. FF가 0이어도 실패 뒤 다음 결과가 세 번뿐이었다면 드문 실패를 배제할 수 없다. 인접 쌍은 중간 결과를 공유하므로 서로 독립인 실험 표본처럼 간주해 일반적인 이항 오차 공식을 곧바로 붙이지 않는다.

끝으로 구간의 조건을 대조한다. 버전·업무 유형·관측 기간이 바뀌면 표를 분리해 보고, 같은 조건에서도 차이가 재현되는지 새 자료로 확인한다. 이 글은 신뢰구간·검정·장기 상태확률·재시도 최적화를 구현하지 않는다. 전이 비율이 같아 보인다는 이유로 독립성이나 안전성을 선언하지 않는다.

복사해 쓸 기록 양식

집계 단위 / 순서 기준 / 세션 경계 / 관측 기간 / S·F 판정 기준 / 확인된 S·F 수 / 미확인 결과 수 / 제외된 쌍 수 / SS·SF·FS·FF / 행별 분모 / 이전 S·F별 다음 F 비율 / 구간별 버전·작업 구성 / 미래 예측은 검증했는가. 고객 원문·인증값 대신 비식별 식별자와 필요한 집계값을 쓴다.

이번 표에서 A와 B의 차이는 “실패가 몇 번 있었나” 다음에 드러났다. 그 차이를 보존하려면 분자보다 먼저 다음 결과를 실제로 보았는지 확인해야 한다. 그다음에야 실패 뒤의 상황을 조사할 근거가 생긴다.

이어 읽기

런 수와 정확 계산: 배열을 어떤 모형과 비교하는가
연속 실패와 실행 순서: 묶음의 모양부터 읽기
미확인 결과와 성공률의 분모


다른 글 보기 · 주제 탐색 · 작성자와 편집 기준 · 문의·정정 요청

RUDA DIRECTOR에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기