AI 업무 활용

퓨샷 예제로 AI 출력 형식을 일정하게 만들고 자동 검증하기

AI에게 원하는 출력 구조의 예시를 먼저 보여준 뒤, 반환된 JSON을 실제 사용 전에 자동 검증합니다. 작은 합성 업무 티켓 데이터로 형식 예시와 검증 스크립트를 함께 사용하는 방법을 확인합니다.

목차 보기

이런 분께 맞아요반복 업무를 AI로 구조화하면서 자유 형식 문장 대신 예측 가능한 기계 판독형 출력을 얻고 싶은 사람을 위한 안내입니다.

준비할 것
  • Python 3.12와 해당 버전을 실행하는 터미널 명령이 준비되어 있어야 합니다.
  • 예시 프롬프트를 붙여 넣을 수 있는 AI 도구가 필요합니다.
  • UTF-8 JSON 파일을 저장할 수 있는 텍스트 편집기가 필요합니다.
  • 검증에는 Python 표준 라이브러리인 csv, json, pathlib만 사용합니다.

01프롬프트 전에 출력 규칙부터 정의하기

짧은 업무 지원 티켓을 구조화된 레코드로 변환한다고 가정합니다. 각 결과에는 ticket_id, priority, owner_team, action의 네 키만 정확히 있어야 합니다. priority는 LOW, MEDIUM, HIGH 중 하나이며 owner_team은 IT, FACILITIES, FINANCE 중 하나여야 합니다. 네 값 모두 비어 있지 않은 문자열이어야 합니다.

중요한 점은 AI 응답과 별개로 출력 규칙을 먼저 정의하는 것입니다. AI는 후보 레코드를 생성하고, 실제로 요구한 구조를 만족하는지는 검증 스크립트가 판단합니다.

02합성 업무 티켓 3개 만들기

아래 티켓은 이 글을 위해 작성한 합성 데이터입니다. 사람이 직접 의도된 구조화 결과를 판단할 수 있도록 단순하게 구성했습니다.

ticket_id합성 티켓 내용예상 분류
T101Payroll spreadsheet cannot be opened before today's payment run.HIGH · FINANCE
T102Meeting room B light is flickering but the room is still usable.LOW · FACILITIES
T103New employee laptop cannot connect to the office Wi-Fi.MEDIUM · IT

이 튜토리얼에서는 예상 action도 미리 고정합니다. T101은 Check payroll workbook access, T102는 Inspect meeting room B light, T103은 Troubleshoot laptop Wi-Fi connection입니다. 이 값들은 합성 연습을 위한 정답이며 실제 지원 업무의 일반 규칙이 아닙니다.

03AI에게 정확한 출력 형식 예시 보여주기

퓨샷 프롬프트는 새로운 입력을 주기 전에 완료된 예시를 하나 이상 보여주는 방식입니다. 아래 예시는 분류 방식뿐 아니라 정확한 JSON 구조도 보여줍니다. 실제 검사 대상 3개와는 다른 합성 티켓을 예시로 사용합니다.

text
Convert each ticket into a JSON object.

Rules:
- Return one JSON array only.
- Do not add explanations or Markdown.
- Use exactly these keys in every object: ticket_id, priority, owner_team, action.
- priority must be LOW, MEDIUM, or HIGH.
- owner_team must be IT, FACILITIES, or FINANCE.

Example 1 input:
Ticket ID: E001
Text: Printer on floor 2 is out of paper.

Example 1 output:
{"ticket_id":"E001","priority":"LOW","owner_team":"IT","action":"Refill or check floor 2 printer"}

Example 2 input:
Ticket ID: E002
Text: Expense approval file is unavailable before today's reimbursement deadline.

Example 2 output:
{"ticket_id":"E002","priority":"HIGH","owner_team":"FINANCE","action":"Check expense approval file access"}

Now process these tickets:
T101: Payroll spreadsheet cannot be opened before today's payment run.
T102: Meeting room B light is flickering but the room is still usable.
T103: New employee laptop cannot connect to the office Wi-Fi.

예시는 단순히 필드 이름만 보여주지 않습니다. 대문자 분류값, 짧은 action 문장, JSON 문자열 표기, 주변 설명을 넣지 않는 방식까지 보여줍니다. 따라서 원하는 출력 형태에 대한 모호함을 줄일 수 있습니다.

04AI 응답을 보기 전에 예상 출력 정의하기

이 합성 연습에서의 예상 답은 아래 JSON 배열입니다. 실제 AI 응답은 검증 전에 ai_output.json으로 저장하세요. 잘못된 응답을 먼저 사람이 수정하면 안 됩니다. 원래 응답이 규칙을 따랐는지 확인하는 것이 검증기의 목적입니다.

json
[
  {
    "ticket_id": "T101",
    "priority": "HIGH",
    "owner_team": "FINANCE",
    "action": "Check payroll workbook access"
  },
  {
    "ticket_id": "T102",
    "priority": "LOW",
    "owner_team": "FACILITIES",
    "action": "Inspect meeting room B light"
  },
  {
    "ticket_id": "T103",
    "priority": "MEDIUM",
    "owner_team": "IT",
    "action": "Troubleshoot laptop Wi-Fi connection"
  }
]

객체는 정확히 3개이고 전체 필드 값은 12개입니다. 허용된 priority 세 종류가 각각 한 번씩 나오고, 허용된 owner_team 세 종류도 각각 한 번씩 나옵니다.

05AI 응답 자동 검증하기

다음 스크립트를 check_ai_output.py로 저장하세요. JSON 문법, 배열 길이, 정확한 키, 데이터 형식, 허용 분류값, 티켓 ID, 합성 예제의 예상 분류를 검사합니다. AI 응답을 파싱한 뒤 검토용 CSV를 작성합니다.

python
import csv
import json
from pathlib import Path

SOURCE = Path("ai_output.json")
OUTPUT_DIR = Path("outputs") / "few_shot_check_result"
REPORT = OUTPUT_DIR / "format_check.csv"

EXPECTED = {
    "T101": ("HIGH", "FINANCE", "Check payroll workbook access"),
    "T102": ("LOW", "FACILITIES", "Inspect meeting room B light"),
    "T103": ("MEDIUM", "IT", "Troubleshoot laptop Wi-Fi connection"),
}
REQUIRED_KEYS = {"ticket_id", "priority", "owner_team", "action"}
PRIORITIES = {"LOW", "MEDIUM", "HIGH"}
TEAMS = {"IT", "FACILITIES", "FINANCE"}


def main() -> None:
    if not SOURCE.is_file():
        raise FileNotFoundError(f"AI output not found: {SOURCE}")
    if OUTPUT_DIR.exists():
        raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")

    with SOURCE.open("r", encoding="utf-8") as stream:
        data = json.load(stream)

    if not isinstance(data, list):
        raise ValueError("Top-level JSON value must be an array.")
    if len(data) != len(EXPECTED):
        raise ValueError(f"Expected {len(EXPECTED)} objects, got {len(data)}.")

    rows = []
    seen = set()
    for index, item in enumerate(data, start=1):
        errors = []
        if not isinstance(item, dict):
            raise ValueError(f"Item {index} is not a JSON object.")

        if set(item) != REQUIRED_KEYS:
            errors.append("keys")

        for key in REQUIRED_KEYS:
            if key not in item or not isinstance(item.get(key), str) or not item.get(key).strip():
                errors.append(f"invalid_{key}")

        ticket_id = item.get("ticket_id", "")
        if ticket_id in seen:
            errors.append("duplicate_ticket_id")
        seen.add(ticket_id)

        if item.get("priority") not in PRIORITIES:
            errors.append("priority")
        if item.get("owner_team") not in TEAMS:
            errors.append("owner_team")

        expected = EXPECTED.get(ticket_id)
        if expected is None:
            errors.append("unexpected_ticket_id")
        else:
            actual = (
                item.get("priority"),
                item.get("owner_team"),
                item.get("action"),
            )
            if actual != expected:
                errors.append("content_mismatch")

        rows.append({
            "item": index,
            "ticket_id": ticket_id,
            "status": "PASS" if not errors else "FAIL",
            "errors": ";".join(errors),
        })

    missing_ids = set(EXPECTED) - seen
    if missing_ids:
        raise ValueError(f"Missing ticket IDs: {sorted(missing_ids)}")

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()
    with REPORT.open("x", encoding="utf-8", newline="") as stream:
        writer = csv.DictWriter(
            stream,
            fieldnames=["item", "ticket_id", "status", "errors"],
        )
        writer.writeheader()
        writer.writerows(rows)

    failures = sum(row["status"] == "FAIL" for row in rows)
    print(f"Objects checked: {len(rows)}.")
    print(f"Passed: {len(rows) - failures}; failed: {failures}.")
    print(f"Report: {REPORT.as_posix()}")

    if failures:
        raise RuntimeError("AI output failed one or more checks.")


if __name__ == "__main__":
    main()

06예상 검증 결과 확인하기

ai_output.json이 합성 예제의 예상 출력과 정확히 같다면 객체 3개가 모두 통과해야 합니다. 아래 콘솔 출력은 손으로 도출한 예상값이며 실제 실행 로그가 아닙니다.

text
Objects checked: 3.
Passed: 3; failed: 0.
Report: outputs/few_shot_check_result/format_check.csv
  • 최상위 값이 JSON 배열인지 확인하고 배열 주변에 설명 문장이 없는지 확인합니다.
  • 모든 객체가 정확히 네 개의 필수 키만 갖는지 확인합니다.
  • 누락되거나 중복된 티켓이 없는지 확인합니다.
  • priority와 owner_team이 허용된 값만 사용하는지 확인합니다.
  • OUTPUT_DIR을 바꾸지 않고 다시 실행합니다. 이전 보고서를 덮어쓰지 않고 FileExistsError로 중단되어야 합니다.

07자주 발생하는 실패와 한계 이해하기

문제검증에서 나타나야 하는 결과
AI가 JSON 앞에 설명 문장을 추가파일 전체가 하나의 유효한 JSON 값이 아니므로 json.load가 실패합니다.
객체 하나에서 priority 대신 urgency 사용정확한 키 검사에 실패합니다.
priority가 urgent허용값 검사에 실패합니다.
T103이 두 번 등장중복 ticket_id 검사에 실패하고 다른 예상 ID가 누락될 수도 있습니다.
JSON 구조는 맞지만 분류가 틀림이 합성 연습에서는 content_mismatch가 기록됩니다.

퓨샷 프롬프트는 지시를 명확하게 만드는 데 도움이 되지만 결정적인 출력 보장을 제공하지는 않습니다. AI 시스템, 설정, 대화 문맥, 이후 프롬프트가 달라지면 결과도 달라질 수 있습니다. 여러 번 성공했더라도 검증기를 유지하세요.

이 예제는 작은 스키마를 직접 작성한 Python 조건으로 검증합니다. 더 큰 실제 스키마에서는 정식 스키마 도구와 별도의 의미 검증이 필요할 수 있습니다. 또한 JSON이 유효하다는 것은 형식이 맞다는 뜻일 뿐이며, AI가 만든 분류 자체가 사실이나 업무적으로 올바르다는 보장은 아닙니다.

실행·검증 기록

2026-09-20 · 예제 수동 검토 · 대상: Python 3.12 · 표준 라이브러리: csv, json, pathlib · 실행하지 않음

  • 합성 티켓 3개의 예상 priority, owner_team, action을 수동으로 정의했습니다.
  • 예상 JSON에 정확히 객체 3개와 객체당 키 4개가 있는지 수동으로 확인했습니다.
  • LOW, MEDIUM, HIGH가 각각 한 번씩, IT, FACILITIES, FINANCE도 각각 한 번씩 나오는지 확인했습니다.
  • JSON 파싱, 정확한 키 검사, 비어 있지 않은 문자열 검사, 허용값 검사, 중복 ID, 누락 ID, 예상 내용 비교 로직을 코드로 검토했습니다.
  • 예상 결과가 통과 3개, 실패 0개임을 손으로 도출했습니다.
검증 한계
  • 이 응답의 작성자는 AI 시스템에 실제 요청을 보내지 않았으므로 모델의 실제 출력 일관성은 시험하지 않았습니다.
  • Python 검증 코드를 실행하지 않았으며 JSON이나 CSV 파일을 실제로 만들지 않았습니다.
  • 합성 분류값은 이 연습을 위한 예상값이며 일반적인 지원 업무 배정 규칙이 아닙니다.
  • 구조가 유효하다고 해서 AI가 만든 분류가 사실적으로 정확하다는 뜻은 아닙니다.
  • 공식 문서 URL은 알려진 문서 위치를 사용했지만 실시간으로 접속해 확인하지 않았습니다.

사이트 전체 작성·검증 원칙

참고 출처

설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.