제 7 장

AI 에이전트 개발

이 장의 수치는 2026-09-30 운영 데이터베이스 실측값이다. 화면(/ai-lab)이 읽는 것과 같은 API에서 가져왔고, 표본이 작은 구간은 작다고 적었다.

1) 에이전트 구성

세 축이 같은 경기를 각자 다른 근거로 본다.

에이전트 근거 LLM 의견 없음이 정상인 경우
storyline 대립 각본의 진행 방향 · 타이틀 명분 · 푸시 흐름 씀 코퍼스에 걸릴 글이 없을 때
odds 북메이커 배당의 내재 확률 안 씀 카드에 배당이 없을 때
rumor 부상 · 복귀 · 계약 만료처럼 출전을 흔드는 소식 씀 그런 소식이 없을 때 (대부분)

오즈만 LLM을 쓰지 않는다. 판단 근거가 숫자뿐이라 추론이 필요 없고, 그래서 비용도 지연도 없다. 세 축 중 유일하게 항상 돌 수 있다. 이 에이전트는 “누구를 고를지”만이 아니라 얼마나 확신하는지를 함께 내며, 그 값이 합성에서 나머지 둘과 겨루는 무게가 된다.

서사는 지식이 없으면 모델을 부르지 않는다. 카드만 보고 각본을 논하는 것은 추측이고, 추측을 근거처럼 내보내면 나머지 두 축의 판단까지 오염된다. 실제로 서사의 리포트 수는 22건이 아니라 16건인데(아래 5절), 그 차이가 “물어보지 않은 경기”다.

루머는 의견 없음이 기본값이다. 대부분의 경기에는 출전을 흔드는 소식이 없다. 없는 소식을 있는 것처럼 만들면 나머지 판단까지 끌어내리므로, 프롬프트에 “관련 소식이 없으면 null”을 명시했다.

합성

세 리포트를 하나의 승률 분포로 평균한다. 에이전트별 기본 가중치를 코드에 박지 않았다 — 서사·오즈·루머 중 무엇을 더 믿을지는 아직 정해지지 않았고, 근거 없는 숫자를 상수로 남기지 않기 위해서다. 합성 함수는 리포트가 실어 온 weight를 분포로 펴서 평균할 뿐이다.

각 에이전트의 의견은 그 자체로 분포다 — “Roman에 0.7 확신”은 곧 Roman 0.7 · 나머지 0.3이다. 득표 비중만 쓰던 이전 방식은 의견을 낸 에이전트가 전원 같은 쪽이면 무조건 100% 가 되어, 오즈가 배당에서 계산해 온 내재 확률이 정규화 과정에서 통째로 사라졌다.

확신도는 승률과 다른 축이다.

confidence = agreement × coverage
  agreement = 답한 것 중 최종 pick과 같은 쪽의 비율
  coverage  = 물어본 에이전트 중 답한 비율

coverage의 분모가 리포트 수가 아니라 물어본 수인 이유는, 실패해서 리포트를 못 낸 에이전트가 목록에서 아예 빠지기 때문이다. 그 경우까지 합의도가 만점이 되면 “셋 중 하나만 답했는데 확신 100%”가 되어 화면이 사용자를 오도한다.

산식 판본

합성 산식에는 판본이 있고, 옛 판본을 지우지 않는다.

판본 기권 처리 결과
v1 분포에서 뺀다 셋 중 하나만 답하고 확신 1.0이면 6인 경기에도 승률 100%
v2 (현행) 균등분포로 세고 물어본 수로 나눈다 답한 수가 줄수록 균등 쪽으로 끌려간다

v1의 100%는 버그가 아니라 문서화된 동작이었다 — 약함을 말하는 칸이 confidence 하나뿐이라, 승률만 보는 화면과 집계가 그것을 확정으로 읽는 것이 문제였다. v2로 옮기면서 실측한 영향(당시 20건): 값이 바뀌는 예측 16건 · 그대로 4건(셋 다 답한 것들) · pick은 한 건도 바뀌지 않아 채점과 적중률은 불변.

v1을 남긴 이유는 저장된 예측을 재현하려면 그때의 함수가 필요하기 때문이다. 판본은 예측 행에 함께 기록되고, 재현은 그 값으로 산식을 고른다. 기록이 비어 있으면 v1이다 — 그 칼럼이 v2와 함께 생겼으므로 기록 없는 행은 전부 v1이 만든 것이다.

2) RAG 코퍼스와 출처 인용

항목 값
문서 74건
청크 915건 (전부 임베딩 완료 · 검색 대상)
개정본(revision) 기록 915 / 915
발행일(published_at) 기록 0 / 915

개정본은 전부 있고 발행일은 하나도 없다. 이것은 결함이 아니라 결정이다. 위키가 발행일 메타를 내보내지 않으므로, 채우려면 “최초 개정본 시각”을 추정해 넣어야 한다. 그 값은 문서가 생긴 날이지 그 내용이 그때 있었다가 아니다. 시간 판정의 기준을 개정본으로 옮긴 것도 같은 이유였고, 그래서 이 칸은 발행일을 실제로 주는 출처가 들어올 때까지 비워 둔다. 비워 두는 편이 추정값을 넣는 것보다 정확하다.

인용은 두 층으로 남는다.

  • 질의 — 검색에 쓴 문자열을 생성 경로에서 만들어 함께 저장한다. 저장할 때 다시 만들면 그 사이 조립 규칙이 바뀌어도 아무도 모른다. 검색이 실패해도 남긴다 — 무엇으로 찾았는데 안 나왔는지가 코퍼스를 의심할 때 쓰인다.
  • 본문 스냅샷 — 해시는 대조만 되고 복원은 안 된다. 재수집이 옛 청크를 지우면 원문이 사라지므로, 그때 읽은 본문을 그대로 함께 남긴다.

코퍼스 판본은 하나다. 같은 URL을 다시 수집하면 옛 청크를 지우는 구조라, 과거 판본은 위 스냅샷으로만 남는다. 그래서 “시점 T의 코퍼스로 검색” 같은 기능은 이 저장 구조 위에서 성립하지 않는다. 하려면 저장부터 다시 설계해야 하고, 그 판단은 아직 내리지 않았다.

3) 예측 리포트

리포트 하나는 pick · weight · summary 셋을 싣는다. pick이 null이면 기권이고, 위 1절의 이유로 그것은 정상 동작이다.

셋이 모두 기권하면 예측을 만들지 못한다. 그때 배당이 있으면 북메이커 폴백으로 강등하되 그 사실을 source에 남기고 확신도는 0으로 둔다 — 승률은 배당의 내재 확률을 쓴다. 임의의 0.5보다 정직하다. 시장이 실제로 매긴 값이기 때문이다.

배당조차 없으면 예측을 만들지 않는다. “우열을 가리지 못했다”와 “물어보지 못했다”는 다른 상태이고, 후자에 임의 승률을 채워 넣지 않기 위해 예외로 구분한다. 실측 사례가 있다 — 2026-09-30 자동 생성에서 MITB 5경기 중 2경기가 이 경로로 비어 남았다. 세 에이전트가 모두 근거 부족으로 기권했고 위키에 배당이 없었다. 화면은 그 자리를 “예측 없음”으로 두고, 자동 생성은 다음 날 다시 시도한다.

4) 평가 무결성

예측이 맞았는지를 세기 전에, 그 예측을 셀 자격이 있는지를 먼저 판정한다. 규칙은 여덟이고 세 등급이다.

규칙 등급 뜻 해당
not_applicable 제외 평가 대상 아님 0
external_outcome_known 제외 만들기 전에 이미 결과가 알려져 있었음 7
withdrawn_match 제외 경기가 대진표에서 사라짐 0
pending 제외 아직 결과 없음 3
temporal_inversion 실격 결과가 기록된 뒤에 만들어짐 12
self_reference 실격 그 대회를 다룬 글을 근거로 씀 11
unverifiable_corpus 보류 읽은 글이 경기보다 먼저 쓰인 것인지 확인 불가 11
revision_after_prediction 보류 예측보다 나중에 고쳐진 글을 읽었다고 적혀 있음 0

예측 22건 중 자격을 통과한 것은 0건이다.

self_reference가 이 중 가장 무거운 규칙이다. 그 대회 자체를 다룬 문서에는 경기 결과가 적혀 있으므로, 그것을 근거로 든 예측은 예측이 아니라 정답을 읽은 것일 수 있다. 실제로 그런 사례를 하나 잡아 지웠다 — 2026-09-21에 만들어진 한 예측의 서사 리포트가 “…에서 인터콘티넨탈 챔피언십 경기를 가졌으며 방어에 성공했습니다“라고 적고 있었다. 지난 회차 결과가 실린 문서를 읽은 것이다.

보류와 실격을 가른 기준은 모름을 유죄로 치지 않는다는 것이다. unverifiable_corpus는 글이 언제 쓰였는지 모르는 경우이고, 모르는 것을 “그때는 없던 내용”으로 단정하지 않으므로 통과도 실격도 아니다.

문서를 지워도 이미 만들어진 예측의 자격은 되살아나지 않는다. 그 예측은 그 글을 실제로 읽었다. 누수 화면의 쓰임새는 과거를 고치는 것이 아니라 다음 수집을 고치는 것이다.

5) 에이전트별 정확도 분석

최종 예측이 맞았는지가 아니라 각 에이전트의 의견이 맞았는지를 잰다. 둘은 다르고, 갈리는 자리가 이 화면의 존재 이유다.

지금은 낼 수 없다

에이전트별 정확도는 의도적으로 공백이다. API가 available: false를 돌려주고 사유 셋을 함께 싣는다.

표본이 12건으로 작습니다.
예측이 대회 1개에만 걸쳐 있습니다.
11건이 그 대회를 다룬 글을 근거로 썼습니다 — 결과가 적힌 글을 읽고 낸 예측일 수 있습니다.

채점된 12건은 전부 맞았지만(적중률 100%), 그 숫자를 성능으로 읽으면 안 된다. 12건 전원이 위 4절의 실격 규칙에 걸려 있다. 결과가 이미 기록된 뒤에 만들어졌거나, 결과가 적힌 문서를 근거로 들었다. 숫자를 만들지 않고 무엇이 모자란지를 적는 것이 이 자리의 설계다.

대신 지금 말할 수 있는 것

에이전트가 얼마나 자주 의견을 내는가는 지금도 잴 수 있다.

에이전트 리포트 의견 의견율 확신도 폭
odds 22 17 77% 0.50 ~ 0.89 (12종)
rumor 22 12 55% 1.0 고정 (1종)
storyline 16 7 44% 0.85 ~ 1.00 (3종)

여기서 한 가지가 드러난다. 루머는 확신도를 변주하지 않는다 — 의견을 낼 때 항상 1.0이다. 오즈가 배당에서 12종의 값을 계산해 오는 것과 대비된다. 확신도가 상수라면 그 축은 합성에서 “의견을 냈는가”만 말하고 “얼마나”는 말하지 않는 셈이다. 이것을 성능 문제로 단정하기에는 표본이 이르지만, 관측된 사실로 기록해 둔다.

합의 층위별 채점 현황도 같은 이유로 사실까지만 적는다.

답한 수 / 같은 쪽 확신도 예측 채점 적중
1 / 1 0.33 12 3 3
2 / 1 0.33 1 0 –
2 / 2 0.67 5 5 5
3 / 2 0.67 1 1 1
3 / 3 1.00 3 3 3

확신도가 높은 층이 더 자주 맞는지는 이 표로 답할 수 없다 — 모든 층이 100%이고 각 층의 채점 수가 한 자리다. 층이 갈리려면 실격되지 않은 표본이 여러 대회에 걸쳐 쌓여야 하고, 그것이 다음 단계의 과제다(제 9 장).