실전 가이드 ·

툴 호출과 구조화 출력은 역할을 나눌 때 안정된다

먼저 답부터

에이전트가 외부 기능을 실행할 때와 사용자에게 결과를 돌려줄 때, 각각 어떤 스키마와 검증을 둬야 하는지 공식 문서를 바탕으로 정리했습니다.

툴 호출과 구조화 출력은 역할을 나눌 때 안정된다 대표 이미지

에이전트가 가끔 엉뚱한 동작을 하는 문제를 모델 탓으로만 돌리기 쉽습니다. 하지만 운영 화면을 들여다보면 더 자주 보이는 원인은 단순합니다. 모델이 외부 시스템에 무엇을 요청해야 하는지, 앱이 받은 결과를 어떤 형태로 화면에 보여 줄지, 실패하면 어디에서 멈출지를 한 흐름으로 섞어 둔 경우입니다.

툴 호출과 구조화 출력은 비슷해 보이지만 맡은 일이 다릅니다. OpenAI의 현재 가이드도 모델을 앱의 기능·데이터·외부 시스템에 연결할 때는 함수 호출을, 사용자에게 돌려줄 결과의 형식을 고정할 때는 구조화된 응답 형식을 쓰도록 구분합니다. 이 차이를 먼저 나누면 추측에 의존하던 단계를 테스트 가능한 계약으로 바꿀 수 있습니다.

먼저 한 문장으로 끝 상태를 정한다

예를 들어 사용자가 “지난주 미처리 문의를 요약해 줘”라고 했을 때 목표는 'AI가 답한다'가 아닙니다. '문의 시스템에서 지난 7일의 미처리 건을 조회하고, 각 건의 제목·담당자·다음 조치를 정해진 화면 데이터로 돌려준다'가 목표입니다. 이 문장이 있어야 조회 도구의 권한, 결과의 필드, 사람이 확인할 지점을 정할 수 있습니다.

여기서 중요한 것은 자연어 요청을 곧바로 실행하지 않는 일입니다. 날짜 범위와 조회 대상처럼 이미 앱이 알고 있는 값은 코드에서 넣고, 모델에게 불필요한 식별자를 다시 채우게 하지 않는 편이 안전합니다. OpenAI 함수 호출 가이드도 애플리케이션이 이미 아는 인수를 모델에게 만들게 하지 말라고 권합니다.

함수 호출은 모델의 제안이고, 실행은 애플리케이션의 책임이다

함수 호출에서 모델은 실제 데이터베이스나 결제 시스템을 직접 건드리지 않습니다. 도구 이름과 JSON 인수를 담은 호출을 반환하고, 애플리케이션이 그 호출을 검사한 뒤 자체 권한으로 실행합니다. 실행 결과를 다시 모델에 전달해야 다음 답변이나 다음 호출이 이어집니다.

따라서 호출을 받자마자 실행하는 코드는 피해야 합니다. 도구 이름을 허용 목록과 비교하고, 사용자 권한과 조직 범위를 서버에서 다시 확인하며, 금액·수신자·삭제 여부처럼 되돌리기 어려운 값은 승인 화면에서 보여 주세요. 모델이 올바른 JSON을 냈다는 사실은 업무 규칙을 통과했다는 뜻이 아닙니다.

조회 도구라면 빈 결과도 정상적인 결과로 표현해야 합니다. 쓰기 도구라면 실행 전과 후의 상태를 기록하고, 성공 응답 뒤에 다시 조회해 실제 반영 여부를 확인하는 편이 좋습니다. 특히 하나의 응답에 호출이 여러 개 들어올 수 있으므로, 한 번만 온다고 가정하거나 첫 호출만 처리하면 안 됩니다.

구조화 출력은 화면과 다음 단계의 계약이다

사용자에게 보여 줄 내용을 카드, 표, 후속 자동화가 읽는 JSON으로 만들 때는 구조화 출력을 씁니다. 예를 들어 문의 요약의 최종 형식을 status, items, needs_human_review처럼 정하면 렌더러가 문장 속에서 값을 억지로 찾아낼 필요가 없습니다. 필수 키가 빠지거나 열거형에 없는 상태값이 오는 문제도 줄일 수 있습니다.

그렇다고 스키마가 사실을 보증하는 것은 아닙니다. 스키마는 '담당자'라는 칸이 있다는 것을 보장할 뿐, 그 이름이 원본 문의와 맞는지는 도구 결과와 업무 검증이 확인해야 합니다. 사용자 입력이 스키마와 맞지 않을 때 모델이 그럴듯한 값을 지어낼 수 있다는 점도 공식 가이드가 짚습니다. 그래서 needs_human_review, reason, source_count처럼 모름과 보류를 표현할 필드를 처음부터 두는 편이 낫습니다.

strict mode를 켜기 전에 스키마를 작게 만든다

함수 호출에서 strict mode는 인수가 정의한 스키마를 안정적으로 따르도록 돕습니다. 현재 가이드 기준으로 각 객체에는 additionalProperties: false가 필요하고, properties에 둔 필드는 모두 required여야 합니다. 이 조건이 맞지 않으면 요청이 거부될 수 있으므로, 복잡한 스키마를 한 번에 넣기보다 작은 도구 하나에서 먼저 확인하세요.

선택 필드가 필요할 때는 필드를 빼는 방식에 기대기보다 null을 허용하고 의미를 문서화하는 방법을 검토할 수 있습니다. 예를 들어 담당자가 없는 문의는 assignee: null과 needs_human_review: true로 표현하면, 화면과 후속 작업이 같은 해석을 공유합니다. 스키마 버전도 결과에 포함해 새 필드를 추가할 때 소비자를 함께 점검하세요.

상태 변경 도구는 병렬 호출을 기본값으로 두지 않는다

서로 독립적인 읽기 작업은 여러 개를 함께 처리해도 편리할 수 있습니다. 하지만 같은 고객 레코드를 수정하거나 재고를 차감하는 작업은 순서가 곧 결과입니다. 함수 호출 가이드는 병렬 호출을 끌 수 있으며, 그 경우 한 번의 응답에서 0개 또는 1개의 도구만 호출되도록 할 수 있다고 안내합니다.

이런 작업은 '조회 → 변경안 생성 → 사람 승인 → 한 건 실행 → 재조회'로 분리하세요. 도구 결과에는 성공 여부만 넣지 말고, 대상 ID·변경 전후 값·실행 시각·재조회 결과를 함께 남깁니다. 호출 실패와 업무상 거절도 구분해야 재시도 버튼이 위험한 변경을 되풀이하지 않습니다.

배포 전에 해 볼 여섯 가지

  1. 정상 요청 한 건에서 도구 인수, 실제 실행 결과, 최종 화면 데이터가 모두 일치하는지 확인합니다.
  2. 필수 값이 빠지거나 허용되지 않은 값이 들어왔을 때, 도구 실행 전에 이해할 수 있는 오류가 나는지 확인합니다.
  3. 데이터가 없는 조회와 안전 정책상 거절을 서로 다른 상태로 표시하는지 확인합니다.
  4. 한 응답에 여러 호출이 들어왔을 때 모두 식별하고, 상태 변경 작업은 병렬 실행되지 않게 설정했는지 확인합니다.
  5. 권한 없는 사용자, 다른 조직의 ID, 너무 넓은 날짜 범위를 서버가 다시 막는지 확인합니다.
  6. 변경 뒤 재조회가 실패하거나 값이 다를 때 자동 실행을 멈추고 사람이 볼 기록을 남기는지 확인합니다.

안정성은 잘 말하는 답변보다 확인 가능한 경계에서 나온다

에이전트의 설명이 매끄러워도 잘못된 대상을 수정하면 운영에는 도움이 되지 않습니다. 반대로 함수 호출은 외부 행동의 경계로, 구조화 출력은 다음 화면과 다음 단계의 계약으로 쓰면 실패 위치가 분명해집니다. 처음에는 읽기 전용 도구 하나와 작은 결과 스키마로 시작해 보세요. 그 흐름의 로그와 예외가 충분히 보인 뒤에만 권한과 자동 실행 범위를 넓히는 쪽이 오래 갑니다.

확인한 자료