Skip to main content
Summary

채팅창을 제품으로 바꾸는 순간

ChatGPT를 잘 쓰는 사람과 ChatGPT로 시스템을 만드는 사람 사이에는 작은 듯 큰 차이가 있습니다. 전자는 채팅창에 들어가 "이 문서 요약해 줘"라고 말합니다. 후자는 사용자가 업로드한 문서, 권한, 로그, 비용 제한, 출력 형식, 실패 처리까지 포함해 "요약 기능이 항상 같은 방식으로 작동하는 서비스"를 만듭니다.

이 장의 주제는 바로 그 차이입니다. 사람이 ChatGPT 화면에서 대화하는 단계에서, 조직의 애플리케이션이 모델을 호출하는 단계로 넘어가면 질문이 완전히 달라집니다. 이제 중요한 것은 "무슨 프롬프트를 넣을까"만이 아닙니다. 어떤 모델을 쓸지, 어떤 입력을 보낼지, 어떤 도구를 허용할지, 응답을 어떤 구조로 받을지, 실패했을 때 어떻게 복구할지, 사용자가 결제나 발송 같은 위험한 작업을 승인했는지까지 설계해야 합니다.

앞 장에서 우리는 apps, connectors, MCP가 ChatGPT를 외부 도구와 연결하는 방식임을 보았습니다. 이번 장은 그 연결을 개발자의 손으로 구현하는 방법입니다. 핵심 도구는 세 가지입니다. Responses API, function calling, Structured Outputs입니다. 이 셋을 이해하면 ChatGPT는 더 이상 "똑똑한 문장 생성기"가 아니라, 업무 시스템 안에서 예측 가능한 입력과 출력을 주고받는 구성 요소가 됩니다.

단, 처음부터 마음을 단단히 먹어야 합니다. API를 쓴다고 해서 모델이 갑자기 완벽한 소프트웨어 컴포넌트가 되는 것은 아닙니다. 모델은 여전히 확률적으로 텍스트와 구조를 생성합니다. 그래서 개발자는 모델을 믿는 사람이 아니라, 모델을 통제하고 검증하고 복구하는 사람이어야 합니다.

API 실전과 구조화 출력

ChatGPT 화면과 API는 무엇이 다른가

ChatGPT 웹 화면은 사용자가 바로 쓰기 좋게 만들어진 제품입니다. 로그인하고, 모델을 선택하거나 자동 선택에 맡기고, 파일을 올리고, 대화를 이어 가면 됩니다. 기억, 프로젝트, 커넥터, 도구 사용, UI 요소 같은 많은 부분이 제품 안에서 처리됩니다.

API는 다릅니다. API는 제품이 아니라 부품에 가깝습니다. 아주 강력한 엔진을 받는 대신, 운전석과 계기판과 브레이크를 직접 설계해야 합니다. 같은 모델을 사용하더라도 API에서는 애플리케이션이 인증, 입력 메시지 구성, 모델 선택, 도구 정의, 응답 파싱, 에러 처리, 비용 관리, 로그 기록을 책임집니다.

구분ChatGPT 화면API 기반 시스템
사용자사람이 직접 입력애플리케이션이 요청 생성
인증계정 로그인 중심API 키, 서비스 계정, 서버 인증
입력대화창, 파일 업로드, 프로젝트 맥락JSON 요청, 메시지 배열, 파일 참조, 도구 정의
상태 관리제품이 일부 관리애플리케이션이 직접 선택·저장·요약
출력 처리사람이 읽고 판단코드가 파싱하고 후속 로직 실행
비용·지연사용자가 체감서비스 품질과 운영 비용에 직접 영향
실패 대응다시 물어보기재시도, fallback, 경고, 롤백, 로그 필요

가장 큰 차이는 출력의 소비자입니다. ChatGPT 화면에서는 사람이 답을 읽습니다. 답변이 조금 장황하거나 형식이 흔들려도 사람이 알아서 해석할 수 있습니다. 그러나 API 시스템에서는 다음 코드가 응답을 소비합니다. 고객 문의를 분류하는 시스템이 billing 대신 결제 관련인 것 같습니다라는 문장을 받으면 백엔드는 곤란해집니다. 티켓 라우팅 시스템은 감으로 움직이지 않습니다. 정해진 필드와 값이 필요합니다.

그래서 API 실전의 첫 번째 원칙은 이것입니다. 사람이 읽을 답과 코드가 처리할 답을 분리하라. 사용자에게 보여 줄 자연어는 친절해야 하지만, 시스템이 처리할 데이터는 엄격해야 합니다.

Responses API는 '채팅창 자동화'가 아니다

많은 초보 개발자가 API를 처음 볼 때 이렇게 생각합니다. "ChatGPT 화면에서 하던 대화를 코드로 보내는 거구나." 절반만 맞습니다. 단순 질의응답이라면 그렇게 이해해도 시작은 할 수 있습니다. 하지만 Responses API의 핵심은 채팅창을 흉내 내는 데 있지 않습니다. 애플리케이션이 모델 입력, 도구 사용, 멀티모달 입력, 출력 형식, 응답 흐름을 한 요청 또는 연속 요청 안에서 제어하는 통합 인터페이스로 이해해야 합니다.

OpenAI의 function calling 문서는 모델을 애플리케이션이 제공하는 외부 데이터와 동작에 연결하는 방식으로 function calling, 또는 tool calling을 설명합니다.1 그리고 Structured Outputs 문서는 모델 응답이 개발자가 지정한 JSON Schema를 따르도록 만드는 기능을 설명하며, 가능한 경우 JSON mode보다 Structured Outputs를 우선 사용하라고 안내합니다.2 이 두 흐름이 Responses API와 결합되면, API 호출은 단순한 "질문 보내기"가 아니라 다음과 같은 업무 단위가 됩니다.

사용자 요청 수집
→ 필요한 맥락과 정책 삽입
→ 모델 선택
→ 사용할 도구 목록 제공
→ 출력 스키마 지정
→ 모델 응답 수신
→ 도구 호출 여부 확인
→ 인자 검증 및 권한 확인
→ 실제 함수 실행
→ 결과를 다시 모델 또는 후속 시스템에 전달
→ 최종 응답·로그·평가 저장

이 흐름에서 모델은 중심에 있지만, 혼자 일하지 않습니다. 모델은 애플리케이션이 만든 레일 위에서 움직입니다. 좋은 API 시스템은 모델에게 모든 것을 맡기는 시스템이 아니라, 모델이 잘하는 자연어 이해와 생성은 활용하고, 코드가 잘하는 검증·상태 관리·권한 통제는 코드가 맡는 시스템입니다.

예를 들어 고객지원 제품을 만든다고 해 봅시다. 사용자가 "결제가 두 번 됐어요. 당장 환불해 주세요"라고 입력합니다. ChatGPT 화면이라면 상담원이 이 문장을 붙여 넣고 답변 초안을 받을 수 있습니다. API 시스템이라면 더 복잡합니다. 시스템은 사용자의 계정 상태를 확인하고, 결제 내역 조회 도구를 호출할지 판단하고, 환불 가능 여부를 내부 정책과 비교하고, 실제 환불 실행 전 관리자 승인을 요구해야 합니다. 이것은 대화가 아니라 업무 흐름입니다.

API 요청을 구성하는 다섯 가지 축

API 요청은 보통 몇 줄의 코드로 보입니다. 그러나 그 안에는 중요한 설계 선택이 숨어 있습니다. 실무에서는 다음 다섯 가지를 먼저 결정해야 합니다.

축질문실무 판단
모델어떤 모델을 쓸 것인가정확도, 속도, 비용, 멀티모달, 추론 능력의 균형
입력무엇을 보낼 것인가사용자 요청, 정책, 이전 대화, 파일, 도구 결과
도구무엇을 할 수 있게 할 것인가검색, DB 조회, 계산, 업무 시스템 호출, 쓰기 작업
출력어떤 형태로 받을 것인가자연어, JSON Schema, 함수 호출 인자, 혼합 응답
운영실패하면 어떻게 할 것인가재시도, 타임아웃, 승인, 로그, fallback, 비용 제한

이 다섯 가지를 생각하지 않고 API를 호출하면, 처음 데모는 빠르게 만들 수 있지만 운영에서 무너집니다. 특히 "일단 자연어로 답하게 하고 나중에 정규표현식으로 파싱하자"는 접근은 오래 버티기 어렵습니다. 모델이 어느 날 "분류: 결제" 대신 "이 문의는 결제와 관련되어 보입니다"라고 답하면 후속 코드가 깨집니다. 작은 표현 변화가 장애가 되는 시스템은 프로덕션에 적합하지 않습니다.

API를 제품에 넣는다는 것은 모델을 호출하는 함수를 하나 만드는 일이 아닙니다. 입력과 출력의 계약을 정하는 일입니다. 계약이 있어야 테스트할 수 있고, 테스트할 수 있어야 개선할 수 있습니다.

가장 작은 Responses API 예시

아래 코드는 개념을 보여 주기 위한 최소 예시입니다. 실제 SDK 문법은 버전과 모델에 따라 조금씩 달라질 수 있으므로, 운영 코드에서는 공식 문서를 기준으로 확인해야 합니다. 여기서 중요한 것은 코드의 줄 수가 아니라 구조입니다.

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4.1-mini",
    input=[
        {
            "role": "system",
            "content": "당신은 한국어 고객지원 답변을 작성하는 보조 도구다. 모르는 사실은 추정하지 않는다."
        },
        {
            "role": "user",
            "content": "결제가 두 번 됐어요. 환불해 주세요."
        }
    ]
)

print(response.output_text)

이 예시는 ChatGPT 화면에서 질문하는 것과 비슷해 보입니다. 하지만 API 관점에서는 이미 몇 가지 선택이 들어 있습니다. 모델을 직접 골랐고, 시스템 지시를 코드에 넣었고, 응답을 프로그램에서 받아 출력했습니다. 여기서 한 걸음만 더 나아가면 고객지원 시스템, 사내 문서 요약기, 보고서 초안 생성기, 코드 리뷰 봇을 만들 수 있습니다.

하지만 최소 예시는 최소 예시일 뿐입니다. 실무에서는 여기에 인증 키 관리, 입력 길이 제한, 민감정보 제거, 응답 파싱, 토큰 사용량 기록, 에러 처리, 재시도 정책이 붙어야 합니다. "작동하는 코드"와 "운영 가능한 코드" 사이에는 이 추가 설계가 있습니다.

Function calling: 모델은 실행하지 않고 '요청서'를 쓴다

function calling이라는 이름 때문에 가장 흔한 오해가 생깁니다. "모델이 함수를 호출해 주는구나." 정확히 말하면 아닙니다. 모델은 외부 함수를 직접 실행하지 않습니다. 모델은 어떤 함수를 어떤 인자로 호출해야 할지 구조화된 요청을 생성합니다. 실제 실행은 애플리케이션이 합니다.

이 차이는 매우 중요합니다. 모델이 "환불 함수를 호출하겠다"고 말해도, 실제로 환불 API를 때리는 주체는 여러분의 서버입니다. 그러므로 서버는 모델이 만든 함수명과 인자를 그대로 믿으면 안 됩니다. 서버는 그것을 검증하고, 권한을 확인하고, 업무 규칙을 적용하고, 필요한 경우 사용자 또는 관리자 승인을 받은 뒤 실행해야 합니다.

function calling의 기본 흐름은 다음과 같습니다.

1. 개발자가 사용할 수 있는 함수 목록과 인자 스키마를 정의한다.
2. 사용자 요청과 함수 목록을 모델에 보낸다.
3. 모델이 필요한 함수 이름과 인자를 JSON 형태로 생성한다.
4. 애플리케이션이 함수명과 인자를 검증한다.
5. 애플리케이션이 실제 API, DB, 업무 시스템, RPA를 호출한다.
6. 실행 결과를 다시 모델에 전달한다.
7. 모델이 결과를 바탕으로 최종 사용자 응답을 만든다.

이 흐름을 식당에 비유하면 모델은 요리사가 아니라 주문서를 쓰는 직원에 가깝습니다. 고객이 "덜 맵게 해 주세요"라고 말하면 직원은 주방에 전달할 주문서를 만듭니다. 그러나 실제 요리는 주방이 합니다. 주문서가 이상하면 주방은 확인해야 합니다. "고추 300개 추가" 같은 이상한 주문이 들어오면 그대로 조리하지 않는 것처럼, 애플리케이션도 모델이 만든 인자를 검증해야 합니다.

함수 스키마는 작고 명확해야 한다

function calling에서 가장 중요한 설계물은 함수 스키마입니다. 함수 스키마는 모델에게 "이 도구는 언제 쓰고, 어떤 값을 받아야 하며, 어떤 제약이 있는지" 알려 주는 설명서입니다. 스키마가 모호하면 모델의 도구 선택도 흔들립니다.

나쁜 함수 이름은 보통 너무 넓습니다. process_request, handle_data, do_action 같은 이름은 사람에게도 애매합니다. 좋은 함수 이름은 동사와 대상을 함께 담습니다. fetch_customer_orders, create_support_ticket_draft, search_approved_faq, calculate_refund_eligibility처럼 말입니다.

다음은 고객 주문 조회 도구의 예입니다.

tools = [
    {
        "type": "function",
        "name": "fetch_customer_orders",
        "description": "고객 ID로 최근 주문 내역을 조회한다. 읽기 전용 도구이며 주문 취소나 환불을 실행하지 않는다.",
        "parameters": {
            "type": "object",
            "properties": {
                "customer_id": {
                    "type": "string",
                    "description": "고객 식별자. 예: CUS-10293"
                },
                "days": {
                    "type": "integer",
                    "description": "조회 기간. 1일부터 180일 사이만 허용한다."
                }
            },
            "required": ["customer_id", "days"],
            "additionalProperties": False
        },
        "strict": True
    }
]

여기서 눈여겨볼 부분은 세 가지입니다. 첫째, 함수 이름이 구체적입니다. 둘째, 설명에 "읽기 전용"이라고 적어 위험도를 명확히 했습니다. 셋째, additionalProperties: False와 strict: True를 사용해 예상하지 않은 인자가 들어오는 것을 줄였습니다.

스키마는 모델을 위한 문서인 동시에 개발팀을 위한 문서입니다. 새로운 개발자가 이 함수를 보았을 때 "이 도구는 조회만 하는구나", "환불 실행은 별도 도구겠구나"라고 이해할 수 있어야 합니다.

함수 호출 왕복 흐름 예시

아래는 function calling의 전체 감각을 보여 주는 단순 예시입니다. 실제 결제나 고객 데이터 시스템에서는 보안과 권한 검사가 훨씬 더 엄격해야 합니다.

import json
from openai import OpenAI

client = OpenAI()

# 실제 애플리케이션 함수
def fetch_customer_orders(customer_id: str, days: int):
    # 운영 환경에서는 DB 또는 내부 API를 호출한다.
    return {
        "customer_id": customer_id,
        "orders": [
            {"order_id": "ORD-9001", "amount": 59000, "status": "paid"},
            {"order_id": "ORD-9002", "amount": 59000, "status": "paid"}
        ]
    }

available_functions = {
    "fetch_customer_orders": fetch_customer_orders
}

input_items = [
    {"role": "system", "content": "고객지원 보조 도구다. 환불을 단정하지 말고 조회 결과를 근거로 안내한다."},
    {"role": "user", "content": "CUS-10293 고객이 결제가 두 번 됐다고 문의했다. 최근 주문을 확인해 줘."}
]

response = client.responses.create(
    model="gpt-4.1-mini",
    input=input_items,
    tools=tools
)

# 개념 예시: 응답 안에서 function_call 항목을 찾는다.
for item in response.output:
    if item.type == "function_call":
        function_name = item.name
        raw_args = item.arguments
        args = json.loads(raw_args)

        # 1. 함수명 허용 목록 확인
        if function_name not in available_functions:
            raise ValueError(f"허용되지 않은 함수: {function_name}")

        # 2. 인자 검증
        if not isinstance(args.get("customer_id"), str):
            raise ValueError("customer_id가 올바르지 않음")
        if not (1 <= int(args.get("days", 0)) <= 180):
            raise ValueError("days 범위가 올바르지 않음")

        # 3. 실제 함수 실행
        result = available_functions[function_name](**args)

        # 4. 실행 결과를 모델에 다시 전달
        input_items.append(item)
        input_items.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": json.dumps(result, ensure_ascii=False)
        })

final_response = client.responses.create(
    model="gpt-4.1-mini",
    input=input_items,
    tools=tools
)

print(final_response.output_text)

이 코드에서 가장 중요한 줄은 available_functions입니다. 모델이 아무 함수나 실행하는 것이 아니라, 애플리케이션이 허용한 함수만 실행합니다. 또 json.loads로 인자를 파싱한 뒤 검증합니다. 절대로 문자열을 eval로 실행해서는 안 됩니다. 모델 출력은 외부 입력처럼 다루어야 합니다. 그럴듯해 보여도 신뢰할 수 없는 입력입니다.

Function calling은 검색, DB, RPA의 입구다

function calling의 진짜 힘은 자연어를 업무 시스템의 입력으로 바꾸는 데 있습니다. 사용자는 "지난달 A사 매출 추이 좀 알려줘"라고 말하지만, 데이터베이스는 자연어를 이해하지 못합니다. 사용자는 "이 고객 문의를 담당 팀에 넘겨 줘"라고 말하지만, 헬프데스크 시스템은 정해진 API 파라미터가 필요합니다. function calling은 이 사이를 이어 줍니다.

실무에서 function calling은 다음과 같은 작업에 자주 쓰입니다.

사용 분야함수 예시주의점
검색search_documents, fetch_policy검색 범위와 문서 등급 제한
데이터베이스 조회get_customer_orders, query_sales_summarySQL 직접 생성보다 안전한 파라미터화 권장
업무 시스템create_ticket, update_crm_note쓰기 작업 승인 필요
계산calculate_refund, estimate_shipping_fee비즈니스 규칙은 코드에서 검증
RPAstart_invoice_workflow, generate_report_file실행 전 대상과 결과 확인

여기서 중요한 원칙은 모델에게 "결정권"을 모두 주지 않는 것입니다. 모델은 어떤 도구가 필요할지 제안하고 인자를 구성할 수 있습니다. 그러나 환불 가능 여부, 계약 조건 적용, 고객 등급별 예외, 금융상품 적합성, 의료 판단, 법률 판단 같은 핵심 의사결정은 코드와 전문가 검토 체계 안에서 통제해야 합니다.

function calling은 AI에게 손을 달아 주는 기술입니다. 손을 달아 주었으면 장갑과 안전장치도 같이 줘야 합니다.

JSON mode의 한계와 Structured Outputs의 필요

초기 API 활용에서는 "JSON으로 답해 줘"라는 프롬프트가 흔했습니다. 조금 발전하면 JSON mode를 사용해 "유효한 JSON"을 받으려 했습니다. 이것만으로도 일반 텍스트 파싱보다는 훨씬 낫습니다. 하지만 JSON mode에는 중요한 한계가 있습니다. JSON 문법이 맞는다는 것과, 여러분의 스키마에 맞는다는 것은 다른 문제입니다.

예를 들어 고객 문의 분류 결과로 다음 구조가 필요하다고 합시다.

{
  "category": "billing",
  "urgency": "high",
  "sentiment": "negative",
  "needs_human": true
}

JSON mode는 모델이 유효한 JSON을 만들게 도울 수 있습니다. 하지만 어느 날 모델이 아래처럼 답할 수도 있습니다.

{
  "topic": "결제",
  "priority": "긴급",
  "emotion": "화남",
  "human_review": "필요"
}

문법은 맞지만 백엔드 입장에서는 깨진 응답입니다. 필드명이 다르고, enum 값이 다르고, boolean이어야 할 값이 문자열입니다. 이 문제를 줄이기 위해 Structured Outputs를 사용합니다. Structured Outputs는 개발자가 지정한 JSON Schema에 맞춰 모델 응답을 생성하게 하는 방식입니다. OpenAI의 Structured Outputs 샘플 저장소도 이 기능이 예측하기 어려운 모델 출력과 결정론적 워크플로 사이의 간극을 줄여 준다고 설명합니다.3

간단히 비교하면 이렇습니다.

방식보장하는 것보장하지 못하는 것적합한 용도
일반 프롬프트아무것도 엄격히 보장하지 않음JSON 문법, 필드, 타입실험, 사람용 초안
JSON mode유효한 JSON일 가능성정확한 스키마 준수간단한 내부 도구
Structured Outputs지정한 JSON Schema 준수값의 사실성, 업무 규칙의 타당성분류, 추출, 라우팅, 저장용 데이터

중요한 단서도 있습니다. Structured Outputs가 스키마를 맞춰 준다고 해서 값이 항상 사실이라는 뜻은 아닙니다. order_id 필드가 문자열로 온다는 것은 보장할 수 있어도, 그 주문번호가 실제 존재하는지는 DB에서 확인해야 합니다. urgency가 high라는 enum 값으로 온다는 것은 보장할 수 있어도, 그 판단이 회사 정책상 맞는지는 평가해야 합니다.

스키마는 후속 코드와 맺는 계약이다

Structured Outputs에서 스키마는 단순한 출력 형식이 아닙니다. 후속 코드와 맺는 계약입니다. 계약이 애매하면 후속 코드가 흔들립니다. 계약이 지나치게 복잡하면 모델과 개발자 모두 다루기 어려워집니다. 좋은 스키마는 필요한 만큼만 엄격합니다.

고객 VOC를 분류하는 스키마를 보겠습니다.

{
  "type": "object",
  "properties": {
    "topic": {
      "type": "string",
      "enum": ["billing", "account", "delivery", "product", "refund", "other"]
    },
    "urgency": {
      "type": "string",
      "enum": ["low", "medium", "high"]
    },
    "sentiment": {
      "type": "string",
      "enum": ["positive", "neutral", "negative"]
    },
    "summary": {
      "type": "string"
    },
    "needs_human": {
      "type": "boolean"
    },
    "evidence": {
      "type": "array",
      "items": {"type": "string"}
    }
  },
  "required": ["topic", "urgency", "sentiment", "summary", "needs_human", "evidence"],
  "additionalProperties": false
}

이 스키마에는 실무적으로 중요한 장치가 들어 있습니다. topic, urgency, sentiment는 enum으로 제한했습니다. 후속 코드가 라우팅할 수 있도록 값의 범위를 좁힌 것입니다. needs_human은 boolean으로 정했습니다. 사람이 검토해야 하는지 명확히 판단하기 위해서입니다. evidence는 배열입니다. 모델이 왜 그렇게 분류했는지 근거 문구를 남기게 하기 위해서입니다.

스키마 설계에서 자주 쓰는 요소는 다음과 같습니다.

요소의미예시
required반드시 있어야 하는 필드customer_id, category
enum허용 값 제한low, medium, high
additionalProperties: false정의하지 않은 필드 금지예기치 않은 값 차단
중첩 객체관련 필드 묶기주소, 결제정보, 평가결과
배열여러 항목 반환근거 문장 목록, 액션 아이템 목록
nullable 필드없을 수 있는 값 표현string 또는 null

가능하면 스키마에는 후속 코드가 실제로 쓰는 필드만 넣으십시오. "나중에 쓸지도 모르니까"라는 마음으로 필드를 늘리면 토큰 비용이 늘고, 파싱과 테스트가 복잡해집니다. 스키마는 욕심을 부리는 문서가 아니라 책임을 줄이는 문서입니다.

Structured Outputs 예시: 티켓 분류기

아래는 고객 문의를 분류해 백엔드가 바로 소비할 수 있는 형태로 받는 예시입니다. Responses API에서는 구조화 출력의 파라미터 위치가 SDK 버전에 따라 다르게 표현될 수 있지만, 핵심은 json_schema와 strict입니다.

from openai import OpenAI
import json

client = OpenAI()

support_schema = {
    "type": "object",
    "properties": {
        "action": {
            "type": "string",
            "enum": ["answer", "handoff_team", "default_fallback"]
        },
        "team": {
            "type": ["string", "null"],
            "enum": ["billing", "technical", "returns", "general", None]
        },
        "customer_message": {
            "type": "string"
        },
        "internal_reason": {
            "type": "string"
        },
        "confidence": {
            "type": "number"
        }
    },
    "required": ["action", "team", "customer_message", "internal_reason", "confidence"],
    "additionalProperties": False
}

response = client.responses.create(
    model="gpt-4.1-mini",
    input=[
        {
            "role": "system",
            "content": "이커머스 고객 문의를 분류한다. 환불 가능 여부를 단정하지 말고 필요한 경우 담당 팀으로 넘긴다."
        },
        {
            "role": "user",
            "content": "결제가 두 번 됐어요. 너무 화가 납니다. 바로 처리해 주세요."
        }
    ],
    text={
        "format": {
            "type": "json_schema",
            "name": "support_classification",
            "schema": support_schema,
            "strict": True
        }
    }
)

# 거절 또는 안전 필터 응답이 있는 경우를 먼저 확인하는 습관이 필요하다.
data = json.loads(response.output_text)
print(data)

예상 출력은 다음과 비슷할 수 있습니다.

{
  "action": "handoff_team",
  "team": "billing",
  "customer_message": "중복 결제 문제로 확인이 필요합니다. 결제 담당 팀에 바로 전달하겠습니다.",
  "internal_reason": "고객이 중복 결제를 주장하며 강한 불만을 표현함",
  "confidence": 0.94
}

이제 백엔드는 action이 handoff_team인지 확인하고, team이 billing이면 결제팀 큐로 티켓을 보낼 수 있습니다. 고객에게 보여 줄 문구와 내부 라우팅 이유도 분리되어 있습니다. 고객에게 내부 판단 사유를 그대로 보여 주지 않아도 됩니다. 이처럼 구조화 출력은 고객 경험과 운영 데이터를 분리하는 데 매우 유용합니다.

고객지원 자동화 분야에서는 자연어 답변은 사용자에게 보여 주고, 라우팅 결정은 구조화된 필드로 분리하는 패턴이 특히 중요합니다. 실제 지원 자동화 가이드들도 백엔드가 문단을 파싱하게 만들기보다 action, routingTeam, confidence 같은 명시적 신호를 받는 구조가 안정적이라고 설명합니다.4

Pydantic으로 스키마를 코드로 관리하기

JSON Schema를 손으로 길게 작성하다 보면 실수가 생깁니다. 필드 이름을 바꾸고 required 목록을 갱신하지 않거나, 타입을 바꿨는데 후속 코드가 예전 타입을 기대하는 일이 생깁니다. 그래서 Python에서는 Pydantic 같은 타입 정의 도구를 함께 쓰면 좋습니다.

Pydantic은 데이터 구조를 Python 클래스로 정의하고, 입력값이 그 구조에 맞는지 검증해 주는 라이브러리입니다. 쉽게 말해 "이 응답은 이런 모양이어야 한다"를 코드로 표현하는 도구입니다. 일부 OpenAI SDK 예제에서는 Pydantic 모델을 이용해 Structured Outputs를 파싱하는 방식을 보여 줍니다.5

from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field
from openai import OpenAI

client = OpenAI()

class Action(str, Enum):
    answer = "answer"
    handoff_team = "handoff_team"
    default_fallback = "default_fallback"

class Team(str, Enum):
    billing = "billing"
    technical = "technical"
    returns = "returns"
    general = "general"

class SupportClassification(BaseModel):
    action: Action
    team: Optional[Team] = None
    customer_message: str
    internal_reason: str
    confidence: float = Field(ge=0.0, le=1.0)

completion = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {
            "role": "system",
            "content": "고객 문의를 분류하고 필요한 경우 담당 팀으로 라우팅한다."
        },
        {
            "role": "user",
            "content": "비밀번호를 바꾸고 싶은데 메뉴를 못 찾겠어요."
        }
    ],
    response_format=SupportClassification,
)

result = completion.choices[0].message.parsed
print(result.action)
print(result.team)
print(result.customer_message)

이 방식의 장점은 응답을 단순 딕셔너리로 다루는 것보다 타입 안정성이 높다는 점입니다. confidence가 문자열로 오거나, action이 허용되지 않은 값이면 파싱 단계에서 문제를 발견할 수 있습니다. 운영 코드에서는 이 예외를 잡아 재시도하거나, 더 작은 모델에서 실패하면 더 강한 모델로 fallback하거나, 사람 검토 큐로 넘길 수 있습니다.

중요한 습관은 "파싱 실패는 예외적인 사건이 아니라 운영 흐름의 일부"로 보는 것입니다. 네트워크도 실패하고, DB도 실패하고, 모델 응답도 실패할 수 있습니다. 실패를 부끄러워할 것이 아니라, 실패했을 때 안전하게 멈추는 길을 만들어야 합니다.

실패 처리: 재시도보다 먼저 분류하라

API 시스템에서 실패가 발생하면 초보자는 무조건 재시도부터 생각합니다. 하지만 모든 실패가 재시도로 해결되는 것은 아닙니다. 에러의 종류를 먼저 나누어야 합니다.

실패 유형예시권장 대응
일시적 실패네트워크 오류, 5xx, 타임아웃지수 백오프 재시도
제한 초과rate limit, TPM 초과대기, 큐잉, 요청 축소
입력 문제너무 긴 문맥, 잘못된 파일, 빈 필드입력 축소, 사용자에게 재요청
파싱 문제JSON 파싱 실패, 스키마 불일치Structured Outputs 사용, 재시도, fallback
정책·안전 문제모델 refusal, 금지 요청사용자 안내, 사람 검토
업무 규칙 실패권한 없음, 환불 불가, 승인 누락실행 중단, 승인 요청

재시도는 일시적 실패에는 효과적이지만, 잘못된 입력에는 효과가 없습니다. 사용자가 주문번호를 입력하지 않았는데 같은 요청을 세 번 보내도 주문 조회는 되지 않습니다. 이때는 모델에게 "필요한 추가 정보를 물어보라"고 하거나, 애플리케이션이 사용자에게 주문번호 입력 UI를 보여 주어야 합니다.

특히 function calling에서는 "모델이 함수 호출을 제안했다"와 "함수를 실행해도 된다"를 분리해야 합니다. 모델이 refund_payment 호출을 제안해도, 주문 상태가 환불 불가일 수 있고, 사용자 권한이 부족할 수 있고, 관리자 승인이 필요할 수 있습니다. 이 경우 실패는 모델 실패가 아니라 업무 규칙상 정상 차단입니다. 이런 차단은 로그에 남기고 사용자에게 명확히 안내해야 합니다.

타임아웃, 비용, rate limit은 제품 경험이다

개발자는 종종 비용과 rate limit을 운영팀의 문제로 미룹니다. 하지만 API 제품에서는 비용과 지연시간이 곧 사용자 경험입니다. 사용자가 버튼을 눌렀는데 20초 동안 아무 반응이 없으면 기능이 아무리 똑똑해도 실패한 제품입니다. 월말에 예상 비용의 다섯 배가 나오면 기능이 아무리 편해도 지속하기 어렵습니다.

실무에서는 다음 기준을 미리 정해야 합니다.

항목설계 질문예시 정책
타임아웃몇 초 이상 기다리면 실패로 볼 것인가고객 응대 8초, 배치 요약 60초
재시도몇 번까지 재시도할 것인가429·5xx에 한해 최대 3회
모델 선택어떤 작업에 어떤 모델을 쓸 것인가분류는 소형, 법률 검토 초안은 고성능
토큰 예산요청당 최대 입력·출력 길이는 얼마인가고객 티켓 8k, 보고서 요약 64k
캐싱같은 요청을 다시 계산할 것인가정책 문서 요약은 버전 기준 캐싱
스트리밍중간 응답을 보여 줄 것인가사용자-facing 긴 답변은 스트리밍

비용을 줄이는 가장 좋은 방법은 무조건 싼 모델을 쓰는 것이 아닙니다. 작업을 잘게 나누는 것입니다. 간단한 분류는 작은 모델로 처리하고, 위험하거나 복잡한 건만 큰 모델로 넘깁니다. 문서 전체를 매번 보내지 않고 필요한 조각만 검색해 보냅니다. 반복되는 시스템 지시와 정책 문서는 캐시하거나 요약본을 씁니다.

API 비용 최적화는 "덜 쓰기"가 아니라 "필요한 곳에 정확히 쓰기"입니다.

대화 상태는 모델이 아니라 애플리케이션이 관리한다

ChatGPT 화면에서는 대화가 이어지는 것처럼 보입니다. 그래서 많은 사람이 API에서도 모델이 모든 맥락을 알아서 기억한다고 착각합니다. 그러나 API 시스템에서는 애플리케이션이 어떤 이전 메시지를 다시 보낼지 결정해야 합니다. 모델은 요청에 포함된 맥락만 봅니다. 포함하지 않은 과거는 모릅니다.

대화 상태 관리의 핵심 질문은 이것입니다.

이번 응답을 만들기 위해 꼭 필요한 맥락은 무엇인가?

모든 이전 메시지를 계속 붙이면 언젠가 토큰 한도와 비용 문제가 생깁니다. 더 큰 문제는 민감정보와 오래된 정보가 계속 따라다닌다는 점입니다. 예전에 입력된 잘못된 가정이나 비공개 정보가 뒤쪽 작업에 영향을 줄 수 있습니다.

실무에서는 대화 상태를 다음 네 층으로 나누어 관리하는 편이 좋습니다.

상태 유형내용관리 방식
최근 대화직전 몇 턴의 사용자·모델 메시지필요한 범위만 포함
사용자 프로필역할, 언어, 권한, 선호 형식최소 정보만 저장
작업 메모현재 과업의 목표, 결정사항, 미해결 질문요약본으로 유지
도구 결과DB 조회, 검색 결과, API 응답원문 대신 필요한 결과와 근거 저장

예를 들어 보고서 작성 도우미를 만든다면, 모든 대화 전체를 계속 보내는 대신 "보고서 목적: 임원 보고", "대상 기간: 2026년 1분기", "확정된 KPI: 매출, CAC, 전환율", "미확정: 리텐션 수치" 같은 작업 메모를 유지할 수 있습니다. 그리고 다음 호출에는 이 요약 메모와 최근 메시지만 보냅니다.

이 방식은 비용을 줄이고, 품질을 안정화하고, 재현 가능한 로그를 남기는 데 도움이 됩니다. 나중에 "왜 이런 답변이 나왔는가"를 확인하려면 그 시점에 어떤 맥락이 모델에 전달되었는지 알아야 합니다.

민감정보 제거는 프롬프트 이전의 일이다

API 시스템은 사용자의 입력을 그대로 모델에 보내기 쉽습니다. 하지만 조직 시스템에서는 이것이 가장 위험한 습관입니다. 개인정보, 주민등록번호, 계좌번호, 건강정보, 미공개 계약 조건, 내부 인사 평가, 고객 식별자가 섞여 있을 수 있습니다. 이런 정보는 프롬프트를 잘 쓴다고 해결되지 않습니다. 프롬프트 이전에 필터링해야 합니다.

민감정보 처리의 기본 흐름은 다음과 같습니다.

원본 입력 수집
→ 민감정보 탐지
→ 마스킹 또는 토큰화
→ 필요한 최소 정보만 모델에 전달
→ 응답에서 민감정보 재노출 여부 검사
→ 로그에는 원문 대신 비식별 요약 저장

예를 들어 고객 문의에 "제 카드번호는 1234-5678-..."이 포함되어 있다면, 모델에게 그대로 보내지 말고 [카드번호]로 치환해야 합니다. 주문 조회가 필요하다면 실제 카드번호가 아니라 내부 주문 ID나 고객 ID를 사용해야 합니다. 의료, 금융, 법률, 교육 영역에서는 특히 이 단계가 중요합니다.

API 키도 마찬가지입니다. API 키는 코드에 하드코딩하지 말고 환경 변수나 시크릿 매니저로 관리해야 합니다. 키가 Git 저장소나 클라이언트 코드에 노출되면 비용 피해뿐 아니라 데이터 접근 문제가 생길 수 있습니다. API 키는 비밀번호보다 위험할 수 있습니다. 비밀번호는 로그인만 허용하지만, API 키는 여러분의 과금 계정으로 실제 요청을 실행할 수 있기 때문입니다.

로그는 재현 가능해야 하지만 과도하면 안 된다

좋은 API 시스템은 로그를 남깁니다. 나쁜 API 시스템도 로그를 남깁니다. 차이는 무엇을, 왜, 얼마나 남기느냐입니다. 모델 호출 로그에는 품질 개선, 장애 분석, 감사 대응에 필요한 정보가 있어야 합니다. 그러나 민감한 원문을 과도하게 저장하면 로그 자체가 보안 사고의 원인이 됩니다.

실무 로그에는 다음 정보가 유용합니다.

로그 항목목적
요청 ID장애와 사용자 문의 추적
사용자 또는 서비스 식별자권한 및 사용량 분석
모델명과 버전품질 변화 원인 확인
입력 토큰·출력 토큰비용 분석
도구 호출 목록외부 시스템 영향 추적
스키마 버전파싱 오류 원인 분석
응답 상태성공, 거절, 타임아웃, 재시도 여부
승인자와 승인 시각쓰기 작업 감사

하지만 원문 전체, 개인정보, 인증 토큰, 내부 API 응답 전문을 무조건 저장하는 것은 피해야 합니다. 필요한 경우 원문은 짧은 기간만 암호화해 보관하고, 장기 로그에는 해시, 요약, 식별자만 남기는 방식이 좋습니다. 로그는 기억 장치이지 데이터 쓰레기장이 아닙니다.

되돌리기 어려운 작업은 절대 바로 실행하지 말라

API와 function calling을 연결하면 유혹이 생깁니다. 사용자가 "메일 보내 줘"라고 하면 모델이 send_email을 호출하고, 서버가 바로 메일을 보내면 멋져 보입니다. 사용자가 "환불 처리해 줘"라고 하면 모델이 refund_payment를 호출하고, 서버가 바로 환불하면 자동화가 완성된 것처럼 보입니다.

그러나 운영에서는 바로 그 순간이 가장 위험합니다. 메일 발송, 결제, 환불, DB 수정, 파일 삭제, 권한 변경, 외부 공유처럼 되돌리기 어렵거나 외부에 영향을 주는 작업은 항상 안전 흐름을 거쳐야 합니다.

권장 흐름은 다음과 같습니다.

모델 제안
→ 애플리케이션 검증
→ 업무 규칙 확인
→ 사용자 또는 관리자 승인
→ 실행
→ 실행 결과 기록
→ 사후 평가 및 알림

예를 들어 고객에게 환불 안내 메일을 보내는 경우를 생각해 봅시다. 모델은 메일 초안과 대상 고객, 관련 주문번호를 제안할 수 있습니다. 애플리케이션은 주문번호가 실제 존재하는지, 고객 이메일이 맞는지, 환불 정책상 가능한지 확인합니다. 그다음 상담원이나 매니저에게 승인 화면을 보여 줍니다.

승인 전 확인

작업: 고객 이메일 발송
수신자: kim***@example.com
주문번호: ORD-9002
메일 유형: 중복 결제 확인 안내
첨부파일: 없음
환불 확정 문구 포함 여부: 없음
고객 개인정보 포함 여부: 마스킹 처리됨
실행 후 되돌릴 수 있는가: 발송 취소 불가

이 정도 정보를 사람이 확인한 뒤 실행해야 합니다. 단순히 "메일을 보낼까요?"라고 묻는 것은 충분하지 않습니다. 승인자는 무엇이 실행되는지 이해할 수 있어야 합니다.

모델 응답과 업무 결정을 분리하라

많은 AI 시스템의 실패는 모델 응답을 곧 업무 결정으로 취급할 때 발생합니다. 모델이 "이 고객은 환불 대상입니다"라고 말하면 환불하는 식입니다. 이것은 위험합니다. 모델은 정책 문서를 잘못 읽었을 수도 있고, 최신 규정을 모를 수도 있고, 고객의 예외 조건을 놓쳤을 수도 있습니다.

더 안전한 구조는 모델이 판단 자료를 정리하고, 업무 결정은 코드와 규칙 엔진 또는 사람에게 맡기는 것입니다.

단계모델 역할코드·사람 역할
정보 추출문의에서 주문번호, 불만 유형 추출형식 검증
근거 검색관련 FAQ와 정책 후보 찾기문서 최신성 확인
초안 작성고객 안내문 작성금지 표현과 사실 검토
결정 제안환불 검토 필요 표시환불 가능 여부 최종 판단
실행실행 요청 생성승인 후 API 호출

이렇게 분리하면 모델의 장점은 살리고 위험은 줄일 수 있습니다. 모델은 비정형 언어를 이해하고 초안을 만드는 데 강합니다. 코드는 규칙을 일관되게 적용하고 권한을 통제하는 데 강합니다. 사람은 예외와 책임을 판단하는 데 강합니다. 좋은 시스템은 셋을 섞지 않고 배치합니다.

구조화 출력은 평가의 출발점이다

Structured Outputs의 또 다른 장점은 평가가 쉬워진다는 것입니다. 자연어 답변만 있으면 "좋다", "나쁘다"를 평가하기 어렵습니다. 그러나 구조화된 필드가 있으면 정확도, 누락률, 형식 준수율, 라우팅 성공률을 측정할 수 있습니다.

예를 들어 고객 문의 분류기의 평가 지표는 다음처럼 만들 수 있습니다.

평가 항목측정 방법
스키마 준수율파싱 성공 건수 / 전체 요청 수
분류 정확도정답 라벨과 topic 비교
긴급도 정확도상담원 판정과 urgency 비교
사람 연결 적절성실제 handoff 필요 여부와 needs_human 비교
근거 품질evidence가 원문에 실제 존재하는지 확인
비용건당 입력·출력 토큰과 모델 비용
지연시간p50, p95 응답 시간

이런 평가가 가능해지면 프롬프트 개선도 감이 아니라 실험이 됩니다. 스키마를 바꾸면 정확도가 올라가는지, enum을 줄이면 혼동이 줄어드는지, 모델을 바꾸면 비용 대비 효과가 있는지 비교할 수 있습니다. API 실전은 결국 평가와 함께 가야 합니다. 만들고 끝나는 것이 아니라, 측정하고 고치는 구조여야 합니다.

작은 고객지원 자동화 아키텍처

지금까지의 내용을 하나의 간단한 시스템으로 묶어 보겠습니다. 목표는 고객 문의를 받아 분류하고, 필요한 경우 담당 팀에 넘기며, 고객에게 보여 줄 답변 초안을 만드는 것입니다.

고객 문의 입력
→ 개인정보 마스킹
→ Structured Outputs로 문의 분류
→ urgency와 action 확인
→ 필요한 경우 function calling으로 주문 또는 티켓 조회
→ 조회 결과를 바탕으로 답변 초안 생성
→ 고위험 문구 검사
→ 상담원 승인
→ 헬프데스크에 초안 저장 또는 고객 발송
→ 로그와 평가 데이터 저장

이 아키텍처에서 Structured Outputs는 "무슨 문의인가"를 안정적으로 분류하는 데 쓰입니다. function calling은 주문 내역이나 티켓 상태 같은 외부 데이터를 가져오는 데 쓰입니다. 승인 흐름은 발송이나 상태 변경 같은 쓰기 작업을 통제합니다. 로그와 평가는 나중에 품질을 개선하는 기반이 됩니다.

중요한 것은 이 시스템이 처음부터 완전 자동 발송을 목표로 하지 않는다는 점입니다. 처음에는 분류와 초안까지만 자동화하고, 상담원이 승인하도록 만드는 편이 좋습니다. 일정 기간 동안 정확도와 사고율을 측정한 뒤, 위험이 낮은 문의 유형부터 자동화를 넓힐 수 있습니다.

좋은 자동화는 한 번에 모든 사람을 놀라게 하지 않습니다. 조용히, 작게, 안전하게 시작해서 신뢰를 쌓습니다.

API 설계 패턴: 초급에서 운영까지

개발 단계별로 API 설계의 초점은 달라집니다. 처음부터 완벽한 에이전트 시스템을 만들려고 하면 오히려 실패하기 쉽습니다. 단계적으로 넓혀 가야 합니다.

단계목표구현 포인트금지할 것
1단계단순 응답 생성시스템 지시, 모델 선택, 기본 로그민감정보 입력
2단계구조화 출력JSON Schema, Pydantic, 파싱 예외 처리자연어 파싱 의존
3단계읽기 도구 연결검색, DB 조회, function calling전체 데이터 접근
4단계초안 자동화답변, 보고서, 티켓 초안 저장외부 자동 발송
5단계제한적 쓰기승인 후 상태 변경, 내부 태그 추가되돌릴 수 없는 작업 자동 실행
6단계운영 최적화평가, 비용 제어, rate limit, 관측가능성로그 없는 자동화

이 단계표는 보수적으로 보일 수 있습니다. 그러나 실제 조직에서는 보수적인 접근이 더 빠릅니다. 사고가 한 번 나면 AI 도입 전체가 멈출 수 있습니다. 반대로 작은 성공을 반복하면 조직은 더 넓은 권한을 줄 준비가 됩니다.

실무 체크리스트: API 기능 출시 전

API 기능을 운영에 올리기 전에는 최소한 다음 질문에 답해야 합니다.

출시 전 점검

1. 입력
- 사용자 입력 길이 제한이 있는가?
- 민감정보를 탐지하고 제거하는가?
- 악의적 프롬프트 인젝션을 사용자 데이터로 분리해 처리하는가?

2. 출력
- 사람이 읽을 답과 코드가 처리할 데이터를 분리했는가?
- Structured Outputs 또는 엄격한 스키마를 사용하는가?
- 파싱 실패와 refusal을 처리하는가?

3. 도구
- 함수명과 설명이 명확한가?
- 인자를 서버에서 다시 검증하는가?
- 읽기와 쓰기 도구가 분리되어 있는가?

4. 권한
- 사용자의 실제 권한을 도구 실행 전에 확인하는가?
- 고위험 작업은 승인 흐름을 거치는가?
- 서비스 계정 권한이 최소화되어 있는가?

5. 운영
- 타임아웃과 재시도 정책이 있는가?
- rate limit 초과 시 큐 또는 fallback이 있는가?
- 비용 상한과 사용량 알림이 있는가?

6. 로그와 평가
- 요청 ID와 도구 호출 기록이 남는가?
- 개인정보가 로그에 과도하게 남지 않는가?
- 품질 평가용 샘플과 정답 데이터가 있는가?

이 체크리스트를 통과하지 못한 기능은 아직 데모입니다. 데모는 박수를 받을 수 있지만, 운영 기능은 감사와 장애와 사용자 불만을 견뎌야 합니다.

개발자에게 필요한 태도

API 실전에서 가장 위험한 개발자는 모델을 불신하는 개발자가 아닙니다. 오히려 모델을 너무 쉽게 믿는 개발자입니다. "대부분 잘하던데요"라는 말은 운영 시스템에서 충분하지 않습니다. 대부분 잘하는 기능도 중요한 순간에 틀릴 수 있습니다. 그리고 고객은 평균 정확도보다 자기 사건 하나를 기억합니다.

반대로 모델을 전혀 믿지 않는 태도도 생산적이지 않습니다. 모델은 이미 문서 요약, 분류, 추출, 초안 작성, 코드 보조, 검색 질의 생성에서 강력한 성능을 보여 줍니다. 중요한 것은 믿거나 말거나가 아닙니다. 어디까지 맡기고, 어디서 검증하고, 어디서 사람이 승인할지 결정하는 것입니다.

API 개발자는 이제 프롬프트 작성자이자 백엔드 엔지니어이며, 품질 관리자이자 리스크 설계자입니다. 모델 호출 한 줄이 아니라 업무 흐름 전체를 봐야 합니다.

이 장의 결론: 자연어와 소프트웨어 계약 사이

ChatGPT 화면에서는 자연어가 왕입니다. 사용자는 편하게 말하고, 모델은 알아듣고, 사람은 결과를 읽습니다. 그러나 API 시스템에서는 자연어만으로 충분하지 않습니다. 자연어는 입력의 문이고, 구조화 출력은 소프트웨어의 계약이며, function calling은 외부 세계로 나가는 손입니다.

Responses API는 이 흐름을 하나의 애플리케이션 요청 흐름으로 묶는 기반입니다. function calling은 모델이 외부 도구를 직접 실행하는 것이 아니라, 실행 요청을 구조화해 애플리케이션에 전달하는 패턴입니다. Structured Outputs는 "JSON으로 답해 줘"라는 희망을 넘어, 후속 코드가 소비할 수 있는 스키마 기반 응답을 만드는 방법입니다.

하지만 이 세 가지를 쓴다고 자동으로 안전한 시스템이 되지는 않습니다. 안전은 코드 밖에서 오지 않습니다. 함수 스키마를 작게 만들고, 인자를 검증하고, 권한을 확인하고, 대화 상태를 직접 관리하고, 민감정보를 제거하고, 고위험 작업에는 승인 흐름을 두어야 합니다. 실패를 기록하고, 비용을 측정하고, 평가 루프를 만들어야 합니다.

이제 우리는 ChatGPT를 제품 안으로 들여보낼 준비를 하고 있습니다. 다음 장에서는 여기서 한 걸음 더 나아가 자동화, RPA, 데이터 파이프라인, 관측가능성을 다룹니다. 오늘 배운 구조화 출력과 function calling이 그 자동화의 뼈대가 됩니다. 기억할 것은 하나입니다. AI 시스템의 품질은 모델의 똑똑함만으로 결정되지 않습니다. 모델을 둘러싼 계약, 검증, 승인, 로그가 함께 품질을 만듭니다.

참고

1Function calling | OpenAI API

2Structured model outputs | OpenAI API

3OpenAI Structured Outputs sample apps

4How to Use Structured Outputs in OpenAI: A Production Guide

5OpenAI GPT API에서 structured output 구조로 대답을 받도록 설정하는 방법 정리

책 목록

챗GPT 공략집

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기