Skip to content

코딩 에이전트 밖으로 나가고 싶었다 ​

처음부터 범용 AI 런타임을 만들 생각은 없었다.

그냥 코딩 에이전트를 쓰다 보니 이상한 생각이 들었다.

요즘 코딩 에이전트들은 꽤 잘 움직인다.

파일을 읽고, 검색하고, 도구를 호출하고, 중간에 실패하면 다시 시도하고, 긴 작업은 여러 번의 턴으로 이어간다.

처음에는 당연히 이것들이 전부 "코딩 에이전트의 기능"이라고 생각했다.

그런데 계속 보다 보니 꼭 코딩에만 필요한 기능은 아니었다.

text
대화를 이어간다
필요한 정보를 찾는다
도구를 고른다
도구를 실행한다
결과를 다시 문맥에 넣는다
길어진 문맥을 줄인다
실패하면 상태를 남긴다
다음 요청에서 이어간다

파일 편집과 터미널 실행을 빼면, 일반적인 업무를 처리하는 AI에도 거의 그대로 필요한 것들이었다.

그때부터 조금 이상해졌다.

업무 자동화를 만들 때마다 비슷한 걸 다시 만들고 있었다 ​

한동안 AI를 붙인 작은 프로젝트를 계속 만들었다.

Slack 메시지를 읽는 것, 이슈를 정리하는 것, 문서를 갱신하는 것, 웹을 탐색해서 무언가를 처리하는 것.

겉으로는 전부 다른 프로젝트였다.

그런데 안쪽에서 반복되는 코드는 비슷했다.

text
사용자 입력
  ↓
프롬프트 구성
  ↓
모델 호출
  ↓
도구 실행
  ↓
결과 저장
  ↓
다음 호출

처음 몇 번은 그냥 프로젝트마다 만들었다.

어차피 작은 기능이고, 당장 돌아가는 게 중요했다.

문제는 기능이 조금만 길어지면 생겼다.

대화가 길어지면 토큰이 넘쳤다.

서버가 재시작되면 작업 상태가 사라졌다.

같은 요청이 다시 들어오면 도구가 두 번 실행됐다.

브라우저가 연결을 끊었는데 모델 호출은 계속 돌았다.

메모리와 현재 세션의 경계도 애매했다.

결국 프로젝트마다 비슷한 문제를 다른 방식으로 다시 고치고 있었다.

그때부터 "도메인 로직보다 아래에 하나가 더 있어야 하는 것 아닌가"라는 생각을 하기 시작했다.

처음에는 거대한 실행 엔진을 만들려고 했다 ​

여기서 첫 번째 삽질을 했다.

AI가 외부 시스템을 조작하려면 실행을 아주 엄격하게 통제해야 한다고 생각했다.

그래서 요청, 정책, 권한, 실행, 관측, 검증, 영수증 같은 단계를 최대한 명확히 나눴다.

개념적으로는 마음에 들었다.

text
요청
  ↓
정책 확인
  ↓
권한 부여
  ↓
실행
  ↓
관측
  ↓
검증
  ↓
결과 기록

문제는 실제 업무를 하나 붙여보면서 드러났다.

실행을 안전하게 만드는 것만으로는 AI가 일을 하지 못했다.

무엇을 해야 하는지 판단하고, 처음 보는 화면을 탐색하고, 필요한 파라미터를 찾아내고, 실패하면 다른 방법을 선택하는 쪽이 훨씬 어려웠다.

실행기는 있었는데 뇌가 없었다.

반대로 뇌를 붙이려고 하니 실행기와 에이전트 사이에 계속 새로운 계약이 필요했다.

작은 업무 하나를 붙이는데 구조를 설명하는 코드가 실제 업무 코드보다 많아졌다.

결국 중요한 걸 하나 배웠다.

안전한 실행 경계와 AI가 일을 이어가는 런타임은 같은 문제가 아니었다.

둘 다 필요할 수 있지만 한 덩어리로 만들면 너무 무거워졌다.

잘 돌아가는 코딩 에이전트를 다시 봤다 ​

복잡한 구조를 한참 만들고 나서 다시 코딩 에이전트를 봤다.

재미있는 건 내가 필요하다고 느낀 것들이 이미 그 안에서 꽤 자연스럽게 돌아가고 있었다는 점이다.

코딩 에이전트는 매번 거대한 워크플로 그래프를 먼저 그리지 않는다.

대체로 이런 반복을 가진다.

text
현재 상태를 본다
  ↓
모델이 다음 행동을 결정한다
  ↓
도구를 실행한다
  ↓
결과를 다시 본다
  ↓
필요하면 반복한다

그리고 그 주위에 세션, 컨텍스트 관리, 도구 권한, 취소, 스트리밍, 기록 같은 기능이 붙어 있다.

그제야 질문이 바뀌었다.

"코딩 에이전트를 일반 업무에 쓸 수 있을까?"가 아니라,

"코딩에 종속된 부분을 걷어내면 무엇이 남을까?"

이 질문이 훨씬 단순했다.

코딩을 빼기 시작했다 ​

파일 시스템, 코드 수정, 터미널 같은 기능을 중심에 두지 않기로 했다.

대신 어떤 업무에서도 필요할 것 같은 것만 남겨봤다.

text
Session
Context
Memory
Search
Tool Loop
Persistence
Streaming
Cancellation
Idempotency
Telemetry

처음에는 목록이 너무 평범해 보여서 조금 허무했다.

이런 기능은 새로운 것도 아니고, 이름만 보면 이미 수많은 프레임워크에 있을 법했다.

그런데 직접 하나의 실행 흐름으로 묶으려니 생각보다 문제가 많았다.

세션은 언제 저장해야 하는가.

모델 호출 직전에 프로세스가 죽으면 어떻게 되는가.

도구는 성공했는데 세션 저장이 실패하면 어떻게 되는가.

같은 요청이 재전송되면 모델부터 다시 실행해야 하는가.

컨텍스트가 넘치면 자동으로 줄일 것인가, 사용자에게 물을 것인가.

두 요청이 같은 세션을 동시에 수정하면 누가 이겨야 하는가.

브라우저가 SSE 연결을 끊으면 내부 실행도 취소해야 하는가.

여기부터는 더 이상 "LLM API 한 번 호출하기"의 문제가 아니었다.

AX에서 모델보다 아래쪽이 생각보다 컸다 ​

처음 AI 기능을 만들 때 가장 눈에 띄는 건 모델이다.

어떤 모델을 쓸지, 프롬프트를 어떻게 쓸지, RAG를 어떻게 붙일지부터 생각하게 된다.

그런데 실제 업무로 가져가려고 하니 모델 호출 바깥쪽 코드가 점점 커졌다.

text
업무 제품
   ↓
도메인 규칙
   ↓
AI 실행 생명주기
   ↓
모델 / 검색 / 외부 도구

내가 계속 프로젝트마다 다시 만들던 것은 세 번째 층이었다.

이걸 늦게 알아챘다.

그래서 한동안은 "왜 이렇게 간단한 AI 기능 하나 붙이는데 코드가 많지?"라고 생각했다.

지금 보면 당연했다.

나는 챗봇을 만들고 있던 게 아니라, 상태가 있는 작업 실행기를 프로젝트마다 조금씩 다시 만들고 있었다.

아직 결론은 아니다 ​

이걸 직접 만드는 게 최선인지는 아직 모르겠다.

비슷한 역할을 하는 오픈소스와 프레임워크가 이미 많다.

아마 내가 몰랐던 기능도 상당히 많을 것이다.

그래도 한 번 직접 파본 건 의미가 있었다.

이제는 프레임워크 문서에서 session, checkpoint, memory, tool, resume, durable execution 같은 단어를 볼 때 예전과 다르게 보인다.

기능 이름이 아니라 그 기능이 왜 필요한지 먼저 보게 됐다.

다음 삽질은 이 평범해 보이는 기능들을 하나의 실행 흐름으로 묶으면서 시작됐다.

세션 하나 저장하는 것도 생각보다 간단하지 않았다.

마지막 수정:

Boundary · Execution · Verification · Operation