에이전트 워크플로우에서 아무도 읽지 않는 스위치들
요약
에이전트 워크플로우는 설정 스위치와 플래그가 과도하게 쌓여 관리의 어려움을 초래합니다. 본문은 실제로 시스템 동작에 영향을 주지 않는 '죽은 스위치(dead switches)'를 식별하는 방법을 제시하며, 이를 통해 불필요한 설정을 정리하고 워크플로우의 안정성을 높이는 것이 중요함을 강조합니다.
핵심 포인트
- 설정 파일의 모든 스위치가 실제로 사용되는 것은 아닐 수 있습니다.
- 죽은 스위치는 충돌 없이 조용히 존재하여 오도하는 위험이 있습니다.
- 사용되지 않는 설정을 찾으려면, 키 목록화 및 grep 검색을 통해 감사해야 합니다.
- 에이전트 워크플로우는 설정 표면의 과도한 성장이 문제입니다.
에이전트 워크플로우에서 아무도 읽지 않는 스위치들
대부분의 에이전트 워크플로우는 수많은 스위치를 쌓아갑니다. 여기서는 타임아웃(timeout) 설정이 있고, 저기에서는 특정 단계를 건너뛰는 플래그가 있으며, 바쁜 주간에 추가했던 다크 모드 선언 같은 것들이죠. 각각은 이유가 있어서 추가되었고, 이제 그 모든 것이 시스템의 동작 방식을 설명한다고 믿는 설정 파일의 일부가 되었습니다.
여기 불편한 질문이 있습니다: 그 스위치들 중 실제로 무언가를 변경하는 것은 몇 개나 될까요? '문서화되어 있느냐'거나 '검증기(validator)가 받아들이느냐'가 아니라, 정말로 읽는 것이 몇 개나 되는지 말입니다?
요약(TL;DR) — 스위치는 선언되고, 문서화되며, 스키마 검증을 거칠 수 있지만, 여전히 아무런 효과가 없을 수 있습니다. 왜냐하면 검증은 단지 선언이 잘 구성되었음을 증명할 뿐이지, 실제로 무언가가 그 값에 따라 동작한다는 것을 결코 증명하지 못하기 때문입니다. 이러한 '죽은 스위치(dead switches)'는 세 단계로 직접 찾을 수 있습니다: 모든 설정 키를 나열하고, 해당 키가 읽히는 지점을 grep으로 검색한 다음, 오직 검증기만이 읽는 키에 대해서는 명시적으로 연결할지 아니면 삭제할지 결정하는 것입니다. 저희가 자체 프로토콜에 이 방법을 적용해 본 결과, 저희의
실패 모드는 아무것도 실패하지 않기 때문에 조용합니다. 작동하지 않는 스위치는 충돌하거나 경고를 보내지 않고, 드리프트(drift)하지도 않습니다. 그저 설정 파일에 권위 있는 것처럼 자리 잡고 있을 뿐입니다. 이 존재 자체가 당신을 오도합니다. 이미 해당 설정을 조정했다고 믿게 만들어서, 더 이상 그것을 확인하는 것을 멈추게 만듭니다. 비용은 단순히 적용되지 않은 설정이 아닙니다. 얻지 못한 최적화가 있다는 잘못된 확신입니다.
이는 에이전트 워크플로우에만 국한된 문제가 아니며, 애초에 얼마나 많은 프로세스를 추가해야 하는가라는 질문 아래 깔려 있는 실패 모드입니다. 만약 일부 설정이 연결되지 않았다면, 그 설정들이 제 역할을 하는지 판단할 수 없습니다. 어떤 브랜치에서도 사용되지 않는 기능 플래그(Feature flags), 검증하는 linter가 읽기 전용으로 처리하는 CI 환경 변수, linter가 로드하지 않는 설정 파일에 활성화된 linter 규칙 등—모양이 같고 스택만 다릅니다. 에이전트 워크플로우는 이 현상을 만들어내는 데 비정상적으로 능숙한데, 그 이유는 설정 표면(config surface)이 그것을 활용할 수 있는 장소의 개수보다 더 빠르게 성장하기 때문입니다.
3단계 감사 (The three-step audit)
grep과 텍스트 편집기만 있으면 오늘 바로 자신의 저장소에서 실행해 볼 수 있습니다.
1단계 — 설정 표면 목록화(List the config surface). 스키마나 문서에 명시된, 워크플로우가 받아들이는 모든 키를 나열합니다. 기억하는 사용 사례가 아니라 전체 목록이어야 합니다.
2단계 — 각 키에 대해 읽기 지점 찾기(Find its read points). 코드베이스에서 해당 키 이름을 grep하고, 발견된 각각의 경우를 분류합니다:
| 발견 유형 (kind of hit) | 예시 (example) | 리더로 간주하는가? (counts as a reader?) |
|---|---|---|
| 스키마 / 열거형 정의 (schema / enum definition) | ` |
3단계 — 행동 분기(behavior branches)가 없는 모든 핵심 키를 판정합니다. 두 가지 정직한 선택지가 있습니다: 연결하거나(읽는 무언가를 만들거나), 아니면 삭제하는 것입니다. 문서화되었지만 읽히지 않는 키를 남겨두는 것은 최악입니다. 왜냐하면 시스템이 가지고 있지 않은 행동을 문서화하기 때문입니다.
우리가 발견한 것과 잘못했던 점
저희는 자체 프로토콜에 대해 이 감사를 수행했는데, 여기에는 작업이 얼마나 많은 프로세스를 거치는지 조정하는 것처럼 보이는 여섯 개의 노브(knobs)가 있습니다. 생존한 항목을 포함하여 솔직한 표를 소개합니다.
| knob | 문서화된 내용 | 행동 분기 | 판정 |
|---|---|---|---|
ceremony: thin | 가벼운 작업을 위한 검토 간소화 | 스크립트에는 없고, 오케스트레이터가 실제로 읽는 P2/P4 단계 카드에도 없음 | 선언되었으나 소비되지 않음 (declared, not consumed) |
| ... |
여기서 나온 패턴이 유용한 부분입니다. 대부분의 스위치가 검증되고 있었고, 우리는 그 검증을 소비(consumption)로 오해하고 있었습니다. 정확히 하나의 노브만이 시스템의 동작을 변경했습니다. 나머지는 체크를 제한했거나(유용하지만 문서화된 행동과는 다름), 비활성 상태였습니다.
그리고 이제 표보다 더 가치 있는 부분입니다. 저희의 첫 진단은 전체 체인이 망가졌다는 것이었고—모든 노브가 죽었다는 것이었습니다. 우리는 그것을 작성했고, 제대로 측정했으며, 그 측정 결과는 이를 뒷받침하지 않았습니다. 두 개의 노브는 실제로 소비됩니다. 그리고 스크립트가 강제하지 않는 가지치기 규칙(pruning convention)은 약 94%의 확률로 지켜지는 것으로 나타났습니다—선언된 건너뛰기 48건, 위반 3건—왜냐하면 단계 카드가 이 규칙을 문서화하고 오케스트레이팅 에이전트가 카드를 읽고 이를 준수하기 때문입니다. 이 수치에 대한 한 가지 주의사항이 있습니다: 해당 레포지토리가 일회성 마이그레이션으로 요구 사항 문서를 평탄화했기 때문에, 일부 선언이 사후에 작성되었을 가능성을 배제할 수 없습니다. 94%는 증거가 아닌 참고 자료로 간주하십시오.
정확한 진술은 우리가 시작했던 것보다 더 좁습니다. 어떤 스크립트도 이 규칙을 강제하지는 않지만, 거의 모든 경우에 따르고 있으며, 간극이 있는 곳은 가장자리뿐입니다. 단순히 리더만 검색하는 데드 스위치 감사(dead-switch audit)를 수행했다면 '소비자가 없으므로 효과가 없다'고 말했을 것이며, 이는 작동하는 규칙에 대해 잘못된 결론을 내렸을 것입니다.
우리가 측정하면서 저지른 실수
저는 처음으로 금요일 오후에 모든 태스크 디렉토리를 순회하며 그 수치를 계산했고, 돌아온 숫자는 **41.7%**였습니다. 이는 정반대의 결론이었습니다. 제 첫 반응은 이 규칙이 로드맵에서 주장한 것보다 훨씬 나쁜 상태라는 것이었습니다. 그렇지 않았습니다. 문제는 제 스크립트였습니다. 저는 태스크의 선언된 목록에 없는 모든 단계를 '건너뛰기(skip)'로 계산했는데, 여기에는 해당 목록에 포함되지 않는 가장 초기의 단계들까지도 포함되어 있었습니다. 범위를 실제로 가지치기할 수 있는 단계로 수정하여 동일한 데이터를 사용했을 때, 건너뛰기는 48개, 위반은 3개가 나왔습니다.
여기 두 가지 교훈이 있으며, 두 번째가 이 섹션이 존재하는 이유입니다. 첫째, 규정 준수(compliance) 숫자는 분모만큼만 유효하며, 설정 의미론(config semantics)은 분모가 미끄러워지기 쉬운 바로 그 지점입니다. 둘째—그리고 이것이 일반화 가능한 교훈입니다—같은 데이터셋을 사용했음에도 불구하고, 제가 기록하지 않은 정의에 따라 상반된 답변이 나왔습니다. 만약 이 감사를 여러분의 설정(config)에 대해 직접 수행한다면, 계산하기 전에 범위를 명시하거나 작성해야 하며, 그렇지 않으면 기대했던 어떤 답변이든 얻게 될 것입니다.
나머지 절반: 당신이 지불하고 있는 설정 비용
데드 스위치를 찾는 것은 정확성(correctness)에 관한 문제입니다. 측정 가능한 비용 차원도 존재합니다.
저희 워크플로우에서는 전송되는 각 단계가 태스크 카드와 프로토콜 파일로부터 구성된 컨텍스트 문서(context document)를 얻습니다. 저희는 이 문서들이 무엇을 포함하는지 계산하기 위해 작은 스크립트를 작성했습니다 (agate-dispatch-cost.py, 저희의 태스크 TAG0042에 대해 실행). 아래 발췌문은 그 출력 내용을 그대로 보여줍니다:
派发上下文 50 份 / 878749 B (858 KiB)
rev 修订重发 0 份 / 0 B = 0% ← 可避免(整份重发)
卡片注入 403148 B = 46%
...
마지막 두 줄을 읽어보세요. 저희가 자체 에이전트에게 전송한 바이트 중 46%는 주입된 참조 카드였고, 전체의 38%는 동일한 카드가 재전송되는 경우였습니다. 이것은 스위치(switch) 문제가 아닙니다. '우리가 세지 않았던' 문제인 것입니다. 즉, 아무도 측정하지 않으면 그 드리프트 현상을 아무도 알아차리지 못한다는 것과 같은 교훈입니다 일반적인 계측(instrumentation)의 경우와 같습니다. 카운터를 만드는 것이 전체 해결책입니다. 도구 자체의 출력은 중국어이고, 중요한 것은 비율 그 자체입니다.
한 가지 주의할 점이 있습니다. 이 부분이 보통 블로그 게시물에서 과장하기 쉬운 지점이기 때문입니다. 이번 조사를 시작하게 된 사건—24줄 변경에 대해 약 18시간과 350만 출력 토큰이라는 수치—은 기계적으로 검증할 수 없습니다. 이는 세션 기록에서 나온 것이며, 저희 자체 검토에서도 재현 불가능하다고 표시되었습니다. 이를 측정값이라기보다는 일화로 취급하세요. 위의 바이트 비율은 재현 가능하므로 표에 있는 값들입니다.
이번 주에 실행해 보세요
- 스키마에서 에이전트 워크플로우가 수용하는 모든 설정 키(config key)를 나열하세요. 기억에 의존하지 말고, 스키마를 참조해야 합니다.
- 각 키에 대해 검색(Grep)을 수행하고 발견된 항목들을 분류하세요. 스키마, 검증기(validator), 메타데이터 또는 동작 분기(behavior branch) 중 어디에 해당하는지입니다. 가장 마지막에 해당하는 것만 계산합니다.
- 동작 분기가 0개인 모든 키에 대해 선택하세요: 연결할지 아니면 삭제할지. 그 선택을 기록으로 남기세요.
- 어떤 규정 준수 수치를 계산하기 전에 범위를 정의(scope)하세요. 정의가 답을 결정합니다.
- 실제로 에이전트에게 전송하는 양을 세세요. 반복적인 카드 주입과 전체 문서 재전송은 보통 가장 줄일 수 있는 큰 항목이며, 카운터 없이는 아무도 알아차리지 못합니다.
이 모든 것에 프로토콜이나 프레임워크, 설치가 필요하지 않습니다. 그저 검색(grep), 표, 그리고 '설정이 이것을 허용한다'와 '무언가가 이것으로 동작한다'를 구별하는 규율만 있으면 됩니다.
단 하나만 기억한다면: 검증된 설정이 곧 작동하는 설정은 아닙니다. 검증기는 당신의 설정이 잘 구성되었음을 증명하지만, 오직 읽는 사람(reader)만이 그것이 실제로 무언가를 하는지 증명합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기