대화 도중 요구사항이 변경될 때 AI 에이전트(AI Agent) 테스트하기
요약
대화 도중 사용자의 요구사항이 변경될 때 AI 에이전트가 이전 계획을 무효화하고 새로운 목표를 정확히 수행하는지 테스트하는 방법론을 다룹니다. 단순한 텍스트 인지를 넘어 계획, 캐시, 함수 인자 등 내부 상태를 올바르게 업데이트하는지 검증하는 실험적 접근법을 제시합니다.
핵심 포인트
- 요구사항 변경 시 이전 계획과 파라미터를 무효화하는 것이 핵심
- 단순 답변 인지를 넘어 구조화된 상태(state)의 일관성 검증 필요
- 채팅 기록, 목표 요약, 구조화된 상태를 비교하는 테스트 방식 제안
- 비동기 실행기 등 시스템 구성 요소 간의 컨텍스트 불일치 문제 해결
Testing an AI Agent When Requirements Change Mid-Conversation — Agent Lab Journal
Agent Lab Journal
Guides
...
연습 · 에이전트 평가 (Agent evaluation)
대화 도중 요구사항이 변경될 때 AI 에이전트 (AI Agent) 테스트하기
사용자의 수정 사항은 에이전트의 다음 문장 그 이상을 변경해야 합니다. 즉, 다음 외부 동작(external action)을 수행하기 전에 쓸모없어진 계획을 무효화해야 합니다. 예산이 줄어들거나, 날짜가 변경되거나, "예약해줘"가 "옵션을 비교해줘"로 바뀌는 경우, 답변이 정중하게 변경 사항을 인지하더라도 이전 파라미터(parameters)를 그대로 유지하며 진행하는 것은 기능적 실패입니다. 이 실험실(laboratory)에서는 이러한 대화의 전환(pivots)을 반복 가능한 테스트로 변환하며, 성능 결과를 임의로 만들어내지 않고 단순 채팅 기록(chat history), 재작성된 목표 요약(goal summary), 그리고 구조화된 상태(structured state)를 비교합니다.
중급 수준 (Intermediate level)
45분
결과: 검증 가능한 공개, 수정, 그리고 기능 전환 시나리오
목차
- 실패를 정확하게 정의하기
- 의도 변경 분류하기
- 구체적인 테스트 케이스 구축하기
- 세 가지 상태(state) 접근 방식 비교하기
- 실험 준비하기
- 시나리오 세트 실행하기
- 체크 항목 자동화하기
- 테스트 하네스(test harness) 검증하기
- 일반적인 실패 진단하기
- 한계점 이해하기
1. 실패를 정확하게 정의하기
AI 에이전트(AI agent)는 목표를 수신하고, 모델(model)과 메모리(memory)를 사용하며, 외부 함수(external functions)를 통해 일련의 동작을 실행할 수 있습니다. 이러한 능력 때문에 대화 도중의 변경 사항은 텍스트 전용 채팅보다 더 위험할 수 있습니다. 쓸모없어진 가정(obsolete assumption)이 이미 계획, 캐시된 검색 결과, 대기 중인 함수 인자(function arguments), 생성된 결과물(artifact), 또는 대화 요약에 존재할 수 있기 때문입니다.
다음 대화 상황을 고려해 보십시오:
- 사용자가 금요일 15:00 이후에 12명이 사용할 회의실을 요청하며 에이전트에게 가장 좋은 옵션을 예약하라고 말합니다.
- 에이전트는 카탈로그를 검색하고 예약을 준비합니다.
- 사용자가 말합니다: "인원이 6명으로 줄었어요. 아직 아무것도 예약하지 마세요. 가장 저렴하고 적합한 방 두 곳을 비교해 주세요."
마지막 메시지는 작업의 독립적인 세 가지 부분을 변경합니다:
-
인원수(capacity)가 12명에서 6명으로 변경됨;
-
작업(operation)이 예약에서 비교로 변경됨;
-
선택 규칙(selection rule)이 정의되지 않은 "최적"에서 가장 저렴하고 적합한 두 가지 옵션으로 변경됨.
성능이 낮은 구현체는 산문(prose)으로 이 세 가지 변경 사항을 모두 인지하면서도, 여전히 12명의 참석자로 예약 함수(reservation function)를 호출할 수 있습니다. 또 다른 구현체는 참석 인원은 업데이트하지만 이전의 작업(operation)을 유지할 수도 있습니다. 세 번째 구현체는 예약을 중단하지만, 이전의 12인 검색 결과로 반환된 후보들만 비교할 수도 있습니다.
문제는 단순히 모델이 메시지를 "망각"한 것이 아닙니다. 활성 컨텍스트(active context)에는 요청에 대한 여러 가지 그럴듯한 버전이 포함되어 있으며, 서로 다른 구성 요소들이 서로 다른 버전을 읽고 있을 수 있습니다. 응답 생성기(response generator)는 수정 사항을 볼 수 있는 반면, 비동기 실행기(asynchronous executor)는 한 턴 전에 생성된 스냅샷(snapshot)을 여전히 보유하고 있을 수 있습니다.
요구사항 변경을 올바르게 처리한다는 것은, 더 이상 유효하지 않은 약속(obsolete commitments)을 무효화하고, 의존적인 데이터(dependent data)를 재계산한 다음, 허용된 작업(allowed action)을 계속 진행하는 것을 의미합니다.
이 글에서 테스트 통과 기준은 관찰 가능한 상태(observable state)와 작업(actions)이 최신의 유효한 의도(intent)를 따를 때뿐입니다. 안심시키는 답변을 내놓는 것만으로는 충분한 증거가 되지 않습니다.
2. 의도 변경(intent changes) 분류
테스트 세트에 세 가지 레이블을 사용하십시오. 이것들은 보편적인 표준은 아니며, 예상되는 전환(transitions)을 명시적으로 만드는 실용적인 카테고리입니다.
Reveal: 사용자가 중요한 사실을 추가함
Reveal(공개) 상황에서는 원래의 요청이 반드시 철회되는 것은 아닙니다. 사용자가 이전에는 알 수 없었던 정보를 제공하며, 이 정보가 자격(eligibility), 안전성(safety), 또는 순위(ranking)를 변경합니다.
-
에이전트가 메뉴를 제안한 후, "참가자 중 한 명이 견과류 알레르기가 있어요"라고 말함.
-
에이전트가 내부 요약(internal summary)을 시작한 후, "이 보고서는 외부 감사인(external auditor)용입니다"라고 말함.
-
소프트웨어 후보군이 선정된 후, "노트북은 인터넷 연결 없이도 작동해야 합니다"라고 말함.
-
화상 장비가 없는 방들이 검색된 후, "한 명은 원격으로 참여할 것입니다"라고 말함.
Reveal 테스트(reveal test)는 에이전트가 이전 결과 중 어떤 것이 더 이상 사용할 수 없는지 식별하는지 묻습니다. 에이전트는 부적절한 후보를 조용히 유지하거나, 새로 요구된 속성이 이미 확인된 것처럼 가장해서는 안 됩니다.
수정 (Revision): 사용자가 이전 값을 교체함
수정(Revision) 상황에서는 한 버전이 다른 버전을 명시적으로 대체합니다. 예를 들어, 참석자 12명 대신 6명, 월요일 대신 화요일, 또는 예산 7,000 대신 4,000과 같은 경우입니다. 두 값 모두 메시지 기록(message history)에 남아 있으므로, 시스템에는 모호하지 않은 활성 버전(active version)이 필요합니다.
핵심 불변량(invariant)은 간단합니다. 수정 사항이 수락된 후에는, 사용자가 명시적으로 버전 비교를 요청하지 않는 한 대체된(superseded) 값이 새로운 종속적 작업(dependent actions)에 나타나서는 안 됩니다.
기능 전환 (Function switch): 요청된 작업이 변경됨
기능 전환(Function switch)에서는 대상은 그대로 유지되면서 작업만 변경될 수 있습니다.
- "예약(book)"이 "비교(compare)"로 변경
- "이메일 전송(send the email)"이 "초안 보여주기(show me a draft)"로 변경
- "코드 수정(modify the code)"이 "원인 설명(explain the cause)"으로 변경
- "티켓 생성(create the ticket)"이 "티켓에 필요한 정보 목록 나열(list the information the ticket would require)"로 변경
이 카테고리는 도구 호출(tool calling)에서 특히 중요합니다. 선택된 함수가 구식이 되었거나 명시적으로 금지된 상태일 때, 대상(object)과 그 매개변수(parameters)는 올바를 수 있기 때문입니다.
주제 변경만으로 기능 전환을 추론하지 마십시오. 테스트 케이스는 이전 작업이 취소되었는지, 일시 중지되었는지, 대체되었는지, 또는 하위 작업(subtask) 후에 재개될 것으로 예상되는지를 명시해야 합니다. 사용자가 부수 효과(side effect)를 재개할 권한을 부여하지 않은 경우, 안전한 기본값은 이를 일시 중지하는 것입니다.
3. 실제 부수 효과가 없는 구체적인 사례 구축
실험에 자격 증명(credentials), 캘린더 접근 권한, 결제 또는 고객 데이터가 필요하지 않도록 로컬 회의실 카탈로그를 사용하십시오. 다음 값들은 합성 픽스처(synthetic fixtures)입니다:
{
"rooms": [
{
...
두 개의 스텁 함수(stub functions)를 노출합니다. 첫 번째는 픽스처를 검색합니다. 두 번째는 제안된 예약을 기록하지만 실제 시스템을 수정하지는 않습니다.
search_rooms({
"capacity": 12,
"starts_at": "2026-08-07T15:00:00+03:00",
...
테스트 모드에서, reserve_room은 인자(arguments)를 트레이스(trace)에 추가하고 다음을 반환해야 합니다:
{
"accepted": true,
"dry_run": true,
...
공유된 대화 접두사(dialogue prefix)는 다음과 같습니다:
User: 8월 7일 금요일, 15:00 이후에 12명이 사용할 수 있는 회의실을 찾아줘. 스크린이 필요해.
가장 좋은 옵션으로 예약해줘.
...
마지막 사용자 턴(user turn) 이후, 예상되는 활성 상태(active state)는 다음과 같습니다:
{
"revision": 2,
"goal": "compare_rooms",
...
이 객체는 구조화된 변형(structured variant)에 대한 예상 상태이며, 특정 모델이 어떻게 동작하는지에 대한 주장(claim)이 아닙니다. 실험은 여전히 실행되어야 합니다.
4. 활성 작업(active task)에 대한 세 가지 접근 방식 비교
모델, 도구(tools), 픽스처(fixtures), 메시지, 생성 설정(generation settings) 및 단계 제한(step limits)은 고정합니다. 에이전트가 다음 결정을 내리기 전에 현재 작업이 표현되는 방식만 변경합니다.
변형 A: 일반 채팅 기록 (plain chat history)
모델은 시스템 프롬프트(system prompt), 사용 가능한 대화 기록, 그리고 함수 결과(function results)를 받습니다. 별도의 활성 목표(active-goal) 객체는 없습니다. 매 턴마다 메시지 시퀀스로부터 현재 의도(intent)를 재구성해야 합니다.
이는 구성 요소가 가장 적기 때문에 유용한 베이스라인(baseline)이 됩니다. 주요 위험 요소는 이전 지시사항과 새로운 지시사항 간의 충돌이며, 특히 여러 번의 계획 메시지(planning messages)나 도구 결과가 원래의 목표를 강화한 이후에 발생할 수 있습니다.
변형 B: 재작성된 목표 요약 (rewritten goal summary)
매 사용자 턴 이후, 전용 단계에서 다음과 같은 짧은 요약을 재작성합니다:
현재 작업: 8월 7일 15:00 이후에 사용 가능하며, 스크린이 있고, 6인용인 가장 저렴한 회의실 두 곳을 비교할 것.
아무것도 예약하지 마시오.
요약은 이력상의 노이즈(historical noise)를 줄여주지만, 여전히 자유 텍스트(free text) 형태입니다. 부정어(negation)를 누락하거나, 상충하는 버전을 보존하거나, 오래된 작업과 새로운 매개변수(parameters)를 병합할 수 있습니다. 요약을 플래너(planner)에게 전달하기 전에 요약 자체를 먼저 테스트하십시오.
변형 C: 구조화된 상태 (structured state)
매 턴(turn)이 끝날 때마다, 업데이트 컴포넌트(update component)는 목표(goal), 제약 사항(constraints), 권한(permissions), 대체된 값(superseded values), 무효화된 결과(invalidated results), 그리고 미결 질문(open questions)에 대한 명시적인 필드를 가진 객체를 생성합니다. 플래너(planner)는 여전히 메시지 이력(message history)을 받을 수 있지만, 해당 객체가 권위 있는 활성 버전(authoritative active version)이 됩니다.
구조화된 상태(structured state)는 단언(assertion)을 더 쉽게 만들지만, 올바른 해석을 보장하지는 않습니다. 결함이 있는 업데이터(updater)는 지속적으로 잘못된 목표를 인코딩할 수 있습니다. 따라서 업데이트 단계(update stage)와 계획 단계(planning stage)는 반드시 별도로 점수를 매겨야 합니다.
접근 방식 (Approach)
주요 장점 (Primary advantage)
주요 리스크 (Primary risk)
...
5. 반복 가능한 실험 준비하기
동일한 작업들의 저장된 세트는 작은 벤치마크(benchmark)를 형성합니다. 모든 실행에 사용된 설정을 기록하십시오:
-
모델 식별자(model identifier) 또는 배포 버전(deployment version);
-
시스템 지침(system instructions) 및 해당 버전 또는 해시(hash);
-
생성 파라미터(generation parameters);
-
사용 가능한 함수 이름(function names) 및 스키마(schemas);
-
픽스처 버전(fixture version);
-
에이전트 단계(agent steps)의 최대 횟수;
-
상태 관리 변형(state-management variant);
-
시나리오(scenario) 및 반복 식별자(repetition identifiers);
-
단언 버전(assertion version).
만약 제공자(provider)가 결정론적 출력(deterministic output)을 보장하지 않는다면, 단 한 번의 실행으로는 안정적인 결론을 내릴 수 없습니다. 결과를 확인하기 전에 반복 횟수를 결정하고 이를 모든 변형(variant)에 적용하십시오. 별도의 트레이스(trace)를 저장하십시오. 불편한 결과가 나온 변형만 다시 실행해서는 안 됩니다.
트레이스 형식 (Trace format)
{
"run_id": "revision-01__structured__repeat-01",
"scenario_id": "revision-01",
...
측정할 수 없는 값은 null로 유지하십시오. 문자열 길이를 통해 토큰 수(token counts)를 추정하거나, 환경에서 보고하지 않은 비용을 임의로 삽입하지 마십시오.
상태 업데이트와 액션 분리하기
구조화된 상태(structured state)의 경우, 2단계 제어 루프(two-phase control loop)를 사용하십시오:
-
새로운 메시지를 수락하고 상태 전이(state transition)를 제안합니다;
-
스키마(schema)와 도메인 불변량(domain invariants)을 검증합니다;
-
새로운 상태 개정(state revision)을 커밋(commit)합니다;
-
변경된 필드에 의존하는 결과들을 무효화(invalidate)합니다;
-
그 후에만 계획(planning)과 함수 호출(function calls)을 허용합니다.
상태 업데이트(state update)와 행동(action)이 하나의 분리할 수 없는 출력으로 발생할 경우, 수정 사항이 확정(commit)되기 전에 폐기된 계획(obsolete plan)으로부터 함수가 선택될 수 있습니다.
되돌릴 수 없거나 외부로 드러나는 행동의 경우, 계획(planning) 단계 이후에 승인 게이트(approval gate)를 추가하고 실행 직전에 현재 상태의 수정 사항을 즉시 재확인하십시오.
6. 공개(reveal), 수정(revision), 함수 전환(function-switch) 시나리오 실행
각 테스트는 안정적인 접두사(stable prefix), 피벗 메시지(pivot message), 예상되는 활성 상태(expected active state), 허용된 행동(permitted actions), 금지된 행동(forbidden actions), 그리고 기계 검증 가능한 단언(machine-checkable assertions)을 포함해야 합니다. 아래의 예시들은 전이 구조(transition structure)를 변경하지 않고도 다른 도메인으로 변환될 수 있습니다.
R1 — 변경 사항 공개에 따른 후보 적격성 확인
Turn 1: 15:00 이후에 6인용 방을 찾아줘.
Turn 2: [에이전트가 amber와 birch를 수신함]
Pivot: 참가자 중 한 명이 원격으로 참여할 거야,
...
통과 조건:
- required_features에 video가 포함됨;
- amber가 더 이상 적합한 것으로 제시되지 않음;
- 에이전트가 새로운 검색을 수행하거나 로컬 전체 데이터를 명확하게 재필터링함;
- 적격성이 재계산되기 전에 예약이 발생하지 않음.
R2 — 공개로 인해 답변되지 않은 질문이 생성되는 경우
Turn 1: 팀 세션을 위한 회의실 옵션을 준비해줘.
Pivot: 회의는 외부 감사인과 진행하며,
자료는 기밀 사항이야.
설정(fixture)에는 방음 시설이나 방문객 출입 정책에 대한 정보가 없습니다. 올바른 응답은 누락된 정보를 식별하거나 명확한 설명을 요청하는 것입니다. 해당 방이 기밀 외부 회의에 적합하다는 근거 없는 주장은 거부하십시오.
V1 — 수정으로 인한 숫자 파라미터 교체
Turn 1: 12인용 방을 찾아줘.
Turn 2: [capacity=12로 검색 완료]
Pivot: 수정 사항: 우리 인원은 6명이야.
피벗 이후의 모든 새로운 search_rooms 또는 reserve_room 호출은 capacity 또는 attendees 값으로 6을 사용해야 합니다. 12는 오직 이력(history)에 나타나거나 명시적으로 대체된 값으로만 나타날 수 있습니다.
V2 — 가용성 확인 후 수정으로 인한 시간 변경
Turn 1: 8월 7일 15:00에 방이 필요합니다.
Turn 2: [에이전트가 예약 가능한 방 목록을 수신함]
Pivot: 같은 날 16:00로 변경해 주세요.
15:00의 가용성(Availability)이 16:00의 가용성을 보장하지는 않습니다. 에이전트가 새로운 시간을 확인하고 15:00 슬롯을 예약하지 않아야만 해당 실행(run)이 통과됩니다.
V3 — 수정 사항이 다시 수정되는 경우
Turn 1: 12명이 올 것입니다.
Turn 2: 아니요, 6명입니다.
Turn 3: 확인했습니다. 결국 8명입니다.
최종 활성 값은 8입니다. 이는 숫자들을 병합하거나, 첫 번째 수정 사항을 유지하거나, 반복되는 모든 수정 사항을 해결되지 않은 모순으로 취급하는 시스템을 잡아냅니다.
F1 — 행동에서 분석으로 전환
Turn 1: 가장 좋은 방을 찾아서 예약해 주세요.
Turn 2: [검색 완료]
Pivot: 중단하세요. 아무것도 예약하지 마세요.
...
필수 조건:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기