처음 보는 사람을 위한 FAQ샅샅이 · 95문항

"주제 리서치: base vs 개선" 비교 페이지를 처음 접하는 분이 궁금해할 모든 것을 한 곳에. 용어·개념부터 회의적 질문·방법론·내부구조까지 — 헷갈리는 건 여기서 풀고 가세요. · 실제 기업명은 익명 처리(A사·B사 등)
이 페이지는 무엇인가요? 옆 페이지(주제 리서치 비교)는 "같은 질문을 두 AI 에이전트 팀에 돌려 어느 쪽이, 왜 더 나은가"를 데이터로 보여줍니다. 그런데 거기엔 grounding·pass@k·토폴로지·두 개의 grounded% 같은 낯선 말이 많죠. 이 FAQ는 그 모든 용어와 숫자를 처음 듣는 사람 기준으로 아주 자세히 풀어놓은 짝꿍 문서입니다. 아래 카드에서 궁금한 주제로 바로 뛰어가거나, 난이도(기초 중급 심화)로 걸러 보세요.
🎯12문항
큰 그림 — 이게 대체 뭔가
처음 듣는 사람을 위한 한 문단 요약, base vs 개선, 왜 만들었나
🧩14문항
기초 개념 — 에이전트·도구·LLM 용어
에이전트·멀티에이전트·AutoGen·function calling·토큰·temperature
🔍12문항
근거성과 환각 — 가장 헷갈리는 핵심
grounding·hallucination·RAG·두 개의 grounded%·검색 백엔드
📐16문항
측정과 실험 방법론
벤치마크·pass@k/pass^k·paired A/B·1변수·유령 메트릭·비결정성
📊16문항
결과 해석 — 숫자가 진짜 의미하는 것
0→100%·토큰/지연·EXP 래더·일반화 경계·도구>프롬프트
🔁14문항
재현과 운영 — 직접 돌리려면
폐쇄망·config 재현·Studio 임포트·도메인 이식·비용·가드레일
🗄️11문항
시스템 내부 — DB·코드·아키텍처
trace DB·runs/events/experiments·로깅 안전·마스킹·메트릭 경로
난이도 전체 기초 중급 심화
검색 결과가 없어요. 다른 키워드로 시도해보세요.
🎯 큰 그림 — 이게 대체 뭔가 12
기초이 페이지(그리고 이 프로젝트)를 한마디로 하면 무엇인가요?
같은 주제로 작성한 두 AI 리포트를 나란히 놓고, 어느 쪽이 왜 더 믿을 만한지를 "느낌"이 아니라 "측정된 숫자"로 비교하는 페이지입니다.

두 AI 팀에게 똑같은 질문("반도체 업계 동향 2026")을 던졌습니다. 한 팀(base)은 인터넷 검색 없이 자기 기억만으로 썼고, 다른 팀(개선, improved)은 웹 검색(web_search) 도구로 실제 자료를 찾아보고 출처(URL)를 달아 썼습니다. 페이지는 그 두 리포트 본문을 보여주고(§2), 무엇이 어떻게 달라졌는지 품질 해석을 붙이고, 마지막으로 "이걸 어떻게 객관적으로 쟀는가"라는 측정 방법론까지 설명합니다.

비유하자면, 같은 시험 문제를 오픈북 없이 푼 학생자료를 찾아보고 출처를 적은 학생의 답안을 나란히 채점표와 함께 펼쳐 놓은 것입니다.

한 가지 구분이 중요합니다. 화면의 리포트(§2)는 반도체 1개 주제만 보여줍니다. 하지만 표에 나오는 메트릭(품질 숫자)은 서로 다른 3개 주제(반도체 + 전기차 배터리 + AI 가속기 GPU)의 평균입니다(N=3). 한 주제만 보면 우연일 수 있어서 세 주제를 평균 냅니다.

등장하는 모델은 z.ai의 GLM(GLM-4.7) 하나이고, 두 팀 모두 같은 모델·같은 설정으로 돌려 조건을 똑같이 맞췄습니다.

개요비교측정
기초base와 개선(improved)은 정확히 무엇이 다른가요? "1변수"라는 말은 무슨 뜻인가요?
두 팀은 거의 모든 게 똑같고, 딱 하나 "웹 근거(web grounding)"만 다릅니다. 이렇게 한 곳만 바꾸는 걸 "1변수" 원칙이라고 합니다.

공통점부터 보면, 두 팀 다 4명의 AI 일꾼(planner→analyst→synthesizer→critic)이 정해진 순서대로 돌아가며(RoundRobin이라고 부릅니다 — 회의에서 자리 순서대로 발언하는 것과 같습니다) 협업합니다. 모델도 z.ai GLM-4.7로 같고, 무작위성을 통제하는 값(seed)과 답변의 들쭉날쭉함을 조절하는 값(temperature)도 고정했습니다.

다른 점은 개선 팀의 analyst가 web_search 도구를 쓸 수 있다는 것입니다. 구체적으로 묶음 하나가 추가됐습니다.

  • ① analyst에 웹 검색 도구(실제로 인터넷에서 자료를 찾는 기능)
  • 기준 날짜 주입("지금은 2026-06-14"라고 알려줘 검색어에 올해를 넣게 함)
  • ③ synthesizer가 핵심 출처만 인용하도록 상한
왜 1변수가 중요한가: 만약 검색 도구도 추가하고 프롬프트도 바꾸고 모델도 바꿨다면, 결과가 좋아졌을 때 어느 변경이 효과였는지 영영 알 수 없습니다. 한 번에 한 곳만 바꿔야 "이 차이는 웹 근거 때문"이라고 깨끗하게 말할 수 있습니다. 마치 요리 레시피에서 소금만 바꿔야 맛 차이가 소금 때문인지 알 수 있는 것과 같습니다.
1변수grounding통제비교
기초왜 이런 걸 만들었나요? 어떤 문제를 푸는 건가요?
"AI 팀을 이렇게 바꿨더니 정말 좋아졌다"는 주장을 느낌이 아니라 데이터로 검증하기 위해서입니다. AI가 그럴듯하게 말하지만 사실은 틀린 "환각(hallucination)" 문제를 잡는 게 핵심입니다.

가장 큰 문제는 이렇습니다. AI에게 최신 사건을 물으면, 검색 없이도 아주 그럴듯한 답을 술술 내놓습니다. 문제는 그 답이 학습 시점 이후의 사실은 틀릴 수 있다는 것입니다. 그런데 글이 매끄럽다 보니 사람이 보기엔 진짜인지 가짜인지 구분이 안 됩니다.

이 프로젝트는 그 차이를 숫자로 드러냅니다. 핵심은 근거성(grounding) — 리포트의 사실들이 실제 검색 자료에 "뿌리내리고" 있느냐입니다. base 팀은 출처가 0개라 모든 문장이 기억에 의존하고, 개선 팀은 출처 URL을 달아 "이 수치는 여기서 왔다"를 검증 가능하게 만듭니다.

실제 일화(이 페이지의 정직성 사례): 초안에서 작성자(AI)가 개선 리포트의 "A사 1분기 영업이익 57조"를 자기 기억(과거 약 17조)으로 "틀렸다"고 적었습니다. 그런데 틀린 건 AI였습니다. 2026년 업황 호조로 57조가 실제로 맞았고 도메인 출처로 재확인됐습니다. 즉 출처 있는 웹 리포트가 옳았고, 기억에 기댄 "검증"이 오히려 환각이었습니다. 이것이 정확히 base 팀의 실패 방식입니다 — 그래서 이런 측정 도구가 필요합니다.
환각근거성동기
중급"에이전트 팀을 벤치마크로 평가한다"는 게 구체적으로 무슨 뜻인가요?
여러 AI에게 똑같은 과제 묶음을 풀게 하고, 정해진 채점 기준으로 점수를 매겨 팀끼리 공정하게 줄 세우는 것입니다. "벤치마크(benchmark)"는 "공통 시험문제 + 채점 기준"이라고 보면 됩니다.

여기서 "에이전트(agent)"는 스스로 도구를 쓰고 여러 단계를 거쳐 일하는 AI를 말합니다. 단순히 한 번 묻고 답하는 챗봇이 아니라, 검색하고 → 분석하고 → 글을 쓰고 → 스스로 검토하는 식의 작업자입니다. 그런 여럿이 모이면 "팀"입니다.

평가 과정은 이렇게 흐릅니다.

  1. 정해진 과제(예: 특정 주제 리포트 작성, 또는 사내 문서 기반 사실 QA)를 각 팀에 똑같이 던진다
  2. 팀이 일하는 모든 과정(누가 무슨 말을 했는지, 도구를 몇 번 불렀는지, 토큰을 얼마나 썼는지, 시간이 얼마 걸렸는지)을 한 줄도 빠짐없이 데이터베이스(PostgreSQL)에 기록한다
  3. 정답이 있는 과제는 채점기(scorer)로 맞았는지 자동 채점한다
  4. 같은 task끼리 팀을 짝지어(paired) 비교한다
"같은 task끼리 짝지어(paired)" 비교가 핵심입니다. 서로 다른 문제로 비교하면 한 팀이 쉬운 문제를 받아 이긴 것인지, 진짜 더 잘한 것인지 구분이 안 됩니다. 그래서 난이도 차이에 속지 않도록 같은 문제로 맞붙입니다.
벤치마크평가trace
중급왜 에이전트가 4개(planner/analyst/synthesizer/critic)인가요? AI 한 개로 하면 안 되나요?
한 명에게 "기획·조사·집필·검토"를 동시에 시키는 것보다, 역할을 나눠 분업시키면 각자 한 가지에 집중해 더 좋은 결과가 나오기 때문입니다.

네 일꾼의 역할은 이렇습니다.

  • planner(기획자) — 주제를 소주제로 쪼개 작업 계획을 세운다
  • analyst(분석가) — 각 소주제를 조사한다(개선 팀은 여기서 web_search로 실제 검색)
  • synthesizer(종합자) — 조사 결과를 하나의 리포트로 엮는다
  • critic(검토자) — 결과를 비판적으로 검토하고, 통과면 "APPROVE"라고 말해 작업을 끝낸다

이들은 정해진 순서대로 발언을 돌아가며(RoundRobin) 진행하고, critic이 승인하거나("APPROVE") 메시지가 너무 많아지면(10개 한도) 멈춥니다.

이 "구조(토폴로지)"는 생각보다 훨씬 중요합니다. 리서치 라더 실험에서, 일꾼을 고르는 방식 한 줄만(누가 다음에 말할지 LLM이 매번 고르는 방식 → 단순 순서 방식으로) 바꿨더니 정답률이 0.967에서 0.067로 무너졌습니다(EXP-60). 협업 구조가 결과를 떠받치는 "load-bearing(하중을 받치는 기둥)" 역할이라는 뜻입니다. 그래서 한 개 AI로 뭉뚱그리지 않고 역할을 나누되, 그 구조를 함부로 건드리지 않습니다.
멀티에이전트역할분담토폴로지
중급그냥 시중의 Deep Research(검색형 챗봇)를 쓰면 되지, 왜 직접 만드나요?
목적이 "멋진 리포트 한 장"이 아니라 "측정·재현·검증"이기 때문입니다. 우리에게 필요한 건 결과물 자체보다, 어떤 변경이 품질을 얼마나 바꾸는지 데이터로 증명하는 능력입니다.

시중의 검색형 리서치 기능은 훌륭하지만, 우리 목적에는 빠진 게 있습니다. 직접 만든 이유는 다음과 같습니다(다른 제품의 성능을 깎아내리려는 게 아니라, 용도가 다릅니다).

  • 측정/재현 — 모든 실행 과정을 DB에 기록해 "이 변경 → 이 숫자 변화"를 언제든 재현하고 비교할 수 있어야 한다. 블랙박스 제품은 내부를 못 본다.
  • 1변수 통제 — 검색 도구 하나, 프롬프트 한 줄 같은 정확히 한 곳만 바꿔 효과를 격리해야 한다. 외부 제품은 그 손잡이를 우리가 못 쥔다.
  • 폐쇄망(closed network) — 운영 환경은 외부 인터넷이 막힌 망이다. 내부 모델·내부 검색만 써야 한다.
  • 팩트성 검증 — 단순 결과물이 아니라 "이 사실이 실제 검색에 근거했나"를 events(도구 호출 기록)로 독립 검증해야 한다.
정리하면, 시중 제품은 "좋은 답을 받는" 도구이고, 이 프로젝트는 "무엇이 답을 좋게 만드는지 측정하는" 도구입니다. 둘은 경쟁이 아니라 층위가 다릅니다.
측정폐쇄망재현성
중급이건 완성된 제품인가요, 아니면 실험·측정 도구인가요?
제품이 아니라 "실험·측정 도구"입니다. 좋은 리포트를 파는 게 아니라, 팀 구성을 바꿨을 때 품질이 어떻게 변하는지를 데이터로 검증하는 실험실에 가깝습니다.

그래서 작업 방식이 과학 실험을 닮았습니다. 각 변경 시도를 실험(experiment)으로 기록하고, 그 결과로 채택(promoted) 또는 기각(rejected) 판정을 내립니다. 예를 들면,

  • 채택된 시도 — web_search 도구 추가(EXP-65), 과검색 정리(EXP-66), 중복 출처 제거 도구(EXP-68) 등
  • 기각된 시도 — "출처를 전부 나열하라"는 프롬프트(EXP-67: 평균 출처 수가 2.3→15.7개로 폭증해 중복·노이즈가 끼고, 토큰은 17.8k→28.6k(약 +61%)로 늘었으며, grounded(호출기준)도 100→67%로 오히려 떨어짐 → 순개선 아님)

각 라더(ladder)는 base에서 출발해 한 변수씩 바꿔가며 점점 나아지는 "개선 사다리"입니다. 그 과정 자체가 산출물입니다.

핵심 메타패턴: 채택된 개선은 전부 도구·입력 레벨(무엇을 할 수 있게 할까)이었고, 기각된 시도는 전부 프롬프트·구조 추가(무엇을 시킬까)였습니다. 가장 큰 단일 점프(grounded 0→100%, 호출기준)도 프롬프트가 아니라 도구(web_search) 하나가 만들었습니다. 단, 이건 "이미 베이스라인이 높고 근거성이 정답을 좌우하는" 이 환경(regime)에서의 결론입니다.
실험도구promoted/rejected메타패턴
심화이 작업에서 우리가 얻는 실질적 가치는 무엇인가요? 왜 의미가 있나요?
"AI를 어떻게 개선해야 하는가"에 대해 추측이 아닌 검증된 레버(손잡이)를 손에 쥐게 된다는 것입니다. 가장 큰 발견은 "프롬프트로 시키기"보다 "도구로 할 수 있게 하기"가 더 크고 믿을 만한 개선을 낸다는 것입니다.

이 도구가 준 구체적 가치는 다음과 같습니다.

얻은 것구체적 근거(실측)
근거성 도약출처 0→6개, web 검색 0→5회, grounded 0→100%(호출기준), 검증불가→검증가능
비용·속도까지 동시 개선토큰 29.4k→21.3k(−28%), 지연 280s→177s(−37%) — 더 좋으면서 더 쌈
나쁜 시도를 거르는 안전망tokens_per_success가 기각 케이스를 정확히 적발(EXP-60 119k, EXP-63 55k, EXP-65 97k)

또한 "웹의 가치가 만능은 아니다"라는 경계도 배웠습니다. 5개 주제 실측(§9)에서, 웹은 최신 사실이 중요한 주제(신제품 경쟁 등)에선 명확히 이겼지만, 개념 설명처럼 시점이 무관한 주제에선 무승부였습니다. 심지어 검색이 빈약하면 개선 팀은 정직하게 "데이터 부족, 작성 불가"라고 거부한 반면, base는 출처 0으로 통계를 환각했습니다 — 거부가 환각보다 낫다는 교훈입니다.

가장 값진 자산은 "가드레일 7계명"입니다. 예: ①근거성은 events(도구 호출 수)로 재라(AI 자기보고 금지) ③같은 task끼리 짝지어(paired) 비교하라 ⑤N≥3로 재라(단발 극적 결과는 노이즈) ⑦메트릭을 좇기 전 그 메트릭이 맞는지부터 검증하라(유령 메트릭 주의). 이 원칙들은 다른 AI 개선 작업에도 그대로 재사용됩니다.
가치레버가드레일
기초리서치 라더(pass@1 0.967)와 분석 라더(success 1.00, 승부처는 grounding)는 메트릭도 채점법도 다른데, 왜 한 페이지에 같이 두나요?
채점법은 다르지만 '같은 실험 규율로 같은 질문(무엇을 바꾸면 정말 좋아지나)을 푼' 두 사례라, 나란히 둘 때만 결론이 우연이 아님이 드러나기 때문입니다.

먼저 둘이 왜 다르게 생겼는지부터. 리서치 라더(research ladder)는 정답이 딱 하나로 정해진 사실 문제(멀티홉 사실 QA — 여러 자료를 거쳐야 답이 나오는 문제)를 풉니다. 사내 문서 더미(doc corpus)에서 답을 찾고, 답이 정답과 글자 그대로 맞는지(exact-match)로 채점합니다. 그래서 점수가 pass@1(여러 번 시도 중 한 번이라도 맞힌 비율)로 깔끔하게 떨어집니다. 베이스라인이 이미 0.967(거의 만점)이라, 여기서 승부는 '더 망가뜨리지 않기'입니다.

분석 라더(analysis ladder)는 정답이 없는 개방형 장문 리포트("업계 동향" 같은)를 씁니다. 형식만 갖추면 거의 항상 '완성'되므로 success=1.00은 변별력이 없습니다. 진짜 승부처는 근거성(grounding) — 리포트의 사실이 실제 자료에서 왔는가, 아니면 모델 머릿속에서 지어낸 것인가입니다. 베이스라인(웹 없음)은 grounding 0%, 개선(웹 검색 도입)은 0→100%로 점프합니다.

한 줄 비유: 리서치 라더는 '정답이 있는 시험'(틀리면 0점), 분석 라더는 '리포트 과제'(그럴듯하게 쓰는 건 쉽고, 출처를 실제로 댔는지가 관건). 시험 점수와 과제 점수는 합산하지 않지만, '벼락치기 대신 자료조사가 통한다'는 공부 습관의 교훈은 둘에서 똑같이 끌어낼 수 있습니다.

두 라더를 한 페이지에 두는 이유는 n=1 결론을 피하기 위해서입니다. 한 영역에서만 보면 "이 도메인 우연 아니냐"는 반박이 가능합니다. 채점법도 과제도 정반대인 두 곳에서 같은 신호가 나오면, 그건 도메인 특성이 아니라 더 일반적인 무언가입니다.

라더 비교채점법메타패턴
중급두 라더에서 끌어낸 '메타패턴(meta-pattern)'을 합쳐서 하나의 일반 결론으로 말해도 되나요?
네, 단 한 가지 단서(이 regime에 한정)를 붙인다면 됩니다. 두 라더의 채택/기각 명단이 정확히 같은 방향을 가리키기 때문에, 우연이 아닌 합쳐도 되는 패턴입니다.

메타패턴(meta-pattern, 여러 실험을 가로질러 반복되는 큰 규칙)을 데이터로 보면 이렇습니다. 채택(promoted)된 개입은 전부 도구·입력 레벨이고(분석: EXP-65·66·68, 리서치: EXP-64), 기각(rejected)된 개입은 전부 프롬프트·구조 추가입니다(분석: EXP-67, 리서치: EXP-57·63·60).

실험라더레버 종류판정
EXP-65/66/68분석도구(web_search)·정제promoted
EXP-64리서치도구(decoy 출처 강등)promoted
EXP-67분석프롬프트(인용 강제)rejected
EXP-57/63/60리서치프롬프트·토폴로지 교체rejected

특히 같은 목표를 두 방식으로 시도한 짝이 결정적 증거입니다. 리서치 라더에서 '엉뚱한 출처를 덜 인용하게 하라'는 목표를, EXP-63은 "더 신중히 하라"는 프롬프트 한 줄로 시도했다가 pass@1 0.80→0.27로 붕괴했고, EXP-64는 도구 단에서 가짜 후보 출처를 검색 결과에서 강등시켜 pass@1·인용 정확도·지연·토큰을 동시에 개선했습니다(유일한 dominant 케이스). 목표가 같은데 레버만 달랐고, 결과는 정반대였습니다.

그래서 합친 일반 결론: "무엇을 시킬까(프롬프트)"보다 "무엇을 할 수 있게 할까(도구·검색)"와 "어떻게 협업시킬까(토폴로지)"가 더 높고 신뢰할 레버였다. 가장 큰 단일 점프(grounding 0→100%)조차 똑똑한 프롬프트가 아니라 도구(web_search) 하나가 만들었습니다.
메타패턴도구 vs 프롬프트promoted/rejected
심화그 일반 결론('도구·토폴로지가 프롬프트보다 낫다')은 어떤 상황에서도 항상 맞나요, 아니면 한계가 있나요?
아닙니다 — 이건 '이 regime(국면)'의 결론입니다. 베이스라인이 이미 높고, 근거성(grounding)이 정답을 좌우하는 과제이기 때문에 도구가 이긴 것입니다.

이 결론이 성립한 조건을 솔직히 짚어야 합니다. 두 라더 모두 베이스라인이 이미 잘 작동합니다(리서치 pass@1 0.967, 분석은 형식상 success 1.00). 모델 자체의 능력은 충분한데, 빠진 건 '능력'이 아니라 '재료'(최신 사실, 실제 출처)였던 상황입니다. 모델은 시계가 없어서 도구가 없으면 학습 시점(컷오프) 지식을 현재처럼 출력합니다 — 이게 분석 라더 베이스라인의 grounding 0%의 정체입니다. 이런 국면에서 부족한 재료를 채우는 가장 강한 수단은 '더 잘 시키는 말'이 아니라 '재료를 가져올 도구'입니다.

그래서 진단별로 레버를 골라야 합니다.

증상맞는 레버
필요한 사실·근거가 없다도구·검색(grounding)
능력은 있는데 협업 흐름이 깨진다토폴로지(누가 언제 발화하나)
도구·사실은 충분한데 형식·톤이 문제이때는 프롬프트가 지배적일 수 있음

프롬프트가 약하다는 게 아니라 프롬프트는 비단조적(non-monotonic)·취약하다는 것입니다 — 좋은 의도의 지시 한 줄이 잘 돌던 흐름(working flow)을 깰 수 있습니다(EXP-63이 그 증거). 반면 토폴로지는 load-bearing(구조를 떠받치는 핵심)이라, 발화 순서 한 줄만 round_robin으로 바꿔도 리서치 라더가 0.967→0.067로 무너졌습니다(EXP-60). 즉 토폴로지는 강력하지만 그만큼 위험한 레버이기도 합니다.

정직하게: '항상 도구 > 프롬프트'가 아니라, '이 국면에서는 도구·토폴로지가 더 높고 신뢰할 레버였다'가 맞는 진술입니다. 베이스라인이 낮거나 모델 능력 자체가 한계라면 결론이 달라질 수 있습니다.
일반화 경계regime프롬프트 취약성
심화채점법이 다른 두 라더의 결과를 섞으면 사과와 오렌지를 비교하는 것 아닌가요? 왜 이 합침은 안전한가요?
섞는 게 아니라 '두 독립 증인의 일치'를 보는 것이라 안전합니다. 점수 숫자를 합산하지 않고, 채택/기각의 방향만 비교하기 때문입니다.

핵심 구분: 우리는 리서치의 0.967과 분석의 1.00을 더하거나 평균 내지 않습니다. 그건 정말로 사과+오렌지일 겁니다. 대신 각 라더 안에서 "어떤 종류의 개입이 채택되고 어떤 게 기각됐나"라는 방향(부호)만 뽑아, 두 방향이 같은지를 봅니다. 채점 단위가 달라도 방향은 단위가 없으므로 비교가 정당합니다.

오히려 채점법이 다른 게 장점입니다. 만약 두 라더가 채점법까지 똑같았다면, 패턴이 채점법의 부산물일 수 있다는 의심이 남습니다. exact-match로 채점하는 리서치 라더와 grounding으로 채점하는 분석 라더가 둘 다 '도구는 채택, 프롬프트 추가는 기각'을 가리킨다면, 그 패턴은 특정 채점법의 인공물(artifact)일 수 없습니다. 서로 다른 두 측정기가 같은 눈금을 가리킨 셈입니다.

이 안전장치를 더 든든하게 하는 가드레일도 있습니다.

  • paired(matched A/B)만 신뢰: 베이스와 개선을 동일 task·동일 seed·temperature 고정으로 1변수만 바꿔 비교 — 난이도 교란을 제거합니다.
  • N≥3·pass^k: 단발의 극적인 숫자는 노이즈로 보고, 여러 주제·여러 시도의 평균만 신뢰합니다.
  • 유령 메트릭(phantom metric) 검증: 메트릭을 좇기 전에 그 메트릭이 맞는지부터 확인합니다. 실제로 EXP-57은 채점기가 엉뚱한 텍스트(critic 추론 속 출처)를 인용으로 긁던 버그였고, 그걸 고치니 베이스라인이 이미 만점(F1 0.967)이었습니다.
요약: 이 페이지가 두 라더를 합치는 방식은 '점수 합산'이 아니라 '두 독립 실험에서 같은 결론이 재현됨'입니다. 재현(replication)은 단일 실험보다 강한 증거이고, 그래서 이 합침은 정당합니다.
replicationpaired 비교유령 메트릭
🧩 기초 개념 — 에이전트·도구·LLM 용어 14
기초AI 에이전트(agent)는 일반 챗봇과 무엇이 다른가요?
챗봇은 '말'만 하지만, 에이전트는 '행동'을 합니다 — 스스로 도구를 쓰고, 결과를 보고, 끝났다고 판단할 때까지 여러 단계를 돈다는 점이 핵심 차이입니다.

일반 챗봇은 질문 하나에 답 하나를 내놓고 끝납니다. 자기가 아는 것(학습한 지식)만으로 한 번에 대답하죠. 마치 '닫힌 책으로 보는 시험'과 같습니다 — 책을 다시 펴거나 검색할 수 없고, 기억에만 의존합니다.

AI 에이전트(agent)는 여기에 두 가지가 더 붙습니다.

  • 도구(tool)를 쓸 수 있다 — 웹 검색, 계산, 파일 읽기 같은 외부 능력을 직접 호출합니다. '열린 책 시험'에 가깝습니다.
  • 여러 단계를 스스로 돈다(loop) — '검색한다 → 결과를 본다 → 부족하면 또 검색한다 → 충분하면 글을 쓴다 → 스스로 점검한다' 식으로 자기 판단으로 다음 행동을 정합니다.

이 프로젝트에서 그 차이가 숫자로 드러납니다. 도구가 없는 base 팀(v2-analyst-deep)은 기억만으로 리포트를 쓰고 인용 출처가 0개입니다. 도구가 있는 개선 팀(v6-analyst-web)은 웹 검색을 5회 하고 출처 URL을 6개 붙입니다. 같은 모델·같은 4명·같은 순서인데 '검색이라는 도구 하나'를 줬느냐 아니냐만 다릅니다 — 그게 에이전트와 챗봇의 본질적 차이를 보여줍니다.

agenttool usechatbot
기초멀티에이전트 시스템(MAS)은 무엇이고, 왜 한 개가 아니라 여러 개를 쓰나요?
멀티에이전트 시스템(MAS, Multi-Agent System)은 여러 AI를 역할별로 나눠 협업시키는 구조입니다 — 한 명에게 다 시키는 대신 '분업 + 상호 검수'로 품질을 올리려는 겁니다.

한 개의 AI에게 '조사도 하고, 글도 쓰고, 동시에 틀린 데 없나 검사도 해라'라고 시키면 산만해지기 쉽습니다. 사람 회사로 비유하면, 기획·실무·정리·검수를 한 사람이 동시에 하는 셈이죠. MAS는 이걸 여러 전문가로 나눕니다.

이 프로젝트의 두 팀은 모두 4명으로 구성됩니다 — planner → analyst → synthesizer → critic (기획자 → 분석가 → 정리자 → 검수자). 각자 한 가지 일에만 집중합니다.

  • 분업의 이점 — 각 에이전트의 지시문(프롬프트)이 단순해지고, 한 역할의 실수가 다음 역할에서 걸러질 수 있습니다.
  • 상호 검수 — 마지막 critic이 '이 글이 기준을 통과했나'를 따져서 통과(APPROVE)시키거나 한 번 되돌립니다.
단, MAS가 항상 정답은 아닙니다. 이 프로젝트의 한 실험(EXP-60)에서는 협업 방식(토폴로지)을 한 줄 바꿨더니 정답률이 0.967 → 0.067로 붕괴했습니다. 여러 명을 어떻게 '엮느냐'가 그 자체로 성능을 좌우하는(load-bearing) 부품이라는 뜻입니다. 즉 MAS는 강력하지만, 구성하기에 따라 깨지기도 쉽습니다.
MASmulti-agentcollaboration
기초AutoGen은 무엇인가요?
AutoGen은 여러 AI 에이전트로 이루어진 팀을 코드로 조립하고 대화를 굴려주는 오픈소스 프레임워크입니다 — 이 프로젝트의 모든 팀은 AutoGen으로 만들어졌습니다.

여러 에이전트를 직접 손으로 엮으려면 '누가 먼저 말하나, 도구 결과를 어떻게 넘기나, 언제 멈추나' 같은 배관(plumbing) 작업이 잔뜩 필요합니다. AutoGen은 이 배관을 이미 만들어 둔 '레고 부품 상자'라고 보면 됩니다. 부품을 조립하면 팀이 됩니다.

이 프로젝트가 쓰는 주요 AutoGen 부품(1급 객체)은:

  • OpenAIChatCompletionClient — 어떤 LLM을 어떤 설정(seed·temperature)으로 부를지 정하는 모델 연결부
  • AssistantAgent — 지시문(system_message)을 가진 에이전트 한 명
  • FunctionTool — 평범한 파이썬 함수를 도구로 자동 변환(함수의 독스트링이 곧 '도구 설명'이 됨)
  • RoundRobinGroupChat / SelectorGroupChat — 여러 에이전트를 한 팀으로 묶는 두 가지 협업 방식
  • *Termination — 대화를 언제 끝낼지 정하는 종료 조건

AutoGen의 또 다른 장점은 만든 팀을 dump_component()로 '실행 가능한 JSON'으로 통째로 내보낼 수 있다는 점입니다. 이 JSON에는 모델 설정·도구 소스코드·플래그가 들어 있어 다른 환경(예: AutoGen Studio)으로 그대로 불러올(load) 수 있습니다.

AutoGenframework
기초planner·analyst·synthesizer·critic은 각각 무슨 일을 하나요?
이 4명은 '기획 → 조사·분석 → 글 정리 → 검수'라는 리포트 작성의 4단계를 한 명씩 맡은 에이전트입니다.

두 팀(base·개선) 모두 이 4명을 같은 순서로 씁니다. 차이는 오직 analyst가 검색 도구를 갖느냐뿐입니다.

역할하는 일
planner
(기획자)
큰 주제를 5~6개 소주제(시장·기술·경쟁·공급망·리스크·전망)로 쪼갭니다. 개선 팀에서는 여기에 오늘 날짜(2026-06-14)를 끼워넣어 '학습 시점이 아니라 지금 기준으로 보라'고 알려줍니다.
analyst
(분석가)
각 소주제를 구체적 사실·수치로 채웁니다. base는 기억(학습지식)만으로, 개선web_search 도구로 실제 검색해 날짜·수치를 확인하고 각 사실에 출처 URL을 답니다.
synthesizer
(정리자)
흩어진 분석을 하나의 마크다운 리포트(FINAL ANSWER)로 묶습니다. 개선 팀은 중복 출처를 정리해 핵심 5~8개만 '## 출처'로 인용하고 '기준 시점'을 명시합니다.
critic
(검수자)
완성된 글이 기준(구체성·균형·구조, 개선 팀은 '출처 URL ≥3개')을 통과하는지 점검해 통과(APPROVE)시키거나 한 번 되돌립니다.

핵심은 도구를 가진 건 analyst 한 명뿐이라는 점입니다. planner·synthesizer·critic은 두 팀 모두 도구가 없습니다. 그래서 '검색 능력이 결과를 얼마나 바꾸나'를 깨끗하게 측정할 수 있습니다.

agent rolesplannercritic
기초'도구 호출(tool use / function calling)'이 무엇이고, LLM은 어떻게 함수를 부르나요?
도구 호출(function calling)은 LLM이 직접 코드를 실행하는 게 아니라, '이 함수를 이런 값으로 불러 달라'고 구조화된 요청을 내놓으면 프레임워크가 대신 실행해 결과를 돌려주는 방식입니다.

흔한 오해부터 풀자면, LLM은 글자를 생성하는 모델이라 스스로 웹에 접속하거나 파일을 열 수 없습니다. 대신 이렇게 협력합니다 — 식당에서 손님(LLM)이 메뉴(도구 목록)를 보고 주문서(호출 요청)를 쓰면, 주방(프레임워크)이 요리(실행)해서 내오는 것과 같습니다.

  1. 도구 목록을 본다 — 모델에게 'web_search(query) 같은 함수를 쓸 수 있다'고 미리 알려줍니다. AutoGen에서는 FunctionTool이 파이썬 함수를 자동으로 감싸고, 그 함수의 독스트링이 곧 도구 설명이 됩니다.
  2. 호출을 요청한다 — 모델이 '검색이 필요하다'고 판단하면 일반 문장 대신 web_search(query="...") 같은 구조화된 호출(JSON)을 내놓습니다.
  3. 프레임워크가 실행하고 결과를 되돌린다 — 실제 파이썬 함수가 돌고, 그 결과(tool_result)가 모델에게 전달됩니다. 모델은 그 결과를 보고 다음 행동을 정합니다.

이게 가능하려면 모델 설정에 function_calling=True가 켜져 있어야 합니다 — 이 프로젝트가 도구 실험을 할 수 있는 전제 조건입니다.

중요한 함정: '검색을 호출했다'와 '근거를 실제로 얻었다'는 다릅니다. 이 프로젝트에서는 검색이 빈손으로 돌아온 경우((검색 결과 없음))가 에러가 아니라 정상 결과(tool_result) 문자열로 저장됐습니다. 그래서 '호출만 세면' 근거 없는 리포트도 100% 근거 있는 것처럼 보일 수 있습니다(자세한 건 grounding 관련 항목 참고).
function callingtool use
기초토큰(token)이 무엇인가요? 왜 토큰이 곧 비용이고 적을수록 좋은가요?
토큰(token)은 LLM이 글을 다루는 최소 조각(대략 단어/음절 단위)이며, 모델은 토큰 단위로 입력을 읽고 출력을 만들기 때문에 토큰 수가 곧 처리량·비용·시간이 됩니다.

LLM은 글자 그대로의 단어가 아니라 '토큰'이라는 잘게 쪼갠 조각으로 읽고 씁니다. 영어는 대략 단어 비슷한 단위, 한국어는 그보다 더 잘게 쪼개집니다. 모델이 처리하는 모든 입력 + 출력 글자가 토큰으로 환산됩니다.

토큰이 비용인 이유는 단순합니다 — 모델은 토큰을 하나씩 처리하므로, 많이 읽고 많이 쓸수록 계산량·시간·돈이 늘어납니다. 그래서 '같은 품질이면 토큰이 적은 쪽'이 더 좋은 팀입니다.

이 프로젝트에서 토큰은 핵심 비용 지표입니다. 흥미롭게도 개선 팀이 도구를 더 쓰는데도 토큰은 줄었습니다:

  • base v2-analyst-deep: 29.4k 토큰/run (정확히 29,409)
  • 개선 v6-analyst-web: 21.3k 토큰/run (정확히 21,285, −28%)

또 하나 중요한 파생 지표가 tokens_per_success(성공 1건당 토큰)입니다. 이건 망가진 실험을 정확히 집어냅니다 — 정답이 적어 분모가 줄고 헛도느라 토큰이 늘면 폭증하죠. 기각된 실험들이 EXP-60 119k, EXP-63 55k, EXP-65 97k로 정확히 잡혔습니다. 즉 토큰은 단순 비용을 넘어 '팀이 헛돌고 있나'를 보여주는 건강 신호이기도 합니다.

tokencost
기초temperature와 seed가 무엇인가요? 왜 낮은 고정값으로 두나요?
temperature는 모델 출력의 '무작위성(다양성) 다이얼'이고, seed는 그 무작위성을 똑같이 재현하기 위한 '난수 시드'입니다 — 둘을 고정해야 실험을 공정하게 비교할 수 있습니다.

temperature는 모델이 다음 토큰을 고를 때 얼마나 모험을 하느냐를 정합니다. 높으면 답이 더 다양·창의적이지만 들쭉날쭉하고, 낮을수록(0에 가까울수록) 매번 거의 같은 안정적 답을 냅니다. 비유하면 '주사위를 얼마나 흔드느냐'입니다.

seed는 그 주사위의 시작점을 고정하는 값입니다. 같은 seed면 같은 무작위 경로를 밟아 결과가 재현됩니다.

이 둘을 고정하는 이유는 공정한 비교 때문입니다. base와 개선 팀을 비교할 때, 만약 매번 답이 무작위로 달라진다면 '개선이 좋아진 건지, 그냥 운이 좋았던 건지' 구분할 수 없습니다. 이 프로젝트는 두 팀 모두 seed 42 · temperature 0.2로 똑같이 묶어(낮은 고정값), 바뀐 변수를 '웹 검색 하나'로만 한정합니다(이른바 1변수 통제). temperature를 정확히 0이 아니라 0.2로 둔 건, 0에 가깝게 낮춰 거의 결정적으로 만들되 모델이 자연스럽게 동작하도록 한 선택입니다.

단, 낮게 고정해도 LLM이 완벽히 결정적이진 않습니다. 그래서 이 프로젝트는 같은 작업을 여러 번 돌려보는 pass^k(k회 전부 성공)도 봅니다. 실제로 EXP-69 실험에서 pass@1은 0.889인데 pass^k는 0.667로 갈렸습니다 — temperature를 고정해도 남는 '비결정성'이 드러난 자리입니다. 그래서 이 프로젝트의 규칙 중 하나가 '단발(N=1)의 극적인 수치는 노이즈로 보고 믿지 않는다'입니다.
temperatureseedreproducibility
기초이 실험에 쓰인 z.ai GLM(GLM-4.7) 모델은 무엇인가요?
z.ai GLM(GLM-4.7)은 이 프로젝트의 모든 에이전트를 구동하는 LLM(대형 언어모델)입니다 — base 팀과 개선 팀 둘 다 같은 모델·같은 설정을 씁니다.

이 프로젝트에서 모델 자체는 실험 변수가 아닙니다. 오히려 의도적으로 고정한 '상수'입니다. 두 팀이 똑같이 GLM-4.7을 쓰고, 똑같은 seed 42 · temperature 0.2 설정을 씁니다. 그래야 '팀 구성(도구·협업 방식)의 차이'만 순수하게 측정할 수 있기 때문입니다.

GLM-4.7은 function_calling(도구 호출)을 지원합니다. 모델 설정의 model_info.function_calling=True가 바로 이것을 켜는 부분이고, 이게 켜져 있어야 analyst가 web_search 같은 도구를 부를 수 있습니다. 도구 호출을 지원하지 않는 모델이었다면 이 프로젝트의 핵심 실험(웹 검색 도구 추가) 자체가 불가능했을 겁니다.

AutoGen에서는 이 모델을 OpenAIChatCompletionClient라는 연결부로 부릅니다 — 이름에 'OpenAI'가 들어가지만 이건 'OpenAI 호환 API 규격'을 따른다는 뜻일 뿐, 실제로는 GLM 엔드포인트에 연결됩니다. 모델을 바꾸지 않고 팀만 바꿔서 측정하는 게 이 프로젝트의 설계 철학입니다.

GLMLLMz.ai
기초run·batch·turn·events·seq 같은 측정 단위 용어를 한 번에 정리해 줄 수 있나요?
한 줄로: batch(여러 실행 묶음) ⊃ run(작업 1회 실행) ⊃ turn(에이전트 발언 1번) — 그 모든 사건의 기록이 events(seq 순서대로 매겨진 사건 로그)입니다.

이 프로젝트는 모든 실행 과정을 PostgreSQL DB에 기록합니다. 그 기록을 이해하려면 다섯 단위만 알면 됩니다.

단위비유
batch여러 run을 묶은 한 번의 실험 단위(예: 3개 주제 × k회).'시험 한 회차 전체'
run작업(task) 하나를 한 번 실행한 결과. 성공 여부·토큰·지연 등 결과/조건이 여기 기록됩니다.'학생 한 명의 답안지 한 장'
turn팀 안에서 에이전트가 한 번 발언하는 것. 예: base는 약 5턴, 개선도 약 5턴.'회의에서 한 사람이 한 번 말하는 것'
events한 run 안에서 일어난 모든 사건(발언·도구 호출·도구 결과)의 로그.'회의록의 모든 줄'
seqevents에 붙는 순번. 사건의 순서를 보존합니다.'회의록 줄 번호'

DB 구조로 말하면 runs 테이블 = 실행 1회의 결과/조건, events 테이블 = 그 run 안의 모든 사건(seq 순)입니다. 이 분리 덕분에 '결과가 왜 그렇게 나왔나'를 사건 단위로 되짚을 수 있습니다. 특히 근거성(grounding)을 LLM의 자기보고가 아니라 events의 실제 도구 호출 기록으로 세는 것이 이 프로젝트의 원칙입니다.

runbatchevents
기초selector와 round_robin 토폴로지(topology)는 무엇이고, 어떻게 다른가요?
토폴로지(topology)는 '누가 다음에 말할지 정하는 규칙'입니다 — round_robin은 정해진 순서대로 돌고, selector는 매 턴 LLM이 다음 발언자를 골라 줍니다.

여러 에이전트가 한 팀이 되면 '다음 차례를 누구에게 줄까'를 정해야 합니다. 그 규칙이 토폴로지이고, AutoGen에는 두 방식이 있습니다.

RoundRobinSelector
방식정해진 순서로 차례차례(planner→analyst→synthesizer→critic→다시 처음)매 턴 LLM이 '지금은 누가 말할 차례'인지 판단해 지목
비유자리 순서대로 돌아가며 발표사회자가 그때그때 적임자를 지명
이 프로젝트분석 라더(리포트 작성)가 사용리서치 라더(멀티홉 사실 QA)가 사용

RoundRobin은 단순하고 예측 가능합니다. Selector는 더 유연해서, 단계마다 흐름이 달라지는 작업(여러 단계를 거쳐 답을 추적하는 멀티홉 QA)에 강합니다.

토폴로지는 '장식'이 아니라 핵심 부품입니다. 리서치 라더에서 Selector를 RoundRobin으로 바꾼 단 한 줄짜리 실험(EXP-60)이 정답률을 pass@1 0.967 → 0.067로 붕괴시켰습니다. 고정 순환이 멀티홉 흐름을 깨서, critic이 답이 채 나오기도 전에 조기 APPROVE하고 환각이 통과돼 버린 겁니다. 교훈: selector의 '매번 누구를 부를까' 판단은 낭비가 아니라 성능을 떠받치는(load-bearing) 부품이었습니다. 작업 성격에 맞는 토폴로지를 골라야 합니다.
topologyround_robinselector
기초종료조건(termination)과 critic 게이트는 무엇인가요?
종료조건(termination)은 '대화를 언제 멈출지' 정하는 규칙이고, critic 게이트는 그 종료를 검수자(critic)의 합격 판정(APPROVE)에 연결한 장치입니다 — 합격해야 끝나고, 무한정 돌면 강제로 끊습니다.

에이전트 팀은 스스로 여러 턴을 돕니다. 그러면 '언제 끝났다고 볼 것인가'를 정해줘야 합니다. 안 그러면 영원히 돌거나, 반대로 너무 일찍 멈출 수 있습니다. AutoGen은 두 종류의 종료조건을 함께 씁니다(둘 중 먼저 걸리는 쪽이 발동):

  • TextMentionTermination("APPROVE") — critic이 'APPROVE'라고 말하면 끝. 이게 바로 critic 게이트입니다. 검수자가 합격 도장을 찍어야 통과하는 '품질 관문(gate)'이죠. 개선 팀에서는 'FINAL ANSWER가 있고 출처 URL이 3개 이상'이면 APPROVE하도록 게이트를 완화했습니다.
  • MaxMessageTermination(10) — 메시지가 정해진 수(10개)에 도달하면 무조건 끝. critic이 끝없이 되돌리거나 팀이 헛도는 걸 막는 '안전장치(브레이크)'입니다.

critic 게이트는 양날의 칼입니다. 너무 빡빡하면 팀이 통과 못 해 헛돌고(토큰·시간 낭비), 너무 헐겁거나 흐름이 깨지면 critic이 답이 채 나오기도 전에 조기 APPROVE해 환각이 통과됩니다 — EXP-60 붕괴가 정확히 이 경우였습니다.

이와 별개로, 빈 검색 결과를 모델이 다시 곱씹게 하는 옵션(reflect_on_tool_use)을 False로 둔 것도 중요한 안정장치입니다. True면 '검색 결과 없음'에 빈 응답을 만들다 RuntimeError로 배치 전체가 죽을 수 있어서, 도구를 쓰는 개선 팀에선 이 설정이 없으면 안 되는(load-bearing) 부품입니다.
terminationcritic gate
기초왜 하필 z.ai GLM-4.7 모델과 AutoGen 프레임워크를 골랐나요?
핵심은 '무엇을 쓰느냐'가 아니라 '바꾸지 않고 고정시킬 수 있느냐'였습니다 — GLM-4.7과 AutoGen은 폐쇄망에서 돌고, 설정을 한 글자 단위로 고정·재현할 수 있어서 골랐습니다.

먼저 용어부터 쉽게 풀겠습니다. LLM(Large Language Model, 거대 언어모델)은 글을 읽고 쓰는 AI의 '두뇌'입니다. 여기서는 z.ai의 GLM-4.7을 씁니다. 프레임워크(framework)는 그 두뇌 여러 개를 한 팀으로 묶어 순서대로 일 시키는 '뼈대 도구'인데, 여기서는 AutoGen(마이크로소프트가 만든 멀티에이전트 라이브러리)을 씁니다. 비유하자면 LLM은 '직원', 프레임워크는 '회의 진행 규칙'입니다.

고른 이유는 세 가지입니다.

  • 폐쇄망(closed network)에서 돌아야 함 — 이 시스템의 운영 환경은 바깥 인터넷이 막힌 망입니다. 그래서 외부 API에 의존하지 않고 내부에서 부를 수 있는 LLM 조합이 필요했고, GLM 계열이 그 제약을 만족했습니다.
  • 도구 호출(function calling)이 됨 — 에이전트가 '웹 검색해줘' 같은 도구를 부르려면 모델이 함수 호출을 지원해야 합니다. 이 페이지의 핵심 실험(웹 검색 추가)은 모델의 function_calling=True 능력이 전제입니다.
  • 완전 재현이 가능함 — AutoGen은 팀 구성(에이전트 명단·순서·프롬프트·도구·종료조건)을 JSON으로 통째로 떠낼 수 있습니다(dump_component()). 덕분에 '프롬프트 한 글자만 바뀌어도 새 팀으로 강제'하는 재현성 규칙을 코드로 박을 수 있었습니다.
두 팀(웹 없는 base, 웹 있는 개선)은 같은 모델 GLM-4.7, 같은 seed 42, 같은 temperature 0.2로 고정했습니다. '직원'과 '온도(창의성 정도)'를 똑같이 두고 '웹 검색 도구 하나'만 바꿨기 때문에, 결과 차이를 그 하나에 정직하게 귀속시킬 수 있습니다.
GLM-4.7AutoGen폐쇄망
심화모델이나 프레임워크를 다른 것으로 바꾸면 이 페이지의 결론(도구가 프롬프트보다 강하다, 토폴로지는 취약하다)이 그대로 유지되나요?
핵심: 두 결론은 '방향'은 다른 조합에서도 재현될 가능성이 높지만, '숫자'는 이 조합(GLM-4.7+AutoGen, 이미 높은 베이스라인)에 묶인 결과라 그대로 옮길 수 없습니다 — 정직한 답은 '재측정해야 안다'입니다.

이 페이지가 발견한 두 메타패턴(meta-pattern, 여러 실험을 가로지르는 큰 규칙)을 먼저 정리하면:

  • 도구 > 프롬프트 — 채택된 개선은 전부 도구·입력 레벨(검색 도구 추가, decoy 출처 강등 등)이었고, 기각된 개선은 전부 프롬프트·구조를 '추가'한 것이었습니다. 가장 큰 단일 점프인 근거성(grounding) 0→100%도 프롬프트가 아니라 web_search 도구 하나가 만들었습니다.
  • 토폴로지(topology) 취약성 — 팀의 협업 구조를 한 줄 바꾼 것만으로 성능이 무너졌습니다. 발언자 선택 방식을 selector에서 round_robin으로 바꾼 EXP-60은 pass@1이 0.967→0.067로 붕괴했습니다. '토폴로지가 load-bearing(구조를 떠받치는 기둥)'이라는 뜻입니다.

왜 방향은 옮겨갈 가능성이 높은가: 이 패턴들은 GLM-4.7의 특정 버릇이라기보다, 'LLM에게 더 시키는 것(프롬프트)은 비단조적·취약하고, 할 수 있는 것을 늘려주는 것(도구)이 더 신뢰할 레버다'라는 일반적 성질에 가깝습니다. 다른 모델이라도 빈손으로는 최신 사실을 못 만들어내므로, '근거를 주면 환각이 준다'는 방향은 비슷하게 나올 공산이 큽니다.

왜 숫자는 못 옮기는가: 이 페이지 스스로 단서를 답니다 — "이건 이 regime(체제)의 결론"이라고요. 여기서 regime이란 이미 베이스라인이 매우 높고(예: 리서치 pass@1 0.967), 정답을 좌우하는 게 근거성인 상황을 말합니다. 베이스라인이 낮은 모델이나 다른 과제라면, 프롬프트 개입이 오히려 큰 도움이 될 수도 있습니다. 토폴로지 취약성도 마찬가지로, AutoGen의 selector(매 턴 LLM이 다음 발언자를 고름)와 round_robin(고정 순환)의 차이에서 나온 현상이라 다른 프레임워크에선 다른 형태로 나타날 수 있습니다.

그래서 운영 원칙으로 못 박은 것: 결론을 다른 조합에 '믿고 옮기지' 말고, 같은 측정 루프(N≥3, paired A/B, pass^k)로 재측정하라. 이 시스템의 가치는 특정 결론이 아니라 '조합을 바꿔도 똑같이 재현·검증할 수 있는 측정 장치' 그 자체입니다.
일반화경계메타패턴regime
중급모델을 바꾸면 측정이 통째로 흔들릴 텐데, 이 시스템은 어떻게 '한 번에 한 변수'를 지키나요?
핵심: 모델·온도·seed는 팀이 아니라 'run(실행 1회)' 단위로 따로 기록하고, 팀 변경은 프롬프트 한 글자만 바뀌어도 새 team_id를 강제합니다 — 덕분에 무엇을 바꿨는지가 항상 분리되어 남습니다.

여기서 run은 '한 번의 실행'이고, team은 '에이전트 팀의 설계도'입니다. 이 둘을 일부러 분리해 놓은 게 측정을 지키는 비결입니다.

  • LLM 설정은 run 테이블에 — 모델 이름(glm-4.7), seed 42, temperature 0.2는 팀 설계가 아니라 매 실행마다 runs에 적힙니다. 그래서 "모델을 바꿔 다시 돌렸다"는 사실이 데이터에 그대로 남고, 비교가 가능해집니다. seed·temperature가 안 적힌 실행은 아예 비교 불능으로 취급합니다.
  • 팀 변경은 해시로 감시 — 팀의 프롬프트·도구를 묶어 prompt_bundle_hash를 계산합니다. 프롬프트가 한 글자라도 바뀌면 해시가 달라지고, 같은 team_id인데 다른 해시가 들어오면 시스템이 즉시 에러로 멈춥니다(재현성 가드, AC9). '몰래 두 가지를 동시에 바꾸는' 사고를 원천 차단합니다.
  • 나머지는 import로 고정 — 개선은 teams/*.py 한 파일에만 격리되고, 바꾸지 않는 부분(러너·로거·채점기·DB)은 베이스라인에서 그대로 가져다 씁니다. 그래서 '팀만 바꿔 측정'이 성립합니다.
정리하면, 모델·프레임워크를 새 것으로 바꾸고 싶으면 그 변경을 명시적으로 한 변수로 두고 재측정하면 됩니다. 시스템이 '두 변수가 섞이는' 상황을 데이터 구조 차원에서 막아주기 때문에, 조합을 바꿔도 측정의 정직성은 유지됩니다.
1변수재현성team_id
🔍 근거성과 환각 — 가장 헷갈리는 핵심 12
기초근거성(grounding)이 무엇이고 왜 중요한가요?
근거성(grounding)이란 리포트의 주장이 '모델의 기억'이 아니라 '실제로 확인한 자료'에서 나왔는지를 뜻합니다.

비유하자면, 어떤 친구가 "올해 그 산업이 슈퍼사이클이래"라고 말할 때 두 종류가 있습니다. (1) 예전에 어디서 들은 기억으로 말하는 친구, (2) 지금 막 자료를 찾아보고 출처까지 짚어주는 친구. 근거성은 두 번째 친구처럼 작동하는 정도를 말합니다.

이게 중요한 이유는 언어모델(LLM)의 구조 때문입니다. 모델은 학습이 끝난 시점(컷오프) 이후의 일을 모릅니다. 그런데도 마치 현재를 아는 것처럼 술술 대답합니다 — 시계가 없는 사람이 자신 있게 시간을 말하는 것과 같습니다. 그래서 "최신 사실"을 묻는 질문일수록, 모델이 정말 자료를 찾아봤는지(=근거성)가 답의 신뢰도를 좌우합니다.

이 프로젝트에서는 근거성을 grounded%(근거율)라는 숫자로 잽니다. 핵심은 모델이 "검색했어요"라고 말한 걸 믿지 않는다는 것입니다. 대신 실행 기록(events)에 검색 도구를 진짜 호출한 흔적이 남았는지로 독립 계산합니다.

한 줄 요약: 0% = 기억으로만 답함, 100% = 실제 검색에 근거함. 같은 4-에이전트 팀에서 검색 도구 하나(1변수)를 넣었더니 근거율(호출 기준) 0%→100%, 인용 출처 0개→6개로 바뀌었습니다.
grounding근거성
기초환각(hallucination)이 무엇이고, 이 프로젝트에서 실제로 겪은 사례가 있나요?
환각(hallucination)은 모델이 사실이 아닌 내용을 '사실처럼' 자신 있게 지어내는 현상입니다. 이 프로젝트에서는 사람(AI 작성자)이 환각을 일으킨 생생한 사례까지 있습니다.

환각이 위험한 이유는 틀린 답이 맞는 답과 똑같이 매끄럽고 자신 있게 나온다는 점입니다. 모델은 "모르겠다"보다 "그럴듯한 무언가"를 만들도록 학습되어 있어서, 근거가 없으면 빈자리를 추측으로 메웁니다.

이 페이지에는 특히 교훈적인 일화가 있습니다(흔히 '57조 환각'이라 부릅니다). 페이지 초안을 쓰던 AI가, 검색 기반 개선 리포트에 적힌 "A사 1분기 영업이익 57조"라는 수치를 보고 자기 학습지식("예전엔 약 17조였다")에 비춰 "이건 틀렸다"고 적었습니다.

그런데 틀린 건 AI였습니다. 2026년 슈퍼사이클로 57조가 실제로 맞았고, 도메인 출처로 재확인되었습니다. 즉 출처 있는 웹 리포트가 옳았고, AI의 '기억에 기반한 검증'이 오히려 환각이었던 것입니다.

이 일화가 핵심인 이유: 이것이 바로 검색 없는 base 팀의 실패 모드 그 자체입니다. "모델의 기억"은 그럴듯하지만 컷오프 이후엔 신뢰할 수 없고, 실제 출처로 검증해야만 환각을 잡을 수 있다는 것을 사람조차 당한 사례로 보여줍니다.
환각hallucination
중급이건 RAG인가요? RAG와 무엇이 같고 다른가요?
넓게 보면 RAG의 한 형태(웹 검색을 근거로 끌어오는 방식)이지만, 정확히는 '에이전트가 도구로 검색을 호출하는' 구조라 전형적 RAG 파이프라인과는 다릅니다.

RAG(Retrieval-Augmented Generation, 검색증강생성)란, 모델이 답하기 전에 외부 자료를 먼저 '검색해서 가져온 뒤' 그 자료를 근거로 답을 만드는 기법입니다. "기억으로만 답하지 말고, 자료를 찾아 보고 답해라"는 발상이 핵심으로, 이 프로젝트의 근거성 개선과 같은 정신입니다.

  • 같은 점: 둘 다 "모델 내부 지식만으로 답하지 않고, 외부에서 근거를 가져온다"는 목표가 같습니다. 가져온 자료의 URL을 출처로 인용하는 것도 같습니다.
  • 다른 점 ①: 전형적 RAG는 보통 고정된 문서 보관소(벡터DB 등)에서 미리 색인된 자료를 꺼냅니다. 여기서는 그게 아니라 라이브 웹 검색 도구를 에이전트가 필요할 때 직접 호출합니다(같은 질문이라도 다른 날 = 다른 기사 = 다른 수치가 나올 수 있음).
  • 다른 점 ②: 검색을 한 번 끼워 넣는 게 아니라, 4명의 에이전트(planner→analyst→synthesizer→critic) 중 analyst만 검색 도구를 갖고, critic이 "출처가 부족하면 재검색하라"고 반려하는 에이전트 루프 안에서 동작합니다.
폐쇄망(외부 인터넷이 막힌 환경)에서는 라이브 웹 대신 사내 검색이나 사내 RAG로 검색 도구의 본문만 교체하면 같은 측정 구조를 그대로 씁니다. 즉 '근거를 어디서 가져오느냐'는 바꿔 끼울 수 있는 부품입니다.
RAG검색증강생성
심화이 페이지의 핵심이라는 '두 개의 grounded%'는 정확히 무슨 차이인가요?
같은 'grounded%'라는 이름이지만 ① 검색을 '호출했나'(호출기준)와 ② 검색이 '결과를 돌려줬나'(성공기준)는 전혀 다른 숫자입니다. 이 둘을 혼동하면 환각 리포트가 100%로 둔갑합니다.

이게 이 페이지가 가장 강조하는 함정입니다. 두 정의를 표로 비교하면:

구분① 호출 기준② 성공 기준
무엇을 보나검색 도구를 1번이라도 불렀나검색이 실제 결과를 돌려줬나
출처원장(EXP 기록)v_analysis_grounded 뷰 (마이그레이션 010)
v3·v4·v5 결과100% (v5는 67%)0%
v6-dedup 결과100%100%

비유하자면 전화를 걸었느냐(①)와 상대가 받아서 정보를 줬느냐(②)의 차이입니다. v3·v4는 검색을 분명히 호출했지만(전화는 걸었지만), 결과가 빈손이라 근거를 한 톨도 못 얻었습니다(상대가 안 받음). 호출만 세면 100%, 실제 결과를 세면 0%인 것입니다.

왜 010 뷰가 따로 생겼나: ① 호출 기준만 보면 "검색 100% 했으니 근거 충분"이라 착각하게 됩니다. 그래서 010 마이그레이션이 ② 성공(실결과) 기준을 새로 정의해, 빈손 검색을 근거에서 걸러냅니다. 이 페이지 §1·§5의 헤드라인(grounded 0→100%)은 ① 호출 기준임을 명시해 둡니다.
grounded%호출기준성공기준
중급그럼 헤드라인 'grounded 0→100%'는 정직한 숫자인가요, 부풀린 건가요?
부풀린 게 아니라 '호출 기준'으로는 정확한 숫자이고, 페이지가 그 한계를 스스로 명시한다는 점에서 정직합니다 — 다만 '성공 기준'으로 보면 같은 100%가 아니라는 단서를 반드시 함께 읽어야 합니다.

핵심은 두 가지를 한꺼번에 봐야 한다는 것입니다.

  • 정직한 면: base 팀(v2-analyst-deep)은 검색 도구가 아예 없어서 호출이 0건 → 0%가 사실입니다. 개선 팀은 검색 도구가 생겨 모든 run에서 검색을 호출 → 호출 기준 100%가 사실입니다. 능력 자체(검색을 할 수 있느냐)의 차이는 진짜로 0→100입니다.
  • 조심할 면: 헤드라인 100%는 ①호출 기준입니다. 같은 100%가 ②성공 기준(실제 결과를 받은 검색)에서는 팀마다 다릅니다 — v3·v4는 성공 기준 0%, v6-dedup만 성공 기준 100%입니다.

그래서 페이지는 부풀리는 대신 그 함정을 본문(§11 caveat)과 010 뷰의 존재 이유로 드러냅니다. "검색을 했다 ≠ 근거를 얻었다"는 문장을 헤드라인 옆에 붙여 둔 것이 그 증거입니다.

정직성의 척도: 좋은 평가는 자기에게 유리한 숫자(0→100%)를 외치면서, 동시에 그 숫자를 무너뜨릴 수 있는 단서(성공 기준에선 0%인 팀이 있다)까지 같은 페이지에 적어 두는 것입니다. 그래서 운영 결론을 검색 백엔드 교체(Firecrawl)로 이어 갑니다.
grounded%정직성
심화DuckDuckGo가 '빈손'이었다는데, 그러면 'grounding 개선'은 사실 가짜였던 건가요?
전부 가짜는 아닙니다 — 검색 '능력'은 진짜로 생겼지만, 검색 백엔드가 빈손이라 그 능력이 '실제 근거'로 이어지지 못한 run이 많았습니다. 그래서 페이지는 한 번 더 측정(성공 기준)해 진짜와 가짜를 갈라냅니다.

무슨 일이 있었는지 단계로 풀면:

  1. 개선 팀의 analyst에 웹 검색 도구를 달았습니다 → 검색을 실제로 호출합니다(호출 기준 100%, v3는 run당 17.3회까지).
  2. 그런데 키 없는 DuckDuckGo 폴백이 rate-limit(요청 과다 차단)·timed out으로 빈손('(검색 결과 없음)')을 자주 돌려줬습니다.
  3. 그 결과 v3·v4는 검색은 했지만 받은 근거가 0 → 성공 기준 grounded 0%가 되었습니다.
  4. 중복 제거를 추가한 v6-dedup만 빈손을 줄여 성공 기준 100%를 달성했습니다.

즉 '가짜'였던 부분은 호출만 세는 측정이 만든 착시이지, 개선 시도 자체가 사기였던 건 아닙니다. 오히려 이 프로젝트의 가치는 그 착시를 스스로 적발해, 측정 정의를 ②성공 기준으로 한 단계 올리고(010 뷰), 백엔드를 키 기반 실검색(Firecrawl)으로 교체하는 운영 결론까지 낸 점입니다.

참고: 메모리에도 "analyst-web의 '최신 grounding 개선'은 환각(web_search 100% 실패)"이라는 경고가 남아 있습니다. 즉 호출 100%만 믿었다면 가짜 개선을 진짜로 채택할 뻔했고, 성공 기준 측정이 그걸 막은 것입니다.
DuckDuckGogrounding빈손
중급Firecrawl과 DuckDuckGo — 왜 검색 백엔드가 결과를 좌우하나요?
검색 도구가 '근거를 가져오는 수도꼭지'인데, DuckDuckGo 폴백은 자주 막혀 물(근거)이 안 나오고, Firecrawl은 키 기반 정식 검색이라 안정적으로 물이 나오기 때문입니다.

이 프로젝트의 검색 도구(web_tool.py)는 런타임에 두 갈래로 갈립니다:

  • 키가 있으면 Firecrawl: EVAL_FIRECRAWL_API_KEY로 정식 검색 API(/v1/search)를 호출합니다. 안정적으로 실제 결과를 돌려줍니다.
  • 키가 없으면 DuckDuckGo 폴백: 키 없이 HTML 페이지를 긁는 방식이라, 요청이 몰리면 rate-limit(요청 과다 차단)이나 timed out으로 빈손('(검색 결과 없음)')을 돌려주기 쉽습니다.

왜 이게 결과를 좌우하느냐 — 근거성의 성공 여부는 결국 검색이 실제 텍스트를 돌려줬는가에 달려 있기 때문입니다. 백엔드가 빈손이면 모델은 받은 근거가 없어, 다시 기억으로 답하거나(환각 위험), critic이 "출처 부족"으로 반려해 재검색 루프가 돌면서 토큰·지연만 폭증합니다(v3는 토큰 3.3배).

그래서 운영 결론은 백엔드를 Firecrawl로 교체입니다. 중요한 점: 팀의 다른 부분(프롬프트·토폴로지)은 그대로 두고 '수도꼭지'만 바꿔도 근거 성공률이 달라집니다. 폐쇄망에서는 같은 자리에 사내 검색/RAG를 끼우면 됩니다. 도구는 stdlib(urllib)만 써서 직렬화가 안전하게 설계돼 있어, 본문 교체가 쉽습니다.
Firecrawl검색백엔드
심화왜 grounding을 리포트 텍스트(출처 줄 수)가 아니라 events(도구 호출 기록)로 측정하나요?
리포트 텍스트는 모델이 '그럴듯하게 꾸며낼 수 있는' 자기보고라 신뢰할 수 없고, events(실행 기록)는 도구를 진짜 때렸는지를 객관적으로 남기는 영수증이기 때문입니다.

핵심 위험은 모델의 자기보고입니다. 모델은 시계가 없어, 도구가 없어도 학습 컷오프(2024) 지식을 현재처럼 출력하고, 심지어 그럴듯한 URL을 텍스트에 박아 넣을 수도 있습니다. 그래서 "리포트에 출처 줄이 몇 개냐"로 세면 — 검색 안 하고 지어낸 출처도 근거로 잡힐 수 있습니다.

반면 events는 시스템이 자동으로 남기는 실행 로그입니다. analyst가 web_search를 실제로 호출하면 그 호출이 seq 순서대로 events에 기록됩니다. 모델이 거짓말을 해도 events는 바뀌지 않습니다 — 이게 견고한 신호(robust signal)입니다.

  • crosscheck에서 stale(검색 없는) 팀은 0/3 run이 검색 사용 → events에 호출 0건.
  • grounded 팀은 3/3 run, 총 104 calls가 events에 찍힘 → 독립 검증.
한 줄 원칙(가드레일 1계명): "grounding은 events(도구 호출 수)로 재라 — LLM 자기보고 금지." 비유하면 "공부했다"는 말 대신 실제 도서관 출입 기록으로 확인하는 셈입니다. 텍스트에 URL이 박혔는지가 아니라, 도구를 정말 때렸는지가 거짓말에 강합니다.
events측정방법
중급'검색을 했다 ≠ 근거를 얻었다'가 무슨 뜻인가요?
검색 도구를 호출하는 것(행위)과, 그 검색이 실제로 쓸 만한 결과를 돌려주는 것(성과)은 별개라는 뜻입니다. 둘을 한 단어로 묶으면 평가가 망가집니다.

비유가 가장 빠릅니다. 도서관에 갔다(검색을 했다)고 해서 필요한 책을 찾았다(근거를 얻었다)는 보장은 없습니다. 문이 잠겨 있었거나(차단), 책이 대출 중이었거나(빈손) 하면, 출입 기록은 남지만 손에 든 자료는 없습니다.

이 프로젝트에서 정확히 그 일이 있었습니다:

  • v3·v4는 검색을 호출했습니다 → '검색을 했다'(호출 기준 grounded 100%).
  • 그러나 DuckDuckGo 빈손으로 결과가 0 → '근거를 얻지 못했다'(성공 기준 grounded 0%).

그래서 평가 정의를 둘로 나눴습니다(앞의 '두 개의 grounded%'). 만약 이 둘을 구분하지 않고 "검색했으니 근거 있다"고 처리하면, 실제로는 기억으로 채운 환각 리포트가 grounded=100%로 둔갑합니다. 가장 위험한 종류의 측정 오류입니다.

실전 함의: 개선을 채택(promote)할지 결정할 때 '성과(성공 기준)'를 봐야지 '행위(호출 기준)'만 보면 안 됩니다. 그래서 v3·v4는 호출 100%였어도, 성공 기준까지 만족시킨 v6 계열이 실제 가치를 입증한 단계로 취급됩니다.
근거성측정함정
심화검색이 빈손이면 왜 에러(tool_error)가 아니라 정상결과(tool_result)로 저장되나요? 이게 왜 함정인가요?
검색이 '결과 0건'을 돌려준 건 도구가 고장 난 게 아니라 '정상적으로 빈손을 반환'한 것이라, 시스템은 이를 tool_error가 아니라 tool_result에 '(검색 결과 없음)' 문자열로 저장합니다. 그래서 에러율은 0%인데 근거 성공률은 0%일 수 있는 함정이 생깁니다.

구분이 핵심입니다:

  • tool_error = 도구가 예외로 죽었다(예: 네트워크 timed out → '(검색 실패: ...)'). 명백한 고장 신호.
  • tool_result = 도구가 정상적으로 끝나고 무언가를 반환했다. 그 '무언가'가 결과 목록일 수도, '(검색 결과 없음)'이라는 빈손 문자열일 수도 있습니다.

검색 도구 입장에서 "검색했는데 일치 항목이 없음"은 비정상이 아니라 정상적인 빈 결과입니다(그래서 예외를 던지지 않고 문자열을 리턴). 결과적으로 통계에서 도구 에러율(err_rate)은 0%로 깨끗해 보입니다.

왜 함정인가: 에러율 0%만 보면 "검색이 잘 됐다"고 오해합니다. 그러나 그 0% 뒤에 빈손 run이 잔뜩 숨어 있을 수 있습니다. 빈손은 에러가 아니라 정상결과로 분류되니까요. 그래서 err_rate 0%인데 grounding 성공 기준은 0%인 상황이 멀쩡히 성립합니다.

이 함정 때문에 010 마이그레이션의 v_analysis_grounded 뷰가 존재합니다. 이 뷰는 단순히 "에러 없었나"가 아니라, events를 실시간으로 뒤져 실제 결과를 반환한 검색이 1건이라도 있었나(n_search_ok ≥ 1)를 따로 셉니다. 즉 '에러 없음'에 속지 않으려고 성공 기준을 별도 축으로 만든 것입니다.
tool_resulttool_error함정
심화57조 일화에서 '출처 있는 웹 리포트가 맞고 AI 기억이 틀렸다'고 결론냈는데, 그 웹 출처 자체가 틀렸을 가능성은 어떻게 배제하나요?
핵심 직답: 우리는 '출처가 무조건 옳다'고 결론낸 게 아니라, '확인 가능한 출처'와 '확인 불가능한 기억'이 충돌했을 때 전자를 의심의 출발점으로 삼았을 뿐입니다. 출처도 틀릴 수 있다는 게 정확히 다음 단계의 과제(클레임 단위 교차검증)입니다.

먼저 그 일화를 정확히 짚겠습니다. 이 페이지 초안에서 작성자(AI)가 개선 리포트의 "A사 1분기 영업이익 57조"라는 수치를, 자기 학습지식(2025년 기준 약 17조)에 비춰 "틀렸다"고 적었습니다. 그런데 2026년 업황 호조(슈퍼사이클)로 57조가 실제로 맞았고, 도메인 출처로 재확인됐습니다. 즉 틀린 건 출처가 아니라 AI의 기억이었습니다.

여기서 핵심은 우리가 도출한 결론의 정확한 범위입니다. 우리는 "출처가 붙었으니 무조건 참"이라고 결론낸 게 아닙니다. 두 종류의 주장이 충돌했을 때 어느 쪽을 더 신뢰할지를 정한 것입니다.

주장의 종류검증 가능성이 사건에서
AI 기억(파라미터)불가능 — 출처도, 시점도 없음. "왜 그렇게 아는가"를 물을 수 없다학습 컷오프 시점의 옛 일반론(17조)
웹 출처(URL)가능 — 클릭해서 원문·날짜를 사람이 직접 대조최신 실측(57조), 재확인됨

둘 다 틀릴 수 있다는 점에선 같습니다. 하지만 결정적 차이는 틀렸는지 확인할 방법이 있느냐입니다. 기억 기반 주장은 "내가 그렇게 기억한다"가 끝이라 반박도 검증도 봉쇄됩니다. 출처 기반 주장은 적어도 원문을 열어 날짜와 숫자를 사람이 대조할 수 있습니다. 그래서 우리는 출처를 "정답"으로 모신 게 아니라, 검증을 시작할 수 있는 자리로 삼은 것입니다. 일상 비유로, 누가 "내 기억엔 그래"라고만 하는 것과 "여기 영수증 있어"라고 하는 것은 둘 다 위조 가능하지만, 후자만 대조라는 절차를 허용합니다.

그래서 이 사건의 진짜 교훈은 "출처를 믿어라"가 아니라 "확인 가능한 것확인 불가능한 것이 부딪칠 때, 확인 불가능한 기억으로 확인 가능한 출처를 '틀렸다'고 단정하지 마라"입니다. 이것이 정확히 base 팀(v2-analyst-deep)의 실패 모드입니다 — 출처 0개로 한국 통계 같은 수치를 기억만으로 만들어내(환각), 아무도 대조할 수 없습니다.
grounding환각출처신뢰성
심화출처만 붙으면 무조건 믿는 거라면, 잘못된 출처를 권위로 착각하는 또 다른 환각을 부르지 않나요?
핵심 직답: 정확한 지적이고, 그래서 우리는 '출처를 붙였다'를 끝점이 아니라 중간점으로 봅니다. 이 페이지가 다루는 건 ①조사(출처 부착)까지이고, ②각 주장을 그 출처에 실제로 대조하는 팩트체크는 별도 단계로 명시돼 있습니다. '출처가 있다'와 '출처가 그 문장을 실제로 뒷받침한다'는 다른 일입니다.

맞습니다. 출처를 권위로 착각하는 건 실재하는 위험이며, 우리는 이걸 두 가지 서로 다른 환각으로 구분합니다.

  • 1세대 환각: 근거 없이 사실을 지어냄(base의 모드). 출처 0개, 기억으로만.
  • 2세대 환각: 출처는 붙였는데 그 출처가 해당 문장을 실제로는 뒷받침하지 않음(엉뚱한 URL을 권위처럼 인용). 웹 검색을 켜면 1세대는 줄지만 2세대가 새로 생깁니다.

이 페이지는 정직하게 아직 2세대를 다 풀지 못했다고 적고 있습니다. 실제로 §6의 EXP-69(inconclusive) 측정에서, 9개 run 중 8/9가 검색을 실제로 했지만 작성자(synthesizer)가 그 URL을 최종 리포트 '## 출처'로 옮긴 건 3/9뿐이었습니다. 즉 "검색했다", "출처를 달았다", "그 출처가 문장을 뒷받침한다"는 세 개의 다른 단계이고, 현재 챔피언(v6)도 뒤쪽 두 단계가 불안정합니다.

그래서 우리 설계에서 '출처 부착'은 신뢰의 종점이 아니라 다음 검증을 가능케 하는 입구입니다. §4에 명시된 파이프라인이 이 점을 분명히 합니다:

  1. 조사 — analyst가 검색해 각 사실에 출처 URL을 붙임(이 페이지가 보여주는 단계).
  2. 팩트체크(교차검증) — 각 주장을 그 인용 출처에 실제로 대조해, 출처가 정말 뒷받침하는지 별도로 채점(별도 구축됨).
  3. 반복 검색 — 빠진 부분을 추적(기획 단계).
핵심 구분: grounded%를 우리는 모델의 자기보고("검색했어요")가 아니라 events의 실제 검색 도구 호출수로 잽니다. 이건 "검색이라는 행위가 일어났는가"를 견고하게 확인해 줄 뿐, "인용된 출처가 그 문장을 옳게 뒷받침하는가"까지 보증하진 않습니다. 후자는 클레임(claim) 단위로 출처와 대조하는 별도 채점 축이며, 페이지에 다음 개선 과제 = 클레임 단위 교차검증 + 인용 강제로 명시돼 있습니다. 정리하면: 출처를 붙이는 건 권위를 부여하는 게 아니라 의심할 권리를 부여하는 것입니다. 권위로 착각하지 않는 유일한 방법은, 붙은 출처를 한 건씩 다시 열어 대조하는 절차를 계속 돌리는 것뿐입니다.
출처신뢰성교차검증환각
📐 측정과 실험 방법론 16
기초벤치마크(benchmark)가 무엇이고, GAIA는 무엇인가요?
벤치마크(benchmark)는 '똑같은 시험지'다 — 여러 에이전트를 같은 문제로 풀게 해 누가 더 잘하는지 공정하게 재는 기준이다.

비유하면 학원에서 학생들 실력을 비교하려고 같은 모의고사를 보게 하는 것과 같다. 문제가 제각각이면 누가 더 똑똑한지 알 수 없으니, '동일한 시험지'가 공정 비교의 전제가 된다. 우리 시스템은 에이전트 팀(여러 AI가 협업하는 한 묶음)에게 같은 과제를 던지고, 답이 맞았는지(success)와 토큰·지연 같은 비용까지 모두 기록한다.

GAIA는 이런 시험지 중 하나로, '에이전트가 여러 단계를 거쳐 추론하고 도구를 써야만 풀 수 있는 실전형 문제 모음'이다. 단순 상식 퀴즈가 아니라, 자료를 찾고 계산하고 종합해야 답이 나오는 문제들이다. 정답이 사람 손으로 미리 정해져 있어 exact-match(정답 문자열과 정확히 일치하는지)로 자동 채점할 수 있다.

이 비교 페이지의 분석/리서치 라더는 GAIA 외에도 자체적으로 만든 과제(예: 사내 문서 코퍼스 기반 멀티홉 사실 QA 30문항, exact-match 채점)를 함께 쓴다. 핵심은 '시험지가 고정되어 있어야 팀을 바꿔가며 그 차이만 측정할 수 있다'는 점이다.
benchmarkGAIA
중급pass@1, pass@k, pass^k 는 각각 어떻게 다른가요?
pass@1=한 번 시도해 맞힐 확률, pass@k=k번 중 한 번이라도 맞힐 확률, pass^k=k번 모두 맞힐 확률이다. 셋의 간격이 곧 '운빨' 즉 비결정성의 크기다.

AI는 같은 문제를 같은 조건으로 줘도 매번 답이 조금씩 다를 수 있다. 그래서 한 번만 풀려보고 판단하면 운에 속는다. 그래서 한 과제를 여러 번(k번) 반복시킨 뒤 세 가지로 본다.

  • pass@1 — 과제당 시도 한 건당 성공 비율. '평균적으로 한 번 풀면 맞히나'.
  • pass@k — k번 중 한 번이라도 성공한 비율. '여러 번 주면 결국 맞히나'(관대한 기준).
  • pass^k — k번 전부 성공한 비율. '항상 안정적으로 맞히나'(엄격한 기준).

k=1(한 번만 돌린 배치)이면 셋이 모두 같은 값이다. 차이는 여러 번 돌렸을 때만 드러난다.

실측 예(EXP-69, analysis_v6_k3, k=3, n=9): pass@1 0.889인데 pass^k는 0.667로 뚝 떨어진다. 같은 팀·같은 과제인데도 '평균은 잘하지만 항상 일관되진 않다'는 뜻 — 이 0.889와 0.667의 간격이 바로 비결정성(non-determinism)이 모습을 드러낸 자리다. 그래서 단발 성공에 들뜨면 안 되고, pass^k까지 봐야 진짜 신뢰성을 안다.
pass@1pass^k신뢰성
기초N=3 이 무슨 뜻이고, 왜 한 번이 아니라 여러 번 평균을 내나요?
N=3은 '서로 다른 과제 3개로 재서 평균 냈다'는 뜻이다. 단 한 번/한 과제의 결과는 우연일 수 있어서, 우연을 평균으로 씻어내려는 것이다.

이 페이지의 헤드라인 숫자(토큰/run 29.4k→21.3k, 지연/run 280s→177s 등)는 반도체·전기차배터리·AI가속기GPU 세 가지 서로 다른 주제로 각각 측정한 값을 평균한 것이다(N=3). 본문 리포트(§2)는 그중 반도체 하나만 보여주지만, 메트릭은 세 주제의 평균이다.

왜 한 번이 아닌가? AI 결과는 출렁인다. 한 주제만 보면 그 주제가 마침 쉬웠거나 운이 좋았을 수 있다. 동전을 한 번 던져 앞면이 나왔다고 '이 동전은 항상 앞면'이라 할 수 없는 것과 같다. 여러 번·여러 과제로 평균을 내면 이런 우연이 상쇄돼 '진짜 실력'에 가까워진다.

우리 가드레일 5번이 바로 이것이다 — N≥3 + pass^k로 봐라. 단발(N=1)의 극적인 수치는 노이즈로 취급한다. 실제로 EXP-69에서 pass@1 0.889와 pass^k 0.667의 간극이 '한 번만 봤으면 속았을' 출렁임을 증명했다. 분석 라더의 각 단계가 모두 n=3인 이유다(EXP-69만 k=3이라 n=9).
N=3반복노이즈
중급paired(짝지은) A/B 비교가 무엇이고, 왜 그냥 두 팀의 평균을 비교하면 안 되나요?
paired 비교는 '같은 과제'를 두 팀에 똑같이 풀려서 짝(쌍)으로 맞대보는 것이다. 서로 다른 과제로 잰 평균끼리 비교하면 팀 실력이 아니라 과제 난이도 차이에 속는다.

비유하자면, 두 학생의 실력을 비교할 때 같은 시험지를 둘 다 풀게 해야 공정하다. A는 쉬운 시험, B는 어려운 시험을 봤다면 점수 차이가 실력 때문인지 시험 난이도 때문인지 구분이 안 된다. paired는 '같은 문제를 둘 다 풀게 한 뒤, 문제별로 A와 B를 1:1로 비교'하는 방식이다.

그냥 두 팀의 raw 팀 평균(각자 아무 과제나 풀어 낸 평균)을 비교하면, 한 팀이 우연히 더 쉬운 과제를 더 많이 받았을 때 그게 실력 우위로 둔갑한다. 이걸 난이도 교란(confounding)이라 한다 — 진짜 원인(팀 차이)이 가짜 원인(과제 난이도)에 섞여 흐려지는 것이다.

우리 가드레일 3번: paired(matched A/B)만 신뢰한다. raw 팀 평균은 난이도 교란 때문에 믿지 않는다. 이 페이지의 base→개선 비교가 모두 '같은 3개 주제'를 양쪽 팀에 똑같이 돌려 짝으로 맞댄 것인 이유다. 통계적으로 더 엄밀히 보려면 McNemar 검정이나 Wilcoxon 같은 paired 검정을 쓴다.
pairedA/B교란
기초"한 번에 1변수만 바꾼다"는 원칙은 왜 그렇게 중요한가요?
한 번에 딱 한 곳만 바꿔야, 결과 변화가 '바로 그 한 곳' 때문이라고 단정할 수 있다. 두 곳을 동시에 바꾸면 어느 게 효과였는지 영원히 못 가린다.

요리에 비유하면, 소금과 설탕을 동시에 늘리고 맛이 좋아졌다면 둘 중 뭐가 효과였는지 알 수 없다. 한 번에 소금만 바꿔봐야 '소금이 답이었다'고 말할 수 있다. 실험에서 이걸 변수 격리(isolation)라 부른다.

이 프로젝트에서 base(v2-analyst-deep, 웹 없음)와 개선(v6-analyst-web) 팀의 유일한 차이는 '웹 근거 하나'(web_search 도구 + 기준날짜 주입 + 출처 인용)다. 4-에이전트 구성(planner→analyst→synthesizer→critic)도, 토폴로지(고정순환 RoundRobin)도, 모델(z.ai GLM-4.7)도, seed·temperature도 전부 똑같다. 그래서 grounded 0→100% 같은 차이를 '웹 근거 효과'로 깨끗이 귀속할 수 있다.

중요한 건 이 원칙이 의지가 아니라 코드 구조로 강제된다는 점이다 — 개입 1건 = 새 팀 모듈 1개를 만들되 바꿀 곳만 손대고 나머지(executor·selector·다른 프롬프트)는 베이스라인에서 그대로 import한다. 프롬프트가 한 글자라도 바뀌면 prompt_bundle_hash가 달라져 새 team_id가 강제된다. 가드레일 2번: 한 번에 1변수·1파일(나머지는 import).
1변수격리team_id
심화"유령 메트릭(ghost metric)"이 무엇이고, EXP-57에서 무슨 일이 있었나요?
유령 메트릭은 '실제로는 엉뚱한 걸 세고 있는데 진짜인 척하는 숫자'다. EXP-57에서는 채점기 버그가 만든 가짜 낮은 점수를 보고 멀쩡한 베이스라인을 '고치려다' 오히려 정답 1건을 망가뜨렸다.

배경: 대시보드가 인용 정확도(cite F1)를 ~0.58(58%)로, 출처 누락을 13건으로 가리켰다. 그래서 '근거가 부실하다'는 가설을 세우고, planner에 'SOURCES 줄 항상 출력', critic에 'SOURCES 없으면 반려' 게이트를 추가했다(EXP-57, v2-research-grounded).

그런데 메트릭 자체가 버그였다. 채점기가 모든 에이전트 이벤트에서 SOURCES를 긁었고, 특히 critic의 추론 텍스트 속에 등장한 'SOURCES' 단어까지 인용으로 오인해 planner가 낸 진짜 인용을 가려버렸다. 채점 범위를 planner의 FINAL ANSWER로 한정하자 베이스라인이 이미 F1 0.97(97%), 누락 1건이었다. 즉 부실하지 않았다. 개입 효과는 0이었고, 덤으로 정답 1건이 회귀했다 → 기각.

교훈: 낮아 보이는 점수가 '진짜 약점'인지 '채점기 착시'인지 먼저 가려야 한다. 가드레일 4번(측정 전 하니스 버그부터)과 7번(메트릭 좇기 전 그 메트릭이 맞는지 검증)이 여기서 나왔다. 이 일을 다루는 법: ① 메트릭 정의를 코드로 다시 읽어 '무엇을 세고 있나' 확인 ② 의심 run 몇 건을 손으로 라벨링해 채점값과 대조 ③ 채점기 자체에 known 정답을 박은 단위 테스트를 둔다.
유령 메트릭EXP-57채점기
심화왜 "메트릭을 좇기 전에 그 메트릭이 맞는지부터" 검증하나요?
틀린 자(尺)로는 아무리 열심히 재도 헛수고이기 때문이다. 잘못된 메트릭을 좇으면, 멀쩡한 시스템을 망가뜨리거나 진짜 문제를 놓친다.

메트릭은 '눈금이 잘못된 체중계'일 수 있다. 체중계가 늘 5kg 더 나온다면, 그걸 믿고 굶으면 건강만 해친다. 먼저 '이 저울이 맞나'부터 확인해야 한다. AI 평가에서도 마찬가지로, 숫자가 가리키는 방향으로 달려가기 전에 '이 숫자가 정말 내가 재려던 걸 재고 있나'를 점검해야 한다.

EXP-57이 정확히 이 함정이었다 — 채점기 버그가 만든 가짜 0.58(58%) 점수를 진짜로 믿고 베이스라인을 '고치려다' 효과 0 + 정답 1건 회귀를 얻었다. 진짜 점수는 0.97(97%)이었다. 메트릭을 의심하지 않았다면, 멀쩡한 팀에 불필요한 제약을 영구히 붙일 뻔했다.

이 프로젝트가 grounded%를 모델의 자기보고가 아니라 events(실제 도구 호출 기록)로 독립 재계산하는 것도 같은 정신이다. '검색했다'는 모델 말을 믿지 않고, 시스템 로그가 '진짜 web_search를 때렸나'로 확인한다. 가드레일 1번(grounding은 events로 재라)과 7번(메트릭 검증 먼저)이 한 쌍이다. 단 '호출했다'와 '근거를 얻었다'도 다르다 — 호출은 됐지만 빈 결과를 받은 run이 많아, 호출기준 grounded%와 성공기준 grounded%(뷰 v_analysis_grounded)를 따로 본다.
메트릭 검증가드레일events
심화temperature=0으로 고정했는데도 결과가 흔들립니다 — 이 비결정성(non-determinism)은 왜 생기나요?
temperature=0은 '같은 입력이면 가장 확률 높은 답을 고르라'는 설정일 뿐, 완전한 재현을 보장하지 않는다. 입력 자체가 매번 달라지거나, 시스템 내부의 미세한 비결정성이 남기 때문이다.

temperature(온도)는 모델이 답을 고를 때의 '무작위성 다이얼'이다. 0이면 매번 가장 그럴듯한 토큰을 고르므로 '결정적'일 것 같지만, 현실에서는 다음 이유로 흔들린다.

  • 입력이 라이브로 바뀐다 — 개선 팀은 실제 웹을 검색한다. 다른 시각에 돌리면 다른 기사, 다른 수치가 들어오니 같은 질문도 다른 입력이 된다(웹은 외부·라이브).
  • 다단계 협업의 분기 — 4개 에이전트가 주고받는 과정에서 한 곳의 미세한 차이가 뒤로 갈수록 증폭된다. 특히 grounding(검색 행위)은 events로 견고히 재지만, 그 결과가 리포트 인용으로 옮겨지는지는 비결정적이다(synthesizer의 변덕).
  • 인프라 레벨 비결정성 — 부동소수점 연산 순서·배치 처리·동시성 등에서 미세한 차이가 남을 수 있다.
실측 증거(EXP-69): seed·temperature를 고정했는데도 pass@1 0.889 vs pass^k 0.667로 갈렸다. 같은 팀·같은 과제인데 '3번 중 일부만 성공'한 자리가 있었다는 뜻 — 이게 비결정성이다. 그래서 우리는 단발 결과를 믿지 않고 N≥3 반복 + pass^k로, 평균만이 아니라 run별 분포까지 본다. 정확한 숫자(예: 특정 기사의 매출 수치)는 '같은 시점·같은 검색 백엔드·N≥3 평균'에서만 재현 가능하다.
비결정성temperature재현성
중급실험 원장(experiments ledger)이 무엇이고, 왜 DB에 일일이 기록하나요?
실험 원장은 '모든 개선 시도의 가설→개입→측정→판정을 영구히 적어두는 장부'다. 기억은 휘발되고 왜곡되지만, 장부는 나중에 누구든 같은 결론을 재현·반박할 수 있게 한다.

회계 장부가 '돈이 언제 왜 들고 났나'를 빠짐없이 적듯, 실험 원장(experiments 테이블)은 각 실험(EXP-xx)마다 ① 가설(무엇이 좋아질 거라 봤나) ② 개입(정확히 어디를 한 곳 바꿨나) ③ 측정 변화(DB로 재계산한 숫자) ④ 판정(promoted/rejected/inconclusive)을 한 행으로 남긴다.

왜 DB에 적나? 기억이나 채팅 로그로는 '예전에 그거 효과 있었던 것 같은데'로 흐려진다. 장부에 박아두면 같은 실수를 반복하지 않고, 'EXP-63은 프롬프트로 시도했다 붕괴, EXP-64는 도구로 같은 목표를 달성' 같은 대조 학습이 가능해진다. 이게 채택은 전부 도구레벨·기각은 전부 프롬프트레벨이라는 메타 패턴을 드러내준 토대다.

설계상 스키마가 SSOT(단일 진실 공급원)다 — runs(실행 1회의 조건·결과), events(run 안 모든 이벤트, seq 순), experiments(개선 원장)가 분리돼 있고 불변식은 CHECK 제약으로 강제된다. 개선 루프는 '특이점 발견 → 가설 1개 → 딱 한 곳 개입 → paired 측정 → 판정 → 원장 기록 → promoted면 새 베이스라인'을 반복한다. 핵심은 도구가 아니라 이 규율이다.
experiments원장재현성
심화prompt_bundle_hash / team_id 재현성 가드는 무엇을 막아주나요?
'프롬프트가 바뀌었는데 같은 팀인 척하는 사고'를 막는다. 프롬프트가 한 글자라도 바뀌면 자동으로 다른 team_id가 강제돼, 시스템이 '바뀐 팀'과 '안 바뀐 팀'을 절대 혼동하지 않는다.

prompt_bundle_hash는 팀을 이루는 모든 프롬프트를 한 줄 지문(fingerprint)으로 압축한 값이다. 글자 하나만 달라져도 지문이 완전히 달라진다. team_id는 그 팀의 고유 이름표인데, 지문이 바뀌면 새 이름표가 강제로 발급된다.

이게 왜 중요한가? 비교 실험의 생명은 '내가 비교한 두 팀이 정말 내가 의도한 그 두 팀이 맞나'다. 만약 프롬프트를 슬쩍 고치고 같은 team_id로 측정하면, 두 실험 결과를 같은 팀의 것으로 착각해 잘못된 결론을 낸다. 가드는 이 실수를 코드 레벨에서 차단한다 — '같은 team_id + 다른 해시' 조합이 발견되면 즉시 에러로 중단한다(AC9).

덕분에 '변경 없는 단순 재측정'과 '변경 있는 개입'이 DB 레벨에서 명확히 구분된다. 즉 '1변수 원칙'이 사람의 의지가 아니라 시스템의 강제로 지켜진다. 단 예외가 하나 있다 — 오늘 날짜 주입은 prompt_bundle_hash에 들어가지 않는다. 날짜는 빌드 시점 프롬프트가 아니라 도구가 런타임에 주입하는 실행 맥락(runtime context)일 뿐, 팀의 정체성을 바꾸지 않기 때문이다(모델엔 시계가 없어 주입할 뿐).
prompt_bundle_hashteam_idAC9
중급성격이 다른 3개 주제(반도체+배터리+GPU)를 섞어 평균을 냈다는데, 난이도가 제각각인 주제를 한 숫자로 평균 내는 게 정당한가요?
핵심: 우리가 보는 건 '난이도가 다른 주제들의 절대 점수'가 아니라, 같은 주제 안에서 base와 개선의 차이(paired diff)다. 그래서 주제마다 난이도가 달라도 평균이 정당하다.

먼저 비유부터. 키가 제각각인 세 사람에게 같은 신발을 신겨보고 '평균 키'를 내면 의미가 없다. 하지만 각자에게 깔창을 넣기 전과 후를 재서 '늘어난 키'를 평균 내면, 키가 달라도 '깔창 효과'는 깨끗하게 보인다. 우리가 하는 게 바로 후자다.

이걸 matched A/B(짝지은 비교)라고 부른다. 반도체 주제는 base도 반도체로, 개선도 반도체로 돌린다. 배터리도, GPU도 마찬가지. 그래서 한 주제 안에서 base와 개선은 난이도가 같다(같은 주제니까). 둘의 차이만 빼면 '주제 난이도'라는 방해 요인(교란변수, confounder)이 상쇄된다. 평균 내는 대상은 '각 주제의 점수'가 아니라 '각 주제에서의 개선폭'이다.

왜 일부러 성격이 다른 3개를 골랐나: 한 주제만 보면 그게 우연일 수 있다. 반도체에서만 웹이 도움됐을 수도 있으니, 일부러 최신성에 민감한 주제(반도체·배터리·GPU)를 섞어, 효과가 한 주제에 우연히 묶이지 않았는지 본다. 만약 효과가 주제마다 들쭉날쭉하면 그 자체가 신호다 — 실제로 §9에서 '한국 반도체업황' 주제는 검색이 빈약해 개선이 정직하게 '데이터 부족'으로 거부했다. 평균 하나에 숨지 않고 주제별로도 뜯어봤다는 뜻이다.
N=3paired교란변수
심화표본 3개로 '토큰 -28%', 'grounding +100%' 같은 결론을 내리는 건 통계적으로 너무 약하지 않나요?
핵심: 맞다, N=3은 작다. 그래서 우리는 'p값으로 유의하다'고 주장하지 않는다. 대신 두 가지로 버틴다 — ① 효과가 노이즈보다 압도적으로 클 때만, ② 짝지은 비교(paired)일 때만 결론을 낸다.

표본이 작을 때의 함정은 '작은 차이를 큰 발견으로 착각'하는 것이다. 그래서 이 페이지의 가드레일 7계명 중 ⑤가 'N≥3, 그리고 단발 dramatic은 노이즈로 의심하라'이다. 작은 N에서는 효과 크기(effect size)가 '눈에 띄게 크냐'가 핵심이다.

  • grounded 0→100%: 이건 미세한 차이가 아니라 능력의 유무다. base는 도구가 없어 구조적으로 0%일 수밖에 없고, 개선은 검색 도구가 있다. 통계 검정이 필요한 종류의 차이가 아니다(없던 기능이 생긴 것).
  • 토큰 -28%, 지연 -37%: 이건 정도(degree)의 차이라 더 조심해야 한다. 그래서 단정적으로 '항상 28% 절감'이라 말하지 않고, '이 3개 배치에서의 평균 절감폭'으로 한정한다.
비결정성을 정면으로 드러낸 자리: 우리는 작은 N의 약점을 숨기지 않았다. v6를 k=3(같은 문제를 3번씩, n=9)으로 돌린 EXP-69에서 pass@1 0.889인데 pass^k 0.667이 나왔다. pass@1은 '여러 번 중 한 번이라도 성공', pass^k는 'k번 전부 성공'이다. 둘이 벌어졌다는 건 같은 입력에도 결과가 흔들린다는 증거다. 그래서 EXP-69의 판정은 'promoted'가 아니라 inconclusive(결론 보류)였다. 좋은 평가 시스템은 자기가 'promoted'라 적은 숫자조차 다시 의심한다 — N=3은 출발선이지 결승선이 아니다.
표본크기효과크기pass^k
심화EXP-60은 selector를 round_robin으로 '한 줄' 바꿔 pass@1이 0.967→0.067로 붕괴했는데, 분석 라더는 처음부터 RoundRobin인데도 멀쩡합니다. 같은 round_robin인데 왜 한쪽만 무너지나요?
핵심: 토폴로지(협업 방식)는 그 팀이 '그 토폴로지를 전제로 설계됐을 때'만 작동한다. 분석 라더는 처음부터 RoundRobin용으로 만들어졌고, 리서치 라더는 Selector용으로 만들어졌다. EXP-60은 Selector용 팀에 round_robin을 강제로 끼워 넣어 깨진 것이다.

용어 먼저. RoundRobin(라운드로빈)은 '정해진 순서로 돌아가며 발언'(planner→analyst→synthesizer→critic→다시 planner...)이다. Selector(셀렉터)는 '매 턴 LLM이 다음 발언자를 직접 고르는' 방식이다. 비유하면, RoundRobin은 고정 좌석 회의(시계방향으로 발언권), Selector는 사회자가 그때그때 발언자를 지명하는 회의다.

  • 분석 라더는 정해진 4단계(계획→분석→종합→비평)를 한 바퀴 돌면 끝나는 일이다. 고정 순서가 곧 정답 흐름이라 RoundRobin이 자연스러운 설계다. 그래서 멀쩡하다.
  • 리서치 라더는 멀티홉 QA다 — '먼저 A를 찾고, 그 결과를 봐야 B를 누구에게 시킬지 정해지는' 일이다. 다음 발언자가 이전 결과에 따라 달라져야 하므로 Selector(매 턴 지명)가 load-bearing(없으면 무너지는 핵심)이다.
그래서 '같은 round_robin'이 아니다. 분석 라더의 RoundRobin은 그 팀이 처음부터 그걸 기준으로 프롬프트·역할을 맞춰 설계된 것이고, EXP-60은 Selector를 전제로 짜인 리서치 팀에서 그 Selector만 뽑아내고 round_robin을 끼웠다. 사회자가 상황을 보고 지명해야 굴러가던 회의에서 사회자를 빼고 '무조건 시계방향'으로 바꾼 셈이라 흐름이 끊겼다. 교훈은 7계명 ②와 맞닿는다 — 한 번에 한 변수만, 나머지는 그대로 import. EXP-60의 진짜 메시지는 'round_robin이 나쁘다'가 아니라 '토폴로지는 팀과 한 몸이라 함부로 바꾸면 load-bearing 구조가 무너진다'이다.
토폴로지Selectorround_robin
심화메타패턴 결론을 스스로 '이 regime(이미 높은 베이스라인 + grounding이 정답을 좌우)에 한정'이라고 적었는데, 그럼 베이스라인이 낮거나 추론이 승부처인 일에서는 '프롬프트보다 도구·토폴로지가 낫다'가 뒤집힐 수도 있다는 건가요?
핵심: 그렇다. '도구·토폴로지가 프롬프트보다 낫다'는 보편 법칙이 아니라 이 병목(bottleneck)에서의 결론이다. 병목이 다르면 최고 레버도 달라진다. 그대로 복붙하지 말고, 먼저 '우리 일의 병목이 무엇인지'부터 진단하라.

왜 한정인가. 이 프로젝트의 과제는 ① 베이스라인이 이미 꽤 잘하고(research pass@1 0.967) ② 승부가 '최신 사실을 가졌나(grounding)'에서 갈렸다. 이런 곳에서는 '없던 정보를 가져다주는 도구'가 가장 큰 레버다. 정보가 없어서 못 푸는 문제에 말투를 다듬는 프롬프트는 효과가 작다.

그래서 결론은 '레버 = 병목'이라는 형태로 일반화해야 안전하다:

병목(무엇이 문제인가)가장 높은 레버
필요한 정보·능력이 없다도구·검색·grounding
정보는 있는데 잘못 쓴다(형식·톤·추론)프롬프트·역할 설계
협업이 깨진다토폴로지·종료조건·라우팅
비용이 문제다제약 제거·dedup·상한
당신 일에 가져다 쓰는 법: 결론을 옮기지 말고 방법(measurement loop)을 옮겨라. (1) base를 먼저 측정해 어디서 실패하는지 본다 — 정보 부족인가, 추론 실패인가, 협업 붕괴인가. (2) 그 병목에 맞는 레버 하나만 바꿔 paired로 비교한다. 만약 당신 과제가 베이스라인이 낮고 추론이 승부처라면, 여기선 대개 기각됐던 '프롬프트·역할 설계'가 오히려 지배적 레버일 수 있다(위 표 2행). 실제로 이 페이지도 §6에서 '모델이 도구는 충분한데 형식·톤이 문제면 프롬프트가 지배적일 수 있다'고 명시한다. 보편 진리로 일반화하지 말 것 — 이건 우리 regime의 측정 결과다.
메타패턴일반화경계병목
심화tokens_per_success가 rejected를 '정확히' 때린다고 했는데, 결과를 보고 사후에 맞춘 지표 아닌가요? rejected를 미리 거르는 사전 게이트로 쓸 수 있나요?
핵심: 사후 설명이자 동시에 사전 게이트로 쓸 수 있다 — 단, 단독 판정자가 아니라 '의심 신호(red flag)'로서다. 절대 임계값을 미리 박지 말고, base 대비 배수로 본다.

먼저 왜 사후 끼워맞춤이 아닌지. tokens_per_success는 '성공 1건당 몇 토큰 썼나' = 총 토큰 ÷ 성공 건수다. 정의상 두 가지에 동시에 반응한다 — 토큰을 많이 쓰면(분자↑) 또는 성공이 적으면(분모↓) 폭증한다. 그래서 '낭비가 큰데 결과는 시원찮은' 개입을 한 숫자로 잡는다. 이건 결과를 보고 만든 게 아니라 비용효율의 자연스러운 정의다.

실험tokens_per_success판정
EXP-60(토폴로지 교체)119,429rejected
EXP-65(과검색 v3)97,089*비용 폭증
EXP-63(disambig 프롬프트)54,628rejected
v1-research(base)6,528기준선

*EXP-65는 tokens/run 97k(성공은 유지). 기준선 base가 6.5k인 점이 비교 닻이다.

사전 게이트로 쓰는 법: 베이스라인이 6.5k인데 어떤 개입이 50k~120k로 한 자릿수 배수가 튀면, 채택 결정을 자동으로 통과시키지 말고 보류(review)하라. 이건 'pass@1이 좋아 보여도 비용으로 망했을 수 있다'는 경보다. 주의 — 단독 게이트로 쓰면 안 된다: ① 절대 임계값은 과제마다 다르다(반드시 base 대비 배수로). ② 성공률이 같은데 토큰만 줄이는 정당한 개선을 막으면 안 된다. ③ 진짜 판정은 paired·N≥3·pass^k가 함께 내린다. 즉 tokens_per_success는 '여길 다시 봐라'를 외치는 경보등이지, 합격/불합격을 혼자 찍는 도장이 아니다.
tokens_per_success사전게이트비용효율
📊 결과 해석 — 숫자가 진짜 의미하는 것 16
기초"웹검색 0→5회, 출처 0→6개"가 왜 그렇게 큰 의미인가요?
핵심: 이 숫자는 리포트가 '기억으로 지어낸 글'에서 '실제 자료를 보고 쓴 글'로 바뀌었다는 증거입니다.

먼저 비유로 풀어보겠습니다. AI 모델은 시계도 인터넷도 없는 방에 갇힌 사람과 같습니다. 아무리 똑똑해도, 그 방에 갇힌 시점(학습 컷오프, 대략 2024년)까지 외운 지식만으로 말합니다. "오늘 무슨 일이 있었냐"고 물으면, 모르면서도 아는 것처럼 그럴듯하게 지어냅니다. 이것을 환각(hallucination)이라고 합니다.

base 팀(웹 없음)은 검색 도구가 없어서 web_search 호출이 0회, 따라서 리포트에 출처(인용 URL)도 0개입니다. 즉 모든 문장이 "기억"에서 나왔고, 그게 맞는지 틀린지 아무도 확인할 수 없습니다.

개선 팀(v6)은 검색 도구를 손에 쥐어줬더니 검색 5회, 출처 6개를 실제로 가져옵니다(헤드라인 KPI 기준). 그래서 헤드라인이 검증불가 → 검증가능으로 바뀝니다.

왜 이게 가장 중요한 단일 지표인가: 다른 모든 품질(정확성, 최신성)은 "실제 자료를 봤느냐"에 좌우됩니다. 자료를 안 본 글은 아무리 매끄러워도 신뢰할 근거가 없습니다. 그래서 이 0→5(메트릭은 서로 다른 3개 주제의 N=3 평균이라, 출처는 v6 기준 약 4.7개, 실검색 백엔드 적용 시 최대 8개까지 관측)는 능력 자체가 생긴 자리입니다.
grounding출처환각
중급도구를 추가했는데 토큰 -28%, 지연 -37%로 오히려 더 싸고 빨라졌다 — 어떻게 가능한가요?
핵심: 모델이 '혼자 끙끙 추론하던 시간'을 '검색 한 번으로 사실 확인'이 대체했고, 동시에 과검색·재검색 낭비를 덜어냈기 때문입니다.

먼저 흔한 오해부터 짚겠습니다. "도구를 붙이면 단계가 늘어나니 당연히 느리고 비쌀 것"이라 생각하기 쉽습니다. 실제로 처음(EXP-65)엔 그랬습니다 — 토큰이 29.4k에서 97k(3.3배)로 폭증했죠. 검색을 너무 많이 시키고(run당 평균 17.3회), critic이 계속 "다시 찾아봐"라며 재검색 루프를 돌았기 때문입니다.

그런데 최종 챔피언 v6은 base보다도 쌉니다(토큰 29.4k→21.3k, -28% / 지연 280s→177s, -37%). 두 가지 힘이 겹친 결과입니다:

  • 추론을 사실 조회로 대체: 모델이 "이 수치가 맞나" 머릿속으로 길게 저울질하는 대신, 검색 한 번으로 사실을 확정합니다. 머뭇거리는 긴 추론 텍스트가 줄어 토큰이 절약됩니다.
  • 과검색·재검색 제거(EXP-66, EXP-68): 검색을 1라운드로 제한하고, 도메인 중복 검색을 도구 단에서 걸러냅니다(web_search_deduped). 쓸데없는 반복이 사라져 비용이 base 밑으로 떨어집니다.
교훈: grounding(근거 확보)은 "검색을 몇 번 했냐"가 아니라 "검색 도구를 쓰느냐"에서 나옵니다. 그래서 검색 횟수를 17.3→4.0(v6 기준)으로 줄여도 grounded 100%는 그대로 유지됐고, 비용만 빠졌습니다. "많이 찾기"가 아니라 "제대로 한 번 찾기"가 핵심입니다.
토큰지연과검색
중급EXP-65 → 66 → 67 → 68 개선 래더에서 순서대로 실제로 무슨 일이 일어났나요?
핵심: ①능력 획득(검색 도구) → ②비용 회복(과검색 제거) → ③인용 과욕으로 역행 → ④질로 회복, 이렇게 한 번에 한 변수씩 밟아 올라갔습니다.

래더(ladder, 사다리)란 한 번에 한 칸씩 변수 하나만 바꿔 "무엇이 효과를 냈는지"를 깨끗이 분리하는 방식입니다. 순서대로 보면(메트릭은 서로 다른 3개 주제의 N=3 평균):

단계무엇을 바꿨나결과판정
EXP-65analyst에 web_search 도구 + 오늘 날짜 주입grounded 0→100%(호출 기준), 출처 0→4.7. 단 토큰 29.4k→97k(3.3배), 지연 280→602s채택
EXP-66과검색·재검색 루프 제거(검색 1라운드)토큰 97k→17.8k(-82%), 지연 -65%, 턴 10.3→5.0. grounded 100% 유지채택
EXP-67"검색 URL 전부 나열" 강제출처 2.3→15.7(한 task는 37개 덤프), 토큰 +61%, grounded 100→67%기각
EXP-68도메인 중복제거 도구 + "핵심 5~8개만" 상한출처 4.7 회복, grounded 100%, 지연 177s(최저)채택
전체 패턴: 능력(grounding)은 도구 도입 한 번(EXP-65)에 통째로 얻었고, 이후 EXP-66·68은 전부 "같은 능력을 어떻게 더 싸고 깔끔하게"라는 튜닝이었습니다. 중간에 EXP-67은 "출처가 줄어 아쉬우니 다 인용시키자"는 욕심으로 한 칸 헛디뎠다가, EXP-68에서 "양이 아니라 질"로 제대로 다시 올라섰습니다.
래더1변수experiments
심화EXP-65는 채택(promoted)인데 왜 토큰이 97k로 폭증했나요? 채택해도 되는 건가요?
핵심: EXP-65는 '능력을 처음 획득한' 결정적 도약이라 채택했지만, 동시에 '과검색·재검색 루프'라는 새 비용을 떠안았습니다. 그 비용은 바로 다음 칸(EXP-66)에서 정리됩니다.

EXP-65에서 한 일은 단 하나, analyst에게 검색 도구와 "오늘 날짜"를 준 것입니다. 그 효과로 grounded(호출 기준)가 0→100%, 검색 0→17.3회로 뛰었습니다. 그런데 토큰이 29.4k에서 97k(3.3배), 지연이 280s에서 602s로 같이 폭증했죠. 원인은 두 가지입니다:

  • 과검색: 모델이 검색을 남발했습니다(평균 17.3회/run). 검색 결과 텍스트가 매번 대화에 쌓여 토큰을 잡아먹습니다.
  • critic 재검색 루프: 검증 담당 critic이 "부족해, 더 찾아봐"를 반복시켜 검색·요약이 계속 돌았습니다.

그래도 왜 채택(promoted)일까요? 래더에서 promoted의 의미는 "완성형"이 아니라 "이전 베이스라인을 넘어선 새 기준점"입니다. EXP-65는 0이었던 grounding을 (호출 기준) 100%로 만든 능력의 첫 획득이라, 비용이 비싸도 "앞으로 다듬을 가치가 있는 새 토대"로 채택됩니다.

그리고 약속대로 다음 칸 EXP-66이 주범(과검색+재검색)을 제거해 토큰을 97k→17.8k(-82%)로 떨어뜨렸습니다. 즉 EXP-65의 "비싼 채택"은 한 칸 더 가면 정리될 임시 비용이었고, 분리해서 측정했기에 정확히 무엇을 깎아야 할지 알 수 있었습니다.
EXP-65과검색promoted
중급EXP-67(출처를 전부 인용하도록 강제)은 왜 기각됐나요?
핵심: "검색한 URL을 전부 나열하라"고 강제했더니 출처가 핵심 없는 노이즈 덤프로 불어났고, 토큰은 늘고 근거율(grounded)은 오히려 떨어졌기 때문입니다.

동기는 선했습니다. EXP-66에서 출처가 4.7→2.3개로 줄어든 게 아쉬워서, synthesizer(종합 담당)에게 "검색해서 나온 URL을 전부 적어"라고 시킨 거죠. 결과는:

  • 출처 2.3→15.7개로 늘긴 했지만, 한 task는 37개를 통째로 덤프했습니다. 중복·관련 없는 링크가 잔뜩 섞인 "노이즈"였습니다.
  • 토큰 +61%(쓸데없는 URL 나열 비용).
  • 그리고 결정타: grounded가 100→67%역행했습니다.

왜 기각(rejected)인가: 래더에서 rejected는 "개입이 순개선이 아니다"라는 뜻입니다. 출처 숫자 하나만 올랐을 뿐, 더 중요한 지표(근거율·비용)가 같이 나빠졌으니 부작용이 이득을 넘었습니다.

교훈: 인용은 양(전부)이 아니라 질(핵심)입니다. 출처 37개를 던지는 리포트는 5~8개를 엄선한 리포트보다 신뢰가 떨어집니다 — 독자가 무엇이 진짜 근거인지 분간할 수 없으니까요. 그래서 다음 칸 EXP-68은 "전부" 대신 "핵심 5~8개만"으로 상한을 두고 중복을 제거해, 같은 목표를 더 싸고 깔끔하게 달성했습니다.
EXP-67인용rejected
심화EXP-69는 왜 "판단보류(inconclusive)"인가요? pass^k 0.667이 무슨 신호인가요?
핵심: EXP-69는 개입(변경) 없이 v6을 같은 task에 3번씩 다시 돌려본 '재측정'인데, 출처 수가 실행마다 들쭉날쭉(9번 중 3번만 인용)이라 "출처 4.7"을 믿을 만한 KPI로 확정할 수 없었습니다. 그래서 채택도 기각도 아닌 판단보류입니다.

먼저 두 용어를 풀겠습니다. 같은 문제를 k번 반복해서 풀게 한 뒤:

  • pass@1 = 작업당 k회 중 1회 이상 성공한 비율 (운 좋게 한 번이라도 맞으면 OK)
  • pass^k = k회 전부 성공한 비율 (항상 일관되게 맞아야 OK)

EXP-69(v6, k=3, n=9)에서 pass@1 = 0.889인데 pass^k = 0.667입니다. 둘의 간격이 신호입니다: 어떤 task는 "3번 중 일부만" 성공했다는 뜻 — 즉 같은 입력·같은 설정(seed·temperature 고정)인데도 답이 흔들리는 비결정성(non-determinism)이 드러난 자리입니다.

여기에 더해 출처 인용은 9번 중 단 3번(cited 3/9)만 리포트에 박혔습니다. 검색은 했는데(grounding 행위는 견고) 그 결과를 본문 인용으로 옮기는 건 비결정적이었던 거죠.

그래서 inconclusive: "검색을 했나"(events)는 안정적으로 잴 수 있지만, "리포트 출처 수"(report_sources)는 N=3 행운에 가까워 KPI로 신뢰 불가. pass^k가 pass@1보다 크게 낮으면 "한 번의 화려한 숫자(N=1 dramatic)는 노이즈일 수 있다"는 경고등입니다. 다음 과제는 클레임 단위 교차검증과 인용 강제로 이 흔들림을 잡는 것입니다.
pass^k비결정성inconclusive
중급같은 개선을 5개 주제에 돌린 일반화 결과 — 어디서 돕고 어디서 안 돕나요?
핵심: 웹 검색의 가치는 "학습 컷오프 이후의 최신 사실" 주제에서만 뚜렷하고, 시간을 안 타는 개념이나 출처가 빈약한 지역 주제에선 우위가 약하거나 무의미합니다.

한 주제(반도체)에서 좋았다고 해서 다 좋은 건 아닙니다. 그래서 5개 주제 각각에 base vs 개선을 돌려본 결과입니다(두 숫자는 본문 grounded 횟수 / 출처 인용 횟수입니다).

주제 유형base → 개선판정
최신 LLM 신제품(컷오프 이후)0/0 → 17/6명확한 우위 — 본문·출처 모두 성공
시점 민감 실적0/0 → 5/0애매 — 본문은 근거 있으나 인용 누락(flaky)
최신 규제 동향0/0 → 6/0애매(flaky)
지역 업황(로컬 출처)0/0 → 6/0안전성 우위 — 개선은 정직하게 거부, base는 환각
시간 안 타는 개념(대조군)0/0 → 5/0무승부 — 검색이 무용
읽는 법: 두 숫자는 "본문에 근거가 박힌 횟수 / 출처 URL로 인용된 횟수"입니다. 가장 명확한 승리는 최신 신제품 주제(17/6)입니다 — 모델이 모를 수밖에 없는 사실이라 검색이 결정적이고, 5개 주제 중 유일하게 출처 인용까지 성공했죠. 반대로 시간을 안 타는 개념(동적 계획법 같은 교과서 지식)은 모델이 이미 잘 알아서 검색이 도움이 안 됩니다(의도대로 "검색이 안 돕는 경계"를 확인). 즉 도구는 만능이 아니라 "문제 유형"에 맞을 때만 값어치를 합니다. 그리고 본문 근거는 생겼는데 출처 인용이 들쭉날쭉(flaky)한 것이 다음 숙제입니다.
일반화5주제경계
중급출처 인용이 "flaky(들쭉날쭉)"하다는 게 무슨 뜻인가요?
핵심: 검색을 시도하는 행위(검색 도구 호출)는 안정적으로 일어나는데, 그 검색 결과가 리포트 본문의 출처 목록으로 옮겨지는지는 실행마다 달라진다(가끔 박히고 가끔 빠진다)는 뜻입니다.

flaky는 "같은 조건인데 결과가 안정적이지 않다"는 뜻의 일상어입니다. 여기서 핵심은 두 가지가 분리된다는 점입니다:

  • 검색 도구를 썼나(grounding 행위): 도구를 실제로 호출했는지를 events로 잽니다. 호출 자체는 안정적입니다 — 실검색 백엔드(Firecrawl) 적용 시 v6은 grounded 100%.
  • 리포트에 출처가 박혔나(report_sources): synthesizer가 검색한 URL을 본문 "## 출처"에 옮겨 적었는지입니다. 이게 비결정적이라 들쭉날쭉합니다.

실측이 이를 적나라하게 보여줍니다. EXP-69(k=3, n=9)에서 검색 도구는 9번 중 8번 호출됐지만, 리포트 인용은 9번 중 3번(3/9)만 박혔습니다(이 배치는 DDG 폴백이라 실제 검색 성공 자체도 약 33%로 낮았습니다 — 호출과 성공은 또 다른 문제입니다). 5주제 일반화에서도 5개 주제 중 출처 인용에 성공한 건 최신 LLM 주제 단 하나뿐이었습니다.

왜 중요한가: 그래서 이 프로젝트는 "리포트에 URL이 몇 개 있나"를 1급 신뢰 지표로 쓰지 않습니다. 대신 "검색 도구를 실제로 때렸나"(events)를 견고한 grounding 신호로 삼고, 출처 수는 참고 지표로만 둡니다. 비유하자면 "자료를 찾아봤다"는 확실히 기록되지만 "각주를 빠짐없이 달았다"는 사람(모델)에 따라 깜빡하는 것과 같습니다. 각주를 꼭 달게 만드는 것(인용 강제)이 다음 과제입니다.
flakyreport_sources비결정성
기초지역(로컬 출처가 빈약한) 주제에서 base는 "환각", 개선은 "거부"했다는데 — 거부가 더 좋은 건가요?
핵심: 네. 모를 때 그럴듯하게 지어내는 것(환각)보다, "자료가 부족해 쓸 수 없다"고 정직하게 멈추는 것(거부)이 더 안전하고 신뢰할 수 있는 행동입니다.

상황은 이렇습니다. 지역 업황 주제는 로컬 출처가 빈약해서 검색이 잘 안 됐습니다. 두 팀의 반응이 갈렸습니다:

  • base(웹 없음): 검색 도구가 없으니 그냥 기억에서 지역 통계 수치를 지어냈습니다(환각). 출처는 0개. 그럴듯해 보이지만 검증 불가능한 가짜일 수 있습니다.
  • 개선(웹 있음): 검색을 시도했으나 결과가 빈약하자, 정직하게 "데이터 부족 — 작성 불가"로 거부했습니다.

왜 거부가 더 나은가: 의사결정에 쓰일 리포트라면, "틀린 답을 자신 있게 주는 것"이 "모른다고 인정하는 것"보다 훨씬 위험합니다. 틀린 숫자를 믿고 결정을 내리면 피해가 크지만, "데이터 부족"이라고 하면 사람이 직접 더 찾아보면 됩니다.

이게 바로 base 팀의 근본 실패 모드입니다 — 모델은 시계도 자료도 없는데 "모른다"고 말하는 대신 빈칸을 그럴듯한 말로 채웁니다. 검색 도구는 단지 "최신 정보를 준다"를 넘어, "근거가 없으면 멈출 줄 알게" 만드는 안전장치 역할도 합니다. 다만 이 거부 능력 자체가 항상 완벽하진 않으니(다른 주제에선 인용이 flaky), 이것도 계속 측정 대상입니다.
환각거부안전성
기초base 리포트도 술술 읽히고 그럴듯한데, 왜 나쁘다고 하나요?
핵심: 글이 매끄러운 것과 내용이 사실인 것은 전혀 다른 문제입니다. base 리포트는 출처가 0개라 "맞는지 틀린지 확인할 방법 자체가 없는" 글입니다.

AI 모델은 문장을 자연스럽게 만드는 데 매우 뛰어납니다. 그래서 base 리포트도 전문가가 쓴 것처럼 유창합니다. 함정이 바로 여기 있습니다 — 유창함은 정확성의 증거가 아닙니다. 모델은 사실을 모를 때도 똑같이 자신 있고 매끄럽게 씁니다.

이 페이지에 실제로 그 위험을 보여주는 일화가 있습니다. 이 글 초안에서 작성자(AI)가 개선 리포트의 "A사 1분기 영업이익 57조"를 자기 학습 기억(2025년 약 17조)에 비춰 "틀렸다"고 적었습니다. 그런데 틀린 건 AI 쪽이었습니다 — 2026년 업황 슈퍼사이클로 57조가 실제로 맞았고, 도메인 출처로 재확인됐습니다. 즉 출처 있는 웹 리포트가 옳았고, 기억에 기댄 "검증"이 도리어 환각이었습니다.

이게 핵심입니다: base 리포트의 문제는 "틀렸다"가 아니라 "맞는지 틀린지 누구도 확인할 수 없다"는 것입니다. 출처가 0개니 독자가 뒤를 밟아 검증할 길이 없습니다. 매끄러운 가짜와 매끄러운 진짜를 구별할 수 없다면, 그 글은 의사결정에 쓸 수 없습니다. 그래서 이 프로젝트는 "읽기 좋은가"가 아니라 "실제 자료에 근거했나(grounded)"를 1급 품질로 봅니다. 그리고 그 근거조차 모델의 자기보고가 아니라 도구 호출 기록(events)으로 독립 검증합니다 — 모델 말은 믿지 않습니다.
유창함≠정확성57조검증가능성
심화결국 멀티에이전트 개선이란 프롬프트(시스템 메시지) 튜닝이 핵심 아닌가요?
핵심: 아닙니다. 이 실험들의 가장 일관된 발견은 정반대입니다 — 채택된 개선은 전부 도구·입력 레벨이었고, 기각된 시도는 전부 프롬프트·구조 추가였습니다.

이 직관은 매우 흔합니다 — "AI를 더 잘 시키려면 지시문(프롬프트)을 잘 쓰면 되지". 하지만 실측 메타패턴은 다릅니다:

레버 종류실험결과
도구/입력 레벨EXP-64·65·66·68전부 채택(promoted)
프롬프트/구조 추가EXP-57·63·67전부 기각(rejected)

가장 큰 단일 도약(grounded 0→100%)은 프롬프트 한 줄이 아니라 검색 도구(web_search) 하나가 만들었습니다. 반대로 프롬프트를 만진 시도들은 자주 무너졌습니다:

  • EXP-63: "더 신중히 하라"는 지시 한 줄에 정확도 pass@1이 0.80→0.27로 붕괴.
  • EXP-67: "출처를 전부 인용하라"는 지시가 노이즈 덤프와 근거율 역행을 부름.
왜 그런가: 프롬프트는 취약하고 비단조적입니다 — "더 잘하라"는 지시가 잘 돌던 흐름을 깨기 쉽습니다. 반면 도구는 "할 수 있는 일" 자체를 늘리고, 토폴로지(누가 언제 말하나)는 협업 구조를 바꿉니다. 그래서 결론은: "무엇을 시킬까(프롬프트)"보다 "무엇을 할 수 있게 할까(도구)"와 "어떻게 협업시킬까(토폴로지)"가 더 높고 신뢰할 레버입니다. 단, 이건 "이미 베이스라인이 높고 grounding이 정답을 좌우하는" 이 실험 영역(regime)에서의 결론이며, 모든 상황에 일반화하진 않습니다.
메타패턴도구>프롬프트토폴로지
기초리포트(§2)는 반도체 1개 주제만 보여주는데 헤드라인 KPI 숫자는 3개 주제 평균입니다. 보여주는 사례와 자랑하는 숫자가 다른 주제라면 유리한 사례만 고른 체리피킹 아닌가요?
거꾸로입니다. 숫자를 1개 주제로만 냈다면 체리피킹이지만, 일부러 3개 주제 평균(N=3)으로 낸 것이 바로 체리피킹을 막는 장치입니다. 리포트는 사람이 읽고 느끼라고 1개만 보여주고, 숫자는 그 느낌이 우연이 아님을 증명하려고 3개로 잰 것입니다.

두 가지를 분리해서 보면 오해가 풀립니다. 하나는 '사람이 직접 읽고 질을 느끼는 예시'이고, 다른 하나는 '그 차이가 통계적으로 믿을 만한지 재는 숫자'입니다. 한 화면에 긴 리포트 3개를 나란히 붙이면 아무도 안 읽습니다. 그래서 읽기용으로는 대표 1개(반도체)만 펼쳤습니다.

반대로 숫자는 1개 주제(N=1)로만 내면 '운'일 수 있습니다. 그래서 성격이 다른 세 주제 — 반도체 + 전기차 배터리 + AI 가속기(GPU) — 를 각각 돌려 평균을 냈습니다. 이것이 N=3의 뜻입니다. 비유하면, 동전 한 번 던져 앞면이 나왔다고 '이 동전은 앞면이 잘 나온다'고 말하면 우연이지만, 세 번 던져 평균을 보면 더 믿을 만한 것과 같습니다.

체리피킹이라면 오히려 평균을 피하고 가장 좋은 단일 run 하나를 헤드라인에 박았을 겁니다. 여기서는 정반대로, 평균이 흔들린다는 사실(예: 출처 4.7개는 'N=3 행운'이고 실제 분포는 대개 0, 가끔 분출)까지 §5·§6에 그대로 적어 두었습니다. 불리한 변동성을 숨기지 않는 것이 체리피킹과의 결정적 차이입니다.
체리피킹N=3방법론
심화채택된 v6도 k=3으로 다시 돌리니(EXP-69) grounding 성공기준이 100%에서 33%로 떨어지고 인용은 9건 중 3건뿐이었습니다. 그러면 §1·§5의 '0→100%' 헤드라인은 운 좋은 한 배치 아닌가요?
'0→100%' 헤드라인은 호출기준(검색 도구를 한 번이라도 호출했나)이고, k=3에서 33%로 떨어진 것은 성공기준(검색이 실제 결과를 반환했나)입니다. 둘은 다른 자(尺)이며, 두 숫자가 같이 흔들린 게 아니라 애초에 다른 것을 재고 있습니다.

이 페이지에서 가장 큰 함정이 바로 이 두 개의 grounded%입니다.

  • 호출기준(원장 기록): web_search를 1회라도 불렀나 → v6 100%. §1·§5 헤드라인 '0→100%'가 이것입니다. 도구가 없던 base가 0%, 도구가 생긴 개선이 100%. 즉 '능력 자체'를 켰다는 뜻입니다.
  • 성공기준(뷰 v_analysis_grounded): 그 검색이 실제로 내용을 들고 왔나 → 여기서 k=3 재현 시 33%로 내려갑니다.

왜 갈릴까요. 키 없는 폴백 검색이 차단·rate-limit에 걸리면 '호출은 됐지만 빈손'(검색 결과 없음)을 돌려줍니다. 호출은 셌으니 호출기준 100%, 그러나 내용이 없으니 성공기준은 떨어집니다. '검색을 했다'와 '근거를 얻었다'는 다릅니다.

그래서 채택 근거가 흔들리는 게 아니라 오히려 강해집니다. k=3(EXP-69)은 pass@1 0.889인데 pass^k 0.667 — 같은 작업을 세 번 다 성공하진 못하는 비결정성이 드러난 자리입니다. 이 변동성을 숨겼다면 문제지만, 페이지는 '4.7은 N=3 행운, 실측은 대개 0·가끔 분출'이라 명시하고, 운영 결론으로 폴백 검색 → 키 기반 실검색(Firecrawl) 백엔드 교체를 적어 둡니다. 헤드라인이 호출기준임도 §1·§5에 밝혀 두었습니다.
grounded 두 정의비결정성pass^k
심화EXP-64는 '비활성 decoy 출처를 검색에서 강등'했다는데, decoy가 정확히 무엇이고 코드는 어떤 출처를 강등할지 어떻게 정하나요? 정답에 맞춰 검색을 조작했다면 벤치마크 과적합 아닌가요?
decoy(미끼)는 '정답과 이름은 같지만 비활성(inactive)인 가짜 항목'이고, 코드는 정답을 보고 고르는 게 아니라 항목의 상태=active/inactive 메타데이터만 보고 비활성 것을 순위에서 내립니다. 정답 키를 본 적이 없으므로 과적합이 아니라 일반적인 검색 위생(hygiene)입니다.

실험 환경부터: hard 코퍼스는 문서 60개 중 30개가 decoy — 같은 이름을 쓰는 '비활성 동명 엔티티'(예: 폐업했거나 더는 쓰이지 않는 동명의 항목)입니다. 멀티홉 질문에서 검색이 이 비활성 항목을 윗줄에 올리면 planner가 헷갈려 답을 망칩니다. base(v1)가 hard에서 pass@1 0.80에 머문 원인이 이것입니다.

EXP-64의 처방은 demote_inactive=True 한 줄입니다. 검색 도구가 결과를 정렬할 때 활성 여부 플래그를 보고 비활성 항목의 순위를 내립니다. 핵심은 — 이 규칙이 보는 것은 '이 항목이 살아있는 엔티티인가'이지 '이게 정답인가'가 아니라는 점입니다. 정답이 무엇인지 코드는 모릅니다.

바로 이 점이 같은 약점을 프롬프트로 고치려던 EXP-63과 대비됩니다. EXP-63은 planner·researcher에게 '정확한 전체 이름·활성 엔티티만 써라'고 지시를 강화했다가 working flow가 깨져 0.80→0.27로 붕괴했습니다. EXP-64는 프롬프트를 그대로 두고 검색 레이어만 손봐 0.80→0.87(hop2는 0.60→0.90), 게다가 지연 −38%·토큰 −9%로 동시 개선된 유일한 dominant 케이스입니다.

과적합이 아닌 이유를 한 줄로: 정답에 맞춰 검색을 조작하면 '활성·정답인 decoy'까지 강등돼 점수가 떨어졌을 겁니다. 실제로는 '죽은 항목은 안 보여준다'는 도메인 무관한 규칙이라, 다른 코퍼스에서도 같은 위생이 통합니다.

decoy도구레벨 개입과적합
중급v5(EXP-67)는 출처를 15.7개나 인용하고도 기각됐고 v6은 4.7개로 채택됐습니다. 그러면 '인용 출처 URL 0→6개'를 KPI로 내세우는 것과 모순 아닌가요? 출처 개수는 좋은 지표가 맞나요?
모순이 아닙니다. 핵심 점프는 '0 → 있음'(출처가 아예 없던 base에서 출처를 다는 능력)이지 '많을수록 좋다'가 아닙니다. 출처 개수는 바닥에서 벗어나는 신호로는 훌륭하지만, 많을수록 좋은 단조(monotonic) 지표는 아닙니다 — 어느 선을 넘으면 노이즈가 됩니다.

두 구간을 나눠 보세요.

  • 0 → 소수: base는 출처가 0개입니다. 즉 어떤 주장도 '내 기억'에서 나오고 독자가 확인할 길이 없습니다. 여기서 출처가 몇 개라도 생기면 검증 불가 → 검증 가능으로 질이 도약합니다. KPI '0→6개'가 가리키는 건 이 도약입니다.
  • 적정 → 과다: v5는 synthesizer에게 '검색 URL을 전부 나열하라'고 강제해 출처를 2.3→15.7개로 늘렸습니다. 그러나 한 task는 37개를 덤프(중복·노이즈), 토큰 +61%, grounded는 100→67%로 오히려 나빠졌습니다. 순개선이 아니라 기각입니다.

v6은 도구에 도메인 중복제거(web_search_deduped)를 넣고 synthesizer에 '핵심 5~8개만'이라는 상한을 둬 출처를 4.7개로 회복시켰습니다. 같은 '근거 있는 리포트'라는 목표를 더 싸고(토큰 21k) 더 빠르게(지연 177s, 최저) 달성했습니다.

교훈은 한 줄입니다 — 인용은 양(전부)이 아니라 질(핵심)이다. 그래서 이 프로젝트는 출처 '개수'만 좇지 않고, 그 출처가 실제 검색에서 나왔는지(grounded 성공기준)·중복은 없는지·핵심을 짚는지를 함께 봅니다. 단일 개수 지표를 맹목적으로 올리면 v5처럼 역효과가 난다는 게 측정으로 드러난 사례입니다.
출처 질 vs 양KPI 해석v5 기각
심화base는 turns 5·토큰 29k인데 웹을 붙인 v3는 turns 10.3·토큰 97k로 폭증하고 v6에 와서야 21k로 내려옵니다. v6까지 못 갔다면 '웹=3배 비싸짐'이 결론이었을 텐데, v6에 도달한 건 운인가요 실력인가요? 매번 이런 좋은 끝점에 수렴한다고 보장되나요?
운이 아니라 '측정된 진단 → 표적 수정'을 반복한 결과입니다. 단, '매번 이렇게 좋은 끝점에 수렴한다'는 보장은 없습니다 — 그래서 이 프로젝트는 '결론'을 끝점이 아니라 실패까지 다 적은 래더(ladder, 사다리) 전체로 봅니다.

궤적을 보면 v6은 우연히 떨어진 게 아니라 매 단계가 직전 단계의 측정에서 나온 가설입니다.

단계무슨 일판정
base(v2)웹 없음. turns 5·토큰 29k·grounded 0%·출처 0기준
v3(EXP-65)web_search 첫 투입 → grounded 0→100%. 하지만 과검색으로 토큰 97k·turns 10.3채택(능력)
v4(EXP-66)'grounding은 도구를 한 번 쓰면 달성, 검색 횟수는 비용만'이라는 측정 → 검색 17→5.7로 줄임. 토큰 97k→17.8k(−82%), grounded 100% 유지채택(비용)
v5(EXP-67)출처를 전부 나열 강제 → 15.7개 덤프, grounded 100→67%기각
v6(EXP-68)도구 dedup + '핵심 5~8개' 상한 → 출처 4.7 회복, 토큰 21k, 지연 177s(최저)채택(질)

v3의 '토큰 3배'는 막다른 길이 아니라 진단 데이터였습니다. '왜 비쌌나'를 events 로그로 본 결과 '과검색·재검색 루프'가 범인이라는 게 나왔고, v4는 그걸 정확히 제거했습니다. 즉 v3→v4의 −82%는 운이 아니라 측정이 가리킨 곳을 고친 것입니다.

'매번 수렴 보장'에 대한 정직한 답: 아니오. 그래서 가드레일을 둡니다 — N≥3·pass^k로 단발 드라마를 노이즈로 처리하고, 한 번에 1변수만 바꾸고, paired(같은 작업끼리 짝지은) 비교만 신뢰합니다. 만약 v6까지 못 갔다면 결론은 '웹=3배 비쌈'이 아니라 'v4까지가 이미 −82%, 단 출처 변동성은 미해결'이었을 겁니다(EXP-69의 k=3이 그 변동성을 드러냅니다). 끝점 한 점이 아니라 채택·기각이 함께 적힌 래더 전체가 결론이라는 게 핵심입니다.
수렴측정 기반 반복비용 회복
🔁 재현과 운영 — 직접 돌리려면 14
중급외부 인터넷이 차단된 폐쇄망(air-gapped) 환경에서 이 시스템을 어떻게 재현하나요?
핵심: LLM은 사내 vLLM 같은 내부 엔드포인트로, 웹검색은 사내 검색/RAG로 바꾸면 됩니다. 시스템 자체는 처음부터 폐쇄망 운영을 전제로 설계되어 외부 API를 런타임에 호출하지 않습니다.

폐쇄망(air-gapped)이란 인터넷과 물리적으로 끊긴 망을 말합니다. 이 프로젝트는 운영 환경이 폐쇄망이라는 전제로 만들어졌습니다. 그래서 런타임 코드는 외부에 직접 손을 뻗지 않습니다. 손을 뻗는 곳은 딱 두 군데뿐이고, 둘 다 환경변수로 갈아끼울 수 있습니다.

  • LLM 호출: 모델 주소는 base_url 환경변수 하나(EVAL_LLM_BASE_URL)로 정해집니다. 이 비교 페이지는 외부 GLM-4.7을 썼지만, 폐쇄망에서는 이 값을 사내 vLLM 같은 내부 모델 서버 주소로 바꾸면 됩니다. 코드를 고칠 필요가 없습니다.
  • 웹검색 도구: web_search 한 함수에만 외부 의존이 모입니다. 이걸 사내 검색/RAG로 바꾸는 방법은 별도 질문에서 자세히 다룹니다.
데이터(분석 대상 주제, 채점용 사내 문서 corpus)와 의존성 패키지는 미리 반입해야 합니다. 패키지는 사내 PyPI 미러를 통해 설치합니다. 모든 실행 기록은 사내 PostgreSQL(trace DB)에 쌓이므로, 결과를 외부로 내보내지 않고도 팀 간 비교가 가능합니다.

요약하면, 폐쇄망은 이 시스템에 '특수 케이스'가 아니라 '기본 가정'입니다. 외부망에서만 해야 하는 일은 검색 도구의 백엔드 교체뿐이고, 그조차 함수 하나로 격리돼 있습니다.

폐쇄망재현vLLM
중급config(JSON)만 그대로 가져가서 돌리면 이 페이지와 똑같은 숫자가 나오나요?
핵심: 아니요. 구성(config)은 똑같이 재현되지만, 측정 숫자(grounded%·토큰·지연)는 환경에 따라 달라집니다. 특히 grounded%는 검색 백엔드가 무엇이냐에 절대적으로 좌우됩니다.

config는 '팀을 어떻게 조립할지'를 적은 설계도입니다. 이걸 그대로 로드하면 에이전트 구성, 프롬프트, 토폴로지(고정순환 RoundRobin), seed=42, temperature=0.2까지 동일하게 재현됩니다. 그래서 구성의 재현성은 보장됩니다.

하지만 측정 숫자는 다음 세 가지에 묶여 있어서 환경이 다르면 달라집니다.

  • 검색 백엔드: 이 페이지의 헤드라인 grounded 0→100%는 '검색을 1회라도 호출했나'(호출기준)입니다. 검색이 '실제 결과를 반환했나'(성공기준)는 백엔드에 달려 있습니다. 키 없는 무료 검색 폴백은 차단·속도제한으로 빈손을 자주 반환했고, 키 기반 실검색으로 바꾸자 성공기준이 살아났습니다. 즉 같은 config라도 검색 백엔드가 다르면 grounding 숫자가 완전히 달라집니다.
  • 모델 엔드포인트: 같은 GLM-4.7이라도 서버 버전·라우팅이 다르면 토큰 수와 지연(latency)이 달라집니다.
  • 비결정성(non-determinism): seed·temperature를 고정해도 LLM은 완전히 결정론적이지 않습니다. 한 측정에서 pass@1 0.889인데 pass^k(k회 전부 성공) 0.667이 나온 게 그 증거입니다.
그래서 우리는 단발 숫자를 믿지 않습니다. N>=3 평균과 pass^k를 함께 봅니다. config를 가져가 돌리면 '같은 경향'은 재현되지만 '소수점까지 같은 숫자'를 기대하면 안 됩니다.
재현성config비결정성
심화네이티브 dump_component JSON과 config_full 스냅샷은 무엇이 다른가요?
핵심: 네이티브 JSON(dump_component)은 '실행 가능한 설계도'(다시 로드해서 돌릴 수 있음)이고, config_full 스냅샷은 '사람이 읽는 관측 기록'(돌릴 수는 없음)입니다. 둘을 혼동하면 안 됩니다.

같은 팀을 두 가지 포맷으로 저장합니다. 비유하면, 네이티브 JSON은 '조립 가능한 레고 설명서'이고 config_full은 '완성품 사진과 부품 목록'입니다.

구분네이티브 (dump_component)스냅샷 (config_full)
용도실행 가능 — 다시 로드/임포트관측용 — 사람이 읽기
모델 설정(model_client)인라인 포함(주소·seed·temperature)없음 → 그래서 로드 불가
도구 코드함수 소스코드까지 통째로도구의 impl_sha(지문)만
담은 것실행에 필요한 모든 것프롬프트·도구·종료조건·토폴로지

네이티브 JSON은 한 명령으로 만들어지고, 저장 직전에 api_key를 마스킹(***)하고 프롬프트에 박힌 날짜를 동적화합니다. 이 파일은 그대로 다시 로드하거나 AutoGen Studio로 임포트해 돌릴 수 있습니다.

왜 둘 다 두냐고요? config_full은 사람이 '무엇이 바뀌었나'를 한눈에 비교하기 좋고(프롬프트 지문 변화 등), 네이티브는 '그대로 다시 돌리기'에 좋습니다. config_full에는 모델 설정이 없어서 그것만으로는 절대 실행되지 않는다는 점이 핵심 차이입니다.
dump_componentconfig_fullAutoGen
심화이 팀을 AutoGen Studio에 올려서 그대로 돌릴 수 있나요? 주의할 점은?
핵심: 네, 네이티브 JSON(dump_component 출력)을 AutoGen Studio로 임포트하면 돌아갑니다. 단, api_key를 다시 넣어야 하고, 검색 도구가 직렬화 가능하도록 설계돼 있어야 합니다.

AutoGen Studio는 멀티에이전트 팀을 GUI에서 불러와 실행해 보는 도구입니다. 우리 네이티브 JSON은 처음부터 Studio 임포트를 염두에 두고 만들어졌습니다. 그 안에는 모델 클라이언트 설정, 각 도구의 파이썬 소스코드, 종료조건, 토폴로지가 모두 들어 있어서 자체완결적(self-contained)입니다.

핵심 설계 포인트는 검색 도구입니다. 도구 함수가 표준 라이브러리(urllib)만 쓰도록 만들어, 함수 소스코드가 통째로 JSON에 직렬화돼도 깨지지 않습니다. 외부 SDK에 의존했다면 Studio로 옮겼을 때 임포트가 실패했을 겁니다.

주의점:
  • 키 재주입: 내보낸 JSON의 api_key는 보안상 ***로 마스킹돼 있습니다. Studio에서 돌리려면 실제 키(또는 사내 모델 주소)를 다시 넣어야 합니다.
  • 콘텐츠 필터: 외부 모델 제공자를 쓸 경우 콘텐츠 필터가 간헐적으로 응답을 차단할 수 있습니다. 폐쇄망 사내 모델을 쓰면 이 변수는 사라집니다.
  • 동시성: 여러 run을 한꺼번에 돌리면 런타임 종료 시점 경합으로 일시 크래시가 날 수 있습니다. 한 번에 하나씩 독립적으로 돌리는 편이 안전합니다.

요컨대 '임포트해서 돌리기'는 의도된 기능이고, 막히는 지점은 거의 키와 망 정책뿐입니다.

AutoGen Studio임포트직렬화
중급폐쇄망이라면 web_search를 사내 검색이나 RAG로 어떻게 교체하나요?
핵심: 모든 외부 검색 의존이 web_tool.py의 web_search 함수 한 곳에 모여 있습니다. 같은 입력(질의 문자열)→같은 형식의 출력(번호·제목·요약·URL 블록)을 지키면 함수 내부를 사내 검색/RAG로 통째로 갈아끼울 수 있습니다.

이 시스템은 이미 검색 백엔드를 '런타임 분기'로 다룹니다. 키가 있으면 키 기반 실검색(Firecrawl)을, 없으면 무료 폴백(DuckDuckGo)을 씁니다. 사내 검색/RAG를 추가하는 건 이 분기에 한 갈래를 더 얹는 일입니다.

지켜야 할 계약(contract)은 단순합니다.

  • 입력: 질의(query) 문자열 하나.
  • 출력: '[n] 제목 / 요약 / URL' 블록의 연속 문자열. 결과가 없으면 '(검색 결과 없음)' 문자열을 반환.

이 출력 형식만 맞추면, 도메인 중복제거 래퍼(web_search_deduped)나 채점 로직을 건드릴 필요가 없습니다. 사내 RAG라면 'URL' 자리에 사내 문서 ID나 사내 위키 링크를 넣으면 됩니다.

가장 비싸게 배운 교훈: '검색을 했다'와 '근거를 얻었다'는 다릅니다. 무료 폴백 검색은 차단·속도제한으로 호출은 되지만 빈손(검색 결과 없음)을 자주 반환했습니다. 이때 도구에러가 아니라 정상 결과 자리에 '(검색 결과 없음)' 문자열이 저장되어, 도구에러율은 0%인데 실제 근거 성공률은 0%인 상태가 생깁니다. 사내 RAG로 바꿀 때도 '빈 결과를 정직하게 빈손으로 반환하는지'를 반드시 확인하세요. 그래야 grounding 성공기준이 정확히 측정됩니다.
web_searchRAG폐쇄망
중급반도체가 아닌 우리 도메인에 적용하려면 무엇을 바꿔야 하나요?
핵심: 바꿀 것은 ① 분석 주제(태스크) ② 검색/RAG 백엔드 ③ 채점 정답셋, 이 세 가지뿐입니다. 팀 구조·러너·로거·DB는 그대로 둡니다. 그것이 '팀만 바꿔 측정'이 성립하는 이유입니다.

이 페이지의 사례는 반도체였지만, 시스템은 도메인 중립적으로 설계됐습니다. 개선/실험은 팀 정의 파일(teams/*.py) 한 곳에 격리되고, 그 바깥(러너·로거·채점·DB)은 팀과 무관하게 불변입니다. 그래서 도메인을 바꿔도 측정 인프라는 재사용됩니다.

  • 주제(태스크): 분석 주제를 우리 도메인의 주제로 교체합니다. 한 주제는 우연일 수 있으니 N>=3, 즉 서로 다른 주제 3개 이상을 준비하세요(이 페이지의 메트릭도 반도체·전기차배터리·AI가속기GPU 세 주제의 평균입니다).
  • 검색/RAG: 우리 도메인의 사실을 찾을 수 있는 사내 검색/RAG로 web_search를 교체합니다(앞 질문 참고).
  • 채점: 사실 QA 라더처럼 정답이 정해진 과제라면 사내 문서 corpus와 정답셋(exact-match 기준)을 우리 도메인 것으로 바꿉니다.
한 가지 경계가 있습니다. 이 페이지의 결론('웹 도구 하나가 가장 큰 점프를 만든다')은 '웹 최신 사실이 정답을 좌우하는' 분석 과제에 한정됩니다. 우리 도메인에서 정답이 최신 웹 사실에 의존하지 않는다면(예: 개념·정의 위주), 웹 도구의 효과는 작거나 무승부일 수 있습니다. 실제 측정에서 timeless한 개념 주제는 웹이 무용했습니다. 그러니 도메인을 바꿀 땐 '우리 정답이 무엇에 좌우되는가'를 먼저 따져야 합니다.
도메인 적용태스크채점
기초한 번 돌리는 데 비용이 얼마나 드나요? 토큰을 돈으로 환산하면?
핵심: 비용의 핵심 지표는 run당 토큰 수입니다. base는 run당 약 29.4k 토큰, 개선(현재 챔피언 v6)은 약 21.3k 토큰으로 base보다 28% 적습니다. 절대 금액은 쓰는 모델·단가에 따라 달라지므로, 단가에 토큰 수를 곱해 환산하세요.

이 시스템은 모든 토큰을 events 테이블에 누적해 기록하므로 run마다 정확한 토큰 수를 알 수 있습니다. 분석 라더의 run당 토큰은 다음과 같습니다.

토큰/run지연/run비고
base (v2-analyst-deep)29,409279.7s웹 없음
개선 챔피언 (v6-analyst-web)21,285177.1s검색+dedupe
실패한 변형 (v3)97,089601.6s검색 과다(run당 17.3회)

눈여겨볼 점은, 검색 도구를 '제대로' 붙인 개선판이 base보다 오히려 토큰이 적다는 것입니다. 도구 레벨에서 중복 검색을 차단하고 검색 횟수를 줄였기 때문입니다(v6은 run당 4.0회). 반대로 프롬프트로 검색을 과하게 밀어붙인 변형(v3은 run당 17.3회)은 토큰이 97k까지 치솟았습니다.

비용으로 실패를 잡아내는 신호: 우리는 tokens_per_success(성공 1건당 토큰)라는 지표로 실패 변형을 정확히 가려냈습니다. 기각된 실험들은 이 값이 비정상적으로 컸습니다(한 사례는 119k). 즉 '돌아는 가는데 비싸게 돌아가는' 변형을 돈 지표로 조기에 걸러낼 수 있습니다. 절대 금액은 모델 단가(입력/출력 토큰 단가)에 위 토큰 수를 곱하면 나옵니다 — 단가는 쓰는 엔드포인트마다 다르므로 여기서 단정하지 않습니다.
비용토큰지연
중급이 개선 루프를 한 바퀴 도는 데 필요한 최소 셋업과 절차는 무엇인가요?
핵심: ① DB 초기화 → ② 검색 왕복 확인 → ③ base 팀으로 작은 배치 실행 → ④ 1변수만 바꾼 개선 팀으로 같은 배치 실행 → ⑤ paired(A/B) 비교 후 promoted/rejected 판정을 원장에 기록. 이 다섯 단계가 한 바퀴입니다.

최소 셋업은 사내 PostgreSQL(trace DB), 내부 모델 엔드포인트, 검색/RAG 백엔드, 그리고 분석 주제 N>=3입니다. 절차는 다음과 같습니다.

  1. DB 초기화: 스키마를 멱등하게 만듭니다(두 번 돌려도 안전). 스키마가 단일 진실 공급원(SSOT)이며 불변식은 DB의 CHECK 제약으로 강제됩니다.
  2. 왕복 확인(smoke): 모델 function calling과 검색 도구가 실제로 결과를 반환하는지 먼저 확인합니다. '측정 전 하니스(harness) 버그부터' 잡는 단계입니다.
  3. base 실행: 개선 전 팀으로 작은 배치를 돌려 기준선을 만듭니다.
  4. 개선 실행: 딱 한 가지 변수만 바꾼 팀으로 같은 주제·같은 seed로 돌립니다. 나머지(critic·planner 등)는 코드를 복사하지 말고 그대로 import 합니다.
  5. 비교·기록: 같은 주제끼리 짝지은(paired/matched) A/B로 비교하고, 개선이 grounded·정확도·토큰·지연에서 손해 없이 이겼는지 봅니다. 결과를 experiments(개선 원장)에 promoted/rejected로 남깁니다.
팀 구성을 한 글자라도 바꾸면 새 team_id가 강제됩니다(프롬프트 지문 prompt_bundle_hash가 바뀌므로). 같은 team_id에 다른 프롬프트 조합이 들어오면 시스템이 즉시 에러로 막습니다. 이것이 '바뀐 게 정말 그 한 변수뿐'임을 DB 차원에서 보증합니다. 그래서 한 바퀴의 핵심은 '1변수·1파일' 규율입니다.
개선루프절차1변수
심화'가드레일 7계명'이 무엇이고, 왜 비싸게 배운 교훈인가요?
핵심: 측정을 망치는 7가지 함정을 막는 규칙입니다. 각 계명은 실제 실험이 무너지거나 가짜 성공으로 둔갑한 사건에서 거꾸로 뽑아낸 것이라 '비싸게 배운' 교훈입니다.
  1. 근거성(grounding)은 events(실제 도구 호출수)로 재라 — LLM의 자기보고를 믿지 마라. 모델이 '검색했다'고 말해도 안 했을 수 있다.
  2. 한 번에 1변수·1파일 — 나머지는 복사하지 말고 import. 그래야 효과를 한 원인에 귀속할 수 있다.
  3. paired(matched A/B)만 신뢰 — 팀별 원시 평균은 주제 난이도가 섞여 교란된다. 같은 주제끼리 짝지어 비교하라.
  4. 측정 전 하니스(harness) 버그부터 — 채점기·도구가 멀쩡한지 먼저 확인. 한 사례는 채점기가 엉뚱한 텍스트를 긁어 '유령 메트릭'을 만들었다.
  5. N>=3·pass^k — 단발의 극적인 결과는 노이즈다. pass^k(k회 전부 성공)로 비결정성을 드러내라.
  6. 제약을 더하기보다 제거하거나 도구 레벨에서 고치는 쪽이 이긴다 — 같은 목표를 프롬프트 강화로 시도하면 흐름이 깨지고, 도구로 풀면 동시에 좋아졌다.
  7. 메트릭을 좇기 전에 그 메트릭이 맞는지 검증하라 — 틀린 자(尺)를 최적화하면 가짜 개선이 쌓인다.
왜 비싸게 배웠나 — 실제 실패에서 나왔기 때문입니다. ①은 빈손 검색이 '호출만 세면' grounded=100%로 둔갑한 사건. ④는 채점기가 검수자(critic) 추론 텍스트 속 출처까지 긁어 기준선을 가린 버그(고치니 베이스라인이 이미 F1 97%였고 개입 효과는 0이었음). ⑥은 '더 신중히 하라'는 프롬프트 한 줄이 정답률을 0.80→0.27로 붕괴시킨 반면, 같은 목표를 도구 레벨에서 풀자 정확도·인용·지연·토큰이 한꺼번에 개선된 대조. ⑦은 '유령 메트릭'으로 가짜 개선을 볼 뻔한 사건. selector를 바꾼 한 줄로 정답률이 0.967→0.067로 무너진 적도 있습니다. 이 7계명은 그런 실패를 다시 겪지 않으려는 비싼 보험입니다.
가드레일측정 함정교훈
기초이 측정과 개선 래더를 한 바퀴 도는 데 사람과 시간이 실제로 얼마나 드나요?
핵심 비용은 사람의 '설계 시간'이 아니라, 1변수 가설을 세우고 N≥3로 돌려 원장에 판정을 남기는 한 사이클의 회전 시간입니다.

이 프로젝트의 한 사이클(가설→딱 한 곳 수정→재실행→판정)은 대략 이렇게 구성됩니다. 비유하자면 요리 레시피를 한 줄만 바꿔보고 같은 손님 3명에게 다시 내보는 것과 같습니다.

  • 사람 시간(주된 비용): '무엇이 이상한가(근인)'를 찾고 '딱 한 곳'을 정하는 데 듭니다. 이게 핵심 지적 노동이고, 코드 변경 자체는 teams/*.py 한 파일에 격리돼 보통 수십 분~몇 시간입니다.
  • 기계 시간(부수 비용): run 한 번의 지연(latency)은 base 약 280초, 개선 약 177초였습니다. 한 주제를 N=3로 돌리고, 분석 라더는 보통 5~6개 팀 변형을 비교하니 수십 분~몇 시간의 자동 실행입니다. 사람이 지켜볼 필요는 없습니다.
  • 기록 시간(가장 작음): 결과는 자동으로 events·runs에 쌓이고, 사람은 experiments 원장에 판정 한 줄(promoted/rejected + 한 줄 교훈)만 남깁니다.
한 사이클의 진짜 병목은 '돌리는 시간'이 아니라 올바른 1변수를 고르는 판단입니다. 그래서 이 방식은 사람을 자동화로 대체하는 게 아니라, 사람의 판단을 되돌릴 수 있는 기록으로 만드는 쪽에 가깝습니다.
비용사이클원장
중급매번 1변수씩 바꿔 N≥3로 돌리는 이 방식이, 그냥 더 좋은 모델을 쓰거나 프롬프트를 손보는 것보다 비용 대비 효과가 정말 있나요?
네, 다만 조건이 있습니다. '더 좋은 모델/프롬프트'는 효과를 모른 채 바꾸는 도박이고, 1변수+N≥3는 효과를 숫자로 아는 투자입니다. 이 프로젝트의 실측이 그 차이를 보여줍니다.

'그냥 손보기'의 함정은 무엇이 효과였는지 영원히 모른다는 점입니다. 여러 곳을 한꺼번에 바꾸면 좋아져도 왜 좋아졌는지, 나빠져도 어디가 범인인지 알 수 없습니다. 이 프로젝트에서 그 위험이 실제로 드러났습니다.

  • 프롬프트 한 줄이 시스템을 무너뜨림: '더 신중히, 모호하면 되물어라'는 지시 한 줄을 넣은 EXP-63은 pass@1이 0.80→0.27로 붕괴했습니다. selector를 round_robin으로 바꾼 한 줄(EXP-60)은 0.967→0.067 붕괴. '좋아 보이는 손질'이 정반대 결과를 낸 것입니다.
  • 같은 목표, 정반대 결과: EXP-63은 프롬프트로 출처 품질을 올리려다 붕괴했지만, 같은 목표를 도구 레벨로 푼 EXP-64는 pass@1·인용·지연·토큰이 동시에 개선됐습니다(promoted). 1변수로 격리했기 때문에 '프롬프트가 아니라 도구가 답'이라는 교훈을 건졌습니다.

N≥3가 왜 필수냐: 단발 결과는 노이즈입니다. v6를 k=3로 돌렸더니 pass@1 0.889인데 pass^k(3번 다 성공)는 0.667 — 같은 팀도 비결정적이라는 게 드러났습니다. N=1이었다면 우연한 한 번을 '개선'으로 착각했을 자리입니다.

1변수N≥3비용대비효과
중급매번 원장에 기록하는 게 번거로운데, 그 기록이 실제로 비용을 아껴준 사례가 있나요?
있습니다. 원장과 검증 습관이 없었다면 '유령 개선'을 진짜 개선으로 착각해 잘못된 방향으로 계속 투자했을 사례가 실제로 있었습니다.

가장 비싼 실수는 '나빠지는 것'이 아니라 나아진 척하는 가짜 신호를 믿는 것입니다. 가짜 방향으로 며칠을 더 달리게 만들기 때문입니다. 이 프로젝트가 그걸 두 번 잡아냈습니다.

  • 유령 메트릭(EXP-57): 인용 점수(format)가 1.000으로 '완벽'해 보였지만, 알고 보니 채점기가 엉뚱한 텍스트(critic의 추론 글)에서 출처를 긁어 점수를 부풀린 버그였습니다. 채점을 진짜 답변 영역으로 한정하니 베이스라인이 이미 97%였고, 개입 효과는 0 + 정답 1건 회귀. 원장에 'rejected, 효과 0'으로 남겨 이 방향을 닫았습니다.
  • 57조 환각 일화: 이 페이지 초안에서 AI 작성자가 개선 리포트의 한 수치를 자기 학습지식으로 '틀렸다'고 적었는데, 틀린 건 AI였습니다. 출처 있는 웹 리포트가 맞았고 기억 기반 '검증'이 환각이었죠. 출처와 기록이 없었다면 맞는 걸 틀렸다고 고쳤을 겁니다.

tokens_per_success라는 파생 지표는 기각감을 정확히 때렸습니다(붕괴한 실험들에서 55k·97k·119k로 폭증). 즉 원장은 '느낌'이 아니라 숫자로 손절선을 그어줍니다 — 이게 번거로움의 값을 합니다.

교훈을 한 줄로: 측정 전에 하니스(채점기) 버그부터 잡아라. 잘못된 메트릭을 좇는 비용이, 기록하는 비용보다 훨씬 큽니다.
원장유령메트릭손절
심화비용을 줄이려면 결국 더 싼/좋은 모델로 바꾸면 되지 않나요? 굳이 도구·토폴로지를 만지는 이유는요?
모델 교체는 '무엇이 병목인가'에 따라 효과가 갈립니다. 이 프로젝트의 병목은 모델 능력이 아니라 '근거(grounding)와 협업 구조'였고, 거기서 가장 큰 점프는 모델이 아니라 도구 하나가 만들었습니다.

모델·seed·temperature를 전부 고정한 채 웹 검색 도구 하나만 더했더니, 근거 없이 쓰던 리포트가 출처를 인용하는 리포트로 바뀌었습니다(grounded 0→100%, 인용 출처 0→6개, 기준시점 없음→2026-06-14). 같은 모델로요. 즉 답을 좌우한 건 '무엇을 시킬까'가 아니라 '무엇을 할 수 있게 할까'였습니다.

  • 비용도 구조에서 나왔습니다: 과검색·재검색 루프를 빼는 1변수 수정(EXP-66)으로 토큰을 97k→17.8k(−82%) 깎으면서도 grounded 100%를 유지했습니다. '더 많이 검색=더 근거 있다'는 직관이 틀렸음을 데이터로 보인 겁니다.
  • 토폴로지는 load-bearing: 발화자 선택 방식을 바꾼 한 줄이 시스템을 붕괴시켰습니다(EXP-60). 모델을 바꿔도 이런 구조적 레버는 그대로 남습니다.
경계 조건(정직한 단서): 이건 '이미 베이스라인이 높고 grounding이 정답을 좌우하는' 이 regime의 결론입니다. 만약 모델이 도구는 충분히 쓰는데 형식·톤이 문제라면 프롬프트가 지배적 레버일 수 있고, 모델 교체가 답일 수도 있습니다. 핵심은 바꾸기 전에 병목을 측정하는 것 — 그게 이 방식의 본질입니다.
모델교체병목레버
심화이미 나와 있는 딥리서치 제품을 쓰면 되는데, 왜 직접 이 측정 시스템을 만들어 돌리나요?
외부 제품의 결과를 '믿는' 것과, 팀 구성 변경의 효과를 우리 환경에서 '재현하고 측정하는' 것은 다른 문제이기 때문입니다. 이 시스템은 후자를 위해 존재합니다.

다른 LLM 제품들의 정확한 성능을 단정할 수는 없습니다(그건 우리가 통제·측정한 게 아니니까요). 직접 만든 이유는 그들과의 우열 경쟁이 아니라, 네 가지 요구 때문입니다.

  • 측정: 팀 구성을 한 곳만 바꿨을 때 grounded·토큰·지연·인용이 어떻게 움직이는지 숫자로 알아야 합니다. 외부 제품은 내부 궤적(어떤 도구를 몇 번 호출했나)을 우리 DB로 주지 않습니다.
  • 재현: 프롬프트가 한 글자만 바뀌어도 새 team_id가 강제되는 재현성 가드(AC9)로, '무엇을 바꿨더니 무엇이 변했나'를 1급으로 추적합니다.
  • 폐쇄망: 운영 환경은 외부 네트워크를 가정할 수 없습니다. LLM은 사내 엔드포인트, 검색은 키 기반 백엔드로 교체 가능해야 하며, 런타임이 외부 API에 의존하면 안 됩니다.
  • 팩트성 검증: '검색을 했다 ≠ 근거를 얻었다'를 events로 직접 검증해야 합니다. 키 없는 폴백 검색이 빈손으로 돌아온 run을 '근거 있음'으로 둔갑시킨 함정을, 우리 뷰(v_analysis_grounded)가 잡아냈습니다.

요약하면, 외부 제품은 '좋은 답'을 줄 수 있어도 '우리 변경이 왜 효과였는지'는 알려주지 않습니다. 이 시스템의 산출물은 리포트가 아니라 되돌릴 수 있고 비교 가능한 측정입니다.

재현측정폐쇄망
🗄️ 시스템 내부 — DB·코드·아키텍처 11
기초데이터는 어디서 와서 어디에 저장되나? (trace DB의 전체 그림)
모든 데이터는 "팀이 실제로 한 일"에서 나와, PostgreSQL trace DB(흔적 데이터베이스)에 자동으로 쌓인다.

이 시스템은 멀티에이전트 팀(여러 AI가 협업하는 한 묶음)을 실제로 한 번 돌릴 때마다, 그 과정에서 벌어진 일을 빠짐없이 기록한다. "trace"는 비행기의 블랙박스처럼 나중에 무슨 일이 있었는지 되짚어 볼 수 있는 흔적 기록이라고 생각하면 된다.

흐름은 한 방향으로 흐른다:

  • cli.py(명령 입력 창, typer) 가 실행을 시작하면 → runner(배치 실행기, run_batch)가 한 묶음의 작업을 돌린다.
  • registry(팀 명부, build_team)가 어떤 팀을 쓸지 골라 조립하고 → teams/*(실제 AI 팀)가 작업을 수행한다.
  • 팀이 한 단계씩 발화·도구호출을 흘려보내면(team.run_stream()) → tracing/logger(기록기)가 그걸 받아 events 테이블에 한 줄씩 INSERT(삽입)한다. 토큰·턴·지연 시간도 이때 누적된다.
  • 채점기(scorer)가 정답 여부를 매기면 → runs 테이블의 success 칸이 UPDATE(갱신)된다.

정리하면 데이터의 출처는 "AI가 자기 입으로 보고한 자랑"이 아니라 실제 일어난 사건 그 자체다. 그래서 나중에 "이 팀이 진짜 검색을 했나?" 같은 질문에 추측 없이 답할 수 있다. 이것이 이 프로젝트의 핵심 자산이다.

trace DB데이터 흐름PostgreSQL
기초runs / events / experiments 세 테이블은 각각 무엇을 담나?
runs=실행 1회의 조건과 결과, events=그 실행 안에서 벌어진 모든 사건, experiments=개선 시도의 장부(ledger)다.

세 테이블은 시간 단위가 다르다. 비유하자면 runs는 "한 경기의 스코어보드", events는 "그 경기의 분초별 중계 로그", experiments는 "시즌 전체에 걸친 전술 변경 기록부"다.

테이블담는 것예시 칸
runs실행 1회의 조건과 결과성공 여부(success), 모델, seed, temperature, 토큰 합계, 지연(latency)
events한 run 안의 모든 사건을 순번(seq) 순서로누가 말했나, 어떤 도구를 호출했나(tool_call), 결과(tool_result), 토큰
experiments개선 원장(ledger) — 무엇을 바꿔서 어떤 결론이 났나가설(hypothesis), 개입(intervention), 판정(verdict: promoted/rejected/inconclusive), 교훈(lesson)

왜 이렇게 나눴나. runs만 보면 "이 팀이 성공했다"는 알지만 "왜"는 모른다. 그래서 events로 내려가 "검색을 4번 했고 그중 4번 결과를 받았다" 같은 속사정을 본다(v6-analyst-web의 실측: search/run 4.0, search_ok 4.0). 그리고 "검색 도구를 하나 추가했더니 grounding이 0→100%로 뛰었다"는 식의 변경의 효과experiments에 한 줄로 남긴다. 이 장부 덕분에 채택(promoted)은 전부 도구·입력 레벨, 기각(rejected)은 전부 프롬프트·구조 추가였다는 메타패턴까지 데이터로 읽힌다.

runseventsexperiments
중급왜 모든 이벤트를 빠짐없이 로깅하나? (관측성·observability이 왜 필요한가)
AI의 자기보고를 믿지 않고 "실제로 일어난 사건"으로만 판단하기 위해서다.

관측성(observability)은 어려운 말 같지만 뜻은 단순하다: 겉으로 안 보이는 내부 상태를, 밖에 남은 기록만으로 알아낼 수 있는 정도를 말한다. 자동차 계기판이 엔진 속을 숫자로 보여주는 것과 같다.

이게 왜 결정적인지는 이 프로젝트의 가장 큰 함정이 잘 보여준다. "근거성(grounding)", 즉 리포트가 실제 웹 근거에 발을 딛고 있는지를 두 방식으로 잴 수 있다:

  • 위험한 방식(호출기준) — AI에게 "너 검색했니?"라고 묻거나, 검색을 1번이라도 호출했는지만 센다(원장 EXP 기록 방식). 이러면 검색이 빈손((검색 결과 없음))으로 돌아와도 "grounded 100%"로 둔갑한다. 환각 리포트가 합격 도장을 받는다.
  • 안전한 방식(성공기준) — events에 남은 도구호출 사건과 그 결과를 직접 세어, 검색이 "실제 결과를 반환했는가"까지 확인한다(뷰 v_analysis_grounded, 마이그레이션 010).

실제로 분석 라더에서 v3·v4·v5는 호출기준 grounded는 100%였지만 성공기준 grounded는 0%였다(v3=100/0, v4=100/0, v5=67/0). 호출만 셌다면 셋 다 통과시켰을 것이다. v6만 호출·성공 둘 다 100/100이었다. 빠짐없는 로깅이 있어야 이 차이가 드러난다.

가드레일 7계명 ①: grounding은 events(도구호출 수)로 재라. LLM의 자기보고는 금지. 모든 사건을 남기는 이유가 바로 이 한 줄에 압축돼 있다.
관측성grounding근거성
중급"로깅은 평가를 죽이지 않는다"는 원칙은 무슨 뜻인가?
기록(로깅)에 실패해도 평가 자체는 멈추지 않는다 — 경고만 남기고 계속 간다는 뜻이다.

꼬리(기록)가 몸통(평가)을 흔들면 안 된다는 원칙이다. 비유하자면 경기 중계 카메라 하나가 고장 나도 경기는 계속 진행되어야 하는 것과 같다. 만약 이벤트 한 줄을 DB에 넣다 실패했다고 배치 전체를 중단시키면, 비싸게 돌린 수십 개의 실행 결과를 통째로 날리게 된다.

코드에서도 그대로 구현돼 있다. logger.py_safe_insert가 INSERT(삽입)를 try/except로 감싸고, 실패하면 예외를 위로 던지는 대신 경고 로그를 남긴 뒤 1(누락 카운트)을 반환하고 진행한다. 호출 루프는 이 반환값을 missed_inserts(놓친 삽입 수)에 더해 나간다. 실제로 누적 변수 missed_inserts: int = 0이 logger에 정의돼 있다.

그렇다면 무엇이 배치를 멈출 자격이 있나. 오직 DB 연결 자체의 소실뿐이다. 개별 줄 하나가 안 들어가는 것과, 데이터베이스 자체에 말을 걸 수 없는 것은 차원이 다른 사고이기 때문이다.

균형점: 기록은 "있으면 좋은" 보조 장치이지, 평가의 인질이 아니다. 단 놓친 삽입을 조용히 무시하지 않고 세어 둔다는 게 핵심 — 나중에 "이 배치는 기록에 구멍이 있었다"를 알 수 있다.
로깅 원칙missed_inserts신뢰성
중급비밀정보(API 키 등) 마스킹은 어떻게 보장되나?
저장하기 직전에 단 한 곳(logger.py)에서 민감한 키 값을 모두 ***로 가린다.

마스킹(masking)은 비밀 값을 가려서 안 보이게 하는 것이다. 영수증에서 카드번호 가운데를 별표로 지우는 것과 같다. 위험은 이렇다: AI가 도구를 호출할 때 넘긴 인자(tool_args) 안에 비밀번호나 API 키가 섞여 있을 수 있는데, 그게 그대로 DB나 로그에 남으면 유출 사고가 된다.

이 시스템의 안전장치는 단일 지점(single point) 설계다. logger.py 안의 _mask_secrets 함수 하나가, 저장 직전에 인자 객체(dict·list)를 재귀적으로 훑으면서 키 이름이 다음 중 하나에 걸리면 그 값을 ***로 바꾼다:

  • password · token · secret · api_key · authorization (대소문자 무관)

실제 코드에 정규식 (password|token|secret|api_key|authorization)이 있고, 딕셔너리·리스트를 끝까지 파고들어 마스킹한다(tool_args JSONB 저장 전 반드시 이 함수를 통과한다). 왜 한 곳에만 두나? 마스킹 로직이 여러 군데 흩어지면 어느 한 곳을 빠뜨려 구멍이 생기기 쉽다. 한 문(門)으로만 데이터가 나가게 하면 그 문 하나만 지키면 된다.

같은 절단·축약 원칙은 본문·도구결과에도 적용된다 — content는 8KB(8192자), tool_result는 2KB(2048자)로 잘라 전체 sha256 hash와 함께 저장해 과도한 적재를 막는다.

마스킹보안logger 단일지점
심화"개선이 팀 파일 하나에 격리된다"는 게 코드 구조상 정말 그런가?
그렇다 — 변경은 teams/*.py 한 파일에만 들어가고, runner·logger·scorer·DB는 팀과 무관하게 그대로 둔다.

이것이 "팀만 바꿔서 측정한다"가 성립하는 비결이다. 과학 실험에서 한 번에 변수 하나만 바꾸는 것과 똑같다. 다른 모든 조건이 고정돼 있어야, 결과 차이를 "바꾼 그 하나" 탓으로 돌릴 수 있다.

코드를 직접 확인해 보면 구조가 이를 강제한다:

  • 팀 명부(registry.py)는 각 팀의 조립 함수를 그 팀 자신의 파일에서만 가져온다. 예: from agent_eval.teams.analyst_web_dedup import build_team. 새 팀은 새 파일에 build_team()을 쓰고 한 줄 등록하면 끝이다.
  • 실제로 base(analyst_deep.py)와 개선(analyst_web_dedup.py)은 서로 다른 파일이고, 개선 파일은 공통 부품(프롬프트·종료조건 등)을 import로 재사용한다 — 베끼지 않는다(예: analyst_web_dedup.pyPLANNER_PROMPT·CRITIC_PROMPT를 다른 모듈에서 import). 그래서 "바뀐 것은 검색 도구 하나"가 코드상으로도 명백하다.
  • 반면 runner(실행기)·logger(기록기)·scorer(채점기)·DB 스키마는 어떤 팀을 돌리든 건드리지 않는다. 측정 잣대가 실험마다 흔들리면 비교가 무의미해지기 때문이다.

다운로드 가능한 의미 단위 diff(차이 비교)가 이를 시각적으로 증명한다 — 예컨대 v4→v6는 "analyst 도구를 web_search → web_search_deduped로, 프롬프트 일부 변경"만 빨갛게 뜨고 나머지 전부가 동일하다. 이게 "1변수 원칙"의 눈에 보이는 증거다.

1변수 격리registrybuild_team
심화메트릭(토큰·성공·grounding)이 코드를 거쳐 DB에 꽂히는 경로는 각각 어떻게 되나?
토큰·턴·지연은 logger가 events/runs에, 성공 여부는 scorer가 runs에, grounding은 별도 패스(또는 실시간 뷰)가 채운다 — 출처가 셋 다 다르다.

메트릭(measure, 측정값)마다 "누가 어디서 길어 올리는가"가 다른데, 이걸 구분하는 게 중요하다. 같은 숫자라도 출처가 다르면 의미가 다르기 때문이다.

메트릭출처(누가 만드나)저장 위치
토큰 / 턴 / 지연loggerrun_stream()이 흘리는 이벤트의 사용량(models_usage)을 누적events 줄별 + runs 합계
성공 여부(success)scorer — 정답과 비교(리스트는 순서 보존)runs.success UPDATE
근거성(grounding)인용 채점은 별도 패스(research/groundingresearch_grounding), 성공기준 grounding은 뷰 v_analysis_grounded가 events를 실시간 쿼리(저장 아님)별도 테이블 / 뷰(010)

핵심은 grounding이 두 갈래라는 점이다. ① 호출기준(검색을 1번이라도 했나 — EXP 원장 기록)과 ② 성공기준(검색이 실제 결과를 반환했나 — 뷰 010)은 다른 숫자다. v6만 둘 다 100%였고, v3·v4는 호출 100/성공 0, v5는 호출 67/성공 0이었다.

함정: 검색이 빈손으로 와도 그건 도구 에러가 아니다. tool_result(검색 결과 없음)이라는 문자열로 정상 저장된다. 그래서 도구에러율은 0%인데 성공기준 grounding은 0%가 될 수 있다 — 두 메트릭의 출처가 다르기 때문이다.
메트릭 경로scorergrounding 두갈래
심화"스키마가 SSOT", "불변식은 CHECK 제약", "claims·evidence는 비어 있다"는 각각 무슨 뜻인가?
세 문장은 모두 "데이터 정합성을 코드 의지가 아니라 DB 구조로 강제한다"는 한 철학의 다른 얼굴이다.

하나씩 풀어 본다.

  • 스키마가 SSOT(Single Source of Truth, 단일 진실 공급원) — 테이블 구조를 정의한 migrations/*.sql 파일이 "진실은 여기다"의 유일한 기준이다. 그래서 ORM(코드로 테이블을 흉내 내는 도구)을 금지하고, 스키마 변경은 오직 번호가 늘어나는 새 마이그레이션 파일 추가로만 한다(기존 파일 수정 금지). 코드와 DB가 따로 놀며 어긋나는 사고를 원천 차단한다.
  • 불변식(invariant)은 CHECK 제약으로 — "항상 참이어야 하는 규칙"(예: 완료된 run은 success가 비어 있으면 안 된다)을 애플리케이션 코드의 검사에만 맡기지 않고, DB의 CHECK 제약으로 박는다. 비유하면 "조심하자"는 다짐(코드 검사)이 아니라 "애초에 들어갈 수 없게 만든 좁은 문"(DB 제약)이다. 제약을 통과 못 하는 데이터가 나오면 제약을 푸는 게 아니라 데이터 생성부를 고친다.
  • claims / evidence는 비어 있다 — 이 두 테이블은 스키마(틀)만 존재하고 코드로는 채우지 않는다. 더 정교한 주장-증거 분석은 Tier 1~2 범위 밖의 자리표시자(placeholder)라서, "있으면 좋겠다"는 판단이 들어도 구현하지 않는다. 미리 자리만 잡아 두되 손대지 않는 것이다.
왜 이렇게까지 하나: 측정 시스템의 신뢰는 "사람이 실수 안 한다"는 가정 위에 세우면 안 된다. 잘못된 데이터가 물리적으로 들어갈 수 없게 만들어야 한다. 그게 이 프로젝트가 "팀 구성 변경의 효과"를 데이터로 주장할 수 있는 토대다.
SSOTCHECK 제약claims/evidence
심화검색이 빈손일 때 에이전트가 그 사실을 스스로 곱씹어 보정하는 기능(reflect_on_tool_use)을 꺼버린 셈인데, 품질에 손해 아닌가요?
아니다 — 손해처럼 보이지만, 그 '곱씹기'를 한 에이전트의 머릿속이 아니라 여러 에이전트가 모인 팀 전체에서 하도록 옮긴 것이고, 끄지 않으면 빈 검색 결과가 런(run, 실행 1회) 전체를 RuntimeError로 죽여 측정 자체가 불가능해진다.

먼저 용어부터 쉽게 풀자. reflect_on_tool_use(도구 사용 후 자기 되짚기)는 AutoGen이라는 멀티에이전트 프레임워크의 옵션이다. 켜두면(=True), 에이전트가 도구(여기선 web_search, 즉 웹검색)를 한 번 부른 직후 그 결과를 LLM에게 다시 한 번 먹여 '내가 방금 받은 걸 어떻게 해석하지?'를 한 번 더 생성하게 한다. 마치 자료를 찾은 뒤 곧바로 짧은 소감문을 한 줄 쓰는 셈이다.

문제는 이 모델(z.ai GLM-4.7)이 빈손 검색 결과를 받았을 때다. 키 없는 폴백 검색이 차단/속도제한(rate-limit)에 걸려 (검색 결과 없음)이라는 빈 문자열만 돌아오는 일이 잦은데, 이걸 되짚게 하면 모델이 쓸 말이 없어 빈 응답을 내고, AutoGen은 빈 응답을 받아들이지 못해 RuntimeError로 런 전체를 크래시시킨다. 한 번의 빈 검색이 4-에이전트가 30분 가까이 돌린 작업을 통째로 날리는 것이다.

핵심은 토폴로지(topology, 에이전트들의 연결 구조)다. 이 팀은 planner→analyst→synthesizer→critic 4명이 고정 순서로 도는 구조다. 도구를 든 사람은 오직 analyst 한 명. analyst는 검색 결과를 '해석'하지 않고 출처 URL째로 다음 사람에게 넘기기만 한다. 해석·작성은 synthesizer가, '출처가 부족하다/오래됐다'는 검수는 critic이 맡는다. 즉 '검색이 빈손이었다'는 보정은 한 에이전트의 reflection이 아니라 critic의 반려 → 재검색 루프로 팀 차원에서 일어난다.

그래서 실제 품질은 손해가 아니다. 채택된 v6-analyst-web 구성은 reflect를 끈 상태로 grounded(근거 확보) 100/100, 출처 4.7개, 토큰 21.3k(-28%), 지연 177s(-37%)를 동시에 달성했다. 되짚기를 끈 대가는 '빈 응답으로 안 죽는 안정성'이고, 보정 능력은 토폴로지가 대신 지고 있다. 빈손이 곧 정직하게 드러나는 것도 장점인데, 한 주제에서 검색이 빈약하자 개선 팀이 환각 대신 '데이터 부족 — 작성 불가'로 정직하게 거부한 사례가 이를 보여준다.

reflect_on_tool_use토폴로지안정성
중급검색이 빈손이면 도구 에러로 잡혀서 에이전트가 다시 시도하지 않나요? 왜 reflect까지 꺼야 하죠?
안 잡힌다 — 빈 검색은 '에러'가 아니라 '내용 없는 정상 결과'로 저장되기 때문이다. 도구는 성공적으로 실행됐고(tool_error 0%), 다만 알맹이가 비어 있을 뿐이라 프레임워크 입장에선 정상 응답이다.

이 구분이 이 프로젝트의 가장 미묘한 함정 중 하나다. 검색 도구가 차단당해 결과가 없을 때, 도구는 예외를 던지지 않고 (검색 결과 없음)이라는 문자열을 정상 반환한다. 그래서 trace DB(모든 이벤트를 기록하는 PostgreSQL 원장)에는 tool_error가 아니라 tool_result로 남는다. 도구 에러율은 전 구간 0%인데도 근거 확보는 실패하는 역설이 여기서 생긴다.

  • 호출 기준 grounded%: web_search를 1회라도 불렀나? → v3·v4·v6 모두 100%.
  • 성공 기준 grounded%: 그 검색이 실제 내용을 돌려줬나? → v3·v4·v5는 0%, v6-dedup만 100%.

'검색을 했다 ≠ 근거를 얻었다'가 핵심이다. 호출만 세면 빈손 위에 지어진 환각 리포트가 grounded=100%로 둔갑한다. 그래서 reflect를 켜서 빈손을 '되짚게' 만들면, 모델이 할 말이 없어 빈 응답 → 크래시로 이어지는 것이지, 자동 재시도로 이어지지 않는다. 재검색은 reflection이 아니라 critic이 '출처 부족'을 반려해 루프를 한 바퀴 더 돌게 만드는 방식으로 처리한다.

운영 결론은 reflect 토글이 아니라 검색 백엔드 교체였다: 키 기반 실검색(Firecrawl, /v1/search)을 쓰면 빈손 자체가 줄어든다. web_tool.py가 이미 런타임 분기라 키가 있으면 실검색, 없으면 폴백으로 자동 전환된다. reflect=False는 '빈손이 나도 안 죽는다'는 안전판이고, 백엔드 교체는 '빈손을 애초에 줄인다'는 근본 처방이다.
빈검색tool_errorgrounded
기초reflect_on_tool_use를 끄면 검색 결과 해석은 누가 하나요?
도구를 든 analyst가 아니라 synthesizer와 critic이 한다 — 해석·작성을 '도구 사용 직후 같은 에이전트'에서 떼어내 별도 단계로 분업시킨 것이라, 되짚기를 끈 것이 곧 해석을 버린 것은 아니다.

이 팀의 4단계 역할 분담을 일상 비유로 보면 이렇다.

역할하는 일비유
planner무엇을 검색할지 분해취재 계획서 작성
analystweb_search로 자료 수집, 출처 URL째 전달도서관에서 자료만 복사해 옴
synthesizer모은 자료로 구조화된 최종 리포트 작성기자가 기사 집필
critic출처 수·최신성·구조 검수, 부족하면 반려데스크 데스킹·반려

코드상 도구(tools=[web_search])는 오직 analyst에게만 붙어 있고, analyst의 시스템 프롬프트는 '검색 결과를 해석하지 말고 출처와 함께 그대로 넘겨라'다. 그래서 analyst 단계에서 굳이 reflection(자기 해석)을 돌릴 이유가 없다 — 해석은 다음 차례인 synthesizer의 일이기 때문이다.

왜 중요한가: reflection은 한 에이전트가 도구를 부른 직후 같은 머릿속에서 즉시 해석하는 단계인데, 이 토폴로지는 그 '즉시 해석'을 통째로 다음 에이전트로 옮겼다. 빈손이면 critic이 '출처 부족'으로 반려해 재검색 루프가 돌고, 알맹이가 있으면 synthesizer가 출처째 인용해 작성한다. 즉 보정·해석 능력을 잃은 게 아니라, 한 에이전트의 불안정한 즉석 reflection을 여러 에이전트의 안정적인 단계별 분업으로 대체한 설계다. 이 토폴로지 분업이 load-bearing(구조를 떠받치는 핵심)이라는 건 다른 실험에서도 드러났는데, 협업 방식 한 줄(selector→round_robin)을 바꾼 것만으로 pass@1이 0.967→0.067로 붕괴한 사례가 있다.

역할분담synthesizercritic