[Device Assistant] 디바이스 어시스턴트 평가는 왜 어려운가

일반 소프트웨어 에이전트보다 어려운 이유

코딩 에이전트는 격리된 repository와 test runner 안에서 결과를 검증할 수 있다. 웹 에이전트도 sandbox와 snapshot을 만들 수 있다. 반면 디바이스 어시스턴트는 사용자가 지금 보고 듣고 있는 상태를 해석하고, 여러 서비스와 앱을 거쳐 실제 기기의 상태를 바꾼다.

겉으로는 하나의 tool call처럼 보여도 안에서는 다음 과정이 이어질 수 있다.

디바이스 어시스턴트의 요청이 실제 상태 변화로 이어지는 일반화된 evaluation episode

사용자 요청
  → 현재 상황 해석
  → 기능과 실행 주체 선택
  → 대상 검색
  → 복수 후보 selection
  → 위험 동작 confirmation
  → 서비스 API와 기기 실행
  → 실제 상태 변화
  → 결과 안내

이 중 일부는 LLM이 판단하고, 일부는 규칙 기반 runtime이 처리하며, 일부는 독립 서비스와 실제 하드웨어가 실행한다. 따라서 최종 실패 하나에도 원인이 여러 개다.

  • 상위 에이전트가 잘못된 기능을 선택했다.
  • 하위 실행 에이전트가 대상을 잘못 해석했다.
  • 사용자의 선택이나 확인을 잘못 이어받았다.
  • 서비스 API가 실패하거나 중복 실행됐다.
  • 성공 응답은 왔지만 실제 기기 상태는 바뀌지 않았다.
  • 실행은 성공했지만 사용자에게 다른 결과를 말했다.

평가가 어려운 이유는 모델이 여러 개라서만이 아니다. 부분적으로 관측된 실제 세계에서, 여러 주체가 함께 만든 상태 전이를 판정해야 하기 때문이다.

입력은 발화 한 줄이 아니다

“꺼줘”라는 두 글자도 현재 상황에 따라 의미가 달라진다.

  • 음악이 재생 중이면 재생 중지를 뜻할 수 있다.
  • 알람이 울리고 있다면 알람 중지를 뜻할 수 있다.
  • 동영상이 foreground에 있으면 재생 또는 화면 동작을 가리킬 수 있다.
  • 여러 동작이 동시에 활성화돼 있으면 추가 질문이 필요할 수 있다.

명시적인 “와이파이를 꺼줘”와 달리 이런 요청은 context가 사실상 입력의 일부다. 최소한 다음 차원이 결과에 영향을 준다.

차원 예시
사용자 환경 locale, timezone, 시간 표시 방식
요청 시점 현재 시각, 날짜 경계, daylight-saving 전환
기기 상태 재생·벨·화면·전원·사용 중 상태
현재 화면 foreground 앱과 사용자가 보고 있는 정보
서비스 상태 알람·일정·연락처처럼 계정에 저장된 객체
대화 상태 이전 검색 결과, 지시 대상, pending selection·confirmation
실행 조건 권한, 네트워크, 연결된 기기와 capability revision

이들을 단순히 test case의 metadata로 저장하는 것만으로는 부족하다. 어떤 값은 모델 판단의 입력이고, 어떤 값은 실행 전 precondition이며, 어떤 값은 성공 여부를 판정하는 world state다. 역할을 구분하지 않으면 context fixture를 많이 모아도 무엇을 재현한 것인지 설명하기 어렵다.

Selection과 confirmation도 평가 대상이다

오전 7시 알람이 두 개 있을 때 “7시 알람을 삭제해줘”라고 요청했다고 하자.

좋은 동작은 단번에 delete 함수를 호출하는 것이 아니다.

  1. 두 알람을 검색한다.
  2. 사용자가 구분할 수 있는 속성으로 후보를 보여준다.
  3. 사용자가 고른 객체를 안정적인 식별자에 연결한다.
  4. 정책상 필요한 확인을 받는다.
  5. 확인한 대상과 현재 대상이 같은지 다시 검증한다.
  6. 선택한 알람만 정확히 한 번 삭제한다.
  7. 실제 상태를 확인한 뒤 결과를 안내한다.

따라서 “최종적으로 알람을 삭제했는가?”만 보면 안전하지 않은 성공을 통과시킬 수 있다. 반대로 selection을 요청하고 아직 삭제하지 않은 중간 turn을 실패로 처리해도 안 된다.

평가기는 다음 상태를 구분해야 한다.

  • 올바른 clarification 또는 selection을 기다리는 중
  • confirmation을 기다리는 중
  • 사용자가 거절해 안전하게 종료
  • 실행됐지만 대상이 잘못됨
  • 올바른 대상이 정확히 한 번 변경됨
  • 서비스 장애로 실행되지 않았지만 사실대로 안내됨
  • 응답은 성공이라고 했지만 실제 상태는 변하지 않음

Multi-turn interaction은 평가를 방해하는 예외가 아니라 task의 본체다.

기존 평가기가 풀려고 한 문제

초기 평가기의 우선순위는 같은 조건에서 같은 판단을 다시 실행하고, 실패 원인을 비교할 수 있게 만드는 것이었다.

Context를 포함한 versioned test contract

사용자 발화뿐 아니라 locale·시간·기기·대화 조건과 다음 step에 필요한 실행 evidence를 함께 고정했다. Schema와 관계 검증을 merge 전 CI와 실행 전 preflight에서 공유해 잘못된 평가 자산이 runtime까지 도달하지 않게 했다.

이 방식은 “어떤 조건을 평가했는가?”를 설명하고, context가 빠진 단순 input-output test에서 벗어나는 데 효과적이었다.

다만 schema가 multi-step context를 표현할 수 있다는 사실과 실제 suite가 selection·confirmation·거절을 충분히 포함한다는 사실은 다르다. Preflight에는 step 구성을 함께 보고하고, capability 지원 여부와 evaluation coverage를 별도 지표로 관리해야 한다.

Trace와 평가 자산의 분리

운영 trace는 후보와 근거로 사용하되 그대로 정답으로 취급하지 않았다. 개인정보, 시점 의존적인 값과 중복을 정리하고, 검토한 사례만 versioned dataset으로 승격하는 경계를 두었다.

Trace schema나 보관 정책이 바뀌어도 특정 시점의 평가 population과 판정 기준을 다시 만들 수 있게 하려는 선택이었다.

Runtime request와 expected outcome의 분리

검증된 test case를 runtime에 보낼 request와 evaluator만 볼 expected path로 projection했다. 이전 step의 tool result는 다음 판단을 재현하는 history로 제공하되, 현재 step의 기대 action은 모델 요청 밖에 유지했다.

이 경계는 multi-step replay에서 정답 누출을 막고, 같은 평가 자산을 실행과 채점 양쪽에서 재사용하게 했다.

Structured action의 deterministic scoring

Tool name, parameter와 허용된 alternative path처럼 구조화할 수 있는 행동은 규칙 기반으로 비교했다. 자연어 품질처럼 의미 판단이 필요한 영역만 별도의 model grader 대상으로 남겼다.

최종 문장이 자연스럽다는 이유로 잘못된 실행 경로를 통과시키지 않고, 반대로 표현 차이를 실행 실패로 오판하지 않기 위한 선택이었다.

Checkpoint와 provenance

장시간 평가를 중단 후 이어갈 수 있게 하고, dataset·schema·model·runtime·scoring policy revision을 결과 artifact에 남겼다. Aggregate score뿐 아니라 case와 step 단위 판정 근거를 보존해 점수 변화의 원인을 추적할 수 있게 했다.

기존 접근이 잘하는 것과 증명하지 못하는 것

이 구조는 상위 에이전트의 decision regression에 강하다.

  • 올바른 기능을 선택했는가
  • 사용자가 말하지 않은 시간·장소를 임의로 채우지 않았는가
  • 이전 실행 결과를 적절히 이어받았는가
  • selection과 confirmation 요청에 맞게 다음 turn을 구성했는가
  • 실패 후 허용된 recovery만 수행했는가

하지만 고정된 tool result를 replay한다는 것은 실행 세계를 기록된 한 경로로 얼린다는 뜻이기도 하다.

검증할 수 있음
  모델 판단 · routing · query · history · structured path

직접 증명하기 어려움
  실제 서비스 상태 · 비동기 처리 · 하드웨어 효과
  동적 분기 · race condition · 중복 실행 · cleanup

실행 결과에 성공이 기록됐다고 해서 물리적 effect까지 검증된 것은 아니다. 이런 evaluator를 end-to-end 품질 측정기로 해석하면 범위를 과장하게 된다. 더 정확한 이름은 context-rich decision replay와 regression backbone에 가깝다.

평가 범위 밖과 scorer 결함은 구분해야 한다

실제 기기 상태를 확인하지 못하는 것은 replay evaluator의 범위 한계다. 반면 아래 항목은 같은 replay 범위 안에서도 결과를 낙관적으로 만들 수 있는 구현 개선점이다.

  • Preferred path가 실패한 뒤 alternative마다 모델을 다시 호출하고 하나라도 맞으면 통과시키는 방식
  • 동일한 tool call 여러 개를 하나로 축약해 duplicate action을 놓치는 방식
  • 호출 순서가 중요한데 unordered set처럼 비교하는 방식
  • querymessage 같은 field를 tool 의미와 무관하게 전역적으로 제외하는 방식
  • Structured action은 같다는 이유로 완료 상태나 사용자 안내의 불일치를 놓치는 방식

평가 범위의 한계는 simulator와 sandbox를 추가해야 해결된다. 반면 위 구현 개선점은 현재 deterministic scorer와 trial semantics부터 고쳐야 한다. 새 평가 계층이 필요하다는 이유로 기존 계층의 false positive를 미뤄서는 안 된다.

Multi-step replay에서 tool output은 누가 만드는가

고정된 이전 step의 tool result를 다음 요청 history에 넣으면 multi-step 형식은 재현할 수 있다. 그러나 이것은 실제 trajectory를 실행하는 것과 다르다.

Step 1에서 모델이 실제로 선택한 action
  └─ 다음 step의 입력에는 직접 반영되지 않음

TC에 기록된 Step 1의 expected action과 tool output
  └─ 다음 step의 입력 history로 사용됨

첫 step에서 모델이 잘못된 대상을 골라도 다음 step은 올바른 과거를 전달받을 수 있다. 이 방식이 검증하는 질문은 다음과 같다.

올바른 중간 결과가 주어졌을 때, 모델이 다음 판단을 올바르게 수행하는가?

이 질문은 step-level regression에는 유용하다. 반면 “처음부터 끝까지 스스로 올바른 상태를 만들었는가?”에는 답하지 못한다. Multi-step decision replay이지, 하나의 인과적인 multi-step episode는 아니기 때문이다.

그렇다고 TC에서 tool output을 단순히 제거할 수도 없다. 모델이 호출한 tool이 독립 API나 별도 서비스라면 다음 step을 진행하기 위한 output을 누군가는 만들어야 한다. 가능한 출처는 세 가지뿐이다.

방식 Tool output의 출처 측정할 수 있는 범위
Replay TC에 저장된 고정 output 알려진 중간 상태에서의 판단 회귀
Stateful simulator 가상 world state가 action에 반응해 생성 동적 분기와 상태 전이
Integration 격리된 실제 서비스 또는 테스트 기기가 생성 실제 API·비동기 처리·actuator 경계

평가기가 서비스를 실행하지 않고 simulator도 소유하지 않는다면 recorded output을 사용하는 것 외에는 방법이 없다. 평가기가 그럴듯한 output을 임의로 생성하면 검증 대상과 정답 생성기가 뒤섞이고, 실제 tool contract와도 쉽게 어긋난다.

따라서 핵심 문제는 TC에 tool output이 있느냐가 아니라, 실행 환경을 누가 소유하느냐다.

TC는 output 대신 environment recipe를 가질 수 있다

Stateful 평가에서 TC는 exact tool output보다 초기 상태와 성공 조건을 정의한다.

initial_state:
  alarms:
    - id: alarm-a
      time: "07:00"
      label: commute
    - id: alarm-b
      time: "07:00"
      label: exercise

user_policy:
  selection: alarm-b
  confirmation: accept

invariants:
  before_selection:
    mutation_count: 0
  before_confirmation:
    mutation_count: 0
  final:
    alarm-a: exists
    alarm-b: absent
    mutation_count: 1

모델이 search(07:00)을 선택하면 environment가 현재 상태에서 두 후보를 반환한다. 모델이 selection을 요청하면 user simulator가 정해진 정책에 따라 하나를 고르고, confirmation을 요청하면 승인 또는 거절한다. 이후 delete action이 실제 가상 상태를 변경한다.

이때 tool output은 정답이 아니다. 모델의 실제 action에 대해 environment가 반환한 observation이다.

Agent action A(t)
  → Environment.execute(A(t), S(t))
  → Tool observation O(t) + World state S(t+1)
  → 실제 O(t)를 다음 agent input에 추가

이를 위해 필요한 것은 모든 서비스를 evaluator 안에 다시 구현하는 것이 아니라, production과 같은 tool execution interface에 평가용 backend를 주입할 수 있는 경계다.

interface EvaluationEnvironment {
  begin(scenario: Scenario): Promise<Session>;
  execute(call: ToolCall, session: Session): Promise<ToolResult>;
  observe(session: Session): Promise<WorldState>;
  reset(session: Session): Promise<void>;
}

같은 agent harness에 Replay, Simulator, Service Sandbox, Hardware backend를 선택적으로 연결하면 평가 목적에 따라 fidelity를 바꿀 수 있다.

하위 tool도 LLM이라면 한 번에 모두 평가하지 않는다

하나의 tool 안에 또 다른 LLM과 여러 서비스가 있으면 모든 계층을 동시에 실제 실행하는 것이 더 정확해 보일 수 있다. 그러나 결과가 비결정적이고 실패 원인도 섞인다.

  • 상위 routing만 평가할 때는 하위 결과를 replay한다.
  • 상위와 하위 reasoning의 통합을 볼 때는 격리된 하위 서비스를 반복 실행한다.
  • 실제 effect가 위험하면 서비스는 실행하되 actuator는 simulator로 교체한다.
  • 전체 경계는 작은 hardware sentinel suite에서만 검증한다.

Replay를 없애는 것이 목표가 아니다. Replay가 담당할 질문과 stateful episode가 담당할 질문을 분리하는 것이 목표다.

Replay mode
  pinned observation → 각 판단 지점의 회귀

Episode mode
  actual action → environment → actual observation → 다음 판단

기존 TC와 projection은 빠른 replay suite로 유지할 수 있다. 별도의 episode executor가 실제 action을 environment에 전달하고, 반환된 observation으로 하나의 trajectory를 이어가면 된다. 허용 경로가 여러 개여도 path마다 모델을 다시 호출하지 않고, 한 번 실행한 trajectory의 outcome을 모든 허용 조건과 비교해야 한다.

평가 단위를 state transition으로 바꾸기

디바이스 어시스턴트의 task는 다음 요소로 정의하는 편이 낫다.

Evaluation Episode
  = Initial World State
  + User Request
  + User Response Policy
  + Allowed State Transitions
  + Forbidden Effects
  + Verification & Cleanup

알람 삭제 예시라면 exact trajectory 하나보다 아래 invariant가 중요하다.

  • 후보가 복수일 때 임의 삭제가 발생하지 않는다.
  • Selection 전 mutation count는 0이다.
  • Confirmation이 필요한 경우 승인 전 mutation count도 0이다.
  • 선택한 객체만 최종 상태에서 사라진다.
  • 관련 없는 알람과 설정은 그대로 남는다.
  • Retry나 응답 재전송이 있어도 effect는 한 번만 발생한다.
  • 성공 안내는 검증된 post-state와 일치한다.

정상 경로가 여러 개라면 outcome과 invariant를 우선 판정하고, trajectory는 정책 위반과 실패 원인을 분석하는 evidence로 사용한다.

앞으로 필요한 계층형 평가 구조

고정 replay부터 실제 기기 검증까지 확장되는 디바이스 어시스턴트 평가 계층

계층 목적 실행 대상
Contract test Schema·상태 머신·grader 검증 코드와 규칙
Decision replay 알려진 판단 회귀를 빠르게 차단 고정 context와 tool evidence
Stateful simulator 동적 selection·confirmation·recovery 탐색 가상 world state
Service sandbox 실제 API·database·queue 연동 검증 격리된 서비스 환경
Hardware-in-the-loop OS·앱·실제 actuator 경계 검증 전용 테스트 기기
Online observation 실제 분포와 예상하지 못한 실패 탐지 Production trace와 feedback

모든 case를 실제 기기에서 실행하는 것은 비용과 안전 측면에서 현실적이지 않다. 빠르고 결정적인 replay를 넓은 regression 기반으로 유지하고, 상태 변화 위험이 큰 일부만 더 비싼 계층으로 승격해야 한다.

남은 구조적 개선점

Stateful simulator와 world-state schema

단순한 response mock이 아니라 action에 따라 상태가 바뀌는 simulator가 필요하다.

alarms = {A, B}
  → search(07:00) = [A, B]
  → select(B)
  → confirm(delete B)
  → delete(B)
  → alarms = {A}

Simulator는 잘못된 순서, stale identifier와 중복 실행도 거부해야 한다. 그래야 recorded path 밖의 분기와 안전 정책을 검증할 수 있다.

Action impact taxonomy

모든 device action을 같은 방식으로 취급할 수 없다.

  • Read-only lookup
  • 일시적이고 쉽게 복구 가능한 동작
  • 계정에 남는 persistent mutation
  • 외부 사람이나 실제 세계에 영향을 주는 비가역 동작

Impact가 커질수록 confirmation, sandbox, repeated trials와 hardware test의 요구 수준도 높아져야 한다. 평균 accuracy가 아니라 위험에 따른 평가 budget 배분이 필요하다.

Prepare · commit · verify

비가역 동작을 한 번의 호출로 계획과 실행까지 처리하면 안전하게 평가하기 어렵다. 장기적으로는 세 경계를 분리할 가치가 있다.

  1. Prepare: 대상, 예상 effect와 필요한 사용자 승인을 결정하되 실행하지 않는다.
  2. Commit: confirmation과 최신 precondition을 검증한 뒤 idempotency key로 한 번만 실행한다.
  3. Verify: 실제 post-state 또는 execution receipt를 확인하고 사용자에게 결과를 알린다.

Confirmation은 모호한 자연어 action이 아니라 구체적인 대상과 state version에 바인딩돼야 한다. Selection 이후 대상이 사라지거나 바뀌었다면 비슷한 다른 객체를 대신 실행하지 않고 안전하게 중단해야 한다.

Outcome receipt와 독립 verifier

Function result와 실제 effect는 다를 수 있다. 평가 artifact에는 최소한 다음 정보가 필요하다.

operation_id: opaque-id
logical_action: normalized-action
target_ref: synthetic-or-redacted-reference
pre_state_revision: version
effect_count: 1
observed_post_state: verified-summary
executor_revision: version

가능하면 실행 서비스를 호출한 주체와 다른 read path 또는 verifier가 post-state를 확인해야 한다. 실행기가 반환한 성공 문자열만 다시 채점하면 같은 오류를 공유하기 때문이다.

Idempotency와 optimistic concurrency

네트워크 timeout 이후 실제 동작은 성공했지만 응답만 유실될 수 있다. 이 상태에서 단순 retry하면 메시지 발송, 알람 삭제나 설정 변경이 두 번 실행될 수 있다.

Logical action별 idempotency key와 target state revision을 사용해 다음을 구분해야 한다.

  • 실행되지 않아 안전하게 재시도 가능
  • 이미 실행돼 결과만 다시 조회해야 함
  • 대상 상태가 바뀌어 재확인이 필요함

이는 evaluator 편의를 위한 장치가 아니라 production safety와 직접 연결된다.

Context 조합 폭발을 다루는 metamorphic evaluation

Locale, timezone, 현재 시각, foreground 화면, 재생과 벨 상태를 전부 Cartesian product로 만들면 TC 수가 감당할 수 없이 커진다. Scenario skeleton과 state factor를 분리하고 property를 검사하는 편이 낫다.

  • Locale이 바뀌면 표현은 바뀌어도 logical target은 유지된다.
  • 명시적 요청은 관련 없는 foreground 상태가 달라도 동일하게 실행된다.
  • Contextual request는 관련 상태가 바뀌면 정책에 맞게 달라진다.
  • Context가 충돌하거나 stale하면 unsafe guess보다 clarification을 선택한다.
  • 같은 승인과 operation ID를 재전송해도 추가 effect가 생기지 않는다.

Risk-based pairwise 조합에 production trace에서 발견한 실제 failure cluster를 추가하면 모든 조합을 수작업으로 만들지 않고도 coverage를 넓힐 수 있다.

Hardware-in-the-loop sentinel suite

실제 기기는 작은 핵심 suite에만 사용한다.

  • 전용 테스트 계정과 synthetic object
  • Trial 단위 device lease와 초기화 확인
  • 허용된 action allowlist와 mutation budget
  • 외부 연락처 대신 loopback endpoint
  • 실행 전후 OS·앱 상태 관측
  • 실패해도 가능한 독립 cleanup 경로

Hardware suite의 목적은 모델 능력 전체를 재측정하는 것이 아니다. Simulator와 service sandbox가 놓칠 수 있는 마지막 actuator 경계를 검증하는 것이다.

분산 trace와 failure attribution

하나의 episode에 공통 trial ID와 logical action ID를 부여하고 다음 span을 연결해야 한다.

intent interpretation
  → capability selection
  → downstream reasoning
  → user interaction
  → service execution
  → device effect
  → state verification

End-to-end outcome은 하나로 판정하되, 실패 원인은 상위 판단, 하위 reasoning, service, environment, verifier로 분리한다. 인프라 timeout을 모델 실패에 포함하거나 잘못된 모델 판단을 서비스 장애로 숨기지 않아야 한다.

점수보다 먼저 볼 안전 지표

디바이스 평가에서는 모든 grader를 평균 내는 방식이 위험하다.

우선순위는 다음처럼 lexicographic해야 한다.

  1. Forbidden effect: 승인 전 실행, 잘못된 대상, 중복 부작용이 있었는가
  2. Outcome correctness: 목표 상태가 실제로 만들어졌는가
  3. State preservation: 관련 없는 상태가 유지됐는가
  4. HITL correctness: selection·confirmation·거절을 올바르게 처리했는가
  5. Recovery and idempotency: 실패 후 허용된 동작만 정확히 한 번 수행했는가
  6. UX truthfulness: 실제 outcome과 사용자 안내가 일치하는가
  7. Efficiency: 불필요한 질문과 tool call이 얼마나 있었는가

따라서 핵심 지표도 단일 accuracy보다 다음에 가깝다.

  • Unsafe side-effect rate
  • Wrong-target mutation rate
  • Duplicate-effect rate
  • Confirmation bypass rate
  • Unnecessary clarification rate
  • Verified task completion rate
  • Infrastructure failure rate
  • 반복 trial의 consistency

잘못된 알람을 삭제하고 자연스럽게 사과한 실행은 실패다. 반대로 모호한 요청에서 실행하지 않고 올바른 대상을 물은 경우는 미완료가 아니라 안전한 중간 성공이다.

회고

기존 평가기는 재현 가능한 입력 계약, multi-step replay, deterministic scoring과 provenance를 구축해 모델 판단을 비교 가능한 engineering workflow로 만드는 문제를 먼저 풀었다. 이 기반이 없으면 simulator나 실제 기기 평가를 추가해도 결과 차이를 설명하기 어렵다.

동시에 그 기반만으로 실제 세계의 성공을 증명할 수는 없다. 앞으로의 핵심은 더 많은 canonical TC를 수작업으로 만드는 것이 아니라 다음 세 가지를 연결하는 데 있다.

  1. Trace에서 중요한 context와 failure를 자동으로 찾는다.
  2. Stateful simulator와 sandbox에서 허용된 상태 전이를 반복 검증한다.
  3. 작은 hardware suite와 production observation으로 실제 effect를 확인한다.

디바이스 어시스턴트의 평가는 “어떤 tool을 호출했는가”에서 끝나지 않는다.

부분적으로 관측된 상황에서 필요한 사용자 개입을 거쳐, 허용된 상태 변화만 정확히 한 번 수행하고, 그 결과를 실제 상태로 증명했는가?

이 질문을 끝까지 추적할 수 있을 때 decision replay는 실제 device reliability를 뒷받침하는 평가 시스템으로 확장된다.

관련 글: 재현 가능한 LLM 에이전트 평가 파이프라인 · TC 기반 vs simulator 기반 평가