PHP2Shell 공지를 보고 점검했더니 PHP가 하나도 없었습니다

이미지
10월 21일에 PHP와 Node.js가 같은 날 보안 패치를 냈습니다. PHP 쪽은 사람들이 PHP2Shell이라고 부르고 있고, Node 쪽은 Node2Shell이라는 이름이 붙었습니다. 이름부터가 좀 무섭습니다. 뒤에 Shell이 붙으면 대개 남의 서버에서 명령을 실행할 수 있다는 뜻이니까요. 회사에서도 바로 지시가 내려왔습니다. 전 프로젝트 점검하고, 외부 IP 붙은 서버는 전수조사하라는 내용이었습니다. 회사 쪽 일은 회사 쪽대로 하고, 저는 퇴근하고 개인 프로젝트랑 예전에 알바로 했던 것들을 한번 열어 봤습니다. 그런데 열어 보고 나니 이번 글은 패치 이야기가 아니라 7년 전 이야기가 됐습니다. PHP2Shell과 Node2Shell, 둘이 같은 무게는 아닌 것 같습니다 찾아오신 분들은 아마 이게 뭔지, 내 서버가 해당되는지부터 궁금하실 것 같아서 그것부터 적습니다. PHP2Shell은 CVE 두 건입니다. CVE-2026-109618 과 CVE-2026-109619이고, 둘 다 인증 없이 원격에서 코드를 실행할 수 있는 취약점입니다. CVSS 4.0 기준 9.2점입니다. 하나는 메모리가 부족할 때 PHP가 요청 데이터를 반쯤 만든 상태에서 멈추고, 정리 단계에서 그 망가진 데이터를 멀쩡한 걸로 취급하는 문제입니다. 다른 하나는 요청이 끝난 뒤에도 그 요청이 쓰던 상태가 남아 있다가 같은 워커가 받은 다음 요청에서 다시 쓰이는 문제입니다. PHP 공식 공지 기준으로 고쳐진 버전은 8.5.12, 8.4.27, 8.3.36, 8.2.35입니다. 그보다 낮은 버전은 전부 해당됩니다. 8.1 이하는 PHP 프로젝트가 더 이상 정규 릴리스를 내지 않습니다. 대신 보안팀이 7.4, 8.0, 8.1용으로 최소한의 소스 백포트를 따로 공개해 뒀고, 배포판이나 LTS 벤더 중에도 백포트를 내는 곳이 있다고 합니다. 직접 빌드하거나 벤더 패키지를 쓰는 게 아니면 사실상 버전을 올리는 수밖에 없어 보입니다. 공지에도 설정을 바꾸는 건 부분적인 완화일 뿐이고...

하네스 엔지니어링이란 무엇인가 - 프롬프트·컨텍스트 엔지니어링과 무엇이 다른가

낡은 마구간 나무 기둥에 걸린 가죽 마구와 놋쇠 버클, 뒤쪽에 말이 흐릿하게 서 있는 장면
AI 대화창을 처음 연 게 2024년 11월 말이었습니다. 별 기대 없이 그냥 한번 써보자는 생각이었습니다. 그 뒤로 1년 반쯤 지났는데, 그사이 제가 AI를 쓰는 방식이 세 번 완전히 바뀌었습니다.

처음엔 복붙이었습니다. 이 코드 짜줘 하면 대화창에 코드가 나오고, 그걸 복사해서 에디터에 붙이고, 안 되면 다시 묻고. 그걸 끝도 없이 반복했습니다. 조금이라도 낫게 하려면 프롬프트를 잘 써야 한다는 말을 들었고, 실제로 맞는 말이었습니다. 대충 물으면 대충 답이 나왔고 맥락을 잘 줄수록 쓸 만한 코드가 나왔습니다. 그래서 프롬프트 엔지니어링을 공부했습니다. 역할을 주고, 예시를 몇 개 넣고, 생각하는 과정을 유도하고, 출력 형식을 정해주고. 공을 꽤 들였습니다.

그러다 Claude Code가 나오고 Codex가 나왔습니다. CLI에서 AI가 직접 파일을 읽고 고치고 실행하고 디버깅까지 합니다. 그걸 보는 순간 그동안 프롬프트 공부한 게 좀 허탈해졌습니다.

역프롬프트 엔지니어링이라는 것도 이미 있었습니다. AI한테 이 결과물을 만든 프롬프트가 뭔지 거꾸로 추론해 달라고 하면 꽤 그럴듯하게 뽑아줍니다. 사람이 프롬프트를 다듬는 게 아니라 AI가 AI를 위한 프롬프트를 만드는 겁니다. 복붙하면서 쌓은 감도, 프롬프트를 공들여 쓰던 습관도 도구가 거의 다 가져가 버린 느낌이었습니다.

그때 좀 억울했는데, 그 억울함이 맞았는지 나중에 제 글을 다시 세어본 적이 있습니다. 헛것은 아니었습니다. 다만 남은 게 프롬프트 쓰는 기술은 아니었습니다.

지금은 또 다릅니다. 코드 한 덩어리를 짜는 게 아니라 에이전트가 계획을 세우고, 실행하고, 테스트하고, 계획을 다시 고칩니다. 사람은 방향만 잡아줍니다. 더 가면 위쪽 에이전트가 큰 그림을 받아서 아래 에이전트들을 세팅하고, 그 에이전트들이 다시 팀을 꾸려서 일을 나눠 갖는 데까지 갑니다. 사람 한 명이 AI 조직 하나를 돌리는 셈입니다. 1년 반 전에 대화창에서 코드를 복사하던 그 도구 얘기입니다.

변화가 오는 간격도 처음과는 다릅니다. 처음 쓰기 시작했을 땐 큰 변화가 반년에 한 번쯤 왔습니다. 새 모델, 새 기능, 새 방식. 그게 석 달로 줄더니 지금은 한 주가 멀다 하고 뭔가 나옵니다. 반년 전에 최신이던 게 지금은 레거시가 돼 있고, 따라잡으려고 공부하는 사이에 또 다른 게 나와 있습니다. 저는 요즘이 불편한 이유가 이거라고 생각합니다. 변화가 한 번 오고 끝나는 게 아니라 멈추질 않습니다.

프롬프트로는 안 풀리는 게 보였습니다

프롬프트 엔지니어링 얘기는 이제 좀 오래된 얘기처럼 들립니다. 한때는 그게 전부인 것처럼 말하던 때가 있었습니다. 질문을 어떻게 쓰느냐, 역할을 어떻게 주느냐, 예시를 몇 개 넣느냐. 그 조합 하나로 결과가 꽤 달라졌으니 그럴 만했습니다. 2023년, 2024년에는 실제로 그랬습니다. 같은 모델을 써도 프롬프트를 잘 짠 사람이 더 나은 결과를 뽑았습니다.

그런데 2025년쯤부터 좀 이상했습니다. 프롬프트를 아무리 잘 써도 안 풀리는 게 보이기 시작했습니다.

모델은 분명 좋아졌고 CLI 도구나 에이전트형 인터페이스도 계속 나왔는데, 막상 실제 일을 시키면 엉뚱한 데서 무너졌습니다. 구현은 시작하는데 끝을 못 보고, 파일은 만지는데 상태를 잃고, 스스로 테스트까지 하라고 하면 이상할 만큼 낙관적이 됐습니다. 잘했다고 말하는데 결과물은 별로인 일이 계속 있었습니다. 이런 게 몇 번 쌓이고 나서야 문제가 프롬프트에 있는 게 아니라는 생각이 들었습니다.

그때부터 생긴 버릇이 하나 있습니다. 에이전트가 다 됐습니다, 라고 하면 일단 의심부터 합니다. 정말 돌려봤는지, 돌려봤다면 결과를 봤는지, 아니면 돌렸다는 사실만 가지고 됐다고 하는 건지요. 실행한 것과 확인한 것은 다른 일인데 에이전트는 그 둘을 같은 문장 하나로 말합니다. 만약 에이전트가 실행은 했는데 결과는 확인 못 했습니다, 라고 솔직하게 말해준다면 저는 그쪽을 훨씬 더 믿을 것 같습니다. 그런데 그렇게 말하게 만드는 건 프롬프트 한 줄로는 잘 안 됐습니다. 말투를 고치라고 하면 말투만 고쳐지고, 확인을 안 한 건 그대로였습니다. 결국 확인했는지를 말로 묻는 게 아니라 확인한 흔적이 남게 만드는 수밖에 없었습니다. 그게 프롬프트 바깥의 일이라는 걸 그때는 아직 몰랐습니다.

그다음에 나온 말이 컨텍스트 엔지니어링이었고, 저는 꽤 그럴듯하다고 느꼈습니다. 모델이 똑똑하게 일하려면 질문을 잘 쓰는 것보다 뭘 보여주느냐가 더 중요하다는 얘기였으니까요. RAG, MCP, 메모리, 히스토리 관리, 온갖 검색 레이어가 다 그쪽 얘기였습니다. 저도 한동안은 여기서 끝나는 줄 알았습니다.

올해 들어서는 그것도 모자란다는 쪽으로 생각이 바뀌었습니다. 에이전트를 오래 돌려보면 컨텍스트도 전체의 일부일 뿐이라서요. 어떤 파일에 손댈 수 있는지, 코드를 어디서 돌리는지, 실패하면 뭘로 확인하는지, 상태를 어디에 남기는지, 언제 멈추게 하는지, 어디서 사람한테 넘기는지. 이건 뭘 보여줄까보다 더 바깥의 문제입니다. 말 그대로 환경입니다.

하네스라는 말이 잘 맞는 것 같습니다

이 환경을 설계하는 일을 요즘은 하네스 엔지니어링이라고 부르는데, 저는 이 이름이 꽤 잘 맞는다고 생각합니다.

하네스는 말에게 씌우는 마구입니다. 고삐, 안장, 등자 같은 것들이요. 말의 힘을 빼는 장비가 아니라 그 힘이 엉뚱한 데로 새지 않게 방향을 잡아주는 장비입니다. AI 에이전트도 비슷한 것 같습니다. 모델이 셀수록 더 그렇습니다. 힘이 세다고 다 좋은 게 아니라, 그 힘을 어디로 보낼지를 누군가 설계해야 합니다. 그러니까 하네스 엔지니어링은 모델을 더 똑똑하게 만드는 일이라기보다 모델이 일할 바깥 환경을 만드는 일에 가깝습니다.

짧게 쓰면 이렇게 됩니다.

Agent = Model + Harness

모델이 아닌 나머지가 전부 하네스입니다. 시스템 프롬프트, 도구 정의, 샌드박스, 실행 환경, 오케스트레이션 로직, 파일 시스템, 메모리 관리, 훅, 미들웨어, 피드백 루프. 프롬프트 엔지니어링의 다음 편이 아니라 그보다 훨씬 바깥쪽 레이어이고, 프롬프트와 컨텍스트를 안에 품은 채로 돌아가는 시스템 전체입니다.

프롬프트 엔지니어링은 무엇을 물어볼지, 컨텍스트 엔지니어링은 무엇을 보여줄지, 하네스 엔지니어링은 이 환경 전체를 어떻게 짤지를 고민하는 일입니다. 저는 이 셋이 서로를 밀어내는 관계라고 보지 않습니다. 안에 들어가 있는 관계에 가깝습니다. 프롬프트는 지금도 필요하고 컨텍스트도 여전히 중요합니다. 다만 요즘 에이전트에서는 그 둘만 잘해서는 안 되는 영역이 너무 커졌고, 그래서 관심이 바깥으로 나간 것 같습니다.

시간 순서로 놓고 보면 더 잘 보입니다. 2022년부터 2024년까지는 프롬프트가 주인공이었습니다. 질문을 어떻게 짜느냐가 거의 모든 얘기를 다 먹었습니다. 2025년 중반부터는 컨텍스트가 올라왔습니다. RAG니 MCP니 메모리니, 결국 모델에 어떤 토큰을 언제 넣느냐가 중요하다는 얘기가 많아졌습니다. 그리고 2026년에 와서는 에이전트를 둘러싼 바깥 시스템 전체 얘기가 앞으로 튀어나왔습니다. Mitchell Hashimoto가 2월에 자기가 AI를 들여온 과정을 쓴 글에서 마지막 단계를 하네스를 설계한다고 적었는데, 정의가 짧았습니다. 에이전트가 실수하는 걸 보면 그 실수가 다시는 안 나오게 환경을 고친다는 겁니다. 그 뒤 몇 주 사이에 Anthropic과 OpenAI 쪽에서도 같은 방향의 엔지니어링 글이 이어졌습니다.

용어가 하나 더 늘어난 걸 반가워할 사람은 별로 없을 겁니다. 저도 처음엔 또 이름 하나 붙이는구나 싶었습니다. 그래도 이 말이 쓸모 있다고 느낀 건, 개념이 새로워서라기보다 문제를 어디서 찾을지를 바꿔주기 때문이었습니다. 에이전트가 이상하게 굴 때 프롬프트를 고칠지, 컨텍스트를 고칠지, 환경을 고칠지를 나눠서 물을 수 있게 되면 헛수고가 확 줄어듭니다.

왜 하필 지금인지

이름만 새로 붙인 유행어면 금방 사라질 텐데, 저는 이건 좀 다르다고 봅니다. 모델만으로는 안 되는 게 이미 너무 분명하게 보였으니까요.

LLM은 세션이 끝나면 상태를 잃습니다. 코드를 직접 실행하지 못하고, 실시간 정보에도 알아서 닿지 못하고, 자기 결과물을 자기 기준으로 확인하는 데도 한계가 큽니다. 대화창에서 AI랑 얘기할 때조차 사실은 아주 원시적인 하네스를 쓰고 있는 셈입니다. 이전 메시지를 이어 붙이고, 도구를 부르고, 조건이 맞으면 다시 모델을 부르는 루프요. 그냥 대화 UI처럼 보이지만 기초적인 하네스입니다. 문제는 작업이 길어질수록 이 정도로는 못 버틴다는 겁니다.

Anthropic이 공개한 사례를 보고 이 생각이 더 굳어졌습니다. Claude한테 claude.ai 클론을 만들라는 식의 큰 프롬프트 하나만 주고 여러 컨텍스트 윈도우에 걸쳐 돌렸더니, 모델이 멍청해서가 아니라 환경이 모자라서 무너지는 게 꽤 대놓고 드러났습니다. 한 번에 다 하려고 들다가 구현 중간에 컨텍스트가 끊기고, 다음 세션은 반쯤 만들어진 기능을 아무 설명 없이 물려받습니다. 자기 작업을 평가하라고 하면 엉망인 상태에서도 스스로 만족합니다. Anthropic이 내놓은 해법도 모델을 바꾸는 게 아니었습니다. 첫 세션에 환경을 차려두는 에이전트와, 매 세션 조금씩 나아가면서 다음 세션이 읽을 흔적을 남기는 에이전트로 역할을 나눴습니다.

저는 이걸 보고 모델을 비웃을 생각은 안 들었습니다. 사람한테 같은 조건을 줘도 똑같을 것 같았습니다. 진행 상황 보드도 없고, 테스트도 없고, 기록도 없고, 인수인계도 없는데 복잡한 프로젝트를 끝내라고 하면 누가 잘하겠습니까.

환경이라고만 하면 막연하니까

환경이라고만 하면 금방 또 공허해집니다. 그래서 방금 말한, 사람한테 없으면 무너지는 것들을 하나씩 채워 넣는다고 생각해 봤습니다.

먼저 기억입니다. LLM은 대화창이 끊기면 잊어버리는데 파일은 남습니다. 이 사소한 차이가 제일 큽니다. 세션이 끝날 때 어디까지 했고, 어떤 파일을 건드렸고, 다음엔 뭘 해야 하는지를 텍스트 파일 하나에 적어두면 다음 세션이 그걸 읽고 이어서 합니다. 그게 없으면 매 세션이 기억을 잃은 채로 시작합니다. 여기에 Git을 붙이면 돌아갈 지점이 생기고, 실험을 따로 떼어 둘 수 있고, 여러 에이전트가 한 저장소를 놓고 같이 일할 수도 있습니다. 파일 시스템이 에이전트의 장기 기억이 되는 겁니다.

그다음은 컨텍스트를 어떻게 안 상하게 두느냐입니다. 긴 컨텍스트가 무조건 좋은 게 아니라는 건 이제 다들 압니다. 상관없는 정보가 쌓이면 모델은 바늘보다 건초더미를 더 많이 받습니다. 창이 넓어졌다고 바늘 찾기가 쉬워지진 않고, 덜 맞는 정보가 늘면 오히려 추론이 흐려집니다. 그래서 하네스가 끼어듭니다. 오래된 히스토리는 눌러서 요약하고, 도구 출력은 대화창에 통째로 붓지 않고 파일로 빼두고, 지침은 필요할 때만 불러오고, 통과한 테스트는 조용히 넘기고 실패한 것만 올립니다. 마지막 게 특히 큽니다. 통과 로그 수천 줄을 매번 밀어 넣던 걸 실패만 남기게 바꿨더니 에이전트가 눈에 띄게 나아졌다는 얘기는 과장이 아닌 것 같습니다.

같은 문제를 다르게 푸는 게 서브 에이전트입니다. 이걸 역할 나누는 도구로만 보면 반만 본 것 같습니다. 프론트엔드 담당, 백엔드 담당, 리서치 담당으로 쓸 수도 있지만 더 중요한 건 떼어 놓는 데 있습니다. 위쪽 에이전트가 조사하면서 남긴 흔적, 실패한 시도, 중간에 생긴 잡음을 다 떠안으면 금방 오염됩니다. 아래 작업을 따로 돌리고 결과만 요약해서 올려보내게 하면 그게 그대로 방화벽 노릇을 합니다. 돈 쪽으로도 낫습니다. 메인은 비싼 모델로 두고, 범위가 좁은 일은 싼 모델에 맡기면 됩니다. 필요한 건 끝없는 컨텍스트가 아니라 더 잘 나누는 것이라고 생각합니다.

다만 나누고 나면 위쪽 에이전트가 받는 건 요약뿐입니다. 아래 에이전트가 잘 끝냈습니다, 라고 올려보내면 위쪽은 그걸 그대로 받아서 다음 일로 넘어갑니다. 아래에서 실제로 뭘 했는지는 안 보이니까요. 오염을 막으려고 떼어 놓았는데, 떼어 놓은 만큼 위쪽에서는 확인할 방법도 같이 줄어듭니다. 저는 그래서 요약과 함께 확인한 근거를 같이 올리게 하는 쪽이 맞을 것 같다고 생각하고 있는데, 그러면 또 올라오는 양이 늘어납니다. 어디까지 올려야 적당한지는 아직 감으로 정하고 있습니다. 너무 적으면 믿을 수가 없고 너무 많으면 떼어 놓은 의미가 없어집니다.

손발도 있어야 합니다. 모든 행동을 미리 도구로 정해둘 수는 없으니 셸이나 파일 읽고 쓰기 같은 범용 도구가 필요해집니다. 그런데 범용으로 풀어주는 순간 보안 문제가 따라옵니다. 모델이 만든 코드를 아무 데서나 돌리면 위험하니까요. 그래서 샌드박스가 붙습니다. 따로 떼어 놓은 환경에서 허락된 명령만 돌리고, 네트워크는 제한하고, 필요할 때 잠깐 만들어 쓰고 버리는 식입니다. 생각하는 쪽과 실행하는 쪽을 아예 나누자는 얘기도 같은 맥락이고, 둘을 나누면 공격받을 면이 줄어듭니다. 믿을 수 없는 코드가 자격 증명이 든 컨테이너에서 같이 돌아가는 구성은 지금 보면 좀 아찔합니다. 프롬프트 인젝션 한 번이면 환경 변수부터 새어 나갑니다.

마지막으로 훅과 백프레셔가 있습니다. 훅은 정해진 때에 자동으로 끼어드는 장치입니다. 파일을 고치면 타입 체크를 돌리고, 어떤 파일은 손대기 전에 승인을 받게 하고, 검증을 건너뛰는 커밋 옵션을 막고, 세션이 시작되면 상태 파일부터 읽게 합니다. 일하는 과정 곳곳에 규칙을 심어 두는 겁니다.

백프레셔는 그중에서도 좀 다르게 느껴집니다. 에이전트가 자기 결과를 스스로 확인하게 만드는 쪽입니다. 테스트, 커버리지, 브라우저 자동화, 타입 체크, 스모크 테스트. 다 됐다고 말하기 전에 실제 사용자처럼 한 번 눌러보게 하는 겁니다. 저는 이걸 QA라기보다 에이전트한테 현실을 다시 보여주는 장치로 보고 있습니다.

생각해 보면 백프레셔도 결국 다 됐다는 말을 그대로 믿을 수 없어서 생긴 장치 같습니다. 에이전트가 거짓말을 한다기보다는, 자기가 한 일이 잘됐다고 믿어버리는 쪽에 가깝습니다. 그래서 말로 확인시키는 것보다 테스트 결과를 들이미는 게 낫습니다. 됐다고 말하기 전에 실제로 돌려보게 하고, 그 결과가 빨간색이면 됐다는 말 자체를 못 하게 하는 겁니다. 사람한테 하듯이 잘 확인해 보세요, 라고 부탁하는 것보다 확인하지 않으면 다음으로 못 넘어가게 해두는 게 훨씬 잘 듣습니다.

그렇다고 훅을 걸어두면 끝나는 것도 아니었습니다. 훅을 열 개 넘게 걸어두고 넉 달 동안 한 번도 안 걸린 적이 있는데, 그때 안 걸리는 훅은 안전한 게 아니라 그냥 없는 거라는 걸 알았습니다.

그래서 요즘 제가 하는 일

이런 걸 몇 번 겪고 나서 지난 반년 동안 일하는 방식이 꽤 많이 달라졌습니다.

개발 일은 거의 다 에이전트한테 맡기고 있습니다. 에디터에서 제가 직접 코드를 친 시간은 많지 않습니다. Claude Code와 Codex가 실무자처럼 일했고, 저는 그들이 맥락을 제대로 알고 엉뚱한 실수를 덜 하도록 환경을 만드는 쪽에 붙어 있었습니다. 에이전트가 못 보는 게 코드가 아니라 그 코드 바깥이라는 얘기를 따로 쓴 적이 있는데, 그 바깥을 떠먹여 주는 일이 실제로는 이런 모양이었습니다. 제가 제일 많이 한 일은 코드를 쓰는 게 아니었습니다.

ADR을 썼습니다. 왜 이 구조를 골랐고 왜 다른 안을 버렸는지를 남겨 뒀습니다. ADR이 없으면 AI는 매번 새로운 최선을 찾겠다고 들고, 예전에 정한 걸 아무 생각 없이 엎어 버립니다. ADR이 있으면 반년 뒤에 새 세션이 열려도 같은 판단을 다시 쓸 수 있습니다. 저한테 ADR은 취향을 적어둔 메모라기보다 기억 장치에 가깝습니다.

쓰다 보니 이게 AI만의 문제도 아니라는 생각이 들었습니다. 왜 이렇게 했는지가 안 남아 있으면 사람도 똑같이 엎습니다. 반년 전에 정한 구조를 보고 이거 왜 이렇게 복잡하게 했지, 하고 고쳤다가 고치고 나서야 그때 그렇게 한 이유가 떠오르는 일은 누구한테나 생길 수 있습니다. AI는 그걸 매 세션 처음부터 겪는 거고, 사람은 몇 달에 한 번 겪는 정도의 차이일 뿐인 것 같습니다. 그러니 ADR을 AI 때문에 쓰기 시작했다고 해도, 결국 덕을 보는 건 반년 뒤의 저일 수도 있습니다. 에이전트용으로 적어둔 문서가 사람한테도 그대로 읽힌다는 게 저는 꽤 괜찮은 일이라고 봅니다. 따로 두 벌을 만들 필요가 없으니까요.

문서도 계속 고쳤습니다. AI가 코드를 읽다가 맥락을 놓치는 데가 보이면 그걸 설계 문서로 끌어올려서 적어 뒀습니다. 코드 주석 몇 줄로 버티려 하지 않았습니다. 주석은 자주 무시되는데 설계 문서는 기준으로 계속 다시 쓰이니까요.

스킬 정의도 손봤습니다. AI가 같은 실수를 반복하면 프롬프트 한 번 고치고 끝내지 않고 스킬 정의 자체를 바꿨습니다. 한 번 고치면 그 스킬을 부르는 모든 세션에 같은 수정이 들어가니까, 한 번 고치고 마는 게 아니라 계속 되풀이되는 교정이 됩니다.

에이전트 팀도 나눴습니다. 에이전트 하나가 전부 하게 두지 않고 전략, 실행, 검수, 발행처럼 역할을 쪼갰습니다. 사람 팀에서 책임을 나누는 이유랑 똑같습니다. 맥락을 너무 많이 쥔 에이전트 하나는 금방 흐려집니다.

훅과 MCP도 만들었습니다. 실수가 잘 나는 곳에 자동으로 걸리는 가드레일을 심었습니다. 어떤 파일을 건드리면 필요한 문맥을 자동으로 넣어주고, 커밋 전에 정해둔 규칙을 검사하고, 위험한 경로는 막는 식입니다. AI한테 잘 설명하는 것과는 다른 종류의 일입니다. 설명은 무시당할 수 있지만 구조는 피해 가기 어렵습니다.

계속 신경 쓰는 게 하나 더 있는데 토큰 낭비입니다. 어떤 문맥을 언제 넣을지, 어떤 일을 어떤 에이전트한테 줄지, 어디까지 자동화하고 어디서 끊을지. 이런 결정이 품질도 정하고 비용도 정합니다. 토큰 조금이면 끝날 일을 열 배로 돌린다고 결과가 꼭 좋아지지 않고, 오히려 흐려질 때도 많습니다. 그래서 요즘은 결과물만 보지 않고 그게 얼마의 돈과 얼마의 복잡도로 나왔는지를 같이 봅니다. 예전엔 거의 없던 감각입니다.

코드를 덜 치게 됐다고 일이 줄어든 건 아니었습니다. 일의 무게가 완전히 다른 쪽에 실리게 됐을 뿐입니다.

얇은 래퍼가 아니었습니다

저도 처음엔 좀 의심했습니다. 도구 인터페이스 하나 바꿨다고 점수가 그렇게 뛰나 싶었거든요. 그런데 실험한 사례들을 보면 차이가 생각보다 큽니다.

LangChain 쪽에서 자기 코딩 에이전트의 하네스만 손본 결과를 공개했는데, 검증 루프를 넣고, 끝내기 전에 체크리스트를 꼭 거치게 하고, 같은 편집을 반복하면 알아채서 끊는 장치를 붙였습니다. 모델은 그대로 두고 가중치도 전혀 안 건드렸는데 Terminal Bench 점수가 꽤 올라서 순위가 서른 위권에서 다섯 위권까지 갔습니다.

파일 편집 쪽 사례는 더 대놓고 보여줍니다. Can Bölük이 줄마다 짧은 내용 해시를 붙여서, 모델이 원문을 그대로 다시 쓰지 않고 해시로 위치를 가리키게 바꿨습니다. 여러 모델에 붙여봤더니 평균적으로 꽤 올랐고, 제일 약했던 모델은 거의 열 배가 뛰었습니다. 그러면서 출력 토큰은 오히려 줄었습니다. 편집에 실패하고 다시 시도하는 데 쓰던 토큰이 없어졌기 때문입니다.

모델이 갑자기 천재가 된 게 아니라, 일이 잘 풀리게 손잡이를 바꿔준 결과라고 생각합니다. 사람이 도구를 이해하기 쉽게 만든 게 아니라 모델이 도구 앞에서 덜 헤매게 만든 겁니다. 두 사례를 나란히 놓고 보면 둘 다 모델이 뭘 더 알게 한 게 아니었습니다. 앞쪽은 언제 멈추고 뭘 확인할지를 정해줬고, 뒤쪽은 어디를 고치라고 말하는 방법을 바꿨습니다. 성능이 오르면서 토큰이 줄어든 것도 그래서인 것 같습니다. 헤매는 데 쓰던 걸 안 쓰게 됐으니까요. 하네스를 손봐서 효과가 나는 건 대개 이런 모양인 것 같습니다. 능력을 더해주는 게 아니라 낭비를 걷어내는 쪽이요.

Anthropic이 비교한 다중 에이전트 사례도 기억에 남습니다. 같은 요구사항으로 에이전트 하나짜리와 세 개짜리를 붙여봤는데, 하나짜리는 20분 만에 싸게 끝났지만 핵심 기능이 아예 안 돌았습니다. 만든 게임이 입력에 반응을 안 했습니다. 세 개짜리는 여섯 시간에 스무 배 넘는 돈을 썼고, 실제로 돌아가는 걸 내놨습니다.

비용만 보면 싼 쪽이 나아 보입니다. 그런데 안 돌아가는 결과물은 단가를 따질 수가 없습니다. 그리고 그 세 에이전트가 계획, 생성, 평가로 나뉘어 있었다는 게 저는 제일 눈에 들어왔습니다. 평가를 다른 에이전트로 떼어낸 건 앞에서 말한 자기 평가 문제 때문일 겁니다. 자기가 만든 걸 자기가 채점하면 점수를 후하게 줍니다. 이건 모델이 얼마나 똑똑하냐의 차이가 아니라 일을 어떻게 나누고 누구한테 확인을 맡겼느냐의 차이였습니다.

그러면 엔지니어가 하는 일도 바뀔 수밖에 없다고 봅니다. 예전엔 좋은 엔지니어를 코드를 정확하게 쓰는 사람으로 떠올렸습니다. 지금도 그 능력은 중요합니다. 그런데 에이전트를 쓰다 보면 그 정확함이 필요한 곳이 달라집니다. 한 줄 한 줄 직접 치는 정밀함이 아니라, 에이전트가 제대로 일할 수 있는 환경을 짜는 정밀함입니다. 어떤 도구를 붙일지, 어떤 파일을 상태 저장소로 쓸지, 어떤 테스트를 단계마다 꼭 돌릴지, 실패하면 어디로 되돌릴지, 어느 수준에서 사람한테 넘길지. 손으로 하던 꼼꼼함을 이제는 환경을 짜는 데 쓰게 되는 것 같습니다.

상대에서 도구로

이렇게 적고 나니 제 쪽에서 달라진 게 하나 더 있습니다. AI를 대하는 태도입니다.

처음에 저는 LLM을 상대하고 있었습니다. 어떻게 물어야 잘 대답하는지, 어떤 표현에 민감한지, 어디까지 알아듣는지. 관심이 계속 모델 쪽에 가 있었습니다. 새 모델이 나오면 벤치마크부터 열어보고 뭐가 얼마나 좋아졌는지 확인했습니다.

지금은 그 관심이 거의 없어졌습니다. 언제부턴가 새 모델 발표를 안 열어보게 됐다고 따로 쓴 적도 있는데, 그땐 그냥 피곤해서 그런 줄 알았습니다. 지금 생각해 보면 피곤해서가 아니라 보는 곳이 달라진 거였습니다.

도구를 쓰는 사람은 도구 성능에 감탄하는 데 시간을 쓰지 않는 것 같습니다. 어디에 두고 어떻게 잡을지를 봅니다. 목수가 대패 얘기를 할 때 날이 얼마나 단단한지보다 어떤 나무에 어떤 각도로 쓰는지를 말하는 것처럼요. 도구가 좋아지는 건 반갑지만 그건 만드는 쪽 일이고, 쓰는 쪽 일은 그걸 어디에 놓느냐입니다.

다만 대패와 에이전트가 다른 게 하나 있습니다. 대패는 다 깎았다고 말하지 않습니다. 깎였는지는 목수가 손으로 쓸어보고 압니다. 에이전트는 다 했다고 먼저 말합니다. 그 말이 맞을 때가 많아서 더 곤란합니다. 열 번 중에 아홉 번 맞으면 열 번째에도 그냥 믿게 되니까요. 도구로 대한다는 게 그래서 말을 듣는 게 아니라 결과를 직접 쓸어보는 쪽이어야 할 것 같습니다. 에이전트가 하는 말은 참고로만 듣고, 됐는지는 테스트나 화면이나 로그가 대신 말해주게 해두는 겁니다. 그게 하네스가 하는 일의 꽤 큰 부분이라고 저는 보고 있습니다. 말을 덜 믿게 만드는 장치라고 하면 좀 야박하게 들리는데, 사람끼리도 중요한 일은 그렇게 하니까 이상한 일은 아닌 것 같습니다. 상대가 사람이든 에이전트든요.

그래서 요즘은 묻는 질문도 다릅니다. 예전엔 이 모델이 이걸 할 수 있나를 물었다면, 지금은 이 일을 어떤 조합으로 돌리는 게 맞나를 묻습니다. 어떤 단계를 자동화하고 어디서 끊을지, 어떤 문맥을 언제 넣을지, 어디서 사람이 봐야 할지요. 모델 능력치 얘기가 아니라 배치 얘기입니다.

이게 마냥 편하지는 않습니다. 모델을 상대할 때는 안 되면 모델 탓을 할 수 있었는데, 환경을 만드는 쪽으로 오니까 안 되는 게 대부분 제가 만든 것 탓이 됩니다. 도구 탓을 할 핑계가 없어지는 겁니다. 그래도 일이 앞으로 가는 데는 이쪽이 훨씬 낫습니다. 탓할 데가 없으면 고칠 데를 찾게 되고, 찾기 시작하면 대개 찾아집니다. 상태 파일이 없거나, 확인하는 단계가 없거나, 도구 하나가 손에 안 맞거나 하는 식으로요.

저는 프롬프트 엔지니어링과 하네스 엔지니어링이 여기서 제일 다르다고 생각합니다. 프롬프트는 여전히 모델을 상대하는 일입니다. 잘 물어보는 기술이니까요. 하네스는 모델을 상대하지 않고 모델이 놓일 환경을 만듭니다. 모델이 상대에서 재료로 바뀌는 건데, 그렇게 보기 시작하면 무엇을 공부해야 하는지도 같이 바뀝니다. 모델의 특성을 쫓아가는 게 아니라 우리 일이 어떻게 생겼는지를 보게 됩니다.

모델이 더 좋아지면 하네스가 덜 중요해질 거라는 말도 가끔 듣는데, 저는 반대일 것 같습니다. 프롬프트 엔지니어링도 아직 완전히 사라지지 않았고, 하네스는 모델이 좋아질수록 더 중요해질 것 같습니다. 센 모델일수록 할 수 있는 게 많고, 할 수 있는 게 많을수록 고삐가 중요해집니다. 힘이 약한 말은 멀리 못 가지만 힘센 말은 방향이 틀리면 더 큰 사고를 냅니다.

그래서 코딩 에이전트가 기대만큼 일을 못 할 때 저는 모델부터 탓하지 않으려고 합니다. 상태 파일이 없고, 도구 인터페이스가 거칠고, 확인하는 루프가 없고, 실패를 걸러줄 훅이 없고, 컨텍스트가 이미 썩어가는데 그 위에 로그를 계속 쌓고 있는 경우가 꽤 자주 있습니다. 그럴 땐 모델을 바꾸는 것보다 하네스를 손보는 쪽에서 답이 나올 때가 많았습니다. 모델은 생각보다 괜찮은데, 자꾸 문제가 되는 건 그 모델을 일하게 만든 환경 쪽이었습니다.

지금은 처음 들뜨던 때는 지났지만 안정됐다고 하기도 어려운, 그 사이 어딘가인 것 같습니다. 뭔가 크게 바뀌고 있는데 어디서 멈출지는 아무도 모릅니다. 이 시기에 제가 배운 건 어떤 도구를 익히느냐보다 바뀌는 걸 얼마나 빨리 받아들이느냐가 더 중요하다는 것 하나입니다.

1년 반 동안 세 번 바뀌는 사이에 도구는 계속 바뀌었고 이해하는 감각만 남았습니다. 프롬프트 엔지니어링을 배운 게 쓸모없어진 것도 아니었습니다. 그걸 배우면서 AI가 어떻게 동작하는지 이해하는 감이 생겼고, 그 감은 에이전트를 쓰는 지금도 그대로 쓰고 있습니다. 다음에 또 뭐가 나와도 아마 비슷할 것 같습니다.

댓글

이 블로그의 인기 게시물

봉제인형 사진 한 장, ChatGPT와 Gemini의 다른 대답

AI 부업으로 3개월에 2달러 벌고 50달러를 썼다는 증언을 봤습니다. 그 위에 광고가 네 겹입니다

구글은 서울에서 모델이 아니라 에이전트를 판다