에이전트 애플리케이션은 LLM이 문제를 해결하기 위한 다음 단계를 스스로 결정할 수 있도록 합니다. 이러한 유연성은 강력하지만, 모델의 블랙박스 특성으로 인해 에이전트의 한 부분을 조정했을 때 나머지 부분에 어떤 영향을 미칠지 예측하기 어렵습니다. 프로덕션 수준의 에이전트를 구축하려면 철저한 테스트가 필수적입니다.에이전트를 테스트하는 몇 가지 접근 방식이 있습니다:
단위 테스트는 인메모리 페이크를 사용하여 에이전트의 작고 결정론적인 부분을 독립적으로 검증하므로 정확한 동작을 빠르고 결정론적으로 확인할 수 있습니다.
통합 테스트는 실제 네트워크 호출을 사용하여 에이전트를 테스트함으로써 컴포넌트들이 함께 작동하는지, 자격 증명과 스키마가 일치하는지, 지연 시간이 허용 가능한지 확인합니다.
에이전트 애플리케이션은 여러 컴포넌트를 연결하고 LLM의 비결정론적 특성으로 인한 불안정성을 처리해야 하므로 통합 테스트에 더 많이 의존하는 경향이 있습니다.
API 호출이 필요하지 않은 로직의 경우, 인메모리 스텁을 사용하여 응답을 모킹할 수 있습니다.LangChain은 텍스트 응답을 모킹하기 위한 GenericFakeChatModel을 제공합니다. 이 모델은 응답 이터레이터(AIMessages 또는 문자열)를 받아 호출마다 하나씩 반환합니다. 일반 사용과 스트리밍 사용을 모두 지원합니다.
테스트 중 영속성을 활성화하려면 InMemorySaver 체크포인터를 사용할 수 있습니다. 이를 통해 상태 의존적 동작을 테스트하기 위해 여러 턴을 시뮬레이션할 수 있습니다:
Copy
from langgraph.checkpoint.memory import InMemorySaveragent = create_agent( model, tools=[], checkpointer=InMemorySaver())# 첫 번째 호출agent.invoke(HumanMessage(content="I live in Sydney, Australia."))# 두 번째 호출: 첫 번째 메시지가 영속화되어 있으므로(시드니 위치), 모델이 GMT+10 시간을 반환합니다agent.invoke(HumanMessage(content="What's my local time?"))
많은 에이전트 동작은 실제 LLM을 사용할 때만 나타납니다. 예를 들어 에이전트가 어떤 도구를 호출하기로 결정하는지, 응답을 어떻게 포맷하는지, 또는 프롬프트 수정이 전체 실행 궤적에 영향을 미치는지 등입니다. LangChain의 agentevals 패키지는 실제 모델로 에이전트 궤적을 테스트하기 위해 특별히 설계된 평가자를 제공합니다.AgentEvals를 사용하면 궤적 매칭 또는 LLM 심사를 수행하여 에이전트의 궤적(도구 호출을 포함한 정확한 메시지 시퀀스)을 쉽게 평가할 수 있습니다:
AgentEvals는 에이전트의 궤적을 참조 궤적과 비교하기 위한 create_trajectory_match_evaluator 함수를 제공합니다. 선택할 수 있는 네 가지 모드가 있습니다:
모드
설명
사용 사례
strict
메시지와 도구 호출이 동일한 순서로 정확히 일치
특정 시퀀스 테스트 (예: 권한 부여 전 정책 조회)
unordered
순서에 관계없이 동일한 도구 호출 허용
순서가 중요하지 않은 정보 검색 검증
subset
에이전트가 참조에 있는 도구만 호출 (추가 호출 없음)
에이전트가 예상 범위를 초과하지 않도록 보장
superset
에이전트가 최소한 참조 도구를 호출 (추가 호출 허용)
최소 필수 작업이 수행되는지 검증
엄격한 매칭
strict 모드는 궤적이 동일한 순서로 동일한 메시지를 포함하고 동일한 도구 호출을 하는지 확인합니다. 메시지 내용의 차이는 허용됩니다. 이는 작업을 권한 부여하기 전에 정책 조회를 요구하는 것과 같이 특정 작업 시퀀스를 강제해야 할 때 유용합니다.
Copy
from langchain.agents import create_agentfrom langchain.tools import toolfrom langchain.messages import HumanMessage, AIMessage, ToolMessagefrom agentevals.trajectory.match import create_trajectory_match_evaluator@tooldef get_weather(city: str): """Get weather information for a city.""" return f"It's 75 degrees and sunny in {city}."agent = create_agent("openai:gpt-4o", tools=[get_weather])evaluator = create_trajectory_match_evaluator( trajectory_match_mode="strict", ) def test_weather_tool_called_strict(): result = agent.invoke({ "messages": [HumanMessage(content="What's the weather in San Francisco?")] }) reference_trajectory = [ HumanMessage(content="What's the weather in San Francisco?"), AIMessage(content="", tool_calls=[ {"id": "call_1", "name": "get_weather", "args": {"city": "San Francisco"}} ]), ToolMessage(content="It's 75 degrees and sunny in San Francisco.", tool_call_id="call_1"), AIMessage(content="The weather in San Francisco is 75 degrees and sunny."), ] evaluation = evaluator( outputs=result["messages"], reference_outputs=reference_trajectory ) # { # 'key': 'trajectory_strict_match', # 'score': True, # 'comment': None, # } assert evaluation["score"] is True
순서 무관 매칭
unordered 모드는 순서에 관계없이 동일한 도구 호출을 허용합니다. 이는 특정 정보를 검색했는지 확인하고 싶지만 시퀀스는 중요하지 않을 때 유용합니다. 예를 들어, 에이전트가 도시의 날씨와 이벤트를 모두 확인해야 할 수 있지만 순서는 중요하지 않습니다.
Copy
from langchain.agents import create_agentfrom langchain.tools import toolfrom langchain.messages import HumanMessage, AIMessage, ToolMessagefrom agentevals.trajectory.match import create_trajectory_match_evaluator@tooldef get_weather(city: str): """Get weather information for a city.""" return f"It's 75 degrees and sunny in {city}."@tooldef get_events(city: str): """Get events happening in a city.""" return f"Concert at the park in {city} tonight."agent = create_agent("openai:gpt-4o", tools=[get_weather, get_events])evaluator = create_trajectory_match_evaluator( trajectory_match_mode="unordered", ) def test_multiple_tools_any_order(): result = agent.invoke({ "messages": [HumanMessage(content="What's happening in SF today?")] }) # 참조는 실제 실행과 다른 순서로 도구가 호출되었음을 보여줍니다 reference_trajectory = [ HumanMessage(content="What's happening in SF today?"), AIMessage(content="", tool_calls=[ {"id": "call_1", "name": "get_events", "args": {"city": "SF"}}, {"id": "call_2", "name": "get_weather", "args": {"city": "SF"}}, ]), ToolMessage(content="Concert at the park in SF tonight.", tool_call_id="call_1"), ToolMessage(content="It's 75 degrees and sunny in SF.", tool_call_id="call_2"), AIMessage(content="Today in SF: 75 degrees and sunny with a concert at the park tonight."), ] evaluation = evaluator( outputs=result["messages"], reference_outputs=reference_trajectory, ) # { # 'key': 'trajectory_unordered_match', # 'score': True, # } assert evaluation["score"] is True
부분집합 및 상위집합 매칭
superset과 subset 모드는 부분 궤적을 매칭합니다. superset 모드는 에이전트가 참조 궤적의 도구를 최소한 호출했는지 확인하며, 추가 도구 호출을 허용합니다. subset 모드는 에이전트가 참조에 있는 도구 외에 다른 도구를 호출하지 않았는지 확인합니다.
Copy
from langchain.agents import create_agentfrom langchain.tools import toolfrom langchain.messages import HumanMessage, AIMessage, ToolMessagefrom agentevals.trajectory.match import create_trajectory_match_evaluator@tooldef get_weather(city: str): """Get weather information for a city.""" return f"It's 75 degrees and sunny in {city}."@tooldef get_detailed_forecast(city: str): """Get detailed weather forecast for a city.""" return f"Detailed forecast for {city}: sunny all week."agent = create_agent("openai:gpt-4o", tools=[get_weather, get_detailed_forecast])evaluator = create_trajectory_match_evaluator( trajectory_match_mode="superset", ) def test_agent_calls_required_tools_plus_extra(): result = agent.invoke({ "messages": [HumanMessage(content="What's the weather in Boston?")] }) # 참조는 get_weather만 요구하지만, 에이전트는 추가 도구를 호출할 수 있습니다 reference_trajectory = [ HumanMessage(content="What's the weather in Boston?"), AIMessage(content="", tool_calls=[ {"id": "call_1", "name": "get_weather", "args": {"city": "Boston"}}, ]), ToolMessage(content="It's 75 degrees and sunny in Boston.", tool_call_id="call_1"), AIMessage(content="The weather in Boston is 75 degrees and sunny."), ] evaluation = evaluator( outputs=result["messages"], reference_outputs=reference_trajectory, ) # { # 'key': 'trajectory_superset_match', # 'score': True, # 'comment': None, # } assert evaluation["score"] is True
tool_args_match_mode 속성 및/또는 tool_args_match_overrides를 설정하여 평가자가 실제 궤적과 참조 간의 도구 호출 동등성을 고려하는 방식을 커스터마이즈할 수 있습니다. 기본적으로 동일한 도구에 대한 동일한 인수를 가진 도구 호출만 동등하다고 간주됩니다. 자세한 내용은 저장소를 참조하세요.
실제 LLM API를 호출하는 통합 테스트는 특히 CI/CD 파이프라인에서 자주 실행될 때 느리고 비용이 많이 들 수 있습니다. HTTP 요청과 응답을 기록한 다음 실제 네트워크 호출 없이 후속 실행에서 재생하는 라이브러리를 사용하는 것을 권장합니다.이를 위해 vcrpy를 사용할 수 있습니다. pytest를 사용하는 경우, pytest-recording 플러그인은 최소한의 구성으로 이를 활성화하는 간단한 방법을 제공합니다. 요청/응답은 카세트에 기록되며, 후속 실행에서 실제 네트워크 호출을 모킹하는 데 사용됩니다.카세트에서 민감한 정보를 필터링하도록 conftest.py 파일을 설정합니다:
이 테스트를 처음 실행하면 에이전트가 실제 네트워크 호출을 하고 pytest가 tests/cassettes 디렉터리에 test_agent_trajectory.yaml 카세트 파일을 생성합니다. 후속 실행에서는 에이전트의 요청이 이전 실행과 동일하다면 해당 카세트를 사용하여 실제 네트워크 호출을 모킹합니다. 변경된 경우 테스트가 실패하고 카세트를 삭제하고 테스트를 다시 실행하여 새로운 상호작용을 기록해야 합니다.
프롬프트를 수정하거나, 새 도구를 추가하거나, 예상 궤적을 변경하면 저장된 카세트가 오래되어 기존 테스트가 실패합니다. 해당 카세트 파일을 삭제하고 테스트를 다시 실행하여 새로운 상호작용을 기록해야 합니다.