Skip to main content

7장. 구조화 출력과 툴 호출

6장에서 우리는 프롬프트를 통해 모델의 말투와 길이, 태도를 제어하는 법을 살펴봤다. 그런데 서비스 개발자는 곧바로 다음 벽에 부딪힌다. 문장이 아무리 그럴듯해도, 코드가 바로 쓸 수 없는 답변은 서비스에 넣기 어렵다는 벽이다.

예를 들어 사용자가 "이 문의는 결제 문제 같아?"라고 묻고, 모델이 "네, 결제와 관련된 이슈로 보입니다. 아마 이중 청구일 가능성이 있어요"라고 답했다고 해 보자. 사람은 이해할 수 있다. 하지만 서버 입장에서는 난감하다. 그래서 정확한 카테고리가 billing인지, 긴급도가 high인지, 상담 티켓을 바로 생성해야 하는지, 아니면 추가 질문을 해야 하는지 알 수 없다.

여기서부터 LLM은 단순한 대화 상대가 아니라 시스템의 일부가 된다. 그리고 시스템의 일부가 되려면 답변이 문장만으로 끝나면 안 된다. 자유 텍스트를 구조화된 데이터로 바꾸고, 필요하면 구조화된 액션 요청으로 바꾸는 단계가 필요하다.

이 장에서는 바로 그 전환을 다룬다. JSON 스키마 기반 출력이 왜 중요한지, 툴 호출이 어떻게 외부 API와 내부 시스템을 연결하는지, 그리고 검증과 재시도로 이 과정을 어떻게 안전하게 만드는지를 차근차근 살펴보자.

2부. LLM API의 공통 원리 이해하기 / 7장. 구조화 출력과 툴 호출

좋은 답변과 쓸 수 있는 답변은 다르다

사람에게 좋은 답변은 읽기 쉽고 자연스러운 답변이다. 하지만 서비스에 좋은 답변은 조금 다르다. 읽기 쉽기 전에 먼저 처리 가능해야 한다.

예를 들어 고객 문의 분류 기능을 만든다고 하자. 우리가 진짜로 필요한 것은 보통 이런 값들이다.

  • 문의 유형: billing, bug, feature, account

  • 긴급도: low, medium, high

  • 사람 상담 필요 여부: true 또는 false

  • 사용자에게 보낼 답장 초안

이 정보를 자유 문장으로 받으면, 그다음부터는 전부 후처리 지옥이 열린다. 정규식으로 뽑고, 문장을 다시 파싱하고, 예외 처리를 덕지덕지 붙여야 한다. 결국 "AI가 똑똑해서 편해질 줄 알았는데 왜 파서만 더 복잡해졌지?"라는 감정이 찾아온다.

반대로 처음부터 구조화된 형태로 받으면 이야기가 달라진다.

{
  "category": "billing",
  "priority": "high",
  "needs_human": true,
  "reply_draft": "결제 문제를 확인할 수 있도록 주문 정보를 보내주시면 빠르게 도와드리겠습니다."
}

이제 서버는 고민할 것이 줄어든다. 카테고리는 라우팅에 쓰고, 긴급도는 큐 우선순위에 쓰고, needs_human은 상담사 배정 여부에 쓰고, reply_draft는 화면에 보여 주면 된다. 같은 LLM 호출인데, 앞의 경우는 "그럴듯한 말"이고 뒤의 경우는 "바로 연결 가능한 데이터"다.

이 차이가 실전에서는 매우 크다. 챗봇 데모에서는 전자가 충분해 보이지만, 실제 서비스 운영에서는 후자만이 오래 버틴다.

JSON 스키마는 LLM과 백엔드 사이의 계약서다

구조화 출력의 핵심은 단순히 JSON을 받는 데 있지 않다. 어떤 JSON을 받을지 미리 약속하는 것에 있다. 이 약속이 바로 JSON 스키마다.

JSON 스키마를 너무 거창하게 생각할 필요는 없다. 쉽게 말하면 "이 응답은 이런 키를 가져야 하고, 각 키는 이런 타입이어야 하며, 어떤 값만 허용한다"를 선언하는 계약서다.

예를 들어 문의 분류용 스키마는 이렇게 잡을 수 있다.

{
  "type": "object",
  "properties": {
    "category": {
      "type": "string",
      "enum": ["billing", "bug", "feature", "account"]
    },
    "priority": {
      "type": "string",
      "enum": ["low", "medium", "high"]
    },
    "needs_human": {
      "type": "boolean"
    },
    "reply_draft": {
      "type": "string"
    }
  },
  "required": ["category", "priority", "needs_human", "reply_draft"],
  "additionalProperties": false
}

이 스키마가 주는 힘은 꽤 크다. category에는 허용된 라벨만 들어올 수 있고, needs_human은 반드시 불리언이어야 하며, 쓸데없는 키가 덧붙는 것도 막을 수 있다. OpenAI 문서도 JSON 모드는 "유효한 JSON"까지만 보장하고, 스키마와 정확히 맞는 출력이 필요하다면 Structured Outputs를 쓰라고 설명한다.1

실무에서는 스키마를 만들 때 세 가지부터 신경 쓰면 된다.

  • required: 빠지면 안 되는 필드가 무엇인가

  • enum: 자유 입력이 아니라 제한된 선택지로 고정할 수 있는가

  • additionalProperties: false: 예상하지 않은 필드를 막을 것인가

이 세 가지만 잘 잡아도 출력의 흔들림이 크게 줄어든다. 마치 프런트엔드 폼 검증에서 필수값과 선택값을 구분하는 것처럼, LLM 출력도 경계를 주면 훨씬 다루기 쉬워진다.

JSON으로 보이기만 하면 충분한가

처음 구조화 출력을 시도하는 팀은 보통 이렇게 시작한다.

"응답을 JSON으로 반환하세요."

물론 이 방법도 아예 쓸모없지는 않다. 하지만 서비스 규모가 조금만 커져도 금방 한계가 드러난다. JSON처럼 보여도 필드가 빠질 수 있고, 타입이 틀릴 수 있고, 키 이름이 미묘하게 바뀔 수 있기 때문이다.

예를 들어 우리는 priority를 원했는데 모델은 urgency를 줄 수 있다. 우리는 needs_human: true를 기대했는데 모델은 "yes"를 줄 수 있다. JSON 파서는 통과할지 몰라도, 서비스는 그대로 멈춘다.

그래서 구조화 출력에는 단계가 있다.

방식보장 수준흔한 문제
프롬프트로 "JSON으로 답해" 요청매우 약함코드블록, 설명 문장, 필드 누락
JSON 모드JSON 문법은 비교적 안정스키마 불일치 가능
스키마 기반 구조화 출력형태가 계약에 가까워짐의미 오류는 여전히 가능
구조화 출력 + 앱 검증운영 가능한 수준추가 비용과 설계 필요

핵심은 이것이다. 구조는 모델이 맞추게 하고, 의미는 애플리케이션이 검증해야 한다. 이 구분이 잡히면 이후 설계가 훨씬 명확해진다.

툴 호출은 답변 생성이 아니라 행동 제안이다

구조화 출력이 "데이터를 꺼내는 기술"이라면, 툴 호출은 "행동을 연결하는 기술"이다.

툴 호출을 처음 보면 마치 모델이 함수를 직접 실행하는 것처럼 느껴질 수 있다. 하지만 실제로는 그렇지 않다. 모델은 실행하지 않는다. 모델은 어떤 함수를 어떤 인자로 호출하면 좋을지 제안할 뿐이고, 실제 실행은 항상 당신의 코드가 한다. Gemini 공식 문서도 function calling을 자연어와 실제 시스템 행동을 잇는 다리로 설명한다.2

이 차이는 아주 중요하다. 모델이 재고 조회 도구를 "호출하겠다"고 말할 수는 있어도, 데이터베이스 연결 권한을 갖는 것은 아니다. 모델이 환불 API를 "사용하겠다"고 제안할 수는 있어도, 실제 결제 취소 버튼을 누르는 주체는 서버다.

툴 호출의 기본 흐름은 대체로 같다.

  1. 개발자가 사용할 수 있는 도구 목록을 정의한다.

  2. 모델에 사용자 요청과 도구 설명을 함께 보낸다.

  3. 모델이 특정 도구 호출과 인자를 구조화해서 돌려준다.

  4. 서버가 그 인자를 검증한 뒤 실제 함수를 실행한다.

  5. 필요하면 결과를 다시 모델에 넣어 최종 답변을 생성한다.

예를 들어 사용자가 "주문 1234의 배송 상태 알려줘"라고 물었다고 해 보자. 이때 모델이 자유 문장으로 "배송 중일 것 같아요"라고 말하면 위험하다. 하지만 아래처럼 도구 호출을 제안하면 훨씬 안전하다.

{
  "tool_name": "get_order_status",
  "arguments": {
    "order_id": "1234"
  }
}

이제 서버는 order_id가 비어 있지 않은지 확인하고, 해당 사용자가 그 주문의 소유자인지 검증한 뒤, 진짜 주문 시스템을 조회할 수 있다. 이 흐름에서는 LLM이 전지전능한 답변자가 아니라, 의도 해석기이자 호출 계획 생성기 역할을 맡는다.

구조화 출력과 툴 호출은 같이 쓸 때 더 강하다

많은 초보자는 둘 중 하나만 고르려고 한다. 하지만 실무에서는 구조화 출력과 툴 호출이 자주 함께 움직인다.

예를 들어 사용자의 메시지에서 먼저 구조화된 의도를 뽑을 수 있다.

{
  "intent": "refund_request",
  "order_id": null,
  "reason": "duplicate_charge",
  "needs_confirmation": true
}

이 구조화 데이터를 바탕으로 서버는 다음 행동을 결정한다. 주문 번호가 없으면 추가 질문을 보내고, 주문 번호가 있으면 조회 도구를 호출하고, 금전적 변경이 필요한 경우에는 사람 확인 단계를 넣는다.

즉, 흐름은 보통 이렇게 된다.

  • 1단계: 자유 텍스트를 구조화된 데이터로 바꾼다.

  • 2단계: 그 데이터를 근거로 어떤 도구를 쓸지 결정한다.

  • 3단계: 도구 결과를 받아 최종 응답을 만든다.

이 방식이 좋은 이유는, 질문 이해와 시스템 실행을 한 덩어리로 섞지 않기 때문이다. 먼저 의미를 정리하고, 그다음 행동을 결정하면 디버깅도 쉬워진다. 어디서 잘못됐는지 추적하기도 좋다. 의도 해석이 틀렸는지, 도구 호출 인자가 틀렸는지, 아니면 백엔드 시스템 응답이 이상한지 구분할 수 있기 때문이다.

검증은 한 번이 아니라 세 겹으로 한다

여기서 아주 중요한 사실 하나를 꼭 기억하자. 스키마에 맞는 JSON이 왔다고 해서, 그 내용이 맞다는 뜻은 아니다. 구조화 출력은 구조를 안정시켜 주지만, 의미까지 자동으로 보증해 주지는 않는다.3

그래서 실무에서는 검증을 대체로 세 겹으로 쌓는다.

1단계: 문법 검증

가장 바깥쪽은 "파싱 가능한가"다. JSON이 깨졌는지, 문자열이 중간에 잘렸는지, 코드블록이 끼어들었는지 확인한다.

요즘은 많은 API가 이 문제를 상당히 줄여 줬지만, 완전히 사라지지는 않는다. 특히 오래된 모델이나 비공식 경로를 쓸수록 이 단계에서 자주 흔들린다.

2단계: 스키마 검증

그다음은 "정해진 모양에 맞는가"다. 필수 필드가 있는지, priority가 low|medium|high 중 하나인지, 숫자여야 할 값이 문자열로 오지 않았는지 확인한다.

이 단계에서는 Zod, Pydantic, JSON Schema validator 같은 도구가 매우 유용하다. 모델이 생성한 결과를 그대로 믿지 말고, 애플리케이션에서 다시 한 번 검사해야 한다.

3단계: 비즈니스 검증

마지막이자 가장 중요하지만 가장 자주 빼먹는 단계다. "우리 서비스 규칙에 맞는가"를 확인하는 것이다.

예를 들어 이런 검증은 스키마만으로는 충분하지 않다.

  • 주문 ID가 실제로 존재하는가

  • 현재 로그인한 사용자가 그 주문의 소유자인가

  • 환불 가능한 기간이 남아 있는가

  • 예약 날짜가 과거가 아닌가

  • 금액이 현재 잔액이나 한도를 넘지 않는가

이 검증을 빼면 구조화 출력은 오히려 더 위험해질 수 있다. 형태가 반듯하니 더 믿고 싶어지기 때문이다. 하지만 반듯한 거짓말은 흐트러진 거짓말보다 더 위험하다.

재시도는 두 종류로 나눠서 생각하라

LLM 연동에서 재시도는 중요하다. 다만 한 종류의 재시도만 떠올리면 곤란하다. 실전에서는 보통 의미 재시도와 전송 재시도를 구분해서 설계한다.

의미 재시도: 출력이 틀렸을 때 다시 생성

의미 재시도는 모델 응답이 파싱은 됐지만 검증에 실패했을 때 사용한다. 예를 들어 priority가 허용된 enum 밖의 값이거나, order_id 형식이 잘못되었거나, 필수 필드가 비어 있을 때다.

이때 가장 단순하고 효과적인 방법은 검증 오류를 짧게 요약해서 모델에 다시 보내는 것이다. 예를 들면 이렇게 말할 수 있다.

이전 응답에서 priority 값이 허용 범위 밖이었습니다. low, medium, high 중 하나만 사용해 다시 생성하세요.

이런 식의 self-healing 재시도는 Pydantic 같은 스키마 검증기와 함께 자주 쓰이며, 실제 실무에서도 꽤 잘 통한다.4

다만 여기에는 규칙이 있다. 재시도 횟수는 보통 1~2회 정도로 짧게 제한하고, 같은 입력에 같은 실패가 반복되면 사람 검토나 보수적 폴백으로 넘겨야 한다. 세상 모든 문제를 "한 번만 더 물어보자"로 해결하려 들면 비용만 늘고 응답 시간만 길어진다.

전송 재시도: API 호출 자체가 실패했을 때 다시 요청

전송 재시도는 네트워크 오류, 타임아웃, 429, 5xx 같은 일시적 장애를 다룬다. 이 경우에는 모델 출력 품질 문제가 아니라 인프라 문제이므로, 재시도 방식도 달라야 한다.

여기서는 보통 지수 백오프와 지터를 사용한다. 쉽게 말해 바로 다시 두드리지 말고, 조금씩 기다리는 시간을 늘리면서 무작위 지연도 섞는 방식이다. 그렇지 않으면 여러 요청이 한꺼번에 재시도하면서 오히려 더 큰 혼잡을 만들 수 있다.5

의미 재시도와 전송 재시도를 섞어 버리면 디버깅이 어려워진다. "모델이 이상했던 것인지", "API가 잠깐 죽었던 것인지"가 헷갈리기 때문이다. 로그와 코드 구조에서도 이 둘을 분리하는 습관이 좋다.

쓰기 동작은 읽기 동작보다 훨씬 더 보수적으로 다뤄라

툴 호출에서 특히 조심할 부분은 쓰기 동작이다. 조회, 검색, 계산처럼 읽기 성격의 도구는 비교적 안전하다. 잘못 호출해도 보통은 잘못된 정보 한 번 보여 주는 선에서 끝난다.

하지만 환불, 예약 변경, 이메일 발송, 티켓 종료, 권한 부여 같은 쓰기 동작은 다르다. 한 번 실행되면 되돌리기 어렵다. 그래서 같은 툴 호출이라도 체급이 다르다.

실무에서는 아래 원칙이 도움이 된다.

  • 읽기 도구와 쓰기 도구를 분리한다.

  • 쓰기 도구는 가능한 한 명시적 확인 단계를 둔다.

  • 부작용이 큰 작업에는 idempotency key를 둔다.

  • 같은 요청을 자동 재실행하지 않는다.

  • 권한 검사는 LLM이 아니라 서버가 수행한다.

예를 들어 사용자가 "내일 오전 회의 취소해 줘"라고 말했다고 해서, 모델이 곧바로 cancel_meeting을 호출하게 두는 것은 위험하다. 같은 시간대 회의가 여러 개 있을 수 있고, 본인 회의가 아닐 수도 있고, 취소 대신 일정 변경이 더 맞는 상황일 수도 있다.

그래서 더 안전한 흐름은 이렇다. 먼저 후보 일정을 구조화해서 보여 주고, 사용자가 확인하면 그때 취소 API를 호출한다. 한 박자 느리더라도, 운영 사고를 줄이는 편이 훨씬 낫다.

안전한 툴 호출을 위한 최소 원칙

이 부분은 조금 딱딱하지만 정말 중요하다. LLM이 똑똑해 보인다는 이유로 시스템 열쇠를 통째로 넘기면 안 된다. 아래 원칙은 작게 시작하는 팀일수록 더 잘 지켜야 한다.

  • 도구는 allowlist로 제한하라. 모델이 아무 함수나 부를 수 있게 하지 말고, 사전에 등록한 이름만 허용한다.

  • 인자는 항상 서버에서 다시 검증하라. 모델이 만든 JSON도 결국 외부 입력이다.

  • 권한 검사는 툴 실행 직전에 하라. "이 사용자가 이 주문을 볼 수 있는가"는 모델이 판단할 일이 아니다.

  • 타임아웃과 실패 경계를 둬라. 도구 하나가 느리다고 전체 대화가 영원히 매달리면 안 된다.

  • 로그를 남겨라. 어떤 요청에서 어떤 도구가 어떤 인자로 호출되었는지 추적 가능해야 한다.

  • 폴백을 준비하라. 도구 호출 실패 시 사람 상담 전환, 일반 답변, 재시도 대기열 같은 우회로가 있어야 한다.

이 원칙을 보면 특별한 마법은 없다. 결국 전통적인 백엔드 원칙과 비슷하다. 다른 점은 입력을 사람이 아니라 LLM이 만든다는 것뿐이다. 하지만 보안 관점에서는 그 차이가 오히려 더 까다롭다. 자연어로 그럴듯하게 포장된 입력이라, 방심하기 쉽기 때문이다.

실전 예시: 환불 요청을 안전하게 처리하는 흐름

이제 조금 더 서비스다운 예시를 보자. 사용자가 이렇게 말했다고 하자.

지난주에 결제가 두 번 됐어요. 하나 환불해 주세요.

이 문장을 읽은 LLM이 곧바로 환불 API를 호출하도록 만들면, 사고가 나기 딱 좋다. 주문 번호도 없고, 진짜 중복 결제인지도 모른다. 계정 소유자 본인인지도 아직 확인되지 않았다.

그래서 더 실무적인 흐름은 아래와 같다.

1단계: 의도와 필요한 정보를 구조화한다

먼저 모델에게 액션이 아니라 해석을 맡긴다.

{
  "intent": "refund_request",
  "reason": "duplicate_charge",
  "order_id": null,
  "needs_human": false,
  "needs_confirmation": true,
  "missing_fields": ["order_id"]
}

이 응답을 받으면 서버는 바로 환불하지 않는다. 대신 "주문 번호를 알려 주세요"라는 후속 질문을 보낼 수 있다.

2단계: 읽기 도구로 사실을 확인한다

주문 번호를 받으면 서버는 다음 같은 읽기 도구를 사용할 수 있다.

  • find_order(order_id)

  • check_duplicate_charge(order_id)

  • get_refund_policy(order_id)

이 단계의 목적은 행동이 아니라 확인이다. LLM은 어떤 정보를 조회해야 할지 도와줄 수 있지만, 실제 사실 판단은 내부 시스템 데이터가 한다.

3단계: 최종 행동은 확인 뒤에 실행한다

이제야 비로소 환불 요청 생성 도구를 고려할 수 있다.

{
  "tool_name": "create_refund_request",
  "arguments": {
    "order_id": "A12345",
    "reason": "duplicate_charge"
  }
}

하지만 이 호출도 서버는 그대로 믿지 않는다. 현재 사용자와 주문 소유자가 일치하는지, 이미 환불 진행 중인 건이 아닌지, 중복 결제 판정이 실제로 맞는지 확인한다. 그리고 가능하면 한 번 더 사용자 확인을 거친다.

이 예시가 보여 주는 핵심은 단순하다. LLM은 행동의 트리거를 만들 수 있지만, 행동의 승인자가 되어서는 안 된다. 특히 돈, 권한, 외부 발송, 상태 변경이 걸린 작업에서는 더 그렇다.

스키마는 단순할수록 오래 산다

구조화 출력을 처음 도입하면 욕심이 생긴다. 필드도 많이 넣고, 중첩도 깊게 하고, 예외 케이스도 전부 한 번에 담고 싶어진다. 하지만 대부분의 경우 첫 버전은 너무 복잡해서 오래 못 간다.

모델도 복잡한 스키마를 채우느라 헷갈리고, 개발자도 검증과 디버깅에 지치기 쉽다. 무엇보다 제품 요구사항이 아직 고정되지 않은 초기에 스키마를 너무 세밀하게 박아 버리면, 나중에 작은 정책 변경에도 전체 구조를 흔들어야 한다.

그래서 초반에는 대체로 이렇게 가는 편이 좋다.

  • 꼭 필요한 필드만 둔다.

  • 자유 텍스트보다 enum을 우선한다.

  • 중첩 구조는 정말 필요할 때만 도입한다.

  • 설명용 필드와 실행용 필드를 분리한다.

예를 들어 reply_draft는 사람이 읽는 필드고, category와 priority는 시스템이 처리하는 필드다. 둘을 한데 섞어 "모든 걸 담은 만능 응답"을 만들려 하지 말고, 역할을 나눠 두는 편이 유지보수에 유리하다.

흔한 실패 패턴을 미리 피하자

이 장의 내용을 적용할 때 자주 나오는 실수도 있다. 아래 패턴은 꽤 흔하다.

  • 프롬프트로만 JSON을 강요하고 검증은 생략한다.

  • 구조화 출력이 들어오자마자 DB에 저장하거나 API를 실행한다.

  • 스키마가 지나치게 복잡해 모델도 사람도 이해하기 어렵다.

  • 쓰기 도구를 읽기 도구처럼 자동 실행한다.

  • 검증 실패와 네트워크 실패를 같은 재시도로 뭉갠다.

  • 재시도 횟수를 무한정 늘려 비용과 지연만 키운다.

특히 세 번째와 네 번째가 치명적이다. 스키마가 복잡하면 출력은 형식상 맞아도 의미가 엉키기 쉽고, 쓰기 도구를 자동 실행하면 작은 착오가 바로 운영 사고로 번진다. 데모에서는 멋져 보여도, 운영에서는 늘 "덜 똑똑하지만 더 안전한 쪽"이 이긴다.

결국 중요한 것은 모델이 아니라 경계선이다

구조화 출력과 툴 호출을 배우다 보면 자꾸 모델 능력에 시선이 쏠린다. 어떤 모델이 스키마를 더 잘 맞추는지, 어떤 모델이 툴 호출을 더 똑똑하게 하는지 궁금해진다. 물론 중요하다.

하지만 서비스 관점에서 더 중요한 것은 모델 자체보다 경계선의 설계다.

어디까지를 모델이 하게 할 것인가. 어디부터는 검증기가 막을 것인가. 어떤 동작은 자동 실행하고, 어떤 동작은 사람 확인을 거치게 할 것인가. 실패하면 재시도할 것인가, 폴백할 것인가, 아니면 포기하고 더 안전한 답을 줄 것인가.

이 경계선이 설계되어 있으면 모델을 바꿔도 시스템이 무너지지 않는다. 반대로 경계선이 없으면, 아무리 좋은 모델을 써도 언젠가는 그럴듯한 실수가 실제 사고가 된다.

정리

이 장의 핵심은 분명하다. 자유 텍스트를 구조화된 데이터와 액션으로 바꿔야 비로소 LLM을 서비스에 넣을 수 있다.

JSON 스키마 기반 구조화 출력은 모델 응답을 계약 가능한 형태로 바꿔 준다. 툴 호출은 모델이 외부 시스템을 직접 실행하는 대신, 어떤 도구를 어떤 인자로 써야 하는지 제안하게 만든다. 그리고 이 둘 사이를 실제 서비스로 연결해 주는 것은 검증, 권한 확인, 재시도, 타임아웃, 로그 같은 백엔드의 기본기다.

정리하면 역할은 이렇게 나뉜다. 모델은 해석하고 제안한다. 스키마는 형태를 제한한다. 검증기는 틀린 값을 걸러낸다. 서버는 권한과 비즈니스 규칙을 확인한다. 실제 시스템은 마지막에만 실행된다.

이 순서를 지키면 LLM은 더 이상 "가끔 잘 말하는 기능"이 아니라, 안전하게 연결된 서비스 구성요소가 된다. 다음 장부터는 이 공통 원리를 바탕으로 벤더별 API 차이와 확장 패턴을 더 구체적으로 살펴보게 된다.

참고

1Structured model outputs | OpenAI API

2Function calling with the Gemini API

3How to Use Structured JSON Outputs With LLMs

4LLM Structured Output 실전 가이드 — JSON Mode, Tool Use, Pydantic 스키마 검증

5How to Implement Retry Logic for LLM API Failures in 2025

책 목록

실전 LLM 서비스 개발: ChatGPT·Claude·Gemini API로 만드는 AI 웹 서비스의 설계부터 운영까지

전체 목차 보기

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

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

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

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

참여하기