에이전틱스
도메인 8. 툴과 MCP

8.1툴 구현

예상 학습 시간 30

툴은 정의하는 것보다 호출을 받아 처리하는 쪽에서 실수가 납니다. 이 수업에서는 왕복 순서와 오류 처리, 클라이언트 툴과 서버 툴의 차이를 코드 수준에서 다룹니다.

왕복 순서

툴을 붙이면 한 번의 요청이 여러 번의 왕복이 됩니다. 순서를 틀리면 다음 요청이 거부되거나 모델이 같은 호출을 반복합니다.

  1. 도구 목록과 함께 요청을 보냅니다.
  2. 응답의 stop_reasontool_use이면 tool_use 블록에 이름과 인자가 옵니다.
  3. 모델 응답 전체를 assistant 메시지로 그대로 붙입니다.
  4. 도구를 실행하고 user 메시지의 tool_result 블록으로 결과를 붙입니다. tool_use_id로 짝을 표시합니다.
  5. 다시 요청을 보냅니다.
messages.append({"role": "assistant", "content": resp.content})

results = []
for block in resp.content:
    if block.type != "tool_use":
        continue
    try:
        out = execute(block.name, block.input)
        results.append({"type": "tool_result", "tool_use_id": block.id, "content": out})
    except ToolError as e:
        results.append({"type": "tool_result", "tool_use_id": block.id,
                        "content": f"실패: {e}", "is_error": True})
messages.append({"role": "user", "content": results})

한 응답에 호출이 여럿 올 수 있습니다. 모두 실행해 결과를 한 번에 돌려줘야 합니다. 일부만 처리하면 짝이 맞지 않아 다음 요청이 거부됩니다.

실패를 결과로 돌려주기

도구 실행이 실패했을 때 예외를 그대로 위로 던져 루프를 끝내는 구현이 흔합니다. 대개 잘못된 선택입니다.

실패도 결과입니다. 오류 표시를 붙여 돌려주면 모델이 다른 인자로 재시도하거나 다른 경로를 찾습니다. 아무 것도 돌려주지 않으면 대화가 끊기고, 사용자는 이유 없이 멈춘 화면을 봅니다.

돌려줄 때 행동으로 이어지는 문구를 씁니다.

나쁨:  "Error: 500"
나쁨:  "Traceback (most recent call last): ..."
나음:  "조회 실패: 주문번호 형식이 올바르지 않습니다. 'ORD-' 로 시작하는 12자리여야 합니다."

무엇이 잘못됐고 어떻게 고치면 되는지 적으면 다음 호출이 성공할 확률이 크게 오릅니다. 스택 트레이스를 그대로 넣는 것은 컨텍스트만 먹고 도움이 안 됩니다.

다만 예외를 던져야 하는 경우도 있습니다. 복구 불가능한 상황 — 인증 실패, 설정 오류 — 은 모델이 다시 시도해도 소용없으므로 루프를 끝내고 사람에게 알리는 편이 맞습니다.

도구 설명이 곧 선택 기준

모델은 도구 이름과 설명만 보고 무엇을 쓸지 정합니다. 설명이 부실하면 오호출이 늘고, 그 비용은 매 요청에 붙습니다.

효과가 큰 설명에는 공통점이 있습니다.

  • 언제 쓰는지를 적습니다. "주문 조회" 대신 "주문번호로 배송 상태와 결제 정보를 조회합니다".
  • 언제 쓰지 않는지도 적습니다. "환불 처리에는 쓰지 마십시오. refund_order를 사용하십시오."
  • 인자의 형식을 적습니다. 형식이 정해져 있으면 스키마와 설명 양쪽에 씁니다.
  • 결과의 형태를 알립니다. 무엇이 돌아오는지 알면 다음 행동을 계획합니다.
{
  "name": "search_orders",
  "description": (
      "고객 이메일로 최근 주문 목록을 조회합니다. 최대 20건까지 반환하며 "
      "최신순입니다. 특정 주문의 상세 정보가 필요하면 get_order를 사용하십시오."
  ),
  "input_schema": {
      "type": "object",
      "properties": {"email": {"type": "string", "description": "고객 이메일 주소"}},
      "required": ["email"],
  },
}

도구가 비슷비슷하면 모델이 헷갈립니다. 이름과 설명에서 차이가 분명히 드러나야 합니다.

누가 실행하는가

도구는 실행 주체에 따라 성격이 다릅니다.

클라이언트 툴 — 우리 코드가 실행합니다. 사내 데이터베이스 조회, 내부 API 호출처럼 우리만 할 수 있는 일입니다. 정의도 실행도 우리 몫이고, 그만큼 권한과 오류 처리를 직접 챙겨야 합니다.

서버 툴 — 우리가 실행하지 않고 제공되는 기능을 켜서 씁니다. 웹 검색이나 코드 실행 같은 것입니다. 실행 환경을 만들 필요가 없어 빠르게 붙일 수 있습니다.

구현에서 갈리는 지점이 있습니다. 클라이언트 툴은 호출을 받아 실행하고 결과를 되돌리는 왕복을 우리가 관리해야 하지만, 서버 툴은 그 과정이 우리 코드 밖에서 처리됩니다.

승인 패턴도 함께 정합니다. 되돌릴 수 없는 동작은 실행 전에 확인을 받습니다. 확인 대상을 넓게 잡으면 사람이 습관적으로 승인하게 되므로, 정말 위험한 것만 골라야 방어가 유지됩니다.

시험 함정

  • 도구 결과를 assistant 역할로 넣는 구현 — user 역할의 tool_result 블록이 맞습니다.
  • 한 응답의 여러 도구 호출 중 하나만 처리하는 선택지 — 짝이 맞지 않아 다음 요청이 거부됩니다.
  • 도구 실행 실패 시 예외를 던져 루프를 끝내는 처리 — 복구 가능한 실패는 결과로 돌려줘야 합니다.
  • 오류 결과에 스택 트레이스를 그대로 넣는 구현 — 컨텍스트만 먹고 다음 행동에 도움이 안 됩니다.
  • 도구 설명에 언제 쓰는지만 적는 접근 — 언제 쓰지 않는지를 함께 적어야 오호출이 줄어듭니다.
  • 모든 도구 호출에 사람 승인을 거는 설계 — 습관적 승인으로 방어가 무력해집니다.

실습 시나리오

실제 시험과 같은 형식의 시나리오 문제입니다.

주문 조회 도구가 잘못된 형식의 주문번호를 받으면 예외를 던지고, 그 예외가 에이전트 루프를 종료시킵니다. 사용자는 아무 설명 없이 멈춘 화면을 봅니다. 가장 적절한 수정은 무엇입니까?

빌드 연습 · 왕복과 오류 처리를 갖춘 도구 붙이기

35
  1. 1.도구 정의

    언제 쓰고 언제 쓰지 않는지가 담긴 설명과 스키마를 작성합니다.

    기대 결과 · 비슷한 도구와 구분되는 문구가 들어 있습니다.

  2. 2.왕복 구현

    assistant와 tool_result 순서를 지켜 한 바퀴 돌립니다.

    기대 결과 · tool_use_id가 짝을 이룹니다.

  3. 3.다중 호출

    한 응답에 두 도구가 호출되게 만들고 모두 처리합니다.

    기대 결과 · 결과 둘이 한 user 메시지에 담깁니다.

  4. 4.복구 가능한 실패

    형식 오류를 오류 결과로 돌려주고 올바른 형식을 알립니다.

    기대 결과 · 모델이 인자를 고쳐 재호출합니다.

  5. 5.복구 불가 실패

    인증 오류는 루프를 끝내고 사람에게 알리도록 구분합니다.

    기대 결과 · 두 경우의 처리가 코드에서 갈립니다.

출처 및 더 읽기