
Deep Agents 평가하기: 우리의 학습 내용
요약
LangChain은 최근 DeepAgents CLI, LangSmith Assist 등 다양한 에이전트 애플리케이션을 출시하며 딥 에이전트 평가에 대한 중요한 통찰을 얻었습니다. 딥 에이전트는 단순한 최종 응답 평가를 넘어, 각 데이터 포인트마다 고유한 성공 기준과 맞춤형 테스트 로직을 적용해야 합니다.
핵심 포인트
- 딥 에이전트는 데이터 포인트별로 맞춤화된 테스트 로직과 구체적인 성공 기준이 필요합니다.
- 에이전트 실행 방식에 따라 단일 단계(의사 결정 검증), 전체 턴(최종 상태 검증), 여러 번의 턴(사용자 상호작용 시뮬레이션)으로 구분하여 테스트할 수 있습니다.
- 평가 대상에는 에이전트의 도구 호출 궤적(Trajectory), 최종 응답(Final response), 그리고 실행 중 생성된 기타 상태(Other state)가 포함됩니다.
- 재현 가능한 테스트를 위해 깨끗하고 통제된 환경 설정(Environment setup)이 필수적입니다.
지난 한 달 동안 LangChain에서는 Deep Agents 하네스(harness)를 기반으로 네 가지 애플리케이션을 출시했습니다:
- DeepAgents CLI: 코딩 에이전트 (coding agent)
- LangSmith Assist: LangSmith 내의 다양한 작업을 돕는 인앱 에이전트 (in-app agent)
- Personal Email Assistant: 각 사용자와의 상호작용을 통해 학습하는 이메일 어시스턴트
- Agent Builder: 메타 딥 에이전트 (meta deep agents) 기반의 노코드 에이전트 구축 플랫폼
이러한 에이전트들을 구축하고 출시한다는 것은 각 에이전트에 대한 평가(evals)를 추가해야 함을 의미하며, 우리는 그 과정에서 많은 것을 배웠습니다! 이 포스트에서는 딥 에이전트(deep agents)를 평가하기 위한 다음과 같은 패턴들을 심도 있게 다룰 것입니다.
딥 에이전트는 각 데이터 포인트에 대해 맞춤형 테스트 로직이 필요합니다 — 각 테스트 케이스는 고유한 성공 기준을 가집니다. 단일 단계(single-step)로 딥 에이전트를 실행하는 것은 특정 시나리오에서의 의사 결정(decision-making)을 검증하는 데 매우 유용하며 (토큰도 절약할 수 있습니다!) **전체 에이전트 턴(Full agent turns)**은 에이전트의 "최종 상태(end state)"에 대한 단언(assertions)을 테스트하는 데 적합합니다. 여러 번의 에이전트 턴(Multiple agent turns)은 현실적인 사용자 상호작용을 시뮬레이션하지만 궤도(on rails)를 벗어나지 않도록 유지해야 합니다. 환경 설정(Environment setup)이 중요합니다 — Deep Agents는 깨끗하고 재현 가능한 테스트 환경이 필요합니다.
용어 정리 (Glossary)
본론으로 들어가기에 앞서, 이 포스트 전반에서 사용하는 몇 가지 용어를 정의하겠습니다.
에이전트 실행 방식:
단일 단계 (Single step): 핵심 에이전트 루프(agent loop)를 단 한 번의 턴만 실행하도록 제한하여, 에이전트가 취할 다음 행동을 결정합니다.
전체 턴 (Full turn): 단일 입력에 대해 에이전트를 전체적으로 실행하며, 이는 여러 번의 도구 호출(tool-calling) 반복으로 구성될 수 있습니다.
여러 번의 턴 (Multiple turns): 에이전트를 전체적으로 여러 번 실행합니다. 주로 에이전트와 사용자 간의 여러 차례 주고받는 상호작용이 포함된 "멀티 턴(multi-turn)" 대화를 시뮬레이션하는 데 사용됩니다.
테스트 가능한 항목:
궤적 (Trajectory): 에이전트에 의해 호출되는 도구(tools)의 순서와 에이전트가 생성하는 특정 도구 인자(tool arguments).
최종 응답 (Final response): 에이전트가 사용자에게 반환하는 최종 응답.
기타 상태 (Other state): 에이전트가 실행되는 동안 생성한 기타 값들 (예: 파일, 기타 산출물(artifacts))
#1: Deep Agents는 각 데이터 포인트마다 더 맞춤화된 테스트 로직(코드)을 필요로 합니다
전통적인 LLM 평가 방식은 간단합니다:
-
예시 데이터셋 구축
-
평가기 (evaluator) 작성
-
데이터셋에 대해 애플리케이션을 실행하여 출력을 생성하고, 평가기로 해당 출력의 점수를 매김
모든 데이터 포인트는 동일하게 취급됩니다. 즉, 동일한 애플리케이션 로직을 거치고 동일한 평가기에 의해 점수가 매겨집니다.
Deep Agents는 이러한 가정을 깨뜨립니다. 단순히 최종 메시지만 테스트하는 것 이상의 작업이 필요할 것입니다. "성공 기준 (success criteria)" 또한 각 데이터 포인트마다 더 구체적일 수 있으며, 에이전트의 궤적 (trajectory) 및 상태 (state)에 대한 특정 단언 (assertions)을 포함할 수 있습니다.
다음 예시를 고려해 보십시오:
사용자의 선호도를 기억할 수 있는 기능이 있는 일정 예약용 Deep Agent가 있습니다. 사용자가 에이전트에게 "오전 9시 이전에는 절대 회의를 잡지 않도록 기억해 줘"라고 요청합니다. 우리는 일정 예약 에이전트가 이 정보를 기억하기 위해 자신의 파일 시스템 내 메모리를 업데이트하는지 확인(assert)하고자 합니다.
이를 테스트하기 위해, 우리는 다음과 같은 사항을 검증하는 단언 (assertions)을 작성하고 싶을 것입니다:
-
에이전트가 memories.md 파일 경로에 대해
edit_file을 호출했는가 -
에이전트가 최종 메시지에서 사용자에게 메모리 업데이트 내용을 전달했는가
-
memories.md 파일에 실제로 이른 아침 회의를 잡지 말라는 정보가 포함되어 있는가. 다음과 같은 방법을 사용할 수 있습니다:
- 정규 표현식 (regex)을 사용하여 "9am"에 대한 언급이 있는지 확인
- 또는 파일 업데이트에 대한 더 총체적인 분석을 위해 특정 성공 기준을 가진 LLM-as-judge를 사용
LangSmith의 Pytest 및 Vitest 통합은 이러한 유형의 맞춤형 테스트를 지원합니다. 각 테스트 케이스에 대해 에이전트의 궤적 (trajectory), 최종 메시지, 그리고 상태 (state)에 대해 서로 다른 단언 (assertions)을 수행할 수 있습니다.
# LangSmith 테스트 케이스로 표시
@pytest.mark.langsmith
def test_remember_no_early_meetings() -> None:
user_input = "I don't want any meetings scheduled before 9 AM ET"
# 에이전트에 대한 입력을 LangSmith에 기록할 수 있습니다
t.log_inputs({"question": user_input})
response = run_agent(user_input)
# 에이전트의 출력을 LangSmith에 기록할 수 있습니다
t.log_outputs({"outputs": response})
agent_tool_calls = get_agent_tool_calls(response)
에이전트가 자신의 메모리를 업데이트하기 위해 edit_file 도구를 호출했는지 확인합니다
assert any([tc["name"] == "edit_file" and tc["args"]["path"] == "memories.md" for tc in agent_tool_calls])
최종 메시지가 메모리 업데이트를 확인했는지에 대해 llm-as-judge (LLM 판사)로부터 피드백을 기록합니다
communicated_to_user = ll_as_judge_A(response)
t.log_feedback(key="communicated_to_user", score=communicated_to_user)
메모리 파일에 이제 올바른 정보가 포함되어 있는지에 대해 llm-as-judge로부터 피드백을 기록합니다
memory_updated = llm_as_judge_B(response)
t.log_feedback(key="memory_updated", score=memory_updated)
Pytest 사용 방법에 대한 일반적인 코드 스니펫은 다음 문서를 확인하세요:
이 LangSmith 통합은 모든 테스트 케이스를 실험(experiment)에 자동으로 기록하므로, 실패한 테스트 케이스의 트레이스(trace)를 확인하여(무엇이 잘못되었는지 디버깅하기 위해) 시간이 지남에 따른 결과를 추적할 수 있습니다.
#2: 단일 단계 평가(Single step evals)는 가치 있고 효율적입니다
Deep Agents를 위한 평가(evals)를 실행할 때, 테스트 케이스의 약 절반은 단일 단계 평가(single step evals) 형태였습니다. 즉, 특정 일련의 입력 메시지 직후에 LLM이 무엇을 하기로 결정했는지를 확인하는 방식입니다.
이는 특히 특정 시나리오에서 에이전트가 올바른 인자(arguments)와 함께 올바른 도구(tool)를 호출했는지 검증하는 데 유용합니다. 일반적인 테스트 케이스는 다음과 같습니다:
- 회의 시간을 검색하기 위해 올바른 도구를 호출했는가?
- 올바른 디렉토리 내용을 조사했는가?
- 메모리를 업데이트했는가?
회귀(Regressions)는 전체 실행 시퀀스 전체보다는 개별 결정 지점에서 자주 발생합니다. LangGraph를 사용하는 경우, 스트리밍(streaming) 기능을 통해 단일 도구 호출 후에 에이전트를 중단시켜 출력을 검사할 수 있습니다. 따라서 전체 에이전트 시퀀스의 오버헤드 없이 문제를 조기에 발견할 수 있습니다.
아래 코드 스니펫에서는 도구 노드(tools node) 이전에 수동으로 중단점(break point)을 설정하여, 에이전트를 단일 단계(single step)만 실행하도록 쉽게 구성할 수 있습니다. 이를 통해 해당 단일 단계 이후의 상태(state)를 검사하고 어설션(assertion)을 수행할 수 있습니다.
@pytest.mark.langsmith
def test_single_step() -> None:
state_before_tool_execution = await agent.ainvoke(
inputs,
# interrupt_before는 중단할 노드를 지정합니다.
# 도구 노드 이전에 중단하면 도구 호출 인자(tool call args)를 검사할 수 있습니다.
interrupt_before=["tools"]
)
# 최신 도구 호출을 포함한 에이전트의 메시지 기록을 확인할 수 있습니다.
print(state_before_tool_execution["messages"])
#3: 전체 에이전트 턴(Full agent turns)은 전체적인 그림을 보여줍니다
단일 단계 평가(single-step evals)는 에이전트가 특정 시나리오에서 예상된 행동을 취하는지 확인하는 "단위 테스트(unit tests)"라고 생각하면 됩니다. 한편, 전체 에이전트 턴(full agent turns) 또한 가치가 있습니다. 이는 에이전트가 취하는 엔드 투 엔드(end-to-end) 행동의 전체적인 그림을 보여줍니다.
전체 에이전트 턴을 사용하면 다음과 같은 여러 방식으로 에이전트의 동작을 테스트할 수 있습니다:
1) 궤적 (Trajectory): 전체 궤적을 평가하는 매우 일반적인 방법은 행동 과정 중 특정 시점에 특정 도구가 호출되었는지 확인하는 것이지만, 정확히 언제 호출되었는지는 중요하지 않습니다. 우리의 캘린더 스케줄러 예시에서, 스케줄러는 모든 당사자에게 적합한 시간대를 찾기 위해 여러 번의 도구 호출이 필요할 수도 있습니다.
2) 최종 응답 (Final Response): 어떤 경우에는 에이전트가 취한 특정 경로보다 최종 출력의 품질이 더 중요합니다. 우리는 코딩이나 리서치와 같이 개방형(open-ended) 작업에서 이것이 사실임을 확인했습니다.
3) 기타 상태 (Other State): 다른 상태를 평가하는 것은 에이전트의 최종 응답을 평가하는 것과 매우 유사합니다. 어떤 에이전트들은 채팅 형식으로 사용자에게 응답하는 대신 아티팩트(artifacts)를 생성하기도 합니다. LangGraph에서 에이전트의 상태를 조사함으로써 이러한 아티팩트를 검사하고 테스트하는 것은 쉽습니다.
- 코딩 에이전트의 경우 → 에이전트가 작성한 파일을 읽고 테스트합니다.
- 리서치 에이전트의 경우 → 에이전트가 올바른 링크나 출처를 찾았는지 어설션(assert)합니다.
전체 에이전트 턴(Full agent turns)은 에이전트 실행에 대한 완전한 그림을 제공합니다. LangSmith를 사용하면 전체 에이전트 턴을 트레이스(traces)로 매우 쉽게 확인할 수 있으며, 여기서 지연 시간(latency) 및 토큰 사용량(token use)과 같은 상위 수준의 메트릭(metrics)을 확인하는 동시에, 각 모델 호출(model call)이나 도구 호출(tool invocation)에 이르기까지 구체적인 단계를 분석할 수 있습니다.
#4: 여러 턴에 걸친 에이전트 실행은 전체 사용자 상호작용을 시뮬레이션합니다
어떤 시나리오에서는 여러 개의 순차적인 사용자 입력이 포함된 멀티 턴(multi-turn) 대화 전반에 걸쳐 에이전트를 테스트해야 합니다. 문제는 입력 시퀀스를 단순히 하드코딩(hardcode)했을 때, 에이전트가 예상 경로에서 벗어나면 이후의 하드코딩된 사용자 입력이 의미가 없어질 수 있다는 점입니다.
우리는 Pytest 및 Vitest 테스트에 조건부 로직(conditional logic)을 추가하여 이 문제를 해결했습니다. 예를 들어, 다음과 같이 수행합니다:
- 첫 번째 턴을 실행한 다음, 에이전트의 출력을 확인합니다.
- 출력이 예상대로라면, 다음 턴을 실행합니다.
- 출력이 예상과 다르다면, 테스트를 조기에 실패 처리합니다. (이는 각 단계 이후에 검사(checks)를 추가할 수 있는 유연성이 있었기에 가능했습니다.)
이러한 접근 방식 덕분에 가능한 모든 에이전트 분기(branch)를 모델링할 필요 없이 멀티 턴 평가(evals)를 수행할 수 있었습니다. 만약 두 번째 또는 세 번째 턴을 독립적으로 테스트하고 싶다면, 적절한 초기 상태(initial state)와 함께 해당 시점부터 시작하는 테스트를 설정하기만 하면 됩니다.
#5: 올바른 평가 환경(eval environment)을 설정하는 것이 중요합니다
Deep Agents는 상태를 유지하며(stateful), 복잡하고 오래 지속되는 작업을 처리하도록 설계되었습니다. 따라서 이를 평가하기 위해서는 종종 더 복잡한 환경이 필요합니다.
환경이 보통 상태가 없는(stateless) 몇 가지 도구로 제한되는 단순한 LLM 평가와 달리, Deep Agents는 재현 가능한 결과를 보장하기 위해 각 평가 실행마다 새롭고 깨끗한 환경이 필요합니다.
코딩 에이전트(Coding agents)가 이를 명확하게 보여줍니다. Harbor는 전용 Docker 컨테이너 또는 샌드박스(sandbox) 내부에서 실행되는 TerminalBench를 위한 평가 환경을 제공합니다. DeepAgents CLI의 경우, 우리는 더 가벼운 방식을 사용합니다. 즉, 임시 디렉터리를 생성하고 각 테스트 케이스에 대해 그 안에서 에이전트를 실행합니다.
더 넓은 관점에서의 핵심은 다음과 같습니다: Deep Agent 평가 (evals)에는 테스트마다 초기화되는 환경이 필요합니다. 그렇지 않으면 평가 결과가 불안정해지고(flaky) 재현하기 어려워집니다.
Tip: API 요청을 모킹(Mock out)하세요
LangSmith Assist를 사용하려면 실제 LangSmith API에 연결해야 합니다. 라이브 서비스에 대해 평가를 실행하면 속도가 느려지고 비용이 많이 들 수 있습니다. 대신, HTTP 요청을 파일 시스템에 기록하고 테스트 실행 중에 이를 재생(replay)하십시오. Python의 경우 vcr이 잘 작동하며, JS의 경우 Hono 앱을 통해 fetch 요청을 프록시(proxy)하는 방식이 효과적입니다.
API 요청을 모킹하거나 재생하면, 특히 에이전트가 외부 시스템 상태에 크게 의존하는 경우 Deep Agent 평가를 더 빠르고 디버깅하기 쉽게 만들 수 있습니다.
LangSmith를 사용하여 Deep Agents 평가하기
위의 기술들은 우리가 Deep Agent 기반 애플리케이션을 위한 자체 테스트 스위트(test suites)를 작성할 때 발견한 일반적인 패턴들입니다. 귀하의 특정 애플리케이션에는 위 패턴 중 일부만 필요할 수도 있으므로, 평가 프레임워크가 유연하게 설계되는 것이 중요합니다. 만약 Deep Agent를 구축하고 있으며 평가를 시작하려는 단계라면, LangSmith의 테스트 통합(testing integrations) 기능을 확인해 보세요!
AI 자동 생성 콘텐츠
본 콘텐츠는 LangChain Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기