에이전트의 블랙박스를 노트북 위로 끌어내리는 법
AI agent가 실패하거나 조용히 비용을 태울 때, 필요한 건 더 긴 prompt보다 실행 흔적을 읽는 눈이다. Lookspan은 local-first 방식으로 span, 비용, replay, eval을 한자리에 묶어두며 AI agent 개발의 디버깅 루프를 훨씬 짧고 선명하게 만들 가능성을 보여준다.
DevInsight 편집팀 발행
AI 보조 초안과 편집 검수를 거쳐 발행했습니다.
에이전트 시스템이 진짜 어려워지는 순간은 모델이 틀린 답을 했을 때가 아니라, 왜 틀렸는지 아무도 설명하지 못할 때다. 더 곤란한 장면은 실패조차 조용하다는 점이다. 겉으로는 “작동했다”는 로그 한 줄만 남는데, 실제로는 불필요한 tool 호출이 몇 차례 반복됐고, 컨텍스트는 중간에 비대해졌으며, 토큰 비용은 눈치채기 어려운 속도로 새고 있다. 결과가 나쁘면 원인을 찾기 어렵고, 결과가 얼핏 괜찮아 보이면 더 위험하다. 시스템은 계속 돈을 태우면서도 자신이 건강하다고 착각하게 만들기 때문이다.
그래서 요즘 에이전트 개발에서 중요한 질문은 더 이상 “어떤 모델을 붙일까”가 아니다. “이 실행이 어떤 경로로 흘렀는가”, “어디서 맥락이 오염됐는가”, “비용과 품질이 어느 지점에서 역전되기 시작하는가” 같은 질문이 훨씬 실무적이다. Lookspan 같은 local-first observability 도구가 시선을 끄는 이유도 여기에 있다. 이름만 놓고 보면 단순한 디버거처럼 들리지만, 실제로 이 부류의 도구가 겨냥하는 것은 훨씬 넓다. 에이전트의 블랙박스를 단순히 열어보는 것이 아니라, 실행 흔적을 개발자의 책상 위로 끌어내려 반복 가능한 관찰 대상으로 바꾸는 일이다.
대부분의 팀은 에이전트를 처음 만들 때 관측보다 동작에 먼저 집착한다. 프롬프트를 다듬고, tool schema를 정리하고, 메모리 전략을 바꾸고, 재시도 정책을 얹는다. 그러다 어느 순간부터 이상한 현상이 생긴다. 같은 입력인데 가끔만 실패한다. staging에서는 괜찮았는데 production에서만 답이 흔들린다. 특정 고객 계정에서만 비용이 튄다. 재현을 시도하면 또 멀쩡하다. 이때 기존 애플리케이션 로그 방식은 거의 무력하다. HTTP 요청과 DB 쿼리는 비교적 결정적이지만, LLM 호출은 본질적으로 확률적이고, 외부 도구의 상태와 히스토리 길이, 중간 요약의 품질, function call 결정까지 모두 한 덩어리로 얽혀 있기 때문이다.
이런 시스템에서는 “한 번의 요청”이라는 단위가 너무 거칠다. 에이전트는 실제로 여러 층의 미세한 판단으로 움직인다. 어떤 메시지를 잘라서 넘겼는지, 어떤 tool을 왜 선택했는지, 그 tool 결과를 다시 어떻게 요약했는지, 재시도는 몇 번 있었는지, 중간 단계에서 비용이 얼마나 누적됐는지 같은 정보가 다 따로 보여야 한다. 여기서 span이라는 개념이 유용해진다. 원래 분산 시스템 추적에서 널리 쓰이던 단위지만, 에이전트 세계에 가져오면 생각보다 잘 맞는다. 한 번의 실행을 부모 span으로 보고, 그 아래에 model inference, retrieval, tool execution, memory read/write, rerank, post-processing 같은 하위 span을 매달면 흐름이 드러난다. “무엇을 했다”보다 “어떤 순서로, 얼마의 시간과 비용으로, 어떤 맥락을 소비하며 했다”가 보이기 시작한다.
local-first라는 표현도 그냥 취향 문제로 보면 놓치는 게 있다. 에이전트 관측 데이터는 평범한 애플리케이션 로그보다 훨씬 민감한 경우가 많다. 프롬프트 원문, 내부 사고를 암시하는 중간 텍스트, 고객 문서 조각, 검색 결과, tool 인자, 응답 일부가 모두 포함될 수 있기 때문이다. 이 데이터를 처음부터 원격 SaaS로 보내는 구조는 편리하지만, 가장 먼저 보안팀과 법무팀의 질문을 부른다. 반대로 로컬에 우선 저장하고, 필요한 범위에서만 공유하거나 익명화해 내보내는 방식은 개발 속도와 통제력을 함께 준다. 특히 초기 단계에서는 “관측을 위해 또 다른 운영 복잡성을 도입하는” 역설을 피하기 좋다. 노트북에서 바로 실행 흐름을 보고, 비용을 확인하고, replay를 돌리고, 간단한 eval까지 이어지는 루프가 짧아질수록 프롬프트 수정보다 설계 판단이 빨라진다.
여기서 replay가 중요해진다. 에이전트 디버깅이 자꾸 사람을 지치게 만드는 이유는, 실패를 봤어도 다시 같은 실패를 만나기 어렵기 때문이다. 입력만 저장해 두는 것으로는 부족하다. 어떤 system prompt가 붙었는지, 어떤 tool 결과가 들어왔는지, 모델 파라미터가 어땠는지, 컨텍스트 윈도우가 얼마나 찼는지, 심지어 도중에 어떤 압축이나 요약이 수행됐는지까지 함께 남아야 같은 경로를 비슷하게 재생할 수 있다. replay는 단순 재현 도구가 아니라 설계 실험 장치다. 예를 들어 retrieval 상위 개수를 8에서 4로 줄였을 때, 또는 planner 단계를 제거하고 direct execution으로 바꿨을 때 어떤 span에서 시간이 줄고 어떤 span에서 품질 저하가 생기는지 비교할 수 있다. 에이전트를 감으로 손보는 대신, 실행 이력을 비교 가능한 실험군으로 바꾸는 셈이다.
이때 eval이 같은 화면 안에 붙어 있으면 디버깅의 성격이 달라진다. 많은 팀이 관측과 평가를 분리해 생각한다. 관측은 운영용, 평가는 연구용이라는 식이다. 하지만 에이전트에서는 둘이 자주 같은 질문을 향한다. 비용이 낮아졌는데 정답률도 유지됐는가. 더 빠른 모델로 바꿨더니 tool 호출 횟수가 늘지는 않았는가. hallucination은 줄었지만 불필요한 보수성 때문에 해결 가능 문제를 포기하진 않았는가. span과 replay, eval이 따로 놀면 이런 비교가 느리고 피곤하다. 반대로 한 자리에서 이어지면 “이 실패 케이스는 왜 생겼는가”와 “이 수정이 전체 분포에서 어떤 영향을 주는가”를 한 호흡으로 볼 수 있다. 에이전트 개발에서 가장 비싼 자원은 토큰보다도 집중력인데, 이 연결이 그 낭비를 크게 줄인다.
많은 장애는 거창한 모델 한계보다 소소한 관측 부재에서 터진다. 대표적인 것이 비용 폭주다. 겉으로는 한 번 답을 내는 것처럼 보였지만, 내부적으로는 계획 수립과 검증 단계를 오가며 비슷한 tool을 여러 번 두드렸고, 실패한 호출을 재시도하면서 컨텍스트를 계속 덧붙였다. 응답 품질은 별로 좋아지지 않았는데 토큰과 latency만 누적된다. 이 문제는 “더 싼 모델을 쓰자”로 해결되지 않는다. 오히려 싼 모델이 도구 선택을 더 불안정하게 만들어 전체 비용을 높일 수도 있다. 필요한 것은 어느 span이 비정상적으로 길어지는지, 어떤 조건에서 fan-out이 발생하는지, 반복 호출의 패턴이 있는지 보는 일이다. 관측이 없으면 비용은 회계 항목으로만 보이고, 관측이 있으면 비용은 제어 가능한 실행 특성으로 보인다.
또 하나 흔한 함정은 성공과 실패의 정의가 너무 조악하다는 점이다. 에이전트는 정답을 맞혔는지만으로 평가하기 어렵다. 너무 오래 걸렸을 수도 있고, 지나치게 많은 문서를 읽었을 수도 있고, 사용자에게는 답을 줬지만 내부적으로는 위험한 우회를 했을 수도 있다. 예를 들어 민감한 시스템에서 tool 권한을 조금이라도 잘못 행사하는 순간, 최종 응답이 멀쩡해도 이미 실패다. 이런 환경에서는 단순한 pass/fail보다 실행 궤적 자체가 중요해진다. 특정 도구가 호출되면 무조건 경고를 띄우거나, 특정 프롬프트 패턴이 나오면 redaction을 강제하거나, 너무 긴 추론 체인이 생성되면 요약 정책을 개입시키는 식의 운영 규칙이 필요해진다. 관측 도구는 예쁜 화면보다 이런 규칙을 어디에 꽂을 수 있는지가 더 중요하다.
Lookspan 같은 이름이 흥미로운 이유는, 시선의 방향을 결과가 아니라 과정으로 돌리겠다는 선언처럼 들리기 때문이다. 많은 개발 도구가 “더 똑똑한 모델”을 파는 동안, 이 부류는 “더 읽기 쉬운 실행 흔적”을 판다. 얼핏 덜 화려하지만 현장에서는 훨씬 오래 남는 종류의 가치다. 에이전트는 결국 여러 불확실성을 묶어 자동화한 시스템이다. 불확실성이 많은 시스템은 성능보다 설명력이 먼저 필요하다. 설명력이 확보되어야 성능 튜닝도, 비용 최적화도, 권한 통제도 가능한데, 지금까지는 이 설명력의 대부분을 사람이 머릿속에서 조합해 왔다. 실행 그래프와 span, 비용, replay, eval을 한 자리로 가져오려는 시도는 바로 그 비효율을 줄이려는 방향으로 읽힌다.
그렇다고 local-first observability가 만능은 아니다. 첫 번째 착시는 “로컬에 있으니 안전하다”는 안도감이다. 실제로는 로컬에 남는 데이터가 더 민감할 수 있다. 프롬프트와 문서 조각, 테스트 입력, API 응답, 심지어 실패 사례까지 모두 개발자 개인 장비에 남기 때문이다. 암호화, 보존 기간, 공유 범위, 마스킹 정책이 없으면 위험은 그냥 위치만 바뀐 셈이다. 두 번째 착시는 “다 기록하면 언젠가 도움이 된다”는 욕심이다. 에이전트 관측은 금세 과잉이 된다. 전체 메시지 전문, 모든 tool payload, 중간 후보군, 디버그 텍스트를 빠짐없이 저장하면 저장 비용보다 먼저 읽기 비용이 폭발한다. 무엇을 남길지에 대한 판단이 없다면 관측 도구는 또 하나의 블랙박스가 된다.
세 번째 함정은 OpenTelemetry 같은 기존 표준을 너무 애플리케이션 관점으로만 적용하는 것이다. 웹 요청 추적에서는 span 이름만 잘 붙여도 어느 정도 의미가 생기지만, 에이전트에서는 semantic convention이 훨씬 중요하다. llm.model, prompt.version, tool.name, retrieval.k, input.tokens, output.tokens, cache.hit, eval.score 같은 필드가 일관되게 붙어야 나중에 비교가 가능하다. 그렇지 않으면 화면은 그럴듯해도 서로 다른 실행을 제대로 나란히 놓을 수 없다. 에이전트 관측에서 중요한 것은 “보였다”가 아니라 “비교할 수 있다”다. 비교가 안 되면 회고는 가능해도 개선은 어렵다.
실제로 운영 신호를 읽는 방법도 기존 서비스와 다르다. CPU나 메모리보다 먼저 봐야 하는 것은 tool 호출 폭, 평균 컨텍스트 길이, 재시도 비율, 동일 쿼리 대비 토큰 편차, 특정 프롬프트 버전에서의 실패 집중도다. 예를 들어 에러율은 낮은데 응답 시간이 서서히 늘고 있다면, 모델 자체보다 retrieval 결과가 길어졌거나 중간 요약이 무너지고 있을 가능성이 크다. 비용이 특정 시간대에만 뛴다면 단순 트래픽 증가보다도 특정 질의 유형이 planner를 과도하게 활성화했을 수 있다. 동일한 질문군에서 답변 품질이 흔들린다면 temperature보다 cache invalidation이나 문서 스냅샷 차이를 의심해야 할 때가 있다. 이런 징후는 전통적인 APM 화면만으로는 거의 읽히지 않는다. 에이전트 전용 관측은 결국 시스템의 “행동 문법”을 이해하기 위한 계측이다.
간단한 예시를 하나 들면, span 구조는 대략 이런 감각에 가깝다.
trace("customer-support-run", async () => { await span("retrieve-docs", { k: 6 }, retrieveDocs) await span("plan-actions", { model: "gpt-4.1-mini" }, planActions) await span("tool.create-ticket", { tool: "jira" }, createTicket) await span("final-answer", { evalTarget: "groundedness" }, answerUser) })
이 코드의 핵심은 문법이 아니라 관점이다. 한 번의 실행을 기능 단위가 아니라 판단 단위로 쪼개 기록하는 것, 그리고 각 단계에 시간뿐 아니라 비용과 품질 평가의 연결점을 남기는 것이다. 에이전트가 실패했을 때 필요한 것은 장문의 프롬프트가 아니라 “어느 단계에서 맥락이 변질됐는가”를 보는 이런 시야다.
흥미로운 지점은 이런 도구가 결국 개발 문화까지 바꾼다는 데 있다. 지금까지 에이전트 개선은 종종 장인의 감각에 의존했다. 누군가가 프롬프트를 만져 보고, 느낌상 나아진 것 같으면 배포하고, 문제가 생기면 또 다른 사람이 로그를 뒤져 감으로 복원했다. 실행 흔적이 잘 남기 시작하면 논의의 단위가 달라진다. “이 문장을 고치자”보다 “이 단계의 tool 선택 분산이 너무 크다”, “이 prompt version부터 retrieval span 시간이 늘었다”, “이 변경은 평균 비용을 낮췄지만 실패 시 fan-out을 키운다” 같은 대화가 가능해진다. 취향의 논쟁이 계측의 논쟁으로 옮겨가는 것이다. 그때 비로소 에이전트 개발은 마법이 아니라 엔지니어링에 가까워진다.
로컬 우선 접근은 특히 작은 팀이나 개인 개발자에게 의미가 크다. 거대한 관측 파이프라인을 먼저 깔지 않아도 되고, 초기 실험을 원격 플랫폼의 데이터 모델에 맞추느라 설계를 굽힐 필요도 없다. npx 한 번으로 바로 시작하는 배포 감각이 암시하는 것도 아마 이 지점일 것이다. 에이전트 실험은 대개 짧고 자주 바뀌며, 실패를 빨리 보는 편이 성공을 크게 만드는 경우가 많다. 이때 설치와 설정, 외부 의존성 자체가 무거우면 관측은 늘 “나중에”로 밀린다. 하지만 에이전트는 나중에 붙이는 관측이 특히 비싸다. 처음부터 실행 단위와 메타데이터를 어떻게 남길지 생각하지 않으면, 어느 순간 기록은 남는데 해석은 불가능한 상태에 도달한다.
결국 중요한 건 도구 이름이 아니라 태도다. 에이전트가 실패하거나 조용히 비용을 태울 때 필요한 것은 더 긴 프롬프트가 아니라 실행 흔적을 읽는 눈이다. span은 그 눈의 초점이고, replay는 기억력이며, eval은 판단 기준이다. local-first는 이 세 가지를 더 가까운 거리에서 다루게 해 주는 배치 전략에 가깝다. 한동안 에이전트 업계는 “얼마나 똑똑한가”를 경쟁해 왔지만, 실제 현장에서는 “얼마나 읽을 수 있는가”가 더 오래 남는 경쟁력이 될 가능성이 크다. 블랙박스를 완전히 없앨 수는 없더라도, 적어도 무슨 일이 일어났는지 추적할 수 있는 수준까지 끌어내리는 것. 에이전트 시대의 관측은 바로 그 겸손한 목표에서 가장 큰 생산성을 만들어낼지 모른다.
댓글
댓글을 읽어오는 중입니다.
같이 읽으면 좋은 글
방금 읽은 주제와 이어지는 글을 골랐습니다.
ESLint Flat Config 마이그레이션 실패 일지와 살아남는 체크리스트
ESLint 9의 flat config로 넘어가면서 extends가 사라지고, 플러그인 호환성 문제, VS Code ESLint 확장과의 설정 불일치, 글로벌 변수 선언 방식 변화 등 현장에서 마주치는 장애물을 해결 순서대로 정리한다. 삽질을 줄이는 실전 체크리스트. 2025년 4월, ESLint 9가 정식 릴리스되면서 파일은 deprecated 경고를 넘어 아예 무시되기 시작했다.
타입스크립트 배포의 마지막 런타임을 지우는 실험
Rust 위에 SWC와 LLVM을 얹어 TypeScript를 곧바로 네이티브 실행 파일로 바꾸려는 시도는 단순한 성능 경쟁이 아니다. Node와 Electron 의존성을 덜어내고, 배포 단순화와 크로스플랫폼 전략을 다시 계산하게 만드는 도구로 읽을 만하다.
에이전트가 늘어날수록 개발이 느려지는 이유와 그 병목을 푸는 작업 공간
여러 coding agent를 한 화면과 여러 worktree 안에서 동시에 다루는 흐름이 왜 필요한지, 그리고 IDE가 단순 채팅창을 넘어 orchestration 계층으로 바뀔 때 생기는 생산성·운영상의 변화와 함정을 짚는 글.
이전 글
타입스크립트 배포의 마지막 런타임을 지우는 실험
다음 글
메모리 한 페이지를 아끼는 쓸모없는 열정
DevInsight Digest
새 글이 쌓이면, 피드에서 바로 이어 읽으세요.
과장된 알림 대신 발행한 글 전체를 RSS로 제공합니다.