내가 APX 루틴을 의도적으로 지루하게 유지하는 이유
요약
APX 루틴 설계 시 복잡한 에이전트 대신 의도적으로 단순하고 지루한 파이프라인을 구축해야 함을 강조합니다. 텍스트 생성, 오케스트레이션, 전달 단계를 분리하여 시스템의 신뢰성과 디버깅 용이성을 높이는 방법을 제안합니다.
핵심 포인트
- 루틴은 모든 것을 수행하는 에이전트가 아닌 작은 파이프라인이어야 함
- 텍스트 생성, 오케스트레이션, 전달 단계를 명확히 분리할 것
- 단순 텍스트 작업에는 도구 루프가 없는 exec_agent 사용 권장
- 작업을 세분화하면 실패 원인 파악과 테스트가 용이해짐
내가 APX 루틴을 의도적으로 지루하게 유지하는 이유
내가 반복적인 작업을 APX에 연결하기 시작했을 때, 소규모 자동화 시스템에서 계속해서 목격하게 되는 것과 똑같은 실수를 저질렀습니다. 바로 하나의 루틴(routine)에게 모든 것을 수행하도록 요구한 것입니다.
나는 모델이 텍스트를 초안 작성하고, 출력이 충분히 좋은지 결정하고, 그것을 게시하고, 어쩌면 다른 곳에 알림을 보내면서, 그 과정에서 지능적이라는 느낌까지 주기를 원했습니다. 그것은 멋지게 들렸습니다. 하지만 실제로 그것은 경계를 모호하게 만들었고 실패 원인을 파악하기 더 어렵게 만들었습니다.
교훈은 간단했습니다. 루틴은 의도적으로 지루해야 합니다. 텍스트 생성 (Text generation), 오케스트레이션 (orchestration), 그리고 전달 (delivery)은 서로 다른 작업입니다. 이들을 분리해 두면 APX를 더 신뢰하기 쉬워집니다.
이 아이디어는 내가 계속해서 되돌아오는 APC/APX 분리 개념과 일치합니다. APC는 리포지토리 (repo)에 내구성이 있는 프로젝트 컨텍스트 (context)를 보유합니다. APX는 로컬 런타임 (runtime) 작업을 처리합니다. 예약된 루틴은 동일한 경계를 존중해야 합니다. 한 부분은 프로젝트가 원하는 것을 설명하고, 다른 부분은 이 머신에서 그것을 실행하며, 마지막 쉘 (shell) 단계가 결과를 전달해야 합니다.
루틴의 실제 정체
APX 루틴은 예약된 작업 (scheduled tasks)입니다. 데몬 (daemon)은 5초마다 스케줄러 (scheduler)를 확인하고 기한이 된 모든 것을 실행합니다. 루틴은 kind, schedule, 선택 사항인 spec 블록, 그리고 모델 단계 전후의 선택 사항인 pre_commands 및 post_commands를 가집니다.
이러한 형태가 중요한 이유는 그것이 이미 나에게 올바른 멘탈 모델 (mental model)을 제시하기 때문입니다. 루틴은 "영원히 실행되는 에이전트 (agent)"가 아닙니다. 그것은 작은 파이프라인 (pipeline)입니다.
종류 (kinds)는 명시적입니다:
- 단순한 마커를 위한
heartbeat - 순수 쉘 작업을 위한
shell - 도구 (tools) 없이 한 번의 모델 호출을 위한
exec_agent - 도구를 사용한 다단계 작업을 위한
super_agent - 고정된 메시지를 위한
telegram
이 목록은 APX가 나에게 적합한 가장 작은 도구를 선택하기를 원한다는 첫 번째 힌트입니다. APX는 모든 반복 작업이 미니 자율 시스템 (mini-autonomous system)이 되는 것을 원하지 않습니다.
텍스트를 위한 올바른 기본값은 exec_agent이다
출력이 주로 산문 (prose)인 모든 경우에 대해, exec_agent가 보통 가장 깔끔한 선택입니다.
문서 설명은 직설적입니다: exec_agent는 프로젝트 에이전트 프롬프트 (agent prompt)를 로드하고, spec.prompt를 전송하며, 일반 텍스트 (plain text)를 반환합니다. 단 한 번의 호출입니다. 도구 루프 (tool loop)도 없고, 숨겨진 부작용 (side effects)도 없습니다.
이것이 바로 제가 반복적인 글쓰기 작업에서 원하는 바입니다.
매주 작성해야 하는 Dev.to 초안, 릴리스 노트 (release note), 짧은 상태 요약, 또는 텔레그램 (Telegram) 문구가 필요할 때, 루틴이 파일 시스템 (filesystem)을 탐색하고, MCP를 호출하며, 스스로와 협상할 필요는 없습니다. 제가 검토할 수 있는 한 단락을 작성하기만 하면 됩니다.
텍스트 전용 루틴은 테스트하기도 더 쉽습니다. 출력이 잘못되었다면, 문제는 프롬프트 (prompt)나 소스 입력 (source input)에 있다는 것을 알 수 있습니다. 모델이 사고를 마치기 전에 도구 호출 (tool call)이 상태를 변경했는지 여부를 물어볼 필요가 없습니다.
super_agent는 단순한 산문 (prose)이 아닌 오케스트레이션 (orchestration)을 위한 것입니다
super_agent도 쓰임새가 있습니다. 이 에이전트는 전체 도구 레지스트리 (tool registry)를 가져옵니다. 파일을 작성하고, MCP를 호출하며, 태스크 (task)를 생성하고, 실제 다단계 작업 (multi-step work)을 수행할 수 있습니다.
이러한 강력함은 루틴이 단순한 텍스트 생성을 넘어 여러 동작을 조정해야 할 때 유용합니다. 만약 제가 APX가 상태를 검사하고, 결정을 내린 다음, 그에 따라 행동하기를 원한다면 super_agent가 적합한 유형입니다.
하지만 필요하지 않다면 그런 복잡성 비용 (complexity tax)을 지불하고 싶지 않습니다.
루틴이 할 수 있는 일이 많아질수록, 저를 놀라게 할 방법도 많아집니다. 이는 오케스트레이션 작업에는 괜찮지만, 결과물의 형태를 이미 알고 있는 주간 포스팅에는 좋지 않습니다.
이 지점에서 저는 단순해 보이지만 시간을 아껴주는 규칙을 스스로에게 계속 상기시킵니다: 만약 작업이 "무언가를 쓰는 것"이라면, 가장 강력한 도구로 시작하지 마세요. 오직 쓰기만 할 수 있는 가장 작은 도구로 시작하세요.
전달 단계는 모델 외부에 머물러야 합니다
제가 발견한 가장 깔끔한 패턴은 다음과 같습니다:
exec_agent가 텍스트를 작성한다post_commands가 전달을 처리한다- 셸 (shell)이 결과를 게시하거나 전송한다
이러한 분리는 부작용 (side effects)을 가시적으로 유지해 줍니다.
게시 단계에서 실패한다면, 어디서 실패했는지 알 수 있습니다. 텍스트가 나쁘다면, 그것이 모델의 출력임을 알 수 있습니다. 전달 스크립트가 잘못되었다면, 모델이 버그의 원인이 아님을 알 수 있습니다.
APX는 이 파이프라인을 명확하게 문서화합니다. pre_commands는 모델을 위한 입력을 생성할 수 있습니다. post_commands는 $APX_LLM_OUTPUT, $APX_PRE_OUTPUT, 그리고 $APX_STATUS를 사용할 수 있습니다. 이는 핸드오프 (handoff)를 마법처럼 처리하는 대신 명시적으로 만들기 때문에 좋은 구조입니다.
또한 이는 제가 실제로 기계를 사용하고 싶은 방식과도 일치합니다.
매주 작성하는 개인적인 Dev.to 포스트를 위해, 저는 반복 가능한 흐름을 원합니다:
- 메모 수집
- 기사 초안 작성
- JSON 검증
- Dev.to에 게시
- Bluesky에 짧은 링크 게시
이것은 하나의 문제가 아닙니다. 다섯 개의 작은 문제입니다.
모델은 언어가 도움이 되는 부분만을 담당해야 합니다.
이중 전송(double-send)의 함정이 내게 규칙을 가르쳐 주었다
저는 이 교훈을 뼈저리게 느끼게 만든 한 가지 실패 사례를 겪었습니다.
만약 super_agent 루틴에 apx telegram send를 호출하는 post_commands 단계가 포함되어 있다면, 두 개의 메시지가 전송되는 결과가 발생할 수 있습니다. 하나는 에이전트 자체의 send_telegram 도구 호출(tool call)에서 오는 것이고, 다른 하나는 셸 (shell) 단계에서 오는 것입니다.
APX에는 해당 케이스에 대한 억제 휴리스틱 (suppression heuristic)이 있지만, 깔끔한 해결책은 여전히 동일합니다: 셸이 직접 수행할 수 있을 때 모델에게 전달 책임을 맡기지 마세요.
그것은 제가 매주 다시 발견하고 싶지 않은 종류의 버그입니다. 그것은 어려운 알고리즘 문제가 아닙니다. 경계 (boundary)의 문제입니다.
텍스트와 전달을 분리하고 나니, 루틴을 추론하기가 훨씬 쉬워졌습니다:
- 모델이 틀리더라도 부작용 (side effects)이 없습니다.
- 셸이 실패하더라도 초안을 손상시키지 않습니다.
- 최종 전달 단계가 로그에서 명확하게 보입니다.
이것은 지루합니다. 하지만 또한 좋은 것입니다.
자동화에서 왜 지루함이 영리함보다 나은가
예전에는 영리한 루틴을 만드는 것이 목표라고 생각했습니다.
이제는 설명하기 쉬운 루틴을 만드는 것이 목표라고 생각합니다.
지루한 자동화는 하나의 기능입니다. 왜냐하면 재시도 동작 (retry behavior)을 합리적으로 만들기 때문입니다. 출력을 검사 가능하게 만듭니다. 미래의 변경 사항을 국소적 (local)으로 유지합니다. 가장 중요한 것은, 무엇이 리포지토리 (repo)에 속하고 무엇이 기계 (machine)에 속하는지에 대해 런타임 (runtime)을 정직하게 유지한다는 점입니다.
이것이 다시 한번 APC/APX의 구조입니다:
- APC는 지속 가능한 프로젝트 계약 (durable project contract)을 유지합니다.
- APX는 로컬 런타임 상태 (local runtime state) 및 실행 (execution)을 유지합니다.
- 루틴 (routines)은 이러한 분리를 스케줄링 가능하고 반복 가능한 무언가로 전환합니다.
만약 모델이 이러한 경계선을 흐릿하게 만들도록 내버려 둔다면, 저는 보통 나중에 스스로 일을 더 만들게 됩니다. 경계를 엄격하게 유지하면, 실제로 유지 관리할 수 있는 시스템을 얻게 됩니다.
이것이 제가 가장 좋아하는 APX 루틴이 현재 가장 흥미롭지 않은 루틴이 된 이유입니다: exec_agent가 텍스트를 작성하고, shell이 이를 전송하며, 모델은 전체 파이프라인 (pipeline)을 자신이 소유하고 있는 척할 필요가 전혀 없습니다.
제가 현재 따르고 있는 실질적인 규칙
루틴을 설계할 때, 저는 세 가지 질문을 던집니다:
- 이 단계에 산문 (prose)만 필요한가? 그렇다면
exec_agent를 사용하십시오. - 이 단계에 도구 사용 (tool use) 또는 다단계 조정 (multi-step coordination)이 필요한가? 그렇다면
super_agent를 사용하십시오. - 이 단계에 결정론적 부수 효과 (deterministic side effect)만 필요한가? 그렇다면 shell에 그대로 두십시오.
그 규칙 덕분에 APX는 더 신뢰할 수 있고 덜 연극적으로 느껴지게 되었습니다.
이 시스템의 목적은 모든 작업이 자율적으로 느껴지게 만드는 것이 아닙니다. 목적은 작업의 올바른 부분은 자율적으로 만들고, 나머지는 명확하게 남겨두는 것입니다.
그것이 제가 신뢰하는 버전입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기