교과서적 분석: 에이전트의 해부학 및 진화의 다섯 가지 물결
요약
본 글은 에이전트의 핵심 개념과 진화 과정을 교과서적으로 분석합니다. 특히 ReAct 루프를 중심으로, 에이전트가 단순한 정적 답변을 넘어 환경과의 상호작용(Observation)을 통해 지식을 축적하고 추론하는 방식을 깊이 있게 다룹니다.
핵심 포인트
- 에이전트의 핵심은 Think-Act-Observe 루프 구조입니다.
- 행동 궤적은 단순한 긴 프롬프트로 대체될 수 없습니다. 환경 피드백이 중요합니다.
- CodeSmith는 에이전트 실행을 위한 내부 루프 구현체를 제공합니다.
- 에이전트는 도구 호출, 실행, 결과를 히스토리에 반복적으로 추가하며 작동합니다.
교과서: 에이전트의 해부학과 진화의 다섯 가지 물결
CodeSmith의 출처 버전:
v0.5.0(커밋3a74c82f). 모든 경로는 리포지토리 루트를 기준으로 하며, 줄 번호는 이 버전을 참조합니다.
예상 독자층: '루프–자율성–다중 에이전트–진화'의 좌표계를 원하는 에이전트 엔지니어링 초심자들.
서론에서는 보이지 않는 전쟁에 대해 이야기한 후, '하네스(harness)'라는 단어를 던졌습니다. 앞으로 20여 개의 분량을 거치며 우리는 CodeSmith의 기관들—캐시(caches), 핸들(handles), 압축(compaction), 승인 게이트(approval gates)—속으로 깊이 뛰어들 것입니다. 하지만 메스가 나오기 전에, 이번 편에서는 '에이전트(Agent)'라는 단어 자체를 해부하고 싶습니다. 루프가 어떻게 생겼는지, 자율성이 몇 단계로 나뉘는지, 다중 에이전트를 신뢰할 수 있는지, 그리고 왜 2026년에 모든 엔지니어링 팀이 하네스 엔지니어링에 대해 이야기하는지를 말입니다. 이것들은 특정 프로젝트가 소유한 것이 아닌 이 기술 분야의 공통 지식이며; CodeSmith의 소스는 이를 확인하기 위해 계속 등장할 것입니다—아래 붙여넣은 코드 조각을 포함하여, 전체 리포지토리에서 교과서와 가장 가까운 형태를 보여줍니다.
루프: 생각–행동–관찰 (Think–Act–Observe)
현대 에이전트의 핵심 추상화는 ReAct 루프입니다. 모델은 먼저 현재 상태와 사용 가능한 행동에 대해 추론하고, 하나의 행동(검색, 데이터베이스 질의, 코드 실행)을 실행하며, 환경은 관찰 결과를 반환하고, 모델은 그 관찰 결과로부터 다음 라운드를 추론합니다. 평범하게 들리지만, 이는 덜 명확한 결론을 내포하고 있습니다: 에이전트의 행동 궤적은 하나의 긴 정적인 답변으로 축소될 수 없습니다.
그 이유는 행동이 사용 가능한 정보를 변화시키기 때문입니다. 순수 추론 모드에서는 컨텍스트에 '이 코드가 컴파일되는지 여부'에 대한 정보가 없다면, 모델은 이를 '생각해 낼' 수 없으며 단지 추측할 뿐입니다. 반면에 루프(loop) 안에서는 모델이 컴파일 명령을 실행하고, 컴파일러의 오류 출력이 컨텍스트 내 새로운 사실이 되며, 후속적인 추론은 이 새로운 사실들을 기반으로 구축됩니다. 따라서 20단계를 거친 에이전트는 최종 판단을 그 단계들 각각에 대한 환경 피드백 덕분에 얻게 되는데, 이 피드백은 행동이 일어나기 전에는 전혀 존재하지 않았던 것입니다. 그러므로 '루프를 더 긴 프롬프트로 대체하는 것'은 작업에 환경과의 상호작용이 전혀 관련되지 않을 때만 유효합니다.
CodeSmith의 엔진 내부에는 교과서처럼 보이는 루프 구현체가 자리 잡고 있습니다. 프로덕션 경로의 16,000줄짜리 host_executor가 아니라, 프레임워크 크레이트(crate)에 있는 참조 구현체인 DefaultAgentExecutor::run_inner (crates/agent/src/executor/mod.rs:140)입니다. 이 모듈 헤더 주석은 스스로를 'LangChain의 AgentExecutor 아날로그'라고 소개합니다. 주변적인 세부 사항을 제거하고 나면 그 골격은 다음과 같습니다:
// crates/agent/src/executor/mod.rs:140 (발췌)
let mut step: u32 = 0;
loop {
...
이 코드는 정확히 한 가지 일만 수행합니다. 모델에게 질문하고, 응답에서 도구 호출(tool calls)을 수집하며, 이를 실행한 다음, 그 결과를 사용자 역할(user role) 아래의 히스토리(history)에 다시 넣는 과정을 반복하여, 모델이 더 이상 도구를 요청하지 않을 때까지 계속하는 것입니다. '생각하기(Thinking)'는 어시스턴트 메시지(assistant message)이며, '행동하기(Acting)'는 ToolUse 블록이고, '관찰하기(Observing)'는 백필드된(backfilled) ToolResult입니다. 이는 ReAct의 세 가지 단계가 메시지 구조에 단어 그대로 매핑된 것입니다. 두 가지 세부 사항은 다시 살펴볼 가치가 있습니다: 도구 결과는 `role:
네 가지 값은 루프 종료의 전체 이야기를 들려줍니다: 모델이 말하는 것을 마쳤거나, 단계가 소진되었거나 (max_steps는 기본값 50), 오류가 발생했거나, 사용자가 중단했습니다. '사용자 중단'은 단순히 '오류'에 포함되지 않고 별도의 변형을 갖게 되는데, 이는 인터페이스가 '오류' 대신 '취소됨(cancelled)'이라고 진실되게 말할 수 있도록 하기 위함입니다. 정직한 종료 조건과 정직한 오류 보고는 같은 미덕입니다.
생산적인 HostAgentExecutor의 단계 루프 골격(crates/agent-runtime/src/engine/host_executor.rs:2642)은 참조 구현과 일치하지만, 각 단계 전후에 대략 열 개의 가드레일이 걸려 있습니다: 취소 체크포인트, 시스템 프롬프트 스냅샷 새로고침, 압축(compaction), 용량 사전 점검(capacity preflight), LSP 진단 플러시, 루프 가드... 이 가드레일들이 이번 시리즈의 후반부 주인공들입니다. 지금은 무대 뒤에 머물러 있습니다.
여섯 가지 '가짜 에이전트': 사격 전에 목표 설정하기
산업적 사용에서 'Agent'라는 단어는 명백한 마케팅 인플레이션을 겪고 있습니다. 이 개념의 일반적인 오용 사례들을 테이블 위에 펼쳐놓으면, 그 이후 모든 논의의 경계가 훨씬 더 명확해집니다.
| 주장 | 실제 의미 | 한 문장으로 꿰뚫어 보기 |
|---|---|---|
| "자율 에이전트가 검색을 완료했다" | 단일 API 호출 | 상태를 유지하지 않고, 관찰에 기반하여 어떤 행동도 선택하지 않으며, 중지 조건이 없는 시스템은 에이전트가 아니다 |
| ... | ||
| One touchstone for telling the real from the fake: when the environment returns an unexpected result, can the system change its sequence of actions? If it can, it has earned the right to talk about loops; if it cannot, it is nothing more than a fill-in-the-blank exam with a temperature setting. The touchstone cuts both ways — against the things we build ourselves too. CodeSmith's loop guard (Article 10) exists precisely because the distance between a system that "can change its action sequence" and one "destined to repeat the same action" is a single guardrail. |
제거 연구(Ablation Study): 네 가지 컨텍스트 구성 요소가 동등하게 중요한 것은 아니다
누군가가 에이전트의 컨텍스트에 대해 체계적인 제거 연구를 수행했습니다. 이 연구는 전체 기준선(baseline)을 유지한 다음, 제어 변수들—도구 정의(tool definitions), 도구 실행 결과(tool execution results), 추론 과정(reasoning process), 메시지 기록(message history)(시스템 프롬프트는 정체성 정의이므로 제거하면 테스트를 실행하는 것 자체가 무의미해져서 분석 대상에서 제외됨)—중 하나씩을 제거했습니다. 이 결론들은 줄마다 암기할 가치가 있습니다:
- **도구 정의(Tool definitions)**는 행동할 수 있는 능력의 기반입니다. 이것이 제거되어도 모델은 침묵하지 않습니다. 여전히 깔끔하게 형식화되고 자신감 있는 어조의 답변을 제공하지만, 데이터가 관찰에 기반한 것처럼 보이는 것이 아니라 매개변수 메모리(parametric memory)에서 온 것입니다.
- **도구 결과(Tool results)**는 폐쇄 루프 제어(closed-loop control)의 핵심입니다. 이것 없이는 에이전트(Agent)가 "눈을 가린 채" 실행하며, 반복 예산이 소진될 때까지 계속 시도합니다.
- **추론 과정(The reasoning process)**은 "왜 이것이 수행되었는지"를 기록하는 반면, 도구 결과는 "무슨 일이 일어났는지"를 기록합니다. 전자가 후자로부터 재구성될 수 있을 때, 사고 과정을 역사에서 제거하는 비용은 거의 들지 않습니다 (이는 Article 11의 압축 기능이 작동할 수 있는 근거입니다).
- **메시지 기록(Message history)**은 중복 작업을 방지하고 같은 실수를 두 번 반복하는 것을 막아줍니다.
실험의 핵심 통찰은 한 문장으로 요약됩니다: 컨텍스트가 에이전트가 볼 수 있는 것을 결정하며, 에이전트는 자신이 보는 것만을 바탕으로 결정을 내릴 수 있습니다. 그리고 이 구성 요소들은 동등하지 않습니다. 측정 기준은 특정 구성 요소가 담고 있는 정보가 다른 곳에서 재구성될 수 있는지 여부입니다. 엔지니어링 관행에 더욱 중요한 또 다른 발견이 있습니다: 손상된 컨텍스트 하에서의 일반적인 실패는 오류 종료(error exit)가 아니라 완벽해 보이는 답변입니다. "답변을 생성했다"는 것이 곧 "과제를 완료했다"를 의미하지 않습니다. Article 12부터 Article 15까지의 모든 컨텍스트 엔지니어링, 그리고 개선 계획이 교과서에 비추어 자체 감사하는 체크리스트는 이 토대 위에 서 있습니다.
ACI: 능력은 모델과 인터페이스의 공동 산물이다
SWE-agent는 중대한 발견을 했습니다. 동일한 기반 모델(foundation model)이 일반 셸 인터페이스를 사용하는 코드베이스 수정 작업과 목적에 맞게 설계된 에이전트-컴퓨터 인터페이스(Agent-Computer Interface, ACI) 하에서 수행될 때 완전히 다른 성능을 보인다는 것입니다. 파일 내용을 어떻게 제시하는지, 편집 명령어를 어떻게 형식화하는지, 오류 메시지를 어떻게 반환하는지 등 이 모든 것이 모델이 코드를 효과적으로 작동시킬 수 있는지에 영향을 미칩니다. 다르게 말하자면, 에이전트의 역량은 단순히 모델 가중치(model weights)만의 속성이 아니라, 모델과 환경 인터페이스가 결합된 산물입니다.
이는 학술적인 관점에서 서론에서 언급한 "차이는 하네스(harness)"라는 개념을 풀어낸 것입니다. 이 하네스를 분해하면 세 가지 구성 요소 그룹이 나타납니다:
- 인지 인터페이스 (The cognitive interface) — 시스템 프롬프트가 역할을 고정하고 출력 형식을 결정하며, 도구 스키마(tool schemas)는 호출할 수 있는 것을 정의하고, ACI는 결과가 어떻게 돌아올지 결정합니다. 이들이 함께 모델이 무엇을 볼 수 있고 무엇을 할 수 있는지 결정합니다;
- 실행 환경 (The execution environment) — 파일 시스템, 컨텍스트 구성, 권한 샌드박스(permission sandbox) 등이 포함됩니다. 문제가 발생했을 때 무슨 일이 일어날지를 결정합니다;
- 감사 및 제약 (Audit and constraint) — 궤적 로깅(trajectory logging), 예산 및 중지 메커니즘, 평가자(evaluators), 인간 개입(human takeover) 등이 포함됩니다. 행동이 추적 가능하고 경계 내에 머물도록 보장합니다.
이 세 가지 그룹을 CodeSmith의 21개 크레이트(crates)에 적용해 보면 깔끔하게 정렬된 순서로 배치되어 있음을 알 수 있습니다: agent-runtime의 Constitution 및 프롬프트와 tool-impls의 50가지 도구들은 인지 인터페이스에 속하며; agent/providers의 메시지 기록과 샌드박스는 실행 환경에 속하고; execpolicy의 명령어 검토, side-git 스냅샷, 루프 가드는 감사 및 제약에 속합니다. 이 시리즈가 앞으로 다룰 모든 설치본은 이 세 그룹 중 한 블록에 관한 것입니다.
자율성의 다섯 단계와 루프의 다섯 형태
"자율성(Autonomy)"은 가지고 있거나 없거나 하는 이분법적 속성이 아니라 연속적인 변수이며, 최소 다섯 가지 수준으로 나눌 수 있습니다. 레벨 1: 개발자가 각 행동 단계를 순차적으로 지정하고 모델은 텍스트를 채우기만 합니다. 레벨 2: 모델이 주어진 도구 세트(tool set)에서 행동을 선택합니다. 이는 오늘날 대부분의 에이전트 시스템의 기본 모드입니다. 레벨 3: 환경으로부터 예상치 못한 결과가 돌아왔을 때, 모델은 계획을 수정하고 원래 경로를 포기할 수 있습니다. 레벨 4: 모델 스스로 하위 목표(subgoals)를 제안하고 이를 분해할 수 있습니다. 레벨 5: 모델이 작업의 목표와 평가 기준 자체를 검토합니다. 즉, "이 작업 자체가 할 가치가 있는가?"입니다. 앞의 네 가지 레벨은 "작업을 어떻게 완료할 것인가"에 답하는 반면, 다섯 번째 레벨은 "작업 자체가 서 있는지 여부"라는 질문으로 나아갑니다.
이러한 척도로 볼 때, CodeSmith는 레벨 2와 레벨 3 사이에 존재합니다. 모델이 도구와 인자(arguments)를 자유롭게 선택할 수 있으며 (레벨 2), 루프 가드(loop guard)와 용량 제어기(capacity controller)가 경로가 이탈하는 경우 VerifyAndReplan을 강제하여 초기화할 수 있습니다 (수동적인 레벨 3). 레벨 5는 현재로서는 어떤 프로덕션 시스템에게도 사치품입니다.
루프 자체 또한 여러 변형 계열로 진화했습니다. 무엇이 저장되는지, 무엇이 읽히는지, 그리고 다음 라운드를 트리거하는지에 대한 세 가지 차원을 따라 최소 다섯 가지의 구별 가능한 형태가 존재합니다:
| Form | What it saves | Echo in this series |
|---|---|---|
| ReAct (reactive) | 단계 간에 추가 메모리 유지 안 함 | 위의 참조 구현(reference implementation) |
| ... |
다섯 가지 형태 사이의 차이점은 본질적으로 기억 전략의 차이입니다: ReAct는 "기억 없음", Reflexion은 "실패했을 때만 기억", Voyager는 "성공했을 때만 기억", MemGPT는 "메모리 자체가 페이징(paging)되어야 함"입니다. Article 8에서는 CodeSmith의 VarHandle과 MemGPT를 연결하는 혈통을 볼 수 있습니다. 그 논문의 비유적 선택은 운영체제의 가상 메모리였습니다.
멀티 에이전트: 차가운 물 한 양동이
어떤 작업이 한 에이전트에서 다른 에이전트로 넘어갈 때, 위임(delegation)은 명시적으로 계약되어야 합니다. 그렇지 않으면 어떤 경계 지점에서든 실패할 것입니다. 완전한 위임 계약에는 최소한 여덟 가지 조항이 필요합니다: 목표(objective), 도구 권한(tool permissions), 금지된 행동(forbidden actions), 자원 상한선(resource ceilings), 중단 조건(abort conditions), 출력 형식(output format), 책임 범위(lines of responsibility), 그리고 환경이 변경될 때를 위한 재협상 메커니즘(renegotiation mechanism)입니다. 만약 자원 상한선을 생략하면 위임 에이전트가 끝없이 컴퓨팅 자원을 소모할 수 있고, 중단 조건을 생략하면 목표에서 벗어난 하위 작업은 단순히 쓸모없는 결과를 계속 생성하게 될 것입니다.
다중 에이전트 구현에 대해 너무 낙관적인 분들에게 차가운 물 세 바가지를 끼얹고 싶습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기