사람들이 에이전트라고 부르는 것 대부분은 중간에 한두 번의 모델 호출이 낀 고정된 단계의 나열이다. 제어 흐름은 사전에 알 수 있는데도 팀들은 어쨌든 이를 모델에 넘기고, 비결정적인 루프를 다스리느라 몇 주를 쓴다. 작업의 형태를 이미 알고 있다면 오케스트레이터는 결정적이어야 하며, AWS Step Functions가 직접 짠 에이전트 루프보다 이 작업에 더 적합하다. Step Functions는 재시도, 타임아웃, 오류 처리, 병렬성, 사람의 승인을 이미 해결해 두었다. 바로 에이전트 프레임워크가 우리에게 다시 구현하라고 요구하는 것들이다.

내면화할 가치가 있는 재구성은 다음과 같다. 결정은 모델이 하게 하고, 배관은 그렇게 하지 마라. 진짜로 판단이 필요한 단계, 즉 추출, 분류, 초안 작성에는 모델을 쓰고, 무엇이 어떤 순서로 실행되고 어떤 단계가 실패했을 때 무슨 일이 일어나는지는 상태 머신이 결정하게 하라. 3단계가 2단계 다음에 온다는 것을 아는 데 LLM은 필요 없다.

결정적 오케스트레이션은 한계가 아니라 기능이다

직접 만든 에이전트 루프는 모델을 제어 흐름의 책임자로 둔다. 어떤 도구를 호출할지 결정하고, 결과를 읽고, 다음 도구를 결정하고, 스스로 끝났다고 판단할 때까지 이를 반복한다. 경로가 작성 시점에 정말로 알려져 있지 않을 때는 이것이 옳은 설계다. 하지만 경로가 화이트보드에 그릴 수 있는 워크플로일 때는 잘못된 설계다. 왜냐하면 이제 실행마다 다른 경로를 탈 수 있고, 재시도는 임기응변이 되며, 중간에 실패하면 재구성해야 할 상태가 남기 때문이다.

Step Functions는 이를 뒤집는다. 상태와 전이를 선언한다. 판단이 필요한 각 상태는 (직접 또는 Lambda를 통해) Bedrock을 호출하고, 그렇지 않은 각 상태는 평범한 로직이다. 실행은 재현 가능하고, 모든 전이가 로깅되며, 실행 이력이 내장된 감사 추적이 된다. 형태가 정해진 워크플로에는 결정성이야말로 우리가 원하는 것이다.

직접 작성하지 않아도 되는 재시도와 오류 처리

에이전트 신뢰성 작업의 화려하지 않은 대부분은 재시도 정책이다. 모델 호출이 스로틀되고, 도구가 타임아웃되고, 다운스트림 API가 일시적인 500을 반환한다. 직접 만든 루프에서는 백오프, 지터, 오류별 처리를 손으로 작성해야 하며, 대개 미묘하게 틀리게 된다.

Step Functions는 이것을 선언적으로 만든다. 각 상태는 자신만의 재시도 및 캐치 동작을 갖는다.

"InvokeModel": {
  "Type": "Task",
  "Resource": "arn:aws:states:::bedrock:invokeModel",
  "Retry": [{
    "ErrorEquals": ["Bedrock.ThrottlingException"],
    "IntervalSeconds": 2,
    "BackoffRate": 2.0,
    "MaxAttempts": 5
  }],
  "Catch": [{
    "ErrorEquals": ["States.ALL"],
    "Next": "HandleFailure"
  }]
}

스로틀링은 지수 백오프를 받고, 그 외의 오류는 우리가 통제하는 실패 상태로 라우팅된다. 재시도 코드도 없고, 놓치는 예외도 없으며, 그 동작은 루프 안에 숨어 있지 않고 정의 안에서 보인다.

휴먼인더루프는 일급 상태다

직접 만든 에이전트가 가장 아픈 지점은 사람을 위해 멈추는 부분이다. 돈을 쓰거나, 이메일을 보내거나, 프로덕션을 바꿀 수 있는 에이전트는 멈추고 승인을 기다려야 하는데, 오래 실행되는 프로세스에서 이를 처리하려면 사람이 몇 시간이나 며칠에 걸쳐 응답할 때까지 어딘가에 상태를 붙들고 있어야 한다.

Step Functions는 콜백 패턴을 통해 이를 내장하고 있다. .waitForTaskToken으로 시작한 태스크는 실행을 일시 정지하고, 토큰을 발급하며, 무언가가 그 토큰과 함께 SendTaskSuccessSendTaskFailure를 호출할 때까지 아무 일도 하지 않는다. 워크플로는 프로세스를 실행하지 않고도 승인이 걸리는 시간만큼 정지 상태로 있을 수 있고, 멈췄던 바로 그 지점에서 재개된다. 큐와 상태 저장소와 재개 메커니즘을 직접 만들지 않고도 내구성 있고 감사 가능한 사람 게이트를 얻는 것이다.

그래도 진짜 에이전트 루프가 필요할 때

이것은 에이전트 루프 자체에 반대하는 주장이 아니라, 제 값을 하는 곳에서 쓰라는 주장이다. 경로가 정말로 열려 있을 때, 즉 모델이 실행 시점에 여러 도구 중 어느 것을 어떤 순서로 쓸지 결정해야 하고 고정된 그래프로는 그것을 담아낼 수 없을 때는 모델 주도 루프를 써라. 열린 연구, 자유형 디버깅, 이전 단계가 무엇을 반환했는지에 따라 다음 단계가 열거할 수 없는 방식으로 달라지는 작업이 에이전트 루프의 진짜 서식지다.

함정은 이 패턴을 분기 하나 있는 5단계 파이프라인에 쓰는 것이다. 순서도를 그릴 수 있다면 순서도를 그대로 인코딩하라. 비결정적 루프는 정말로 비결정적인 작업을 위해 아껴 두어라.

결론

프로덕션 에이전트 작업의 상당 부분은 에이전트 옷을 입은 결정적 오케스트레이션이다. 그런 작업에는 Step Functions가 재시도, 오류 라우팅, 병렬성, 그리고 우리가 직접 만들고 유지해야 했을 내구성 있는 사람 승인 상태를 제공하며, 덤으로 감사 로그를 겸하는 실행 이력까지 준다. 판단이 필요한 호출은 모델이 하게 하고, 제어 흐름은 상태 머신이 소유하게 하라. 가장 신뢰할 수 있는 에이전트는 모델이 가장 적게 결정하는 에이전트인 경우가 많다.

이어서 읽으면 좋은 글

고위험 작업에 대한 승인 게이트를 포함해 같은 패턴을 인프라와 플랫폼 관점에서 다룬 글은 ercan.cloud의 클라우드 현장 노트에 있고, 허브는 ercanermis.com이다.