
로그가 곧 에이전트다
요약
AI 에이전트의 본질을 모델이나 런타임이 아닌, 이벤트 이력인 '로그(log)'로 정의합니다. 로그는 사용자 입력, 모델 출력, 도구 호출 등을 포함한 영구적인 상태를 의미하며, 이를 통해 에이전트의 상태를 완벽하게 재구성하고 재개할 수 있습니다.
핵심 포인트
- 에이전트의 핵심은 모델이 아닌 이벤트 이력(log)인 상태 데이터임
- 로그는 사용자 입력, 모델 출력, 도구 호출 등을 포함한 추가 전용 기록임
- 올바른 로그 구축 시 에이전트의 상태를 어디서든 재개할 수 있음
- 에이전트 시스템의 모든 작업은 로그를 읽거나 쓰는 과정으로 단순화됨
AI 에이전트(AI agents)들이 동일한 변곡점에 도달하고 있습니다.
대부분의 사람들은 에이전트가 모델(model), 런타임(runtime), 또는 현재 작업을 실행 중인 루프(loop)라고 생각합니다. 그러한 요소들도 중요합니다. 하지만 그것들이 에이전트는 아닙니다.
이 글에서 저는 에이전트란 곧 그 데이터, 구체적으로는 우리가 로그(log)라고 부를 이벤트 이력(history of events)이라고 주장하고자 합니다. 로그가 올바르게 구축되면, 에이전트는 로그만으로도 재개(resume)될 수 있습니다. 그리고 이 단 하나의 속성은 고급 에이전트 유스케이스(use cases)조차 추론하기 쉽고 일상적인 애플리케이션으로 구축하기 용이하게 만드는 일련의 기능들을 가능하게 합니다.
에이전트 정의하기
그렇다면 로그란 무엇일까요? 에이전트를 정의한다는 것은 로그를 정의하는 것을 의미합니다. 핵심적으로 로그는 이벤트 이력(event history)입니다. 에이전트가 작동하면서 축적되는 모든 사용자 입력(user input), 모델 출력(model output), 도구 호출(tool call), 그리고 도구 결과(tool result) — 즉, 발생한 모든 일에 대한 추가 전용(append-only) 기록입니다.
로그는 또한 세션 정의(session definition)에 대한 참조를 포함합니다. 즉, 에이전트가 실행되는 시스템 프롬프트(system prompt), 도구 설명(tool descriptions), 그리고 기술(skills)입니다. 이것은 턴(turn)마다 바뀌지 않으므로, 스트림(stream)의 활성 부분이라기보다는 버전 관리되는 상수(versioned constant)에 가깝습니다.
이들이 합쳐져 에이전트의 전체 상태(full state)를 형성합니다. 에이전트가 존재하는 그 무엇도 런타임(runtime), 모델(model), 또는 도구(tools) 안에 머물지 않습니다. 그것들은 단지 해석기(interpreters)이자 추가기(appenders)일 뿐입니다. 그것들은 영구적인 상태(durable state)를 읽고, 그에 따라 행동하며, 다음 이벤트를 다시 기록합니다. 따라서 새로운 실행기(executor)에 동일한 로그를 전달하면, 실행기는 에이전트가 정확히 어디에 있었는지 재구성하여 그 지점부터 계속 진행할 수 있습니다. 로그는 그 자체만으로 에이전트를 재개하기에 충분합니다.
실전에서의 로그
에이전트를 로그로 정의하고 나면, 나머지 시스템을 추론하기가 더 쉬워집니다. 에이전트에 대한 모든 작업은 로그를 읽거나, 로그에 추가하거나, 로그의 뷰 (view)를 렌더링하는 작업 중 하나입니다. 모델은 뷰를 읽고 다음 행동 (action)을 생성합니다. 도구 실행기 (tool runner)는 도구 호출 (tool call)을 실행하고 그 결과를 추가합니다. UI는 로그를 읽어 타임라인을 렌더링하고, 트레이싱 시스템 (tracing system)은 로그를 읽어 트레이스 (traces)를 렌더링하며, 감사자 (auditor)는 무슨 일이 일어났는지 재구성하기 위해 로그를 읽습니다. 이는 데이터베이스가 수년 전 확립한 것과 동일한 패턴입니다. 테이블 (tables), 인덱스 (indexes), 캐시 (caches)는 모두 근간이 되는 변경 로그 (log of changes)에 대한 투영 (projections)입니다. 이 글의 온라인 버전에서 @dexhorthy의 훌륭한 12-factor agents exploration을 참고하여 수정한 다이어그램을 확인해 보세요. 읽어볼 가치가 있습니다.
실제로는 다음과 같이 단순화된 루프 (loop) 형태를 띨 수 있습니다:
단순해 보이지만, 핵심적인 통찰은 각 패스 (pass)가 일시적인 임대 (lease)를 확보하고, 로그를 읽고, 한 단계를 진행한 뒤, 결과를 다시 기록한다는 점입니다. 이 루프는 멱등성 (idempotent)을 가지며 결함 허용 (fault-tolerant)이 가능합니다. 모든 의미 있는 상태 전이 (state transition)가 내구성 있게 기록되는 한, 어떤 실행기 (executor)라도 세션을 이어받아 계속 진행할 수 있습니다.
압축 (Compaction)은 로그가 아니다
로그는 무한히 커질 수 있지만, 모델이 바라보는 로그의 뷰는 그럴 수 없습니다. 컨텍스트 윈도우 (context windows)는 유한하기 때문입니다. 매 호출마다 모델에게 전체 원시 이력 (raw history)을 전달할 수는 없습니다. 여기서 압축 (compaction)이 등장합니다. 그런데 압축은 이전의 모든 것을 요약본으로 대체하는데, 그렇다면 로그가 곧 에이전트라는 주장이 파괴되는 것 아닌가요?
아니요, 그렇지 않습니다. 압축은 손실이 발생하는 (lossy) 작업임을 기억해야 합니다. 압축된 요약본은 에이전트의 상태를 더 작은 형태로 재현하는 것이 아니라, 정보를 버리는 것입니다.
그것은 실제로 그 주장을 강화합니다. 전체 로그(full log)가 기록이며, 압축(compaction)은 그 기록의 하나의 투영(projection)일 뿐입니다. 이는 구체화된 뷰(materialized view)가 데이터베이스 그 자체가 아니고, 요약본이 대화 그 자체가 아닌 것과 같습니다. 원본 로그를 유지하면 언제든 그것으로부터 새로운 투영을 생성할 수 있습니다. 하지만 원본 로그를 버리고 압축본만 남긴다면, 에이전트의 일부를 잃어버리는 것입니다. 따라서 압축을 최선을 다해 수행하되 정보 손실이 발생하는 포크(lossy fork)로 취급하고, 이를 새로운 로그로서 재개하는 것이 가장 깔끔합니다.
전체 세션 로그는 진행 과정에서 세부 사항을 덜어내며 더 작은 압축된 큐브(compacted cube)로 스트리밍됩니다.
로그가 세상의 전부는 아니다
진지하게 고려할 만한 명백한 반론이 있습니다. 로그 외부에서 상태를 변경하는 도구들은 어떻게 할 것인가 하는 점입니다. 에이전트가 파일을 수정하거나, GitHub 이슈를 생성하거나, 이메일을 보낸다고 가정해 봅시다. 이제 로그가 아닌 다른 어딘가에 상태가 존재하게 됩니다. 이것이 앞선 주장을 무너뜨리지 않을까요?
아니요, 그렇지 않습니다. 에이전트의 상태는 세상이 아니라 로그로부터 재도출(re-derive)하는 것입니다. 만약 에이전트가 이미 이메일을 보냈다면, 다시 포크(forking)한다고 해서 보낸 이메일이 취소되지는 않으며, 에이전트가 수정했던 파일은 그 사이 변경되었을 수도 있습니다. 로그가 세상을 결정론적(deterministic)이거나 가역적(reversible)으로 만드는 것은 아닙니다. 로그는 에이전트가 무엇을 했고 무엇을 보았는지에 대한 충실하고 재개 가능한 기록을 유지하며, 이것이 바로 결코 잃어서는 안 되는 핵심입니다. 세이브 파일(save file)도 동일한 방식을 취합니다. 세이브 파일은 스카이림(Skyrim) 엔진이나 전체 지도를 포함하는 것이 아니라, 플레이어를 다시 게임에 투입하는 데 필요한 플레이어 특화 상태만을 포함합니다. 그리고 에이전트가 마지막으로 실행된 이후 세상이 변했다면, 에이전트는 게임 캐릭터와 마찬가지로 주변 환경과 다시 상호작용하면서 자신의 세계관을 업데이트합니다.
파생되는 속성들
에이전트를 이런 방식으로 추론하면 일련의 훌륭한 시스템 속성(system properties)을 얻을 수 있습니다. 이 속성들은 독립적이지 않으며, 로그가 곧 에이전트라는 동일한 가정으로부터 모두 파생됩니다.
-
신뢰성 (Reliability). 현재 Claude Code에서 발생하는 상황을 생각해 보십시오. 만약 에이전트가 권한 요청 프롬프트(permission prompt)에 도달했을 때 프로세스가 종료되고 사용자가 이를 재개한다면, 권한 요청 프롬프트는 사라지고 에이전트는 일시 정지된 상태가 됩니다. 이는 프로덕션 환경에서 용납될 수 없는 일입니다. 로그가 곧 에이전트라면, 실행기(executor)는 오류가 발생해도 괜찮습니다. 새로운 워커(worker)가 세션을 이어받아 상태를 재구성하면, 권한 요청 프롬프트는 원래 있던 자리에 그대로 있게 됩니다. 프로세스는 종료되었을지언정, 에이전트는 종료되지 않은 것입니다.
-
확장성 (Scalability). 대부분의 하네스(harnesses)는 에이전트당 하나의 프로세스를 실행하며, 이는 에이전트가 실행 중인 머신에 종속됨을 의미합니다. 로그가 상태(state)가 되면 이 모델을 뒤집을 수 있습니다. 하나의 프로세스가 수천 명의 에이전트를 전진시킬 수 있으며, 각 에이전트는 매 턴마다 로그로부터 자신의 상태를 재구성합니다. 에이전트는 특정 머신이나 워커에 묶이지 않으므로 장애 조치(failover)가 매우 간단해집니다. 또한 확장은 단순히 더 많은 워커를 추가하는 것일 뿐입니다. 스티키 세션(sticky sessions), 상태 마이그레이션(state migration), 조정 오버헤드(coordination overhead)가 필요 없습니다.
-
포킹 (Forking). 하나의 선형적인 경로 대신 로그를 분기(branch)할 수 있습니다. 한 분기는 Claude에서, 다른 분기는 GPT에서, 또 다른 분기는 로컬 Qwen에서 실행하며, 각각 서로 다른 샌드박스(sandbox)와 서로 다른 도구(tools)를 사용하여 서로 다른 전략을 탐색할 수 있습니다. 이를 통해 다양한 접근 방식을 탐색하는 아키텍처 설계가 훨씬 단순해집니다.
-
멀티플레이어 (Multiplayer). 에이전트를 공유한다는 것이 대화 기록을 Slack에 복사하는 것을 의미해서는 안 됩니다. 그것은 데이터베이스의 스크린샷을 공유하면서 복제(replication)라고 부르는 것과 같습니다. 로그가 곧 에이전트라면, 공유한다는 것은 다른 사람이 검사하거나, 재개하거나, 확장할 수 있는 영구적인 히스토리(durable history)에 대한 접근 권한을 부여하는 것을 의미합니다.
-
마이그레이션 (Migration). 에이전트의 정체성이 특정 제공업체(provider)에 종속된 가정 안에 갇혀 있다면, 제공업체를 옮기는 것은 고통스러운 일이 됩니다. 로그가 곧 에이전트라면, 마이그레이션은 어댑터(adapter)의 문제가 됩니다. 서로 다른 모델은 서로 다른 투영(projections)을 원할 수 있지만, 이는 엔지니어링의 문제이지 정체성의 문제가 아닙니다. 로그가 연속성(continuity)을 제공하며, 에이전트는 어떤 모델 제공업체와도 상호 교환하며 작업을 이어받고 재개할 수 있어야 합니다.
이것들은 단지 몇 가지 예시에 불과합니다. 에이전트를 이런 방식으로 생각하기 시작하면, 에이전트는 데이터의 조각이 되며, 데이터를 통해 할 수 있는 많은 일들이 에이전트를 통해서도 가능해집니다. 이 기사의 나머지 내용은 DEV에서 읽어보세요. link.dev.to/aie26 에서 더 읽어보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기