Engineering · AI Agents
결정론적 소프트웨어에서 확률론적 에이전트로. 수십 년간 몸에 밴 엔지니어링 습관이 새로운 현실과 충돌하는 다섯 가지 지점.
소프트웨어 엔지니어링은 지난 수십 년 동안 사실상 한 가지 목표를 향해 달려왔다. 모호함을 제거하는 일이다. 엄격한 인터페이스를 정의하고, 타입을 강제하고, “입력 A에 코드 B를 더하면 언제나 출력 C가 나온다”는 약속을 지키는 것. 좋은 엔지니어란 시스템에서 불확실성을 깎아내는 사람이었다.
그런데 AI(인공지능, Artificial Intelligence) 에이전트를 만드는 현장에서는 묘한 역설이 관찰된다. 경력이 짧은 주니어 엔지니어가 오히려 작동하는 에이전트를 더 빨리 내놓는 경우가 많다는 것이다. 구글 딥마인드에서 개발자 경험·개발자 관계 팀을 이끄는 스태프 엔지니어이자, 이전에 허깅페이스(Hugging Face)의 기술 리드였던 필립 슈미트는 그 이유를 이렇게 짚는다. 경력이 깊을수록 모델의 추론 능력과 지시 수행 능력을 덜 믿고, 확률적인 본성을 “코드로 눌러 없애려”다가 모델과 싸우게 된다는 것이다.
문제의 뿌리는 두 세계의 성질이 다르다는 데 있다. 전통적인 소프트웨어는 결정론적(deterministic)이다. 같은 입력이면 같은 출력이 나온다. 반면 에이전트 엔지니어링은 확률론적(probabilistic)이다. 같은 지시라도 매번 다른 경로를 밟을 수 있다.
비유 — 관제관에서 배차원으로
전통 소프트웨어에서 개발자는 교통 관제관이었다. 도로와 신호등과 법규를 모두 소유하고, 차량이 어디로 언제 갈지를 직접 통제한다.
에이전트에서 개발자는 배차원에 가깝다. 기사(LLM)에게 “런던으로 가라”는 목적지만 건넨다. 기차를 탈지 비행기를 탈지, 어떤 지름길로 갈지는 기사가 정한다. 때로는 “이쪽이 빠를 것 같아서” 인도로 달리기도 한다. 목표는 분명히 정하되, 거기에 닿는 단계 하나하나를 지정하지는 않는 방식이다.
Text is the New State
전통적인 엔지니어링에서는 세계를 데이터 구조로 모델링한다. 스키마를 정하고, 인터페이스를 규정하고, 엄격한 타입을 강제한다. 예측 가능하기 때문에 안전하게 느껴진다. 그래서 우리는 본능적으로 에이전트도 그 상자 안에 욱여넣으려 한다.
함정
현실의 의도·선호·설정은 좀처럼 참/거짓이나 정해진 칸에 들어맞지 않는다. 사용자의 입력은 띄엄띄엄한 칸이 아니라 연속적인 자연어다. 이를 불리언(boolean) 한 칸에 가두는 순간, 맥락이 잘려 나간다.
딥 리서치(Deep Research) 계획을 승인하는 상황을 떠올려 보자. 사용자가 이렇게 말한다. “계획 좋아 보이네요. 다만 미국 시장에 집중해 주세요.” 결정론적 시스템은 이 말을 is_approved: true/false라는 한 칸으로 욱여넣고, 그 순간 “미국 시장에 집중하라”는 정보를 통째로 버린다.
{
"plan_id": "123",
"status": "APPROVED"
// 미국 시장 의도는 증발
}
{
"plan_id": "123",
"text": "좋아요, 다만
미국 시장에 집중해 주세요"
}
텍스트를 그대로 남겨 두면, 뒤이어 일하는 에이전트가 “승인되었지만 미국 시장 중심으로”라는 뉘앙스를 읽고 행동을 스스로 조정할 수 있다. 사용자 선호도 마찬가지다. 결정론적 시스템은 is_celsius: true 한 칸에 가두지만, 에이전트는 “날씨는 섭씨로 보되 요리할 때는 화씨로”라는 문장을 그대로 저장한다. 그리고 작업의 종류에 따라 맥락을 알아서 전환한다.
정해진 칸의 안온함을 버리고, 의미 그 자체를 상태로 다루어야 한다.
Hand over Control
마이크로서비스에서는 사용자의 의도가 곧 경로와 맞물린다. “구독 해지”라는 의도는 POST /subscription/cancel이라는 길로 정확히 이어진다. 그러나 에이전트에는 자연어로 들어오는 단 하나의 입구가 있고, 그 안에서 두뇌(LLM) 역할을 하는 모델이 가진 도구와 입력과 지시를 종합해 흐름을 스스로 결정한다.
함정
흐름을 에이전트 안에 그대로 못 박으려 하지만, 사람의 상호작용은 직선으로 흐르지 않는다. 빙빙 돌고, 되짚고, 방향을 튼다. 해지하러 왔다가 재계약에 동의하고 돌아가기도 한다.
실제 대화는 이렇게 흐른다.
모든 예외 상황을 코드로 못 박으려 든다면 그것은 더 이상 AI 에이전트를 만드는 일이 아니다. 전체 맥락을 바탕으로 지금의 의도를 파악하도록 에이전트를 믿고 흐름을 맡겨야 한다. 물론 이 “신뢰”가 제멋대로 굴게 두라는 뜻은 아니다. 뒤에서 보겠지만, 핵심은 통제와 자율 사이의 중간 지대를 찾는 데 있다.
Errors are just Inputs
전통적인 소프트웨어에서 API(응용 프로그래밍 인터페이스, Application Programming Interface) 호출이 실패하거나 변수가 비면, 우리는 예외를 던져 프로그램을 즉시 멈춘다. 빨리 죽어야 빨리 고칠 수 있기 때문이다.
함정
에이전트는 한 번 도는 데 5분이 걸리고 0.5달러를 쓰기도 한다. 다섯 단계 중 네 번째가 입력 하나 잘못되어 실패했다고 전체 실행을 무너뜨리는 것은 받아들이기 어렵다. 이미 쌓인 맥락까지 함께 날아간다.
그래서 발상을 뒤집는다. 오류를 또 하나의 입력으로 취급한다. 곧장 멈추는 대신, 오류를 붙잡아 그 내용을 모델에게 되먹이고, 복구를 시도하며 앞으로 나아간다. 프로그래밍 언어 고(Go)가 함수의 반환을 “값이거나 오류”로 똑같이 다루듯, 에이전트도 오류를 정상적인 입력의 하나로 받아들인다.
From Unit Tests to Evals
테스트 주도 개발(TDD, Test-Driven Development)은 더 견고한 코드를 짜는 데 도움을 주었다. 하지만 에이전트는 단위 테스트(unit test)로 검증할 수 없다. 확률적인 시스템에서 “이진(binary) 정답”을 찾겠다고 몇 주를 허비하기 쉽다. 우리가 검증해야 할 것은 정해진 출력이 아니라 행동의 양상이다.
함정
창의나 추론이 필요한 과제에는 “맞다/틀리다”의 단정을 쓸 수 없다. “이 이메일을 요약하라”에는 무한히 많은 올바른 답이 있다. 모델을 가짜(mock)로 대체해 시험한다면, 그것은 에이전트를 시험하는 것이 아니라 문자열 잇기를 시험하는 셈이다.
그래서 테스트 대신 평가(eval)로 옮겨 간다. 추론은 단위 테스트의 대상이 아니다. 대신 신뢰도와 품질을 검증하고, 중간 과정을 추적한다.
50회 실행 가운데 45회 성공하고 품질 점수가 5점 만점에 4.5점이라면, 그 에이전트는 운영에 투입할 만하다. 우리는 변동을 없애는 것이 아니라 위험을 관리하는 것이다.
Agents Evolve, APIs Don't
과거의 API는 사람 개발자를 위해 설계되었다. 암묵적 맥락에 기대고, “깔끔한” 인터페이스를 추구했다. 사람은 맥락을 미루어 짐작한다. 에이전트는 그러지 못한다. 에이전트는 곧이곧대로 받아들이는 직역가다. 식별자(ID) 형식이 모호하면, 에이전트는 그럴듯한 값을 지어낸다.
함정
id라는 변수명은 우리에게는 당연히 “사용자 고유 식별자”로 읽힌다. 그러나 에이전트는 그 배경을 모른 채 이메일이나 이름을 id 자리에 넣으려 할 수 있다. 우리가 만든 “사람용 API”는 직역가에게는 함정이 된다.
그래서 에이전트용 도구는 장황할 만큼 명시적인 의미 타입과, “맥락” 역할을 하는 충실한 설명문(docstring)을 요구한다. email이 아니라 user_email_address처럼, 이름 자체가 의미를 말하게 한다.
한 가지 더, 에이전트는 즉석 적응이 가능하다. 보통의 API는 개발자에게 건넨 약속이다. 우리가 그 API에 기대는 코드를 커밋해 두고 떠나면, 누군가 get_user_by_id(id)를 get_user_by_email(email)로 바꾸는 순간 약속이 깨지고 모든 것이 즉시 무너진다. 반면 에이전트는 새 도구 정의를 읽고 스스로 거기에 맞춰 행동을 바꾼다.
결정론적 시스템에서 확률론적 에이전트로 넘어가는 일은 불편하다. 확실성을 의미의 유연함과 맞바꾸는 셈이기 때문이다. 우리는 더 이상 실행 경로를 알지도 소유하지도 못하고, 애플리케이션의 상태를 자연어 속에 담아 둔다. 엄격한 인터페이스로 훈련된 머리에는 이것이 어딘가 잘못된 것처럼 느껴진다.
그러나 에이전트를 결정론의 상자에 욱여넣으려는 시도는 에이전트를 쓰는 목적 자체를 무너뜨린다. 확률은 코드로 없앨 수 없다. 평가와 자기 교정으로 관리해야 한다. 동시에 “신뢰”가 제멋대로 굴게 두라는 뜻은 아니다. 모든 일을 에이전트에게 맡길 필요도 없다. 흐름이 정해져 있는 부분은 정해진 작업 흐름(workflow)으로 묶고, 판단이 필요한 부분만 에이전트에 맡기는 중간 지대를 찾는 것이 현실적인 답이다.
덧붙임 — 폐기를 전제로 짓기 (Build to Delete)
슈미트가 강연 끝에 꺼낸 “쓰라린 교훈(the bitter lesson)”은 강화학습 연구자 리처드 서튼(Richard Sutton)이 2019년에 정리한 통찰이다. 사람이 정교하게 손으로 짜 넣은 규칙보다, 일반적인 방법에 더 많은 연산을 부어 학습시킨 쪽이 결국 이긴다는 것이다.
에이전트에 적용하면 이렇게 된다. 오늘 공들여 만든 에이전트의 골격(harness)이, 몇 달 또는 몇 주 뒤 더 똑똑해진 모델 앞에서는 한 줄 프롬프트로 대체될 수 있다. 그러니 처음부터 버릴 수 있게, 모듈식으로 지어라. 소프트웨어가 일회용이 되어 간다는 사실을 받아들이는 편이 낫다.
에이전트는 앞으로도 예상치 못한 방식으로 자주 실패할 것이다. 그러나 방향은 분명하다. 모호함을 코드로 지워 없애려는 습관을 내려놓고, 그 모호함을 견뎌 낼 만큼 회복력 있는 시스템을 설계하는 쪽으로. 시니어의 본능이 “불확실성을 제거하라”고 말할 때, 에이전트 시대의 기술은 “불확실성을 관리하라”고 답한다.
출처 — 필립 슈미트(Philipp Schmid), “Why (Senior) Engineers Struggle to Build AI Agents”, philschmid.de(2025년 11월) 및 동명의 강연(구글 딥마인드). 리처드 서튼(Richard Sutton), “The Bitter Lesson”(2019).