Agent가 잘한다는 것을 어떻게 숫자로 보일 수 있을까요?

Agent를 몇 번 써 보고 잘 되는 것 같다고 느끼는 것과 잘한다고 말할 수 있는 것은 다릅니다. 같은 질문을 열 번, 질문을 바꿔 백 번 돌려 몇 번 맞았는지 세어야 하고 틀린 때는 어디서 틀렸는지 들여다봐야 합니다. 그러려면 먼저 실행 한 번을 기록으로 남겨야 합니다.

이 기록을 트레이스(trace)라고 부르고 그 안의 모델 호출 하나, 도구 호출 하나를 스팬(span)이라고 부릅니다. 아래에서 질문을 골라 Agent가 어떤 스팬을 거쳤는지 보세요.

질문

모의 모델 서버로 실제 실행해 얻은 호출 횟수와 토큰입니다. 막대 길이는 구간을 구분하려고 그린 것이고 걸린 시간이 아닙니다.

실행 기록은 콜백으로 모읍니다

LangSmith 같은 서비스는 환경 변수만 켜면 LangChain 코드를 고치지 않고도 트레이스를 모아 줍니다. 공식 문서의 설정은 LANGSMITH_TRACING=true와 LANGSMITH_API_KEY이고 프로젝트 이름은 LANGSMITH_PROJECT로 정합니다. 이 글에서는 계정 없이 구조를 보려고 같은 정보를 직접 모았습니다.

LangChain은 모델과 도구가 시작하고 끝날 때마다 콜백(callback)이라는 함수를 불러 줍니다. 그 안에서 시작 시각과 끝 시각, 토큰 수를 적어 두면 트레이스가 됩니다. 업계 공통 규약을 만드는 OpenTelemetry 프로젝트에도 GenAI 전용 규약 저장소가 따로 있는데 안정 상태인지는 확인하지 못했습니다.

trace.pypython
class Trace(BaseCallbackHandler):                   # 1 스팬을 모으는 기록기
    def __init__(self): self.spans, self.t0 = {}, time.time()
    def on_chat_model_start(self, s, m, *, run_id, **kw):     # 2 모델 호출 시작
        self.spans[run_id] = {"kind": "llm", "start": time.time() - self.t0}
    def on_llm_end(self, r, *, run_id, **kw):
        u = r.generations[0][0].message.usage_metadata        # 3 토큰 수 적기
        self.spans[run_id].update(end=time.time() - self.t0, tokens=(u["input_tokens"], u["output_tokens"]))
    # on_tool_start / on_tool_end 도 같은 식으로 도구 스팬을 적고
    # tool_names()가 부른 도구 이름을 순서대로 돌려준다
  1. 기록기를 만듭니다. BaseCallbackHandler를 이어받아 필요한 시점의 메서드만 고칩니다. 스팬을 run_id로 구분해 사전에 모읍니다.
  2. 시작할 때 적습니다. 모델이나 도구가 시작할 때 종류와 시작 시각을 적습니다. 도구 스팬은 on_tool_start에서 도구 이름도 함께 적습니다.
  3. 끝날 때 마무리합니다. 끝 시각과 응답에 붙은 토큰 수(usage_metadata)를 적습니다. 호출할 때 config={"callbacks": [Trace()]}로 넘기면 실행이 기록됩니다.

정답만 보면 왜 틀렸는지 알 수 없습니다

Agent를 평가하려면 두 가지 눈이 필요합니다. 최종 답이 맞았는지 보는 눈과 답에 이르기까지 거친 도구 호출의 순서, 곧 궤적(trajectory)을 보는 눈입니다. LangSmith 평가 문서도 Agent는 올바른 도구를 골랐는지와 거친 궤적으로 평가할 수 있다고 설명합니다.

세 번째 질문은 정답이 틀렸다는 점만 보면 원인을 알 수 없습니다. 경로를 보면 한 번 조회로 끝날 일을 목록 조회로 세 번 돌았다는 것이 드러납니다. 보기 범위를 바꿔 보세요.

데이터셋을 정하고 규칙과 심판으로 채점합니다

평가는 데이터셋(dataset)에서 시작합니다. 질문과 함께 기대하는 정답과 기대하는 도구 경로를 적어 둔 예시 모음입니다. 기대 정답은 Agent에게 보여 주지 않고 채점할 때만 씁니다. Agent를 고칠 때마다 같은 데이터셋으로 다시 돌려 점수가 오르내리는 것을 봅니다.

채점 방법은 두 가지입니다. 코드로 정해진 규칙을 검사하는 방법은 빠르고 확실하지만 정해진 표현만 인정합니다. 심판 모델(LLM-as-judge)이 답을 읽고 합격인지 판단하는 방법은 표현이 달라도 뜻을 보지만 심판도 틀릴 수 있어 일부는 사람이 확인해야 합니다.

eval.pypython
DATA = [{"q": "T-1042 상태 알려 줘", "expect": "부품 대기", "path": ["get_ticket"]},
        ...]                                                     # 1 질문 + 기대 답 + 기대 경로
for ex in DATA:
    tr = Trace()
    r = agent.invoke({"messages": [{"role": "user", "content": ex["q"]}]},
                     config={"callbacks": [tr]})                  # 2 기록하며 실행
    ans = r["messages"][-1].content
    final_ok = ex["expect"] in ans                                # 3 규칙: 최종 답
    path_ok = tr.tool_names() == ex["path"]                       # 4 규칙: 경로
    v = judge.invoke([("system", "답변을 채점한다."), ("user", f"질문: {ex['q']}\n답변: {ans}")])  # 5 심판
터미널
$ python eval.py T-1042 상태 | 정답 ✓ | 경로 ✓ | 심판 pass | 호출 2 | 토큰 280/60 kim 담당 티켓 | 정답 ✓ | 경로 ✓ | 심판 pass | 호출 2 | 토큰 280/60 T-1043 담당자 | 정답 ✗ | 경로 ✗ | 심판 fail | 호출 4 | 토큰 720/120 정답률 2/3 · 경로 일치 2/3 · 심판 통과 2/3

출력은 예시입니다. 모의 모델 서버로 직접 실행한 결과이고 심판도 모의 서버라 실제 모델의 채점과 다릅니다. 도구는 가짜 티켓 데이터를 돌려줍니다.

  1. 데이터셋입니다. 질문과 기대하는 정답 조각, 기대하는 도구 순서를 함께 적습니다. 정답은 통째가 아니라 꼭 들어가야 할 핵심 말로 적어 두면 규칙으로 검사하기 편합니다.
  2. 기록하며 실행합니다. 앞에서 만든 기록기를 콜백으로 넘기면 실행마다 스팬이 모입니다.
  3. 규칙으로 채점합니다. 최종 답에 핵심 말이 들었는지, 부른 도구가 기대한 순서와 같은지 코드로 검사합니다. 경로가 정답이 하나뿐인 일에만 이 검사가 어울립니다.
  4. 심판이 채점합니다. 구조화 출력으로 합격과 불합격 중 하나만 받습니다. 심판 프롬프트의 기준이 모호하면 점수도 흔들립니다.

진짜 도구 대신 모델에게 흉내 내게 합니다

평가를 자주 돌리려면 도구를 진짜로 부르기가 부담스럽습니다. 메일을 보내거나 데이터를 바꾸는 도구라면 시험하다가 실제 일이 벌어집니다. LangChain의 LLMToolEmulator 미들웨어는 도구를 실행하는 대신 모델이 그럴듯한 응답을 만들어 돌려줍니다.

직접 실행해 진짜 도구가 한 번도 불리지 않는 것을 확인했습니다. 다만 흉내 낸 응답은 진짜가 아닙니다. 그래서 도구를 고르고 부르는 흐름을 시험하는 데 쓰고 답의 정확성은 진짜 도구로 따로 확인합니다.

호출이 늘면 토큰도 늘어 비용이 커집니다

평가표에는 점수 말고 비용도 함께 적어 둡니다. 모델을 부를 때마다 지금까지의 대화를 다시 보내므로 호출이 하나 늘 때마다 입력 토큰이 쌓입니다. 같은 도구를 되풀이한 세 번째 질문은 호출이 네 번이라 입력 토큰이 앞의 두 질문의 2.5배가 넘었습니다.

점수가 같다면 호출이 적고 토큰이 적은 쪽이 낫습니다. 보고 싶은 값을 골라 보세요.

다음 글에서는 문서 한 장이 Agent를 조종하는 프롬프트 인젝션을 포함해 Agent의 보안을 알아보겠습니다.

참고 자료

출처 4개 펼치기