← 블로그 목록
가이드2026-05-21

AI 전화 에이전트에 Tool 연동하기: 예약 조회, DB 검색, 외부 API 호출

AI 전화 에이전트에 Tool 연동하기: 예약 조회, DB 검색, 외부 API 호출

ClawOps - AI 전화 에이전트 플랫폼

AI 전화 에이전트가 "안녕하세요"만 말할 수 있다면 ARS와 다를 게 없습니다. 진짜 가치는 AI가 대화 중에 시스템을 조회하고, 예약을 잡고, 주문을 확인할 수 있을 때 나옵니다.

ClawOps Voice Agent SDK는 @agent.tool 데코레이터(Python)와 agent.tool() 메서드(Node.js)로 AI에게 도구를 부여합니다. 이 글에서는 커스텀 Tool 등록부터 내장 Tool(통화 전환, DTMF), MCP 서버 연동까지 실전 패턴을 코드와 함께 다룹니다.

Tool이란?

Tool은 AI가 대화 맥락에서 "이 함수를 호출해야겠다"고 판단하면 자동으로 실행하는 함수입니다. OpenAI의 Function Calling과 동일한 개념이지만, ClawOps SDK는 함수 시그니처에서 스키마를 자동 생성해줘서 별도의 JSON Schema 작성이 필요 없습니다.

예시 흐름:

  1. 고객: "제 주문 상태 좀 확인해주세요. 주문번호는 A1234입니다."
  2. AI → check_order("A1234") 자동 호출
  3. Tool 함수가 DB를 조회해 "배송 중, 내일 도착 예정" 반환
  4. AI: "주문번호 A1234는 현재 배송 중이고, 내일 도착 예정입니다."

기본: 커스텀 Tool 등록

Python

from clawops.agent import ClawOpsAgent, OpenAIRealtime
import asyncio

agent = ClawOpsAgent(
    from_="07012341234",
    session=OpenAIRealtime(
        system_prompt="쇼핑몰 고객 상담원입니다. 주문 조회와 반품 접수를 도와주세요.",
        voice="marin",
        language="ko",
    ),
)

@agent.tool
async def check_order(order_id: str) -> str:
    """주문 상태를 확인합니다. 고객이 주문 번호를 말하면 이 함수를 호출하세요."""
    # 실제로는 DB나 ERP API를 조회
    orders = {
        "A1234": "배송 중 — 내일 도착 예정",
        "B5678": "결제 완료 — 오늘 출고 예정",
    }
    return orders.get(order_id, f"주문번호 {order_id}을 찾을 수 없습니다.")

@agent.tool
async def request_return(order_id: str, reason: str) -> str:
    """반품을 접수합니다. 주문번호와 사유가 필요합니다."""
    # 실제로는 반품 시스템에 등록
    return f"주문 {order_id}의 반품 요청이 접수되었습니다. 3일 이내 수거됩니다."

asyncio.run(agent.serve())

핵심 규칙:

  • Tool 함수는 반드시 async 함수여야 합니다
  • 반환값은 str 타입이어야 합니다
  • 함수의 독스트링이 AI에게 전달되는 도구 설명이 됩니다 — 명확하게 작성하세요

Node.js / TypeScript

import { ClawOpsAgent, OpenAIRealtime } from '@teamlearners/clawops/agent';

const agent = new ClawOpsAgent({
  from: '07012341234',
  session: new OpenAIRealtime({
    systemPrompt: '쇼핑몰 고객 상담원입니다.',
    voice: 'marin',
    language: 'ko',
  }),
});

agent.tool(
  'check_order',
  '주문 상태를 확인합니다. 고객이 주문 번호를 말하면 호출하세요.',
  { orderId: { type: 'string' } },
  async ({ orderId }) => {
    const orders: Record<string, string> = {
      'A1234': '배송 중 — 내일 도착 예정',
      'B5678': '결제 완료 — 오늘 출고 예정',
    };
    return orders[orderId] ?? `주문번호 ${orderId}을 찾을 수 없습니다.`;
  },
);

agent.tool(
  'request_return',
  '반품을 접수합니다. 주문번호와 사유가 필요합니다.',
  { orderId: { type: 'string' }, reason: { type: 'string' } },
  async ({ orderId, reason }) => {
    return `주문 ${orderId}의 반품 요청이 접수되었습니다. 3일 이내 수거됩니다.`;
  },
);

await agent.serve();

내장 Tool: 전화 제어

ClawOps SDK에는 통화 제어를 위한 4가지 내장 도구가 있습니다:

도구상수용도
hang_upBuiltinTool.HANG_UP대화 완료 후 전화 종료
collect_dtmfBuiltinTool.COLLECT_DTMF키패드 입력 수집 (본인 인증, 메뉴 선택)
send_dtmfBuiltinTool.SEND_DTMFDTMF 신호 전송 (ARS 탐색, 내선번호)
transfer_callBuiltinTool.TRANSFER_CALL다른 번호로 통화 전환 (상담원 연결)

기본적으로 모든 내장 도구가 활성화됩니다. 필요한 것만 선택할 수 있습니다:

from clawops.agent import ClawOpsAgent, BuiltinTool, OpenAIRealtime

# 기본: 모든 내장 도구 활성화
agent = ClawOpsAgent(from_="07012341234", session=session)

# hang_up만 사용 (다른 내장 도구 비활성화)
agent = ClawOpsAgent(
    from_="07012341234",
    session=session,
    builtin_tools=[BuiltinTool.HANG_UP],
)

# 내장 도구 전부 비활성화 (커스텀 Tool만 사용)
agent = ClawOpsAgent(
    from_="07012341234",
    session=session,
    builtin_tools=BuiltinTool.NONE,
)

ARS 아웃바운드 봇 처럼 발신 후 ARS를 탐색하는 경우:

agent = ClawOpsAgent(
    from_="07012341234",
    session=session,
    builtin_tools=[BuiltinTool.HANG_UP, BuiltinTool.SEND_DTMF],
)

실전 사례 1: 병원 예약 확인 + DB 조회

from clawops.agent import ClawOpsAgent, OpenAIRealtime
import asyncio
import aiohttp

agent = ClawOpsAgent(
    from_="07012341234",
    session=OpenAIRealtime(
        system_prompt="""치과 예약 상담원입니다.
- 예약 확인, 변경, 취소를 도와줍니다.
- 이름과 전화번호 뒷자리로 본인 확인 후 예약 정보를 알려주세요.
- 예약 변경은 최소 24시간 전에만 가능합니다.""",
        voice="marin",
        language="ko",
    ),
)

@agent.tool
async def find_appointment(name: str, phone_last4: str) -> str:
    """환자 예약을 조회합니다. 이름과 전화번호 뒷자리가 필요합니다."""
    async with aiohttp.ClientSession() as session:
        async with session.get(
            "https://api.my-clinic.com/appointments",
            params={"name": name, "phone_last4": phone_last4},
        ) as resp:
            if resp.status == 200:
                data = await resp.json()
                if data["appointments"]:
                    appt = data["appointments"][0]
                    return f"{appt['date']} {appt['time']}{appt['doctor']} 선생님 예약이 있습니다."
                return "해당 정보로 예약을 찾을 수 없습니다."
            return "시스템 오류가 발생했습니다. 잠시 후 다시 시도해주세요."

@agent.tool
async def cancel_appointment(appointment_id: str) -> str:
    """예약을 취소합니다. 예약 ID가 필요합니다."""
    async with aiohttp.ClientSession() as session:
        async with session.delete(
            f"https://api.my-clinic.com/appointments/{appointment_id}"
        ) as resp:
            if resp.status == 200:
                return "예약이 취소되었습니다."
            return "취소 처리 중 오류가 발생했습니다."

asyncio.run(agent.serve())

실전 사례 2: 쇼핑몰 주문 + 배송 추적

@agent.tool
async def track_delivery(tracking_number: str) -> str:
    """배송 상태를 추적합니다. 운송장 번호가 필요합니다."""
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"https://api.delivery-tracker.com/v1/tracks/{tracking_number}"
        ) as resp:
            data = await resp.json()
            status = data.get("status", "알 수 없음")
            location = data.get("location", "")
            return f"배송 상태: {status}. 현재 위치: {location}"

@agent.tool
async def get_store_hours(branch: str) -> str:
    """매장 영업시간을 조회합니다."""
    hours = {
        "강남점": "10:00-22:00 (연중무휴)",
        "홍대점": "11:00-23:00 (매주 월요일 휴무)",
        "판교점": "10:00-21:00 (연중무휴)",
    }
    return hours.get(branch, "해당 매장을 찾을 수 없습니다. 강남점, 홍대점, 판교점이 있습니다.")

MCP 서버 연동: 외부 도구 무한 확장

커스텀 Tool 외에도, Model Context Protocol (MCP) 서버를 연결해 외부 도구를 자동으로 AI에 추가할 수 있습니다.

Python

pip install clawops[agent,openai,mcp]
from clawops.agent import ClawOpsAgent, OpenAIRealtime
from clawops.agent.mcp import MCPServerStdio, MCPServerHTTP
import asyncio

agent = ClawOpsAgent(
    from_="07012341234",
    session=OpenAIRealtime(
        system_prompt="다양한 도구를 활용하는 AI 상담원입니다.",
    ),
    mcp_servers=[
        MCPServerStdio(
            command="npx",
            args=["-y", "@modelcontextprotocol/server-google"],
            env={"GOOGLE_API_KEY": "..."},
        ),
        MCPServerHTTP(
            url="https://mcp.example.com/sse",
            headers={"Authorization": "Bearer your-token"},
        ),
    ],
)

asyncio.run(agent.serve())

Node.js

npm install @teamlearners/clawops ws @modelcontextprotocol/sdk
import {
  ClawOpsAgent, OpenAIRealtime,
  mcpServerStdio, mcpServerHTTP,
} from '@teamlearners/clawops/agent';

const agent = new ClawOpsAgent({
  from: '07012341234',
  session: new OpenAIRealtime({
    systemPrompt: '다양한 도구를 활용하는 AI 상담원입니다.',
  }),
  mcpServers: [
    mcpServerStdio('npx', {
      args: ['@modelcontextprotocol/server-google'],
      env: { GOOGLE_API_KEY: '...' },
    }),
    mcpServerHTTP('https://mcp.example.com/sse', {
      headers: { Authorization: 'Bearer token' },
    }),
  ],
});

await agent.serve();

MCP 서버는 통화별로 독립적으로 연결됩니다. 동시에 여러 통화가 진행되어도 Tool 이름 충돌 없이 안전하게 동작합니다.

Tool 설계 베스트 프랙티스

1. 독스트링을 명확하게

AI는 독스트링(설명)을 보고 "이 Tool을 호출할지 말지"를 판단합니다:

# ❌ 나쁜 예 — AI가 언제 호출해야 하는지 모름
@agent.tool
async def check(id: str) -> str:
    """체크합니다."""
    ...

# ✅ 좋은 예 — 호출 조건이 명확
@agent.tool
async def check_order(order_id: str) -> str:
    """주문 상태를 확인합니다. 고객이 주문번호를 말하면 이 함수를 호출하세요.
    주문번호는 A로 시작하는 5자리 문자열입니다."""
    ...

2. 에러를 친절한 문자열로 반환

Tool 함수에서 예외가 발생하면 AI가 "오류가 발생했습니다"만 말할 수 있습니다. 대신 에러도 사람이 읽을 수 있는 문자열로 반환하세요:

@agent.tool
async def check_order(order_id: str) -> str:
    """주문 상태를 확인합니다."""
    try:
        result = await db.query_order(order_id)
        return f"주문 {order_id}: {result.status}"
    except OrderNotFoundError:
        return f"주문번호 {order_id}를 찾을 수 없습니다. 주문번호를 다시 확인해주시겠어요?"
    except Exception:
        return "시스템에 일시적인 문제가 있습니다. 잠시 후 다시 시도해주세요."

3. Tool 개수는 적게 유지

Tool이 많을수록 AI가 선택에 혼란을 겪을 수 있습니다. 하나의 에이전트에 5~7개 이내로 유지하고, 복잡한 업무는 여러 에이전트로 나누세요.

4. 시스템 프롬프트에서 Tool 사용법 명시

session=OpenAIRealtime(
    system_prompt="""카페 예약 상담원입니다.

규칙:
- 예약 전 반드시 check_availability로 가용 여부를 확인하세요.
- 예약 확정 전 고객에게 날짜, 시간, 인원을 반복 확인하세요.
- 2인 미만 예약은 불가합니다.
- 예약 변경/취소는 24시간 전까지만 가능합니다.""",
)

이벤트로 Tool 호출 로그 남기기

@agent.on("call_start")
async def on_call_start(call):
    print(f"전화 수신: {call.from_number} -> {call.to_number}")

@agent.on("transcript")
async def on_transcript(call, role, text):
    print(f"[{role}] {text}")

@agent.on("call_end")
async def on_call_end(call):
    print(f"통화 종료: {call.call_id} ({call.duration:.1f}초)")

마무리

Tool 연동은 AI 전화 에이전트를 "말만 하는 봇"에서 "업무를 처리하는 에이전트"로 바꿔줍니다. 커스텀 Tool, 내장 Tool, MCP 서버를 조합하면 예약 시스템, ERP, CRM, 배송 추적 등 어떤 백엔드와도 연결할 수 있습니다.

ClawOps에서 3일 무료 Trial로 바로 테스트해보세요 → claw-ops.com


관련 글: AI 전화 에이전트 만들기 · MCP 서버로 Claude에게 전화 기능 붙이기 · AI 전화 에이전트 프롬프트 엔지니어링

관련 글 더 보기

ClawOps AI 전화 API로 시작하기

070 번호 발급부터 AI 음성 통화까지, REST API 몇 줄이면 됩니다.