본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 15. 15:36

AI 에이전트의 다단계 워크플로우를 '하나의 YAML'로 선언적으로 실행하기 — flowsmith 설계

요약

AI 에이전트의 다단계 워크플로우를 YAML로 선언적으로 제어하는 flowsmith 엔진의 설계 원칙을 다룹니다. 세션 중단 대응, LLM 판정의 불확실성 해결, 비용 효율적인 기계 검사 도입을 통해 실행 인프라의 안정성을 확보하는 방법을 제시합니다.

핵심 포인트

  • YAML을 통한 선언적 워크플로우 제어로 실행 예측 가능성 확보
  • LLM의 주관적 판단 대신 구조화된 데이터와 기계적 규칙으로 합격 여부 결정
  • 상태(State)를 디스크에 실시간 기록하여 중단 시에도 이어서 실행 가능
  • 비용 절감을 위해 기계 검사(Mechanical Gate)를 LLM 리뷰 전 단계에 배치

AI에게 긴 작업을 시키면 매번 똑같은 3가지 벽에 부딪힌다

실무에서 AI 에이전트에게 다단계 작업(조사·생성·리뷰·보고와 같은 흐름)을 시키면,

대체로 똑같은 지점에서 넘어집니다.

세션이 끊기면 전부 다시 해야 한다. 200건의 반복 처리 중 150건째에서 끊겼다면, 150건을 버릴 것인가? -
'완료했습니다'를 신뢰할 수 없다. LLM의 자기 보고식 합격 여부는 맞을 때도 있고 틀릴 때도 있다. -
어디에 얼마가 들었는지 알 수 없다. 끝나고 보니 토큰이 녹아 있다. -

이 세 가지는 "프롬프트를 고민하는 것"만으로는 해결되지 않습니다. 실행 기반(Execution Infrastructure)의 설계 문제입니다.

저는 레거시 마이그레이션 현장에서 AI 워크플로우를 운용해 온 경험을 바탕으로,

이 세 가지에 대한 설계 해답으로서 flowsmith라는

Claude Code용 워크플로우 엔진을 처음부터 설계 및 구현했습니다.

이 기사는 그 설계 판단의 기록입니다.

해결 방향: 제어는 선언으로, 지능은 스텝 안에

ReAct와 같이 "LLM이 다음 수를 생각하며 나아가는" 방식은 유연하지만,

같은 요청을 두 번 실행하면 다른 경로를 통과한다는 근본적인 불안정성이 있습니다.

flowsmith는 반대로 방향을 잡았습니다.

steps:
- id: survey # 만들기 (make)
- id: review # 만들고 검사, 불합격 시 되돌리기 (make-check)
...

경로·합격 여부·라우팅(Routing)은 인간이 YAML로 선언하고, 엔진이 기계적으로 실행한다 -
각 스텝의 내용(코드를 읽기, 표를 작성하기, 리뷰하기)만 LLM의 subagent가 수행한다

"예측 가능성 > 자유도". 에이전트의 지능은 스텝 내부에 가두고,

플로우 제어(Flow Control)에는 일절 사용하지 않겠다는 결단입니다.

설계 판단 1: 합격 여부를 LLM에게 결정하게 하지 않는다

가장 중요한 판단입니다. 리뷰 담당(checker)에게는 다음과 같이 반환하게 합니다:

verdicts:
- {id: V01, severity: high, verdict: pass}
- {id: V02, severity: mid, verdict: fail}

checker가 반환하는 것은 관점별로 구조화된 판정뿐입니다.

"합격입니다/불합격입니다"라는 종합 결론은 반환하게 하더라도 사용하지 않습니다.

엔진이 pass_when: no_high_fail과 같은 규칙을 verdicts에 기계적으로 적용하여 합격 여부를 결정합니다.

나아가 그 직전에 **기계 게이트(Mechanical Gate)**를 둡니다. 파일이 존재하는지, 필수 헤더가 있는지,

표의 행 수가 상류의 건수와 일치하는지——이러한 결정적으로 판정할 수 있는 검사를 먼저 실행하여,

탈락하면 LLM 리뷰로 넘어가지 않게 합니다. 기계로 떨어뜨릴 수 있는 것을 LLM에게 넘기지 않습니다.

LLM 리뷰는 비용이 높고 무작위적이지만, 기계 검사는 비용이 들지 않고 확실하기 때문입니다.

설계 판단 2: 상태는 모두 디스크에 둔다

세션은 언제든 끊겨도 좋다는 전제로 설계합니다.

${OUT}/.flowsmith/
├── state.json # 어느 스텝·어느 행까지 끝났는지
├── run.jsonl # 스트림 로그 (소요 시간·토큰 포함)
...

state.json은 스텝 완료 시마다·반복의 1행 완료 시마다 업데이트합니다.

재실행하면 엔진은 state를 감지하여 완료된 부분을 스킵하고, 이어서 실행합니다.

반복 처리를 행 단위로 기록하는 것이 포인트이며, 150/200건에서 끊겨도 남은 50건만 재개할 수 있습니다.

설계 판단 3: 막히면 멈춘다 (seized)

결과를 해석할 수 없거나, 재시도 상한을 초과하거나, 라우팅 대상이 부적절할 때——

이런 경우 엔진은 추측해서 진행하지 않고 정지합니다. 상태명은 seized (고착)입니다.

정지 시에는 "어떤 관점이·몇 번·어떤 근거로 통과하지 못했는지, 인간의 선택지는 무엇인지"라는

막힘 요약(Summary)을 출력하여 인간에게 판정을 넘깁니다.

묵묵히 잘못된 길로 나아가는 비용은, 멈추는 비용의 백 배가 듭니다.

인간과 AI의 접점은 암묵적으로 두지 않고 명시적으로 설계한다——이것이 전체를 관통하는 방침입니다.

같은 이유로 리뷰·반복·되돌아가기 점프에는 모두 상한이 있으며, 초과 시 반드시 seized 상태가 됩니다.

"언제까지나 계속 돌고 도는" 상태는 구조적으로 존재할 수 없습니다.

실험에서 나온 가장 재미있는 버그: 되돌리기의 오독

branch 단계에는 carry라는 메커니즘이 있습니다. 판정자(arbiter)가 "no"라고 말한 이유를,

되돌리기(rollback) 대상 단계로 전달하는 기능입니다.

초기 구현에서는 이유 문구를 "이전 판정 이유를 바탕으로 해주세요"라는 중립적인 문구로 전달했습니다.

그러자 어떤 일이 벌어졌을까요——

작업자(maker)가 불합격 이유를 '있어야 할 모습에 대한 설명'으로 오독하여,
"이미 요구사항을 충족하고 있습니다. 변경이 필요 없습니다"라며 변경 없이 그대로 반환한 것입니다.

불합격 이유에는 "라이선스에 대한 언급이 없음"이라고 적혀 있습니다. maker는 이를 읽고

"라이선스에 언급해야 하는구나, (내 결과물은) 괜찮구나"라고 해석했습니다.

사람 신입 사원에게서도 일어날 법한 오독입니다.

수정 방법은 문구를 명시화하는 것이었습니다:

## ⚠ 되돌리기 (이전 결과물은 판정에서 불합격됨)
위의 이유는 "현상에 대한 설명"이 아니라 "해결해야 할 부족함"이다.
"변경 불필요"라는 결론은 금지한다.

이렇게 작성하고 나서야 비로소 확실하게 고쳐지기 시작했습니다.

멀티 에이전트(multi-agent)의 결합부에서는 정보의 '내용'뿐만 아니라 '프레이밍(framing)'이 동작을 결정한다——

이것이 이번 실험을 통해 얻은 가장 큰 교훈입니다.

실부하 검증: 레거시 조사 15단계를 완주

설계가 책상 위에서 끝나지 않음을 확인하기 위해, 실제 유스케이스(use case)로 검증했습니다.

migration-factory의 레거시 현황 조사 플로우

(15단계: 자산 인벤토리 작성 → 분류 → 의존성 추출 → 영향 분석 → 검수 게이트 → 2층 보고서)를,

JPetStore 6(160개 파일 / 15,007행)를 대상으로 실행했습니다.

사전 문맥 없는 신규 세션에서 전 15단계 완주 (약 35분) 도중 2회 중단 → 2회 모두 중단 지점부터 재개-
되돌리기가 실제로 발동: 영향 분석과 보고서 2단계가 round 1에서 불합격 →

지적 사항과 함께 재실행 → round 2 합격 - 보고서의 제목이 규정과 단 한 글자 달랐다는 이유만으로 기계 게이트가 되돌리기를 수행한 장면도 있었습니다.

(너무 엄격하다는 느낌도 들지만, "기계는 엄격하다"는 쪽에 무게를 두는 것이 안전합니다.)

출력물 일체(CSV 17개 · 보고서 · 전체 리뷰 표 · 실행 로그)는 그대로

공개하고 있습니다.

되돌리기가 발생한 경위도 로그와 리뷰 표를 통해 추적할 수 있습니다.

그리고 이 실부하 검증은, 엔진 자체의 결함도 하나 찾아냈습니다.

공개된 실행 로그를 보면, 시각이 분 단위로 딱 떨어지는 값뿐이고 토큰 수(token count) 칸도 없습니다.

프롬프트로 구현된 엔진(엔진 자체가 Claude)은 정확한 시간 측정 및 토큰 집계라는

"기계적인 기록"을 확실하게 수행할 수 없습니다. ——이는 "기계로 판정할 수 있는 것을 LLM에게 시키지 마라"라는

본 기사의 주장이 엔진 자신에게도 해당함을 의미합니다. 결정적인 부분(YAML 해석 · 상태 관리 ·

게이트 판정 · 로그 기록)을 코드로 대체하려는 계획의 가장 설득력 있는 근거가,

자신의 로그가 부정확했다는 사실이 된 셈입니다.

요약: 엔진은 "똑똑하게" 만들지 않는다

flowsmith의 설계를 한마디로 말하면 "엔진을 의도적으로 우직하게 만드는 것"입니다.

  • 경로는 YAML 선언대로만 움직인다
  • 합격 여부는 구조화된 데이터에 기계적으로 규칙을 적용할 뿐이다
  • 망설여지면 멈추고 인간에게 돌려준다
  • 지능이 필요한 작업만을 단계 내부의 LLM에게 맡긴다

AI 에이전트의 신뢰성은 에이전트 자신의 똑똑함이 아니라,

똑똑함을 사용하는 곳을 한정하는 설계에서 온다는 것이 직접 만들어보며 느낀 점입니다.

리포지토리:

다음에는 체크포인트 재개 설계(state.json의 업데이트 타이밍과 "작업 중인 미완성 결과물"의

취급)를 깊이 있게 다룰 예정입니다.

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0