기술
19 분 소요

AI 도구 개발은 기존 서비스 개발과 완전히 다르다

AI 에이전트가 호출하는 도구를 만들고 있다

AI 에이전트가 온체인 거래를 실행하는 MCP 도구를 만들고 있다. 체인 읽기, 브릿지, 스왑, 거래소 주문까지 — AI가 직접 판단하고 실행하는 도구들이다.

처음엔 기존 API 개발하듯 접근했다. 엔드포인트 설계하고, 입력 검증하고, 응답 포맷 맞추고. 그런데 만들면 만들수록 기존과 같은 방식이 통하지 않았다. 설계 철학부터 테스트 방법론까지, 거의 모든 게 달랐다.


1. "누가 호출하는가"가 모든 걸 바꾼다

기존 API를 호출하는 건 사람이거나 다른 서비스다. 둘 다 결정론적이다. 프론트엔드 개발자가 POST /orders를 호출하면, 서버 안에서 잔고 확인 → approve → 서명 → 제출이 정해진 순서로 실행된다. 개발자가 이 순서를 코드로 확정했기 때문이다.

AI 도구의 호출자는 AI 에이전트다. 비결정론적이다. 같은 목표를 줘도 AI는 매번 다른 순서로 도구를 조합할 수 있다. 잔고가 부족하면 bridge를 먼저 할 수도 있고, 가스비가 비싸면 다른 체인으로 우회할 수도 있다. 개발자가 예상하지 못한 조합을 AI가 시도하는 것이 정상이다.

이 차이를 체감한 건 구조를 바꿨을 때다. 처음에는 venue.trade_polymarket 하나에 모든 단계를 하드코딩했다. Polymarket 전용이었고, 다른 거래소를 추가하려면 venue.trade_xxx를 처음부터 만들어야 했다.

이걸 chain.execute, defi.bridge, defi.approve 같은 조합 블록으로 쪼갰다. 그러자 AI가 이 블록들을 자유롭게 조합해서 새로운 프로토콜에도 코드 변경 없이 대응하기 시작했다.

핵심 전환은 이것이다: "서비스를 만든다"에서 "AI의 행동 공간(action space)을 설계한다"로 바뀐다.

2. 스키마가 곧 프롬프트다

기존 API 개발에서 파라미터 이름은 크게 중요하지 않았다. scsource_chain이든, Swagger 문서를 보고 코드에서 매핑하면 끝이었다. 호출하는 개발자는 문서를 읽을 수 있으니까.

AI 도구에서는 완전히 다른 문제가 된다. AI가 도구를 선택하고 파라미터를 채우는 근거는 도구의 이름, description, 파라미터 설명뿐이다. 별도의 Swagger 문서를 참조하지 않는다. 스키마 자체가 AI가 읽는 유일한 문서다.

그래서 파라미터를 amt에서 amount_usd로 바꾸는 것만으로 AI의 성공률이 달라질 수 있다. description에 "Cross-chain token bridge. Handles gas, approve, deposit automatically"라고 쓰는 것과 "Bridge tool"이라고 쓰는 건 AI 입장에서 완전히 다른 도구다.

도구 스키마 변경은 코드 리팩토링이 아니라 프롬프트 엔지니어링이다. A/B 테스트 대상이 코드가 아니라 description 문구가 되는 셈이다.

3. 에러는 "실패 알림"이 아니라 "의사결정 데이터"

기존 서비스에서 에러 처리는 비교적 단순하다. HTTP 400이면 프론트엔드에서 "잔고가 부족합니다"를 표시하고, 500이면 "서버 오류"를 표시한다. 에러 메시지의 소비자는 사람이고, 사람이 다음 행동을 판단한다.

AI 도구에서 에러의 소비자는 AI다. AI는 에러 메시지를 보고 다음에 어떤 도구를 호출할지 결정한다. 그래서 에러 메시지의 품질이 AI의 판단 품질을 직접 좌우한다.

실제로 겪은 문제가 있다. venue 도구에서 잔고 부족 에러가 발생했는데, 이게 MCP 프로토콜 에러(JSON-RPC error)로 반환됐다. AI가 받은 건 "Tool call failed"라는 메시지뿐이었다. 실제로는 "not enough balance on Polygon"이라는 구체적 메시지가 있었는데, 프로토콜 에러로 감싸지면서 내용이 사라졌다.

만약 AI가 "not enough balance on Polygon"을 봤다면? defi.bridge로 다른 체인에서 USDC를 가져오는 판단을 했을 것이다. 하지만 "Tool call failed"만 보고는 그냥 포기했다.

수정은 간단했다. 비즈니스 에러를 프로토콜 에러가 아닌 tool result의 isError 플래그로 전달한 것이다. 그 순간부터 AI가 에러 내용을 읽고 자율적으로 대응하기 시작했다.

4. 출력에 "중간 맥락"을 담아야 한다

기존 서비스의 응답은 최종 결과면 충분하다. { "status": "success", "tx_hash": "0x..." } — 호출한 쪽에서 성공을 확인하면 끝이다. 중간에 어떤 과정을 거쳤는지는 내부 구현 디테일이고, 숨기는 게 맞다.

AI 도구는 다르다. AI가 "approve는 성공했는데 order 제출에서 실패했다"고 판단하려면, 중간 과정이 출력에 드러나야 한다.

json
{ "status": "submitted", "steps": [ { "action": "balance_check", "result": "USDC: 4.2" }, { "action": "approve", "status": "confirmed" }, { "action": "order_submit", "status": "submitted" } ] }

steps 배열이 없으면 AI는 최종 상태만 보고 판단한다. 중간에 뭐가 잘못됐는지 추론할 근거가 없다. 하지만 너무 많은 정보를 담으면 토큰을 낭비하고 AI가 오히려 핵심을 놓칠 수 있다. AI에게 필요한 것만 간결하게 담는 것이 설계 과제다.

5. 추상화 레벨의 기준이 다르다

기존 서비스는 사람의 인지 부하를 기준으로 API를 나눈다. REST는 리소스 중심(/users, /orders)이고, 사람이 이해하기 쉬운 단위로 쪼갠다.

AI 도구는 AI의 추론 효율이 기준이다. 이건 미묘한 균형이다:

  • 너무 저수준이면 AI가 너무 많은 단계를 밟아야 한다. 매 단계마다 토큰을 소모하고, 단계가 늘수록 실수 확률도 올라간다.
  • 너무 고수준이면 AI가 예외 상황에 유연하게 대처를 못 한다. "bridge 후 approve 후 trade"가 하나로 묶여 있으면, "bridge만 하고 싶을 때"를 처리할 수 없다.

이 균형을 잡기 위해 Layer 0~3으로 나눴다:

LayerAI에게 노출?이유
L0 (서명, nonce, gas)안 함AI가 알 필요 없는 기계적 세부사항. 잘못 건드리면 위험하기만 하다
L1 (chain.execute)노출범용 블록. 새 프로토콜을 만나면 AI가 이걸로 직접 조합한다
L2 (defi.bridge)주로 사용AI의 일반적 판단 단위에 맞는 추상화. 대부분의 작업이 이 레벨에서 해결된다
L3 (polymarket.trade)편의 제공자주 쓰는 패턴을 한 단계로 축약. 토큰 절약 효과도 있다

기존에는 없던 설계 판단이다 — "이 API를 사용하는 AI의 인지 부하가 어느 정도인가?"를 기준으로 추상화 레벨을 정하는 것.

6. 밸리데이션이 아니라 가드레일

기존 서비스의 input validation은 입력이 올바른지 검사한다. if amount <= 0이면 거부하고, 주소 포맷이 잘못되면 에러를 뱉는다. 호출자가 올바른 입력을 보낼 책임이 있고, 서버는 그걸 검증만 하면 된다.

AI 도구에는 다른 레이어가 필요하다 — AI의 행동이 안전한지 검사하는 가드레일이다.

차이를 예로 들면: AI가 chain.execute를 호출하면서 파라미터는 문법적으로 완벽하다. 주소 포맷도 맞고, 금액도 양수다. input validation은 통과한다. 하지만 그 요청이 $10,000을 unverified 컨트랙트에 보내는 것이라면?

그래서 chain.execute 내부에서 자동으로 chain.simulate를 실행해 revert를 사전 감지하고, 금액이 일정 기준을 넘으면 AI에게 확인을 요청하고, defi.analyze로 컨트랙트 위험도를 평가한다. 이건 입력 검증이 아니라 행동 안전성 검증이다.

7. 멱등성: 선택에서 필수로

기존 서비스에서 멱등성은 "좋은 관행"이었다. PUT은 멱등이고, POST는 아닐 수 있으며, 개발자가 중복 호출을 코드로 제어했다.

AI 도구에서 멱등성은 생존 조건이다. AI는 같은 도구를 의도치 않게 두 번 호출할 수 있다:

  • 응답이 길어서 중간에 잘렸을 때 → 재시도
  • 에러로 판단하고 재시도할 때 → 실은 성공이었을 수 있음
  • context가 압축되면서 이전 호출을 잊었을 때 → 동일 요청을 다시 보냄

돈이 움직이는 도구에서 이건 치명적이다. defi.bridge가 두 번 실행되면 의도한 금액의 두 배가 이동한다.

기존에는 클라이언트(호출하는 코드)가 중복을 방지할 책임이 있었다. AI 도구에서는 그 책임을 AI에게 맡길 수 없다. 도구 자체가 멱등 키(idempotency key)를 요구하거나, "30초 내 동일한 bridge 요청이 이미 실행됨"을 감지해서 스스로 방어해야 한다.

8. 상태 관리: Context Window가 상태 저장소

기존 서비스에서 상태는 서버가 관리한다. 세션, DB, Redis 캐시. 클라이언트는 stateless하게 요청만 보내면 된다.

AI 도구에서는 도구 자체가 stateless다. 그런데 누군가는 상태를 들고 있어야 한다 — 그게 AI의 context window다.

구체적으로: 첫 번째 호출에서 prepare_bridge{ approval_tx: "0x...", swap_tx: "0x..." }를 반환한다. 두 번째 호출에서 AI가 그 approval_tx를 꺼내 chain.execute(approval_tx)에 넘기고, 세 번째 호출에서 swap_tx를 사용한다. 이 흐름이 가능한 이유는 AI가 이전 응답을 context에 가지고 있기 때문이다.

그래서 도구 출력 설계가 중요하다. "다음 단계에 필요한 값"이 출력에 명시적으로 포함되어야 한다. 기존에는 서버 세션에 넣으면 끝이었지만, AI 도구에서는 출력 자체가 상태 전달 메커니즘이다. 출력에서 빠뜨리면 AI가 다음 단계를 수행할 방법이 없다.

9. 토큰 경제학: 새로운 비용 차원

기존 서비스의 비용은 서버 compute + DB I/O + 네트워크다. API 응답이 1KB든 100KB든 비용 차이는 미미하다.

AI 도구에서는 토큰 수 x 모델 단가가 비용이다. 그리고 토큰은 도구 정의(스키마)와 출력 모두에서 소모된다.

계산해 보면: 도구 10개의 스키마가 평균 500 토큰이면, 도구 정의만으로 5,000 토큰이 매 요청의 context에 깔린다. 여기에 매 호출 출력이 누적되면 하나의 태스크에 12,000+ 토큰이 "인프라 비용"으로 소모된다.

이건 API 응답 크기를 신경 쓰지 않던 기존 개발과는 완전히 다른 비용 구조다. 설계 단계에서부터:

  • 도구 수를 최소화한다. 전용 도구 30개보다 범용 도구 10개가 스키마 토큰을 절약한다
  • 출력은 AI에게 필요한 것만 담는다. 사람을 위한 friendly message는 토큰 낭비다
  • Layer 3 같은 축약 도구는 토큰 효율 목적도 있다. 7단계를 1단계로 줄이면 중간 출력 6개분의 토큰이 절약된다

10. 테스트: 결정론 vs 비결정론

이게 가장 근본적인 차이라고 생각한다.

기존 E2E 테스트는 결정론적이다. 로그인 버튼 클릭 → ID/PW 입력 → 대시보드 렌더링 확인 → assert(title === "Dashboard"). 입력이 같으면 결과가 같다. 매번 같은 순서로 같은 assertion을 실행한다.

AI 도구 E2E는 비결정론적이다. AI에게 "ETH를 $5 매수해줘"라는 목표를 부여하면, AI가 어떤 도구를 어떤 순서로 호출할지 미리 알 수 없다. 잔고가 충분하면 바로 매수하겠지만, 부족하면 bridge를 먼저 하거나, 다른 체인에서 자금을 가져올 수도 있다. 경로는 매번 달라질 수 있지만, 검증할 건 하나다 — "최종적으로 ETH 포지션이 생겼는가?"

테스트 방법론의 차이를 정리하면:

구분기존 서비스AI 도구
단위 테스트함수 input→output도구 input→output (여기까진 동일)
통합 테스트API 호출 시퀀스 고정도구 조합이 유효한지 (순서 무관)
E2EUI flow 재현목표 부여 → 최종 상태 검증
에러 테스트에러 코드 확인AI가 에러를 보고 올바르게 대응하는지

마지막 줄이 핵심이다. "AI가 에러를 보고 올바르게 대응하는지"는 기존에 없던 테스트 카테고리다. 서명이 실패했을 때 AI가 "다시 시도"가 아니라 "credential 재발급"을 선택하는지 — 이건 도구의 에러 메시지 품질이 테스트 통과를 좌우한다.

11. 설계 방법론: 관찰 → 조정의 반복

기존 서비스 개발은 Top-Down이다. 요구사항 → API 설계 → DB 스키마 → 구현 → 테스트. "사용자가 주문을 생성한다"라는 요구사항이 있으면 POST /ordersorders 테이블 → 핸들러 구현으로 이어진다.

AI 도구 개발은 Outside-In이다. AI에게 목표를 주고 관찰한다. AI가 어디서 막히는지, 어떤 도구가 부족한지, 어떤 에러 메시지가 불친절한지를 확인하고, 도구를 수정한 뒤 다시 관찰한다.

실제로 이 루프를 반복했다:

  1. AI가 거래를 못 한다 → 서명 로직에 버그가 있었다
  2. AI가 에러를 못 본다 → 에러 전달 방식이 잘못됐다
  3. AI가 이전 결과를 기억 못 한다 → 출력 구조에 다음 단계 값이 빠져 있었다
  4. AI가 저수준 작업에 시간을 쓴다 → 추상화 계층이 필요했다

설계서를 먼저 쓰고 구현한 게 아니다. AI가 도구를 실제로 사용하는 걸 보고 나서야 올바른 추상화 레벨을 알 수 있었다. "설계 → 구현"이 아니라 "관찰 → 조정"의 반복이다.

12. 확장성: 조합으로 무한 확장

기존 서비스에서 새 거래소를 추가하려면 전용 어댑터를 구현하고, 전용 API를 만들고, 전용 테스트를 작성한다. n개 거래소 = n개 구현이다.

AI 도구에서는 다르다. L1 도구 4개 + L2 도구 4개만 잘 만들어두면, AI가 이것들을 조합해서 사실상 무한한 venue에 대응한다. "이 DEX는 chain 137에 있고, approve + swap 호출이 필요하다"는 걸 AI가 스스로 파악하고, 기존 도구를 조합해서 실행한다. 새 코드가 필요 없다.

도구 개수가 늘어나는 게 아니라 조합 가능성이 늘어나는 방식으로 확장된다. Unix 철학 — "작은 도구를 파이프로 연결한다" — 의 AI 버전이다.


한 문장으로

기존 서비스 개발: 사람의 의도를 코드로 확정하고, 코드가 맞는지 테스트한다. AI 도구 개발: AI의 행동 공간을 설계하고, AI가 그 공간 안에서 올바른 판단을 할 수 있는지 테스트한다.

"제어"에서 "환경 설계"로의 전환이다.

© 2025 Forrest Kim. Copyright.

contact: humblefirm@gmail.com